aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format111
-rw-r--r--.clang-tidy275
-rw-r--r--.github/actions/download-dm/action.yml15
-rw-r--r--.github/actions/download-mingw/action.yml19
-rw-r--r--.github/actions/download-ow/action.yml15
-rw-r--r--.github/actions/download-qt/action.yml22
-rw-r--r--.github/actions/download-qtc/action.yml15
-rw-r--r--.github/workflows/docs.yml38
-rw-r--r--.github/workflows/main.yml919
-rw-r--r--.github/workflows/release.yml229
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules3
-rw-r--r--.travis.yml123
-rw-r--r--CMakeLists.txt74
-rw-r--r--CONTRIBUTING.md102
-rw-r--r--README8
-rw-r--r--README.md36
-rw-r--r--RELEASE.md105
-rw-r--r--VERSION2
-rw-r--r--changelogs/changes-1.14.0.md18
-rw-r--r--changelogs/changes-1.15.0.md14
-rw-r--r--changelogs/changes-1.16.0.md126
-rw-r--r--changelogs/changes-1.17.0.md98
-rw-r--r--changelogs/changes-1.17.1.md10
-rw-r--r--changelogs/changes-1.18.0.md84
-rw-r--r--changelogs/changes-1.18.2.md41
-rw-r--r--changelogs/changes-1.19.0.md65
-rw-r--r--changelogs/changes-1.19.1.md12
-rw-r--r--changelogs/changes-1.19.2.md5
-rw-r--r--changelogs/changes-1.20.0.md62
-rw-r--r--changelogs/changes-1.20.1.md12
-rw-r--r--changelogs/changes-1.21.0.md56
-rw-r--r--changelogs/changes-1.22.0.md31
-rw-r--r--changelogs/changes-1.22.1.md16
-rw-r--r--changelogs/changes-1.23.0.md16
-rw-r--r--changelogs/changes-1.23.1.md19
-rw-r--r--changelogs/changes-1.23.2.md10
-rw-r--r--changelogs/changes-1.24.0.md12
-rw-r--r--changelogs/changes-1.24.1.md9
-rw-r--r--changelogs/changes-2.0.0.md22
-rw-r--r--changelogs/changes-2.0.1.md27
-rw-r--r--changelogs/changes-2.0.2.md12
-rw-r--r--changelogs/changes-2.1.0.md28
-rw-r--r--changelogs/changes-2.1.1.md8
-rw-r--r--changelogs/changes-2.1.2.md19
-rw-r--r--changelogs/changes-2.2.0.md36
-rw-r--r--changelogs/changes-2.2.1.md12
-rw-r--r--changelogs/changes-2.2.2.md18
-rw-r--r--changelogs/changes-2.3.0.md37
-rw-r--r--changelogs/changes-2.3.1.md17
-rw-r--r--cmake/QbsBuildConfig.cmake261
-rw-r--r--cmake/QbsDocumentation.cmake301
-rw-r--r--doc/CMakeLists.txt30
-rw-r--r--doc/appendix/json-api.qdoc20
-rw-r--r--doc/appendix/qbs-porting.qdoc1
-rw-r--r--doc/codeattributions.qdoc97
-rw-r--r--doc/config/macros.qdocconf3
-rw-r--r--doc/config/qbs-project.qdocconf3
-rw-r--r--doc/doc.pri18
-rw-r--r--doc/doc.qbs16
-rw-r--r--doc/doc_shared.pri14
-rw-r--r--doc/doc_targets.pri86
-rw-r--r--doc/external-resources.qdoc7
-rwxr-xr-xdoc/fix-qmlimports.py6
-rw-r--r--doc/howtos.qdoc290
-rw-r--r--doc/man/CMakeLists.txt3
-rw-r--r--doc/man/man.pri5
-rw-r--r--doc/man/man.qbs1
-rw-r--r--doc/qbs-hugo.qdocconf6
-rw-r--r--doc/qbs-online.qdocconf2
-rw-r--r--doc/qbs.qdoc469
-rw-r--r--doc/reference/cli/builtin/cli-build.qdoc3
-rw-r--r--doc/reference/cli/builtin/cli-clean.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-dump-nodes-tree.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-generate.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-help.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-install.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-list-products.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-resolve.qdoc3
-rw-r--r--doc/reference/cli/builtin/cli-run.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-session.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-shell.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-status.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-update-timestamps.qdoc1
-rw-r--r--doc/reference/cli/builtin/cli-version.qdoc1
-rw-r--r--doc/reference/cli/cli-options.qdocinc24
-rw-r--r--doc/reference/cli/cli.qdoc1
-rw-r--r--doc/reference/cli/tools/cli-config-ui.qdoc1
-rw-r--r--doc/reference/cli/tools/cli-config.qdoc1
-rw-r--r--doc/reference/cli/tools/cli-create-project.qdoc1
-rw-r--r--doc/reference/cli/tools/cli-setup-android.qdoc1
-rw-r--r--doc/reference/cli/tools/cli-setup-qt.qdoc1
-rw-r--r--doc/reference/cli/tools/cli-setup-toolchains.qdoc1
-rw-r--r--doc/reference/commands.qdoc5
-rw-r--r--doc/reference/items/convenience/appleapplicationdiskimage.qdoc8
-rw-r--r--doc/reference/items/convenience/applediskimage.qdoc1
-rw-r--r--doc/reference/items/convenience/application.qdoc39
-rw-r--r--doc/reference/items/convenience/applicationextension.qdoc1
-rw-r--r--doc/reference/items/convenience/autotestrunner.qdoc35
-rw-r--r--doc/reference/items/convenience/cppapplication.qdoc5
-rw-r--r--doc/reference/items/convenience/dynamiclibrary.qdoc53
-rw-r--r--doc/reference/items/convenience/innosetup.qdoc1
-rw-r--r--doc/reference/items/convenience/installpackage.qdoc1
-rw-r--r--doc/reference/items/convenience/javaclasscollection.qdoc1
-rw-r--r--doc/reference/items/convenience/javajarfile.qdoc3
-rw-r--r--doc/reference/items/convenience/library.qdoc129
-rw-r--r--doc/reference/items/convenience/loadablemodule.qdoc5
-rw-r--r--doc/reference/items/convenience/qtapplication.qdoc3
-rw-r--r--doc/reference/items/convenience/qtguiapplication.qdoc1
-rw-r--r--doc/reference/items/convenience/staticlibrary.qdoc27
-rw-r--r--doc/reference/items/convenience/xpcservice.qdoc1
-rw-r--r--doc/reference/items/language/artifact.qdoc1
-rw-r--r--doc/reference/items/language/depends.qdoc24
-rw-r--r--doc/reference/items/language/export.qdoc52
-rw-r--r--doc/reference/items/language/filetagger.qdoc1
-rw-r--r--doc/reference/items/language/group.qdoc37
-rw-r--r--doc/reference/items/language/joblimit.qdoc1
-rw-r--r--doc/reference/items/language/module.qdoc60
-rw-r--r--doc/reference/items/language/moduleprovider.qdoc40
-rw-r--r--doc/reference/items/language/parameter.qdoc1
-rw-r--r--doc/reference/items/language/parameters.qdoc5
-rw-r--r--doc/reference/items/language/probe.qdoc3
-rw-r--r--doc/reference/items/language/product.qdoc17
-rw-r--r--doc/reference/items/language/profile.qdoc1
-rw-r--r--doc/reference/items/language/project.qdoc23
-rw-r--r--doc/reference/items/language/properties.qdoc21
-rw-r--r--doc/reference/items/language/propertyoptions.qdoc1
-rw-r--r--doc/reference/items/language/rule.qdoc1
-rw-r--r--doc/reference/items/language/scanner.qdoc1
-rw-r--r--doc/reference/items/language/subproject.qdoc15
-rw-r--r--doc/reference/items/probe/binary-probe.qdoc10
-rw-r--r--doc/reference/items/probe/conanfile-probe.qdoc257
-rw-r--r--doc/reference/items/probe/dmc-probe.qdoc111
-rw-r--r--doc/reference/items/probe/framework-probe.qdoc5
-rw-r--r--doc/reference/items/probe/iar-probe.qdoc8
-rw-r--r--doc/reference/items/probe/include-probe.qdoc6
-rw-r--r--doc/reference/items/probe/keil-probe.qdoc5
-rw-r--r--doc/reference/items/probe/library-probe.qdoc61
-rw-r--r--doc/reference/items/probe/path-probe.qdoc38
-rw-r--r--doc/reference/items/probe/pkgconfig-probe.qdoc20
-rw-r--r--doc/reference/items/probe/sdcc-probe.qdoc5
-rw-r--r--doc/reference/items/probe/watcom-probe.qdoc129
-rw-r--r--doc/reference/jsextensions/jsextension-binaryfile.qdoc1
-rw-r--r--doc/reference/jsextensions/jsextension-environment.qdoc1
-rw-r--r--doc/reference/jsextensions/jsextension-file.qdoc4
-rw-r--r--doc/reference/jsextensions/jsextension-fileinfo.qdoc23
-rw-r--r--doc/reference/jsextensions/jsextension-host.qdoc139
-rw-r--r--doc/reference/jsextensions/jsextension-process.qdoc1
-rw-r--r--doc/reference/jsextensions/jsextension-propertylist.qdoc1
-rw-r--r--doc/reference/jsextensions/jsextension-temporarydir.qdoc1
-rw-r--r--doc/reference/jsextensions/jsextension-textfile.qdoc1
-rw-r--r--doc/reference/jsextensions/jsextension-utilities.qdoc1
-rw-r--r--doc/reference/jsextensions/jsextension-xml.qdoc1
-rw-r--r--doc/reference/jsextensions/jsextensions-general.qdoc25
-rw-r--r--doc/reference/module-providers/qbspkgconfig-module-provider.qdoc133
-rw-r--r--doc/reference/module-providers/qt-module-provider.qdoc76
-rw-r--r--doc/reference/modules/android-ndk-module.qdoc12
-rw-r--r--doc/reference/modules/android-sdk-module.qdoc51
-rw-r--r--doc/reference/modules/archiver-module.qdoc1
-rw-r--r--doc/reference/modules/asan-module.qdoc77
-rw-r--r--doc/reference/modules/autotest-module.qdoc12
-rw-r--r--doc/reference/modules/bundle-module.qdoc24
-rw-r--r--doc/reference/modules/capnprotocpp-module.qdoc123
-rw-r--r--doc/reference/modules/codesign-module.qdoc395
-rw-r--r--doc/reference/modules/cpp-module.qdoc240
-rw-r--r--doc/reference/modules/cpufeatures-module.qdoc1
-rw-r--r--doc/reference/modules/dmg-module.qdoc1
-rw-r--r--doc/reference/modules/exporter-cmake.qdoc135
-rw-r--r--doc/reference/modules/exporter-pkgconfig-module.qdoc1
-rw-r--r--doc/reference/modules/exporter-qbs-module.qdoc8
-rw-r--r--doc/reference/modules/freedesktop-module.qdoc140
-rw-r--r--doc/reference/modules/ib-module.qdoc1
-rw-r--r--doc/reference/modules/ico-module.qdoc1
-rw-r--r--doc/reference/modules/innosetup-module.qdoc1
-rw-r--r--doc/reference/modules/java-module.qdoc1
-rw-r--r--doc/reference/modules/lexyacc-module.qdoc1
-rw-r--r--doc/reference/modules/nodejs-module.qdoc1
-rw-r--r--doc/reference/modules/nsis-module.qdoc1
-rw-r--r--doc/reference/modules/pkgconfig-module.qdoc1
-rw-r--r--doc/reference/modules/protobufcpp-module.qdoc51
-rw-r--r--doc/reference/modules/protobufnanopb-module.qdoc155
-rw-r--r--doc/reference/modules/protobufobjc-module.qdoc32
-rw-r--r--doc/reference/modules/qbs-module.qdoc471
-rw-r--r--doc/reference/modules/qnx-module.qdoc1
-rw-r--r--doc/reference/modules/qt-android_support-module.qdoc21
-rw-r--r--doc/reference/modules/qt-core-module.qdoc79
-rw-r--r--doc/reference/modules/qt-dbus-module.qdoc1
-rw-r--r--doc/reference/modules/qt-declarative-module.qdoc1
-rw-r--r--doc/reference/modules/qt-gui-module.qdoc1
-rw-r--r--doc/reference/modules/qt-modules.qdoc19
-rw-r--r--doc/reference/modules/qt-plugin_support-module.qdoc1
-rw-r--r--doc/reference/modules/qt-qml-module.qdoc72
-rw-r--r--doc/reference/modules/qt-quick-module.qdoc1
-rw-r--r--doc/reference/modules/qt-scxml-module.qdoc1
-rw-r--r--doc/reference/modules/texttemplate-module.qdoc1
-rw-r--r--doc/reference/modules/typescript-module.qdoc1
-rw-r--r--doc/reference/modules/vcs-module.qdoc1
-rw-r--r--doc/reference/modules/wix-module.qdoc1
-rw-r--r--doc/reference/modules/xcode-module.qdoc1
-rw-r--r--doc/reference/reference.qdoc15
-rw-r--r--doc/targets/qbs-target-android.qdoc1
-rw-r--r--doc/targets/qbs-target-integrity.qdoc1
-rw-r--r--doc/targets/qbs-target-ios.qdoc1
-rw-r--r--doc/targets/qbs-target-linux.qdoc1
-rw-r--r--doc/targets/qbs-target-macos.qdoc1
-rw-r--r--doc/targets/qbs-target-platforms.qdoc1
-rw-r--r--doc/targets/qbs-target-qnx.qdoc1
-rw-r--r--doc/targets/qbs-target-tvos.qdoc1
-rw-r--r--doc/targets/qbs-target-vxworks.qdoc1
-rw-r--r--doc/targets/qbs-target-watchos.qdoc1
-rw-r--r--doc/targets/qbs-target-windows.qdoc1
-rw-r--r--doc/tutorial.qdoc647
-rw-r--r--docker-compose.yml155
-rw-r--r--docker/bionic/Dockerfile191
-rw-r--r--docker/docker.qbs39
-rwxr-xr-xdocker/entrypoint.sh (renamed from docker/bionic/entrypoint.sh)0
-rw-r--r--docker/jammy/Dockerfile116
-rw-r--r--docker/jammy/test-android-no-qt.Dockerfile105
-rw-r--r--docker/jammy/test-android.Dockerfile137
-rw-r--r--docker/jammy/test-baremetal.Dockerfile50
-rw-r--r--docker/jammy/test-qt4.Dockerfile40
-rw-r--r--docker/jammy/test-qt6-static.Dockerfile221
-rw-r--r--docker/leap/Dockerfile96
-rwxr-xr-xdocker/leap/entrypoint.sh96
-rw-r--r--docker/stretch/Dockerfile73
-rwxr-xr-xdocker/stretch/entrypoint.sh69
-rw-r--r--docker/windowsservercore/Dockerfile83
-rw-r--r--docker/windowsservercore/build-qt.bat23
-rw-r--r--examples/app-and-lib/app-and-lib.qbs57
-rw-r--r--examples/app-and-lib/app/app.qbs6
-rw-r--r--examples/app-and-lib/app/main.cpp2
-rw-r--r--examples/app-and-lib/app_and_lib.qbs59
-rw-r--r--examples/app-and-lib/lib/lib.cpp2
-rw-r--r--examples/app-and-lib/lib/lib.qbs7
-rw-r--r--examples/baremetal/at90can128olimex/at90can128olimex.qbs2
-rw-r--r--examples/baremetal/at90can128olimex/redblink/README.md2
-rw-r--r--examples/baremetal/at90can128olimex/redblink/redblink.qbs2
-rw-r--r--examples/baremetal/baremetal.qbs8
-rw-r--r--examples/baremetal/cc2540usbdongle/cc2540usbdongle.qbs2
-rw-r--r--examples/baremetal/cc2540usbdongle/greenblink/greenblink.qbs2
-rw-r--r--examples/baremetal/cc2540usbdongle/greenblink/system.h3
-rw-r--r--examples/baremetal/cy7c68013a/cy7c68013a.qbs2
-rw-r--r--examples/baremetal/cy7c68013a/nes-gamepads/nes-gamepads.qbs2
-rw-r--r--examples/baremetal/esp8266/README.md34
-rw-r--r--examples/baremetal/esp8266/access-point/access-point.qbs28
-rw-r--r--examples/baremetal/esp8266/access-point/user_config.h62
-rw-r--r--examples/baremetal/esp8266/access-point/user_main.c105
-rw-r--r--examples/baremetal/esp8266/esp8266.qbs57
-rw-r--r--examples/baremetal/esp8266/qbs/imports/Esp8266SdkProbe.qbs25
-rw-r--r--examples/baremetal/esp8266/qbs/modules/esp8266_sdk/Esp8266SdkModule.qbs66
-rw-r--r--examples/baremetal/msp430f5529/msp430f5529.qbs2
-rw-r--r--examples/baremetal/msp430f5529/nes-gamepads/nes-gamepads.qbs2
-rw-r--r--examples/baremetal/msp430f5529/redblink/README.md2
-rw-r--r--examples/baremetal/msp430f5529/redblink/redblink.qbs2
-rw-r--r--examples/baremetal/pca10001/greenblink/README.md10
-rw-r--r--examples/baremetal/pca10001/greenblink/gcc/flash.ld150
-rw-r--r--examples/baremetal/pca10001/greenblink/gcc/startup.s173
-rw-r--r--examples/baremetal/pca10001/greenblink/gpio.c96
-rw-r--r--examples/baremetal/pca10001/greenblink/gpio.h65
-rw-r--r--examples/baremetal/pca10001/greenblink/greenblink.qbs170
-rw-r--r--examples/baremetal/pca10001/greenblink/iar/flash.icf73
-rw-r--r--examples/baremetal/pca10001/greenblink/iar/startup.s124
-rw-r--r--examples/baremetal/pca10001/greenblink/keil/flash.sct65
-rw-r--r--examples/baremetal/pca10001/greenblink/keil/startup.s134
-rw-r--r--examples/baremetal/pca10001/greenblink/main.c69
-rw-r--r--examples/baremetal/pca10001/greenblink/system.h82
-rw-r--r--examples/baremetal/pca10001/pca10001.qbs56
-rw-r--r--examples/baremetal/pca10040/greenblink/README.md10
-rw-r--r--examples/baremetal/pca10040/greenblink/gcc/flash.ld151
-rw-r--r--examples/baremetal/pca10040/greenblink/gcc/startup.s253
-rw-r--r--examples/baremetal/pca10040/greenblink/gpio.c96
-rw-r--r--examples/baremetal/pca10040/greenblink/gpio.h65
-rw-r--r--examples/baremetal/pca10040/greenblink/greenblink.qbs176
-rw-r--r--examples/baremetal/pca10040/greenblink/iar/flash.icf102
-rw-r--r--examples/baremetal/pca10040/greenblink/iar/startup.s202
-rw-r--r--examples/baremetal/pca10040/greenblink/keil/flash.sct65
-rw-r--r--examples/baremetal/pca10040/greenblink/keil/startup.s215
-rw-r--r--examples/baremetal/pca10040/greenblink/main.c69
-rw-r--r--examples/baremetal/pca10040/greenblink/system.h84
-rw-r--r--examples/baremetal/pca10040/pca10040.qbs56
-rw-r--r--examples/baremetal/stm32f103/greenblink/README.md10
-rw-r--r--examples/baremetal/stm32f103/greenblink/gcc/flash.ld172
-rw-r--r--examples/baremetal/stm32f103/greenblink/gcc/startup.s177
-rw-r--r--examples/baremetal/stm32f103/greenblink/gpio.c83
-rw-r--r--examples/baremetal/stm32f103/greenblink/gpio.h65
-rw-r--r--examples/baremetal/stm32f103/greenblink/greenblink.qbs172
-rw-r--r--examples/baremetal/stm32f103/greenblink/iar/flash.icf74
-rw-r--r--examples/baremetal/stm32f103/greenblink/iar/startup.s132
-rw-r--r--examples/baremetal/stm32f103/greenblink/keil/flash.sct65
-rw-r--r--examples/baremetal/stm32f103/greenblink/keil/startup.s145
-rw-r--r--examples/baremetal/stm32f103/greenblink/main.c69
-rw-r--r--examples/baremetal/stm32f103/greenblink/system.h102
-rw-r--r--examples/baremetal/stm32f103/stm32f103.qbs56
-rw-r--r--examples/baremetal/stm32f4discovery/blueblink/blueblink.qbs23
-rw-r--r--examples/baremetal/stm32f4discovery/stm32f4discovery.qbs2
-rw-r--r--examples/baremetal/stm8s103f3/redblink/redblink.qbs2
-rw-r--r--examples/baremetal/stm8s103f3/stm8s103f3.qbs2
-rw-r--r--examples/capnproto/addressbook_cpp/addressbook.capnp55
-rw-r--r--examples/capnproto/addressbook_cpp/addressbook.cpp288
-rw-r--r--examples/capnproto/addressbook_cpp/addressbook_cpp.qbs11
-rw-r--r--examples/capnproto/calculator_cpp/calculator-client.cpp367
-rw-r--r--examples/capnproto/calculator_cpp/calculator-server.cpp215
-rw-r--r--examples/capnproto/calculator_cpp/calculator.capnp118
-rw-r--r--examples/capnproto/calculator_cpp/calculator_cpp.qbs28
-rw-r--r--examples/cocoa-application/CocoaApplication.qbs66
l---------examples/cocoa-application/CocoaApplication/en_US.lproj1
-rw-r--r--examples/cocoa-application/CocoaApplication/en_US.lproj/Credits.rtf29
-rw-r--r--examples/cocoa-application/CocoaApplication/en_US.lproj/InfoPlist.strings1
-rw-r--r--examples/cocoa-application/CocoaApplication/en_US.lproj/LICENSE27
-rw-r--r--examples/cocoa-application/CocoaApplication/en_US.lproj/MainMenu.xib4666
-rw-r--r--examples/cocoa-application/app.qbs11
-rw-r--r--examples/cocoa-application/cocoa-application.qbs65
-rw-r--r--examples/cocoa-application/dmg.qbs3
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication.qbs107
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication.xcodeproj/project.pbxproj68
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.m24
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Info.plist4
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication/Default-568h@2x.pngbin18594 -> 0 bytes
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication/Default.pngbin6540 -> 0 bytes
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication/Default@2x.pngbin16107 -> 0 bytes
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.m11
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication/LaunchScreen.storyboard49
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.m13
-rw-r--r--examples/cocoa-touch-application/CocoaTouchApplication/main.m8
-rw-r--r--examples/cocoa-touch-application/cocoa-touch-application.qbs118
-rw-r--r--examples/code-generator/code-generator.qbs6
-rw-r--r--examples/code-generator/hwgen.cpp10
-rw-r--r--examples/collidingmice/collidingmice.qbs2
-rw-r--r--examples/collidingmice/main.cpp1
-rw-r--r--examples/collidingmice/mouse.cpp14
-rw-r--r--examples/collidingmice/mouse.h2
-rw-r--r--examples/compiled-qml/compiled-qml.qbs64
-rw-r--r--examples/compiled-qml/myapp.qbs66
-rw-r--r--examples/examples.qbs22
-rw-r--r--examples/exporters/exporters.qbs58
-rw-r--r--examples/exporters/lib_a/lib_a.cpp56
-rw-r--r--examples/exporters/lib_a/lib_a.h56
-rw-r--r--examples/exporters/lib_a/lib_a.qbs67
-rw-r--r--examples/exporters/lib_b/lib_b.cpp61
-rw-r--r--examples/exporters/lib_b/lib_b.h56
-rw-r--r--examples/exporters/lib_b/lib_b.qbs64
-rw-r--r--examples/exporters/qbs/imports/MyLibrary.qbs96
-rw-r--r--examples/grpc/client.cpp56
-rw-r--r--examples/grpc/grpc.qbs76
-rw-r--r--examples/grpc/ping-pong-grpc.qbs65
-rw-r--r--examples/grpc/server.cpp54
-rw-r--r--examples/helloworld-complex/hello.qbs89
-rw-r--r--examples/helloworld-complex/helloworld-complex.qbs87
-rw-r--r--examples/helloworld-complex/src/main.cpp8
-rw-r--r--examples/helloworld-minimal/hello.qbs56
-rw-r--r--examples/helloworld-minimal/helloworld-minimal.qbs54
-rw-r--r--examples/helloworld-qt/hello.qbs56
-rw-r--r--examples/helloworld-qt/helloworld-qt.qbs54
-rw-r--r--examples/helloworld-qt/main.cpp4
-rw-r--r--examples/install-bundle/install-bundle.qbs10
-rw-r--r--examples/pkgconfig-provider/main.c137
-rw-r--r--examples/pkgconfig-provider/pkgconfig-provider.qbs60
-rw-r--r--examples/protobuf/addressbook_cpp/README.md (renamed from examples/protobuf/cpp/README.md)0
-rw-r--r--examples/protobuf/addressbook_cpp/addressbook_cpp.qbs18
-rw-r--r--examples/protobuf/addressbook_cpp/main.cpp179
-rw-r--r--examples/protobuf/addressbook_objc/README.md (renamed from examples/protobuf/objc/README.md)0
-rw-r--r--examples/protobuf/addressbook_objc/addressbook_objc.qbs13
-rw-r--r--examples/protobuf/addressbook_objc/main.m183
-rw-r--r--examples/protobuf/cpp/addressbook.qbs19
-rw-r--r--examples/protobuf/cpp/main.cpp171
-rw-r--r--examples/protobuf/objc/addressbook.qbs15
-rw-r--r--examples/protobuf/objc/main.m171
-rw-r--r--examples/rpaths/main.cpp68
-rw-r--r--examples/rpaths/objecta.cpp71
-rw-r--r--examples/rpaths/objecta.h70
-rw-r--r--examples/rpaths/objectb.cpp64
-rw-r--r--examples/rpaths/objectb.h71
-rw-r--r--examples/rpaths/rpaths.qbs61
-rw-r--r--examples/rule/rule.qbs2
-rw-r--r--qbs-resources/imports/QbsApp.qbs18
-rw-r--r--qbs-resources/imports/QbsAutotest.qbs18
-rw-r--r--qbs-resources/imports/QbsLibrary.qbs39
-rw-r--r--qbs-resources/imports/QbsProduct.qbs12
-rw-r--r--qbs-resources/imports/QbsStaticLibrary.qbs9
-rw-r--r--qbs-resources/imports/QbsUnittest.qbs10
-rw-r--r--qbs-resources/modules/docker/docker.qbs50
-rw-r--r--qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs30
-rw-r--r--qbs-resources/modules/qbsversion/qbsversion.qbs1
-rw-r--r--qbs.pro71
-rw-r--r--qbs.qbs34
-rw-r--r--qbs_version.pri4
-rw-r--r--scripts/address-sanitizer-suppressions.txt3
-rwxr-xr-xscripts/build-qbs-doc.sh59
-rwxr-xr-xscripts/build-qbs-with-cmake.sh105
-rwxr-xr-xscripts/build-qbs-with-qbs.sh50
-rwxr-xr-xscripts/build-qbs-with-qmake.sh84
-rwxr-xr-xscripts/cpu-count.sh50
-rwxr-xr-xscripts/git-clang-format683
-rw-r--r--scripts/install-dm.sh135
-rwxr-xr-xscripts/install-ow.sh129
-rwxr-xr-xscripts/install-qt.sh150
-rw-r--r--scripts/make-release-archives.bat3
-rwxr-xr-xscripts/run-analyzer.sh31
-rw-r--r--scripts/scripts.qbs2
-rwxr-xr-xscripts/test-baremetal.sh54
-rwxr-xr-xscripts/test-for-android.sh66
-rwxr-xr-xscripts/test-qbs.sh53
-rwxr-xr-xscripts/test-qt-for-android.sh86
-rwxr-xr-xscripts/test-qt.sh53
-rwxr-xr-xscripts/test-qt4.sh58
-rw-r--r--share/CMakeLists.txt99
-rw-r--r--share/qbs/imports/qbs/DarwinTools/darwin-tools.js99
-rw-r--r--share/qbs/imports/qbs/ModUtils/utils.js51
-rw-r--r--share/qbs/imports/qbs/PathTools/path-tools.js2
-rw-r--r--share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs14
-rw-r--r--share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs29
-rw-r--r--share/qbs/imports/qbs/Probes/BinaryProbe.qbs6
-rw-r--r--share/qbs/imports/qbs/Probes/ClBinaryProbe.qbs71
-rw-r--r--share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs73
-rw-r--r--share/qbs/imports/qbs/Probes/ClangClProbe.qbs28
-rw-r--r--share/qbs/imports/qbs/Probes/ConanfileProbe.qbs145
-rw-r--r--share/qbs/imports/qbs/Probes/CosmicProbe.qbs87
-rw-r--r--share/qbs/imports/qbs/Probes/DmcProbe.qbs97
-rw-r--r--share/qbs/imports/qbs/Probes/FrameworkProbe.qbs7
-rw-r--r--share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs41
-rw-r--r--share/qbs/imports/qbs/Probes/GccProbe.qbs11
-rw-r--r--share/qbs/imports/qbs/Probes/GccVersionProbe.qbs4
-rw-r--r--share/qbs/imports/qbs/Probes/IarProbe.qbs31
-rw-r--r--share/qbs/imports/qbs/Probes/IncludeProbe.qbs2
-rw-r--r--share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs27
-rw-r--r--share/qbs/imports/qbs/Probes/JdkProbe.qbs7
-rw-r--r--share/qbs/imports/qbs/Probes/KeilProbe.qbs27
-rw-r--r--share/qbs/imports/qbs/Probes/LibraryProbe.qbs35
-rw-r--r--share/qbs/imports/qbs/Probes/MsvcProbe.qbs5
-rw-r--r--share/qbs/imports/qbs/Probes/NodeJsProbe.qbs3
-rw-r--r--share/qbs/imports/qbs/Probes/NpmProbe.qbs35
-rw-r--r--share/qbs/imports/qbs/Probes/PathProbe.qbs31
-rw-r--r--share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs19
-rw-r--r--share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs64
-rw-r--r--share/qbs/imports/qbs/Probes/QmakeProbe.qbs41
-rw-r--r--share/qbs/imports/qbs/Probes/SdccProbe.qbs44
-rw-r--r--share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs31
-rw-r--r--share/qbs/imports/qbs/Probes/WatcomProbe.qbs98
-rw-r--r--share/qbs/imports/qbs/Probes/XcodeLocationProbe.qbs47
-rw-r--r--share/qbs/imports/qbs/Probes/XcodeProbe.qbs5
-rw-r--r--share/qbs/imports/qbs/Probes/path-probe.js36
-rw-r--r--share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js118
-rw-r--r--share/qbs/imports/qbs/Probes/qmake-probe.js1256
-rw-r--r--share/qbs/imports/qbs/ProviderUtils/provider-utils.js128
-rw-r--r--share/qbs/imports/qbs/base/AndroidApk.qbs2
-rw-r--r--share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs5
-rw-r--r--share/qbs/imports/qbs/base/Application.qbs14
-rw-r--r--share/qbs/imports/qbs/base/ApplicationExtension.qbs13
-rw-r--r--share/qbs/imports/qbs/base/AutotestRunner.qbs2
-rw-r--r--share/qbs/imports/qbs/base/DynamicLibrary.qbs20
-rw-r--r--share/qbs/imports/qbs/base/InnoSetup.qbs3
-rw-r--r--share/qbs/imports/qbs/base/Library.qbs57
-rw-r--r--share/qbs/imports/qbs/base/NativeBinary.qbs6
-rw-r--r--share/qbs/imports/qbs/base/QtGuiApplication.qbs2
-rw-r--r--share/qbs/imports/qbs/base/QtModule.qbs87
-rw-r--r--share/qbs/imports/qbs/base/QtPlugin.qbs51
-rw-r--r--share/qbs/imports/qbs/base/StaticLibrary.qbs9
-rw-r--r--share/qbs/module-providers/Qt/provider.qbs10
-rw-r--r--share/qbs/module-providers/Qt/setup-qt.js1462
-rw-r--r--share/qbs/module-providers/Qt/templates/QtModule.qbs86
-rw-r--r--share/qbs/module-providers/Qt/templates/QtPlugin.qbs51
-rw-r--r--share/qbs/module-providers/Qt/templates/android_support.qbs385
-rw-r--r--share/qbs/module-providers/Qt/templates/core.qbs112
-rw-r--r--share/qbs/module-providers/Qt/templates/dbus.js1
-rw-r--r--share/qbs/module-providers/Qt/templates/dbus.qbs11
-rw-r--r--share/qbs/module-providers/Qt/templates/gui.qbs13
-rw-r--r--share/qbs/module-providers/Qt/templates/moc.js25
-rw-r--r--share/qbs/module-providers/Qt/templates/module.qbs4
-rw-r--r--share/qbs/module-providers/Qt/templates/plugin.qbs2
-rw-r--r--share/qbs/module-providers/Qt/templates/plugin_support.qbs2
-rw-r--r--share/qbs/module-providers/Qt/templates/qml.js56
-rw-r--r--share/qbs/module-providers/Qt/templates/qml.qbs100
-rw-r--r--share/qbs/module-providers/Qt/templates/qmlcache.qbs4
-rw-r--r--share/qbs/module-providers/Qt/templates/quick.js14
-rw-r--r--share/qbs/module-providers/Qt/templates/quick.qbs22
-rw-r--r--share/qbs/module-providers/Qt/templates/rcc.js8
-rw-r--r--share/qbs/module-providers/Qt/templates/scxml.qbs8
-rw-r--r--share/qbs/module-providers/__fallback/fallback.qbs3
-rw-r--r--share/qbs/module-providers/qbspkgconfig.qbs226
-rw-r--r--share/qbs/modules/Android/ndk/ndk.qbs19
-rw-r--r--share/qbs/modules/Android/ndk/utils.js24
-rw-r--r--share/qbs/modules/Android/sdk/sdk.qbs316
-rw-r--r--share/qbs/modules/Android/sdk/utils.js311
-rw-r--r--share/qbs/modules/Exporter/cmake/cmakeexporter.js239
-rw-r--r--share/qbs/modules/Exporter/cmake/cmakeexporter.qbs84
-rw-r--r--share/qbs/modules/Exporter/exporter.js66
-rw-r--r--share/qbs/modules/Exporter/pkgconfig/pkgconfig.js32
-rw-r--r--share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs6
-rw-r--r--share/qbs/modules/Exporter/qbs/qbsexporter.js52
-rw-r--r--share/qbs/modules/Exporter/qbs/qbsexporter.qbs3
-rw-r--r--share/qbs/modules/Sanitizers/address/asan.qbs76
-rw-r--r--share/qbs/modules/archiver/archiver.qbs9
-rw-r--r--share/qbs/modules/bundle/BundleModule.qbs237
-rw-r--r--share/qbs/modules/bundle/MacOSX-Package-Types.xcspec943
-rw-r--r--share/qbs/modules/bundle/MacOSX-Product-Types.xcspec1345
-rw-r--r--share/qbs/modules/bundle/bundle.js96
-rw-r--r--share/qbs/modules/capnproto/capnproto.js97
-rw-r--r--share/qbs/modules/capnproto/capnprotobase.qbs64
-rw-r--r--share/qbs/modules/capnproto/cpp/capnprotocpp.qbs65
-rw-r--r--share/qbs/modules/cli/CLIModule.qbs21
-rw-r--r--share/qbs/modules/cli/cli.js10
-rw-r--r--share/qbs/modules/cli/mono.qbs7
-rw-r--r--share/qbs/modules/cli/windows-dotnet.qbs2
-rw-r--r--share/qbs/modules/codesign/CodeSignModule.qbs52
-rw-r--r--share/qbs/modules/codesign/android.qbs116
-rw-r--r--share/qbs/modules/codesign/apple.qbs387
-rw-r--r--share/qbs/modules/codesign/codesign.js493
-rw-r--r--share/qbs/modules/codesign/noop.qbs35
-rw-r--r--share/qbs/modules/codesign/signtool.qbs103
-rw-r--r--share/qbs/modules/cpp/CppModule.qbs59
-rw-r--r--share/qbs/modules/cpp/DarwinGCC.qbs52
-rw-r--r--share/qbs/modules/cpp/GenericGCC.qbs236
-rw-r--r--share/qbs/modules/cpp/LinuxGCC.qbs7
-rw-r--r--share/qbs/modules/cpp/MingwBaseModule.qbs9
-rw-r--r--share/qbs/modules/cpp/UnixGCC.qbs6
-rw-r--r--share/qbs/modules/cpp/android-gcc.qbs110
-rw-r--r--share/qbs/modules/cpp/cosmic.js443
-rw-r--r--share/qbs/modules/cpp/cosmic.qbs147
-rw-r--r--share/qbs/modules/cpp/cpp.js431
-rw-r--r--share/qbs/modules/cpp/darwin.js33
-rw-r--r--share/qbs/modules/cpp/dmc.js506
-rw-r--r--share/qbs/modules/cpp/dmc.qbs194
-rw-r--r--share/qbs/modules/cpp/freebsd-gcc.qbs7
-rw-r--r--share/qbs/modules/cpp/gcc.js408
-rw-r--r--share/qbs/modules/cpp/iar.js892
-rw-r--r--share/qbs/modules/cpp/iar.qbs64
-rw-r--r--share/qbs/modules/cpp/ios-gcc.qbs21
-rw-r--r--share/qbs/modules/cpp/keil.js1137
-rw-r--r--share/qbs/modules/cpp/keil.qbs64
-rw-r--r--share/qbs/modules/cpp/macos-gcc.qbs4
-rw-r--r--share/qbs/modules/cpp/msvc.js384
-rw-r--r--share/qbs/modules/cpp/qnx-qcc.qbs6
-rw-r--r--share/qbs/modules/cpp/sdcc.js562
-rw-r--r--share/qbs/modules/cpp/sdcc.qbs66
-rw-r--r--share/qbs/modules/cpp/setuprunenv.js27
-rw-r--r--share/qbs/modules/cpp/tvos-gcc.qbs8
-rw-r--r--share/qbs/modules/cpp/watchos-gcc.qbs8
-rw-r--r--share/qbs/modules/cpp/watcom.js568
-rw-r--r--share/qbs/modules/cpp/watcom.qbs196
-rw-r--r--share/qbs/modules/cpp/windows-clang-cl.qbs33
-rw-r--r--share/qbs/modules/cpp/windows-clang-mingw.qbs18
-rw-r--r--share/qbs/modules/cpp/windows-mingw.qbs18
-rw-r--r--share/qbs/modules/cpp/windows-msvc-base.qbs140
-rw-r--r--share/qbs/modules/cpp/windows-msvc.qbs13
-rw-r--r--share/qbs/modules/dmg/DMGModule.qbs10
-rw-r--r--share/qbs/modules/dmg/dmg.js27
-rw-r--r--share/qbs/modules/freedesktop/FreeDesktop.qbs122
-rw-r--r--share/qbs/modules/freedesktop/freedesktop.js69
-rw-r--r--share/qbs/modules/ib/IBModule.qbs5
-rw-r--r--share/qbs/modules/ib/ib.js18
-rw-r--r--share/qbs/modules/ico/ico.js7
-rw-r--r--share/qbs/modules/innosetup/InnoSetupModule.qbs6
-rw-r--r--share/qbs/modules/java/JavaModule.qbs19
-rw-r--r--share/qbs/modules/java/utils.js59
-rw-r--r--share/qbs/modules/nodejs/NodeJS.qbs7
-rw-r--r--share/qbs/modules/nsis/NSISModule.qbs15
-rw-r--r--share/qbs/modules/pkgconfig/pkgconfig.qbs3
-rw-r--r--share/qbs/modules/protobuf/cpp/protobufcpp.qbs109
-rw-r--r--share/qbs/modules/protobuf/nanopb/nanopb.qbs104
-rw-r--r--share/qbs/modules/protobuf/objc/protobufobjc.qbs58
-rw-r--r--share/qbs/modules/protobuf/protobuf.js74
-rw-r--r--share/qbs/modules/protobuf/protobufbase.qbs24
-rw-r--r--share/qbs/modules/qbs/common.qbs52
-rw-r--r--share/qbs/modules/qnx/qnx.qbs11
-rw-r--r--share/qbs/modules/typescript/TypeScriptModule.qbs2
-rw-r--r--share/qbs/modules/typescript/typescript.js6
-rw-r--r--share/qbs/modules/wix/WiXModule.qbs27
-rw-r--r--share/qbs/modules/xcode/xcode.js80
-rw-r--r--share/qbs/modules/xcode/xcode.qbs292
-rw-r--r--share/share.qbs18
-rwxr-xr-xsrc/3rdparty/python/bin/dmgbuild41
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/biplist/LICENSE25
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/biplist/__init__.py977
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/biplist/qt_attribution.json13
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/__init__.py3
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/badge.py143
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/colors.py494
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/core.py597
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/licensing.py461
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/resources.py355
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/ds_store/__init__.py3
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/ds_store/buddy.py478
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/ds_store/store.py1251
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/mac_alias/__init__.py27
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/mac_alias/alias.py607
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/mac_alias/bookmark.py665
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/mac_alias/osx.py827
-rw-r--r--src/3rdparty/python/lib/python2.7/site-packages/mac_alias/utils.py18
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/LICENSE (renamed from src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/LICENSE)0
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__init__.py5
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__main__.py58
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/badge.py169
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/colors.py504
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/core.py943
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/licensing.py329
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/qt_attribution.json (renamed from src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/qt_attribution.json)0
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/ds_store/LICENSE (renamed from src/3rdparty/python/lib/python2.7/site-packages/ds_store/LICENSE)0
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/ds_store/__init__.py5
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/ds_store/__main__.py102
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/ds_store/buddy.py486
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/ds_store/qt_attribution.json (renamed from src/3rdparty/python/lib/python2.7/site-packages/ds_store/qt_attribution.json)0
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/ds_store/store.py1262
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/mac_alias/LICENSE (renamed from src/3rdparty/python/lib/python2.7/site-packages/mac_alias/LICENSE)0
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/mac_alias/__init__.py47
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py772
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/mac_alias/bookmark.py677
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/mac_alias/osx.py1074
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/mac_alias/qt_attribution.json (renamed from src/3rdparty/python/lib/python2.7/site-packages/mac_alias/qt_attribution.json)0
-rw-r--r--src/3rdparty/python/lib/python3.9/site-packages/mac_alias/utils.py20
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/app/CMakeLists.txt8
-rw-r--r--src/app/app.pri23
-rw-r--r--src/app/app.pro10
-rw-r--r--src/app/apps.qbs3
-rw-r--r--src/app/config-ui/CMakeLists.txt19
-rw-r--r--src/app/config-ui/config-ui.pro26
-rw-r--r--src/app/config-ui/config-ui.qbs3
-rw-r--r--src/app/config-ui/main.cpp2
-rw-r--r--src/app/config-ui/mainwindow.cpp11
-rw-r--r--src/app/config/CMakeLists.txt13
-rw-r--r--src/app/config/config.pro13
-rw-r--r--src/app/config/config.qbs2
-rw-r--r--src/app/config/configcommand.h2
-rw-r--r--src/app/config/configcommandexecutor.cpp106
-rw-r--r--src/app/config/configcommandlineparser.cpp33
-rw-r--r--src/app/config/configcommandlineparser.h2
-rw-r--r--src/app/qbs-create-project/CMakeLists.txt10
-rw-r--r--src/app/qbs-create-project/create-project-main.cpp5
-rw-r--r--src/app/qbs-create-project/createproject.cpp29
-rw-r--r--src/app/qbs-create-project/createproject.h6
-rw-r--r--src/app/qbs-create-project/qbs-create-project.pro9
-rw-r--r--src/app/qbs-create-project/qbs-create-project.qbs2
-rw-r--r--src/app/qbs-setup-android/CMakeLists.txt12
-rw-r--r--src/app/qbs-setup-android/android-setup.cpp40
-rw-r--r--src/app/qbs-setup-android/commandlineparser.cpp7
-rw-r--r--src/app/qbs-setup-android/commandlineparser.h4
-rw-r--r--src/app/qbs-setup-android/main.cpp1
-rw-r--r--src/app/qbs-setup-android/qbs-setup-android.pro12
-rw-r--r--src/app/qbs-setup-android/qbs-setup-android.qbs9
-rw-r--r--src/app/qbs-setup-qt/CMakeLists.txt12
-rw-r--r--src/app/qbs-setup-qt/main.cpp3
-rw-r--r--src/app/qbs-setup-qt/qbs-setup-qt.pro16
-rw-r--r--src/app/qbs-setup-qt/qbs-setup-qt.qbs9
-rw-r--r--src/app/qbs-setup-qt/setupqt.cpp30
-rw-r--r--src/app/qbs-setup-toolchains/CMakeLists.txt32
-rw-r--r--src/app/qbs-setup-toolchains/clangclprobe.cpp127
-rw-r--r--src/app/qbs-setup-toolchains/cosmicprobe.cpp179
-rw-r--r--src/app/qbs-setup-toolchains/cosmicprobe.h61
-rw-r--r--src/app/qbs-setup-toolchains/dmcprobe.cpp264
-rw-r--r--src/app/qbs-setup-toolchains/dmcprobe.h61
-rw-r--r--src/app/qbs-setup-toolchains/gccprobe.cpp99
-rw-r--r--src/app/qbs-setup-toolchains/gccprobe.h2
-rw-r--r--src/app/qbs-setup-toolchains/iarewprobe.cpp94
-rw-r--r--src/app/qbs-setup-toolchains/keilprobe.cpp341
-rw-r--r--src/app/qbs-setup-toolchains/main.cpp1
-rw-r--r--src/app/qbs-setup-toolchains/msvcprobe.cpp310
-rw-r--r--src/app/qbs-setup-toolchains/msvcprobe.h10
-rw-r--r--src/app/qbs-setup-toolchains/probe.cpp82
-rw-r--r--src/app/qbs-setup-toolchains/probe.h13
-rw-r--r--src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro30
-rw-r--r--src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs15
-rw-r--r--src/app/qbs-setup-toolchains/sdccprobe.cpp105
-rw-r--r--src/app/qbs-setup-toolchains/sdccprobe.h2
-rw-r--r--src/app/qbs-setup-toolchains/watcomprobe.cpp255
-rw-r--r--src/app/qbs-setup-toolchains/watcomprobe.h59
-rw-r--r--src/app/qbs-setup-toolchains/xcodeprobe.cpp26
-rw-r--r--src/app/qbs/CMakeLists.txt56
-rw-r--r--src/app/qbs/commandlinefrontend.cpp45
-rw-r--r--src/app/qbs/lspserver.cpp629
-rw-r--r--src/app/qbs/lspserver.h65
-rw-r--r--src/app/qbs/parser/commandlineoption.cpp44
-rw-r--r--src/app/qbs/parser/commandlineoption.h33
-rw-r--r--src/app/qbs/parser/commandlineoptionpool.cpp16
-rw-r--r--src/app/qbs/parser/commandlineoptionpool.h2
-rw-r--r--src/app/qbs/parser/commandlineparser.cpp76
-rw-r--r--src/app/qbs/parser/commandlineparser.h9
-rw-r--r--src/app/qbs/parser/parser.pri16
-rw-r--r--src/app/qbs/parser/parsercommand.cpp25
-rw-r--r--src/app/qbs/qbs.pro54
-rw-r--r--src/app/qbs/qbs.qbs15
-rw-r--r--src/app/qbs/session.cpp37
-rw-r--r--src/app/qbs/sessionpacket.cpp9
-rw-r--r--src/app/qbs/sessionpacket.h2
-rw-r--r--src/app/qbs/sessionpacketreader.cpp9
-rw-r--r--src/app/qbs/status.cpp58
-rw-r--r--src/app/qbs/stdinreader.cpp206
-rw-r--r--src/app/shared/CMakeLists.txt1
-rw-r--r--src/app/shared/logging/CMakeLists.txt12
-rw-r--r--src/app/shared/logging/coloredoutput.cpp16
-rw-r--r--src/app/shared/logging/coloredoutput.h4
-rw-r--r--src/app/shared/logging/consolelogger.cpp8
-rw-r--r--src/app/shared/logging/logging.pri2
-rw-r--r--src/app/shared/logging/logging.qbs14
-rw-r--r--src/app/shared/shared.qbs5
-rw-r--r--src/install_prefix.pri1
-rw-r--r--src/lib/CMakeLists.txt3
-rw-r--r--src/lib/bundledlibs.pri3
-rw-r--r--src/lib/corelib/CMakeLists.txt493
-rw-r--r--src/lib/corelib/api/api.pri52
-rw-r--r--src/lib/corelib/api/changeset.cpp35
-rw-r--r--src/lib/corelib/api/changeset.h6
-rw-r--r--src/lib/corelib/api/internaljobs.cpp33
-rw-r--r--src/lib/corelib/api/internaljobs.h16
-rw-r--r--src/lib/corelib/api/jobs.cpp13
-rw-r--r--src/lib/corelib/api/jobs.h7
-rw-r--r--src/lib/corelib/api/languageinfo.cpp22
-rw-r--r--src/lib/corelib/api/project.cpp328
-rw-r--r--src/lib/corelib/api/project.h8
-rw-r--r--src/lib/corelib/api/project_p.h23
-rw-r--r--src/lib/corelib/api/projectdata.cpp153
-rw-r--r--src/lib/corelib/api/projectdata.h52
-rw-r--r--src/lib/corelib/api/projectdata_p.h4
-rw-r--r--src/lib/corelib/api/projectfileupdater.cpp58
-rw-r--r--src/lib/corelib/api/projectfileupdater.h15
-rw-r--r--src/lib/corelib/api/qmljsrewriter.cpp68
-rw-r--r--src/lib/corelib/api/qmljsrewriter.h6
-rw-r--r--src/lib/corelib/api/rulecommand.h3
-rw-r--r--src/lib/corelib/api/runenvironment.cpp38
-rw-r--r--src/lib/corelib/api/runenvironment.h11
-rw-r--r--src/lib/corelib/buildgraph/artifact.cpp19
-rw-r--r--src/lib/corelib/buildgraph/artifact.h6
-rw-r--r--src/lib/corelib/buildgraph/artifactcleaner.cpp4
-rw-r--r--src/lib/corelib/buildgraph/artifactcleaner.h2
-rw-r--r--src/lib/corelib/buildgraph/artifactsscriptvalue.cpp257
-rw-r--r--src/lib/corelib/buildgraph/artifactsscriptvalue.h13
-rw-r--r--src/lib/corelib/buildgraph/artifactvisitor.cpp4
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.cpp554
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.h13
-rw-r--r--src/lib/corelib/buildgraph/buildgraph.pri89
-rw-r--r--src/lib/corelib/buildgraph/buildgraphloader.cpp209
-rw-r--r--src/lib/corelib/buildgraph/buildgraphloader.h7
-rw-r--r--src/lib/corelib/buildgraph/buildgraphnode.cpp6
-rw-r--r--src/lib/corelib/buildgraph/cycledetector.cpp2
-rw-r--r--src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp28
-rw-r--r--src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h7
-rw-r--r--src/lib/corelib/buildgraph/depscanner.cpp56
-rw-r--r--src/lib/corelib/buildgraph/depscanner.h7
-rw-r--r--src/lib/corelib/buildgraph/environmentscriptrunner.cpp58
-rw-r--r--src/lib/corelib/buildgraph/executor.cpp111
-rw-r--r--src/lib/corelib/buildgraph/executor.h5
-rw-r--r--src/lib/corelib/buildgraph/executorjob.h2
-rw-r--r--src/lib/corelib/buildgraph/filedependency.h4
-rw-r--r--src/lib/corelib/buildgraph/inputartifactscanner.cpp91
-rw-r--r--src/lib/corelib/buildgraph/jscommandexecutor.cpp78
-rw-r--r--src/lib/corelib/buildgraph/nodeset.h8
-rw-r--r--src/lib/corelib/buildgraph/nodetreedumper.cpp6
-rw-r--r--src/lib/corelib/buildgraph/nodetreedumper.h4
-rw-r--r--src/lib/corelib/buildgraph/processcommandexecutor.cpp62
-rw-r--r--src/lib/corelib/buildgraph/productbuilddata.cpp2
-rw-r--r--src/lib/corelib/buildgraph/productbuilddata.h3
-rw-r--r--src/lib/corelib/buildgraph/productinstaller.cpp8
-rw-r--r--src/lib/corelib/buildgraph/productinstaller.h4
-rw-r--r--src/lib/corelib/buildgraph/projectbuilddata.cpp17
-rw-r--r--src/lib/corelib/buildgraph/projectbuilddata.h6
-rw-r--r--src/lib/corelib/buildgraph/qtmocscanner.cpp59
-rw-r--r--src/lib/corelib/buildgraph/qtmocscanner.h14
-rw-r--r--src/lib/corelib/buildgraph/requesteddependencies.cpp6
-rw-r--r--src/lib/corelib/buildgraph/rescuableartifactdata.h9
-rw-r--r--src/lib/corelib/buildgraph/rulecommands.cpp339
-rw-r--r--src/lib/corelib/buildgraph/rulecommands.h29
-rw-r--r--src/lib/corelib/buildgraph/rulegraph.cpp22
-rw-r--r--src/lib/corelib/buildgraph/rulenode.cpp6
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.cpp258
-rw-r--r--src/lib/corelib/buildgraph/rulesapplicator.h16
-rw-r--r--src/lib/corelib/buildgraph/rulesevaluationcontext.cpp24
-rw-r--r--src/lib/corelib/buildgraph/rulesevaluationcontext.h17
-rw-r--r--src/lib/corelib/buildgraph/scriptclasspropertyiterator.h110
-rw-r--r--src/lib/corelib/buildgraph/timestampsupdater.cpp2
-rw-r--r--src/lib/corelib/buildgraph/timestampsupdater.h2
-rw-r--r--src/lib/corelib/buildgraph/transformer.cpp235
-rw-r--r--src/lib/corelib/buildgraph/transformer.h26
-rw-r--r--src/lib/corelib/buildgraph/transformerchangetracking.cpp26
-rw-r--r--src/lib/corelib/corelib.pro42
-rw-r--r--src/lib/corelib/corelib.qbs136
-rw-r--r--src/lib/corelib/generators/generator.cpp22
-rw-r--r--src/lib/corelib/generators/generator.h4
-rw-r--r--src/lib/corelib/generators/generatordata.cpp2
-rw-r--r--src/lib/corelib/generators/generators.pri38
-rw-r--r--src/lib/corelib/generators/generatorutils.cpp25
-rw-r--r--src/lib/corelib/generators/generatorutils.h15
-rw-r--r--src/lib/corelib/generators/generatorversioninfo.cpp32
-rw-r--r--src/lib/corelib/generators/generatorversioninfo.h29
-rw-r--r--src/lib/corelib/generators/xmlpropertygroup.cpp3
-rw-r--r--src/lib/corelib/generators/xmlpropertygroup.h2
-rw-r--r--src/lib/corelib/jsextensions/binaryfile.cpp245
-rw-r--r--src/lib/corelib/jsextensions/domxml.cpp741
-rw-r--r--src/lib/corelib/jsextensions/environmentextension.cpp130
-rw-r--r--src/lib/corelib/jsextensions/file.cpp337
-rw-r--r--src/lib/corelib/jsextensions/fileinfoextension.cpp410
-rw-r--r--src/lib/corelib/jsextensions/host.cpp151
-rw-r--r--src/lib/corelib/jsextensions/jsextension.h339
-rw-r--r--src/lib/corelib/jsextensions/jsextensions.cpp23
-rw-r--r--src/lib/corelib/jsextensions/jsextensions.h14
-rw-r--r--src/lib/corelib/jsextensions/jsextensions.pri26
-rw-r--r--src/lib/corelib/jsextensions/moduleproperties.cpp397
-rw-r--r--src/lib/corelib/jsextensions/moduleproperties.h25
-rw-r--r--src/lib/corelib/jsextensions/pkgconfigjs.cpp263
-rw-r--r--src/lib/corelib/jsextensions/pkgconfigjs.h103
-rw-r--r--src/lib/corelib/jsextensions/process.cpp352
-rw-r--r--src/lib/corelib/jsextensions/propertylist.cpp10
-rw-r--r--src/lib/corelib/jsextensions/propertylist.h115
-rw-r--r--src/lib/corelib/jsextensions/propertylist.mm373
-rw-r--r--src/lib/corelib/jsextensions/propertylist_darwin.mm328
-rw-r--r--src/lib/corelib/jsextensions/propertylistutils.mm28
-rw-r--r--src/lib/corelib/jsextensions/temporarydir.cpp85
-rw-r--r--src/lib/corelib/jsextensions/textfile.cpp248
-rw-r--r--src/lib/corelib/jsextensions/utilitiesextension.cpp705
-rw-r--r--src/lib/corelib/language/artifactproperties.cpp1
-rw-r--r--src/lib/corelib/language/astimportshandler.cpp305
-rw-r--r--src/lib/corelib/language/astimportshandler.h94
-rw-r--r--src/lib/corelib/language/astpropertiesitemhandler.cpp192
-rw-r--r--src/lib/corelib/language/astpropertiesitemhandler.h63
-rw-r--r--src/lib/corelib/language/asttools.cpp6
-rw-r--r--src/lib/corelib/language/asttools.h2
-rw-r--r--src/lib/corelib/language/builtindeclarations.cpp59
-rw-r--r--src/lib/corelib/language/builtindeclarations.h2
-rw-r--r--src/lib/corelib/language/deprecationinfo.h50
-rw-r--r--src/lib/corelib/language/evaluationdata.h66
-rw-r--r--src/lib/corelib/language/evaluator.cpp913
-rw-r--r--src/lib/corelib/language/evaluator.h76
-rw-r--r--src/lib/corelib/language/evaluatorscriptclass.cpp776
-rwxr-xr-xsrc/lib/corelib/language/evaluatorscriptclass.h133
-rw-r--r--src/lib/corelib/language/forward_decls.h4
-rw-r--r--src/lib/corelib/language/item.cpp259
-rw-r--r--src/lib/corelib/language/item.h131
-rw-r--r--src/lib/corelib/language/itemdeclaration.cpp6
-rw-r--r--src/lib/corelib/language/itemdeclaration.h3
-rw-r--r--src/lib/corelib/language/itempool.cpp2
-rw-r--r--src/lib/corelib/language/itemreader.cpp140
-rw-r--r--src/lib/corelib/language/itemreader.h99
-rw-r--r--src/lib/corelib/language/itemreaderastvisitor.cpp405
-rw-r--r--src/lib/corelib/language/itemreaderastvisitor.h97
-rw-r--r--src/lib/corelib/language/itemreadervisitorstate.cpp198
-rw-r--r--src/lib/corelib/language/itemreadervisitorstate.h81
-rw-r--r--src/lib/corelib/language/itemtype.h3
-rw-r--r--src/lib/corelib/language/jsimports.h2
-rw-r--r--src/lib/corelib/language/language.cpp227
-rw-r--r--src/lib/corelib/language/language.h99
-rw-r--r--src/lib/corelib/language/language.pri82
-rw-r--r--src/lib/corelib/language/loader.cpp222
-rw-r--r--src/lib/corelib/language/loader.h88
-rw-r--r--src/lib/corelib/language/moduleloader.cpp4142
-rw-r--r--src/lib/corelib/language/moduleloader.h486
-rw-r--r--src/lib/corelib/language/modulemerger.cpp292
-rw-r--r--src/lib/corelib/language/modulemerger.h88
-rw-r--r--src/lib/corelib/language/moduleproviderinfo.h36
-rw-r--r--src/lib/corelib/language/preparescriptobserver.cpp22
-rw-r--r--src/lib/corelib/language/preparescriptobserver.h11
-rw-r--r--src/lib/corelib/language/projectresolver.cpp1875
-rw-r--r--src/lib/corelib/language/projectresolver.h205
-rw-r--r--src/lib/corelib/language/property.h12
-rw-r--r--src/lib/corelib/language/propertydeclaration.cpp282
-rw-r--r--src/lib/corelib/language/propertydeclaration.h28
-rw-r--r--src/lib/corelib/language/propertymapinternal.h2
-rw-r--r--src/lib/corelib/language/qualifiedid.cpp2
-rw-r--r--src/lib/corelib/language/qualifiedid.h2
-rw-r--r--src/lib/corelib/language/scriptengine.cpp943
-rw-r--r--src/lib/corelib/language/scriptengine.h278
-rw-r--r--src/lib/corelib/language/scriptimporter.cpp36
-rw-r--r--src/lib/corelib/language/scriptimporter.h9
-rw-r--r--src/lib/corelib/language/scriptpropertyobserver.h7
-rw-r--r--src/lib/corelib/language/value.cpp165
-rw-r--r--src/lib/corelib/language/value.h130
-rw-r--r--src/lib/corelib/loader/astimportshandler.cpp293
-rw-r--r--src/lib/corelib/loader/astimportshandler.h94
-rw-r--r--src/lib/corelib/loader/astpropertiesitemhandler.cpp195
-rw-r--r--src/lib/corelib/loader/astpropertiesitemhandler.h65
-rw-r--r--src/lib/corelib/loader/dependenciesresolver.cpp1175
-rw-r--r--src/lib/corelib/loader/dependenciesresolver.h57
-rw-r--r--src/lib/corelib/loader/groupshandler.cpp300
-rw-r--r--src/lib/corelib/loader/groupshandler.h56
-rw-r--r--src/lib/corelib/loader/itemreader.cpp274
-rw-r--r--src/lib/corelib/loader/itemreader.h121
-rw-r--r--src/lib/corelib/loader/itemreaderastvisitor.cpp403
-rw-r--r--src/lib/corelib/loader/itemreaderastvisitor.h103
-rw-r--r--src/lib/corelib/loader/itemreadervisitorstate.cpp149
-rw-r--r--src/lib/corelib/loader/itemreadervisitorstate.h83
-rw-r--r--src/lib/corelib/loader/loaderutils.cpp961
-rw-r--r--src/lib/corelib/loader/loaderutils.h464
-rw-r--r--src/lib/corelib/loader/localprofiles.cpp148
-rw-r--r--src/lib/corelib/loader/localprofiles.h49
-rw-r--r--src/lib/corelib/loader/moduleinstantiator.cpp340
-rw-r--r--src/lib/corelib/loader/moduleinstantiator.h82
-rw-r--r--src/lib/corelib/loader/moduleloader.cpp410
-rw-r--r--src/lib/corelib/loader/moduleloader.h58
-rw-r--r--src/lib/corelib/loader/modulepropertymerger.cpp323
-rw-r--r--src/lib/corelib/loader/modulepropertymerger.h87
-rw-r--r--src/lib/corelib/loader/moduleproviderloader.cpp416
-rw-r--r--src/lib/corelib/loader/moduleproviderloader.h112
-rw-r--r--src/lib/corelib/loader/probesresolver.cpp293
-rw-r--r--src/lib/corelib/loader/probesresolver.h80
-rw-r--r--src/lib/corelib/loader/productitemmultiplexer.cpp259
-rw-r--r--src/lib/corelib/loader/productitemmultiplexer.h64
-rw-r--r--src/lib/corelib/loader/productresolver.cpp1618
-rw-r--r--src/lib/corelib/loader/productresolver.h57
-rw-r--r--src/lib/corelib/loader/productscollector.cpp754
-rw-r--r--src/lib/corelib/loader/productscollector.h63
-rw-r--r--src/lib/corelib/loader/productsresolver.cpp599
-rw-r--r--src/lib/corelib/loader/productsresolver.h47
-rw-r--r--src/lib/corelib/loader/projectresolver.cpp562
-rw-r--r--src/lib/corelib/loader/projectresolver.h84
-rw-r--r--src/lib/corelib/logging/categories.cpp1
-rw-r--r--src/lib/corelib/logging/categories.h1
-rw-r--r--src/lib/corelib/logging/ilogsink.cpp7
-rw-r--r--src/lib/corelib/logging/ilogsink.h4
-rw-r--r--src/lib/corelib/logging/logger.cpp16
-rw-r--r--src/lib/corelib/logging/logger.h2
-rw-r--r--src/lib/corelib/logging/logging.pri18
-rw-r--r--src/lib/corelib/parser/parser.pri21
-rw-r--r--src/lib/corelib/parser/qmlerror.cpp41
-rw-r--r--src/lib/corelib/parser/qmlerror.h4
-rw-r--r--src/lib/corelib/parser/qmljs.g44
-rw-r--r--src/lib/corelib/parser/qmljsast_p.h4
-rw-r--r--src/lib/corelib/parser/qmljsastvisitor_p.h2
-rw-r--r--src/lib/corelib/parser/qmljsengine_p.cpp6
-rw-r--r--src/lib/corelib/parser/qmljsengine_p.h16
-rw-r--r--src/lib/corelib/parser/qmljsglobal_p.h35
-rw-r--r--src/lib/corelib/parser/qmljsgrammar.cpp1902
-rw-r--r--src/lib/corelib/parser/qmljsgrammar_p.h287
-rw-r--r--src/lib/corelib/parser/qmljslexer.cpp78
-rw-r--r--src/lib/corelib/parser/qmljslexer_p.h6
-rw-r--r--src/lib/corelib/parser/qmljsparser.cpp12
-rw-r--r--src/lib/corelib/parser/qmljsparser_p.h2
-rw-r--r--src/lib/corelib/tools/applecodesignutils.cpp8
-rw-r--r--src/lib/corelib/tools/architectures.cpp2
-rw-r--r--src/lib/corelib/tools/buildoptions.cpp4
-rw-r--r--src/lib/corelib/tools/buildoptions.h2
-rw-r--r--src/lib/corelib/tools/clangclinfo.cpp173
-rw-r--r--src/lib/corelib/tools/clangclinfo.h61
-rw-r--r--src/lib/corelib/tools/cleanoptions.cpp2
-rw-r--r--src/lib/corelib/tools/codelocation.cpp49
-rw-r--r--src/lib/corelib/tools/codelocation.h74
-rw-r--r--src/lib/corelib/tools/commandechomode.h6
-rw-r--r--src/lib/corelib/tools/deprecationwarningmode.cpp98
-rw-r--r--src/lib/corelib/tools/deprecationwarningmode.h59
-rw-r--r--src/lib/corelib/tools/error.cpp44
-rw-r--r--src/lib/corelib/tools/error.h17
-rw-r--r--src/lib/corelib/tools/executablefinder.cpp8
-rw-r--r--src/lib/corelib/tools/fileinfo.cpp123
-rw-r--r--src/lib/corelib/tools/fileinfo.h14
-rw-r--r--src/lib/corelib/tools/filesaver.cpp9
-rw-r--r--src/lib/corelib/tools/filesaver.h3
-rw-r--r--src/lib/corelib/tools/filetime.cpp168
-rw-r--r--src/lib/corelib/tools/filetime.h26
-rw-r--r--src/lib/corelib/tools/hostosinfo.h106
-rw-r--r--src/lib/corelib/tools/id.cpp13
-rw-r--r--src/lib/corelib/tools/id.h3
-rw-r--r--src/lib/corelib/tools/installoptions.cpp2
-rw-r--r--src/lib/corelib/tools/iosutils.h8
-rw-r--r--src/lib/corelib/tools/jsliterals.cpp13
-rw-r--r--src/lib/corelib/tools/jsonhelper.h13
-rw-r--r--src/lib/corelib/tools/launcherinterface.cpp21
-rw-r--r--src/lib/corelib/tools/launcherpackets.h2
-rw-r--r--src/lib/corelib/tools/launchersocket.cpp36
-rw-r--r--src/lib/corelib/tools/launchersocket.h4
-rw-r--r--src/lib/corelib/tools/msvcinfo.cpp454
-rw-r--r--src/lib/corelib/tools/msvcinfo.h33
-rw-r--r--src/lib/corelib/tools/mutexdata.h104
-rw-r--r--src/lib/corelib/tools/pathutils.h2
-rw-r--r--src/lib/corelib/tools/persistence.cpp16
-rw-r--r--src/lib/corelib/tools/persistence.h118
-rw-r--r--src/lib/corelib/tools/pimpl.h61
-rw-r--r--src/lib/corelib/tools/porting.h55
-rw-r--r--src/lib/corelib/tools/preferences.cpp1
-rw-r--r--src/lib/corelib/tools/processresult.h3
-rw-r--r--src/lib/corelib/tools/processutils.cpp2
-rw-r--r--src/lib/corelib/tools/profile.cpp12
-rw-r--r--src/lib/corelib/tools/profile.h7
-rw-r--r--src/lib/corelib/tools/profiling.cpp13
-rw-r--r--src/lib/corelib/tools/profiling.h6
-rw-r--r--src/lib/corelib/tools/progressobserver.h11
-rw-r--r--src/lib/corelib/tools/projectgeneratormanager.h5
-rw-r--r--src/lib/corelib/tools/propagate_const.h418
-rw-r--r--src/lib/corelib/tools/qbs_export.h4
-rw-r--r--src/lib/corelib/tools/qbsassert.h5
-rw-r--r--src/lib/corelib/tools/qbspluginmanager.cpp2
-rw-r--r--src/lib/corelib/tools/qbsprocess.cpp10
-rw-r--r--src/lib/corelib/tools/qbsprocess.h2
-rw-r--r--src/lib/corelib/tools/qttools.cpp28
-rw-r--r--src/lib/corelib/tools/qttools.h185
-rw-r--r--src/lib/corelib/tools/scripttools.cpp263
-rw-r--r--src/lib/corelib/tools/scripttools.h163
-rw-r--r--src/lib/corelib/tools/set.h70
-rw-r--r--src/lib/corelib/tools/settings.cpp37
-rw-r--r--src/lib/corelib/tools/settings.h10
-rw-r--r--src/lib/corelib/tools/settingscreator.cpp53
-rw-r--r--src/lib/corelib/tools/settingscreator.h2
-rw-r--r--src/lib/corelib/tools/settingsmodel.cpp33
-rw-r--r--src/lib/corelib/tools/settingsmodel.h4
-rw-r--r--src/lib/corelib/tools/settingsrepresentation.cpp26
-rw-r--r--src/lib/corelib/tools/setupprojectparameters.cpp124
-rw-r--r--src/lib/corelib/tools/setupprojectparameters.h13
-rw-r--r--src/lib/corelib/tools/shellutils.cpp17
-rw-r--r--src/lib/corelib/tools/shellutils.h2
-rw-r--r--src/lib/corelib/tools/stlutils.h165
-rw-r--r--src/lib/corelib/tools/stringconstants.h4
-rw-r--r--src/lib/corelib/tools/toolchains.cpp7
-rw-r--r--src/lib/corelib/tools/tools.pri144
-rw-r--r--src/lib/corelib/tools/version.cpp15
-rw-r--r--src/lib/corelib/tools/version.h8
-rw-r--r--src/lib/corelib/tools/visualstudioversioninfo.cpp11
-rw-r--r--src/lib/corelib/tools/visualstudioversioninfo.h3
-rw-r--r--src/lib/corelib/tools/vsenvironmentdetector.cpp26
-rw-r--r--src/lib/corelib/tools/vsenvironmentdetector.h3
-rw-r--r--src/lib/corelib/use_corelib.pri47
-rw-r--r--src/lib/corelib/use_installed_corelib.pri38
-rw-r--r--src/lib/library.pri49
-rw-r--r--src/lib/libs.qbs5
-rw-r--r--src/lib/msbuild/CMakeLists.txt72
-rw-r--r--src/lib/msbuild/io/msbuildprojectwriter.cpp235
-rw-r--r--src/lib/msbuild/io/msbuildprojectwriter.h60
-rw-r--r--src/lib/msbuild/io/visualstudiosolutionwriter.cpp153
-rw-r--r--src/lib/msbuild/io/visualstudiosolutionwriter.h (renamed from src/plugins/generator/visualstudio/io/visualstudiosolutionwriter.h)0
-rw-r--r--src/lib/msbuild/msbuild.qbs86
-rw-r--r--src/lib/msbuild/msbuild/imsbuildgroup.cpp (renamed from src/plugins/generator/visualstudio/msbuild/imsbuildgroup.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/imsbuildgroup.h (renamed from src/plugins/generator/visualstudio/msbuild/imsbuildgroup.h)0
-rw-r--r--src/lib/msbuild/msbuild/imsbuildnode.cpp (renamed from src/plugins/generator/visualstudio/msbuild/imsbuildnode.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/imsbuildnode.h (renamed from src/plugins/generator/visualstudio/msbuild/imsbuildnode.h)0
-rw-r--r--src/lib/msbuild/msbuild/imsbuildnodevisitor.h (renamed from src/plugins/generator/visualstudio/msbuild/imsbuildnodevisitor.h)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildclcompile.cpp (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildclcompile.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildclcompile.h (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildclcompile.h)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildclinclude.cpp (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildclinclude.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildclinclude.h (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildclinclude.h)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildfileitem.cpp (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildfileitem.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildfileitem.h (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildfileitem.h)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildfilter.cpp (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildfilter.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildfilter.h (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildfilter.h)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildlink.cpp (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildlink.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildlink.h (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildlink.h)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildnone.cpp (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildnone.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/items/msbuildnone.h (renamed from src/plugins/generator/visualstudio/msbuild/items/msbuildnone.h)0
-rw-r--r--src/lib/msbuild/msbuild/msbuildimport.cpp (renamed from src/plugins/generator/visualstudio/msbuild/msbuildimport.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/msbuildimport.h (renamed from src/plugins/generator/visualstudio/msbuild/msbuildimport.h)0
-rw-r--r--src/lib/msbuild/msbuild/msbuildimportgroup.cpp (renamed from src/plugins/generator/visualstudio/msbuild/msbuildimportgroup.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/msbuildimportgroup.h (renamed from src/plugins/generator/visualstudio/msbuild/msbuildimportgroup.h)0
-rw-r--r--src/lib/msbuild/msbuild/msbuilditem.cpp (renamed from src/plugins/generator/visualstudio/msbuild/msbuilditem.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/msbuilditem.h (renamed from src/plugins/generator/visualstudio/msbuild/msbuilditem.h)0
-rw-r--r--src/lib/msbuild/msbuild/msbuilditemdefinitiongroup.cpp (renamed from src/plugins/generator/visualstudio/msbuild/msbuilditemdefinitiongroup.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/msbuilditemdefinitiongroup.h (renamed from src/plugins/generator/visualstudio/msbuild/msbuilditemdefinitiongroup.h)0
-rw-r--r--src/lib/msbuild/msbuild/msbuilditemgroup.cpp (renamed from src/plugins/generator/visualstudio/msbuild/msbuilditemgroup.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/msbuilditemgroup.h (renamed from src/plugins/generator/visualstudio/msbuild/msbuilditemgroup.h)0
-rw-r--r--src/lib/msbuild/msbuild/msbuilditemmetadata.cpp57
-rw-r--r--src/lib/msbuild/msbuild/msbuilditemmetadata.h60
-rw-r--r--src/lib/msbuild/msbuild/msbuildproject.cpp (renamed from src/plugins/generator/visualstudio/msbuild/msbuildproject.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/msbuildproject.h (renamed from src/plugins/generator/visualstudio/msbuild/msbuildproject.h)0
-rw-r--r--src/lib/msbuild/msbuild/msbuildproperty.cpp57
-rw-r--r--src/lib/msbuild/msbuild/msbuildproperty.h59
-rw-r--r--src/lib/msbuild/msbuild/msbuildpropertybase.cpp81
-rw-r--r--src/lib/msbuild/msbuild/msbuildpropertybase.h67
-rw-r--r--src/lib/msbuild/msbuild/msbuildpropertygroup.cpp (renamed from src/plugins/generator/visualstudio/msbuild/msbuildpropertygroup.cpp)0
-rw-r--r--src/lib/msbuild/msbuild/msbuildpropertygroup.h (renamed from src/plugins/generator/visualstudio/msbuild/msbuildpropertygroup.h)0
-rw-r--r--src/lib/msbuild/solution/ivisualstudiosolutionproject.cpp (renamed from src/plugins/generator/visualstudio/solution/ivisualstudiosolutionproject.cpp)0
-rw-r--r--src/lib/msbuild/solution/ivisualstudiosolutionproject.h (renamed from src/plugins/generator/visualstudio/solution/ivisualstudiosolutionproject.h)0
-rw-r--r--src/lib/msbuild/solution/visualstudiosolution.cpp117
-rw-r--r--src/lib/msbuild/solution/visualstudiosolution.h (renamed from src/plugins/generator/visualstudio/solution/visualstudiosolution.h)0
-rw-r--r--src/lib/msbuild/solution/visualstudiosolutionfileproject.cpp76
-rw-r--r--src/lib/msbuild/solution/visualstudiosolutionfileproject.h (renamed from src/plugins/generator/visualstudio/solution/visualstudiosolutionfileproject.h)0
-rw-r--r--src/lib/msbuild/solution/visualstudiosolutionfolderproject.cpp47
-rw-r--r--src/lib/msbuild/solution/visualstudiosolutionfolderproject.h (renamed from src/plugins/generator/visualstudio/solution/visualstudiosolutionfolderproject.h)0
-rw-r--r--src/lib/msbuild/solution/visualstudiosolutionglobalsection.cpp (renamed from src/plugins/generator/visualstudio/solution/visualstudiosolutionglobalsection.cpp)0
-rw-r--r--src/lib/msbuild/solution/visualstudiosolutionglobalsection.h (renamed from src/plugins/generator/visualstudio/solution/visualstudiosolutionglobalsection.h)0
-rw-r--r--src/lib/pkgconfig/CMakeLists.txt32
-rw-r--r--src/lib/pkgconfig/pcpackage.cpp243
-rw-r--r--src/lib/pkgconfig/pcpackage.h218
-rw-r--r--src/lib/pkgconfig/pcparser.cpp788
-rw-r--r--src/lib/pkgconfig/pcparser.h85
-rw-r--r--src/lib/pkgconfig/pkgconfig.cpp337
-rw-r--r--src/lib/pkgconfig/pkgconfig.h89
-rw-r--r--src/lib/pkgconfig/pkgconfig.qbs68
-rw-r--r--src/lib/scriptengine/include/QtScript/qtscriptglobal.h46
-rw-r--r--src/lib/scriptengine/scriptengine.pro92
-rw-r--r--src/lib/scriptengine/scriptengine.qbs433
-rw-r--r--src/lib/scriptengine/use_scriptengine.pri11
-rw-r--r--src/libexec/CMakeLists.txt1
-rw-r--r--src/libexec/libexec.pri11
-rw-r--r--src/libexec/libexec.pro3
-rw-r--r--src/libexec/libexec.qbs2
-rw-r--r--src/libexec/qbs_processlauncher/CMakeLists.txt25
-rw-r--r--src/libexec/qbs_processlauncher/launchersockethandler.cpp6
-rw-r--r--src/libexec/qbs_processlauncher/qbs_processlauncher.pro21
-rw-r--r--src/libexec/qbs_processlauncher/qbs_processlauncher.qbs1
-rw-r--r--src/library_dirname.pri1
-rw-r--r--src/packages/archive/archive.qbs7
-rw-r--r--src/packages/chocolatey/chocolatey.qbs5
-rw-r--r--src/packages/chocolatey/chocolateyinstall.ps14
-rw-r--r--src/packages/chocolatey/icon.pngbin0 -> 6915 bytes
-rw-r--r--src/packages/chocolatey/qbs.nuspec2
-rw-r--r--src/packages/packages.qbs4
-rw-r--r--src/plugins/CMakeLists.txt2
-rw-r--r--src/plugins/generator/CMakeLists.txt5
-rw-r--r--src/plugins/generator/clangcompilationdb/CMakeLists.txt10
-rw-r--r--src/plugins/generator/clangcompilationdb/clangcompilationdb.pri1
-rw-r--r--src/plugins/generator/clangcompilationdb/clangcompilationdb.pro11
-rw-r--r--src/plugins/generator/clangcompilationdb/clangcompilationdb.qbs1
-rw-r--r--src/plugins/generator/clangcompilationdb/clangcompilationdbgenerator.cpp26
-rw-r--r--src/plugins/generator/generator.pro6
-rw-r--r--src/plugins/generator/iarew/CMakeLists.txt119
-rw-r--r--src/plugins/generator/iarew/archs/arm/armgeneralsettingsgroup_v8.cpp8
-rw-r--r--src/plugins/generator/iarew/archs/arm/armgeneralsettingsgroup_v8.h4
-rw-r--r--src/plugins/generator/iarew/archs/arm/armlinkersettingsgroup_v8.cpp3
-rw-r--r--src/plugins/generator/iarew/archs/avr/avrgeneralsettingsgroup_v7.cpp2
-rw-r--r--src/plugins/generator/iarew/archs/avr/avrlinkersettingsgroup_v7.cpp3
-rw-r--r--src/plugins/generator/iarew/archs/mcs51/mcs51generalsettingsgroup_v10.cpp59
-rw-r--r--src/plugins/generator/iarew/archs/mcs51/mcs51linkersettingsgroup_v10.cpp3
-rw-r--r--src/plugins/generator/iarew/archs/msp430/msp430generalsettingsgroup_v7.cpp7
-rw-r--r--src/plugins/generator/iarew/archs/msp430/msp430linkersettingsgroup_v7.cpp3
-rw-r--r--src/plugins/generator/iarew/archs/stm8/stm8generalsettingsgroup_v3.cpp4
-rw-r--r--src/plugins/generator/iarew/archs/stm8/stm8linkersettingsgroup_v3.cpp16
-rw-r--r--src/plugins/generator/iarew/iarew.pri1
-rw-r--r--src/plugins/generator/iarew/iarew.pro132
-rw-r--r--src/plugins/generator/iarew/iarew.qbs2
-rw-r--r--src/plugins/generator/iarew/iarewfileversionproperty.cpp4
-rw-r--r--src/plugins/generator/iarew/iarewfileversionproperty.h5
-rw-r--r--src/plugins/generator/iarew/iarewgenerator.cpp2
-rw-r--r--src/plugins/generator/iarew/iarewgenerator.h4
-rw-r--r--src/plugins/generator/iarew/iarewgeneratorplugin.cpp2
-rw-r--r--src/plugins/generator/iarew/iarewoptionpropertygroup.cpp4
-rw-r--r--src/plugins/generator/iarew/iarewoptionpropertygroup.h2
-rw-r--r--src/plugins/generator/iarew/iarewproject.cpp10
-rw-r--r--src/plugins/generator/iarew/iarewproject.h4
-rw-r--r--src/plugins/generator/iarew/iarewsettingspropertygroup.cpp7
-rw-r--r--src/plugins/generator/iarew/iarewsettingspropertygroup.h4
-rw-r--r--src/plugins/generator/iarew/iarewutils.cpp6
-rw-r--r--src/plugins/generator/iarew/iarewversioninfo.cpp77
-rw-r--r--src/plugins/generator/iarew/iarewversioninfo.h19
-rw-r--r--src/plugins/generator/keiluv/CMakeLists.txt80
-rw-r--r--src/plugins/generator/keiluv/archs/arm/armtargetcommonoptionsgroup_v5.cpp2
-rw-r--r--src/plugins/generator/keiluv/archs/arm/armtargetlinkergroup_v5.cpp14
-rw-r--r--src/plugins/generator/keiluv/archs/mcs51/mcs51utils.cpp5
-rw-r--r--src/plugins/generator/keiluv/keiluv.pri1
-rw-r--r--src/plugins/generator/keiluv/keiluv.pro91
-rw-r--r--src/plugins/generator/keiluv/keiluv.qbs2
-rw-r--r--src/plugins/generator/keiluv/keiluvfilesgroupspropertygroup.cpp26
-rw-r--r--src/plugins/generator/keiluv/keiluvgenerator.cpp2
-rw-r--r--src/plugins/generator/keiluv/keiluvgenerator.h4
-rw-r--r--src/plugins/generator/keiluv/keiluvgeneratorplugin.cpp2
-rw-r--r--src/plugins/generator/keiluv/keiluvproject.cpp7
-rw-r--r--src/plugins/generator/keiluv/keiluvproject.h4
-rw-r--r--src/plugins/generator/keiluv/keiluvutils.cpp29
-rw-r--r--src/plugins/generator/keiluv/keiluvversioninfo.cpp76
-rw-r--r--src/plugins/generator/keiluv/keiluvversioninfo.h17
-rw-r--r--src/plugins/generator/makefilegenerator/CMakeLists.txt10
-rw-r--r--src/plugins/generator/makefilegenerator/makefilegenerator.cpp17
-rw-r--r--src/plugins/generator/makefilegenerator/makefilegenerator.pri1
-rw-r--r--src/plugins/generator/makefilegenerator/makefilegenerator.pro11
-rw-r--r--src/plugins/generator/makefilegenerator/makefilegenerator.qbs1
-rw-r--r--src/plugins/generator/visualstudio/CMakeLists.txt25
-rw-r--r--src/plugins/generator/visualstudio/io/msbuildprojectwriter.cpp238
-rw-r--r--src/plugins/generator/visualstudio/io/msbuildprojectwriter.h58
-rw-r--r--src/plugins/generator/visualstudio/io/visualstudiosolutionwriter.cpp167
-rw-r--r--src/plugins/generator/visualstudio/msbuild/imsbuildproperty.cpp81
-rw-r--r--src/plugins/generator/visualstudio/msbuild/imsbuildproperty.h67
-rw-r--r--src/plugins/generator/visualstudio/msbuild/msbuilditemmetadata.cpp57
-rw-r--r--src/plugins/generator/visualstudio/msbuild/msbuilditemmetadata.h60
-rw-r--r--src/plugins/generator/visualstudio/msbuild/msbuildproperty.cpp57
-rw-r--r--src/plugins/generator/visualstudio/msbuild/msbuildproperty.h59
-rw-r--r--src/plugins/generator/visualstudio/msbuildfiltersproject.cpp245
-rw-r--r--src/plugins/generator/visualstudio/msbuildqbsproductproject.cpp4
-rw-r--r--src/plugins/generator/visualstudio/msbuildtargetproject.cpp2
-rw-r--r--src/plugins/generator/visualstudio/msbuildutils.h6
-rw-r--r--src/plugins/generator/visualstudio/solution/visualstudiosolution.cpp117
-rw-r--r--src/plugins/generator/visualstudio/solution/visualstudiosolutionfileproject.cpp76
-rw-r--r--src/plugins/generator/visualstudio/solution/visualstudiosolutionfolderproject.cpp47
-rw-r--r--src/plugins/generator/visualstudio/visualstudio.pri1
-rw-r--r--src/plugins/generator/visualstudio/visualstudio.pro94
-rw-r--r--src/plugins/generator/visualstudio/visualstudio.qbs77
-rw-r--r--src/plugins/generator/visualstudio/visualstudiogenerator.cpp16
-rw-r--r--src/plugins/generator/visualstudio/visualstudioguidpool.cpp2
-rw-r--r--src/plugins/plugins.pri21
-rw-r--r--src/plugins/plugins.pro2
-rw-r--r--src/plugins/plugins.qbs2
-rw-r--r--src/plugins/qbs_plugin_common.pri9
-rw-r--r--src/plugins/qbsplugin.qbs1
-rw-r--r--src/plugins/scanner/CMakeLists.txt2
-rw-r--r--src/plugins/scanner/cpp/CMakeLists.txt16
-rw-r--r--src/plugins/scanner/cpp/Lexer.cpp18
-rw-r--r--src/plugins/scanner/cpp/Lexer.h6
-rw-r--r--src/plugins/scanner/cpp/Token.cpp9
-rw-r--r--src/plugins/scanner/cpp/Token.h10
-rw-r--r--src/plugins/scanner/cpp/cpp.pri1
-rw-r--r--src/plugins/scanner/cpp/cpp.pro10
-rw-r--r--src/plugins/scanner/cpp/cpp.qbs1
-rw-r--r--src/plugins/scanner/cpp/cppscanner.cpp18
-rw-r--r--src/plugins/scanner/qt/CMakeLists.txt4
-rw-r--r--src/plugins/scanner/qt/qt.pri1
-rw-r--r--src/plugins/scanner/qt/qt.pro8
-rw-r--r--src/plugins/scanner/qt/qt.qbs1
-rw-r--r--src/plugins/scanner/qt/qtscanner.cpp21
-rw-r--r--src/plugins/scanner/scanner.pro3
-rw-r--r--src/plugins/use_plugin.pri10
-rw-r--r--src/shared/CMakeLists.txt4
-rw-r--r--src/shared/bundledqt/bundledqt.qbs24
-rw-r--r--src/shared/json/CMakeLists.txt4
-rw-r--r--src/shared/json/json.cpp33
-rw-r--r--src/shared/json/json.pri3
-rw-r--r--src/shared/json/json.qbs11
-rw-r--r--src/shared/lsp/.clang-tidy3
-rw-r--r--src/shared/lsp/CMakeLists.txt57
-rw-r--r--src/shared/lsp/algorithm.h1518
-rw-r--r--src/shared/lsp/basemessage.cpp188
-rw-r--r--src/shared/lsp/basemessage.h48
-rw-r--r--src/shared/lsp/callhierarchy.cpp28
-rw-r--r--src/shared/lsp/callhierarchy.h108
-rw-r--r--src/shared/lsp/client.cpp17
-rw-r--r--src/shared/lsp/client.h94
-rw-r--r--src/shared/lsp/clientcapabilities.cpp122
-rw-r--r--src/shared/lsp/clientcapabilities.h674
-rw-r--r--src/shared/lsp/completion.cpp59
-rw-r--r--src/shared/lsp/completion.h269
-rw-r--r--src/shared/lsp/diagnostics.cpp13
-rw-r--r--src/shared/lsp/diagnostics.h38
-rw-r--r--src/shared/lsp/filepath.h68
-rw-r--r--src/shared/lsp/initializemessages.cpp137
-rw-r--r--src/shared/lsp/initializemessages.h187
-rw-r--r--src/shared/lsp/jsonkeys.h231
-rw-r--r--src/shared/lsp/jsonobject.cpp28
-rw-r--r--src/shared/lsp/jsonobject.h181
-rw-r--r--src/shared/lsp/jsonrpcmessages.cpp107
-rw-r--r--src/shared/lsp/jsonrpcmessages.h415
-rw-r--r--src/shared/lsp/languagefeatures.cpp399
-rw-r--r--src/shared/lsp/languagefeatures.h836
-rw-r--r--src/shared/lsp/languageserverprotocol_global.h14
-rw-r--r--src/shared/lsp/languageserverprotocoltr.h15
-rw-r--r--src/shared/lsp/link.cpp35
-rw-r--r--src/shared/lsp/link.h78
-rw-r--r--src/shared/lsp/lsp.qbs72
-rw-r--r--src/shared/lsp/lsptypes.cpp364
-rw-r--r--src/shared/lsp/lsptypes.h680
-rw-r--r--src/shared/lsp/lsputils.cpp68
-rw-r--r--src/shared/lsp/lsputils.h160
-rw-r--r--src/shared/lsp/messages.cpp45
-rw-r--r--src/shared/lsp/messages.h93
-rw-r--r--src/shared/lsp/predicates.h110
-rw-r--r--src/shared/lsp/progresssupport.cpp54
-rw-r--r--src/shared/lsp/progresssupport.h152
-rw-r--r--src/shared/lsp/semantictokens.cpp141
-rw-r--r--src/shared/lsp/semantictokens.h230
-rw-r--r--src/shared/lsp/servercapabilities.cpp386
-rw-r--r--src/shared/lsp/servercapabilities.h435
-rw-r--r--src/shared/lsp/shutdownmessages.cpp13
-rw-r--r--src/shared/lsp/shutdownmessages.h29
-rw-r--r--src/shared/lsp/textsynchronization.cpp84
-rw-r--r--src/shared/lsp/textsynchronization.h215
-rw-r--r--src/shared/lsp/textutils.cpp126
-rw-r--r--src/shared/lsp/textutils.h46
-rw-r--r--src/shared/lsp/workspace.cpp82
-rw-r--r--src/shared/lsp/workspace.h245
m---------src/shared/qtscript0
-rw-r--r--src/shared/quickjs/.clang-tidy3
-rw-r--r--src/shared/quickjs/CMakeLists.txt33
-rw-r--r--src/shared/quickjs/LICENSE22
-rw-r--r--src/shared/quickjs/cutils.c630
-rw-r--r--src/shared/quickjs/cutils.h309
-rw-r--r--src/shared/quickjs/libregexp-opcode.h58
-rw-r--r--src/shared/quickjs/libregexp.c2610
-rw-r--r--src/shared/quickjs/libregexp.h92
-rw-r--r--src/shared/quickjs/libunicode-table.h4449
-rw-r--r--src/shared/quickjs/libunicode.c1558
-rw-r--r--src/shared/quickjs/libunicode.h124
-rw-r--r--src/shared/quickjs/list.h100
-rw-r--r--src/shared/quickjs/quickjs-atom.h273
-rw-r--r--src/shared/quickjs/quickjs-opcode.h365
-rw-r--r--src/shared/quickjs/quickjs.c54367
-rw-r--r--src/shared/quickjs/quickjs.diff4061
-rw-r--r--src/shared/quickjs/quickjs.h1027
-rw-r--r--src/shared/quickjs/quickjs.qbs33
-rw-r--r--src/shared/variant/CMakeLists.txt4
-rw-r--r--src/shared/variant/LICENSE.md23
-rw-r--r--src/shared/variant/README.md37
-rw-r--r--src/shared/variant/variant.h63
-rw-r--r--src/shared/variant/variant.hpp2465
-rw-r--r--src/shared/variant/variant.qbs13
-rw-r--r--src/src.qbs7
-rw-r--r--static-res.pro74
-rw-r--r--static.pro83
-rw-r--r--tests/CMakeLists.txt5
-rw-r--r--tests/auto/CMakeLists.txt10
-rw-r--r--tests/auto/api/CMakeLists.txt9
-rw-r--r--tests/auto/api/api.pro28
-rw-r--r--tests/auto/api/api.qbs3
-rw-r--r--tests/auto/api/testdata/QBS-728/QBS-728.qbs2
-rw-r--r--tests/auto/api/testdata/app-without-sources/app-without-sources.qbs2
-rw-r--r--tests/auto/api/testdata/build-properties-source/build-properties-source.qbs5
-rw-r--r--tests/auto/api/testdata/build-properties-source/main.cpp4
-rw-r--r--tests/auto/api/testdata/change-dependent-lib/change-dependent-lib.qbs2
-rw-r--r--tests/auto/api/testdata/change-dependent-lib/main.cpp5
-rw-r--r--tests/auto/api/testdata/change-dependent-lib/mylib.cpp4
-rw-r--r--tests/auto/api/testdata/codegen/codegen.qbs6
-rw-r--r--tests/auto/api/testdata/dependency-on-multiplexed-type/dependency-on-multiplexed-type.qbs2
-rw-r--r--tests/auto/api/testdata/disabled-product/disabled-product.qbs2
-rw-r--r--tests/auto/api/testdata/disappeared-wildcard-file/disappeared-wildcard-file.qbs4
-rw-r--r--tests/auto/api/testdata/disappeared-wildcard-file/file1.txt (renamed from tests/auto/blackbox/testdata/concurrent-executor/dummy1.input)0
-rw-r--r--tests/auto/api/testdata/disappeared-wildcard-file/file2.txt (renamed from tests/auto/blackbox/testdata/concurrent-executor/dummy2.input)0
-rw-r--r--tests/auto/api/testdata/explicitly-depends-on/explicitly-depends-on.qbs7
-rw-r--r--tests/auto/api/testdata/export-simple/export-simple.qbs2
-rw-r--r--tests/auto/api/testdata/export-simple/lib1.cpp4
-rw-r--r--tests/auto/api/testdata/export-simple/main.cpp5
-rw-r--r--tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs2
-rw-r--r--tests/auto/api/testdata/infinite-loop-process/infinite-loop.qbs8
-rw-r--r--tests/auto/api/testdata/infinite-loop-scanning-scan/file.in (renamed from tests/auto/blackbox/testdata/innosetup/inc/qbsinc.iss)0
-rw-r--r--tests/auto/api/testdata/infinite-loop-scanning-scan/infinite-loop.qbs21
-rw-r--r--tests/auto/api/testdata/infinite-loop-scanning-scan/modules/m/m.qbs6
-rw-r--r--tests/auto/api/testdata/infinite-loop-scanning-searchpaths/file.in0
-rw-r--r--tests/auto/api/testdata/infinite-loop-scanning-searchpaths/infinite-loop.qbs21
-rw-r--r--tests/auto/api/testdata/infinite-loop-scanning-searchpaths/modules/m/m.qbs7
-rw-r--r--tests/auto/api/testdata/installed-artifact/installed-artifact.qbs1
-rw-r--r--tests/auto/api/testdata/is-runnable/is-runnable.qbs2
-rw-r--r--tests/auto/api/testdata/lib-same-source/main.cpp4
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs-staticlibs/dynamic1.cpp4
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs-staticlibs/dynamic2.cpp4
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs-staticlibs/link-dynamiclibs-staticlibs.qbs9
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs-staticlibs/main.cpp4
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs-staticlibs/static1.cpp4
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs-staticlibs/static2.cpp4
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs/lib1.cpp4
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs/lib2.cpp4
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs/lib3.cpp4
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs/lib4.cpp4
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs/lib4.h2
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs/link-dynamiclibs.qbs10
-rw-r--r--tests/auto/api/testdata/link-dynamiclibs/main.cpp4
-rw-r--r--tests/auto/api/testdata/link-static-lib/link-static-lib.qbs4
-rw-r--r--tests/auto/api/testdata/link-static-lib/main.cpp2
-rw-r--r--tests/auto/api/testdata/link-static-lib/mystaticlib.cpp5
-rw-r--r--tests/auto/api/testdata/link-staticlibs-dynamiclibs/dynamic1.cpp4
-rw-r--r--tests/auto/api/testdata/link-staticlibs-dynamiclibs/dynamic2.cpp4
-rw-r--r--tests/auto/api/testdata/link-staticlibs-dynamiclibs/link-staticlibs-dynamiclibs.qbs14
-rw-r--r--tests/auto/api/testdata/link-staticlibs-dynamiclibs/main.cpp4
-rw-r--r--tests/auto/api/testdata/link-staticlibs-dynamiclibs/static1.cpp5
-rw-r--r--tests/auto/api/testdata/link-staticlibs-dynamiclibs/static2.cpp4
-rw-r--r--tests/auto/api/testdata/lots-of-dots/m.a.i.n.cpp2
-rw-r--r--tests/auto/api/testdata/moc-hpp-included/moc-hpp-included.qbs2
-rw-r--r--tests/auto/api/testdata/moc-hpp-included/object.cpp2
-rw-r--r--tests/auto/api/testdata/moc-hpp/object.cpp2
-rw-r--r--tests/auto/api/testdata/multiplexing/multiplexing.qbs7
-rw-r--r--tests/auto/api/testdata/new-output-artifact-in-dependency/new-output-artifact-in-dependency.qbs2
-rw-r--r--tests/auto/api/testdata/objc/objc.qbs2
-rw-r--r--tests/auto/api/testdata/precompiled-header-dynamic/precompiled-header-dynamic.qbs2
-rw-r--r--tests/auto/api/testdata/process-result/process-result.qbs11
-rw-r--r--tests/auto/api/testdata/project-with-properties-item/project-with-properties-item.qbs2
-rw-r--r--tests/auto/api/testdata/properties-blocks/main.cpp7
-rw-r--r--tests/auto/api/testdata/properties-blocks/properties-blocks.qbs2
-rw-r--r--tests/auto/api/testdata/qt5-plugin/qt5-plugin.qbs3
-rw-r--r--tests/auto/api/testdata/remove-file-dependency/main.cpp2
-rw-r--r--tests/auto/api/testdata/rename-product/rename.qbs2
-rw-r--r--tests/auto/api/testdata/rename-target-artifact/rename.qbs2
-rw-r--r--tests/auto/api/testdata/renamed-qbs-source-file/renamed-qbs-source-file.qbs9
-rw-r--r--tests/auto/api/testdata/renamed-qbs-source-file/the-product/the-prodduct.qbs1
-rw-r--r--tests/auto/api/testdata/restored-warnings/restored-warnings.qbs27
-rw-r--r--tests/auto/api/testdata/same-base-name/same-base-name.qbs4
-rw-r--r--tests/auto/api/testdata/static-lib-deps/static-lib-deps.qbs10
-rw-r--r--tests/auto/api/testdata/timeout-js/timeout.qbs2
-rw-r--r--tests/auto/api/testdata/timeout-process/timeout.qbs14
-rw-r--r--tests/auto/api/testdata/tool-in-module/use-outside-project/modules/thetool/thetool.qbs3
-rw-r--r--tests/auto/api/testdata/tool-in-module/use-within-project/use-within-project.qbs9
-rw-r--r--tests/auto/api/testdata/transformer-data/transformer-data.qbs4
-rw-r--r--tests/auto/api/testdata/two-default-property-values/modules/mymodule/mymodule.qbs2
-rw-r--r--tests/auto/api/tst_api.cpp662
-rw-r--r--tests/auto/api/tst_api.h6
-rw-r--r--tests/auto/auto.pri18
-rw-r--r--tests/auto/auto.pro19
-rw-r--r--tests/auto/auto.qbs10
-rw-r--r--tests/auto/blackbox/CMakeLists.txt99
-rw-r--r--tests/auto/blackbox/blackbox-android.pro21
-rw-r--r--tests/auto/blackbox/blackbox-apple.pro20
-rw-r--r--tests/auto/blackbox/blackbox-baremetal.qbs21
-rw-r--r--tests/auto/blackbox/blackbox-clangdb.pro18
-rw-r--r--tests/auto/blackbox/blackbox-examples.qbs21
-rw-r--r--tests/auto/blackbox/blackbox-java.pro18
-rw-r--r--tests/auto/blackbox/blackbox-joblimits.pro18
-rw-r--r--tests/auto/blackbox/blackbox-providers.qbs21
-rw-r--r--tests/auto/blackbox/blackbox-qt.pro18
-rw-r--r--tests/auto/blackbox/blackbox-tutorial.qbs21
-rw-r--r--tests/auto/blackbox/blackbox-windows.qbs21
-rw-r--r--tests/auto/blackbox/blackbox.pro21
-rw-r--r--tests/auto/blackbox/blackbox.qbs1
-rw-r--r--tests/auto/blackbox/find/find-android.qbs50
-rw-r--r--tests/auto/blackbox/find/find-xcode.qbs4
-rw-r--r--tests/auto/blackbox/testdata-android/aidl/AndroidManifest.xml1
-rw-r--r--tests/auto/blackbox/testdata-android/minimal-native/minimal-native.qbs2
-rw-r--r--tests/auto/blackbox/testdata-android/minimal-native/src/main/AndroidManifest.xml1
-rw-r--r--tests/auto/blackbox/testdata-android/minimal-native/src/main/native/native.c1
-rw-r--r--tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/product1.qbs9
-rw-r--r--tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml2
-rw-r--r--tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/product2.qbs2
-rw-r--r--tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml2
-rw-r--r--tests/auto/blackbox/testdata-android/multiple-libs-per-apk/multiple-libs-per-apk.qbs4
-rw-r--r--tests/auto/blackbox/testdata-android/multiple-libs-per-apk/src/main/AndroidManifest.xml2
-rw-r--r--tests/auto/blackbox/testdata-android/qml-app/qml-app.qbs4
-rw-r--r--tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml5
-rw-r--r--tests/auto/blackbox/testdata-android/qt-app/MainWindow.cpp11
-rw-r--r--tests/auto/blackbox/testdata-android/qt-app/MainWindow.h15
-rw-r--r--tests/auto/blackbox/testdata-android/qt-app/Test.java54
-rw-r--r--tests/auto/blackbox/testdata-android/qt-app/TestQt6.java54
-rw-r--r--tests/auto/blackbox/testdata-android/qt-app/main.cpp11
-rw-r--r--tests/auto/blackbox/testdata-android/qt-app/qt-app.qbs27
-rw-r--r--tests/auto/blackbox/testdata-android/qt-app/test.keystorebin0 -> 1284 bytes
-rw-r--r--tests/auto/blackbox/testdata-android/teapot/teapot.qbs5
-rw-r--r--tests/auto/blackbox/testdata-apple/aggregateDependencyLinking/aggregateDependencyLinking.qbs31
-rw-r--r--tests/auto/blackbox/testdata-apple/apple-multiconfig/apple-multiconfig.qbs65
-rw-r--r--tests/auto/blackbox/testdata-apple/bundle-structure/bundle-structure.qbs24
-rw-r--r--tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/ByteArray-Info.plist11
-rw-r--r--tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/byteArrayInfoPlist.qbs37
-rw-r--r--tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/main.c (renamed from tests/auto/blackbox/testdata/innosetupDependencies/main.c)0
-rw-r--r--tests/auto/blackbox/testdata-apple/codesign/app.cpp (renamed from tests/auto/blackbox/testdata/path-probe/main.cpp)0
-rw-r--r--tests/auto/blackbox/testdata-apple/codesign/codesign.qbs57
-rw-r--r--tests/auto/blackbox/testdata-apple/deploymentTarget/deployment.qbs2
-rw-r--r--tests/auto/blackbox/testdata-apple/frameworkStructure/frameworkStructure.qbs6
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/100.pngbin0 -> 6652 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/114.pngbin0 -> 7757 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/120.pngbin0 -> 7734 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/128.pngbin0 -> 8774 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/144.pngbin0 -> 9498 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/152.pngbin0 -> 10734 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/16.pngbin0 -> 811 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/167.pngbin0 -> 11990 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/172.pngbin0 -> 12179 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/180.pngbin0 -> 12444 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/196.pngbin0 -> 14143 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/20.pngbin0 -> 1012 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/216.pngbin0 -> 15831 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/256.pngbin0 -> 18825 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/29.pngbin0 -> 1644 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/32.pngbin0 -> 1835 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/40.pngbin0 -> 2484 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/48.pngbin0 -> 2746 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/50.pngbin0 -> 3075 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/512.pngbin0 -> 41488 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/55.pngbin0 -> 3418 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/57.pngbin0 -> 3532 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/58.pngbin0 -> 3543 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/60.pngbin0 -> 3679 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/64.pngbin0 -> 3984 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/72.pngbin0 -> 4525 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/76.pngbin0 -> 4785 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/80.pngbin0 -> 5180 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/87.pngbin0 -> 5632 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/88.pngbin0 -> 5784 bytes
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/Contents.json299
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/appiconset.qbs10
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/appiconset/main.c (renamed from tests/auto/blackbox/testdata/wixDependencies/main.c)0
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/assetcatalog/assetcatalogempty.qbs10
-rw-r--r--tests/auto/blackbox/testdata-apple/ib/iconsetapp/iconsetapp.qbs5
-rw-r--r--tests/auto/blackbox/testdata-apple/infoPlistVariables/Info.plist24
-rw-r--r--tests/auto/blackbox/testdata-apple/infoPlistVariables/infoPlistVariables.qbs16
-rw-r--r--tests/auto/blackbox/testdata-apple/infoPlistVariables/main.c1
-rw-r--r--tests/auto/blackbox/testdata-apple/multiarch-helpers.js74
-rw-r--r--tests/auto/blackbox/testdata-apple/objc-arc/objc-arc.qbs2
-rw-r--r--tests/auto/blackbox/testdata-apple/overrideInfoPlist/Override-Info.plist10
-rw-r--r--tests/auto/blackbox/testdata-apple/overrideInfoPlist/main.c1
-rw-r--r--tests/auto/blackbox/testdata-apple/overrideInfoPlist/overrideInfoPlist.qbs16
-rw-r--r--tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs21
-rw-r--r--tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs29
-rw-r--r--tests/auto/blackbox/testdata-baremetal/BareMetalProduct.qbs158
-rw-r--r--tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs3
-rw-r--r--tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/app.c4
-rw-r--r--tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/compiler-defines-by-language.qbs73
-rw-r--r--tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/cpptest.cpp0
-rw-r--r--tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/ctest.c0
-rw-r--r--tests/auto/blackbox/testdata-baremetal/compiler-include-paths/compiler-include-paths.qbs9
-rw-r--r--tests/auto/blackbox/testdata-baremetal/compiler-include-paths/main.c4
-rw-r--r--tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs14
-rw-r--r--tests/auto/blackbox/testdata-baremetal/compiler-listing/fun.c4
-rw-r--r--tests/auto/blackbox/testdata-baremetal/compiler-listing/main.c6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/cosmic.lkf1
-rw-r--r--tests/auto/blackbox/testdata-baremetal/defines/defines.qbs6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/defines/main.c11
-rw-r--r--tests/auto/blackbox/testdata-baremetal/distribution-include-paths/bar/bar.h6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/distribution-include-paths/distribution-include-paths.qbs6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/distribution-include-paths/foo/foo.h6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/distribution-include-paths/main.c7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/external-static-libraries/external-static-libraries.qbs34
-rw-r--r--tests/auto/blackbox/testdata-baremetal/external-static-libraries/lib-a.c4
-rw-r--r--tests/auto/blackbox/testdata-baremetal/external-static-libraries/lib-b.c6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/external-static-libraries/main.c6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/linker-map/linker-map.qbs8
-rw-r--r--tests/auto/blackbox/testdata-baremetal/linker-map/main.c4
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-application/main.c4
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-application/one-object-application.qbs5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/78k-iar.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-cosmic.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-gcc.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-keil.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-gcc.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr32-gcc.s8
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr32-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/c166-keil.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/cr16-iar.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs12-cosmic.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs12-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-cosmic.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-iar.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-sdcc.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m16c-iar.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32c-gcc.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32c-iar.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32r-gcc.s9
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-cosmic.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-gcc.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-iar.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs251-keil.s8
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-keil.s8
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-sdcc.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-gcc.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-iar.s6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs122
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/r32c-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rh850-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/riscv-gcc.s11
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/riscv-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rl78-gcc.s11
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rl78-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rx-gcc.s8
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rx-iar.s5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/sh-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-cosmic.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-sdcc.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/v850-gcc.s11
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/v850-iar.s7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/one-object-asm-application/xtensa-gcc.s11
-rw-r--r--tests/auto/blackbox/testdata-baremetal/preinclude-headers/main.c4
-rw-r--r--tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs18
-rw-r--r--tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude.h6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/shared-libraries/app.c12
-rw-r--r--tests/auto/blackbox/testdata-baremetal/shared-libraries/shared-libraries.qbs32
-rw-r--r--tests/auto/blackbox/testdata-baremetal/shared-libraries/shared.c19
-rw-r--r--tests/auto/blackbox/testdata-baremetal/static-library-dependencies/a1.c4
-rw-r--r--tests/auto/blackbox/testdata-baremetal/static-library-dependencies/a2.c4
-rw-r--r--tests/auto/blackbox/testdata-baremetal/static-library-dependencies/app.c6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/static-library-dependencies/b.c6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/static-library-dependencies/c.c6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/static-library-dependencies/d.c7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/static-library-dependencies/e.c7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/static-library-dependencies/static-library-dependencies.qbs40
-rw-r--r--tests/auto/blackbox/testdata-baremetal/system-include-paths/bar/bar.h6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/system-include-paths/foo/foo.h6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/system-include-paths/main.c7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/system-include-paths/system-include-paths.qbs6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/target-platform/target-platform.qbs19
-rw-r--r--tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/dmc.qbs31
-rw-r--r--tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/sdcc.qbs29
-rw-r--r--tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/watcom.qbs34
-rw-r--r--tests/auto/blackbox/testdata-baremetal/toolchain-probe/toolchain-probe.qbs7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/two-object-application/fun.c4
-rw-r--r--tests/auto/blackbox/testdata-baremetal/two-object-application/main.c6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/two-object-application/two-object-application.qbs5
-rw-r--r--tests/auto/blackbox/testdata-baremetal/user-include-paths/bar/bar.h6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/user-include-paths/foo/foo.h6
-rw-r--r--tests/auto/blackbox/testdata-baremetal/user-include-paths/main.c7
-rw-r--r--tests/auto/blackbox/testdata-baremetal/user-include-paths/user-include-paths.qbs6
-rw-r--r--tests/auto/blackbox/testdata-clangdb/project1/project.qbs4
-rw-r--r--tests/auto/blackbox/testdata-java/java/vehicles.qbs18
-rw-r--r--tests/auto/blackbox/testdata-joblimits/job-limits-init/job-limits-init.qbs10
-rw-r--r--tests/auto/blackbox/testdata-joblimits/job-limits/job-limits.qbs4
-rw-r--r--tests/auto/blackbox/testdata-joblimits/job-limits/main.cpp12
-rw-r--r--tests/auto/blackbox/testdata-providers/allowed-values/allowed-values.qbs4
-rw-r--r--tests/auto/blackbox/testdata-providers/allowed-values/module-providers/provider.qbs14
-rw-r--r--tests/auto/blackbox/testdata-providers/broken-provider/broken-provider.qbs13
-rw-r--r--tests/auto/blackbox/testdata-providers/broken-provider/module-providers/provider_a.qbs5
-rw-r--r--tests/auto/blackbox/testdata-providers/fallback-module-provider/fallback-module-provider.qbs (renamed from tests/auto/blackbox/testdata/fallback-module-provider/fallback-module-provider.qbs)0
-rw-r--r--tests/auto/blackbox/testdata-providers/fallback-module-provider/libdir/qbsmetatestmodule.pc (renamed from tests/auto/blackbox/testdata/fallback-module-provider/libdir/qbsmetatestmodule.pc)0
-rw-r--r--tests/auto/blackbox/testdata-providers/fallback-module-provider/main.cpp (renamed from tests/auto/blackbox/testdata/fallback-module-provider/main.cpp)0
-rw-r--r--tests/auto/blackbox/testdata-providers/module-providers-cache/module-providers-cache.qbs11
-rw-r--r--tests/auto/blackbox/testdata-providers/module-providers-cache/module-providers/provider_a.qbs9
-rw-r--r--tests/auto/blackbox/testdata-providers/module-providers/main.cpp8
-rw-r--r--tests/auto/blackbox/testdata-providers/module-providers/module-providers.qbs36
-rw-r--r--tests/auto/blackbox/testdata-providers/module-providers/module-providers/mygenerator/provider.qbs (renamed from tests/auto/blackbox/testdata/module-providers/module-providers/mygenerator/provider.qbs)0
-rw-r--r--tests/auto/blackbox/testdata-providers/module-providers/module-providers/othergenerator/provider.qbs19
-rw-r--r--tests/auto/blackbox/testdata-providers/non-eager-provider/module-providers/provider_a.qbs11
-rw-r--r--tests/auto/blackbox/testdata-providers/non-eager-provider/non-eager-provider.qbs13
-rw-r--r--tests/auto/blackbox/testdata-providers/probe-in-module-provider/module-providers/provider_a.qbs23
-rw-r--r--tests/auto/blackbox/testdata-providers/probe-in-module-provider/probe-in-module-provider.qbs9
-rw-r--r--tests/auto/blackbox/testdata-providers/providers-properties/module-providers/provider_a.qbs9
-rw-r--r--tests/auto/blackbox/testdata-providers/providers-properties/module-providers/provider_b.qbs9
-rw-r--r--tests/auto/blackbox/testdata-providers/providers-properties/providers-properties.qbs12
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-properties-in-providers/module-providers/provider_a.qbs9
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-properties-in-providers/qbs-module-properties-in-providers.qbs34
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/module-providers/provider_a.qbs8
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/module-providers/provider_b.qbs9
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/qbs-module-providers-cli-override.qbs13
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/module-providers/named_provider.qbs8
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/module-providers/qbsmetatestmodule/provider.qbs8
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/qbs-module-providers-compatibility.qbs7
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-providers-helpers.js23
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-providers/module-providers/provider_a.qbs8
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-providers/module-providers/provider_b.qbs9
-rw-r--r--tests/auto/blackbox/testdata-providers/qbs-module-providers/qbs-module-providers.qbs28
-rw-r--r--tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libA.cpp14
-rw-r--r--tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libA.h21
-rw-r--r--tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libs.qbs42
-rw-r--r--tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/main.cpp11
-rw-r--r--tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/qbspkgconfig-module-provider.qbs6
-rw-r--r--tests/auto/blackbox/testdata-providers/removal-version/module-providers/provider_a.qbs14
-rw-r--r--tests/auto/blackbox/testdata-providers/removal-version/removal-version.qbs12
-rw-r--r--tests/auto/blackbox/testdata-qt/auto-qrc/auto-qrc.qbs9
-rw-r--r--tests/auto/blackbox/testdata-qt/dbus-adaptors/car.cpp4
-rw-r--r--tests/auto/blackbox/testdata-qt/dbus-adaptors/main.cpp6
-rw-r--r--tests/auto/blackbox/testdata-qt/dbus-interfaces/controller.cpp3
-rw-r--r--tests/auto/blackbox/testdata-qt/dbus-interfaces/main.cpp7
-rw-r--r--tests/auto/blackbox/testdata-qt/forced-moc/forced-moc.qbs13
-rw-r--r--tests/auto/blackbox/testdata-qt/included-moc-cpp/included-moc-cpp.qbs9
-rw-r--r--tests/auto/blackbox/testdata-qt/linker-variant/qt-linker-variant.qbs2
-rw-r--r--tests/auto/blackbox/testdata-qt/metatypes/metatypes.qbs28
-rw-r--r--tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.cpp3
-rw-r--r--tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.h8
-rw-r--r--tests/auto/blackbox/testdata-qt/metatypes/mocableclass2.cpp10
-rw-r--r--tests/auto/blackbox/testdata-qt/metatypes/unmocableclass.cpp7
-rw-r--r--tests/auto/blackbox/testdata-qt/mixed-build-variants/mixed-build-variants.qbs2
-rw-r--r--tests/auto/blackbox/testdata-qt/moc-compiler-defines/main.cpp7
-rw-r--r--tests/auto/blackbox/testdata-qt/moc-compiler-defines/moc-compiler-defines.qbs3
-rw-r--r--tests/auto/blackbox/testdata-qt/moc-compiler-defines/object.cpp6
-rw-r--r--tests/auto/blackbox/testdata-qt/moc-compiler-defines/object.h58
-rw-r--r--tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/lib.cpp8
-rw-r--r--tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/lib.h18
-rw-r--r--tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/main.cpp3
-rw-r--r--tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/symbols-test.qbs27
-rw-r--r--tests/auto/blackbox/testdata-qt/pkgconfig-qt/dump-libpath.qbs6
-rw-r--r--tests/auto/blackbox/testdata-qt/pkgconfig-qt/main.cpp1
-rw-r--r--tests/auto/blackbox/testdata-qt/pkgconfig-qt/module-providers/dummyProvider.qbs3
-rw-r--r--tests/auto/blackbox/testdata-qt/pkgconfig-qt/pkgconfig-qt.qbs6
-rw-r--r--tests/auto/blackbox/testdata-qt/pkgconfig/pkgconfig.qbs7
-rw-r--r--tests/auto/blackbox/testdata-qt/plugin-meta-data/plugin-meta-data.qbs21
-rw-r--r--tests/auto/blackbox/testdata-qt/plugin-support/plugin-support.qbs10
-rw-r--r--tests/auto/blackbox/testdata-qt/qdoc/qdoc.qbs22
-rw-r--r--tests/auto/blackbox/testdata-qt/qdoc/qdoc.qdoc7
-rw-r--r--tests/auto/blackbox/testdata-qt/qdoc/qdoc.qdocconf13
-rw-r--r--tests/auto/blackbox/testdata-qt/qml-debugging/qml-debugging.qbs5
-rw-r--r--tests/auto/blackbox/testdata-qt/qmltyperegistrar/example.qml58
-rw-r--r--tests/auto/blackbox/testdata-qt/qmltyperegistrar/main.cpp71
-rw-r--r--tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.cpp78
-rw-r--r--tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.h78
-rw-r--r--tests/auto/blackbox/testdata-qt/qmltyperegistrar/qmltyperegistrar.qbs33
-rw-r--r--tests/auto/blackbox/testdata-qt/qrc/i.qbs13
-rw-r--r--tests/auto/blackbox/testdata-qt/qtscxml/qtscxml.qbs31
-rw-r--r--tests/auto/blackbox/testdata-qt/quick-compiler/quick-compiler.qbs6
-rw-r--r--tests/auto/blackbox/testdata-qt/static-qt-plugin-linking/lib.cpp1
-rw-r--r--tests/auto/blackbox/testdata-qt/static-qt-plugin-linking/static-qt-plugin-linking.qbs9
-rw-r--r--tests/auto/blackbox/testdata-windows/codesign/app.cpp1
-rw-r--r--tests/auto/blackbox/testdata-windows/codesign/codesign.qbs43
-rw-r--r--tests/auto/blackbox/testdata-windows/innosetup/inc/qbsinc.iss0
-rw-r--r--tests/auto/blackbox/testdata-windows/innosetup/innosetup.qbs25
-rw-r--r--tests/auto/blackbox/testdata-windows/innosetup/test.iss (renamed from tests/auto/blackbox/testdata/innosetup/test.iss)0
-rw-r--r--tests/auto/blackbox/testdata-windows/innosetupDependencies/innosetupDependencies.qbs76
-rw-r--r--tests/auto/blackbox/testdata-windows/innosetupDependencies/main.c1
-rw-r--r--tests/auto/blackbox/testdata-windows/innosetupDependencies/test.iss (renamed from tests/auto/blackbox/testdata/innosetupDependencies/test.iss)0
-rw-r--r--tests/auto/blackbox/testdata-windows/wix/ExampleScript.bat (renamed from tests/auto/blackbox/testdata/wix/ExampleScript.bat)0
-rw-r--r--tests/auto/blackbox/testdata-windows/wix/QbsBootstrapper.wxs (renamed from tests/auto/blackbox/testdata/wix/QbsBootstrapper.wxs)0
-rw-r--r--tests/auto/blackbox/testdata-windows/wix/QbsSetup.wxs (renamed from tests/auto/blackbox/testdata/wix/QbsSetup.wxs)0
-rw-r--r--tests/auto/blackbox/testdata-windows/wix/Qt.wxs (renamed from tests/auto/blackbox/testdata/wix/Qt.wxs)0
-rw-r--r--tests/auto/blackbox/testdata-windows/wix/WiXInstallers.qbs39
-rw-r--r--tests/auto/blackbox/testdata-windows/wix/de.wxl (renamed from tests/auto/blackbox/testdata/wix/de.wxl)0
-rw-r--r--tests/auto/blackbox/testdata-windows/wixDependencies/QbsSetup.wxs (renamed from tests/auto/blackbox/testdata/wixDependencies/QbsSetup.wxs)0
-rw-r--r--tests/auto/blackbox/testdata-windows/wixDependencies/main.c1
-rw-r--r--tests/auto/blackbox/testdata-windows/wixDependencies/wixDependencies.qbs (renamed from tests/auto/blackbox/testdata/wixDependencies/wixDependencies.qbs)0
-rw-r--r--tests/auto/blackbox/testdata/allowed-values/allowed-values.qbs19
-rw-r--r--tests/auto/blackbox/testdata/allowed-values/modules/a/a.qbs18
-rw-r--r--tests/auto/blackbox/testdata/assembly/assembly.qbs16
-rw-r--r--tests/auto/blackbox/testdata/autotest-timeout/autotests-timeout.qbs12
-rw-r--r--tests/auto/blackbox/testdata/autotest-with-dependencies/autotest-with-dependencies.qbs9
-rw-r--r--tests/auto/blackbox/testdata/autotests/autotests.qbs10
-rw-r--r--tests/auto/blackbox/testdata/badInterpreter/badInterpreter.qbs9
-rw-r--r--tests/auto/blackbox/testdata/build-variant-defaults/build-variant-defaults.qbs16
-rw-r--r--tests/auto/blackbox/testdata/build-variant-defaults/main.cpp1
-rw-r--r--tests/auto/blackbox/testdata/buildenv-change/buildenv-change.qbs2
-rw-r--r--tests/auto/blackbox/testdata/capnproto/bar.capnp8
-rw-r--r--tests/auto/blackbox/testdata/capnproto/baz.capnp8
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.cpp14
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs20
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_cpp.cpp13
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs18
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs19
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.cpp14
-rw-r--r--tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.qbs19
-rw-r--r--tests/auto/blackbox/testdata/capnproto/foo.capnp6
-rw-r--r--tests/auto/blackbox/testdata/capnproto/greeter-client.cpp25
-rw-r--r--tests/auto/blackbox/testdata/capnproto/greeter-server.cpp27
-rw-r--r--tests/auto/blackbox/testdata/capnproto/greeter.capnp13
-rw-r--r--tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs34
-rw-r--r--tests/auto/blackbox/testdata/capnproto/imports/foo.capnp6
-rw-r--r--tests/auto/blackbox/testdata/change-in-imported-file/change-in-imported-file.qbs2
-rw-r--r--tests/auto/blackbox/testdata/change-tracking-and-multiplexing/change-tracking-and-multiplexing.qbs4
-rw-r--r--tests/auto/blackbox/testdata/choose-module-instance/other-searchpath/modules/limerick/generic.qbs2
-rw-r--r--tests/auto/blackbox/testdata/clean/clean.qbs2
-rw-r--r--tests/auto/blackbox/testdata/cli/dotnettest.qbs6
-rw-r--r--tests/auto/blackbox/testdata/command-file/command-file.qbs4
-rw-r--r--tests/auto/blackbox/testdata/compilerDefinesByLanguage/CppDefinesApp.qbs2
-rw-r--r--tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile-probe-project.qbs23
-rw-r--r--tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile.py26
-rw-r--r--tests/auto/blackbox/testdata/conanfile-probe/testlib/conanfile.py25
-rw-r--r--tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs67
-rw-r--r--tests/auto/blackbox/testdata/concurrent-executor/util.js8
-rw-r--r--tests/auto/blackbox/testdata/conditional-filetagger/conditional-filetagger.qbs2
-rw-r--r--tests/auto/blackbox/testdata/configure/configure.qbs7
-rw-r--r--tests/auto/blackbox/testdata/configure/main.cpp2
-rw-r--r--tests/auto/blackbox/testdata/configure/modules/definition/module.qbs2
-rw-r--r--tests/auto/blackbox/testdata/conflicting-property-values/conflicting-property-values.qbs41
-rw-r--r--tests/auto/blackbox/testdata/cpu-features/cpu-features.qbs4
-rw-r--r--tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs4
-rw-r--r--tests/auto/blackbox/testdata/date-property/date-property.qbs18
-rw-r--r--tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs2
-rw-r--r--tests/auto/blackbox/testdata/dependency-profile-mismatch/dependency-profile-mismatch.qbs12
-rw-r--r--tests/auto/blackbox/testdata/dependency-scanning-loop/dependency-scanning-loop.qbs34
-rw-r--r--tests/auto/blackbox/testdata/dependency-scanning-loop/main.cpp1
-rw-r--r--tests/auto/blackbox/testdata/deprecated-property/deprecated-property.qbs4
-rw-r--r--tests/auto/blackbox/testdata/deprecated-property/modules/themodule/m.qbs8
-rw-r--r--tests/auto/blackbox/testdata/disappeared-profile/modules-dir/modules/m/m.qbs4
-rw-r--r--tests/auto/blackbox/testdata/discard-unused-data/discard-unused-data.qbs6
-rw-r--r--tests/auto/blackbox/testdata/distribution-include-paths/distribution-include-paths.qbs4
-rw-r--r--tests/auto/blackbox/testdata/distribution-include-paths/main.cpp8
-rw-r--r--tests/auto/blackbox/testdata/distribution-include-paths/subdir/gagagugu.h4
-rw-r--r--tests/auto/blackbox/testdata/dot-dot-pc-file/dot-dot-pc-file.qbs7
-rw-r--r--tests/auto/blackbox/testdata/dot-dot-pc-file/libdir/qbs.metatest.module.pc5
-rw-r--r--tests/auto/blackbox/testdata/dot-dot-pc-file/main.cpp5
-rw-r--r--tests/auto/blackbox/testdata/driver-linker-flags/driver-linker-flags.qbs2
-rw-r--r--tests/auto/blackbox/testdata/dynamic-library-in-module/Dll.qbs4
-rw-r--r--tests/auto/blackbox/testdata/dynamic-library-in-module/modules/thelib/thelib.qbs2
-rw-r--r--tests/auto/blackbox/testdata/dynamic-library-in-module/modules/theotherlib/theotherlib.qbs2
-rw-r--r--tests/auto/blackbox/testdata/dynamic-library-in-module/theapp.qbs8
-rw-r--r--tests/auto/blackbox/testdata/dynamic-project/dynamic-project.qbs3
-rw-r--r--tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js4
-rw-r--r--tests/auto/blackbox/testdata/empty-profile/empty-profile.qbs3
-rw-r--r--tests/auto/blackbox/testdata/empty-profile/main.cpp1
-rw-r--r--tests/auto/blackbox/testdata/enableExceptions/exceptions.qbs2
-rw-r--r--tests/auto/blackbox/testdata/enableExceptions/none.qbs2
-rw-r--r--tests/auto/blackbox/testdata/env-merging/env-merging.qbs8
-rw-r--r--tests/auto/blackbox/testdata/escaped-linker-flags/escaped-linker-flags.qbs5
-rw-r--r--tests/auto/blackbox/testdata/export-rule/export-rule.qbs2
-rw-r--r--tests/auto/blackbox/testdata/exports-cmake/Foo.cpp5
-rw-r--r--tests/auto/blackbox/testdata/exports-cmake/Foo.h16
-rw-r--r--tests/auto/blackbox/testdata/exports-cmake/cmake/CMakeLists.txt7
-rw-r--r--tests/auto/blackbox/testdata/exports-cmake/cmake/main.cpp6
-rw-r--r--tests/auto/blackbox/testdata/exports-cmake/exports-cmake.qbs70
-rw-r--r--tests/auto/blackbox/testdata/exports-cmake/find-cmake.qbs46
-rw-r--r--tests/auto/blackbox/testdata/exports-pkgconfig/exports-pkgconfig.qbs9
-rw-r--r--tests/auto/blackbox/testdata/exports-qbs/consumer.qbs8
-rw-r--r--tests/auto/blackbox/testdata/exports-qbs/lib.qbs8
-rw-r--r--tests/auto/blackbox/testdata/exports-qbs/tool.qbs4
-rw-r--r--tests/auto/blackbox/testdata/external-libs/external-libs.qbs4
-rw-r--r--tests/auto/blackbox/testdata/fileDependencies/awesomelib/awesome.h2
-rw-r--r--tests/auto/blackbox/testdata/freedesktop/freedesktop.qbs23
-rw-r--r--tests/auto/blackbox/testdata/freedesktop/main.cpp4
-rw-r--r--tests/auto/blackbox/testdata/freedesktop/myapp.appdata.xml15
-rw-r--r--tests/auto/blackbox/testdata/freedesktop/myapp.desktop4
-rw-r--r--tests/auto/blackbox/testdata/freedesktop/myapp.png0
-rw-r--r--tests/auto/blackbox/testdata/generate-linker-map-file/generate-linker-map-file.qbs7
-rw-r--r--tests/auto/blackbox/testdata/generator/generator.qbs4
-rw-r--r--tests/auto/blackbox/testdata/groups-in-modules/groups-in-modules.qbs11
-rw-r--r--tests/auto/blackbox/testdata/groups-in-modules/imports/Helper7Base.qbs8
-rw-r--r--tests/auto/blackbox/testdata/groups-in-modules/modules/helper/helper.qbs2
-rw-r--r--tests/auto/blackbox/testdata/groups-in-modules/modules/helper7/helper7.c1
-rw-r--r--tests/auto/blackbox/testdata/groups-in-modules/modules/helper7/helper7.qbs3
-rw-r--r--tests/auto/blackbox/testdata/grpc/grpc.cpp4
-rw-r--r--tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs14
-rw-r--r--tests/auto/blackbox/testdata/host-os-properties/host-os-properties.qbs16
-rw-r--r--tests/auto/blackbox/testdata/host-os-properties/main.cpp7
-rw-r--r--tests/auto/blackbox/testdata/importing-product/importing-product.qbs2
-rw-r--r--tests/auto/blackbox/testdata/includeLookup/includeLookup.qbs7
-rw-r--r--tests/auto/blackbox/testdata/includeLookup/main.cpp2
-rw-r--r--tests/auto/blackbox/testdata/innosetup/innosetup.qbs22
-rw-r--r--tests/auto/blackbox/testdata/innosetupDependencies/innosetupDependencies.qbs72
-rw-r--r--tests/auto/blackbox/testdata/input-tags-change-tracking/input-tags-change-tracking.qbs4
-rw-r--r--tests/auto/blackbox/testdata/inputs-from-dependencies/inputs-from-dependencies.qbs2
-rw-r--r--tests/auto/blackbox/testdata/install-locations/install-locations.qbs26
-rw-r--r--tests/auto/blackbox/testdata/install-locations/theplugin.cpp3
-rw-r--r--tests/auto/blackbox/testdata/installable-as-auxiliary-input/installable-as-auxiliary-input.qbs13
-rw-r--r--tests/auto/blackbox/testdata/installable/installable.qbs3
-rw-r--r--tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs2
-rw-r--r--tests/auto/blackbox/testdata/installpackage/installpackage.qbs4
-rw-r--r--tests/auto/blackbox/testdata/invalid-command-property/invalid-command-property.qbs2
-rw-r--r--tests/auto/blackbox/testdata/jsextensions-binaryfile/binaryfile.qbs3
-rw-r--r--tests/auto/blackbox/testdata/jsextensions-file/file.qbs4
-rw-r--r--tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs2
-rw-r--r--tests/auto/blackbox/testdata/jsextensions-host/host.qbs31
-rw-r--r--tests/auto/blackbox/testdata/jsextensions-process/main.cpp10
-rw-r--r--tests/auto/blackbox/testdata/jsextensions-process/process.qbs11
-rw-r--r--tests/auto/blackbox/testdata/last-module-candidate-broken/last-module-candidate-broken.qbs5
-rw-r--r--tests/auto/blackbox/testdata/last-module-candidate-broken/main.cpp1
-rw-r--r--tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo1.qbs3
-rw-r--r--tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo2.qbs2
-rw-r--r--tests/auto/blackbox/testdata/ld/ld.qbs2
-rw-r--r--tests/auto/blackbox/testdata/ld/main.cpp5
-rw-r--r--tests/auto/blackbox/testdata/lexyacc/modules/bisonhelper/bisonhelper.qbs3
-rw-r--r--tests/auto/blackbox/testdata/lexyacc/one-grammar/one-grammar.qbs14
-rw-r--r--tests/auto/blackbox/testdata/lexyacc/two-grammars/two-grammars.qbs6
-rw-r--r--tests/auto/blackbox/testdata/linker-library-duplicates/setup-run-environment.qbs7
-rw-r--r--tests/auto/blackbox/testdata/linker-module-definition/linker-module-definition.qbs20
-rw-r--r--tests/auto/blackbox/testdata/linker-module-definition/testapp.cpp39
-rw-r--r--tests/auto/blackbox/testdata/linker-module-definition/testlib.cpp41
-rw-r--r--tests/auto/blackbox/testdata/linker-module-definition/testlib.def3
-rw-r--r--tests/auto/blackbox/testdata/linker-variant/linker-variant.qbs2
-rw-r--r--tests/auto/blackbox/testdata/linkerMode/linkerMode.qbs4
-rw-r--r--tests/auto/blackbox/testdata/linkerMode/main.s1
-rw-r--r--tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs7
-rw-r--r--tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs2
-rw-r--r--tests/auto/blackbox/testdata/list-property-order/product.qbs1
-rw-r--r--tests/auto/blackbox/testdata/loadablemodule/loadablemodule.qbs14
-rw-r--r--tests/auto/blackbox/testdata/localDeployment/localDeployment.qbs8
-rw-r--r--tests/auto/blackbox/testdata/lsp/lsp.qbs11
-rw-r--r--tests/auto/blackbox/testdata/lsp/modules/Prefix/m1/m1.qbs5
-rw-r--r--tests/auto/blackbox/testdata/lsp/modules/Prefix/m2/m2.qbs (renamed from tests/auto/language/testdata/erroneous/modules/prefix1/prefix1.qbs)0
-rw-r--r--tests/auto/blackbox/testdata/lsp/modules/Prefix/m3/m3.qbs2
-rw-r--r--tests/auto/blackbox/testdata/lsp/modules/m/m.qbs2
-rw-r--r--tests/auto/blackbox/testdata/makefile-generator/app.qbs10
-rw-r--r--tests/auto/blackbox/testdata/maximum-c-language-version/maximum-c-language-version.qbs9
-rw-r--r--tests/auto/blackbox/testdata/maximum-cxx-language-version/modules/newestmodule/newestmodule.qbs2
-rw-r--r--tests/auto/blackbox/testdata/minimumSystemVersion/fakewindows.qbs2
-rw-r--r--tests/auto/blackbox/testdata/minimumSystemVersion/macappstore.qbs9
-rw-r--r--tests/auto/blackbox/testdata/minimumSystemVersion/specific.qbs19
-rw-r--r--tests/auto/blackbox/testdata/minimumSystemVersion/unspecified-forced.qbs14
-rw-r--r--tests/auto/blackbox/testdata/minimumSystemVersion/unspecified.qbs14
-rw-r--r--tests/auto/blackbox/testdata/module-conditions/module-conditions.qbs2
-rw-r--r--tests/auto/blackbox/testdata/module-providers/main.cpp6
-rw-r--r--tests/auto/blackbox/testdata/module-providers/module-providers.qbs20
-rw-r--r--tests/auto/blackbox/testdata/msvc-asm-flags/include/header.inc0
-rw-r--r--tests/auto/blackbox/testdata/msvc-asm-flags/msvc-asm-flags.asm8
-rw-r--r--tests/auto/blackbox/testdata/msvc-asm-flags/msvc-asm-flags.qbs6
-rw-r--r--tests/auto/blackbox/testdata/multiplexed-tool/multiplexed-tool.qbs10
-rw-r--r--tests/auto/blackbox/testdata/nested-properties/modules/lowerlevel/lower-level.qbs2
-rw-r--r--tests/auto/blackbox/testdata/no-exported-symbols/no-exported-symbols.qbs2
-rw-r--r--tests/auto/blackbox/testdata/no-such-profile/no-such-profile.qbs2
-rw-r--r--tests/auto/blackbox/testdata/nodejs/hello.qbs8
-rw-r--r--tests/auto/blackbox/testdata/nsis/hello.qbs2
-rw-r--r--tests/auto/blackbox/testdata/nsisDependencies/nsisDependencies.qbs2
-rw-r--r--tests/auto/blackbox/testdata/output-redirection/output-redirection.qbs3
-rw-r--r--tests/auto/blackbox/testdata/overrideProjectProperties/helper_lib.qbs2
-rw-r--r--tests/auto/blackbox/testdata/path-list-in-probe/main.cpp4
-rw-r--r--tests/auto/blackbox/testdata/path-list-in-probe/path-list-in-probe.qbs18
-rw-r--r--tests/auto/blackbox/testdata/path-probe/BaseApp.qbs70
-rw-r--r--tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs6
-rw-r--r--tests/auto/blackbox/testdata/path-probe/environment-paths.qbs10
-rw-r--r--tests/auto/blackbox/testdata/path-probe/mult-files-common-suffixes.qbs10
-rw-r--r--tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs3
-rw-r--r--tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs3
-rw-r--r--tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs1
-rw-r--r--tests/auto/blackbox/testdata/path-probe/mult-files.qbs1
-rw-r--r--tests/auto/blackbox/testdata/path-probe/name-filter.qbs1
-rw-r--r--tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs1
-rw-r--r--tests/auto/blackbox/testdata/path-probe/non-existent.qbs1
-rw-r--r--tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs1
-rw-r--r--tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs1
-rw-r--r--tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs1
-rw-r--r--tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs3
-rw-r--r--tests/auto/blackbox/testdata/path-probe/single-file.qbs1
-rw-r--r--tests/auto/blackbox/testdata/path-probe/usr/bin/tool0
-rw-r--r--tests/auto/blackbox/testdata/plugin-dependency/helper1.cpp4
-rw-r--r--tests/auto/blackbox/testdata/plugin-dependency/helper2.cpp4
-rw-r--r--tests/auto/blackbox/testdata/plugin-dependency/plugin-dependency.qbs8
-rw-r--r--tests/auto/blackbox/testdata/plugin-dependency/plugin1.cpp4
-rw-r--r--tests/auto/blackbox/testdata/plugin-dependency/plugin2.cpp4
-rw-r--r--tests/auto/blackbox/testdata/plugin-dependency/plugin3.cpp4
-rw-r--r--tests/auto/blackbox/testdata/plugin-dependency/plugin4.cpp4
-rw-r--r--tests/auto/blackbox/testdata/precompiled-headers-and-redefine/precompiled-headers-and-redefine.qbs2
-rw-r--r--tests/auto/blackbox/testdata/probe-in-exported-module/modules/depmodule/depmodule.qbs2
-rw-r--r--tests/auto/blackbox/testdata/probe-in-exported-module/modules/mymodule/mymodule.qbs2
-rw-r--r--tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs51
-rw-r--r--tests/auto/blackbox/testdata/probes-and-array-properties/modules/mymodule/mymodule.qbs3
-rw-r--r--tests/auto/blackbox/testdata/product-dependencies-by-type/product-dependencies-by-type.qbs4
-rw-r--r--tests/auto/blackbox/testdata/product-in-exported-module/modules/m/m.qbs3
-rw-r--r--tests/auto/blackbox/testdata/product-in-exported-module/product-in-exported-module.qbs10
-rw-r--r--tests/auto/blackbox/testdata/productproperties/header.qbs2
-rw-r--r--tests/auto/blackbox/testdata/proper quoting/main.cpp10
-rw-r--r--tests/auto/blackbox/testdata/proper quoting/my static lib.cpp5
-rw-r--r--tests/auto/blackbox/testdata/proper quoting/proper quoting.qbs10
-rw-r--r--tests/auto/blackbox/testdata/property-assignment-on-non-present-module/property-assignment-on-non-present-module.qbs4
-rw-r--r--tests/auto/blackbox/testdata/property-evaluation-context/modules/base/base.qbs4
-rw-r--r--tests/auto/blackbox/testdata/property-evaluation-context/modules/top/top.qbs6
-rw-r--r--tests/auto/blackbox/testdata/property-evaluation-context/property-evaluation-context.qbs34
-rw-r--r--tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs2
-rw-r--r--tests/auto/blackbox/testdata/propertyChanges/propertyChanges.qbs4
-rw-r--r--tests/auto/blackbox/testdata/protobuf-library-install/hello.proto8
-rw-r--r--tests/auto/blackbox/testdata/protobuf-library-install/hello/world.proto6
-rw-r--r--tests/auto/blackbox/testdata/protobuf-library-install/protobuf-library.qbs34
-rw-r--r--tests/auto/blackbox/testdata/protobuf/addressbook_cpp.qbs12
-rw-r--r--tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.options3
-rw-r--r--tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.proto38
-rw-r--r--tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.qbs29
-rw-r--r--tests/auto/blackbox/testdata/protobuf/addressbook_objc.qbs10
-rw-r--r--tests/auto/blackbox/testdata/protobuf/create-proto-library.qbs53
-rw-r--r--tests/auto/blackbox/testdata/protobuf/import-main.cpp4
-rw-r--r--tests/auto/blackbox/testdata/protobuf/import.qbs12
-rw-r--r--tests/auto/blackbox/testdata/protobuf/main.cpp4
-rw-r--r--tests/auto/blackbox/testdata/protobuf/main.m4
-rw-r--r--tests/auto/blackbox/testdata/protobuf/main_nanopb.cpp61
-rw-r--r--tests/auto/blackbox/testdata/protobuf/needs-import-dir-main.cpp4
-rw-r--r--tests/auto/blackbox/testdata/protobuf/needs-import-dir.qbs12
-rw-r--r--tests/auto/blackbox/testdata/qbs-config-import-export/config.json11
-rw-r--r--tests/auto/blackbox/testdata/qbs-config-import-export/config.txt4
-rw-r--r--tests/auto/blackbox/testdata/recursive_wildcards/recursive_wildcards.qbs2
-rw-r--r--tests/auto/blackbox/testdata/remove-duplicate-libs/remove-duplicate-libs.qbs4
-rw-r--r--tests/auto/blackbox/testdata/reproducible-build/reproducible-build.qbs5
-rw-r--r--tests/auto/blackbox/testdata/require-deprecated/blubb.js13
-rw-r--r--tests/auto/blackbox/testdata/require-deprecated/require.qbs21
-rw-r--r--tests/auto/blackbox/testdata/require-deprecated/zort.js11
-rw-r--r--tests/auto/blackbox/testdata/response-files/response-files.qbs10
-rw-r--r--tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication-lib.cpp3
-rw-r--r--tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication-main.cpp5
-rw-r--r--tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication.qbs47
-rw-r--r--tests/auto/blackbox/testdata/rule-with-non-required-inputs/rule-with-non-required-inputs.qbs2
-rw-r--r--tests/auto/blackbox/testdata/run-multiplexed/main.cpp1
-rw-r--r--tests/auto/blackbox/testdata/run-multiplexed/run-multiplexed.qbs21
-rw-r--r--tests/auto/blackbox/testdata/sanitizer/sanitizer.cpp4
-rw-r--r--tests/auto/blackbox/testdata/sanitizer/sanitizer.qbs44
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.h1
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.qbs4
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/main.cpp3
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-non-dependency/lib/lib.h3
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-non-dependency/other/other.qbs24
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-non-dependency/p.qbs6
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.h1
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.qbs4
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-other-product/app/main.cpp3
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.h3
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.qbs7
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-other-product/other/other.qbs24
-rw-r--r--tests/auto/blackbox/testdata/scan-result-in-other-product/p.qbs7
-rw-r--r--tests/auto/blackbox/testdata/separate-debug-info/separate-debug-info.qbs30
-rw-r--r--tests/auto/blackbox/testdata/setup-run-environment/setup-run-environment.qbs25
-rw-r--r--tests/auto/blackbox/testdata/smart-relinking/smart-relinking.qbs2
-rw-r--r--tests/auto/blackbox/testdata/source-artifact-changes/source-artifact-changes.qbs4
-rw-r--r--tests/auto/blackbox/testdata/static-lib-without-sources/static-lib-without-sources.qbs2
-rw-r--r--tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs2
-rw-r--r--tests/auto/blackbox/testdata/symbolLinkMode/lib.cpp4
-rw-r--r--tests/auto/blackbox/testdata/symbolLinkMode/main.cpp8
-rw-r--r--tests/auto/blackbox/testdata/symbolLinkMode/symbolLinkMode.qbs21
-rw-r--r--tests/auto/blackbox/testdata/system-include-paths/main.cpp2
-rw-r--r--tests/auto/blackbox/testdata/system-run-paths/system-run-paths.qbs5
-rw-r--r--tests/auto/blackbox/testdata/trackAddFile/after/main.cpp2
-rw-r--r--tests/auto/blackbox/testdata/trackAddFile/after/trackAddFile.qbs8
-rw-r--r--tests/auto/blackbox/testdata/trackAddFile/after/zort.cpp2
-rw-r--r--tests/auto/blackbox/testdata/trackAddFile/before/main.cpp2
-rw-r--r--tests/auto/blackbox/testdata/trackAddFile/before/narf.cpp2
-rw-r--r--tests/auto/blackbox/testdata/trackAddFile/before/trackAddFile.qbs8
-rw-r--r--tests/auto/blackbox/testdata/trackExternalProductChanges/trackExternalProductChanges.qbs5
-rw-r--r--tests/auto/blackbox/testdata/trackFileTags/after/main.cpp2
-rw-r--r--tests/auto/blackbox/testdata/trackFileTags/after/trackFileTags.qbs8
-rw-r--r--tests/auto/blackbox/testdata/trackFileTags/before/main.cpp2
-rw-r--r--tests/auto/blackbox/testdata/trackFileTags/before/trackFileTags.qbs8
-rw-r--r--tests/auto/blackbox/testdata/trackProducts/after/zoo.cpp2
-rw-r--r--tests/auto/blackbox/testdata/trackProducts/before/bar.cpp2
-rw-r--r--tests/auto/blackbox/testdata/trackProducts/before/foo.cpp2
-rw-r--r--tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/a/a.qbs5
-rw-r--r--tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/b/b.qbs3
-rw-r--r--tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/c/c.qbs3
-rw-r--r--tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/d/d.qbs4
-rw-r--r--tests/auto/blackbox/testdata/transitive-invalid-dependencies/transitive-invalid-dependencies.qbs11
-rw-r--r--tests/auto/blackbox/testdata/undefined-target-platform/undefined-target-platform.qbs13
-rw-r--r--tests/auto/blackbox/testdata/variant-suffix/variant-suffix.qbs8
-rw-r--r--tests/auto/blackbox/testdata/vcs/vcstest.qbs8
-rw-r--r--tests/auto/blackbox/testdata/versionscript/versionscript.qbs6
-rw-r--r--tests/auto/blackbox/testdata/whole-archive/whole-archive.qbs3
-rw-r--r--tests/auto/blackbox/testdata/wildcards-and-change-tracking/nonrecursive/subdir1/file.txt0
-rw-r--r--tests/auto/blackbox/testdata/wildcards-and-change-tracking/nonrecursive/subdir2/file.txt0
-rw-r--r--tests/auto/blackbox/testdata/wildcards-and-change-tracking/recursive1/recursive.txt0
-rw-r--r--tests/auto/blackbox/testdata/wildcards-and-change-tracking/recursive2/recursive.txt0
-rw-r--r--tests/auto/blackbox/testdata/wildcards-and-change-tracking/wildcards-and-change-tracking.qbs16
-rw-r--r--tests/auto/blackbox/testdata/wildcards-and-rules/wildcards-and-rules.qbs2
-rw-r--r--tests/auto/blackbox/testdata/wix/WiXInstallers.qbs38
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp2185
-rw-r--r--tests/auto/blackbox/tst_blackbox.h57
-rw-r--r--tests/auto/blackbox/tst_blackboxandroid.cpp1053
-rw-r--r--tests/auto/blackbox/tst_blackboxapple.cpp740
-rw-r--r--tests/auto/blackbox/tst_blackboxapple.h12
-rw-r--r--tests/auto/blackbox/tst_blackboxbaremetal.cpp311
-rw-r--r--tests/auto/blackbox/tst_blackboxbaremetal.h77
-rw-r--r--tests/auto/blackbox/tst_blackboxbase.cpp54
-rw-r--r--tests/auto/blackbox/tst_blackboxbase.h23
-rw-r--r--tests/auto/blackbox/tst_blackboxexamples.cpp104
-rw-r--r--tests/auto/blackbox/tst_blackboxexamples.h53
-rw-r--r--tests/auto/blackbox/tst_blackboxjava.cpp21
-rw-r--r--tests/auto/blackbox/tst_blackboxjava.h3
-rw-r--r--tests/auto/blackbox/tst_blackboxjoblimits.cpp15
-rw-r--r--tests/auto/blackbox/tst_blackboxproviders.cpp377
-rw-r--r--tests/auto/blackbox/tst_blackboxproviders.h63
-rw-r--r--tests/auto/blackbox/tst_blackboxqt.cpp297
-rw-r--r--tests/auto/blackbox/tst_blackboxqt.h14
-rw-r--r--tests/auto/blackbox/tst_blackboxtutorial.cpp72
-rw-r--r--tests/auto/blackbox/tst_blackboxtutorial.h48
-rw-r--r--tests/auto/blackbox/tst_blackboxwindows.cpp307
-rw-r--r--tests/auto/blackbox/tst_blackboxwindows.h55
-rw-r--r--tests/auto/blackbox/tst_clangdb.cpp12
-rw-r--r--tests/auto/buildgraph/CMakeLists.txt7
-rw-r--r--tests/auto/buildgraph/buildgraph.pro13
-rw-r--r--tests/auto/buildgraph/buildgraph.qbs5
-rw-r--r--tests/auto/buildgraph/tst_buildgraph.cpp3
-rw-r--r--tests/auto/cmdlineparser/CMakeLists.txt23
-rw-r--r--tests/auto/cmdlineparser/cmdlineparser.pro7
-rw-r--r--tests/auto/cmdlineparser/cmdlineparser.qbs2
-rw-r--r--tests/auto/cmdlineparser/tst_cmdlineparser.cpp22
-rw-r--r--tests/auto/language/CMakeLists.txt10
-rw-r--r--tests/auto/language/language.pro25
-rw-r--r--tests/auto/language/language.qbs9
-rw-r--r--tests/auto/language/testdata/ParentWithExport.qbs2
-rw-r--r--tests/auto/language/testdata/additional-product-types.qbs8
-rw-r--r--tests/auto/language/testdata/chained-probes/modules/m/m.qbs4
-rw-r--r--tests/auto/language/testdata/conditionaldepends.qbs8
-rw-r--r--tests/auto/language/testdata/conditionaldepends_base.qbs2
-rw-r--r--tests/auto/language/testdata/dotted-names/dotted-names.qbs2
-rw-r--r--tests/auto/language/testdata/dotted-names/modules/x/y/xy.qbs2
-rw-r--r--tests/auto/language/testdata/duplicate-multiplex-value.qbs6
-rw-r--r--tests/auto/language/testdata/duplicate-multiplex-value2.qbs6
-rw-r--r--tests/auto/language/testdata/erroneous/ambiguous-multiplex-dependency.qbs14
-rw-r--r--tests/auto/language/testdata/erroneous/conflicting_fileTagsFilter.qbs7
-rw-r--r--tests/auto/language/testdata/erroneous/dependency-profile-mismatch-2.qbs17
-rw-r--r--tests/auto/language/testdata/erroneous/dependency-profile-mismatch.qbs14
-rw-r--r--tests/auto/language/testdata/erroneous/dependency_cycle.qbs6
-rw-r--r--tests/auto/language/testdata/erroneous/dependency_cycle2.qbs8
-rw-r--r--tests/auto/language/testdata/erroneous/dependency_cycle3a.qbs11
-rw-r--r--tests/auto/language/testdata/erroneous/duplicate-multiplex-value.qbs8
-rw-r--r--tests/auto/language/testdata/erroneous/duplicate-multiplex-value2.qbs8
-rw-r--r--tests/auto/language/testdata/erroneous/frozen-object-list.qbs17
-rw-r--r--tests/auto/language/testdata/erroneous/frozen-object.qbs18
-rw-r--r--tests/auto/language/testdata/erroneous/invalid_file.qbs2
-rw-r--r--tests/auto/language/testdata/erroneous/missing-colon.qbs6
-rw-r--r--tests/auto/language/testdata/erroneous/missing-js-file.qbs3
-rw-r--r--tests/auto/language/testdata/erroneous/module-property-binding-in-project.qbs3
-rw-r--r--tests/auto/language/testdata/erroneous/modules/missing-js-file-module/missing-js-file-module.qbs3
-rw-r--r--tests/auto/language/testdata/erroneous/modules/missing-js-file-module/missing-js-file.js1
-rw-r--r--tests/auto/language/testdata/erroneous/original-in-export-item.qbs2
-rw-r--r--tests/auto/language/testdata/erroneous/original-in-export-item2.qbs2
-rw-r--r--tests/auto/language/testdata/erroneous/original-in-export-item3.qbs2
-rw-r--r--tests/auto/language/testdata/erroneous/original-in-module-prototype.qbs2
-rw-r--r--tests/auto/language/testdata/erroneous/properties-item-with-invalid-condition.qbs2
-rw-r--r--tests/auto/language/testdata/eval-error-in-non-present-module.qbs2
-rw-r--r--tests/auto/language/testdata/exports.qbs28
-rw-r--r--tests/auto/language/testdata/exports_product.qbs2
-rw-r--r--tests/auto/language/testdata/getNativeSetting.qbs8
-rw-r--r--tests/auto/language/testdata/groupconditions.qbs2
-rw-r--r--tests/auto/language/testdata/inherited-properties-items/imports/DebugName.qbs2
-rw-r--r--tests/auto/language/testdata/inherited-properties-items/imports/ReleaseName.qbs2
-rw-r--r--tests/auto/language/testdata/inherited-properties-items/inherited-properties-items-product.qbs2
-rw-r--r--tests/auto/language/testdata/inherited-properties-items/inherited-properties-items.qbs2
-rw-r--r--tests/auto/language/testdata/invalid-prop-on-non-required-module/invalid-prop-on-non-required-module.qbs19
-rw-r--r--tests/auto/language/testdata/invalid-prop-on-non-required-module/modules/dep/dep.qbs1
-rw-r--r--tests/auto/language/testdata/invalid-prop-on-non-required-module/modules/deploader/deploader.qbs7
-rw-r--r--tests/auto/language/testdata/jsextensions.js8
-rw-r--r--tests/auto/language/testdata/local-profile-as-top-level-profile.qbs7
-rw-r--r--tests/auto/language/testdata/module-depends-on-product.qbs (renamed from tests/auto/language/testdata/erroneous/module-depends-on-product.qbs)0
-rw-r--r--tests/auto/language/testdata/module-name-collisions/complex-collision.qbs4
-rw-r--r--tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/middle1.qbs1
-rw-r--r--tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/suffix1/suffix1.qbs1
-rw-r--r--tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/suffix2/suffix2.qbs1
-rw-r--r--tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle2/suffix/suffix.qbs1
-rw-r--r--tests/auto/language/testdata/module-name-collisions/modules/prefix1/prefix1.qbs2
-rw-r--r--tests/auto/language/testdata/module-name-collisions/modules/prefix1/suffix/suffix.qbs (renamed from tests/auto/language/testdata/erroneous/modules/prefix1/suffix/suffix.qbs)0
-rw-r--r--tests/auto/language/testdata/module-name-collisions/modules/prefix2/prefix2.qbs3
-rw-r--r--tests/auto/language/testdata/module-name-collisions/modules/prefix2/suffix/suffix.qbs2
-rw-r--r--tests/auto/language/testdata/module-name-collisions/no-collision1.qbs4
-rw-r--r--tests/auto/language/testdata/module-name-collisions/no-collision2.qbs4
-rw-r--r--tests/auto/language/testdata/module-name-collisions/simple-collision1.qbs (renamed from tests/auto/language/testdata/erroneous/same-module-prefix1.qbs)0
-rw-r--r--tests/auto/language/testdata/module-name-collisions/simple-collision2.qbs (renamed from tests/auto/language/testdata/erroneous/same-module-prefix2.qbs)0
-rw-r--r--tests/auto/language/testdata/module-parameters/module-parameters.qbs35
-rw-r--r--tests/auto/language/testdata/module-parameters/modules/broken/broken.qbs4
-rw-r--r--tests/auto/language/testdata/module-parameters/modules/higher/higher.qbs4
-rw-r--r--tests/auto/language/testdata/module-parameters/modules/highest/highest.qbs7
-rw-r--r--tests/auto/language/testdata/module-parameters/modules/lower/lower.qbs3
-rw-r--r--tests/auto/language/testdata/modulepropertiesingroups.qbs9
-rw-r--r--tests/auto/language/testdata/modules/broken/broken.qbs2
-rw-r--r--tests/auto/language/testdata/modules/dummy/dummy.qbs2
-rw-r--r--tests/auto/language/testdata/modules/module-with-product-dependency/module-with-product-dependency.qbs (renamed from tests/auto/language/testdata/erroneous/modules/module-with-product-dependency/module-with-product-dependency.qbs)0
-rw-r--r--tests/auto/language/testdata/modules/multiple_backends/backend1.qbs2
-rw-r--r--tests/auto/language/testdata/modules/multiple_backends/backend2.qbs2
-rw-r--r--tests/auto/language/testdata/modules/multiple_backends/backend3.qbs2
-rw-r--r--tests/auto/language/testdata/multiplexed-exports.qbs2
-rw-r--r--tests/auto/language/testdata/probes-and-multiplexing.qbs15
-rw-r--r--tests/auto/language/testdata/profilevaluesandoverriddenvalues.qbs2
-rw-r--r--tests/auto/language/testdata/qbs-properties-in-project-condition.qbs2
-rw-r--r--tests/auto/language/testdata/rfc1034identifier.qbs2
-rw-r--r--tests/auto/language/testdata/subdir/exports-mylib.qbs4
-rw-r--r--tests/auto/language/testdata/subdir2/exports-mylib2.qbs2
-rw-r--r--tests/auto/language/testdata/suppressed-and-non-suppressed-errors.qbs2
-rw-r--r--tests/auto/language/testdata/throw.qbs16
-rw-r--r--tests/auto/language/testdata/use-internal-profile.qbs13
-rw-r--r--tests/auto/language/tst_language.cpp989
-rw-r--r--tests/auto/language/tst_language.h53
-rw-r--r--tests/auto/pkgconfig/CMakeLists.txt8
-rw-r--r--tests/auto/pkgconfig/pkgconfig.qbs19
-rw-r--r--tests/auto/pkgconfig/testdata/base.name.json20
-rw-r--r--tests/auto/pkgconfig/testdata/base.name.pc12
-rw-r--r--tests/auto/pkgconfig/testdata/empty-variable.json21
-rw-r--r--tests/auto/pkgconfig/testdata/empty-variable.pc13
-rw-r--r--tests/auto/pkgconfig/testdata/lib/pkgconfig/prefix.pc13
-rw-r--r--tests/auto/pkgconfig/testdata/non-l-required.json12
-rw-r--r--tests/auto/pkgconfig/testdata/non-l-required.pc5
-rw-r--r--tests/auto/pkgconfig/testdata/private-dep.pc6
-rw-r--r--tests/auto/pkgconfig/testdata/public-dep.pc6
-rw-r--r--tests/auto/pkgconfig/testdata/requires-test.json18
-rw-r--r--tests/auto/pkgconfig/testdata/requires-test.pc8
-rw-r--r--tests/auto/pkgconfig/testdata/simple.json20
-rw-r--r--tests/auto/pkgconfig/testdata/simple.pc12
-rw-r--r--tests/auto/pkgconfig/testdata/special-flags.json30
-rw-r--r--tests/auto/pkgconfig/testdata/special-flags.pc11
-rw-r--r--tests/auto/pkgconfig/testdata/sysroot.json21
-rw-r--r--tests/auto/pkgconfig/testdata/sysroot.pc12
-rw-r--r--tests/auto/pkgconfig/testdata/system.json17
-rw-r--r--tests/auto/pkgconfig/testdata/system.pc10
-rw-r--r--tests/auto/pkgconfig/testdata/tilde.json11
-rw-r--r--tests/auto/pkgconfig/testdata/tilde.pc5
-rw-r--r--tests/auto/pkgconfig/testdata/variables.json17
-rw-r--r--tests/auto/pkgconfig/testdata/variables.pc11
-rw-r--r--tests/auto/pkgconfig/testdata/whitespace.json24
-rw-r--r--tests/auto/pkgconfig/testdata/whitespace.pc11
-rw-r--r--tests/auto/pkgconfig/tst_pkgconfig.cpp266
-rw-r--r--tests/auto/pkgconfig/tst_pkgconfig.h57
-rw-r--r--tests/auto/shared.h57
-rw-r--r--tests/auto/tools/CMakeLists.txt7
-rw-r--r--tests/auto/tools/tools.pro6
-rw-r--r--tests/auto/tools/tools.qbs3
-rw-r--r--tests/auto/tools/tst_tools.cpp74
-rw-r--r--tests/auto/tools/tst_tools.h4
-rw-r--r--tests/benchmarker/CMakeLists.txt18
-rw-r--r--tests/benchmarker/benchmarker.cpp30
-rw-r--r--tests/benchmarker/benchmarker.h9
-rw-r--r--tests/benchmarker/benchmarker.pro20
-rw-r--r--tests/benchmarker/benchmarker.qbs4
-rw-r--r--tests/benchmarker/commandlineparser.cpp6
-rw-r--r--tests/benchmarker/exception.h2
-rw-r--r--tests/benchmarker/runsupport.h2
-rw-r--r--tests/benchmarker/valgrindrunner.cpp30
-rw-r--r--tests/benchmarker/valgrindrunner.h9
-rw-r--r--tests/clang-format-test/CMakeLists.txt4
-rw-r--r--tests/clang-format-test/clang-format-test.qbs5
-rw-r--r--tests/clang-format-test/clang-format.cpp681
-rw-r--r--tests/fuzzy-test/CMakeLists.txt12
-rw-r--r--tests/fuzzy-test/commandlineparser.h2
-rw-r--r--tests/fuzzy-test/fuzzy-test.pro11
-rw-r--r--tests/fuzzy-test/fuzzy-test.qbs4
-rw-r--r--tests/fuzzy-test/fuzzytester.cpp6
-rw-r--r--tests/fuzzy-test/fuzzytester.h2
-rw-r--r--tests/lspclient/CMakeLists.txt8
-rw-r--r--tests/lspclient/lspclient.cpp434
-rw-r--r--tests/lspclient/lspclient.qbs10
-rw-r--r--tests/tests.pro4
-rw-r--r--tests/tests.qbs3
-rw-r--r--tutorial/LICENSE49
-rw-r--r--tutorial/chapter-1/app/app.qbs12
-rw-r--r--tutorial/chapter-1/app/main.c9
-rw-r--r--tutorial/chapter-1/myproject.qbs9
-rw-r--r--tutorial/chapter-2/app/app.qbs11
-rw-r--r--tutorial/chapter-2/app/main.c10
-rw-r--r--tutorial/chapter-2/lib/lib.c15
-rw-r--r--tutorial/chapter-2/lib/lib.h11
-rw-r--r--tutorial/chapter-2/lib/lib.qbs28
-rw-r--r--tutorial/chapter-2/myproject.qbs10
-rw-r--r--tutorial/chapter-3/app/app.qbs29
-rw-r--r--tutorial/chapter-3/app/main.c10
-rw-r--r--tutorial/chapter-3/lib/lib.c16
-rw-r--r--tutorial/chapter-3/lib/lib.h11
-rw-r--r--tutorial/chapter-3/lib/lib.qbs26
-rw-r--r--tutorial/chapter-3/lib/lib_global.h23
-rw-r--r--tutorial/chapter-3/myproject.qbs8
-rw-r--r--tutorial/chapter-4/app/app.qbs10
-rw-r--r--tutorial/chapter-4/app/main.c10
-rw-r--r--tutorial/chapter-4/lib/lib.c10
-rw-r--r--tutorial/chapter-4/lib/lib.h8
-rw-r--r--tutorial/chapter-4/lib/lib.qbs13
-rw-r--r--tutorial/chapter-4/lib/lib_global.h18
-rw-r--r--tutorial/chapter-4/myproject.qbs11
-rw-r--r--tutorial/chapter-4/qbs/imports/MyApplication.qbs23
-rw-r--r--tutorial/chapter-4/qbs/imports/MyLibrary.qbs21
-rw-r--r--tutorial/chapter-5/app/app.qbs6
-rw-r--r--tutorial/chapter-5/app/main.c10
-rw-r--r--tutorial/chapter-5/lib/lib.c10
-rw-r--r--tutorial/chapter-5/lib/lib.h8
-rw-r--r--tutorial/chapter-5/lib/lib.qbs9
-rw-r--r--tutorial/chapter-5/lib/lib_global.h18
-rw-r--r--tutorial/chapter-5/myproject.qbs15
-rw-r--r--tutorial/chapter-5/qbs/imports/MyApplication.qbs20
-rw-r--r--tutorial/chapter-5/qbs/imports/MyAutoTest.qbs7
-rw-r--r--tutorial/chapter-5/qbs/imports/MyLibrary.qbs17
-rw-r--r--tutorial/chapter-5/test/test.c22
-rw-r--r--tutorial/chapter-5/test/test.qbs9
-rw-r--r--tutorial/chapter-6/app/app.qbs6
-rw-r--r--tutorial/chapter-6/app/main.c10
-rw-r--r--tutorial/chapter-6/lib/lib.c10
-rw-r--r--tutorial/chapter-6/lib/lib.h8
-rw-r--r--tutorial/chapter-6/lib/lib.qbs9
-rw-r--r--tutorial/chapter-6/lib/lib_global.h18
-rw-r--r--tutorial/chapter-6/myproject.qbs35
-rw-r--r--tutorial/chapter-6/qbs/imports/MyApplication.qbs23
-rw-r--r--tutorial/chapter-6/qbs/imports/MyAutoTest.qbs3
-rw-r--r--tutorial/chapter-6/qbs/imports/MyLibrary.qbs18
-rw-r--r--tutorial/chapter-6/test/test.c18
-rw-r--r--tutorial/chapter-6/test/test.qbs5
-rw-r--r--tutorial/chapter-7/app/app.qbs6
-rw-r--r--tutorial/chapter-7/app/main.c10
-rw-r--r--tutorial/chapter-7/lib/lib.c10
-rw-r--r--tutorial/chapter-7/lib/lib.h8
-rw-r--r--tutorial/chapter-7/lib/lib.qbs60
-rw-r--r--tutorial/chapter-7/lib/lib_global.h18
-rw-r--r--tutorial/chapter-7/myproject.qbs27
-rw-r--r--tutorial/chapter-7/qbs/imports/MyApplication.qbs13
-rw-r--r--tutorial/chapter-7/qbs/imports/MyAutoTest.qbs3
-rw-r--r--tutorial/chapter-7/qbs/imports/MyLibrary.qbs25
-rw-r--r--tutorial/chapter-7/qbs/modules/mybuildconfig/mybuildconfig.qbs26
-rw-r--r--tutorial/chapter-7/test/test.c18
-rw-r--r--tutorial/chapter-7/test/test.qbs5
-rw-r--r--tutorial/chapter-8/app/app.qbs6
-rw-r--r--tutorial/chapter-8/app/main.c10
-rw-r--r--tutorial/chapter-8/lib/lib.c10
-rw-r--r--tutorial/chapter-8/lib/lib.h8
-rw-r--r--tutorial/chapter-8/lib/lib.qbs10
-rw-r--r--tutorial/chapter-8/lib/lib_global.h25
-rw-r--r--tutorial/chapter-8/myproject.qbs27
-rw-r--r--tutorial/chapter-8/qbs/imports/MyApplication.qbs10
-rw-r--r--tutorial/chapter-8/qbs/imports/MyAutoTest.qbs3
-rw-r--r--tutorial/chapter-8/qbs/imports/MyLibrary.qbs26
-rw-r--r--tutorial/chapter-8/qbs/modules/mybuildconfig/mybuildconfig.qbs25
-rw-r--r--tutorial/chapter-8/test/test.c18
-rw-r--r--tutorial/chapter-8/test/test.qbs5
-rw-r--r--tutorial/chapter-9/app/app.qbs9
-rw-r--r--tutorial/chapter-9/app/main.c14
-rw-r--r--tutorial/chapter-9/lib/lib.c10
-rw-r--r--tutorial/chapter-9/lib/lib.h8
-rw-r--r--tutorial/chapter-9/lib/lib.qbs10
-rw-r--r--tutorial/chapter-9/lib/lib_global.h25
-rw-r--r--tutorial/chapter-9/myproject.qbs32
-rw-r--r--tutorial/chapter-9/qbs/imports/MyApplication.qbs10
-rw-r--r--tutorial/chapter-9/qbs/imports/MyAutoTest.qbs3
-rw-r--r--tutorial/chapter-9/qbs/imports/MyLibrary.qbs26
-rw-r--r--tutorial/chapter-9/qbs/modules/mybuildconfig/mybuildconfig.qbs30
-rw-r--r--tutorial/chapter-9/test/test.c18
-rw-r--r--tutorial/chapter-9/test/test.qbs5
-rw-r--r--tutorial/chapter-9/version-header/version-header.qbs57
-rw-r--r--tutorial/chapter-9/version-header/version.h.in9
-rw-r--r--tutorial/tutorial.qbs5
2229 files changed, 172823 insertions, 41038 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 000000000..d95a1bafe
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,111 @@
+# .clang-format for Qbs
+# based on https://github.com/qt-creator/qt-creator/blob/v12.0.1/.clang-format
+#
+# This is for clang-format >= 5.0.
+#
+# Use ../../tests/clang-format-test/clang-format-test.cpp for documenting problems
+# or testing changes.
+#
+---
+Language: Cpp
+AccessModifierOffset: -4
+AlignAfterOpenBracket: AlwaysBreak
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: DontAlign
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: false
+BinPackParameters: false
+BraceWrapping:
+ AfterClass: true
+ AfterControlStatement: Never
+ AfterEnum: false
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: true
+ AfterUnion: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+ SplitEmptyFunction: false
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: false
+BreakBeforeBinaryOperators: All
+BreakBeforeBraces: Custom
+BreakBeforeInheritanceComma: false
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeComma
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 100
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros:
+ - forever # avoids { wrapped to next line
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IncludeCategories:
+ - Regex: '^<Q.*'
+ Priority: 200
+IncludeIsMainRegex: '(Test)?$'
+IndentCaseLabels: false
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+InsertBraces: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: false
+# Do not add QT_BEGIN_NAMESPACE/QT_END_NAMESPACE as this will indent lines in between.
+MacroBlockBegin: ""
+MacroBlockEnd: ""
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 4
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PenaltyBreakAssignment: 88
+PenaltyBreakBeforeFirstCallParameter: 50
+PenaltyBreakComment: 500
+PenaltyBreakFirstLessLess: 400
+PenaltyBreakString: 600
+PenaltyExcessCharacter: 500
+PenaltyReturnTypeOnItsOwnLine: 300
+PointerAlignment: Right
+ReflowComments: false
+SortIncludes: CaseSensitive
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: true
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: c++17
+TabWidth: 4
+UseTab: Never
diff --git a/.clang-tidy b/.clang-tidy
index 7545ce79d..92e6eeefe 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,208 +1,73 @@
---
-Checks: '-*,bugprone-*,cppcoreguidelines-c-copy-assignment-signature,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-pro-bounds-constant-array-index,cppcoreguidelines-pro-type-cstyle-cast,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-slicing,fuchsia-virtual-inheritance,google-build-explicit-make-pair,google-build-namespaces,google-global-names-in-headers,google-objc-*,google-readability-casting,google-readability-function-size,google-readability-namespace-comments,google-runtime-operator,misc-definitions-in-headers,misc-misplaced-const,misc-new-delete-overloads,misc-non-copyable-objects,misc-redundant-expression,misc-static-assert,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,misc-uniqueptr-reset-release,misc-unused-*,modernize-avoid-bind,modernize-deprecated-headers,modernize-loop-convert,modernize-make-*,modernize-redundant-void-arg,modernize-replace-*,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-emplace,modernize-use-equals-*,modernize-use-noexcept,modernize-use-override,modernize-use-transparent-functors,modernize-use-using,performance-*,readability-avoid-const-params-in-decls,readability-container-size-empty,readability-delete-null-pointer,readability-deleted-default,readability-function-size,readability-identifier-naming,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-*,readability-simplify-boolean-expr,readability-static-definition-in-anonymous-namespace,readability-uniqueptr-delete-release'
-WarningsAsErrors: 'bugprone-*,-bugprone-suspicious-enum-usage,-bugprone-macro-parentheses,-bugprone-narrowing-conversions,modernize-make-unique,modernize-use-emplace,modernize-use-equals-default,modernize-use-override,performance-move-const-arg'
+Checks: >
+ -*,
+ bugprone-*,
+ -bugprone-easily-swappable-parameters,
+ -bugprone-narrowing-conversions,
+ -bugprone-throw-keyword-missing,
+ cppcoreguidelines-interfaces-global-init,
+ cppcoreguidelines-pro-type-member-init,
+ cppcoreguidelines-slicing,
+ fuchsia-virtual-inheritance,
+ google-build-explicit-make-pair,
+ google-build-namespaces,
+ google-global-names-in-headers,
+ google-objc-*,
+ -google-readability-casting,
+ google-readability-namespace-comments,
+ google-runtime-operator,
+ misc-definitions-in-headers,
+ misc-misplaced-const,
+ misc-new-delete-overloads,
+ misc-non-copyable-objects,
+ misc-redundant-expression,
+ misc-static-assert,
+ misc-uniqueptr-reset-release,
+ misc-unused-*,
+ modernize-avoid-bind,
+ modernize-deprecated-headers,
+ modernize-loop-convert,
+ modernize-make-*,
+ modernize-pass-by-value,
+ modernize-redundant-void-arg,
+ modernize-replace-*,
+ modernize-return-braced-init-list,
+ modernize-shrink-to-fit,
+ modernize-unary-static-assert,
+ modernize-use-auto,
+ modernize-use-bool-literals,
+ modernize-use-emplace,
+ modernize-use-equals-*,
+ modernize-use-noexcept,
+ modernize-use-nullptr,
+ modernize-use-override,
+ modernize-use-transparent-functors,
+ modernize-use-using,
+ performance-*,
+ -performance-no-int-to-ptr,
+ readability-avoid-const-params-in-decls,
+ readability-container-size-empty,
+ readability-delete-null-pointer,
+ readability-deleted-default,
+ readability-identifier-naming,
+ readability-misleading-indentation,
+ readability-misplaced-array-index,
+ readability-non-const-parameter,
+ readability-redundant-*,
+ -readability-redundant-member-init,
+ readability-simplify-boolean-expr,
+ readability-static-definition-in-anonymous-namespace,
+ readability-uniqueptr-delete-release
+
+WarningsAsErrors: >
+ bugprone-*,
+ cppcoreguidelines-*,
+ google-*,
+ misc-unused-*,
+ modernize-*,
+ performance-*,
+ readability-*,
+ -readability-container-size-empty
+
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
-CheckOptions:
- - key: bugprone-argument-comment.StrictMode
- value: '0'
- - key: bugprone-assert-side-effect.AssertMacros
- value: assert
- - key: bugprone-assert-side-effect.CheckFunctionCalls
- value: '0'
- - key: bugprone-dangling-handle.HandleClasses
- value: 'std::basic_string_view;std::experimental::basic_string_view'
- - key: bugprone-exception-escape.FunctionsThatShouldNotThrow
- value: ''
- - key: bugprone-exception-escape.IgnoredExceptions
- value: ''
- - key: bugprone-misplaced-widening-cast.CheckImplicitCasts
- value: '0'
- - key: bugprone-sizeof-expression.WarnOnSizeOfCompareToConstant
- value: '1'
- - key: bugprone-sizeof-expression.WarnOnSizeOfConstant
- value: '1'
- - key: bugprone-sizeof-expression.WarnOnSizeOfIntegerExpression
- value: '0'
- - key: bugprone-sizeof-expression.WarnOnSizeOfThis
- value: '1'
- - key: bugprone-string-constructor.LargeLengthThreshold
- value: '8388608'
- - key: bugprone-string-constructor.WarnOnLargeLength
- value: '1'
- - key: bugprone-suspicious-enum-usage.StrictMode
- value: '0'
- - key: bugprone-suspicious-missing-comma.MaxConcatenatedTokens
- value: '5'
- - key: bugprone-suspicious-missing-comma.RatioThreshold
- value: '0.200000'
- - key: bugprone-suspicious-missing-comma.SizeThreshold
- value: '5'
- - key: bugprone-suspicious-string-compare.StringCompareLikeFunctions
- value: ''
- - key: bugprone-suspicious-string-compare.WarnOnImplicitComparison
- value: '1'
- - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison
- value: '0'
- - key: bugprone-unused-return-value.CheckedFunctions
- value: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty'
- - key: cert-dcl16-c.NewSuffixes
- value: 'L;LL;LU;LLU'
- - key: cppcoreguidelines-no-malloc.Allocations
- value: '::malloc;::calloc'
- - key: cppcoreguidelines-no-malloc.Deallocations
- value: '::free'
- - key: cppcoreguidelines-no-malloc.Reallocations
- value: '::realloc'
- - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
- value: '1'
- - key: cppcoreguidelines-pro-bounds-constant-array-index.GslHeader
- value: ''
- - key: cppcoreguidelines-pro-bounds-constant-array-index.IncludeStyle
- value: '0'
- - key: cppcoreguidelines-pro-type-member-init.IgnoreArrays
- value: '0'
- - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
- value: '0'
- - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
- value: '0'
- - key: google-build-namespaces.HeaderFileExtensions
- value: ',h,hh,hpp,hxx'
- - key: google-global-names-in-headers.HeaderFileExtensions
- value: ',h,hh,hpp,hxx'
- - key: google-readability-braces-around-statements.ShortStatementLines
- value: '1'
- - key: google-readability-function-size.BranchThreshold
- value: '4294967295'
- - key: google-readability-function-size.LineThreshold
- value: '4294967295'
- - key: google-readability-function-size.NestingThreshold
- value: '4294967295'
- - key: google-readability-function-size.ParameterThreshold
- value: '4294967295'
- - key: google-readability-function-size.StatementThreshold
- value: '800'
- - key: google-readability-function-size.VariableThreshold
- value: '4294967295'
- - key: google-readability-namespace-comments.ShortNamespaceLines
- value: '10'
- - key: google-readability-namespace-comments.SpacesBeforeComments
- value: '2'
- - key: google-runtime-int.SignedTypePrefix
- value: int
- - key: google-runtime-int.TypeSuffix
- value: ''
- - key: google-runtime-int.UnsignedTypePrefix
- value: uint
- - key: misc-definitions-in-headers.HeaderFileExtensions
- value: ',h,hh,hpp,hxx'
- - key: misc-definitions-in-headers.UseHeaderFileExtension
- value: '1'
- - key: misc-unused-parameters.StrictMode
- value: '0'
- - key: modernize-loop-convert.MaxCopySize
- value: '16'
- - key: modernize-loop-convert.MinConfidence
- value: reasonable
- - key: modernize-loop-convert.NamingStyle
- value: CamelCase
- - key: modernize-make-shared.IgnoreMacros
- value: '1'
- - key: modernize-make-shared.IncludeStyle
- value: '0'
- - key: modernize-make-shared.MakeSmartPtrFunction
- value: 'std::make_shared'
- - key: modernize-make-shared.MakeSmartPtrFunctionHeader
- value: memory
- - key: modernize-make-unique.IgnoreMacros
- value: '1'
- - key: modernize-make-unique.IncludeStyle
- value: '0'
- - key: modernize-make-unique.MakeSmartPtrFunction
- value: 'std::make_unique'
- - key: modernize-make-unique.MakeSmartPtrFunctionHeader
- value: memory
- - key: modernize-pass-by-value.IncludeStyle
- value: llvm
- - key: modernize-pass-by-value.ValuesOnly
- value: '0'
- - key: modernize-raw-string-literal.ReplaceShorterLiterals
- value: '0'
- - key: modernize-replace-auto-ptr.IncludeStyle
- value: llvm
- - key: modernize-replace-random-shuffle.IncludeStyle
- value: llvm
- - key: modernize-use-auto.MinTypeNameLength
- value: '5'
- - key: modernize-use-auto.RemoveStars
- value: '0'
- - key: modernize-use-emplace.ContainersWithPushBack
- value: '::std::vector;::std::list;::std::deque'
- - key: modernize-use-emplace.SmartPointers
- value: '::std::shared_ptr;::std::unique_ptr;::std::auto_ptr;::std::weak_ptr'
- - key: modernize-use-emplace.TupleMakeFunctions
- value: '::std::make_pair;::std::make_tuple'
- - key: modernize-use-emplace.TupleTypes
- value: '::std::pair;::std::tuple'
- - key: modernize-use-equals-default.IgnoreMacros
- value: '1'
- - key: modernize-use-equals-delete.IgnoreMacros
- value: '1'
- - key: modernize-use-noexcept.ReplacementString
- value: ''
- - key: modernize-use-noexcept.UseNoexceptFalse
- value: '1'
- - key: modernize-use-nullptr.NullMacros
- value: 'NULL'
- - key: modernize-use-transparent-functors.SafeMode
- value: '0'
- - key: modernize-use-using.IgnoreMacros
- value: '1'
- - key: performance-faster-string-find.StringLikeClasses
- value: 'std::basic_string'
- - key: performance-for-range-copy.AllowedTypes
- value: ''
- - key: performance-for-range-copy.WarnOnAllAutoCopies
- value: '0'
- - key: performance-inefficient-string-concatenation.StrictMode
- value: '0'
- - key: performance-inefficient-vector-operation.VectorLikeClasses
- value: '::std::vector'
- - key: performance-move-const-arg.CheckTriviallyCopyableMove
- value: '1'
- - key: performance-move-constructor-init.IncludeStyle
- value: llvm
- - key: performance-type-promotion-in-math-fn.IncludeStyle
- value: llvm
- - key: performance-unnecessary-copy-initialization.AllowedTypes
- value: ''
- - key: performance-unnecessary-value-param.AllowedTypes
- value: ''
- - key: performance-unnecessary-value-param.IncludeStyle
- value: llvm
- - key: readability-function-size.BranchThreshold
- value: '4294967295'
- - key: readability-function-size.LineThreshold
- value: '4294967295'
- - key: readability-function-size.NestingThreshold
- value: '4294967295'
- - key: readability-function-size.ParameterThreshold
- value: '4294967295'
- - key: readability-function-size.StatementThreshold
- value: '800'
- - key: readability-function-size.VariableThreshold
- value: '4294967295'
- - key: readability-identifier-naming.IgnoreFailedSplit
- value: '0'
- - key: readability-inconsistent-declaration-parameter-name.IgnoreMacros
- value: '1'
- - key: readability-inconsistent-declaration-parameter-name.Strict
- value: '0'
- - key: readability-redundant-smartptr-get.IgnoreMacros
- value: '1'
- - key: readability-simplify-boolean-expr.ChainedConditionalAssignment
- value: '0'
- - key: readability-simplify-boolean-expr.ChainedConditionalReturn
- value: '0'
-...
-
diff --git a/.github/actions/download-dm/action.yml b/.github/actions/download-dm/action.yml
new file mode 100644
index 000000000..5d24da3e2
--- /dev/null
+++ b/.github/actions/download-dm/action.yml
@@ -0,0 +1,15 @@
+name: 'Download DigitalMars'
+description: 'Downloads DigitalMars'
+inputs:
+ version:
+ description: 'DigitalMars version'
+ required: false
+ default: '8.57'
+runs:
+ using: "composite"
+ steps:
+ - name: Install DigitalMars
+ run: |
+ DM_DIR=$(./scripts/install-dm.sh -d $HOME/dm --version ${{ inputs.version }})
+ (cygpath -w ${DM_DIR} 2>/dev/null || echo ${DM_DIR}) >> ${GITHUB_PATH}
+ shell: bash
diff --git a/.github/actions/download-mingw/action.yml b/.github/actions/download-mingw/action.yml
new file mode 100644
index 000000000..24d196785
--- /dev/null
+++ b/.github/actions/download-mingw/action.yml
@@ -0,0 +1,19 @@
+name: 'Download MinGW'
+description: 'Downloads MinGW'
+inputs:
+ version:
+ description: 'MinGW version'
+ required: false
+ default: '9.0.0'
+ toolchain:
+ description: 'Toolchain'
+ required: false
+ default: 'win64_mingw'
+runs:
+ using: "composite"
+ steps:
+ - name: Install MinGW
+ run: |
+ MINGW_DIR=$(./scripts/install-qt.sh -d $HOME/Qt --version ${{ inputs.version }} --toolchain ${{ inputs.toolchain }} mingw)
+ (cygpath -w ${MINGW_DIR} 2>/dev/null || echo ${MINGW_DIR}) >> ${GITHUB_PATH}
+ shell: bash
diff --git a/.github/actions/download-ow/action.yml b/.github/actions/download-ow/action.yml
new file mode 100644
index 000000000..a93a229cc
--- /dev/null
+++ b/.github/actions/download-ow/action.yml
@@ -0,0 +1,15 @@
+name: 'Download OpenWatcom'
+description: 'Downloads OpenWatcom'
+inputs:
+ version:
+ description: 'OpenWatcom version'
+ required: false
+ default: '2.0'
+runs:
+ using: "composite"
+ steps:
+ - name: Install OpenWatcom
+ run: |
+ OW_DIR=$(./scripts/install-ow.sh -d $HOME/watcom --version ${{ inputs.version }})
+ (cygpath -w ${OW_DIR} 2>/dev/null || echo ${OW_DIR}) >> ${GITHUB_PATH}
+ shell: bash
diff --git a/.github/actions/download-qt/action.yml b/.github/actions/download-qt/action.yml
new file mode 100644
index 000000000..039f29ca6
--- /dev/null
+++ b/.github/actions/download-qt/action.yml
@@ -0,0 +1,22 @@
+name: 'Download Qt'
+description: 'Downloads Qt'
+inputs:
+ version:
+ description: 'Qt version'
+ required: false
+ default: '6.6.0'
+ target:
+ description: 'Qt target (desktop, ios, android)'
+ required: false
+ default: 'desktop'
+ toolchain:
+ description: 'Qt toolchain'
+ required: true
+runs:
+ using: "composite"
+ steps:
+ - name: Install Qt
+ run: |
+ QT_DIR=$(./scripts/install-qt.sh -d $HOME/Qt --version ${{ inputs.version }} --target ${{ inputs.target }} --toolchain ${{ inputs.toolchain }} qtbase qtdeclarative qttools qtscxml qt5compat)
+ (cygpath -w ${QTC_DIR} 2>/dev/null || echo ${QT_DIR}) >> ${GITHUB_PATH}
+ shell: bash
diff --git a/.github/actions/download-qtc/action.yml b/.github/actions/download-qtc/action.yml
new file mode 100644
index 000000000..a04681df4
--- /dev/null
+++ b/.github/actions/download-qtc/action.yml
@@ -0,0 +1,15 @@
+name: 'Download Qt Creator'
+description: 'Downloads Qt Creator'
+inputs:
+ version:
+ description: 'Qt Creator version'
+ required: false
+ default: '11.0.3'
+runs:
+ using: "composite"
+ steps:
+ - name: Install Qt Creator
+ run: |
+ QTC_DIR=$(./scripts/install-qt.sh -d $HOME/Qt --version ${{ inputs.version }} qtcreator)
+ (cygpath -w ${QTC_DIR} 2>/dev/null || echo ${QTC_DIR}) >> ${GITHUB_PATH}
+ shell: bash
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 000000000..d820a03aa
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,38 @@
+name: Build and Qbs docs
+
+on:
+ push:
+ paths:
+ - 'doc/**'
+ - 'examples/**'
+ - 'scripts/build-qbs-doc.sh'
+ - 'VERSION'
+
+jobs:
+ build-docs:
+ name: ${{ matrix.config.name }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {
+ name: 'Build Qbs Docs',
+ options: '',
+ script: './scripts/build-qbs-doc.sh'
+ }
+ env:
+ BUILD_OPTIONS: ${{ matrix.config.options }}
+ WITH_TESTS: 0
+ steps:
+ - uses: actions/checkout@v1
+ - name: Pull the Docker Image
+ run: docker-compose pull jammy
+ - name: Build Qbs Docs
+ run: docker-compose run jammy ${{ matrix.config.script }}
+ - name: Upload artifacts
+ uses: 'actions/upload-artifact@v2'
+ with:
+ name: qbs-docs-${{ github.run_id }}
+ path: documentation/install-root/usr/local/share/doc/qbs/
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 000000000..71c1ae4ac
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,919 @@
+name: Build and test Qbs
+
+on:
+ push:
+ paths-ignore:
+ - 'changelogs/**'
+ - 'doc/**'
+
+jobs:
+ build-linux:
+ name: ${{ matrix.config.name }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {
+ name: 'Build on Linux (Jammy, gcc)',
+ image: 'jammy-qt6',
+ options: 'modules.cpp.compilerWrapper:ccache
+ modules.qbs.debugInformation:true
+ modules.qbsbuildconfig.enableBundledQt:true
+ products.qbs_archive.targetName:qbs-linux-jammy-${{ github.run_id }}
+ products.qbs_archive.includeTests:true',
+ script: './scripts/build-qbs-with-qbs.sh',
+ cacheid: 'gcc-qt6-jammy',
+ suffix: 'linux-jammy',
+ }
+ env:
+ BUILD_OPTIONS: ${{ matrix.config.options }}
+ WITH_TESTS: 0
+ steps:
+ - uses: actions/checkout@v1
+ - name: Create .ccache dir
+ run: mkdir -p ~/.ccache
+ - name: ccache cache files
+ uses: actions/cache@v4
+ with:
+ path: ~/.ccache
+ key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache
+ - name: Pull the Docker Image
+ run: docker-compose pull ${{ matrix.config.image }}
+ - name: Print ccache stats
+ run: docker-compose run ${{ matrix.config.image }} ccache -s
+ - name: Build Qbs
+ run: docker-compose run ${{ matrix.config.image }} ${{ matrix.config.script }}
+ - name: Print ccache stats
+ run: docker-compose run ${{ matrix.config.image }} ccache -s
+ - name: Upload artifacts
+ uses: 'actions/upload-artifact@v4'
+ with:
+ name: qbs-${{ matrix.config.suffix }}-${{ github.run_id }}.tar.gz
+ path: release/qbs-${{ matrix.config.suffix }}-${{ github.run_id }}.tar.gz
+
+ build-linux-extra:
+ name: ${{ matrix.config.name }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 90
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {
+ name: 'Build on Linux (clang_tidy)',
+ script: './scripts/run-analyzer.sh',
+ image: 'jammy-qt6',
+ options: 'profile:qt-clang_64 modules.cpp.compilerWrapper:ccache',
+ cacheid: 'clang',
+ }
+ - {
+ name: 'Build on Linux (CMake)',
+ script: './scripts/build-qbs-with-cmake.sh',
+ image: 'jammy-qt6',
+ cacheid: 'cmake',
+ }
+ - {
+ name: 'Build on Linux (gcc, ASAN)',
+ image: 'jammy-qt6',
+ script: './scripts/build-qbs-with-qbs.sh',
+ options: 'modules.cpp.compilerWrapper:ccache
+ modules.qbsbuildconfig.enableAddressSanitizer:true
+ modules.qbs.debugInformation:true
+ modules.qbsbuildconfig.enableBundledQt:true',
+ cacheid: 'gcc-asan',
+ }
+ - {
+ name: 'Build on Linux (gcc, UBSAN)',
+ image: 'jammy-qt6',
+ script: './scripts/build-qbs-with-qbs.sh',
+ options: 'modules.cpp.compilerWrapper:ccache
+ modules.qbsbuildconfig.enableUbSanitizer:true
+ modules.qbs.debugInformation:true
+ modules.qbsbuildconfig.enableBundledQt:true',
+ cacheid: 'gcc-ubsan',
+ }
+ - {
+ name: 'Build on Linux (gcc, Qt 5.15)',
+ image: 'jammy-qt5',
+ script: './scripts/build-qbs-with-qbs.sh',
+ options: 'modules.cpp.compilerWrapper:ccache
+ modules.qbs.debugInformation:true
+ modules.qbsbuildconfig.enableBundledQt:true',
+ cacheid: 'gcc-qt5',
+ }
+ env:
+ BUILD_OPTIONS: ${{ matrix.config.options }}
+ QTEST_FUNCTION_TIMEOUT: 9000000
+ steps:
+ - uses: actions/checkout@v1
+ - name: Create .ccache dir
+ run: mkdir -p ~/.ccache
+ - name: ccache cache files
+ uses: actions/cache@v4
+ with:
+ path: ~/.ccache
+ key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache
+ - name: Pull the Docker Image
+ run: docker-compose pull ${{ matrix.config.image }}
+ - name: Print ccache stats
+ run: docker-compose run ${{ matrix.config.image }} ccache -s
+ - name: Build Qbs
+ run: docker-compose run ${{ matrix.config.image }} ${{ matrix.config.script }}
+ - name: Print ccache stats
+ run: docker-compose run ${{ matrix.config.image }} ccache -s
+
+ clang-format:
+ name: Check Style
+ runs-on: macos-13
+ timeout-minutes: 60
+ strategy:
+ fail-fast: false
+ steps:
+ - uses: actions/checkout@v1
+ - name: Update PATH
+ run: echo "$(brew --prefix llvm@15)/bin" >> ${GITHUB_PATH}
+ - name: Run clang-format
+ run: |
+ git diff -U0 --no-color --relative HEAD^ | $(brew --prefix llvm@15)/share/clang/clang-format-diff.py -i -p1 -regex ".*\.(cxx|cpp|hpp|h)" -v
+ - name: Check diff
+ run: git diff --exit-code
+
+ build-macos:
+ name: Build on macOS
+ runs-on: macos-12
+ timeout-minutes: 60
+ env:
+ BUILD_OPTIONS: |
+ modules.cpp.compilerWrapper:ccache
+ modules.qbs.debugInformation:true
+ modules.qbsbuildconfig.enableAddressSanitizer:false
+ modules.qbsbuildconfig.enableBundledQt:true
+ products.qbs_archive.targetName:qbs-macos-${{ github.run_id }}
+ products.qbs_archive.includeTests:true
+ WITH_TESTS: 0
+ steps:
+ - uses: actions/checkout@v1
+ - name: Create .ccache dir
+ run: mkdir -p ~/.ccache
+ - name: ccache cache files
+ uses: actions/cache@v4
+ with:
+ path: ~/.ccache
+ key: ${{ runner.os }}-ccache
+ - name: Install required packages
+ run: |
+ brew install ccache p7zip
+ python3 -m pip install --user beautifulsoup4 lxml
+ - name: Install Qt
+ uses: ./.github/actions/download-qt
+ with:
+ toolchain: clang_64
+ - name: Install Qt Creator
+ uses: ./.github/actions/download-qtc
+ - name: Setup Qbs
+ run: |
+ qbs setup-toolchains --detect
+ qbs setup-qt --detect
+ qbs config profiles.qt.baseProfile xcode_13_2_1-macosx-x86_64
+ qbs config defaultProfile qt
+ qbs config --list
+ - name: Print ccache stats
+ run: ccache -s
+ - name: Build Qbs
+ run: scripts/build-qbs-with-qbs.sh
+ - name: Print ccache stats
+ run: ccache -s
+ - name: Upload artifacts
+ uses: 'actions/upload-artifact@v4'
+ with:
+ name: qbs-macos-${{ github.run_id }}.tar.gz
+ path: release/qbs-macos-${{ github.run_id }}.tar.gz
+
+ build-windows:
+ name: Build on Windows
+ runs-on: windows-2022
+ timeout-minutes: 60
+ env:
+ BUILD_OPTIONS: |
+ modules.cpp.compilerWrapper:clcache
+ modules.qbs.debugInformation:true
+ modules.qbsbuildconfig.enableAddressSanitizer:false
+ modules.qbsbuildconfig.enableBundledQt:true
+ products.qbs_archive.targetName:qbs-windows-${{ github.run_id }}
+ products.qbs_archive.includeTests:true,
+ WITH_TESTS: 0
+ QT_ASSUME_STDERR_HAS_CONSOLE: 1
+ steps:
+ - uses: actions/checkout@v1
+ - name: clcache cache files
+ uses: actions/cache@v4
+ with:
+ path: ~/clcache
+ key: ${{ runner.os }}-msvc-clcache
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: 3.8
+ - name: Install required packages
+ run: |
+ python -m pip install --upgrade pip
+ pip install beautifulsoup4 lxml
+ pip install git+https://github.com/frerich/clcache.git@cae73d8255d78db8ba11e23c51fd2c9a89e7475b
+ - name: Install Qt
+ uses: ./.github/actions/download-qt
+ with:
+ toolchain: win64_msvc2019_64
+ - name: Install Qt Creator
+ uses: ./.github/actions/download-qtc
+ - name: Setup Qbs
+ run: |
+ qbs setup-toolchains --detect
+ qbs setup-qt $(which qmake).exe qt
+ qbs config profiles.qt.baseProfile MSVC2022-1-x86_x64
+ qbs config defaultProfile qt
+ qbs config --list
+ shell: bash
+ - name: Print clcache stats
+ run: clcache -s
+ - name: Build Qbs
+ run: scripts/build-qbs-with-qbs.sh
+ shell: bash
+ - name: Print clcache stats
+ run: clcache -s
+ - name: Upload artifacts
+ uses: 'actions/upload-artifact@v4'
+ with:
+ name: qbs-windows-${{ github.run_id }}.zip
+ path: release/qbs-windows-${{ github.run_id }}.zip
+
+ build-windows-mingw:
+ name: Build on Windows (MinGW)
+ runs-on: windows-2022
+ timeout-minutes: 60
+ env:
+ BUILD_OPTIONS: |
+ modules.cpp.compilerWrapper:ccache
+ modules.qbsbuildconfig.enableAddressSanitizer:false
+ modules.qbsbuildconfig.enableBundledQt:true
+ products.qbs_archive.targetName:qbs-windows-mingw-${{ github.run_id }}
+ products.qbs_archive.includeTests:true,
+ QT_ASSUME_STDERR_HAS_CONSOLE: 1
+ WITH_TESTS: 0
+ CCACHE_DIR: ${{ github.workspace }}\ccache
+ steps:
+ - uses: actions/checkout@v1
+ - name: ccache cache files
+ uses: actions/cache@v4
+ with:
+ path: ${{ github.workspace }}/ccache
+ key: ${{ runner.os }}-mingw-ccache
+ - name: Set up Python ${{ matrix.python-version }}
+ uses: actions/setup-python@v5
+ with:
+ python-version: 3.8
+ - name: Install required packages
+ run: |
+ choco install ccache
+ python -m pip install --upgrade pip
+ pip install beautifulsoup4 lxml
+ - name: Install Qt
+ uses: ./.github/actions/download-qt
+ with:
+ toolchain: win64_mingw
+ - name: Install Qt Creator
+ uses: ./.github/actions/download-qtc
+ - name: Install MinGW
+ uses: ./.github/actions/download-mingw
+ - name: Setup Qbs
+ run: |
+ qbs setup-toolchains --type mingw $(which g++).exe mingw-qt
+ qbs setup-qt $(which qmake).exe qt
+ qbs config profiles.qt.baseProfile mingw-qt
+ qbs config defaultProfile qt
+ qbs config --list
+ shell: bash
+ - name: Print ccache stats
+ run: ccache -s
+ - name: Build Qbs
+ run: scripts/build-qbs-with-qbs.sh
+ shell: bash
+ - name: Print ccache stats
+ run: ccache -s
+ - name: Upload artifacts
+ uses: 'actions/upload-artifact@v4'
+ with:
+ name: qbs-windows-mingw-${{ github.run_id }}.zip
+ path: release/qbs-windows-mingw-${{ github.run_id }}.zip
+
+ test-linux:
+ name: ${{ matrix.config.name }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 90
+ needs: build-linux
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {
+ name: 'Run Linux tests (gcc, Qt 6)',
+ image: 'jammy-qt6',
+ suffix: 'linux-jammy',
+ profile: 'qt-gcc_64',
+ script: './scripts/test-qbs.sh',
+ }
+ - {
+ name: 'Run Linux tests (clang, Qt 6)',
+ image: 'jammy-qt6',
+ suffix: 'linux-jammy',
+ profile: 'qt-clang_64',
+ script: './scripts/test-qbs.sh',
+ }
+ - {
+ name: 'Run Linux tests (gcc, Qt 5)',
+ image: 'jammy-qt5',
+ suffix: 'linux-jammy',
+ profile: 'qt-gcc_64',
+ script: './scripts/test-qt.sh',
+ }
+ - {
+ name: 'Run Linux tests (gcc, Qt 6.5 static)',
+ image: 'jammy-qt6-static',
+ suffix: 'linux-jammy',
+ profile: 'qt-gcc_64',
+ script: './scripts/test-qt.sh',
+ }
+ - {
+ name: 'Run Android tests (Qt 5.15)',
+ image: 'jammy-android-515',
+ suffix: 'linux-jammy',
+ profile: '',
+ script: './scripts/test-qt-for-android.sh',
+ }
+ - {
+ name: 'Run Android tests (Qt 6.2)',
+ image: 'jammy-android-62',
+ suffix: 'linux-jammy',
+ profile: '',
+ script: './scripts/test-qt-for-android.sh',
+ }
+ - {
+ name: 'Run Android tests (Qt 6.5)',
+ image: 'jammy-android-65',
+ suffix: 'linux-jammy',
+ profile: '',
+ script: './scripts/test-qt-for-android.sh',
+ }
+ - {
+ name: 'Run Android tests (ndk r19c)',
+ image: 'jammy-android-ndk-r19c',
+ suffix: 'linux-jammy',
+ profile: '',
+ script: './scripts/test-for-android.sh',
+ }
+ - {
+ name: 'Run Android tests (ndk r21e)',
+ image: 'jammy-android-ndk-r21e',
+ suffix: 'linux-jammy',
+ profile: '',
+ script: './scripts/test-for-android.sh',
+ }
+ - {
+ name: 'Run Android tests (ndk r23)',
+ image: 'jammy-android-ndk-r23',
+ suffix: 'linux-jammy',
+ profile: '',
+ script: './scripts/test-for-android.sh',
+ }
+ - {
+ name: 'Run Linux tests (Qt 4.8.7)',
+ image: 'jammy-qt4',
+ suffix: 'linux-jammy',
+ profile: '',
+ script: './scripts/test-qt4.sh',
+ }
+
+ env:
+ QBS_TEST_SOURCE_ROOT: 'tests'
+ QBS_AUTOTEST_PROFILE: ${{ matrix.config.profile }}
+ QTEST_FUNCTION_TIMEOUT: 9000000
+ steps:
+ - uses: actions/checkout@v1
+ - name: Download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: qbs-${{ matrix.config.suffix }}-${{ github.run_id }}.tar.gz
+ path: ./
+ - name: Unpack artifact
+ run: mkdir -p release/install-root/ && tar xzf qbs-${{ matrix.config.suffix }}-${{ github.run_id }}.tar.gz -C release/install-root/
+ - name: Pull the Docker Image
+ run: docker-compose pull ${{ matrix.config.image }}
+ - name: Run tests
+ run: docker-compose run ${{ matrix.config.image }} ${{ matrix.config.script }} release/install-root/usr/local/bin
+
+ test-baremetal:
+ name: Run Baremetal tests (Linux)
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ needs: build-linux
+ env:
+ QBS_TEST_SOURCE_ROOT: 'tests'
+ steps:
+ - uses: actions/checkout@v1
+ - name: Download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: qbs-linux-jammy-${{ github.run_id }}.tar.gz
+ path: ./
+ - name: Unpack artifact
+ run: mkdir -p release/install-root/ && tar xzf qbs-linux-jammy-${{ github.run_id }}.tar.gz -C release/install-root/
+ - name: Pull the Docker Image
+ run: docker-compose pull jammy-baremetal
+ - name: arm-none-eabi-gcc-10_3
+ run: QBS_AUTOTEST_PROFILE=arm-none-eabi-gcc-10_3 docker-compose run jammy-baremetal scripts/test-baremetal.sh release/install-root/usr/local/bin
+ - name: avr-gcc-5_4
+ run: QBS_AUTOTEST_PROFILE=avr-gcc-5_4 docker-compose run jammy-baremetal scripts/test-baremetal.sh release/install-root/usr/local/bin
+ - name: msp430-gcc-4_6
+ run: QBS_AUTOTEST_PROFILE=msp430-gcc-4_6 docker-compose run jammy-baremetal scripts/test-baremetal.sh release/install-root/usr/local/bin
+ - name: riscv64-unknown-elf-gcc-10_2
+ run: QBS_AUTOTEST_PROFILE=riscv64-unknown-elf-gcc-10_2 docker-compose run jammy-baremetal scripts/test-baremetal.sh release/install-root/usr/local/bin
+ - name: sdcc-4_0_0-hcs8
+ run: QBS_AUTOTEST_PROFILE=sdcc-4_0_0-hcs8 docker-compose run jammy-baremetal scripts/test-baremetal.sh release/install-root/usr/local/bin
+ - name: sdcc-4_0_0-mcs51
+ run: QBS_AUTOTEST_PROFILE=sdcc-4_0_0-mcs51 docker-compose run jammy-baremetal scripts/test-baremetal.sh release/install-root/usr/local/bin
+ - name: sdcc-4_0_0-stm8
+ run: QBS_AUTOTEST_PROFILE=sdcc-4_0_0-stm8 docker-compose run jammy-baremetal scripts/test-baremetal.sh release/install-root/usr/local/bin
+ - name: xtensa-lx106-elf-gcc-10_3
+ run: QBS_AUTOTEST_PROFILE=xtensa-lx106-elf-gcc-10_3 docker-compose run jammy-baremetal scripts/test-baremetal.sh release/install-root/usr/local/bin
+
+ test-baremetal-windows:
+ name: Run Baremetal tests (Windows)
+ runs-on: [self-hosted, windows, x64]
+ timeout-minutes: 30
+ needs: build-windows
+ # baremetal VM is down, disable the workflow for now
+ if: 'false'
+ env:
+ QBS_TEST_SOURCE_ROOT: 'tests'
+ QT_ASSUME_STDERR_HAS_CONSOLE: 1
+ QTEST_FUNCTION_TIMEOUT: 9000000
+ steps:
+ - uses: actions/checkout@v1
+ - name: Download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: qbs-windows-${{ github.run_id }}.zip
+ path: ./
+ - name: Unpack artifact
+ run: mkdir -p release/install-root/ && unzip qbs-windows-${{ github.run_id }}.zip -d release/install-root/
+ shell: bash
+ - name: keil-9_53_0-mcs51
+ run: QBS_AUTOTEST_PROFILE=keil-9_53_0-mcs51 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: keil-5_60_0-mcs251
+ run: QBS_AUTOTEST_PROFILE=keil-5_60_0-mcs251 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: keil-7_57_0-c166
+ run: QBS_AUTOTEST_PROFILE=keil-7_57_0-c166 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: keil-5_30_0-arm
+ run: QBS_AUTOTEST_PROFILE=keil-5_30_0-arm scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: keil-llvm-5_30_0-arm
+ run: QBS_AUTOTEST_PROFILE=keil-5_30_0-arm scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-8_50_5-arm
+ run: QBS_AUTOTEST_PROFILE=iar-8_50_5-arm scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-10_30_1-mcs51
+ run: QBS_AUTOTEST_PROFILE=iar-10_30_1-mcs51 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-7_30_1-avr
+ run: QBS_AUTOTEST_PROFILE=iar-7_30_1-avr scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-4_30_2-avr32
+ run: QBS_AUTOTEST_PROFILE=iar-4_30_2-avr32 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-7_20_1-msp430
+ run: QBS_AUTOTEST_PROFILE=iar-7_20_1-msp430 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-3_11_2_234-stm8
+ run: QBS_AUTOTEST_PROFILE=iar-3_11_2_234-stm8 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-4_20_1-rx
+ run: QBS_AUTOTEST_PROFILE=iar-4_20_1-rx scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-4_20_1-rl78
+ run: QBS_AUTOTEST_PROFILE=iar-4_20_1-rl78 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-5_10_1-v850
+ run: QBS_AUTOTEST_PROFILE=iar-5_10_1-v850 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-2_21_1-rh850
+ run: QBS_AUTOTEST_PROFILE=iar-2_21_1-rh850 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-2_30_1-sh
+ run: QBS_AUTOTEST_PROFILE=iar-2_30_1-sh scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-4_81_1-78k
+ run: QBS_AUTOTEST_PROFILE=iar-4_81_1-78k scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-4_10_1-hcs12
+ run: QBS_AUTOTEST_PROFILE=iar-4_10_1-hcs12 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-1_40_2-r32c
+ run: QBS_AUTOTEST_PROFILE=iar-1_40_2-r32c scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-3_71_1-m16c
+ run: QBS_AUTOTEST_PROFILE=iar-3_71_1-m16c scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-1_21_1-riscv
+ run: QBS_AUTOTEST_PROFILE=iar-1_21_1-riscv scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: iar-3_30_1-cr16
+ run: QBS_AUTOTEST_PROFILE=iar-3_30_1-cr16 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: sdcc-4_0_0-mcs51
+ run: QBS_AUTOTEST_PROFILE=sdcc-4_0_0-mcs51 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: sdcc-4_0_0-stm8
+ run: QBS_AUTOTEST_PROFILE=sdcc-4_0_0-stm8 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: sdcc-4_0_0-hcs8
+ run: QBS_AUTOTEST_PROFILE=sdcc-4_0_0-hcs8 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: avr-gcc-4_6_2
+ run: QBS_AUTOTEST_PROFILE=avr-gcc-4_6_2 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: avr32-gcc-4_4_3
+ run: QBS_AUTOTEST_PROFILE=avr32-gcc-4_4_3 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: m32c-elf-gcc-4_7_3
+ run: QBS_AUTOTEST_PROFILE=m32c-elf-gcc-4_7_3 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: m32r-elf-gcc-4_8_0
+ run: QBS_AUTOTEST_PROFILE=m32r-elf-gcc-4_8_0 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: m68k-elf-gcc-4_8_0
+ run: QBS_AUTOTEST_PROFILE=m68k-elf-gcc-4_8_0 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: msp430-elf-gcc-8_3_1
+ run: QBS_AUTOTEST_PROFILE=msp430-elf-gcc-8_3_1 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: riscv64-unknown-elf-gcc-10_1_0
+ run: QBS_AUTOTEST_PROFILE=riscv64-unknown-elf-gcc-10_1_0 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: v850-elf-gcc-4_7_2
+ run: QBS_AUTOTEST_PROFILE=v850-elf-gcc-4_7_2 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: arm-none-eabi-gcc-9_3_1
+ run: QBS_AUTOTEST_PROFILE=arm-none-eabi-gcc-9_3_1 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: xtensa-lx106-elf-gcc-5_2_0
+ run: QBS_AUTOTEST_PROFILE=xtensa-lx106-elf-gcc-5_2_0 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: xtensa-esp32-elf-gcc-8_4_0
+ run: QBS_AUTOTEST_PROFILE=xtensa-esp32-elf-gcc-8_4_0 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: rl78-elf-gcc-4_9_2_202002-GNURL78
+ run: QBS_AUTOTEST_PROFILE=rl78-elf-gcc-4_9_2_202002-GNURL78 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: rx-elf-gcc-8_3_0_202004-GNURX
+ run: QBS_AUTOTEST_PROFILE=rx-elf-gcc-8_3_0_202004-GNURX scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: cosmic-4_3_11-arm
+ run: QBS_AUTOTEST_PROFILE=cosmic-4_3_11-arm scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: cosmic-4_5_2-stm8
+ run: QBS_AUTOTEST_PROFILE=cosmic-4_5_2-stm8 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: cosmic-4_6_3-hcs8
+ run: QBS_AUTOTEST_PROFILE=cosmic-4_6_3-hcs8 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: cosmic-4_8_11-hcs12
+ run: QBS_AUTOTEST_PROFILE=cosmic-4_8_11-hcs12 scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+ - name: cosmic-4_1_0-m68k
+ run: QBS_AUTOTEST_PROFILE=cosmic-4_1_0-m68k scripts/test-baremetal.sh release/install-root/bin
+ shell: bash
+
+ test-macos:
+ name: ${{ matrix.config.name }}
+ runs-on: ${{ matrix.config.runner }}
+ timeout-minutes: 60
+ needs: build-macos
+ env:
+ QTEST_FUNCTION_TIMEOUT: 9000000
+ QBS_AUTOTEST_PROFILE: 'qt'
+ QBS_TEST_SOURCE_ROOT: 'tests'
+ QBS_EXTRA_GRPC_LIBS: 'absl_cord,absl_cordz_handle,absl_cordz_info,absl_synchronization,grpc,gpr'
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {
+ name: 'Run macOS tests (Xcode 15.1)',
+ runner: 'macos-13',
+ target: 'desktop',
+ toolchain: 'clang_64',
+ xcodeVersion: '15.1',
+ testProfile: 'xcode_15_1-macosx-x86_64',
+ qtVersion: '6.5.0',
+ script: './scripts/test-qbs.sh',
+ }
+ - {
+ name: 'Run macOS tests (Xcode 15.1, Qt 5.15)',
+ runner: 'macos-13',
+ target: 'desktop',
+ toolchain: 'clang_64',
+ xcodeVersion: '15.1',
+ testProfile: 'xcode_15_1-macosx-x86_64',
+ qtVersion: '5.15.2',
+ script: './scripts/test-qt.sh',
+ }
+ - {
+ name: 'Run iOS tests (Xcode 15.1)',
+ runner: 'macos-13',
+ target: 'ios',
+ toolchain: 'ios',
+ xcodeVersion: '15.1',
+ testProfile: 'xcode_15_1-iphoneos-arm64',
+ qtVersion: '6.5.0',
+ script: './scripts/test-qbs.sh',
+ }
+ - {
+ name: 'Run iOS-sim tests (Xcode 15.1)',
+ runner: 'macos-13',
+ target: 'ios',
+ toolchain: 'ios',
+ xcodeVersion: '15.1',
+ testProfile: 'xcode_15_1-iphonesimulator-x86_64',
+ qtVersion: '6.5.0',
+ script: './scripts/test-qbs.sh',
+ }
+ - {
+ name: 'Run macOS tests (Xcode 14.3.1)',
+ runner: 'macos-13',
+ target: 'desktop',
+ toolchain: 'clang_64',
+ xcodeVersion: '14.3.1',
+ testProfile: 'xcode_14_3_1-macosx-x86_64',
+ qtVersion: '6.5.0',
+ script: './scripts/test-qbs.sh',
+ }
+ - {
+ name: 'Run macOS tests (Xcode 14.2)',
+ runner: 'macos-13',
+ target: 'desktop',
+ toolchain: 'clang_64',
+ xcodeVersion: '14.2',
+ testProfile: 'xcode_14_2-macosx-x86_64',
+ qtVersion: '6.5.0',
+ script: './scripts/test-qbs.sh',
+ }
+ - {
+ name: 'Run macOS tests (Xcode-less)',
+ runner: 'macos-13',
+ target: 'desktop',
+ toolchain: 'clang_64',
+ xcodeVersion: '',
+ testProfile: 'xcode_13_4_1-macosx-x86_64',
+ qtVersion: '6.5.0',
+ script: './scripts/test-qbs.sh',
+ }
+ steps:
+ - uses: actions/checkout@v1
+ - name: Download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: qbs-macos-${{ github.run_id }}.tar.gz
+ path: ./
+ - name: Unpack artifact
+ run: mkdir -p release/install-root/ && tar xzf qbs-macos-${{ github.run_id }}.tar.gz -C release/install-root/
+ - name: Update PATH
+ run: echo "./release/install-root/usr/local/bin" >> $GITHUB_PATH
+ - name: Install required packages
+ run: brew install capnp ccache grpc icoutils makensis protobuf p7zip
+ - name: Install Host Qt
+ if: matrix.config.toolchain == 'ios'
+ uses: ./.github/actions/download-qt
+ with:
+ target: 'desktop'
+ toolchain: 'clang_64'
+ version: ${{ matrix.config.qtVersion }}
+ - name: Install Qt
+ uses: ./.github/actions/download-qt
+ with:
+ target: ${{ matrix.config.target }}
+ toolchain: ${{ matrix.config.toolchain }}
+ version: ${{ matrix.config.qtVersion }}
+ - name: Select Xcode
+ run: sudo xcode-select --switch /Applications/Xcode_${{ matrix.config.xcodeVersion }}.app
+ if: matrix.config.xcodeVersion != ''
+ - name: Setup Qbs
+ run: |
+ qbs setup-toolchains --detect
+ qbs setup-qt $(which qmake) qt
+ qbs config profiles.qt.baseProfile ${{ matrix.config.testProfile }}
+ qbs config defaultProfile qt
+ qbs config --list
+ if: matrix.config.xcodeVersion != ''
+ - name: Setup Qbs (Xcode-less)
+ run: |
+ qbs setup-toolchains /usr/bin/clang++ ${{ matrix.config.testProfile }}
+ qbs setup-qt $(which qmake) qt
+ qbs config profiles.qt.baseProfile ${{ matrix.config.testProfile }}
+ qbs config defaultProfile qt
+ qbs config --list
+ if: matrix.config.xcodeVersion == ''
+ - name: Run Tests
+ run: |
+ sudo chmod g+w /cores
+ ulimit -c unlimited
+ ${{ matrix.config.script }} ./release/install-root/usr/local/bin
+ - name: Coredump on failure
+ if: ${{ failure() }}
+ run: |
+ for f in $(find /cores -maxdepth 1 -name 'core.*' -print); do
+ lldb --core $f --batch --one-line "bt all"
+ done;
+
+ test-windows:
+ name: ${{ matrix.config.name }}
+ runs-on: windows-2022
+ timeout-minutes: 60
+ needs: build-windows
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {
+ name: 'Run Windows tests (MSVC 2022)',
+ target: 'desktop',
+ toolchain: 'win64_msvc2019_64',
+ testProfile: 'MSVC2022-x64',
+ qtVersion: '6.5.0',
+ script: './scripts/test-qbs.sh',
+ }
+ - {
+ name: 'Run Windows tests (MSVC 2022, Qt 5.15)',
+ target: 'desktop',
+ toolchain: 'win64_msvc2019_64',
+ testProfile: 'MSVC2022-x64',
+ qtVersion: '5.15.2',
+ script: './scripts/test-qt.sh',
+ }
+ - {
+ name: 'Run Windows tests (clang-cl)',
+ target: 'desktop',
+ toolchain: 'win64_msvc2019_64',
+ testProfile: 'clang-cl-x86_64',
+ qtVersion: '6.5.0',
+ script: './scripts/test-qbs.sh',
+ }
+ - {
+ name: 'Run Windows tests (MinGW)',
+ target: 'desktop',
+ toolchain: 'win64_mingw',
+ testProfile: 'mingw-qt',
+ qtVersion: '6.5.0',
+ script: './scripts/test-qbs.sh',
+ }
+ env:
+ QTEST_FUNCTION_TIMEOUT: 9000000
+ QBS_AUTOTEST_PROFILE: 'qt'
+ QBS_TEST_SOURCE_ROOT: 'tests'
+ QT_ASSUME_STDERR_HAS_CONSOLE: 1
+ steps:
+ - uses: actions/checkout@v1
+ - name: Download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: qbs-windows-${{ github.run_id }}.zip
+ path: ./
+ - name: Unpack artifact
+ run: |
+ mkdir -p release/install-root
+ cd release/install-root
+ 7z x ../../qbs-windows-${{ github.run_id }}.zip
+ shell: bash
+ - name: Update PATH
+ run: echo "./release/install-root/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
+ - name: Install required packages
+ run: choco install -y pkgconfiglite --download-checksum=6004df17818f5a6dbf19cb335cc92702
+ continue-on-error: true # pkgconfiglite installation is flaky
+ - name: Install Qt
+ uses: ./.github/actions/download-qt
+ with:
+ toolchain: ${{ matrix.config.toolchain }}
+ version: ${{ matrix.config.qtVersion }}
+ - name: Install MinGW
+ uses: ./.github/actions/download-mingw
+ - name: Setup Qbs
+ run: |
+ qbs setup-toolchains --detect
+ qbs setup-toolchains --type mingw $(which g++).exe mingw-qt
+ qbs setup-qt $(which qmake).exe qt
+ qbs config profiles.qt.baseProfile ${{ matrix.config.testProfile }}
+ qbs config defaultProfile qt
+ qbs config --list
+ shell: bash
+ - name: Setup self-signed certificate
+ run: |
+ New-SelfSignedCertificate -DnsName qbs@community.test -Type CodeSigning -CertStoreLocation cert:\CurrentUser\My
+ Export-Certificate -Cert (Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert)[0] -FilePath qbs-code-signing.crt
+ Import-Certificate -FilePath .\qbs-code-signing.crt -CertStoreLocation Cert:\CurrentUser\TrustedPublisher
+ shell: powershell
+ - name: Run Tests
+ run: ${{ matrix.config.script }} ./release/install-root/bin
+ shell: bash
+
+ test-windows-extra:
+ name: ${{ matrix.config.name }}
+ runs-on: windows-latest
+ timeout-minutes: 60
+ needs: build-windows
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {
+ name: 'Run Windows tests (OpenWatcom DOS 32 bit)',
+ testProfile: 'watcom-2_0_0-dos-x86',
+ script: './scripts/test-baremetal.sh',
+ }
+ - {
+ name: 'Run Windows tests (OpenWatcom DOS 16 bit)',
+ testProfile: 'watcom-2_0_0-dos-x86_16',
+ script: './scripts/test-baremetal.sh',
+ }
+ - {
+ name: 'Run Windows tests (OpenWatcom OS/2 32 bit)',
+ testProfile: 'watcom-2_0_0-os2-x86',
+ script: './scripts/test-baremetal.sh',
+ }
+ - {
+ name: 'Run Windows tests (OpenWatcom OS/2 16 bit)',
+ testProfile: 'watcom-2_0_0-os2-x86_16',
+ script: './scripts/test-baremetal.sh',
+ }
+ - {
+ name: 'Run Windows tests (OpenWatcom Windows 32 bit)',
+ testProfile: 'watcom-2_0_0-windows-x86',
+ script: './scripts/test-baremetal.sh',
+ }
+ - {
+ name: 'Run Windows tests (OpenWatcom Windows 16 bit)',
+ testProfile: 'watcom-2_0_0-windows-x86_16',
+ script: './scripts/test-baremetal.sh',
+ }
+ - {
+ name: 'Run Windows tests (DigitalMars DOS 16 bit)',
+ testProfile: 'dmc-8_57_0-dos-x86_16',
+ script: './scripts/test-baremetal.sh',
+ }
+ - {
+ name: 'Run Windows tests (DigitalMars Windows 32 bit)',
+ testProfile: 'dmc-8_57_0-windows-x86',
+ script: './scripts/test-baremetal.sh',
+ }
+ - {
+ name: 'Run Windows tests (DigitalMars Windows 16 bit)',
+ testProfile: 'dmc-8_57_0-windows-x86_16',
+ script: './scripts/test-baremetal.sh',
+ }
+ env:
+ QTEST_FUNCTION_TIMEOUT: 9000000
+ QBS_AUTOTEST_PROFILE: 'extra'
+ QBS_TEST_SOURCE_ROOT: 'tests'
+ QT_ASSUME_STDERR_HAS_CONSOLE: 1
+ steps:
+ - uses: actions/checkout@v1
+ - name: Download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: qbs-windows-${{ github.run_id }}.zip
+ path: ./
+ - name: Unpack artifact
+ run: |
+ mkdir -p release/install-root
+ cd release/install-root
+ 7z x ../../qbs-windows-${{ github.run_id }}.zip
+ shell: bash
+ - name: Update PATH
+ run: echo "./release/install-root/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
+ - name: Install OpenWatcom
+ uses: ./.github/actions/download-ow
+ - name: Install DigitalMars
+ uses: ./.github/actions/download-dm
+ - name: Setup Qbs
+ run: |
+ qbs setup-toolchains --detect
+ qbs config profiles.extra.baseProfile ${{ matrix.config.testProfile }}
+ qbs config defaultProfile extra
+ qbs config --list
+ shell: bash
+ - name: Run Tests
+ run: ${{ matrix.config.script }} ./release/install-root/bin
+ shell: bash
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 000000000..1b65762e7
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,229 @@
+name: Build release packages
+
+on:
+ push:
+ branches-ignore:
+ - 'gerrit/*'
+ tags:
+ - 'v*'
+
+jobs:
+ build-linux:
+ name: ${{ matrix.config.name }}
+ runs-on: ubuntu-latest
+ timeout-minutes: 60
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {
+ name: 'Build on Linux (gcc)',
+ options: 'qbs.installPrefix:""
+ modules.cpp.compilerWrapper:ccache
+ modules.qbsbuildconfig.enableAddressSanitizer:false
+ project.withTests:false
+ modules.qbsbuildconfig.enableUnitTests:false
+ modules.qbsbuildconfig.enableBundledQt:true
+ products.qbs_archive.targetName:qbs-linux-${{ github.run_id }}',
+ script: './scripts/build-qbs-with-qbs.sh',
+ cacheid: 'gcc-release',
+ }
+ env:
+ BUILD_OPTIONS: ${{ matrix.config.options }}
+ WITH_TESTS: 0
+ steps:
+ - uses: actions/checkout@v1
+ - name: Create .ccache dir
+ run: mkdir -p ~/.ccache
+ - name: test
+ run: echo ${{ github.ref }}
+ - name: ccache cache files
+ uses: actions/cache@v4
+ with:
+ path: ~/.ccache
+ key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache
+ - name: Pull the Docker Image
+ run: docker-compose pull jammy-qt6
+ - name: Print ccache stats
+ run: docker-compose run jammy-qt6 ccache -s
+ - name: Build Qbs
+ run: docker-compose run jammy-qt6 ${{ matrix.config.script }}
+ - name: Print ccache stats
+ run: docker-compose run jammy-qt6 ccache -s
+ - name: Get archive name
+ id: get-archive-name
+ run: echo "archive-name=$(git describe)" >> $GITHUB_OUTPUT
+ - name: Upload artifacts
+ uses: 'actions/upload-artifact@v4'
+ with:
+ name: qbs-linux-${{ github.run_id }}.tar.gz
+ path: release/qbs-linux-${{ github.run_id }}.tar.gz
+
+ build-macos:
+ name: Build on macOS
+ runs-on: macos-12
+ timeout-minutes: 60
+ env:
+ BUILD_OPTIONS: |
+ qbs.installPrefix:""
+ modules.cpp.compilerWrapper:ccache
+ project.withTests:false
+ modules.qbsbuildconfig.enableUnitTests:false
+ modules.qbsbuildconfig.enableAddressSanitizer:false
+ modules.qbsbuildconfig.enableBundledQt:true
+ products.qbs_archive.targetName:qbs-macos-${{ github.run_id }}
+ WITH_TESTS: 0
+ steps:
+ - uses: actions/checkout@v1
+ - name: Create .ccache dir
+ run: mkdir -p ~/.ccache
+ - name: ccache cache files
+ uses: actions/cache@v4
+ with:
+ path: ~/.ccache
+ key: ${{ runner.os }}-release-ccache
+ - name: Install required packages
+ run: |
+ brew install ccache p7zip
+ python3 -m pip install --user beautifulsoup4 lxml
+ - name: Install Qt
+ uses: ./.github/actions/download-qt
+ with:
+ toolchain: clang_64
+ - name: Install Qt Creator
+ uses: ./.github/actions/download-qtc
+ - name: Setup Qbs
+ run: |
+ qbs setup-toolchains --detect
+ qbs setup-qt --detect
+ qbs config profiles.qt.baseProfile xcode_13_2_1-macosx-x86_64
+ qbs config defaultProfile qt
+ qbs config --list
+ - name: Print ccache stats
+ run: ccache -s
+ - name: Build Qbs
+ run: scripts/build-qbs-with-qbs.sh
+ - name: Print ccache stats
+ run: ccache -s
+ - name: Upload artifacts
+ uses: 'actions/upload-artifact@v4'
+ with:
+ name: qbs-macos-${{ github.run_id }}.tar.gz
+ path: release/qbs-macos-${{ github.run_id }}.tar.gz
+
+ build-windows-with-docker:
+ name: Build on Windows (Docker)
+ runs-on: windows-2019
+ timeout-minutes: 60
+ env:
+ WITH_TESTS: 0
+ QT_ASSUME_STDERR_HAS_CONSOLE: 1
+ CLCACHE_DIR: C:\.ccache
+ steps:
+ - uses: actions/checkout@v1
+ - name: Create .ccache dir
+ run: mkdir -p ~/.ccache
+ shell: bash
+ - name: clcache cache files
+ uses: actions/cache@v4
+ with:
+ path: ~/.ccache
+ key: ${{ runner.os }}-release-msvc-docker-clcache
+ - name: Pull the Windows Image
+ run: docker-compose pull windows
+ - name: Print clcache stats
+ run: docker-compose run --rm windows clcache -s
+ - name: Build Qbs
+ run: >
+ docker-compose run --rm windows qbs build
+ -p dist
+ qbs.buildVariant:release
+ modules.cpp.compilerWrapper:clcache
+ project.withTests:false
+ modules.qbsbuildconfig.enableBundledQt:true
+ modules.qbsbuildconfig.enableUnitTests:false
+ modules.cpp.treatWarningsAsErrors:true
+ project.withDocumentation:true
+ config:release profile:qt64
+ - name: Print clcache stats
+ run: docker-compose run --rm windows clcache -s
+ - name: Get archive name
+ id: get-archive-name
+ run: echo "archive-name=$(git describe)" >> $GITHUB_OUTPUT
+ - name: Upload artifacts
+ uses: 'actions/upload-artifact@v4'
+ with:
+ name: qbs-windows-${{ github.run_id }}
+ path: |
+ release/qbs.*.nupkg
+ release/qbs-windows-*.zip
+
+ create-archives:
+ name: Create Archives
+ runs-on: ubuntu-latest
+ needs: [build-linux, build-windows-with-docker]
+ steps:
+ - uses: actions/checkout@v1
+ with:
+ submodules: true
+ - name: Get version name
+ id: get-version-name
+ run: echo "version-name=$(cat VERSION)" >> $GITHUB_OUTPUT
+ - name: Create directories
+ run: |
+ mkdir release
+ mkdir tmp
+ - name: Copy sources
+ run: rsync -av --exclude='.git/' --exclude='tmp/' . ./tmp/qbs-src-${{ steps.get-version-name.outputs.version-name }}
+ - name: Zip Archive
+ run: |
+ cd tmp/
+ zip -r ../release/qbs-src-${{ steps.get-version-name.outputs.version-name }}.zip qbs-src-${{ steps.get-version-name.outputs.version-name }}
+ - name: Tarball Archive
+ run: |
+ cd tmp/
+ tar czf ../release/qbs-src-${{ steps.get-version-name.outputs.version-name }}.tar.gz qbs-src-${{ steps.get-version-name.outputs.version-name }}
+ - name: Download Linux artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: qbs-linux-${{ github.run_id }}.tar.gz
+ path: ./tmp
+ - name: Repack Linux artifact
+ run: |
+ mkdir -p tmp/qbs-linux-x86_64-${{ steps.get-version-name.outputs.version-name }}
+ tar xzf ./tmp/qbs-linux-${{ github.run_id }}.tar.gz -C tmp/qbs-linux-x86_64-${{ steps.get-version-name.outputs.version-name }}
+ cd tmp/
+ tar czf ../release/qbs-linux-x86_64-${{ steps.get-version-name.outputs.version-name }}.tar.gz qbs-linux-x86_64-${{ steps.get-version-name.outputs.version-name }}
+ - name: Download Windows artifacts
+ uses: actions/download-artifact@v4
+ with:
+ name: qbs-windows-${{ github.run_id }}
+ path: ./tmp/release
+ - name: Copy Windows artifacts
+ run: |
+ cp ./tmp/release/qbs.*.nupkg ./release
+ cp ./tmp/release/qbs-windows-x86_64-*.zip ./release
+ - name: Copy changelog
+ run: cp changelogs/changes-${{ steps.get-version-name.outputs.version-name }}.md release || echo "changelog not found"
+ - name: Generate checksums
+ run: |
+ cd release/
+ find . -type f -printf '%f\n' | grep -v .txt | xargs md5sum > md5sums.txt
+ find . -type f -printf '%f\n' | grep -v .txt | xargs sha256sum > sha256sums.txt
+ - name: Get archive name
+ id: get-archive-name
+ run: echo "archive-name=$(git describe)" >> $GITHUB_OUTPUT
+ - name: Upload artifacts
+ uses: 'actions/upload-artifact@v4'
+ with:
+ name: qbs-release-${{ steps.get-archive-name.outputs.archive-name }}
+ path: |
+ release/qbs-src-*.zip
+ release/qbs-src-*.tar.gz
+ release/qbs-linux-*.tar.gz
+ release/qbs.*.nupkg
+ release/qbs-windows-x86_64-*.zip
+ release/changes-*.md
+ release/md5sums.txt
+ release/sha256sums.txt
+
diff --git a/.gitignore b/.gitignore
index c74f62ac8..bd27569df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
.qmake.cache
.qmake.stash
.qbs
+.vscode*
build
*.dll
*.dylib
@@ -12,6 +13,7 @@ build
*.pdb
*.pro.user*
*.qbs.user*
+CMakeLists.txt.user*
*.xcodeproj/
target_wrapper.sh
*~
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index f2f854c17..000000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "qtscript"]
- path = src/shared/qtscript
- url = ../../qt/qtscript.git
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 4917087a3..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,123 +0,0 @@
-#
-# Required environment variables in the travis config
-#
-# DOCKER_USERNAME
-#
-language: cpp
-cache: ccache
-
-git:
- submodules: false
-
-env:
- global:
- - QT_INSTALL_DIR=~/Qt
- - QT_VERSION=5.12.6
- - QTCREATOR_VERSION=4.10.2
-
-stages:
- - name: Build Qbs and and run autotests
-
-jobs:
- include:
- - &build-on-bionic
- stage: Build Qbs and and run autotests
- name: With Qbs on Ubuntu bionic (linux_gcc64)
- env:
- BUILD_OPTIONS=modules.cpp.compilerWrapper:ccache
- services:
- - docker
- before_script:
- - docker-compose pull bionic
- - ccache -M 5G
- - ccache -s
- after_script:
- - ccache -s
- script:
- - docker-compose run bionic scripts/build-qbs-with-qbs.sh
-
- - <<: *build-on-bionic
- name: With QMake on Ubuntu bionic (linux_gcc64)
- env:
- BUILD_OPTIONS="CONFIG+=ccache"
- script:
- - docker-compose run bionic scripts/build-qbs-with-qmake.sh
-
- - <<: *build-on-bionic
- name: With Qbs on Ubuntu bionic (mingw32_w64)
- env:
- BUILD_OPTIONS="profile:qt-mingw32_w64 modules.cpp.compilerWrapper:ccache modules.cpp.treatWarningsAsErrors:true config:release"
- script:
- - docker-compose run bionic qbs build ${BUILD_OPTIONS}
-
- - <<: *build-on-bionic
- name: With Qbs on Ubuntu bionic (linux_clang64 & clang_tidy)
- env:
- BUILD_OPTIONS="profile:qt-clang_64 modules.cpp.compilerWrapper:ccache"
- services:
- - docker
- script:
- - docker-compose run --rm bionic scripts/run-analyzer.sh
-
- - &build-on-macos
- stage: Build Qbs and and run autotests
- name: With Qbs on macOS (xcode 10.3)
- os: osx
- osx_image: xcode10.3
- addons:
- homebrew:
- packages:
- - p7zip
- - ccache
- update: true
- env:
- # Address sanitizer slows autotests down too much.
- # We would hit the maximum build time on Travis.
- BUILD_OPTIONS="modules.qbsbuildconfig.enableAddressSanitizer:false modules.cpp.compilerWrapper:ccache"
- QMAKE_PATH=${QT_INSTALL_DIR}/${QT_VERSION}/clang_64/bin/qmake
- PATH="${QT_INSTALL_DIR}/Qt Creator.app/Contents/MacOS:${PATH}"
- QBS_BUILD_PROFILE=qt
- before_install:
- - ./scripts/install-qt.sh -d ${QT_INSTALL_DIR} --version ${QT_VERSION} qtbase qtdeclarative qttools qtscript
- - ./scripts/install-qt.sh -d ${QT_INSTALL_DIR} --version ${QTCREATOR_VERSION} qtcreator
- - pip2 install --user beautifulsoup4 lxml
- script:
- - ccache -s
- - qbs setup-toolchains --detect
- - qbs setup-qt ${QMAKE_PATH} qt
- - qbs config qt.baseProfile xcode-macosx-x86_64
- - qbs config defaultProfile qt
- - scripts/build-qbs-with-qbs.sh
- - ccache -s
-
- - <<: *build-on-macos
- name: With Qbs on macOS (xcode 11)
- osx_image: xcode11
-
- - &build-on-windows
- stage: Build Qbs and and run autotests
- name: With Qbs on Windows (Visual Studio 2017)
- os: windows
- env:
- # Need to build in release mode. Otherwise autotests would be too slow.
- BUILD_OPTIONS="config:release modules.cpp.compilerWrapper:clcache"
- QT_INSTALL_DIR=C:/Qt
- QMAKE_PATH=${QT_INSTALL_DIR}/${QT_VERSION}/msvc2017_64/bin/qmake.exe
- PATH="/c/Qt/Tools/QtCreator/bin:/c/Python36:/c/Python36/Scripts:${PATH}"
- WITH_DOCS=0
- QBS_BUILD_PROFILE=qt
- CLCACHE_DIR="${HOME}/.ccache"
- before_install:
- # Install Qbs and Qt
- - ./scripts/install-qt.sh -d ${QT_INSTALL_DIR} --version ${QT_VERSION} --toolchain win64_msvc2017_64 qtbase qtdeclarative qttools qtscript
- - ./scripts/install-qt.sh -d ${QT_INSTALL_DIR} --version ${QTCREATOR_VERSION} qtcreator
- - choco install python --version 3.6.8
- - pip install git+https://github.com/frerich/clcache.git@cae73d8255d78db8ba11e23c51fd2c9a89e7475b
- script:
- - clcache -s
- - qbs setup-toolchains --detect
- - qbs setup-qt ${QMAKE_PATH} qt
- - qbs config qt.baseProfile MSVC2017-x86
- - qbs config defaultProfile qt
- - scripts/build-qbs-with-qbs.sh
- - clcache -s
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 000000000..f08ed0b0e
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,74 @@
+cmake_minimum_required(VERSION 3.10)
+
+if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.15")
+ cmake_policy(VERSION 3.15)
+endif()
+
+## Add paths to check for cmake modules:
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+
+include(FeatureSummary)
+include(QbsBuildConfig)
+include(QbsDocumentation)
+
+file(STRINGS VERSION QBS_VERSION)
+project(Qbs VERSION ${QBS_VERSION})
+
+# Force C++ standard, do not fall back, do not use compiler extensions
+set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+# Set up Qt stuff:
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+set(CMAKE_AUTOUIC ON)
+
+find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
+
+if(WITH_TESTS)
+ enable_testing()
+ set(QT_TEST_COMPONENT Test)
+ set(IMPLICIT_DEPENDS Qt${QT_VERSION_MAJOR}::Test)
+endif()
+
+find_package(Qt${QT_VERSION_MAJOR}
+ COMPONENTS Concurrent Core Gui Network Widgets Xml ${QT_TEST_COMPONENT}
+ REQUIRED
+ )
+
+find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS Script QUIET)
+
+if (Qt6_FOUND)
+ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core5Compat REQUIRED)
+ if(NOT TARGET Qt6Core5Compat)
+ if(CMAKE_VERSION VERSION_LESS 3.18)
+ set_property(TARGET Qt6::Core5Compat PROPERTY IMPORTED_GLOBAL TRUE) # hack for CMake < 3.18
+ endif()
+ add_library(Qt6Core5Compat ALIAS Qt6::Core5Compat)
+ endif()
+else()
+ if(NOT TARGET Qt6Core5Compat)
+ add_library(Qt6Core5Compat INTERFACE)
+ endif()
+ find_package(Qt${QT_VERSION_MAJOR} OPTIONAL_COMPONENTS DocTools)
+endif()
+
+if (QBS_INSTALL_HTML_DOCS OR QBS_INSTALL_QCH_DOCS)
+ find_package(Python3 COMPONENTS Interpreter)
+ if (Qt6_FOUND)
+ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Tools REQUIRED)
+ else()
+ find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Help REQUIRED)
+ endif()
+endif()
+
+add_subdirectory(src)
+add_subdirectory(doc)
+add_subdirectory(share)
+if(WITH_TESTS)
+ add_subdirectory(tests)
+endif()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..1a8a8877b
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,102 @@
+# Contributing to Qbs
+
+The main source code repository is hosted at
+[codereview.qt-project.org](https://codereview.qt-project.org/q/project:qbs/qbs).
+
+The Qbs source code is also mirrored on [code.qt.io](https://code.qt.io/cgit/qbs/qbs.git/)
+and on [GitHub](https://github.com/qbs/qbs). Please note that those mirrors
+are read-only and we do not accept pull-requests on GitHub. However, we gladly
+accept contributions via [Gerrit](https://codereview.qt-project.org/q/project:qbs/qbs).
+
+This document briefly describes steps required to be able to propose changes
+to the Qbs project.
+
+See the [Qt Contribution Guidelines](https://wiki.qt.io/Qt_Contribution_Guidelines)
+page, [Setting up Gerrit](https://wiki.qt.io/Setting_up_Gerrit) and
+[Gerrit Introduction](https://wiki.qt.io/Gerrit_Introduction) for more
+details about how to upload patches to Gerrit.
+
+## Preparations
+
+* [Set up](https://wiki.qt.io/Setting_up_Gerrit#How_to_get_started_-_Gerrit_registration)
+a Gerrit account
+* Tweak your SSH config as instructed [here](https://wiki.qt.io/Setting_up_Gerrit#Local_Setup)
+* Use the recommended Git settings, defined [here](https://wiki.qt.io/Setting_up_Gerrit#Configuring_Git)
+* Get the source code as described below
+
+## Cloning Qbs
+
+Clone Qbs from the [code.qt.io](https://code.qt.io/cgit/qbs/qbs.git/) mirror
+```
+git clone git://code.qt.io/qbs/qbs.git
+```
+Alternatively, you can clone from the mirror on the [GitHub](https://github.com/qbs/qbs)
+```
+git clone https://github.com/qbs/qbs.git
+```
+
+Set up the Gerrit remote
+```
+git remote add gerrit ssh://<gerrit-username>@codereview.qt-project.org:29418/qbs/qbs
+```
+
+## Setting up git hooks
+
+Install the hook generating Commit-Id files into your top level project directory:
+```
+gitdir=$(git rev-parse --git-dir); scp -p -P 29418 codereview.qt-project.org:hooks/commit-msg "${gitdir}/hooks/"
+```
+
+This hook automatically adds a "Change-Id: …" line to the commit message. Change-Id is used
+to identify new Patch Sets for existing
+[Changes](https://wiki.qt.io/Gerrit_Introduction#Terminology).
+
+## Making changes
+
+Commit your changes in the usual way you do in Git.
+
+After making changes, you might want to ensure that Qbs can be built and autotests pass:
+```
+qbs build -p autotest-runner
+```
+See ["Appendix A: Building Qbs"](http://doc.qt.io/qbs/building-qbs.html) for details.
+
+In case your changes might significantly affect performance (in either way), the
+'qbs_benchmarker' tool can be used (Linux only, requires Valgrind to be installed):
+```
+qbs_benchmarker -r <QBS_REPO> -o <OLD_REVISION> -n <NEW_REVISION> -a <ACTIVITY> -p <PROJECT>
+```
+Use 'qbs_benchmarker --help' for details.
+
+## Pushing your changes to Gerrit
+
+After committing your changes locally, push them to Gerrit
+
+```
+git push gerrit HEAD:refs/for/master
+```
+
+Gerrit will print a URL that can be used to access the newly created Change.
+
+## Adding reviewers
+
+Use the "ADD REVIEWER" button on the Change's web page to ask people to do the
+review. To find possible reviewers, you can examine the Git history with
+'git log' and/or 'git blame' or ask on the mailing list: qbs@qt-project.org
+
+## Modifying Commits
+
+During the review process, it might be necessary to do some changes to the commit.
+To include you changes in the last commit, use
+```
+git commit --amend
+```
+This will edit the last commit instead of creating a new one. Make sure to preserve the
+Change-Id footer when amending commits. For details, see
+[Updating a Contribution With New Code](https://wiki.qt.io/Gerrit_Introduction#Updating_a_Contribution_With_New_Code)
+
+## Abandoning changes
+
+Changes which are inherently flawed or became inapplicable should be abandoned. You can do that
+on the Change's web page with the "ABANDON" button. Make sure to remove the abandoned commit
+from your working copy by using '[git reset](https://git-scm.com/docs/git-reset)'.
diff --git a/README b/README
deleted file mode 100644
index 5c11c8465..000000000
--- a/README
+++ /dev/null
@@ -1,8 +0,0 @@
-Qbs
-===
-Qbs is a cross-platform build tool.
-The project's homepage is http://wiki.qt.io/qbs
-Qbs product documentation is available at: http://doc.qt.io/qbs/index.html
-
-For more information about building Qbs from sources, see
-"Appendix A: Building Qbs" at: http://doc.qt.io/qbs/building-qbs.html.
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..c56d8c214
--- /dev/null
+++ b/README.md
@@ -0,0 +1,36 @@
+# Qbs
+
+Qbs is a build automation tool designed to conveniently manage the build
+process of software projects across multiple platforms. Qbs can be used for any
+software project, regardless of programming language, toolkit, or libraries used.
+
+## Documentation
+
+Qbs product documentation is available at [doc.qt.io/qbs](http://doc.qt.io/qbs/index.html)
+
+The project's homepage is [wiki.qt.io/qbs](http://wiki.qt.io/qbs)
+
+## Supported platforms
+
+Qbs binaries are available for Windows, macOS, Linux, and FreeBSD.
+
+For more information about how to install Qbs on your platform, see the
+[Installing](https://doc.qt.io/qbs/installing.html) page in the documentation.
+
+Qbs allows to build applications for different platforms, for the list of
+supported platforms and the details about each platform, see the
+[Target Platforms](https://doc.qt.io/qbs/platforms.html) page.
+
+## Building Qbs
+
+For information about building Qbs from sources, see
+["Appendix A: Building Qbs"](http://doc.qt.io/qbs/building-qbs.html).
+
+## Reporting Bugs
+
+Please report any bugs in our [bug tracker](https://bugreports.qt.io/browse/QBS).
+
+## Contributing
+
+See [Contributing to Qbs](CONTRIBUTING.md)
+
diff --git a/RELEASE.md b/RELEASE.md
new file mode 100644
index 000000000..c75069ae1
--- /dev/null
+++ b/RELEASE.md
@@ -0,0 +1,105 @@
+# Release Instructions
+
+This file contains instructions how to publish a Qbs release into various package systems.
+
+## Building packages
+
+Release packages are built automatically by the
+[Build release packages](https://github.com/qbs/qbs/actions/workflows/release.yml) job on GitHub
+actions. The job builds every commit, so you'll need to find a specific run triggered by
+pushing a git tag instead of a commit.
+
+The only interesting artifact is qbs-release-v1.x.y - it contains Windows binary packages as
+well as source packages.
+
+Packages should then be uploaded to the
+[Qt Website](https://download.qt.io/official_releases/qbs/).
+
+## Chocolatey
+
+For updating Qbs in [Chocolatey](https://community.chocolatey.org/packages/qbs), you'll need
+to be a maintainer of the Qbs project. You'll also will need an API key from your
+[account](https://community.chocolatey.org/account) (make sure you have one).
+
+Get the qbs.1.x.y.nupkg file from the qbs-release-v1.x.y.zip archive and run the following command:
+
+```
+choco push --api-key <YOUR_API_KEY> qbs.1.x.y.nupkg"
+```
+
+Choco will upload the file, download binary packages from the Qt Website and publish Qbs
+automatically.
+
+## Homebrew
+
+First, you'll need to [install](https://docs.brew.sh/Installation) Homewbrew.
+
+Second, you'll need to fork the [homebrew-core](https://github.com/Homebrew/homebrew-core) repo
+to you GitHub account.
+
+Next you'll need to add your remote to the existing repo:
+```
+$ brew update
+$ cd "$(brew --repository homebrew/core)"
+$ git remote add github git@github.com:<USERNAME>/homebrew-core.git
+# Create a new git branch for your formula so your pull request is easy to
+# modify if any changes come up during review.
+$ git checkout -b <some-descriptive-name> origin/master
+```
+
+You'll need a SHA-256 sum for the qbs-src-1.x.y.tar.gz source package. SHA-256 sum can be
+found in the sha256sums.txt file from the qbs-release-v1.x.y.zip.
+
+Check if the same upgrade has been already submitted by searching the open pull requests for
+[Qbs](https://github.com/Homebrew/homebrew-core/pulls?q=is%3Apr+is%3Aopen+qbs).
+
+Run the following command, replacing the version and SHA-256 sum:
+```
+$ brew bump-formula-pr --strict qbs \
+ --url https://download.qt.io/official_releases/qbs/1.x.y/qbs-src-1.x.y.tar.gz \
+ --sha256=<SHA-256>
+```
+
+The above can also be done manually by editing the formula source code and creating a pull request.
+
+Some useful commands to build/test formula locally:
+```
+$ brew install --build-from-source qbs
+$ brew test qbs
+$ brew audit --strict qbs
+```
+
+## Mac Ports
+
+Fork [macports-ports](https://github.com/macports/macports-ports) to your GitHub account
+and clone the repo.
+
+Run the following command in the repo dir (must be done only once):
+```
+$ sudo /opt/local/bin/portindex
+```
+
+Make sure that /opt/local/etc/macports/sources.conf contains a line like
+file:///Users/abbapoh/dev/macports-ports [default]
+pointing to your local repo.
+
+Get the qbs-src-1.x.y.tar.gz source package from the archive or from the
+[Qt Website](https://download.qt.io/official_releases/qbs/) and make note of the file size:
+```
+$ ls -l qbs-src-1.x.y.tar.gz
+```
+Get checksums:
+```
+$ openssl sha256 qbs-src-1.x.y.tar.gz
+$ openssl rmd160 qbs-src-1.x.y.tar.gz
+```
+
+Update the devel/qbs/Portfile file with the new url, filesize and checksums.
+
+Build the package:
+```
+$ /opt/local/bin/port lint qbs
+$ sudo /opt/local/bin/port -vst install qbs
+$ /opt/local/bin/qbs --version # Check that this reports the right version
+```
+Commit. Push. Create merge request.
diff --git a/VERSION b/VERSION
index 15b989e39..197c4d5c2 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.16.0
+2.4.0
diff --git a/changelogs/changes-1.14.0.md b/changelogs/changes-1.14.0.md
index 860578a15..d8a21fe51 100644
--- a/changelogs/changes-1.14.0.md
+++ b/changelogs/changes-1.14.0.md
@@ -24,12 +24,12 @@
* Various fixes and improvements in the Debian Docker image; updated to to Qt 5.11.3.
# Contributors
-* BogDan Vatra <bogdan@kde.org>
-* Christian Kandeler <christian.kandeler@qt.io>
-* Christian Stenger <christian.stenger@qt.io>
-* Davide Pesavento <pesa@gentoo.org>
-* Denis Shienkov <denis.shienkov@gmail.com>
-* hjk <hjk@qt.io>
-* Ivan Komissarov <ABBAPOH@gmail.com>
-* Joerg Bornemann <joerg.bornemann@qt.io>
-* Richard Weickelt <richard@weickelt.de>
+* BogDan Vatra
+* Christian Kandeler
+* Christian Stenger
+* Davide Pesavento
+* Denis Shienkov
+* hjk
+* Ivan Komissarov
+* Joerg Bornemann
+* Richard Weickelt
diff --git a/changelogs/changes-1.15.0.md b/changelogs/changes-1.15.0.md
index cc95011a8..390fe99dc 100644
--- a/changelogs/changes-1.15.0.md
+++ b/changelogs/changes-1.15.0.md
@@ -35,10 +35,10 @@
Windows.
# Contributors
-* Alberto Mardegan <mardy@users.sourceforge.net>
-* Christian Kandeler <christian.kandeler@qt.io>
-* Denis Shienkov <denis.shienkov@gmail.com>
-* Ivan Komissarov <ABBAPOH@gmail.com>
-* Jochen Ulrich <jochenulrich@t-online.de>
-* Joerg Bornemann <joerg.bornemann@qt.io>
-* Richard Weickelt <richard@weickelt.de>
+* Alberto Mardegan
+* Christian Kandeler
+* Denis Shienkov
+* Ivan Komissarov
+* Jochen Ulrich
+* Joerg Bornemann
+* Richard Weickelt
diff --git a/changelogs/changes-1.16.0.md b/changelogs/changes-1.16.0.md
new file mode 100644
index 000000000..23628b904
--- /dev/null
+++ b/changelogs/changes-1.16.0.md
@@ -0,0 +1,126 @@
+# General
+
+* A new freedesktop module helps UNIX application developers to follow the
+ freedesktop.org guidelines.
+* The Android module now allows resourcesDir, sourcesDir and assetsDir to be
+ specified as relative paths.
+* A new ConanfileProbe allows better and more flexible integration of Qbs and
+ the Conan package manager.
+* A new hostArchitecture property has been added to the qbs module.
+
+
+# Language
+
+* List properties in modules are now merged according to inter-module
+ dependencies. This is important when flags like cpp.staticLibraries are
+ contributed by multiple modules with dependencies between each other.
+ (QBS-1517).
+* Dependency matching of multiplexed products is now less strict and does not
+ require all multiplex properties to match. For instance, if product A is
+ multiplexed over qbs.architecture and qbs.buildVariant while product B is only
+ multiplexed over one of these axes, then Qbs no longer fails (QBS-1515).
+
+
+# C/C++ Support
+
+* The Renesas RL78 architecture is now supported in GCC and IAR and the
+ toolchains are auto-detected by qbs-setup-toolchains.
+* The Renesas RX as well as the RH850, V850, 78K are now supported in IAR and
+ the toolchains are auto-detected by qbs-setup-toolchains.
+* The MPLAB X32 GCC-based toolchain is now auto-detected on Windows.
+* Multiple occurrences of static libraries on the linker command line are now
+ pruned and the last instance always wins when using GCC or LLVM-based
+ toolchains. This avoids problems with excessively long linker command lines
+ (QBS-1273).
+* Clang-cl and MSVC toolchains use the compiler frontend instead of the linker
+ when linking. The old behavior can be restored by setting cpp.linkerMode to
+ "manual". This allows to use sanitizers with clang-cl by passing
+ "-fsanitise=xxx" via cpp.driverFlags (QBS-1522).
+* The clang-cl toolchain now uses "link.exe" as the default linker.
+ "lld-link.exe" can be explicitly selected by setting cpp.linkerVariant to
+ "lld" (QBS-1522).
+* The MSVC, clang-cl and MinGW toolchains are now automatically detected if the
+ profile does not set an explicit installation location, for instance because
+ no profile was given.
+* Installation of separate debug information can now be enabled and configured
+ by simply setting the installDebugInformation and debugInformationInstallDir
+ properties in the Application and Library convenience items. This works for
+ toolchains based upon GCC, MSVC or clang-cl.
+* Xcode version 11.4 is now supported on macOS.
+
+
+# Qt Support
+
+* Qbs now supports Qt 5.14 for Android which comes as a multi-architecture
+ package. The qbs-setup-android tool has been updated accordingly (QBS-1497).
+* JSON metatype files generated by moc (Qt >= 5.15) are supported by setting
+ Qt.core.generateMetaTypesFile and Qt.core.metaTypesInstallDir (QBS-1531).
+* Pure debug builds of Qt (>= 5.14) with MinGW are now properly supported. They
+ don't have the 'd' suffix (QTBUG-80792).
+* The QML type declaration mechanism introduced in Qt 5.15 is now supported by
+ the Qt.qml module (QBS-1531).
+* Generated qmltypes files are now named according to the product's targetName
+ property (QTBUG-82710).
+
+
+# Documentation
+
+* The how-to chapter has been extended with sections about debugging Qbs files
+ and about building separate debug information in C++ projects.
+* The item and module reference documentation has been improved for the
+ cpp.libraryPaths, cpp.dynamicLibraries (QBS-1516), qbs.toolchainType and
+ qbs.toolchain properties as well as the Export item and the Library
+ convenience item.
+* Documentation for various path probes has been added (QBS-1187).
+* The README was extended and a CONTRIBUTING file has been added which provides
+ useful information for potential contributors. This is important for people
+ looking at our github mirror.
+
+
+# Infrastructure
+
+* The Debian Docker image has been removed.
+* We are now using ccache and clcache in our CI pipelines to shrink the build
+ time.
+* Clang-tidy is now used to identify potential problems in the code base and a
+ lot of action was taken upon a lot of findings.
+* A Docker image for testing Qbs with Android and Qt has been added.
+
+
+# Important Bug Fixes
+
+* Fix nullpointer access and heap-use-after-free error (QBS-1485).
+* Select the right instance when Depends.profiles is used on a dependency with
+ an aggregator product (QBS-1513).
+* Fix crash when specifying a non-existing profile in Depends.profiles
+ (QBS-1514).
+* Try harder to detect GCC toolchains in qbs-setup-toolchains (QBS-1524).
+* Code signing for Core Foundation Bundles on macOS has been fixed.
+* Automatic artifact scanning now prefers artifacts from product dependencies if
+ multiple candidates are found. This improves dependency tracking in complex
+ projects (QBS-1532).
+* The grpcIncludePath property in the probufcpp module has been fixed
+ (QBS-1542).
+* Qbs does no longer crash when accessing a property of a non-existent module in
+ "IDE mode".
+
+
+# Contributors
+
+* Alberto Mardegan
+* Björn Schäpers
+* BogDan Vatra
+* Christian Kandeler
+* Christian Stenger
+* Denis Shienkov
+* Ivan Komissarov
+* Jochen Ulrich
+* Joerg Bornemann
+* Leon Buckel
+* Marius Sincovici
+* Maximilian Goldstein
+* Mitch Curtis
+* Oliver Wolff
+* Orgad Shaneh
+* Raphaël Cotty
+* Richard Weickelt
diff --git a/changelogs/changes-1.17.0.md b/changelogs/changes-1.17.0.md
new file mode 100644
index 000000000..f34eebe74
--- /dev/null
+++ b/changelogs/changes-1.17.0.md
@@ -0,0 +1,98 @@
+# General
+
+* The lookup order in PathProbe changed to [environmentPaths,
+ searchPaths, platformEnvironmentPaths, platformSearchPaths].
+* The pathPrefix and platformPaths properties have been removed from the
+ PathProbe item. They were deprecated since Qbs 1.13.
+* The protocBinary property in the protobuf module has been renamed to
+ compilerPath.
+* A new module capnp for Cap'n Proto in C++ applications has been added.
+ Cap'n Proto is a serialization protocol similar to protobuf.
+* The qbs-setup-android tool got a --system flag to install profiles
+ system-wide similar to qbs-setup-qt and qbs-setup-toolchains.
+
+
+# Language
+
+* The product and project variables are now available on the
+ right-hand-side of moduleProvider expressions and the default scope is
+ product (QBS-1587).
+
+
+# C/C++ Support
+
+* Lots of improvements have been made on toolchain support for
+ bare-metal devices in general. Bare-metal targets can be selected by
+ setting qbs.targetPlatform to 'none'.
+* KEIL: The ARMCLANG, C166 and C251 toolchains are now supported.
+* IAR: National's CR16, Microchip's AVR32, NXP's M68K, Renesas'
+ M8/16C/M32C/R32C/SuperH targets and RISC-V targets are now supported.
+* GCC: National's CR16, NXP M68K, Renesas M32C/M32R/SuperH/V850 as well
+ as RISC-V and Xtensa targets are now supported.
+* MSVC: Module definition files can now be used to provide the linker
+ with information about exports and attributes (QBS-571).
+* MSVC: "/external:I" is now used to set system include paths (QBS-1573).
+* MSVC: cpp.generateCompilerListingFiles is now supported to generate
+ assembler listings.
+* Xcode: macOS framework paths on the command line are now automatically
+ deduplicated (QBS-1552).
+* Xcode: Support for Xcode 12.0 has been added (QBS-1582).
+
+
+# Qt Support
+
+* The Qt for Android modules have been cleaned up. Support for ARMv5, MIPS and
+ MIPS64 targets has been removed (QBS-1496).
+* Initial support for Qt6 has been added.
+
+
+# Android Support
+
+* A packageType property has been added to the Android.sdk module which
+ allows to create Android App bundles (aab) instead of apk packages
+ only.
+* A aaptName property has been added to the Android.sdk module which
+ allows to use aapt2 (QBS-1562) since aapt has been deprecated.
+
+
+# Documentation
+
+* New bare-metal examples have been added and existing examples have
+ been ported to more toolchains.
+* A new how-to about cpp.rPaths has been added (QBS-1204).
+* Various minor improvements have been made.
+
+
+# Important Bug Fixes
+
+* Building Qt for Android applications as static libraries has been
+ fixed (QBS-1545).
+* Trailing slashes are no longer removed from Visual Studio environment
+ variables (QBS-1551).
+* The MSVC cpp module did not use the cpp.distributionIncludePaths
+ property (QBS-1572).
+* The visual studio generator has been fixed to work with Visual Studio
+ 16.6 (QBS-1569).
+* Fixed extraction of build information from CONFIG and QT_CONFIG
+ variables in Qt installations (QBS-1387).
+* The version number is no longer appended to .so files on Android
+ (QBS-1578).
+* Compiler defines are now correctly passed to moc when processing
+ header files (QBS-1592).
+
+
+# Contributors
+
+* Alberto Mardegan
+* Christian Gagneraud
+* Christian Kandeler
+* Christian Stenger
+* Denis Shienkov
+* Ivan Komissarov
+* Jake Petroules
+* Jochen Ulrich
+* Mitch Curtis
+* Oliver Wolff
+* Raphaël Cotty
+* Richard Weickelt
+* Sergey Zhuravlev
diff --git a/changelogs/changes-1.17.1.md b/changelogs/changes-1.17.1.md
new file mode 100644
index 000000000..ef5a39f83
--- /dev/null
+++ b/changelogs/changes-1.17.1.md
@@ -0,0 +1,10 @@
+# Important bugfixes
+
+* Android: Fix support for Qt 5.15 (QBS-1580).
+* Android: Allow exporting of Qt modules in Export items (QBS-1576)
+* Darwin: Fix ios-simulator builds with XCode 12
+
+# Contributors
+
+* Raphaël Cotty
+* Ivan Komissarov
diff --git a/changelogs/changes-1.18.0.md b/changelogs/changes-1.18.0.md
new file mode 100644
index 000000000..38fb9afb7
--- /dev/null
+++ b/changelogs/changes-1.18.0.md
@@ -0,0 +1,84 @@
+# General
+
+* capnp: The outputDir property is now public and read-only.
+* setup-toochains: Include the Xcode version into the profile name
+ profile when auto-detection an Xcode installation.
+* innosetup module: Add support for InnoSetup v6
+* JSON API: Use the full display name of multiplexed products
+ in the dependencies array. This allows clients to for example to
+ properly update the search path for multiplexed dynamic libraries.
+
+
+# Language
+
+* Deprecate the product variable inside Export items in favor of a new
+ exportingProduct variable. It will be removed in Qbs 1.20 (QBS-1576).
+* Qbs now checks string and stringList values according to the
+ allowedValues property in the PropertyOptions item.
+
+
+# Protocol Buffers Support (Protobuf Module)
+
+* The deprecated protocBinary property has been removed. Use compilerPath
+ instead.
+* A nanopb submodule has been added.
+* The outputDir property is now public and read-only.
+
+# C/C++ Support
+
+* baremetal: cpp.generateCompilerListingFiles has been implemented for
+ KEIL ARM Clang
+* baremetal: cpp.enableDefinesByLanguage does now work with SDCC as well.
+
+
+# Qt Support
+
+* Moc is now disabled when building aggregate products.
+
+
+# Android Support
+
+* Support for the new directory layout of Qt6 has been added (QBS-1609).
+* Input file generation for androiddeployqt has been improved (QBS-1613).
+* Debugging experience of multi-architecture Android projects with Qbs and
+ Qt Creator has been improved. Binaries are now generated in a directory
+ layout that Qt Creator expects and debug information is no longer stripped
+ away.
+
+# Documentation
+
+* baremetal: A new WiFi access point example for the ESP8266 MCU using the
+ GCC toolchain has been added.
+* baremetal: A new example for Nordic's pca10001 board has been
+ added. It supports GCC, KEIL and IAR.
+* baremetal: The stm32f103 example supports IAR as well.
+* A howto has been added showing how to easily disable compiler
+ warnings.
+* Instructions for building Qbs with CMake have been added (QBS-1618).
+
+# Important Bug Fixes
+
+* Qt modules could not be used in Export item when building for Android
+ (QBS-1576).
+* Variable substitution in Info.plist files was broken for
+ '@VAR@' syntax (QBS-1601).
+* CppApplication failed to build for Android when using Qt > 5.14.0
+ and multiplexing over multiple architectures (QBS-1608).
+* Moc output was broken when including Boost project header files (QBS-1621).
+
+# Contributors
+
+* Alberto Mardegan
+* André Pönitz
+* Christian Kandeler
+* Christian Stenger
+* Cristian Adam
+* Denis Shienkov
+* Eike Ziller
+* Ivan Komissarov
+* Jochen Ulrich
+* Kai Dohmen
+* Mitch Curtis
+* Orgad Shaneh
+* Raphaël Cotty
+* Richard Weickelt
diff --git a/changelogs/changes-1.18.2.md b/changelogs/changes-1.18.2.md
new file mode 100644
index 000000000..5019e491f
--- /dev/null
+++ b/changelogs/changes-1.18.2.md
@@ -0,0 +1,41 @@
+# C/C++ Support
+
+* qbs-setup-toolchains is now able to detect clang-cl properly
+* The Library and Include probes take more paths into account on Linux to better
+ support containerization systems such as Flatpak.
+* Xcode autodetection now uses xcode-select to find Xcode on the system.
+
+
+# Protocol Buffers Support (Protobuf Module)
+
+* A missing nanopb generator file extension on windows has been added.
+* The problem that property _libraryName was incorrect when protobuf was not
+ found has been fixed.
+
+
+# Android Support
+
+* An assertion when building Android applications using additional java classes
+ with native methods has been fixed (QBS-1628).
+
+
+# Qt Support
+
+* A problem related to handling Qt6EntryPoint in the Qt module provider has been
+ fixed.
+
+
+# Infrastructure
+
+* A Qt4 docker image for basic testing has been added.
+
+
+# Contributors
+
+* Christian Kandeler
+* Eike Ziller
+* Ivan Komissarov
+* Jan Blackquill
+* Kai Dohmen
+* Raphaël Cotty
+* Richard Weickelt
diff --git a/changelogs/changes-1.19.0.md b/changelogs/changes-1.19.0.md
new file mode 100644
index 000000000..a981ea335
--- /dev/null
+++ b/changelogs/changes-1.19.0.md
@@ -0,0 +1,65 @@
+# General
+
+* New codesign module was added to implement code signing for Apple, Android and Windows
+ platforms (QBS-899, QBS-1546).
+* It is now possible to build Qbs with Qt 6.
+* Project files update API depending on the Qt.gui module was removed. This allows to enable
+ project files update API unconditionally when building Qbs.
+* Convenience items such as Application, DynamicLibrary and StaticLibrary now properly install
+ artifacts when multiplexing.
+
+# C/C++ Support
+
+* Qbs now supports multiple MSVC compiler versions installed in one Visual Studio installation
+ (QBS-1498). Also, multiple compiler versions are properly detected by qbs setup-toolchains.
+* It is now possible to specify Windows SDK version for the MSVC and clang-cl toolchains via the
+ new cpp.windowsSdkVersion property.
+* baremetal: Fix generation of compiler listing files with custom extension for the SDCC compiler.
+* baremetal: Fix generation of compiler listing files for the ARMCC compiler.
+* baremetal: Fix detection for Keil toolchains.
+* baremetal: Add support for HCS08 architectures to SDCC and IAR toolchains (QBS-1631, QBS-1629).
+* baremetal: Add support for HCS12 architectures to GCC and IAR toolchains (QBS-1630, QBS-1550).
+
+# Qt Support
+
+* Fix possible command line length issue with qmlimportscanner (QBS-1633).
+* Fix accessing binaries from libexec for Qt 6.1 and above (QBS-1636).
+
+# Android Support
+
+* Added a workaround for the Qt.Network module dependencies for 5.15.0 < Qt < 5.15.3 (QTBUG-87288)
+* Fix aapt command invocation on Windows.
+* Added support for ndk 22.1.7171670.
+
+# Documentation
+
+* Added How-To about codesigning on Apple platforms.
+* Cocoa Touch Application example is brought up-to date to use modern Apple practices.
+* Added example how to use the cpp.linkerVariant property.
+* Added missing documentation for the cpp.toolchainInstallPath property.
+* Added missing documentation for the supported 'bare-metal' architectures.
+
+# Important Bug Fixes
+
+* Added support for Xcode 12.5 (QBS-1644).
+* Fix support for Python 3.9 for building Apple DMG images (QBS-1642).
+
+# Infrastructure
+
+* Ubuntu Focal image was updated to use Qt 5.15.2 and Qbs 1.17.1.
+* Added automated tests for Qt 6 for macOS, Linux and Windows.
+* Added a self-hosted runner to run 'bare-metal' tests on Windows.
+
+# Contributors
+
+* Andrey Filipenkov
+* Denis Shienkov
+* Christian Kandeler
+* Jan Blackquill
+* Jake Petroules
+* Ivan Komissarov
+* Max Bespalov
+* Mitch Curtis
+* Orgad Shaneh
+* Raphaël Cotty
+* Richard Weickelt
diff --git a/changelogs/changes-1.19.1.md b/changelogs/changes-1.19.1.md
new file mode 100644
index 000000000..c4cab506f
--- /dev/null
+++ b/changelogs/changes-1.19.1.md
@@ -0,0 +1,12 @@
+# C/C++ Support
+
+* Fix system include support with MSVC >= 19.29.30037
+
+# Qt Support
+
+* Fix possible command line length issue with qmlimportscanner when cross-compiling (QBS-1633).
+
+# Apple platforms
+
+* Fix stripping debug symbols in multiplexed products when cpp.separateDebugInformation
+ is false (QBS-1647)
diff --git a/changelogs/changes-1.19.2.md b/changelogs/changes-1.19.2.md
new file mode 100644
index 000000000..8f76b5a38
--- /dev/null
+++ b/changelogs/changes-1.19.2.md
@@ -0,0 +1,5 @@
+# Important Bug Fixes
+
+* codesign: Fix checking if product is a framework (QBS-1649)
+* codesign: Add additional signing flags at end of command (QBS-1651)
+* codesign: Add new codesign.timestampAlgorithm property (QBS-1651)
diff --git a/changelogs/changes-1.20.0.md b/changelogs/changes-1.20.0.md
new file mode 100644
index 000000000..1cf7819d7
--- /dev/null
+++ b/changelogs/changes-1.20.0.md
@@ -0,0 +1,62 @@
+# General
+
+* CMake and Qbs builds now fully support building with Qt6.
+* Qbs now prints the old properties set when refusing to build a project with
+ changed properties.
+* Added convenience command to qbs-config to add a profile in one go instead of
+ setting properties separately (QTCREATORBUG-25463).
+* Added profiling timer for module providers.
+
+# C/C++ Support
+
+* Added support for the COSMIC COLDFIRE (also known as M68K) compiler (QBS-1648).
+* Added support for the COSMIC HCS08 compiler (QBS-1641).
+* Added support for the COSMIC HCS12 compiler (QBS-1640).
+* Added support for the COSMIC STM8 compiler (QBS-1639).
+* Added support for the COSMIC STM32 compiler (QBS-1638).
+* Added support for the new Digital Mars toolchain (QBS-1636).
+* The new cpp.enableCxxLanguageMacro property was added for the MSVC toolchain
+ that controls the /Zc:__cplusplus required for proper support of the new
+ C++ standards (QBS-1655).
+* Added support for the "c++20" language version for the MSVC toolchain
+ which results in adding the /std:c++latest flag (QBS-1656).
+
+# Qt Support
+
+* Consider "external" modules
+* Fix support for qml binaries that were moved to the libexec directory in Qt 6.2 (QBS-1636).
+
+# Android Support
+
+* Added option to use dex compiler d8 instead of dx.
+* Ministro support was removed.
+* Fix link with static stl on Android (QBS-1654)
+* The default Android Asset Packaging Tool was change from aapt to aapt2.
+
+# Apple Support
+
+* Qbs now uses embedded build specs from Xcode 12.4 when bundle.useXcodeBuildSpecs is true
+ instead of older specs from Xcode 9.2.
+
+# Important Bug Fixes
+
+* Fix handling cpp.linkerWrapper with the MSVC toolchain (QBS-1653).
+
+# Infrastructure
+
+* Windows-only tests are moved to a separate tst_blackbox_windows binary.
+* Qbs was updated to 1.18.2 in Docker images as well as in GitHub actions jobs.
+* Added Qbs build and tests with the MinGW toolchain.
+* Added CMake build with Qt6 on Linux.
+
+# Contributors
+
+* Christian Kandeler
+* Denis Shienkov
+* Eike Ziller
+* Ivan Komissarov
+* Jan Blackquill
+* Mitch Curtis
+* Oswald Buddenhagen
+* Raphaël Cotty
+* Richard Weickelt
diff --git a/changelogs/changes-1.20.1.md b/changelogs/changes-1.20.1.md
new file mode 100644
index 000000000..a33ed044b
--- /dev/null
+++ b/changelogs/changes-1.20.1.md
@@ -0,0 +1,12 @@
+# General
+
+* Fixed target linker flags on FreeBSD (QBS-1312).
+* Fixed file time precision on FreeBSD.
+* Added additional logging to the Qt module provider when no qmakes were found.
+* Some fixes to CMake build.
+* Fixed incorrect usage of the qmlcachegen binary (QBS-1676).
+
+# Android Support
+
+* Added stripping for release builds.
+* Fix generation of aab packages.
diff --git a/changelogs/changes-1.21.0.md b/changelogs/changes-1.21.0.md
new file mode 100644
index 000000000..f8c2dbd95
--- /dev/null
+++ b/changelogs/changes-1.21.0.md
@@ -0,0 +1,56 @@
+# General
+
+* A new qbsModuleProviders property was added to Project and Product items which allows
+ to specify which providers will be run (QBS-1604).
+* Added a new library for reading *.pc files which allows to avoid launching multiple pkg-config
+ processes and also gives QBS more information about dependencies between *.pc files (QBS-1615).
+* A new qbspkgconfig provider was added which will replace the fallback provider (QBS-1614).
+ This new provider uses the built-in library and is capable of setting Qt libraries as well.
+* capnproto and protobuf modules can now use runtime provided by the qbspkgconfig provider.
+* A new ConanfileProbe.verbose property was added which can be useful to debug problems with
+ Conan.
+* Qbs no longer migrates the "profiles/" dir from earlier Qbs versions (QTCREATORBUG-26475).
+ Old directories might be cleaned up manually.
+* FileInfo now always uses high-precision timer on all OSes.
+* Fixed a problem with overriding stringList properties in ModuleProviders from command-line.
+
+# C/C++ Support
+
+* Added support for c++23.
+* Add Elbrus E2K architecture for the GCC toolchain (QBS-1675).
+* COSMIC cpp module now avoids using relative file paths as much as possible.
+* Some refactoring was done in the cpp modules to share more code.
+
+# Android Support
+
+* Added Android.ndk.buildId property which allows to overwrite the default value (sha1) for
+ the --build-id linker flag.
+* Fixed reading *.prl files with Qt >= 6.0.
+* Fixed rcc path with Qt >= 6.2.
+
+# Documentation
+
+* Added a new page with the list of ModuleProviders.
+* Qt provider now has its own page.
+* Clarified that application won't be runnable by default, unless env or rpaths are set correctly.
+
+# Infrastructure
+
+* Added standalone job for building documentation.
+* Xcode version was bumped to 12.5.1.
+* Added OpenSUSE Leap docker image.
+* Added Android tests with different NDK versions.
+* Fixed QMake build with Qt 6.
+
+# Contributors
+
+* Christian Kandeler
+* Christian Stenger
+* Davide Pesavento
+* Denis Shienkov
+* Ivan Komissarov
+* Kai Dohmen
+* Orgad Shaneh
+* Raphaël Cotty
+* Richard Weickelt
+* Thorbjørn Lindeijer
diff --git a/changelogs/changes-1.22.0.md b/changelogs/changes-1.22.0.md
new file mode 100644
index 000000000..5de8a1a21
--- /dev/null
+++ b/changelogs/changes-1.22.0.md
@@ -0,0 +1,31 @@
+# General
+* A new Host service was introduced, providing information about the host system that
+ used to be available from the qbs module, but did not really belong there.
+ In addition, some more qbs module properties have moved to the FileInfo service.
+* The product variable in Export items now points to the importing product, rather than
+ the exporting one.
+* Probes are now also available in ModuleProvider items.
+
+# C/C++ Support
+* Added support for the Open Watcom toolchain.
+* Reduced unneeded re-linking on Linux by ignoring changes to weak symbols in library
+ dependencies by default.
+
+# Qt Support
+* Android multi-arch packages are supported again wth Qt >= 6.3.
+* We now use cpp.systemIncludePaths for Qt headers, so that building Qt applications
+ no longer triggers warnings from Qt headers unrelated to the user code.
+
+# Infrastructure
+* Added coverage for Digital Mars compiler.
+* Added coverage for static Qt builds.
+
+# Contributors
+* Christian Kandeler
+* Denis Shienkov
+* Ivan Komissarov
+* Jan Blackquill
+* Leena Miettinen
+* Marius Gripsgard
+* Mitch Curtis
+* Raphael Cotty
diff --git a/changelogs/changes-1.22.1.md b/changelogs/changes-1.22.1.md
new file mode 100644
index 000000000..34c58f2ff
--- /dev/null
+++ b/changelogs/changes-1.22.1.md
@@ -0,0 +1,16 @@
+# General
+
+* Adapted to changes in Xcode 13.3 (QBS-1693).
+* Re-added several bugfixes from 1.21.1 that got lost in 1.22.0.
+
+# Qt Support
+
+* Fixed building against static Qt 6 (QBS-1692).
+
+# Android Support
+
+* Fixed wrong include path being added to the command line (QBS-1691).
+
+# Documentation
+
+* Fixed outdated use of "product" in Export items (QBS-1694).
diff --git a/changelogs/changes-1.23.0.md b/changelogs/changes-1.23.0.md
new file mode 100644
index 000000000..e8234e3fe
--- /dev/null
+++ b/changelogs/changes-1.23.0.md
@@ -0,0 +1,16 @@
+# C/C++ Support
+* Added new module Sanitizers.address for simple cross-platform ASan configuration.
+
+# Qt Support
+* Add the now-required /permissive- flag for MSVC automatically.
+
+# Android Support
+* Support use of cmdline-tools in addition to SDK tools.
+* Consider .jar files also in the product itself, not just in dependencies.
+
+# Contributors
+* Christian Kandeler
+* Dmitry Shachnev
+* Ivan Komissarov
+* Max Bespalov
+* Orgad Shaneh
diff --git a/changelogs/changes-1.23.1.md b/changelogs/changes-1.23.1.md
new file mode 100644
index 000000000..65536bfeb
--- /dev/null
+++ b/changelogs/changes-1.23.1.md
@@ -0,0 +1,19 @@
+# C/C++ Support
+* Added support for c17 and c2x values in cpp.cLanguageVersion.
+* Added support for cpp.cLanguageVersion for the MSVC toolchain.
+* Fix passing linker scripts to iar and keil toolchains (QBS-1704).
+
+# Qt Support
+* Adapted to new location of qscxmlc in Qt 6.3.
+* Adapted to new location of qhelpgenerator in Qt 6.3.
+* Fixed setting up Qt 6.3 with qbspkgconfig.
+* Added QtScript module to the source tarballs (QBS-1703).
+
+# Other modules
+* Fixed protobuf linking on macOS 11.
+* Fixed handling empty variables in qbspkgconfig (QBS-1702)
+
+# Contributors
+* Christian Kandeler
+* Ivan Komissarov
+* Orgad Shaneh
diff --git a/changelogs/changes-1.23.2.md b/changelogs/changes-1.23.2.md
new file mode 100644
index 000000000..56e8a7862
--- /dev/null
+++ b/changelogs/changes-1.23.2.md
@@ -0,0 +1,10 @@
+# Qt Support
+* Set _ENABLE_EXTENDED_ALIGNED_STORAGE for MSVC
+
+# Documentation
+* Fix installation with cmake
+
+# Contributors
+* Christian Kandeler
+* Ivan Komissarov
+* Marius Gripsgard
diff --git a/changelogs/changes-1.24.0.md b/changelogs/changes-1.24.0.md
new file mode 100644
index 000000000..45250ec98
--- /dev/null
+++ b/changelogs/changes-1.24.0.md
@@ -0,0 +1,12 @@
+# General
+* Users can now control if and when warnings for deprecated properties are emitted.
+* Added FileInfo.executableSuffix().
+
+# Qt Support
+* Minimum windows version is now 10.0, like for cmake and qmake.
+
+# Contributors
+* Christian Kandeler
+* Ivan Komissarov
+* Petr Mikhalicin
+* Raphael Cotty
diff --git a/changelogs/changes-1.24.1.md b/changelogs/changes-1.24.1.md
new file mode 100644
index 000000000..acbf25fa3
--- /dev/null
+++ b/changelogs/changes-1.24.1.md
@@ -0,0 +1,9 @@
+# C/C++ Support
+Fix macros and include paths retrieval for IAR
+
+# Build System
+Add fix for cmake >= 3.18
+
+# Contributors
+* Denis Shienkov
+* Eike Ziller
diff --git a/changelogs/changes-2.0.0.md b/changelogs/changes-2.0.0.md
new file mode 100644
index 000000000..14cacb761
--- /dev/null
+++ b/changelogs/changes-2.0.0.md
@@ -0,0 +1,22 @@
+# General
+* Switched JavaScript engine from QtScript to QuickJS
+* Removed the long-deprecated loadFile() and loadExtension() functions
+* Removed the qmake project files
+
+# Qt support
+* Adapt to androiddeployqt on Windows no longer accepting tool paths without suffix in 6.4 (QTBUG-111558)
+
+# BareMetal support
+* Added support for HPPA architectures
+
+# Other modules
+* Renamed "name" to "appName" in the freedesktop module to prevent clash with built-in property
+
+# Infrastructure
+* Added USBSAN CI job
+
+# Contributors
+* Christian Kandeler
+* Ivan Komissarov
+* Orgad Shaneh
+* Pino Toscano
diff --git a/changelogs/changes-2.0.1.md b/changelogs/changes-2.0.1.md
new file mode 100644
index 000000000..7f28b3e55
--- /dev/null
+++ b/changelogs/changes-2.0.1.md
@@ -0,0 +1,27 @@
+# General
+* Fixed crash when importing missing JavaScript file (QBS-1730).
+
+# C/C++ Support
+* Fixed building applications with mingw toolchain and Qt6 (QBS-1724).
+
+# Apple Support
+* Added support for Xcode 14.3.
+* Fixed codesigning on macOS (QBS-1722).
+* Fixed detecting Xcode via xcode-select tool.
+
+# Qt Support
+* Fixed support for Qt 6.3 on iOS.
+* Fixed install-qt.sh to properly support Qt for iOS.
+* Do not setup Qt in qbspkgconfig when cross compiling (QBS-1717).
+
+# Build System
+* Fixed qbsbuildconfig module.
+* Fixed build with Qt6.5.
+* Updated CI to test via Qt 6.5 on macOS and Windows.
+* Updated CI to test via Xcode 14.2 on macOS.
+
+# Contributors
+* Björn Schäpers
+* Christian Kandeler
+* Ivan Komissarov
+* Kai Dohmen
diff --git a/changelogs/changes-2.0.2.md b/changelogs/changes-2.0.2.md
new file mode 100644
index 000000000..cec220db4
--- /dev/null
+++ b/changelogs/changes-2.0.2.md
@@ -0,0 +1,12 @@
+# General
+* Fixed handling of non-string exceptions (QBS-1734).
+* Fixed Utilities.versionCompare() for four-segment versions numbers (QBS-1733).
+
+# Qt Support
+* Added special handling for unclean paths in prl files (QBS-1732).
+* Fixed support for static plugins and Qt >= 6.5 (QBS-1732).
+
+# Contributors
+* Christian Kandeler
+* Ivan Komissarov
+* Leon Buckel
diff --git a/changelogs/changes-2.1.0.md b/changelogs/changes-2.1.0.md
new file mode 100644
index 000000000..bec55631b
--- /dev/null
+++ b/changelogs/changes-2.1.0.md
@@ -0,0 +1,28 @@
+# General
+* Improved speed and correctness of project resolving.
+* Fixed possible segmentation fault when quitting a session.
+* Fixed regression in BinaryFile (QBS-1740).
+* Added possibility to import and export Qbs settings in the JSON format (QBS-1685).
+
+# Modules
+* Dependencies are no longer merged by default in the qbspkgconfig module provider (QBS-1710).
+* Protobuf modules now export the desired c++ version (c++17 on macOS, c++14 otherwise).
+
+# Apple Support
+* Updated dmgbuild to the upstream.
+
+# Documentation
+* Added documentation for the path, filePath, product and project variables.
+* Added sample codesign settings to the Cocoa Touch Application example.
+
+# Build System
+* Updated Qt static Docker image to Qt 6.5.0 and Qbs 1.24.
+
+# Contributors
+* Andrey Filipenkov
+* Christian Kandeler
+* Denis Shienkov
+* Ivan Komissarov
+* Marc Mutz
+* Raphael Cotty
+* Thiemo van Engelen
diff --git a/changelogs/changes-2.1.1.md b/changelogs/changes-2.1.1.md
new file mode 100644
index 000000000..d0cdaad4f
--- /dev/null
+++ b/changelogs/changes-2.1.1.md
@@ -0,0 +1,8 @@
+# General
+* Fixed Probe lookup in multiplexed products.
+* Fixed excessively slow module merging in some circumstances.
+* Fixed building QuickJS on x86 systems.
+
+# Contributors
+* Christian Kandeler
+* Ivan Komissarov
diff --git a/changelogs/changes-2.1.2.md b/changelogs/changes-2.1.2.md
new file mode 100644
index 000000000..b83ecdbf5
--- /dev/null
+++ b/changelogs/changes-2.1.2.md
@@ -0,0 +1,19 @@
+# General
+* Fixed handling JS floating-point values for x86.
+* Fixed scope pollution and potential crash when assigning to provider properties (QBS-1747).
+* Fixed potential access to freed JSValues (QBS-1751).
+
+# Qt
+* Fixed building against Qt with "profiling" build variant (QBS-1758).
+
+# Apple
+* Fixed bundle module with Xcode-less profiles.
+* Fixed ApplicationExtension with Xcode-less profiles.
+
+# Infrastructure
+* Added CI job to be able to test XCode-less profiles on macOS.
+
+# Contributors
+* Christian Kandeler
+* Dmitry Shachnev
+* Ivan Komissarov
diff --git a/changelogs/changes-2.2.0.md b/changelogs/changes-2.2.0.md
new file mode 100644
index 000000000..ffa867f0a
--- /dev/null
+++ b/changelogs/changes-2.2.0.md
@@ -0,0 +1,36 @@
+# General
+* Improved speed of project resolving by employing multiple CPU cores, if available.
+* Improved speed of probes execution on macOS.
+* Object and array (var and varList) properties are now immutable in Probe items.
+
+# Language
+* Modules can now contain `Parameters` items.
+* ModuleProviders can now contain `PropertyOptions` items.
+
+# C/C++ support
+* Allow `"mold"` as value for `cpp.linkerVariant`.
+* The systemIncludePaths property is now handled correctly for clang-cl.
+
+# Apple support
+* Updated dmgbuild tool. This fixes bug that additional licenses are not shown in
+ the combobox in the resulting DMG image.
+
+# Qt support
+* Only create qbs modules for those Qt modules that products actually need.
+* Users can now opt out of using RPATH when linking on Linux.
+
+# Other
+* Protobuf module now requires pkg-config or built-in runtime.
+* Protobuf module now requires C++17 on all platforms.
+* Capnproto module: the outputDir property is now mutable.
+* Added support for Groups to the VisualStudio generator.
+* pkgconfig module provider: mergeDependencies property is deprecated.
+
+# Contributors
+* Christian Kandeler
+* Dmitrii Meshkov
+* Ivan Komissarov
+* Nick Karg
+* Serhii Olendarenko
+* Thiemo van Engelen
+* Thorbjørn Lindeijer
diff --git a/changelogs/changes-2.2.1.md b/changelogs/changes-2.2.1.md
new file mode 100644
index 000000000..323da791a
--- /dev/null
+++ b/changelogs/changes-2.2.1.md
@@ -0,0 +1,12 @@
+# Language
+* Fixed JavaScript Date() constructor on Windows (QBS-1768).
+
+# C/C++ support
+* Worked around crash in cl.exe when retrieving built-in defines (QBS-1743).
+
+# CI
+* Fixed building release packages.
+
+# Contributors
+* Christian Kandeler
+* Ivan Komissarov
diff --git a/changelogs/changes-2.2.2.md b/changelogs/changes-2.2.2.md
new file mode 100644
index 000000000..2f82381ea
--- /dev/null
+++ b/changelogs/changes-2.2.2.md
@@ -0,0 +1,18 @@
+# General
+* Fixed name collision check for multi-part modules (QBS-1772).
+* Fixed potential assertion when attaching properties on non-present modules (QBS-1776).
+
+# C/C++
+* Fixed handling assember flags with MSVC (QBS-1774).
+
+# Qt
+* Fixed the qbspkgconfig Qt provider for the case when there is no Qt (QBS-1777).
+
+# Other
+* Make protobuf usable without qbspkgconfig again (QBS-1663).
+* Add support for the definePrefix option to qbspkgconfig.
+
+# Contributors
+* Björn Schäpers
+* Christian Kandeler
+* Ivan Komissarov
diff --git a/changelogs/changes-2.3.0.md b/changelogs/changes-2.3.0.md
new file mode 100644
index 000000000..9062b27ff
--- /dev/null
+++ b/changelogs/changes-2.3.0.md
@@ -0,0 +1,37 @@
+# General
+* Added an LSP language server that provides support for following symbols and completion
+ in IDEs (QBS-395).
+* Module properties are now directly available within groups in modules (QBS-1770).
+* Added possibility to export products to CMake via the new Exporter.cmake module.
+* Deprecated the pkgconfig-based fallback module provider.
+* If a project needs to be re-resolved, we now print the reason.
+* Added some tutorials.
+* Wildards handling was rewritten to track changes more accurate.
+* Module 'validate' scripts are no longer run for erroneous product in IDE mode.
+* Add example how to use Exporters.
+
+# C/C++ Support
+* Private dependencies of products are not traversed more than once anymore (QBS-1714).
+
+# Language
+* Module properties are now accessible for groups in modules (QBS-1770).
+* Fixed pathList properties in Probes (QBS-1785).
+* The qbspkgconfig.mergeDependencies property was removed.
+* ModuleProviders now support the 'allowedValues' property of the PropertyDeclaration item
+ (QBS-1748).
+
+# Apple
+* Adapted darwin support to Xcode 15.3.
+
+# CI
+* Changed Linux Docker images from Focal to Jammy.
+* Updated compilers and linters to recent versions.
+* Added clang-format job to check code style.
+* The project.withExamples property was removed.
+
+# Contributors
+* Christian Kandeler
+* Dmitrii Meshkov
+* Ivan Komissarov
+* Raphael Cotty
+* Richard Weickelt
diff --git a/changelogs/changes-2.3.1.md b/changelogs/changes-2.3.1.md
new file mode 100644
index 000000000..6c4b6c0ad
--- /dev/null
+++ b/changelogs/changes-2.3.1.md
@@ -0,0 +1,17 @@
+# Language
+* Fixed look-up of qbs properties in module providers via probes (QBS-1742).
+
+# Apple support
+* Fixed codesing module when multiplexing over build variants (QBS-1775).
+
+# Qt support
+* Fixed retrieving minimum macOS/iOS versions for Qt 6.7.1.
+
+# CI
+* Fixed release jobs.
+* Fixed macOS 14.2 job.
+* Updated GitHub actions to recent versions to avoid warnings.
+
+# Contributors
+* Christian Kandeler
+* Ivan Komissarov
diff --git a/cmake/QbsBuildConfig.cmake b/cmake/QbsBuildConfig.cmake
new file mode 100644
index 000000000..e0a7d76d7
--- /dev/null
+++ b/cmake/QbsBuildConfig.cmake
@@ -0,0 +1,261 @@
+option(WITH_TESTS "Build Tests" ON)
+option(WITH_UNIT_TESTS "Build Unit Tests" OFF)
+option(INSTALL_PUBLIC_HEADERS "Whether to install public headers" ON)
+option(QBS_ENABLE_RPATH "Whether to enable RPATH" ON)
+
+include(CMakeDependentOption)
+cmake_dependent_option(QBS_QUICKJS_LEAK_CHECK "Whether to check for quickjs leaks at the end" ON "CMAKE_BUILD_TYPE STREQUAL Debug" OFF)
+
+set(QBS_APP_INSTALL_DIR "bin" CACHE STRING "Relative install location for Qbs binaries.")
+# default paths
+set(QBS_LIBDIR_NAME "lib")
+if(WIN32)
+ set(_DEFAULT_LIB_INSTALL_DIR ${QBS_APP_INSTALL_DIR})
+ set(_DEFAULT_LIBEXEC_INSTALL_DIR ${QBS_APP_INSTALL_DIR})
+else()
+ set(_DEFAULT_LIB_INSTALL_DIR ${QBS_LIBDIR_NAME})
+ set(_DEFAULT_LIBEXEC_INSTALL_DIR "libexec/qbs")
+endif()
+
+# path options
+set(QBS_OUTPUT_PREFIX "" CACHE STRING "Qbs build output location relative to CMAKE_BINARY_DIR.")
+set(QBS_LIB_INSTALL_DIR "${_DEFAULT_LIB_INSTALL_DIR}" CACHE STRING "Relative install location for Qbs libraries.")
+set(QBS_DLL_INSTALL_DIR "${QBS_LIB_INSTALL_DIR}" CACHE STRING "Relative install location for Qbs DLLs.")
+set(QBS_LIBEXEC_INSTALL_DIR "${_DEFAULT_LIBEXEC_INSTALL_DIR}" CACHE STRING "Relative install location for Qbs libexec.")
+set(QBS_PLUGINS_INSTALL_BASE "${QBS_LIBDIR_NAME}" CACHE STRING "Relative install location for Qbs plugins.")
+set(QBS_RESOURCES_INSTALL_BASE "." CACHE STRING "Relative install location for Qbs resources.")
+set(QBS_HEADERS_INSTALL_DIR "include/qbs" CACHE STRING "Relative install location for Qbs headers.")
+set(QBS_DOC_INSTALL_DIR "${QBS_RESOURCES_INSTALL_BASE}/share/doc/qbs" CACHE STRING "Relative install location for Qbs documentation.")
+set(QBS_DOC_HTML_DIR_NAME "html" CACHE STRING "The name of the dir with HTML files, appended to QBS_DOC_INSTALL_DIR.")
+
+set(QBS_PLUGINS_INSTALL_DIR "${QBS_PLUGINS_INSTALL_BASE}/qbs/plugins")
+set(QBS_RESOURCES_INSTALL_DIR "${QBS_RESOURCES_INSTALL_BASE}/share")
+
+# rpaths
+file(RELATIVE_PATH QBS_RELATIVE_LIBEXEC_RPATH "/${QBS_LIBEXEC_INSTALL_DIR}" "/${QBS_LIB_INSTALL_DIR}")
+file(RELATIVE_PATH QBS_RELATIVE_APP_RPATH "/${QBS_APP_INSTALL_DIR}" "/${QBS_LIB_INSTALL_DIR}")
+file(RELATIVE_PATH QBS_RELATIVE_PLUGINS_RPATH "/${QBS_PLUGINS_INSTALL_DIR}" "/${QBS_LIB_INSTALL_DIR}")
+
+if(WIN32 OR NOT QBS_ENABLE_RPATH)
+ set(QBS_MACOSX_RPATH OFF)
+ set(QBS_LIB_RPATH "")
+ set(QBS_LIBEXEC_RPATH "")
+ set(QBS_APP_RPATH "")
+ set(QBS_PLUGINS_RPATH "")
+elseif(APPLE)
+ set(QBS_MACOSX_RPATH ON)
+ set(QBS_LIB_RPATH "@loader_path")
+ set(QBS_LIBEXEC_RPATH "@loader_path/${QBS_RELATIVE_LIBEXEC_RPATH}")
+ set(QBS_APP_RPATH "@loader_path/${QBS_RELATIVE_APP_RPATH}")
+ set(QBS_PLUGINS_RPATH "@loader_path/${QBS_RELATIVE_PLUGINS_RPATH}")
+else()
+ set(QBS_MACOSX_RPATH OFF)
+ set(QBS_LIB_RPATH "\$ORIGIN")
+ set(QBS_LIBEXEC_RPATH "\$ORIGIN/${QBS_RELATIVE_LIBEXEC_RPATH}")
+ set(QBS_APP_RPATH "\$ORIGIN/${QBS_RELATIVE_APP_RPATH}")
+ set(QBS_PLUGINS_RPATH "\$ORIGIN/${QBS_RELATIVE_PLUGINS_RPATH}")
+endif()
+
+function(get_update_path_command var)
+ if(WIN32)
+ get_target_property(_QTCORE_LIBRARY Qt${QT_VERSION_MAJOR}::Core IMPORTED_LOCATION_RELEASE)
+ if(NOT _QTCORE_LIBRARY)
+ get_target_property(_QTCORE_LIBRARY Qt${QT_VERSION_MAJOR}::Core IMPORTED_LOCATION_DEBUG)
+ endif()
+ get_filename_component(_QT_LIBRARY_PATH "${_QTCORE_LIBRARY}" DIRECTORY)
+ get_target_property(_QBS_LIBRARY_PATH qbscore LIBRARY_OUTPUT_DIRECTORY)
+ file(TO_NATIVE_PATH "${_QT_LIBRARY_PATH}\;${_QBS_LIBRARY_PATH}\;$ENV{PATH}" _NEW_PATH)
+ set(${var} "PATH=${_NEW_PATH}" PARENT_SCOPE)
+ else()
+ set(${var} "")
+ endif()
+endfunction()
+
+if(WITH_UNIT_TESTS)
+ set(QBS_UNIT_TESTS_DEFINES "QBS_ENABLE_UNIT_TESTS")
+else()
+ set(QBS_UNIT_TESTS_DEFINES "")
+endif()
+
+file(RELATIVE_PATH QBS_RELATIVE_LIBEXEC_PATH "/${QBS_APP_INSTALL_DIR}" "/${QBS_LIBEXEC_INSTALL_DIR}")
+file(RELATIVE_PATH QBS_RELATIVE_SEARCH_PATH "/${QBS_APP_INSTALL_DIR}" "/${QBS_RESOURCES_INSTALL_BASE}")
+file(RELATIVE_PATH QBS_RELATIVE_PLUGINS_PATH "/${QBS_APP_INSTALL_DIR}" "/${QBS_PLUGINS_INSTALL_BASE}")
+
+set(DEFAULT_DEFINES "")
+if(WIN32)
+ list(APPEND DEFAULT_DEFINES UNICODE _UNICODE _SCL_SECURE_NO_WARNINGS)
+endif()
+if(WITH_TESTS)
+ list(APPEND DEFAULT_DEFINES QBS_WITH_TESTS)
+endif()
+
+# CMake 3.10 doesn't have list(TRANSFORM)
+function(list_transform_prepend var prefix)
+ set(temp "")
+ foreach(f ${${var}})
+ list(APPEND temp "${prefix}${f}")
+ endforeach()
+ set(${var} "${temp}" PARENT_SCOPE)
+endfunction()
+
+function(add_qbs_app target_name)
+ cmake_parse_arguments(_arg
+ ""
+ "DESTINATION"
+ "DEFINES;PUBLIC_DEFINES;DEPENDS;PUBLIC_DEPENDS;INCLUDES;PUBLIC_INCLUDES;SOURCES;"
+ ${ARGN}
+ )
+
+ if (${_arg_UNPARSED_ARGUMENTS})
+ message(FATAL_ERROR "add_qbs_app had unparsed arguments")
+ endif()
+
+ set(_DESTINATION "${QBS_APP_INSTALL_DIR}")
+ if(_arg_DESTINATION)
+ set(_DESTINATION "${_arg_DESTINATION}")
+ endif()
+
+ add_executable(${target_name} ${_arg_SOURCES})
+ target_compile_definitions(
+ ${target_name} PRIVATE ${_arg_DEFINES} ${DEFAULT_DEFINES} PUBLIC ${_arg_PUBLIC_DEFINES})
+ target_include_directories(
+ ${target_name} PRIVATE ${_arg_INCLUDES} PUBLIC ${_arg_PUBLIC_INCLUDES})
+ target_link_libraries(${target_name} PRIVATE ${_arg_DEPENDS} PUBLIC ${_arg_PUBLIC_DEPENDS})
+
+ set_target_properties(${target_name} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${_DESTINATION}
+ BUILD_RPATH "${QBS_APP_RPATH}"
+ INSTALL_RPATH "${QBS_APP_RPATH}"
+ MACOSX_RPATH ${QBS_MACOSX_RPATH}
+ )
+ install(TARGETS ${target_name} RUNTIME DESTINATION ${_DESTINATION})
+endfunction()
+
+function(add_qbs_library target_name)
+ cmake_parse_arguments(_arg
+ "STATIC"
+ ""
+ "DEFINES;PUBLIC_DEFINES;DEPENDS;PUBLIC_DEPENDS;INCLUDES;PUBLIC_INCLUDES;SOURCES;"
+ ${ARGN}
+ )
+
+ if (${_arg_UNPARSED_ARGUMENTS})
+ message(FATAL_ERROR "add_qbs_library had unparsed arguments")
+ endif()
+
+ set(library_type SHARED)
+ if (_arg_STATIC)
+ set(library_type STATIC)
+ endif()
+
+ string(REGEX REPLACE "\\.[0-9]+$" "" _SOVERSION ${QBS_VERSION})
+
+ add_library(${target_name} ${library_type} ${_arg_SOURCES})
+ target_compile_definitions(
+ ${target_name}
+ PRIVATE ${_arg_DEFINES} ${DEFAULT_DEFINES}
+ PUBLIC ${_arg_PUBLIC_DEFINES})
+ target_include_directories(
+ ${target_name}
+ PRIVATE ${_arg_INCLUDES}
+ PUBLIC ${_arg_PUBLIC_INCLUDES}
+ INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>)
+ target_link_libraries(${target_name} PRIVATE ${_arg_DEPENDS} PUBLIC ${_arg_PUBLIC_DEPENDS})
+
+ set_target_properties(${target_name} PROPERTIES
+ ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_LIB_INSTALL_DIR}
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_LIB_INSTALL_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_DLL_INSTALL_DIR}
+ BUILD_RPATH "${QBS_LIB_RPATH}"
+ INSTALL_RPATH "${QBS_LIB_RPATH}"
+ MACOSX_RPATH ${QBS_MACOSX_RPATH}
+ SOVERSION ${_SOVERSION}
+ VERSION ${QBS_VERSION}
+ )
+ if (NOT _arg_STATIC)
+ install(TARGETS ${target_name}
+ LIBRARY DESTINATION ${QBS_LIB_INSTALL_DIR}
+ RUNTIME DESTINATION ${QBS_DLL_INSTALL_DIR}
+ )
+ endif()
+ if(MSVC)
+ target_compile_options(${target_name} PUBLIC /EHsc)
+ endif()
+endfunction()
+
+function(add_qbs_plugin target_name)
+ cmake_parse_arguments(_arg
+ ""
+ ""
+ "DEFINES;PUBLIC_DEFINES;DEPENDS;PUBLIC_DEPENDS;INCLUDES;PUBLIC_INCLUDES;SOURCES;"
+ ${ARGN}
+ )
+
+ if (${_arg_UNPARSED_ARGUMENTS})
+ message(FATAL_ERROR "add_qbs_plugin had unparsed arguments")
+ endif()
+
+ add_library(${target_name} SHARED ${_arg_SOURCES})
+ target_compile_definitions(
+ ${target_name} PRIVATE ${_arg_DEFINES} ${DEFAULT_DEFINES} PUBLIC ${_arg_PUBLIC_DEFINES})
+ target_include_directories(
+ ${target_name} PRIVATE ${_arg_INCLUDES} PUBLIC ${_arg_PUBLIC_INCLUDES})
+ target_link_libraries(${target_name} PRIVATE ${_arg_DEPENDS} PUBLIC ${_arg_PUBLIC_DEPENDS})
+
+ set_target_properties(${target_name} PROPERTIES
+ LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_PLUGINS_INSTALL_DIR}
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_PLUGINS_INSTALL_DIR}
+ BUILD_RPATH "${QBS_PLUGINS_RPATH}"
+ INSTALL_RPATH "${QBS_PLUGINS_RPATH}"
+ MACOSX_RPATH ${QBS_MACOSX_RPATH}
+ )
+ install(TARGETS ${target_name}
+ LIBRARY DESTINATION ${QBS_PLUGINS_INSTALL_DIR}
+ RUNTIME DESTINATION ${QBS_PLUGINS_INSTALL_DIR}
+ )
+endfunction()
+
+function(add_qbs_test test_name)
+ cmake_parse_arguments(_arg
+ ""
+ ""
+ "DEFINES;PUBLIC_DEFINES;DEPENDS;PUBLIC_DEPENDS;INCLUDES;PUBLIC_INCLUDES;SOURCES;"
+ ${ARGN}
+ )
+
+ if (${_arg_UNPARSED_ARGUMENTS})
+ message(FATAL_ERROR "add_qbs_test had unparsed arguments")
+ endif()
+
+ set(target_name "tst_${test_name}")
+
+ string(TOUPPER ${test_name} suite_name) # cmake is beatiful, here we have <input, output>
+ string(REPLACE - _ suite_name ${suite_name}) # and here we have <output, input>
+
+ add_executable(${target_name} ${_arg_SOURCES})
+ target_compile_definitions(${target_name} PRIVATE
+ "QBS_TEST_SUITE_NAME=\"${suite_name}\""
+ "SRCDIR=\"${CMAKE_CURRENT_SOURCE_DIR}\""
+ )
+ target_compile_definitions(
+ ${target_name} PRIVATE ${_arg_DEFINES} ${DEFAULT_DEFINES} PUBLIC ${_arg_PUBLIC_DEFINES})
+ target_include_directories(
+ ${target_name}
+ PRIVATE ${_arg_INCLUDES} "${CMAKE_CURRENT_SOURCE_DIR}/../../../src"
+ PUBLIC ${_arg_PUBLIC_INCLUDES}
+ )
+ target_link_libraries(
+ ${target_name}
+ PRIVATE ${_arg_DEPENDS} qbscore qbsconsolelogger Qt${QT_VERSION_MAJOR}::Test
+ PUBLIC ${_arg_PUBLIC_DEPENDS}
+ )
+
+ set_target_properties(${target_name} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_APP_INSTALL_DIR}
+ BUILD_RPATH "${QBS_APP_RPATH}"
+ INSTALL_RPATH "${QBS_APP_RPATH}"
+ MACOSX_RPATH ${QBS_MACOSX_RPATH}
+ )
+ install(TARGETS ${target_name} RUNTIME DESTINATION ${QBS_APP_INSTALL_DIR})
+ add_test(NAME ${target_name} COMMAND ${target_name})
+endfunction()
diff --git a/cmake/QbsDocumentation.cmake b/cmake/QbsDocumentation.cmake
new file mode 100644
index 000000000..bdcec664f
--- /dev/null
+++ b/cmake/QbsDocumentation.cmake
@@ -0,0 +1,301 @@
+# Options:
+option(QBS_INSTALL_HTML_DOCS "Whether to install Qbs HTML Documentation" OFF)
+option(QBS_INSTALL_QCH_DOCS "Whether to install Qbs QCH Documentation" OFF)
+option(QBS_INSTALL_MAN_PAGE "Whether to install Qbs man page" OFF)
+
+# Get information on directories from qmake
+# as this is not yet exported by cmake.
+# Used for QT_INSTALL_DOCS
+function(qt_query_qmake)
+ if (NOT TARGET Qt${QT_VERSION_MAJOR}::qmake)
+ message(FATAL_ERROR "Qmake was not found. Add find_package(Qt5 COMPONENTS Core) to CMake to enable.")
+ endif()
+ # dummy check for if we already queried qmake
+ if (QT_INSTALL_BINS)
+ return()
+ endif()
+
+ get_target_property(_qmake_binary Qt${QT_VERSION_MAJOR}::qmake IMPORTED_LOCATION)
+ execute_process(COMMAND "${_qmake_binary}" "-query"
+ TIMEOUT 10
+ RESULT_VARIABLE _qmake_result
+ OUTPUT_VARIABLE _qmake_stdout
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+ if (NOT "${_qmake_result}" STREQUAL "0")
+ message(FATAL_ERROR "Qmake did not execute successfully: ${_qmake_result}.")
+ endif()
+
+ # split into lines:
+ string(REPLACE "\n" ";" _lines "${_qmake_stdout}")
+
+ foreach(_line ${_lines})
+ # split line into key/value pairs
+ string(REPLACE ":" ";" _parts "${_line}")
+ list(GET _parts 0 _key)
+ list(REMOVE_AT _parts 0)
+ string(REPLACE ";" ":" _value "${_parts}")
+
+ set("${_key}" "${_value}" CACHE PATH "qmake import of ${_key}" FORCE)
+ endforeach()
+endfunction()
+
+function(_qbs_setup_doc_targets)
+ # Set up important targets:
+ if (NOT TARGET qbs_html_docs)
+ add_custom_target(qbs_html_docs COMMENT "Build HTML documentation")
+ endif()
+ if (NOT TARGET qbs_qch_docs)
+ add_custom_target(qbs_qch_docs COMMENT "Build QCH documentation")
+ endif()
+ if (NOT TARGET BuildQbsDocumentation)
+ add_custom_target(
+ BuildQbsDocumentation ALL COMMENT "Build Qbs documentation")
+ add_dependencies(BuildQbsDocumentation qbs_html_docs qbs_qch_docs)
+ endif()
+ if (NOT TARGET qbs_docs)
+ add_custom_target(
+ qbs_docs ALL COMMENT "Build Qbs documentation")
+ add_dependencies(qbs_docs BuildQbsDocumentation)
+ endif()
+endfunction()
+
+function(_find_python_module module)
+ string(TOUPPER ${module} module_upper)
+ if (NOT PY_${module_upper})
+ if (ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED")
+ set(${module}_FIND_REQUIRED TRUE)
+ endif()
+ # A module's location is usually a directory, but for binary modules
+ # it's a .so file.
+ execute_process(COMMAND "${Python3_EXECUTABLE}" "-c"
+ "import re, ${module}; print(re.compile('/__init__.py.*').sub('',${module}.__file__))"
+ RESULT_VARIABLE _${module}_status
+ OUTPUT_VARIABLE _${module}_location
+ ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if (NOT _${module}_status)
+ set(PY_${module_upper} ${_${module}_location} CACHE STRING
+ "Location of Python module ${module}")
+ endif()
+ endif()
+ find_package_handle_standard_args(PY_${module} DEFAULT_MSG PY_${module_upper})
+endfunction()
+
+function(_qbs_setup_qdoc_targets _qdocconf_file _retval)
+ cmake_parse_arguments(_arg "" "HTML_DIR;INSTALL_DIR;POSTFIX"
+ "INDEXES;INCLUDE_DIRECTORIES;FRAMEWORK_PATHS;ENVIRONMENT_EXPORTS;SOURCES" ${ARGN})
+
+ foreach(_index ${_arg_INDEXES})
+ list(APPEND _qdoc_index_args "-indexdir;${_index}")
+ endforeach()
+
+ set(_env "")
+ foreach(_export ${_arg_ENVIRONMENT_EXPORTS})
+ if (NOT DEFINED "${_export}")
+ message(FATAL_ERROR "${_export} is not known when trying to export it to qdoc.")
+ endif()
+ list(APPEND _env "${_export}=${${_export}}")
+ endforeach()
+
+ get_target_property(_qdoc Qt${QT_VERSION_MAJOR}::qdoc IMPORTED_LOCATION)
+
+ set(_full_qdoc_command "${_qdoc}")
+ if (_env)
+ set(_full_qdoc_command "${CMAKE_COMMAND}" "-E" "env" ${_env} "${_qdoc}")
+ endif()
+
+ if (_arg_HTML_DIR STREQUAL "")
+ set(_arg_HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/doc")
+ endif()
+
+ get_filename_component(_target "${_qdocconf_file}" NAME_WE)
+
+ set(_html_outputdir "${_arg_HTML_DIR}")
+ file(MAKE_DIRECTORY "${_html_outputdir}")
+
+ set(_qdoc_include_args "")
+ if (_arg_INCLUDE_DIRECTORIES OR _arg_FRAMEWORK_PATHS)
+ # pass include directories to qdoc via hidden @ option, since we need to generate a file
+ # to be able to resolve the generators inside the include paths
+ set(_qdoc_includes "${CMAKE_CURRENT_BINARY_DIR}/cmake/qdoc_${_target}.inc")
+ set(_qdoc_include_args "@${_qdoc_includes}")
+ set(_includes "")
+ if (_arg_INCLUDE_DIRECTORIES)
+ set(_includes "-I$<JOIN:${_arg_INCLUDE_DIRECTORIES},\n-I>\n")
+ endif()
+ set(_frameworks "")
+ if (_arg_FRAMEWORK_PATHS)
+ set(_frameworks "-F$<JOIN:${_arg_FRAMEWORK_PATHS},\n-F>\n")
+ endif()
+ file(GENERATE
+ OUTPUT "${_qdoc_includes}"
+ CONTENT "${_includes}${_frameworks}"
+ )
+ endif()
+
+ set(_html_artifact "${_html_outputdir}/index.html")
+ add_custom_command(
+ OUTPUT "${_html_artifact}"
+ COMMAND cmake -E remove_directory "${_html_outputdir}"
+ COMMAND ${_full_qdoc_command} -outputdir "${_html_outputdir}" "${_qdocconf_file}"
+ ${_qdoc_index_args} ${_qdoc_include_args}
+ DEPENDS "${_qdocconf_file}" ${_arg_SOURCES}
+ COMMENT "Build HTML documentation from ${_qdocconf_file}"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ VERBATIM
+ )
+
+ if (NOT Python3_Interpreter_FOUND)
+ message(WARNING "Cannot find python3 binary. Qbs documentation will not be built.")
+ return()
+ endif()
+ _find_python_module(lxml)
+ _find_python_module(bs4)
+ if (NOT PY_LXML OR NOT PY_BS4)
+ message(WARNING "Cannot import lxml and bs4 python modules. Qbs documentation will not be built.")
+ return()
+ endif()
+
+ set(_fixed_html_artifact "${CMAKE_CURRENT_BINARY_DIR}/qbsdoc.dummy")
+ set(_fix_qml_imports_script ${Qbs_SOURCE_DIR}/doc/fix-qmlimports.py)
+ add_custom_command(
+ OUTPUT "${_fixed_html_artifact}"
+ COMMAND ${Python3_EXECUTABLE} "${_fix_qml_imports_script}" ${_html_outputdir}
+ COMMAND cmake -E touch ${_fixed_html_artifact}
+ DEPENDS "${_html_artifact}" "${_fix_qml_imports_script}"
+ COMMENT "Fixing bogus QML import statements"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ VERBATIM
+ )
+
+ set(_html_target "qbs_html_docs_${_target}")
+ add_custom_target(${_html_target} DEPENDS "${_fixed_html_artifact}")
+ add_dependencies(qbs_html_docs "${_html_target}")
+
+ # artifacts might be required for QCH-only installation, so we build them
+ # always, and skip HTML docs installation here
+ if (QBS_INSTALL_HTML_DOCS)
+ install(DIRECTORY "${_html_outputdir}" DESTINATION "${_arg_INSTALL_DIR}"
+ COMPONENT qbs_docs)
+ endif()
+
+ set("${_retval}" "${_html_outputdir}" PARENT_SCOPE)
+endfunction()
+
+function(_qbs_setup_qhelpgenerator_targets _qdocconf_file _html_outputdir)
+ cmake_parse_arguments(_arg "" "QCH_DIR;INSTALL_DIR" "" ${ARGN})
+ if (_arg_UNPARSED_ARGUMENTS)
+ message(FATAL_ERROR "qdoc_build_qdocconf_file has unknown arguments: ${_arg_UNPARSED_ARGUMENTS}.")
+ endif()
+
+ if (NOT _arg_QCH_DIR)
+ set(_arg_QCH_DIR "${CMAKE_CURRENT_BINARY_DIR}/doc")
+ endif()
+
+ if (NOT TARGET Qt${QT_VERSION_MAJOR}::qhelpgenerator)
+ message(WARNING "qhelpgenerator missing: No QCH documentation targets were generated. Add find_package(Qt5 COMPONENTS Help) to CMake to enable.")
+ return()
+ endif()
+
+ get_filename_component(_target "${_qdocconf_file}" NAME_WE)
+
+ set(_html_target "qbs_html_docs_${_target}")
+ if (NOT TARGET ${_html_target})
+ return()
+ endif()
+
+ set(_qch_outputdir "${_arg_QCH_DIR}")
+ file(MAKE_DIRECTORY "${_qch_outputdir}")
+
+ set(_fixed_html_artifact "${CMAKE_CURRENT_BINARY_DIR}/qbsdoc.dummy")
+ set(_qhp_artifact "${_html_outputdir}/${_target}.qhp")
+ set(_qch_artifact "${_qch_outputdir}/${_target}.qch")
+ add_custom_command(
+ OUTPUT "${_qch_artifact}"
+ COMMAND Qt${QT_VERSION_MAJOR}::qhelpgenerator "${_qhp_artifact}" -o "${_qch_artifact}"
+ DEPENDS "${_fixed_html_artifact}"
+ COMMENT "Build QCH documentation from ${_qdocconf_file}"
+ WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ VERBATIM
+ )
+
+ set(_qch_target "qbs_qch_docs_${_target}")
+ add_custom_target("${_qch_target}" DEPENDS "${_qch_artifact}")
+ add_dependencies(qbs_qch_docs "${_qch_target}")
+
+ install(FILES "${_qch_outputdir}/${_target}.qch" DESTINATION "${_arg_INSTALL_DIR}"
+ COMPONENT qbs_docs)
+endfunction()
+
+# Helper functions:
+function(_qbs_qdoc_build_qdocconf_file _qdocconf_file)
+ _qbs_setup_doc_targets()
+
+ if (NOT TARGET Qt${QT_VERSION_MAJOR}::qdoc)
+ message(WARNING "No qdoc binary found: No documentation targets were generated")
+ return()
+ endif()
+
+ cmake_parse_arguments(_arg "QCH" "HTML_DIR;QCH_DIR;INSTALL_DIR;POSTFIX"
+ "INDEXES;INCLUDE_DIRECTORIES;FRAMEWORK_PATHS;ENVIRONMENT_EXPORTS;SOURCES" ${ARGN})
+ if (_arg_UNPARSED_ARGUMENTS)
+ message(FATAL_ERROR "qdoc_build_qdocconf_file has unknown arguments: ${_arg_UNPARSED_ARGUMENTS}.")
+ endif()
+
+ if (NOT _arg_INSTALL_DIR)
+ message(FATAL_ERROR "No INSTALL_DIR set when calling qdoc_build_qdocconf_file")
+ endif()
+
+ _qbs_setup_qdoc_targets("${_qdocconf_file}" _html_outputdir
+ HTML_DIR "${_arg_HTML_DIR}" INSTALL_DIR "${_arg_INSTALL_DIR}"
+ INDEXES ${_arg_INDEXES} ENVIRONMENT_EXPORTS ${_arg_ENVIRONMENT_EXPORTS}
+ POSTFIX "${_arg_POSTFIX}"
+ INCLUDE_DIRECTORIES ${_arg_INCLUDE_DIRECTORIES}
+ FRAMEWORK_PATHS ${_arg_FRAMEWORK_PATHS}
+ SOURCES ${_arg_SOURCES}
+ )
+
+ if (_arg_QCH)
+ _qbs_setup_qhelpgenerator_targets("${_qdocconf_file}" "${_html_outputdir}"
+ QCH_DIR "${_arg_QCH_DIR}" INSTALL_DIR "${_arg_INSTALL_DIR}")
+ endif()
+
+endfunction()
+
+function(add_qbs_documentation qdocconf_file)
+ cmake_parse_arguments(_arg "" ""
+ "INCLUDE_DIRECTORIES;FRAMEWORK_PATHS;SOURCES" ${ARGN})
+ if (_arg_UNPARSED_ARGUMENTS)
+ message(FATAL_ERROR "add_qbs_documentation has unknown arguments: ${_arg_UNPARSED_ARGUMENTS}.")
+ endif()
+
+ ### Skip docs setup if that is not needed!
+ if (NOT QBS_INSTALL_HTML_DOCS AND NOT QBS_INSTALL_QCH_DOCS)
+ return()
+ endif()
+
+ qt_query_qmake()
+ set(SRCDIR "${Qbs_SOURCE_DIR}/doc")
+
+ set(_qch_params)
+ # if QBS_INSTALL_QCH_DOCS is No, qch generation will be skipped entirely
+ if (QBS_INSTALL_QCH_DOCS)
+ set(_qch_params QCH QCH_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+ endif()
+ set(_qdoc_params HTML_DIR "${CMAKE_CURRENT_BINARY_DIR}/${QBS_DOC_HTML_DIR_NAME}")
+ list(APPEND _qdoc_params INSTALL_DIR "${QBS_DOC_INSTALL_DIR}")
+
+ # Set up environment for qdoc:
+ string(REPLACE "." "" QBS_VERSION_TAG "${QBS_VERSION}")
+
+ list(APPEND _qdoc_params ENVIRONMENT_EXPORTS
+ SRCDIR QBS_VERSION QBS_VERSION_TAG
+ QT_INSTALL_DOCS
+ )
+
+ _qbs_qdoc_build_qdocconf_file(${qdocconf_file} ${_qch_params} ${_qdoc_params}
+ INCLUDE_DIRECTORIES ${_arg_INCLUDE_DIRECTORIES}
+ FRAMEWORK_PATHS ${_arg_FRAMEWORK_PATHS}
+ SOURCES ${_arg_SOURCES}
+ )
+endfunction()
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
new file mode 100644
index 000000000..04ff12470
--- /dev/null
+++ b/doc/CMakeLists.txt
@@ -0,0 +1,30 @@
+set(_DOC_SOURCES
+ ../README.md
+ ../CONTRIBUTING.md
+ classic.css
+ external-resources.qdoc
+ fixnavi.pl
+ howtos.qdoc
+ qbs.qdoc
+ qbs-online.qdocconf
+ config/style/qt5-sidebar.html
+ )
+
+file(GLOB_RECURSE _DOC_APPENDIX_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/appendix/*")
+file(GLOB_RECURSE _DOC_REFERENCE_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/reference/*")
+file(GLOB_RECURSE _DOC_TEMPLATES_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/templates/*")
+file(GLOB_RECURSE _DOC_IMAGES_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/images/*")
+file(GLOB_RECURSE _DOC_TARGETS_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/targets/*")
+
+add_qbs_documentation(
+ "qbs.qdocconf"
+ SOURCES
+ ${_DOC_SOURCES}
+ ${_DOC_APPENDIX_SOURCES}
+ ${_DOC_REFERENCE_SOURCES}
+ ${_DOC_TEMPLATES_SOURCES}
+ ${_DOC_IMAGES_SOURCES}
+ ${_DOC_TARGETS_SOURCES}
+ )
+
+add_subdirectory(man)
diff --git a/doc/appendix/json-api.qdoc b/doc/appendix/json-api.qdoc
index f8840de37..7b093bc4e 100644
--- a/doc/appendix/json-api.qdoc
+++ b/doc/appendix/json-api.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\previouspage porting-to-qbs.html
\nextpage attributions.html
\page json-api.html
@@ -95,6 +94,7 @@
\header \li Property \li Type
\row \li api-level \li int
\row \li api-compat-level \li int
+ \row \li lsp-socket \li string
\endtable
The value of \c api-level is increased whenever the API is extended, for instance
@@ -109,6 +109,10 @@
The value of \c api-compat-level is always less than or equal to the
value of \c api-level.
+ The value of \c lsp-socket is a path to a local domain socket (on Unix) or
+ a named pipe (on Windows). It provides a server implementing the
+ \l{https://microsoft.github.io/language-server-protocol}{Language Server Protocol}.
+
\section1 Resolving a Project
To instruct \QBS to load a project from disk, a request of type
@@ -118,13 +122,14 @@
\row \li build-root \li \l FilePath \li yes
\row \li configuration-name \li string \li no
\row \li data-mode \li \l DataMode \li no
+ \row \li deprecation-warning-mode \li string \li no
\row \li dry-run \li bool \li no
\row \li environment \li \l Environment \li no
\row \li error-handling-mode \li string \li no
- \row \li fallback-provider-enabled \li bool \li no
\row \li force-probe-execution \li bool \li no
\row \li log-time \li bool \li no
\row \li log-level \li \l LogLevel \li no
+ \row \li max-job-count \li int \li no
\row \li module-properties \li list of strings \li no
\row \li overridden-properties \li object \li no
\row \li project-file-path \li FilePath \li if resolving from scratch
@@ -402,17 +407,12 @@
\header \li Property \li Type \li Mandatory
\row \li error \li \l ErrorInfo \li no
\row \li failed-files \li \l FilePath list \li no
- \row \li project-data \li \l TopLevelProjectData \li no
\endtable
If the \c error property is present, the operation has at least
partially failed and \c failed-files will list the files
that could not be added or removed.
- If the project data has changed as a result of the operation
- (which it should unless the operation failed completely), then
- the \c project-data property will contain the updated project data.
-
\section1 The \c get-run-environment Message
This request retrieves the full run environment for a specific
@@ -634,8 +634,8 @@
\row \li version \li string
\endtable
- The \c dependencies are the names of products that occur in the (enabled)
- \l Depends items of this product.
+ The elements of the \c dependencies array correspond to the full-display-name
+ properties of the products that this product has pulled in via \l Depends items.
The \c generated-artifacts are files that are created by the \l{Rule}{rules}
in this product.
@@ -771,7 +771,7 @@
\c items is an array of objects with the following structure:
\table
\header \li Property \li Type \li Mandatory
- \row \li \c message \li string \li yes
+ \row \li description \li string \li yes
\row \li location \li \l Location \li no
\endtable
diff --git a/doc/appendix/qbs-porting.qdoc b/doc/appendix/qbs-porting.qdoc
index ba697d7be..47e5f7678 100644
--- a/doc/appendix/qbs-porting.qdoc
+++ b/doc/appendix/qbs-porting.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\previouspage building-qbs.html
\page porting-to-qbs.html
\nextpage json-api.html
diff --git a/doc/codeattributions.qdoc b/doc/codeattributions.qdoc
index ee4e4b901..092f7794f 100644
--- a/doc/codeattributions.qdoc
+++ b/doc/codeattributions.qdoc
@@ -1,6 +1,5 @@
/*!
-\contentspage attributions.html
\ingroup attributions-libs
\ingroup attributions-qbs
\page qbs-attribution-ds_store.html attribution
@@ -49,7 +48,6 @@ THE SOFTWARE.
/*!
-\contentspage attributions.html
\ingroup attributions-libs
\ingroup attributions-qbs
\page qbs-attribution-dmgbuild.html attribution
@@ -98,7 +96,6 @@ THE SOFTWARE.
/*!
-\contentspage attributions.html
\ingroup attributions-libs
\ingroup attributions-qbs
\page qbs-attribution-mac_alias.html attribution
@@ -147,7 +144,6 @@ THE SOFTWARE.
/*!
-\contentspage attributions.html
\ingroup attributions-libs
\ingroup attributions-qbs
\page qbs-attribution-biplist.html attribution
@@ -184,7 +180,7 @@ modification, are permitted provided that the following conditions are met:
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of biplist nor the names of its contributors may be
- used to endorse or promote products derived from this software without
+ 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"
@@ -199,3 +195,94 @@ 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.
\endcode
*/
+
+/*!
+
+\ingroup attributions-libs
+\ingroup attributions-qbs
+\page qbs-attribution-cppscanner.html attribution
+\target cppscanner
+
+\title cppscanner
+\brief MIT License
+
+The cpp scanner module is used to parse C/C++ files and to find includes
+in them.
+
+The sources can be found in src/plugins/scanner/cpp.
+
+\badcode
+Copyright (c) 2008 Roberto Raggi
+\endcode
+
+\l{https://spdx.org/licenses/MIT.html}{MIT License}.
+
+\badcode
+// Copyright (c) 2008 Roberto Raggi <roberto.raggi@gmail.com>
+//
+// 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.
+\endcode
+*/
+
+/*!
+
+\ingroup attributions-libs
+\ingroup attributions-qbs
+\page qbs-attribution-quickjs.html attribution
+\target quickjs
+
+\title QuickJS
+\brief MIT License
+
+The JavaScript engine used to evaluate all JavaScript code in \QBS project files.
+
+The sources can be found in src/shared/quickjs.
+
+\badcode
+Copyright (c) 2017-2021 Fabrice Bellard
+Copyright (c) 2017-2021 Charlie Gordon
+\endcode
+
+\l{https://spdx.org/licenses/MIT.html}{MIT License}.
+
+\badcode
+QuickJS Javascript Engine
+
+Copyright (c) 2017-2021 Fabrice Bellard
+Copyright (c) 2017-2021 Charlie Gordon
+
+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.
+\endcode
+*/
diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf
index a8abe25e5..31b9b9a8d 100644
--- a/doc/config/macros.qdocconf
+++ b/doc/config/macros.qdocconf
@@ -4,6 +4,7 @@ macro.qbsversion = $QBS_VERSION
macro.defaultvalue = "Default:"
macro.nodefaultvalue = "Default: Undefined"
macro.appleproperty = "This property is specific to Apple platforms."
+macro.androidproperty = "This property is specific to Android platforms."
macro.unixproperty = "This property is specific to Unix platforms."
macro.windowsproperty = "This property is specific to Windows."
macro.baremetalproperty = "This property is specific to bare-metal platforms."
@@ -50,6 +51,8 @@ macro.endfloat.HTML = "</div>"
macro.clearfloat.HTML = "<br style=\"clear: both\" />"
macro.emptyspan.HTML = "<span></span>"
+macro.CMAKE = "CMake"
+
# Embed YouTube content by video ID - Example: \youtube dQw4w9WgXcQ
# Also requires a <ID>.jpg thumbnail for offline docs. In .qdocconf, add:
#
diff --git a/doc/config/qbs-project.qdocconf b/doc/config/qbs-project.qdocconf
index bab672e11..bf5051c77 100644
--- a/doc/config/qbs-project.qdocconf
+++ b/doc/config/qbs-project.qdocconf
@@ -5,6 +5,9 @@ include($QT_INSTALL_DOCS/global/fileextensions.qdocconf)
project = "Qbs"
description = "Qbs Manual"
+#Words to ignore for auto-linking
+ignorewords += macOS
+
headerdirs =
sourcedirs = ..
imagedirs = ../images ../templates/images
diff --git a/doc/doc.pri b/doc/doc.pri
deleted file mode 100644
index cd05a9450..000000000
--- a/doc/doc.pri
+++ /dev/null
@@ -1,18 +0,0 @@
-include(../src/install_prefix.pri)
-
-include(doc_shared.pri)
-
-DOC_OUTDIR_POSTFIX = /html
-DOC_HTML_INSTALLDIR = $$QBS_INSTALL_PREFIX/share/doc/qbs
-DOC_QCH_OUTDIR = $$OUT_PWD/doc
-DOC_QCH_INSTALLDIR = $$QBS_INSTALL_PREFIX/share/doc/qbs
-
-include(doc_targets.pri)
-
-fixnavi.commands = \
- cd $$shell_path($$PWD) && \
- perl fixnavi.pl -Dqcmanual -Dqtquick \
- qbs.qdoc
-QMAKE_EXTRA_TARGETS += fixnavi
-
-include(man/man.pri)
diff --git a/doc/doc.qbs b/doc/doc.qbs
index dd8377c6c..0dfc5f474 100644
--- a/doc/doc.qbs
+++ b/doc/doc.qbs
@@ -1,6 +1,6 @@
-import qbs 1.0
import qbs.File
import qbs.FileInfo
+import qbs.Probes
Project {
references: ["man/man.qbs"]
@@ -16,13 +16,21 @@ Project {
Depends { name: "qbsbuildconfig" }
Depends { name: "qbsversion" }
+ Probes.BinaryProbe {
+ id: pythonProbe
+ names: ["python3", "python"] // on Windows, there's no python3
+ }
+ property string pythonPath: pythonProbe.found ? pythonProbe.filePath : undefined
+
files: [
- "../README",
+ "../README.md",
+ "../CONTRIBUTING.md",
"classic.css",
"external-resources.qdoc",
"fixnavi.pl",
"howtos.qdoc",
"qbs.qdoc",
+ "tutorial.qdoc",
"qbs-online.qdocconf",
"config/*.qdocconf",
"config/style/qt5-sidebar.html",
@@ -58,9 +66,11 @@ Project {
outputFileTags: ["qdoc-html", "qbsdoc.dummy"] // TODO: Hack. Rule injection to the rescue?
outputArtifacts: [{filePath: "dummy", fileTags: ["qbsdoc.dummy"]}]
prepare: {
+ if (!product.pythonPath)
+ throw "Python executable was not found";
var scriptPath = explicitlyDependsOn["qbsdoc.fiximports"][0].filePath;
var htmlDir = FileInfo.path(FileInfo.path(inputs["qdoc-png"][0].filePath));
- var fixCmd = new Command("python", [scriptPath, htmlDir]);
+ var fixCmd = new Command(product.pythonPath, [scriptPath, htmlDir]);
fixCmd.description = "fixing bogus QML import statements";
return [fixCmd];
}
diff --git a/doc/doc_shared.pri b/doc/doc_shared.pri
deleted file mode 100644
index 3e4eccf48..000000000
--- a/doc/doc_shared.pri
+++ /dev/null
@@ -1,14 +0,0 @@
-include(../qbs_version.pri)
-
-qbsdoc_version.name = QBS_VERSION
-qbsdoc_version.value = $$QBS_VERSION
-qbsdoc_versiontag.name = QBS_VERSION_TAG
-qbsdoc_versiontag.value = $$replace(QBS_VERSION, "[-.]", )
-qbsdoc_qtdocs.name = QT_INSTALL_DOCS
-qbsdoc_qtdocs.value = $$[QT_INSTALL_DOCS/src]
-QDOC_ENV += qbsdoc_version qbsdoc_versiontag qbsdoc_qtdocs
-
-build_online_docs: \
- DOC_FILES += $$PWD/qbs-online.qdocconf
-else: \
- DOC_FILES += $$PWD/qbs.qdocconf
diff --git a/doc/doc_targets.pri b/doc/doc_targets.pri
deleted file mode 100644
index 0636be2b5..000000000
--- a/doc/doc_targets.pri
+++ /dev/null
@@ -1,86 +0,0 @@
-# Creates targets for building documentation
-# (adapted from qt_docs.prf)
-#
-# Usage: Define variables (details below) and include this pri file afterwards.
-#
-# QDOC_ENV - environment variables to set for the qdoc call (see example below)
-# DOC_INDEX_PATHS - list of paths where qdoc should search for index files of dependent
-# modules (Qt index path is included by default)
-# DOC_FILES - list of qdocconf files
-# DOC_OUTDIR_POSTFIX - html is generated in $$OUT_PWD/<qdocconf_name>$$DOC_OUTDIR_POSTFIX
-# DOC_HTML_INSTALLDIR - path were to install the directory of html files
-# DOC_QCH_OUTDIR - path where to generated the qch files
-# DOC_QCH_INSTALLDIR - path where to install the qch files
-# DOC_TARGET_PREFIX - prefix for generated target names
-#
-# Example for QDOC_ENV:
-# ver.name = VERSION
-# ver.value = 1.0.2
-# foo.name = FOO
-# foo.value = foo
-# QDOC_ENV = ver foo
-
-isEmpty(DOC_FILES): error("Set DOC_FILES before including doc_targets.pri")
-isEmpty(DOC_HTML_INSTALLDIR): error("Set DOC_HTML_INSTALLDIR before including doc_targets.pri")
-isEmpty(DOC_QCH_OUTDIR): error("Set DOC_QCH_OUTDIR before including doc_targets.pri")
-isEmpty(DOC_QCH_INSTALLDIR): error("Set DOC_QCH_INSTALLDIR before including doc_targets.pri")
-
-QT_TOOL_ENV = $$QDOC_ENV
-qtPrepareTool(QDOC, qdoc)
-QT_TOOL_ENV =
-
-!build_online_docs: qtPrepareTool(QHELPGENERATOR, qhelpgenerator)
-
-DOCS_BASE_OUTDIR = $$OUT_PWD/doc
-DOC_INDEXES += -indexdir $$shell_quote($$[QT_INSTALL_DOCS])
-for (index_path, DOC_INDEX_PATHS): \
- DOC_INDEXES += -indexdir $$shell_quote($$index_path)
-
-DTP = $$DOC_TARGET_PREFIX
-for (doc_file, DOC_FILES) {
- !exists($$doc_file): error("Cannot find documentation specification file $$doc_file")
- DOC_TARGET = $$replace(doc_file, ^(.*/)?(.*)\\.qdocconf$, \\2)
- DOC_TARGETDIR = $$DOC_TARGET
- DOC_OUTPUTDIR = $${DOCS_BASE_OUTDIR}/$${DOC_TARGETDIR}$${DOC_OUTDIR_POSTFIX}
-
- $${DTP}html_docs_$${DOC_TARGET}.commands = $$QDOC -outputdir $$shell_quote($$DOC_OUTPUTDIR) $$doc_file $$DOC_INDEXES
- QMAKE_EXTRA_TARGETS += $${DTP}html_docs_$${DOC_TARGET}
-
- !isEmpty($${DTP}html_docs.commands): $${DTP}html_docs.commands += &&
- $${DTP}html_docs.commands += $$eval($${DTP}html_docs_$${DOC_TARGET}.commands)
-
- $${DTP}inst_html_docs.files += $$DOC_OUTPUTDIR
-
- !build_online_docs {
- $${DTP}qch_docs_$${DOC_TARGET}.commands = $$QHELPGENERATOR $$shell_quote($$DOC_OUTPUTDIR/$${DOC_TARGET}.qhp) -o $$shell_quote($$DOC_QCH_OUTDIR/$${DOC_TARGET}.qch)
- $${DTP}qch_docs_$${DOC_TARGET}.depends = $${DTP}html_docs_$${DOC_TARGET}
- QMAKE_EXTRA_TARGETS += $${DTP}qch_docs_$${DOC_TARGET}
-
- !isEmpty($${DTP}qch_docs.commands): $${DTP}qch_docs.commands += &&
- $${DTP}qch_docs.commands += $$eval($${DTP}qch_docs_$${DOC_TARGET}.commands)
-
- $${DTP}inst_qch_docs.files += $$DOC_QCH_OUTDIR/$${DOC_TARGET}.qch
- }
-}
-
-!build_online_docs {
- $${DTP}qch_docs.depends = $${DTP}html_docs
- $${DTP}inst_qch_docs.path = $$DOC_QCH_INSTALLDIR
- $${DTP}inst_qch_docs.CONFIG += no_check_exist no_default_install no_build
- install_$${DTP}docs.depends = install_$${DTP}inst_qch_docs
- $${DTP}docs.depends = $${DTP}qch_docs
- INSTALLS += $${DTP}inst_qch_docs
- QMAKE_EXTRA_TARGETS += $${DTP}qch_docs install_$${DTP}docs
-} else {
- $${DTP}docs.depends = $${DTP}html_docs
-}
-
-$${DTP}inst_html_docs.path = $$DOC_HTML_INSTALLDIR
-$${DTP}inst_html_docs.CONFIG += no_check_exist no_default_install directory
-INSTALLS += $${DTP}inst_html_docs
-install_$${DTP}docs.depends += install_$${DTP}inst_html_docs
-
-QMAKE_EXTRA_TARGETS += $${DTP}html_docs $${DTP}docs
-
-unset(DTP)
-
diff --git a/doc/external-resources.qdoc b/doc/external-resources.qdoc
index 00b7bf975..9d7aa3aeb 100644
--- a/doc/external-resources.qdoc
+++ b/doc/external-resources.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
@@ -121,9 +121,8 @@
*/
/*!
- \externalpage nolink
- \title macOS
- \internal
+ \externalpage https://github.com/nanopb/nanopb
+ \title nanopb
*/
/*!
diff --git a/doc/fix-qmlimports.py b/doc/fix-qmlimports.py
index 93fdb437a..bb022d25b 100755
--- a/doc/fix-qmlimports.py
+++ b/doc/fix-qmlimports.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
#############################################################################
##
## Copyright (C) 2017 The Qt Company Ltd.
@@ -121,7 +121,7 @@ if __name__ == '__main__':
if not (fileCount % progStep):
print('.', end='')
sys.stdout.flush()
- with open(html, 'r+') as file_:
+ with open(html, 'r+', encoding='utf8') as file_:
try:
soup = BeautifulSoup(file_, 'lxml')
actions = []
@@ -145,7 +145,7 @@ if __name__ == '__main__':
if 'lxml' in str(e):
print('(If using pip, try \"pip install lxml\")', file=sys.stderr)
quit(1)
- for k, v in modified.iteritems():
+ for k, v in modified.items():
print ('\n\t', k, 'in %d files' % v, end='')
pb = pre_blocks.get(k, 0)
if pb:
diff --git a/doc/howtos.qdoc b/doc/howtos.qdoc
index b63e2ca96..9a81318ee 100644
--- a/doc/howtos.qdoc
+++ b/doc/howtos.qdoc
@@ -25,8 +25,7 @@
**
****************************************************************************/
/*!
- \contentspage index.html
- \previouspage custom-modules.html
+ \previouspage tutorial-8.html
\nextpage reference.html
\page howtos.html
@@ -37,7 +36,10 @@
\list
\li \l{How do I build a Qt-based project?}
\li \l{How do I make my app build against my library?}
+ \li \l{How do I build release with debug information?}
+ \li \l{How do I separate and install debugging symbols?}
\li \l{How do I use precompiled headers?}
+ \li \l{How do I make use of rpaths?}
\li \l{How do I make sure my generated sources are getting compiled?}
\li \l{How do I run my autotests?}
\li \l{How do I use ccache?}
@@ -45,11 +47,15 @@
\li \l{How do I build against libraries that provide pkg-config files?}
\li \l{How do I create application bundles and frameworks on iOS, macOS, tvOS, and watchOS?}
\li \l{How do I apply C/C++ preprocessor macros to only a subset of the files in my product?}
+ \li \l{How do I disable a compiler warning?}
\li \l{How do I make the state of my Git repository available to my source files?}
\li \l{How do I limit the number of concurrent jobs for the linker only?}
\li \l{How do I add QML files to a project?}
\li \l{How do I define a reusable Group of files that can be included in other \QBS files?}
+ \li \l{How do I access properties of a base type?}
\li \l{How do I print the value of a property?}
+ \li \l{How do I debug \QBS scripts?}
+ \li \l{How do I sign an application for an Apple platform?}
\endlist
\section1 How do I build a Qt-based project?
@@ -94,7 +100,7 @@
]
Export {
Depends { name: "cpp" }
- cpp.includePaths: [product.sourceDirectory]
+ cpp.includePaths: [exportingProduct.sourceDirectory]
}
}
}
@@ -129,6 +135,89 @@
}
\endcode
+ \section1 How do I build release with debug information?
+
+ You can simply use the \c{"profiling"} \l{qbs::buildVariant}{qbs.buildVariant}:
+ \code
+ qbs build qbs.buildVariant:profiling
+ \endcode
+
+ \section1 How do I separate and install debugging symbols?
+
+ First, you need to set the \l{cpp::debugInformation}{cpp.debugInformation} and
+ \l{cpp::separateDebugInformation}{cpp.separateDebugInformation}
+ properties to \c true or use some conditional expression in your product:
+ \code
+ CppApplication {
+ // ...
+ cpp.debugInformation: qbs.buildVariant !== "release"
+ cpp.separateDebugInformation: true
+ }
+ \endcode
+
+ Now, you can install your \l{Application}{application}, \l{DynamicLibrary}{dynamic library}
+ or \l{LoadableModule}{loadable module} among with its debugging symbols as follows:
+ \code
+ CppApplication {
+ // ...
+ install: true
+ installDir: "bin"
+ installDebugInformation: true
+ debugInformationInstallDir: "bin"
+ }
+ \endcode
+
+ If you are not using \l{List of Convenience Items}{convenience items},
+ you can install debug symbols manually using the \l{Group} item. If the
+ \l{cpp::separateDebugInformation}{cpp.separateDebugInformation} property is set to \c true,
+ \QBS will create debugging symbols with the corresponding file tags
+ \c "debuginfo_app" (for an application), \c "debuginfo_dll" (for a dynamic library),
+ or \c "debuginfo_loadablemodule" (for a macOS plugin).
+
+ \code
+ Product {
+ type: "application"
+ Depends { name: "cpp" }
+ cpp.debugInformation: qbs.buildVariant !== "release"
+ cpp.separateDebugInformation: true
+ Group {
+ fileTagsFilter: cpp.separateDebugInformation ? ["debuginfo_app"] : []
+ qbs.install: true
+ qbs.installDir: "bin"
+ qbs.installSourceBase: buildDirectory
+ }
+ }
+ \endcode
+
+ If you're building a shared library, you need to use the \c "debuginfo_dll" tag instead:
+ \code
+ Product {
+ type: "dynamic_library"
+ // ...
+ Group {
+ fileTagsFilter: cpp.separateDebugInformation ? ["debuginfo_dll"] : []
+ qbs.install: true
+ qbs.installDir: "lib"
+ qbs.installSourceBase: buildDirectory
+ }
+ }
+ \endcode
+
+ If you're building a macOS plugin, you need to use the \c "debuginfo_loadablemodule"
+ tag instead:
+ \code
+ Product {
+ type: "loadablemodule"
+ // ...
+ Group {
+ fileTagsFilter: cpp.separateDebugInformation ? ["debuginfo_loadablemodule"] : []
+ qbs.install: true
+ qbs.installDir: "PlugIns"
+ qbs.installSourceBase: buildDirectory
+ }
+ }
+ \endcode
+
\section1 How do I use precompiled headers?
If you use a \l Group item to add a precompiled header file to a product
@@ -152,6 +241,42 @@
}
\endcode
+ \section1 How do I make use of rpaths?
+
+ rpath designates the run-time search path used by the dynamic linker when loading
+ libraries on UNIX platforms. This concept does not apply to Windows.
+
+ Suppose you have a project with two dynamic library products \c LibraryA and \c LibraryB
+ and one dependent application product. Also, \c LibraryB depends on \c LibraryA. The
+ application is installed to the \c bin folder and the libraries are installed to the
+ \c lib folder next to the \c bin folder. You want the application to be able to find the
+ dependent libraries relative to its own location. This can be achieved by usage of the
+ \l{cpp::rpaths}{cpp.rpaths} property.
+
+ First, you need to set \l{cpp::rpaths}{cpp.rpaths} in your libraries so they can
+ find dependent libraries in the same folder where they are located. This can be
+ done as follows:
+
+ \snippet ../examples/rpaths/rpaths.qbs 0
+
+ We are setting \l{cpp::rpaths}{cpp.rpaths} to \l{cpp::rpathOrigin}{cpp.rpathOrigin} which
+ expands to \c "$ORIGIN" on Linux and to \c "@loader_path" on macOS.
+
+ On macOS you also need to set \l{cpp::sonamePrefix}{cpp.sonamePrefix} to \c "@rpath" to
+ tell the dynamic linker to use RPATHs when loading this library.
+
+ \c LibraryB looks exactly the same:
+
+ \snippet ../examples/rpaths/rpaths.qbs 1
+
+ In a real project, it might be a good idea to move common properties to some base item
+ and inherit it in library items.
+
+ The application item is a bit different. It sets \l{cpp::rpaths}{cpp.rpaths} to the
+ \c "lib" folder which is located one level up from the \c bin folder:
+
+ \snippet ../examples/rpaths/rpaths.qbs 2
+
\section1 How do I make sure my generated sources are getting compiled?
The rules in a \QBS project do not care whether its inputs are actual source files
@@ -265,19 +390,19 @@
Depends { name: "cpp" }
cpp.includePaths: ["/somewhere/include"]
Properties {
- condition: qbs.targetOS.contains("android")
+ condition: qbs.targetOS.includes("android")
cpp.dynamicLibraries: ["/somewhere/android/" + Android.ndk.abi + "/lib1.so"]
}
Properties {
- condition: qbs.targetOS.contains("macos")
+ condition: qbs.targetOS.includes("macos")
cpp.dynamicLibraries: ["/somewhere/macos/lib1.dylib"]
}
Properties {
- condition: qbs.targetOS.contains("windows") && qbs.architecture === "x86"
+ condition: qbs.targetOS.includes("windows") && qbs.architecture === "x86"
cpp.dynamicLibraries: ["/somewhere/windows_x86/lib1.lib"]
}
Properties {
- condition: qbs.targetOS.contains("windows") && qbs.architecture === "x86_64"
+ condition: qbs.targetOS.includes("windows") && qbs.architecture === "x86_64"
cpp.dynamicLibraries: ["/somewhere/windows_x86_64/lib1.lib"]
}
}
@@ -342,15 +467,17 @@
\section1 How do I build against libraries that provide pkg-config files?
- Just add a \l Depends item that matches the name of the pkg-config module, and \QBS
- will automatically employ \l{https://www.freedesktop.org/wiki/Software/pkg-config}{pkg-config}
+ Just add a \l Depends item that matches the name of the pkg-config module,
+ set the \l Product::qbsModuleProviders property to \c "qbspkgconfig",
+ and \QBS will employ
+ \l{https://www.freedesktop.org/wiki/Software/pkg-config}{pkg-config}
to find the headers and libraries if no matching \QBS module can be found. For instance,
to build against the OpenSSL library, you would write this:
\code
+ qbsModuleProviders: "qbspkgconfig"
Depends { name: "openssl" }
\endcode
- That's it. The pkg-config behavior can be fine-tuned via the \l pkgconfig module,
- but normally you will not need to pull it in explicitly.
+ That's it. The pkg-config behavior can be fine-tuned via the \l qbspkgconfig provider.
Internally, this functionality is implemented via \l {Module Providers}
@@ -398,6 +525,37 @@
}
\endcode
+ \section1 How do I disable a compiler warning?
+
+ You can use the \l {cpp::commonCompilerFlags}{cpp.commonCompilerFlags} property
+ to pass flags to the compiler. For example, to disable deprecation warnings:
+
+ \code
+ CppApplication {
+ // ...
+
+ readonly property bool isMsvc: qbs.toolchain.includes("msvc")
+
+ cpp.commonCompilerFlags: isMsvc ? "/wd4996" : "-Wno-deprecated-declarations"
+ }
+ \endcode
+
+ It is also possible to disable all warnings at once by setting the
+ \l {cpp::commonCompilerFlags}{cpp.warningLevel} property to \c "none".
+ Usually this approach is discouraged, but it can be useful in some cases,
+ such as when compiling third party code:
+
+ \code
+ Group {
+ cpp.warningLevel: "none"
+
+ files: [
+ "3rdparty.h",
+ "3rdparty.cpp"
+ ]
+ }
+ \endcode
+
\section1 How do I make the state of my Git repository available to my source files?
Add a dependency to the \l{vcs} module to your product:
@@ -474,7 +632,7 @@
\section1 How do I add QML files to a project?
The simplest way to add QML files to a project is to add them to a
- \l {The Qt Resource System}{resource file}:
+ \l {https://doc.qt.io/qt/resources.html}{Qt resource file}:
\code
QtGuiApplication {
@@ -567,6 +725,31 @@
}
\endcode
+ \section1 How do I access properties of a base type?
+
+ You can use the \l base property. For example, to append to a list of files
+ that come from the base type, you can use \c {base.concat()}:
+
+ \code
+ // TestBase.qbs
+
+ QtGuiApplication {
+ files: [
+ "TestCaseBase.h",
+ "TestCaseBase.cpp"
+ ]
+ }
+ \endcode
+
+ \code
+ // tst_stuff.qbs
+ TestBase {
+ files: base.concat(["tst_stuff.cpp"])
+ }
+ \endcode
+
+ See \l {Special Property Values} for more details.
+
\section1 How do I print the value of a property?
Use the \l {Console API}{console API}. For example, suppose your project
@@ -574,7 +757,7 @@
\c qbs.targetOS has the wrong value:
\code
- readonly property bool unix: qbs.targetOS.contains("unix")
+ readonly property bool unix: qbs.targetOS.includes("unix")
\endcode
To find out the value of \c qbs.targetOS, use \c {console.info()}:
@@ -582,7 +765,86 @@
\code
readonly property bool unix: {
console.info("qbs.targetOS: " + qbs.targetOS)
- return qbs.targetOS.contains("unix")
+ return qbs.targetOS.includes("unix")
+ }
+ \endcode
+
+ It is also possible to throw an exception with the text saying what is wrong - this might
+ be useful if the property contains invalid or unsupported value:
+ \code
+ readonly property bool unix: {
+ if (qbs.targetOS.includes("darwin"))
+ throw "Apple platforms are not supported";
+ return qbs.targetOS.includes("unix")
}
\endcode
+
+ \section1 How do I debug \QBS scripts?
+
+ To debug the value of a specific property, see the \l{How do I print the value of a property}
+ section.
+
+ Similar debugging techniques could be used within \l{Rule}{Rules} or \c .js files.
+
+ It is also possible to increase \QBS' logging level using the \c --more-verbose (\c -v) option
+ of the \c{qbs build} command:
+
+ \code
+ qbs build -v config:release
+ \endcode
+
+ \QBS uses the Qt Categorized Logging system which allows to configure logging categories
+ in \l{https://doc.qt.io/qt-5/qloggingcategory.html#configuring-categories}{multiple ways}. For
+ example, to enable debug logging for the \c moduleloader category, use the following command:
+ \code
+ QT_LOGGING_RULES="qbs.moduleloader.debug=true" qbs resolve
+ \endcode
+
+ To list all the files in the project directory and show whether they are known to qbs in the
+ respective configuration, use the \c{qbs status} command:
+ \code
+ qbs status config:release
+ \endcode
+
+ \section1 How do I sign an application for an Apple platform?
+
+ To sign an application for an Apple platform, you need to use the \l{codesign} module.
+
+ \code
+ Depends { name: "codesign" }
+ \endcode
+
+ Several properties should be set to do signing as shown below.
+
+ Make sure that bundle and team indentifiers match the one used for signing:
+
+ \code
+ bundle.identifierPrefix: "com.johndoe"
+ codesign.teamIdentifier: "John Doe"
+ \endcode
+
+ It is also possible to use an ID of the team identifier instead of a name:
+ \code
+ codesign.teamIdentifier: "1234ABCDEF"
+ \endcode
+
+ \QBS will then try to find the matching signing identity and provisioning profile based on
+ \l{codesign::signingType}{codesign.signingType}.
+
+ It is also possible to specify \l{codesign::signingIdentity}{codesign.signingIdentity}
+ manually:
+
+ \code
+ codesign.signingIdentity: "Apple Development: johndoe@apple.com (ABCDEF1234)"
+ \endcode
+
+ It is also possible to use an ID of the signing identity instead of a name:
+ \code
+ codesign.signingIdentity: "ABCDEF1234567890ABCDEF1234567890ABCDEF12"
+ \endcode
+
+ If \QBS cannot find the suitable provisioning profile, you can specify it manually as well:
+ \code
+ codesign.provisioningProfile: "abcdef12-1234-5678-1111-abcdef123456"
+ \endcode
*/
diff --git a/doc/man/CMakeLists.txt b/doc/man/CMakeLists.txt
new file mode 100644
index 000000000..00d003d7f
--- /dev/null
+++ b/doc/man/CMakeLists.txt
@@ -0,0 +1,3 @@
+if (QBS_INSTALL_MAN_PAGE)
+ install(FILES qbs.1 DESTINATION ${QBS_RESOURCES_INSTALL_DIR}/man/man1 COMPONENT qbs_docs)
+endif()
diff --git a/doc/man/man.pri b/doc/man/man.pri
deleted file mode 100644
index 2e29f9112..000000000
--- a/doc/man/man.pri
+++ /dev/null
@@ -1,5 +0,0 @@
-qbs_no_man_install: return()
-
-man.files = $$PWD/qbs.1
-man.path = $${QBS_INSTALL_PREFIX}/share/man/man1
-INSTALLS += man
diff --git a/doc/man/man.qbs b/doc/man/man.qbs
index 32f8e624e..8b81f62ae 100644
--- a/doc/man/man.qbs
+++ b/doc/man/man.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.File
import qbs.FileInfo
import qbs.ModUtils
diff --git a/doc/qbs-hugo.qdocconf b/doc/qbs-hugo.qdocconf
new file mode 100644
index 000000000..66ba0548c
--- /dev/null
+++ b/doc/qbs-hugo.qdocconf
@@ -0,0 +1,6 @@
+include(config/qbs-project.qdocconf)
+
+syntaxhighlighting = true
+
+HTML.prologue = "<div class=\"content\">"
+HTML.footer += "</div>"
diff --git a/doc/qbs-online.qdocconf b/doc/qbs-online.qdocconf
index fc90ebdd1..e7f05e3b2 100644
--- a/doc/qbs-online.qdocconf
+++ b/doc/qbs-online.qdocconf
@@ -3,7 +3,7 @@ include(config/qbs-project.qdocconf)
HTML.footer = \
" </div>\n" \
" <p class=\"copy-notice\">\n" \
- " <acronym title=\"Copyright\">&copy;</acronym> 2018 The Qt Company Ltd.\n" \
+ " <acronym title=\"Copyright\">&copy;</acronym> 2022 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" \
diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc
index 852329f73..cbe33f98a 100644
--- a/doc/qbs.qdoc
+++ b/doc/qbs.qdoc
@@ -33,7 +33,6 @@
/*!
- \contentspage{index.html}{Qbs}
\page index.html
\nextpage overview.html
@@ -68,8 +67,21 @@
\li \l{Generators}
\li \l{Multiplexing}
\li \l{Custom Modules and Items}
+ \li \l{Special Property Values}
\li \l{Module Providers}
\endlist
+ \li \l{Tutorial}
+ \list
+ \li \l{tutorial-1.html}{Application}
+ \li \l{tutorial-2.html}{Static Library}
+ \li \l{tutorial-3.html}{Dynamic Library}
+ \li \l{tutorial-4.html}{Convenience Items}
+ \li \l{tutorial-5.html}{Autotest}
+ \li \l{tutorial-6.html}{Project Properties}
+ \li \l{tutorial-7.html}{Buildconfig Module}
+ \li \l{tutorial-8.html}{Configurable Library}
+ \li \l{tutorial-9.html}{Version Header}
+ \endlist
\li \l{How-tos}
\li \l{Reference}
\list
@@ -82,6 +94,7 @@
\li \l{List of Built-in Services}
\li \l{Command-Line Interface}
\li \l{List of Modules}
+ \li \l{List of Module Providers}
\li \l{Command and JavaScriptCommand}
\endlist
@@ -94,7 +107,6 @@
/*!
- \contentspage index.html
\previouspage index.html
\page overview.html
\nextpage setup.html
@@ -301,25 +313,38 @@
\section2 IDE Integration
\QBS can be used not only from the command line, but also in combination
- with an IDE, such as Qt Creator, Microsoft Visual Studio, or Xcode.
- Qt Creator directly supports \QBS projects. Visual Studio and Xcode users
- can use \QBS to generate Microsoft Visual Studio and Xcode projects.
- For more information, see \l {Generators}.
+ with an IDE, such as Qt Creator or Visual Studio Code. These IDEs directly
+ support \QBS projects using the new \QBS \l{session} feature. Thus, these IDEs
+ can retrieve all the information required to build a single file or project
+ through a session's JSON protocol \l{Appendix C: The JSON API}{API}.
+
+ In addition, \QBS can generate projects for Visual Studio, IAR EW, and
+ Keil uVision, but it still is an experimental option. For more information,
+ see \l {Generators}.
\section3 Qt Creator
- \l{http://doc.qt.io/qtcreator/index.html}{Qt Creator} uses the same \QBS
- library as the \QBS command line tools. Therefore, it can retrieve all the
- information required to build a single file or project through a defined
- public API. Qt Creator provides accurate information about the build
- progress and displays a project tree that reflects the logical structure of
- the project, instead of presenting low-level information, such as the file
- system structure. Adding or removing source files keeps the existing project
- file structure intact.
+ \l{http://doc.qt.io/qtcreator/index.html}{Qt Creator} provides accurate
+ information about the build progress and displays a project tree that
+ reflects the logical structure of the project, instead of presenting low-level
+ information, such as the file system structure. Adding or removing source
+ files keeps the existing project file structure intact.
For more information about using \QBS to build projects from Qt Creator, see
\l{http://doc.qt.io/qtcreator/creator-project-qbs.html}{Setting Up Qbs}.
+ \section3 Visual Studio Code
+
+ \l{https://code.visualstudio.com/}{Visual Studio Code} provides the
+ \l{https://marketplace.visualstudio.com/items?itemName=qbs-community.qbs-tools}
+ {qbs-community} plugin that provides accurate information about the build progress
+ and displays a project tree that reflects the logical structure of the project.
+ Also, it can provide low-level information, such as the file system structure.
+
+ For more information about using \QBS to build projects from Visual Studio Code,
+ see \l{https://github.com/denis-shienkov/vscode-qbs/blob/master/docs/how-to.md}
+ {How To}.
+
\section1 Build Process
\image qbs-build-process.png
@@ -352,12 +377,12 @@
Module {
// ...
Rule {
- inputs {"cpp"]
- Artifact [
+ inputs: ["cpp"]
+ Artifact {
filePath: input.fileName + ".o"
- fileTags: {"obj"]
+ fileTags: ["obj"]
}
- prepare: [
+ prepare: {
// g++ -c main.cpp -o main.o ...
}
}
@@ -409,7 +434,6 @@
/*!
- \contentspage index.html
\previouspage overview.html
\page setup.html
\nextpage installing.html
@@ -424,7 +448,6 @@
*/
/*!
- \contentspage index.html
\previouspage reference.html
\page building-qbs.html
\nextpage porting-to-qbs.html
@@ -451,7 +474,7 @@
To build \QBS from the source, you need:
\list
- \li Qt 5.11, or later
+ \li Qt 5.15, or later
\li Windows: MinGW with GCC 4.9 or Microsoft Visual Studio 2015,
or later
\li Linux: GCC 4.9, or later, or Clang 3.9.0, or later
@@ -471,6 +494,50 @@
Regenerating the man page requires the \c help2man tool.
+ \section1 Building \QBS with СMake
+
+ To build \QBS, enter the following commands:
+
+ \code
+ mkdir build && cd build
+ cmake -DQt5_DIR=${QT_DIR}/lib/cmake/Qt5/ ..
+ make
+ \endcode
+
+ Where \c ${QT_DIR} is the directory where Qt is installed. Passing the \c Qt5_DIR option
+ is not necessary if \c qmake is present in \c PATH.
+
+ Depending on your platform, you might use \c mingw32-make, \c nmake, or
+ \c jom instead of \c make.
+
+ Alternatively, you can use the
+ \l{https://cmake.org/cmake/help/latest/generator/Ninja.html}{Ninja} generator:
+ \code
+ cmake -GNinja -DQt5_DIR=${QT_DIR}/lib/cmake/Qt5/ ..
+ ninja
+ \endcode
+
+ \section2 CMake Configure Options
+
+ \QBS recognizes the following CMake options (passed to CMake in the form of \c{-DOPTION=value})
+ to customize the build:
+
+ \table
+ \header \li Option \li Notes \li Default value
+ \row \li WITH_TESTS \li Enable autotests. \li \c ON
+ \row \li WITH_UNIT_TESTS \li Enable additional autotests. \li \c OFF
+ \li \c OFF
+ \row \li INSTALL_PUBLIC_HEADERS \li Whether to install public headers. \li \c ON
+ \endtable
+
+ \section2 Using ccache with CMake
+
+ To enable using \l{https://ccache.dev}{ccache} when building \QBS, pass the following options
+ to CMake:
+ \code
+ cmake -DQt5_DIR=${QT_DIR}/lib/cmake/Qt5/ -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ..
+ \endcode
+
\section1 Building \QBS with QMake
To build \QBS, enter the following command:
@@ -502,9 +569,6 @@
\row \li qbs_no_dev_install \li Exclude header files from installation, that is, perform a
non-developer build.
\row \li qbs_no_man_install \li Exclude the man page from installation.
- \row \li qbs_enable_project_file_updates \li Enable API for updating project files. This
- implies a dependency to the Qt GUI module.
- \row \li qbs_use_bundled_qtscript \li Use the bundled QtScript library.
\endtable
In addition, you can set the \c QBS_SYSTEM_SETTINGS_DIR environment variable
@@ -576,11 +640,6 @@
\li Enable additional autotests. Enabling this option will export some symbols that would
otherwise be private.
\row
- \li enableProjectFileUpdates
- \li \c false
- \li Enable API for updating project files. This is required for an IDE and implies a
- dependency to the Qt GUI module that would not be needed for the \QBS command-line tool.
- \row
\li enableRPath
\li \c true
\li Use this property to disable the use of rpath. This can be used when packaging \QBS
@@ -592,15 +651,10 @@
is required to build against the \QBS libraries.
\row
\li enableBundledQt
- \li \c true
+ \li \c false
\li Holds whether the Qt libraries that \QBS depends on will be bundled with \QBS during
the \c install step. This option is only implemented on macOS.
\row
- \li useBundledQtScript
- \li \c false
- \li Use the bundled QtScript module instead of the one shipped with Qt. In that case,
- QtScript should be checked out as a git submodule.
- \row
\li libDirName
\li \c "lib"
\li Directory name used by \c libInstallDir and \c importLibInstallDir properties.
@@ -694,7 +748,7 @@
A set of Docker images for developing \QBS (which are maintained by the \QBS team) is available
\l{https://hub.docker.com/u/qbsbuild/}{on Docker Hub}.
- Both Windows Server Core and Debian Linux container types are available.
+ Both Windows 10 and Debian Linux container types are available.
\note The source code for the \QBS development Docker images is located in the \c{docker/}
directory of the \QBS source tree, if you wish to build them yourself.
@@ -712,45 +766,46 @@
\li Latest stable release of \QBS for building \QBS with \QBS
\endlist
- Run the following command to download the \QBS development image based on Ubuntu 18.04 \e Bionic:
+ We are using docker-compose for building and running the Docker images because it simplifies
+ the Docker command line and ensures that the correct image tag is used. All available images
+ are listed in the \c docker-compose.yml file in the project root directory.
+
+ Run the following command to download the \QBS development image based on Ubuntu 20.04
+ \e Focal:
\code
- docker pull qbsbuild/qbsdev:bionic
+ docker-compose pull focal
\endcode
You can then create a new container with the \QBS source directory mounted from your host
machine's file system, by running:
\code
- docker run -it -v ${PWD}:/qbs -w /qbs qbsbuild/qbsdev:bionic
+ docker-compose run --rm focal
\endcode
You will now be in an interactive Linux shell where you can develop and build \QBS.
- For convenience, you can also run \c docker-compose from the project root directory:
-
- \code
- docker-compose run --rm bionic
- \endcode
-
- This will download and run the container in one go and mount the project root directory
- to \c /qbs in the container.
-
\section2 Windows Containers
To build \QBS for Windows using Windows containers, your host OS must be running Windows 10 Pro
- and have Hyper-V enabled. \l{https://docs.docker.com/docker-for-windows/#switch-between-windows-and-linux-containers}{Switch your Docker environment to use Windows containers}, then run the
- following command to download the Windows 10 \QBS development image:
+ and have Hyper-V enabled. \l{https://docs.docker.com/docker-for-windows/#switch-between-windows-and-linux-containers}{Switch your Docker environment to use Windows containers}.
+
+ We are using docker-compose for building and running the Docker images because it simplifies
+ the Docker command line and ensures that the correct image tag is used. All available images
+ are listed in the \c docker-compose.yml file in the project root directory.
+
+ Run the following command to download the \QBS development image based on Windows 10:
\code
- docker pull qbsbuild/qbsdev:windowsservercore
+ docker-compose pull windows
\endcode
You can then create a new container with the \QBS source directory mounted from your host
machine's file system, by running:
\code
- docker run -it -v %CD%:C:\qbs -w C:\qbs qbsbuild/qbsdev:windowsservercore
+ docker-compose run --rm windows
\endcode
If you want to use Windows containers on a macOS or Linux host, you will have to create a
@@ -764,7 +819,7 @@
\code
eval $(docker-machine env windows)
- docker run -it -v C:$PWD:C:\\qbs -w C:\\qbs qbsbuild/qbsdev:windowsservercore
+ docker-compose run --rm windows
\endcode
\section2 Building Release Packages
@@ -772,20 +827,19 @@
Release packages for \QBS for Windows can be built using the following command on Windows:
\code
- docker run --rm -v %CD%:C:\qbs -w C:\qbs qbsbuild/qbsdev:windowsservercore cmd /c scripts\make-release-archives
+ docker-compose run --rm windows cmd /c scripts\make-release-archives
\endcode
For building release packages for Windows on macOS or Linux:
\code
eval $(docker-machine env windows)
- docker run --rm -v C:$PWD:C:\\qbs -w C:\\qbs qbsbuild/qbsdev:windowsservercore cmd /c scripts\\make-release-archives
+ docker-compose run --rm windows cmd /c scripts\\make-release-archives
\endcode
*/
/*!
- \contentspage index.html
\previouspage setup.html
\page installing.html
\nextpage configuring.html
@@ -842,7 +896,6 @@
*/
/*!
- \contentspage index.html
\previouspage installing.html
\page configuring.html
\nextpage qt-versions.html
@@ -866,7 +919,7 @@
For example, a profile for building C++ applications contains at least the
installation path and the type of the compiler toolchain. A profile for
building Qt applications contains the toolchain-specific properties as well
- as \l{Qt-specific Module Provider Properties}{the path to the Qt installation}.
+ as \l{Qt::qmakeFilePaths}{the path to the Qt installation}.
This topic describes profiles stored in the \QBS settings. In some cases it
might be beneficial to keep profiles explicitly in the project sources. This
@@ -936,7 +989,6 @@
*/
/*!
- \contentspage index.html
\previouspage configuring.html
\page qt-versions.html
\nextpage usage.html
@@ -1006,7 +1058,6 @@
*/
/*!
- \contentspage index.html
\previouspage qt-versions.html
\page usage.html
\nextpage language-introduction.html
@@ -1023,14 +1074,13 @@
\li \l{Generators}
\li \l{Multiplexing}
\li \l{Custom Modules and Items}
+ \li \l{Special Property Values}
\li \l{Module Providers}
\endlist
*/
-
/*!
- \contentspage index.html
\previouspage usage.html
\page language-introduction.html
\nextpage building-applications.html
@@ -1055,9 +1105,6 @@
}
\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
@@ -1091,6 +1138,11 @@
---CrazyProduct.qbs---
Product {
property string craziness: "low"
+
+ files: [
+ "Crazy.h",
+ "Crazy.cpp"
+ ]
}
---hellocrazyworld.qbs---
@@ -1099,6 +1151,7 @@
CrazyProduct {
craziness: "enormous"
name: "hellocrazyworld"
+ files: base.concat(["main.cpp"])
// ...
}
\endcode
@@ -1124,6 +1177,7 @@
}
\endcode
+ See \l {Special Property Values} for more details about the \l base property.
\section1 Modules
@@ -1166,27 +1220,17 @@
}
\endcode
- A module can implicitly depend on other modules. For example, the
- \l{Qt.core} module depends on the \l{cpp} module. However, to set the
- properties of a module, you must make the dependency explicit.
+ A module can depend on other modules. For example, the
+ \l{Qt.core} module depends on the \l{cpp} module. The module dependencies are transitive,
+ i.e. in a Product, all dependent modules are accessible:
\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" }
+ // the "cpp" module is available since
+ // "Qt.core" depends on "cpp".
cpp.optimization: "ludicrousSpeed"
}
\endcode
@@ -1216,7 +1260,7 @@
This is how you do it:
\code
Group {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
files: [
"harddiskdeleter_win.cpp",
"blowupmonitor_win.cpp",
@@ -1224,7 +1268,7 @@
]
}
Group {
- condition: qbs.targetOS.contains("linux")
+ condition: qbs.targetOS.includes("linux")
files: [
"harddiskdeleter_linux.cpp",
"blowupmonitor_linux.cpp",
@@ -1240,8 +1284,6 @@
\list
\li \l{qbs::}{buildVariant} that specifies the name of the build variant
for the current build.
- \li \l{qbs::}{hostOS} that is set by \QBS internally and specifies the
- operating system \QBS is running on.
\li \l{qbs::}{targetOS} that specifies the operating system you want to
build the project for.
\endlist
@@ -1437,13 +1479,15 @@
/*!
- \contentspage index.html
\previouspage language-introduction.html
\page building-applications.html
\nextpage running-applications.html
\title Building Applications
+ This section assumes that \QBS is present in \c PATH. For the details how to install \QBS, see
+ the \l{Installing} page.
+
To build applications from the command line, enter the following commands:
\code
@@ -1497,7 +1541,6 @@
*/
/*!
- \contentspage index.html
\previouspage running-applications.html
\page installing-files.html
\nextpage {Target Platforms}
@@ -1524,33 +1567,60 @@
\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):
+ When building, \QBS installs artifacts into the default root folder, namely
+ \c{<build root>/install-root}. The \l{qbs::installPrefix}{qbs.installPrefix} and
+ \l{qbs::installDir}{qbs.installDir} properties are appended to the root folder.
\code
- qbs --clean-install-root qbs.installRoot:/tmp/myProjectRoot
+ qbs build qbs.installPrefix:/usr
\endcode
+ In this example, the executable will be installed into the \c{<build root>/install-root/usr/bin}
+ folder and the QML files will be installed into the
+ \c{<build root>/install-root/usr/share/myproject} folder.
- Here, we want the \c 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.
+ To skip installation during the build, use the \c --no-install option.
+
+ To override the default location, use the \c --install-root option of the \c{qbs install}
+ command:
+ \code
+ qbs build --no-install qbs.installPrefix:/usr
+ sudo qbs install --no-build --install-root /
+ \endcode
+ In this example, artifacts will be installed directly into the \c /usr folder. Since the
+ \c{qbs install} command implies \c build, we use the \c --no-build parameter to ensure that
+ we do not accidentally rebuild the project, thereby changing the artifacts' owner to \c root.
- If the \l{qbs::installRoot}{qbs.installRoot} property is not given, a
- default is used, namely \c{<build root>/install-root}.
+ Sometimes, it makes sense to install the application into a temporary root folder, keeping the
+ same folder structure within that root folder as in the examples above; for instance,
+ when building a Linux package such as \c deb or \c rmp. To install the application into the
+ \c /tmp/myProjectRoot folder, use the following command:
+
+ \code
+ $ qbs install --install-root /tmp/myProjectRoot
+ \endcode
+
+ In this example, the executable will be installed into the \c{/tmp/myProjectRoot/usr/bin} folder
+ and QML files will be installed into the \c{/tmp/myProjectRoot/usr/share/myproject} folder.
+
+ To remove all files from the install root prior to installing, use the \c --clean-install-root
+ parameter:
+
+ \code
+ qbs install --clean-install-root --install-root /tmp/myProjectRoot
+ \endcode
For more information about how the installation path is constructed, see
\l {Installation Properties}.
*/
/*!
- \contentspage index.html
\previouspage building-applications.html
\page running-applications.html
\nextpage installing-files.html
\title Running Applications
- By default, running an application also builds it and installs it to a
- location from where it can be run on the desktop or on a device.
+ Qbs has the convenience \l{run}{qbs run} command that simplifies running applications.
For example, entering the following command runs the Qt Creator application:
@@ -1558,11 +1628,19 @@
qbs run --products qtcreator
\endcode
- This command also builds and installs the product, if necessary.
+ By default, running an application also builds it and installs it to a
+ location from where it can be run on the desktop or on a device. Also, this command
+ \l{Module::setupRunEnvironment}{sets up} the environment so that the application can find
+ dependent libraries.
+
+ This is not the case when running the binary manually - you'll have to make sure that the
+ app will find its dependencies and/or is relocatable. You can achieve that by
+ \l{How do I make use of rpaths?}{configuring rpaths} properly or setting the appropriate
+ environment variable for the respective host system.
+
*/
/*!
- \contentspage index.html
\previouspage {Target Platforms}
\page shell.html
\nextpage generators.html
@@ -1582,10 +1660,9 @@
*/
/*!
- \contentspage index.html
\previouspage multiplexing.html
\page custom-modules.html
- \nextpage module-providers.html
+ \nextpage special-property-values.html
\title Custom Modules and Items
@@ -1614,6 +1691,8 @@
To introduce a custom item \c MyItem, create the file \c{search-path/imports/MyItem.qbs}.
+ \note Item file names must start with a capital letter due to the fact that type names can
+ only start with a capital letter. Otherwise, the file will be silently ignored.
\section1 Making \QBS Aware of Custom Modules and Items
@@ -1654,10 +1733,166 @@
*/
/*!
- \contentspage index.html
\previouspage custom-modules.html
+ \page special-property-values.html
+ \nextpage module-providers.html
+
+ \title Special Property Values
+
+ Depending on the context, \QBS provides the following special values for use in property
+ bindings and JavaScript code:
+
+ \list
+ \li \l base
+ \li \l exportingProduct
+ \li \l filePath
+ \li \l importingProduct
+ \li \l original
+ \li \l outer
+ \li \l path
+ \li \l product
+ \li \l project
+ \endlist
+
+ \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 exportingProduct
+ Within an \l Export item, you can use the \c exportingProduct variable to refer to
+ the product which defines the Export item:
+
+ \code
+ Product {
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: exportingProduct.sourceDirectory
+ }
+ }
+ \endcode
+
+ \section2 \c filePath
+
+ This value holds the full file path to the \c .qbs file it appears in. This property is
+ rarely used, but might be useful when debugging:
+ \code
+ Product {
+ property bool dummy: {
+ console.info("I'm located at " + filePath);
+ }
+ }
+ \endcode
+
+ \section2 \c importingProduct
+ Within an \l Export item, you can use the \c importingProduct variable to refer to
+ the product that pulls in the resulting module:
+
+ \code
+ Product {
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: importingProduct.buildDirectory
+ }
+ }
+ \endcode
+ Usually, you should use the \l product variable instead for consistency with \l Module items.
+
+ \section2 \c original
+ On the right-hand side of a module property binding, this refers to the value of the property
+ in the module itself (possibly overridden from a profile). Use it to set a module property
+ conditionally:
+ \code
+ Module { // This is mymodule
+ property string aProperty: "z"
+ }
+ ----------
+ Product {
+ Depends { name: "mymodule" }
+ Depends { name: "myothermodule" }
+ // "y" if myothermodule.anotherProperty is "x", "z" otherwise:
+ mymodule.aProperty: myothermodule.anotherProperty === "x" ? "y" : original
+ }
+ \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 only valid in \l{Group} and \l{Properties} items:
+ \code
+ Product {
+ Depends { name: "mymodule" }
+ mymodule.someProperty: ["value1"]
+ Group {
+ name: "special files"
+ files: ["somefile1", "somefile2"]
+ mymodule.someProperty: outer.concat(["value"]) // => ["value1", "value2"]
+ }
+ }
+ \endcode
+
+ \section2 \c path
+
+ This value holds the path to the folder where the \c .qbs file is located. Use it to e.g. add
+ the product's directory to file paths:
+ \code
+ Product {
+ Depends { name: "cpp" }
+ cpp.includePaths: path
+ }
+ \endcode
+
+ \section2 \c product
+
+ This value holds the properties of the product that contains the current item or pulls in the
+ current module:
+ \code
+ Module {
+ Rule {
+ Artifact {
+ fileTags: product.type
+ filePath: {
+ var result = input.fileName;
+ // module properties are available as well
+ if (product.qbs.buildVariant === "debug")
+ result = result + "_debug";
+ result = result + ".out";
+ return result;
+ }
+ }
+ }
+ }
+ \endcode
+ Within the \l Export item, same as \l importingProduct.
+
+ \section2 \c project
+ This value holds the properties of the project that references the current item or pulls in the
+ current module:
+ \code
+ Project {
+ property bool enableProduct: true
+ Product {
+ name: "theProduct"
+ condition: project.enableProduct
+ }
+ }
+ \endcode
+ If the nearest project in the project tree does not have the desired property, \QBS looks it
+ up in the parent project, potentially all the way up to the top-level project.
+*/
+
+/*!
+ \previouspage special-property-values.html
\page module-providers.html
- \nextpage howtos.html
+ \nextpage tutorial.html
\title Module Providers
@@ -1673,7 +1908,12 @@
If \QBS encounters a \l Depends item whose name does not match a known module,
it checks whether such a module can be generated. This procedure works as follows:
\list 1
- \li All \l{Project::qbsSearchPaths}{search paths} are scanned for a file called
+ \li If the \l{Product::qbsModuleProviders}{qbsModuleProviders} property is not
+ \c undefined, for each provider name in the list, all
+ \l{Project::qbsSearchPaths}{search paths} are scanned for a file called
+ \c {module-providers/<name>.qbs} or \c {module-providers/<name>/provider.qbs}.
+ \li If the \l{Product::qbsModuleProviders}{qbsModuleProviders} property is \c undefined,
+ \l{Project::qbsSearchPaths}{search paths} are scanned for a file called
\c {module-providers/<name>/provider.qbs}, where \c <name> is the name of the dependency
as specified in the \c Depends item. Multi-component names such as "a.b" are turned
into nested directories, and each of them is scanned, starting with the deepest path.
@@ -1687,14 +1927,22 @@
a second attempt will be made to locate the dependency using the new paths.
The search for a matching module provider ends as soon as one was found, regardless
of whether it created any modules or not.
- \li If no matching module provider was found in any of the search paths, \QBS will fall back
- to a generic module provider, which creates a module that attempts to locate the
- dependency via \c pkg-config.
- This fallback mechanism can be disabled in the respective
- \l{Depends::enableFallback}{Depends} item or globally via the
- \l{no-fallback-module-provider}{--no-fallback-module-provider} option.
\endlist
+ \section1 Selecting Module Providers
+
+ As described above, it is possible to select which providers to run using the
+ \l{Product::qbsModuleProviders}{qbsModuleProviders} property. Providers contribute to the
+ \l{Product::}{qbsSearchPaths} in the order specified by this property, so modules generated
+ by providers specified earlier are prioritized. This property can be set on the
+ Product as well as the \l{Project::qbsModuleProviders}{Project} level:
+
+ \code
+ $ qbs resolve project.qbsModuleProviders:providerA \ # sets property globally for the Project
+ projects.SomeProject.qbsModuleProviders:providerB \ # overrides property for the specific Project
+ products.SomeProduct.qbsModuleProviders:providerC \ # overrides property for the specific Product
+ \endcode
+
\section1 Parameterizing Module Providers
You can pass information to module providers from the command line, via profiles or
@@ -1710,7 +1958,6 @@
*/
/*!
- \contentspage index.html
\previouspage shell.html
\page generators.html
\nextpage multiplexing.html
@@ -1902,7 +2149,6 @@
*/
/*!
- \contentspage index.html
\previouspage generators.html
\page multiplexing.html
\nextpage custom-modules.html
@@ -1966,7 +2212,6 @@
*/
/*!
- \contentspage index.html
\previouspage json-api.html
\page attributions.html
diff --git a/doc/reference/cli/builtin/cli-build.qdoc b/doc/reference/cli/builtin/cli-build.qdoc
index f9312350d..8547a78d2 100644
--- a/doc/reference/cli/builtin/cli-build.qdoc
+++ b/doc/reference/cli/builtin/cli-build.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-build.html
\ingroup cli
@@ -79,8 +78,6 @@
\include cli-options.qdocinc products-specified
\include cli-options.qdocinc settings-dir
\include cli-options.qdocinc show-progress
- \target no-fallback-module-provider
- \include cli-options.qdocinc no-fallback-module-provider
\include cli-options.qdocinc wait-lock
\section1 Parameters
diff --git a/doc/reference/cli/builtin/cli-clean.qdoc b/doc/reference/cli/builtin/cli-clean.qdoc
index bd6ba1616..901ac2f33 100644
--- a/doc/reference/cli/builtin/cli-clean.qdoc
+++ b/doc/reference/cli/builtin/cli-clean.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-clean.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-dump-nodes-tree.qdoc b/doc/reference/cli/builtin/cli-dump-nodes-tree.qdoc
index 2e5485ec2..270927ccd 100644
--- a/doc/reference/cli/builtin/cli-dump-nodes-tree.qdoc
+++ b/doc/reference/cli/builtin/cli-dump-nodes-tree.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-dump-nodes-tree.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-generate.qdoc b/doc/reference/cli/builtin/cli-generate.qdoc
index b6dda6f4f..c7c14fc84 100644
--- a/doc/reference/cli/builtin/cli-generate.qdoc
+++ b/doc/reference/cli/builtin/cli-generate.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-generate.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-help.qdoc b/doc/reference/cli/builtin/cli-help.qdoc
index c36d03317..da19fda15 100644
--- a/doc/reference/cli/builtin/cli-help.qdoc
+++ b/doc/reference/cli/builtin/cli-help.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-help.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-install.qdoc b/doc/reference/cli/builtin/cli-install.qdoc
index b55f05296..92fad06a9 100644
--- a/doc/reference/cli/builtin/cli-install.qdoc
+++ b/doc/reference/cli/builtin/cli-install.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-install.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-list-products.qdoc b/doc/reference/cli/builtin/cli-list-products.qdoc
index 92ef043da..583029a73 100644
--- a/doc/reference/cli/builtin/cli-list-products.qdoc
+++ b/doc/reference/cli/builtin/cli-list-products.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-list-products.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-resolve.qdoc b/doc/reference/cli/builtin/cli-resolve.qdoc
index b13f3de3d..99f1658ea 100644
--- a/doc/reference/cli/builtin/cli-resolve.qdoc
+++ b/doc/reference/cli/builtin/cli-resolve.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-resolve.html
\ingroup cli
@@ -56,7 +55,7 @@
\include cli-options.qdocinc more-verbose
\include cli-options.qdocinc settings-dir
\include cli-options.qdocinc show-progress
- \include cli-options.qdocinc no-fallback-module-provider
+ \include cli-options.qdocinc deprecation-warnings
\section1 Parameters
diff --git a/doc/reference/cli/builtin/cli-run.qdoc b/doc/reference/cli/builtin/cli-run.qdoc
index 9f20f868e..a0d3adee9 100644
--- a/doc/reference/cli/builtin/cli-run.qdoc
+++ b/doc/reference/cli/builtin/cli-run.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-run.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-session.qdoc b/doc/reference/cli/builtin/cli-session.qdoc
index 62999a82e..853674234 100644
--- a/doc/reference/cli/builtin/cli-session.qdoc
+++ b/doc/reference/cli/builtin/cli-session.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-session.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-shell.qdoc b/doc/reference/cli/builtin/cli-shell.qdoc
index 221bef24d..f024fc335 100644
--- a/doc/reference/cli/builtin/cli-shell.qdoc
+++ b/doc/reference/cli/builtin/cli-shell.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-shell.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-status.qdoc b/doc/reference/cli/builtin/cli-status.qdoc
index ddc8537f3..16bea5198 100644
--- a/doc/reference/cli/builtin/cli-status.qdoc
+++ b/doc/reference/cli/builtin/cli-status.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-status.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-update-timestamps.qdoc b/doc/reference/cli/builtin/cli-update-timestamps.qdoc
index 366f1352b..8d66071b0 100644
--- a/doc/reference/cli/builtin/cli-update-timestamps.qdoc
+++ b/doc/reference/cli/builtin/cli-update-timestamps.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-update-timestamps.html
\ingroup cli
diff --git a/doc/reference/cli/builtin/cli-version.qdoc b/doc/reference/cli/builtin/cli-version.qdoc
index 38240eef0..f4dab3cb9 100644
--- a/doc/reference/cli/builtin/cli-version.qdoc
+++ b/doc/reference/cli/builtin/cli-version.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-version.html
\ingroup cli
diff --git a/doc/reference/cli/cli-options.qdocinc b/doc/reference/cli/cli-options.qdocinc
index b02ce68ea..ecb8f5c3e 100644
--- a/doc/reference/cli/cli-options.qdocinc
+++ b/doc/reference/cli/cli-options.qdocinc
@@ -331,6 +331,27 @@
//! [config-ui-system]
+//! [deprecation-warnings]
+
+ \section2 \c {--deprecation-warnings <mode>}
+
+ Uses the specified deprecation warning mode, which controls what to do when deprecated
+ items or properties are encountered in the project. By default, a warning is emitted
+ if the item or property is scheduled for removal in the next minor version of \QBS.
+ Warnings can also be switched on or off unconditionally, and it can be specified that
+ project resolving should abort if deprecated constructs are present.
+
+ Possible values of \c <mode> are:
+
+ \list
+ \li \c error
+ \li \c on
+ \li \c before-removal (default value)
+ \li \c off
+ \endlist
+
+//! [deprecation-warnings]
+
//! [log-level]
\section2 \c {--log-level <level>}
@@ -499,6 +520,9 @@
\li \c iar
\li \c keil
\li \c sdcc
+ \li \c cosmic
+ \li \c dmc
+ \li \c watcom
\endlist
//! [type]
diff --git a/doc/reference/cli/cli.qdoc b/doc/reference/cli/cli.qdoc
index c93b51cd0..ccc67373a 100644
--- a/doc/reference/cli/cli.qdoc
+++ b/doc/reference/cli/cli.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage reference.html
\group cli
\title Command-Line Interface
diff --git a/doc/reference/cli/tools/cli-config-ui.qdoc b/doc/reference/cli/tools/cli-config-ui.qdoc
index d900841d6..ed2287c1e 100644
--- a/doc/reference/cli/tools/cli-config-ui.qdoc
+++ b/doc/reference/cli/tools/cli-config-ui.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-config-ui.html
\ingroup cli
diff --git a/doc/reference/cli/tools/cli-config.qdoc b/doc/reference/cli/tools/cli-config.qdoc
index 2b78ff8ef..6758dce61 100644
--- a/doc/reference/cli/tools/cli-config.qdoc
+++ b/doc/reference/cli/tools/cli-config.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-config.html
\ingroup cli
diff --git a/doc/reference/cli/tools/cli-create-project.qdoc b/doc/reference/cli/tools/cli-create-project.qdoc
index db3af2284..c78d8fb83 100644
--- a/doc/reference/cli/tools/cli-create-project.qdoc
+++ b/doc/reference/cli/tools/cli-create-project.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-create-project.html
\ingroup cli
diff --git a/doc/reference/cli/tools/cli-setup-android.qdoc b/doc/reference/cli/tools/cli-setup-android.qdoc
index 5421de896..357d42706 100644
--- a/doc/reference/cli/tools/cli-setup-android.qdoc
+++ b/doc/reference/cli/tools/cli-setup-android.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-setup-android.html
\ingroup cli
diff --git a/doc/reference/cli/tools/cli-setup-qt.qdoc b/doc/reference/cli/tools/cli-setup-qt.qdoc
index 1cf961d0d..c50348144 100644
--- a/doc/reference/cli/tools/cli-setup-qt.qdoc
+++ b/doc/reference/cli/tools/cli-setup-qt.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-setup-qt.html
\ingroup cli
diff --git a/doc/reference/cli/tools/cli-setup-toolchains.qdoc b/doc/reference/cli/tools/cli-setup-toolchains.qdoc
index 70cb0c23b..97f9c0235 100644
--- a/doc/reference/cli/tools/cli-setup-toolchains.qdoc
+++ b/doc/reference/cli/tools/cli-setup-toolchains.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage cli.html
\page cli-setup-toolchains.html
\ingroup cli
diff --git a/doc/reference/commands.qdoc b/doc/reference/commands.qdoc
index d34c20cb7..3f542e1d1 100644
--- a/doc/reference/commands.qdoc
+++ b/doc/reference/commands.qdoc
@@ -29,7 +29,6 @@
// TODO: "\c" markup is used for all properties in table due to QTBUG-35505.
/*!
- \contentspage reference.html
\page commands.html
\title Command and JavaScriptCommand
@@ -124,9 +123,11 @@
\li Time limit for the command execution in seconds. If the command does not finish within
the timeout, it is cancelled. In case of a \c Command, the process is requested to
terminate. If it does not terminate within three seconds, it is killed. A value below
- or equal to 0 means no timeout.
+ or equal to 0 means no timeout. \br
+ This property was introduced in Qbs 1.15.
\endtable
+
\section2 Command Properties
\table
diff --git a/doc/reference/items/convenience/appleapplicationdiskimage.qdoc b/doc/reference/items/convenience/appleapplicationdiskimage.qdoc
index 69a10c797..d1fa15052 100644
--- a/doc/reference/items/convenience/appleapplicationdiskimage.qdoc
+++ b/doc/reference/items/convenience/appleapplicationdiskimage.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\nextpage AppleDiskImage
\qmltype AppleApplicationDiskImage
\since Qbs 1.9
@@ -78,10 +77,11 @@
The base directory from which artifacts installed into the disk image will be copied.
This directory is always considered to be relative to \l{qbs::installRoot}
- {qbs.installRoot}.
+ {qbs.installRoot}/\l{qbs::installPrefix}
+ {qbs.installPrefix}.
For example, if the application Example.app exists at
- \c{qbs.installRoot/Applications/Example.app}, and the value of this property
- is \c{"/Applications"}, the application will be located at\c{/Example.app}
+ \c{qbs.installRoot/qbs.installPrefix/Applications/Example.app}, and the value of this property
+ is \c{"/Applications"}, the application will be located at \c{/Example.app}
relative to the disk image root. Therefore, its full path when the disk
image is mounted would be something like \c{/Volumes/Example-1.0/Example.app}.
diff --git a/doc/reference/items/convenience/applediskimage.qdoc b/doc/reference/items/convenience/applediskimage.qdoc
index 78772db52..16fec266b 100644
--- a/doc/reference/items/convenience/applediskimage.qdoc
+++ b/doc/reference/items/convenience/applediskimage.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage AppleApplicationDiskImage
\nextpage Application
\qmltype AppleDiskImage
diff --git a/doc/reference/items/convenience/application.qdoc b/doc/reference/items/convenience/application.qdoc
index c09d15918..15b05515d 100644
--- a/doc/reference/items/convenience/application.qdoc
+++ b/doc/reference/items/convenience/application.qdoc
@@ -25,8 +25,7 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
- \previouspage AppleApplicationDiskImage
+ \previouspage AppleDiskImage
\nextpage ApplicationExtension
\qmltype Application
\inherits Product
@@ -41,15 +40,16 @@
The target artifact of this type of product is usually an executable binary
tagged \c "application".
However, on Android, unless you set \l{Product::}{consoleApplication} to \c true,
- the application target will be an APK package tagged \c "android.apk", and a
- dependency to the \l{Android.sdk} module is automatically added to the product.
+ the application target will be an APK or an AAB package tagged \c "android.package"
+ according to the \l{Android.sdk}{packageType} property. A dependency to the \l{Android.sdk}
+ module is automatically added to the product.
*/
/*!
\qmlproperty bool Application::install
If \c{true}, the executable that is produced when building the application will be installed
- to \l installDir.
+ to \l{Application::installDir}{installDir}.
\defaultvalue \c false
\since Qbs 1.13
@@ -58,8 +58,8 @@
/*!
\qmlproperty string Application::installDir
- Where to install the executable that is produced when building the application, if \l install
- is enabled.
+ Where to install the executable that is produced when building the application, if
+ \l{Application::install}{install} is enabled.
The value is appended to \l{qbs::installPrefix}{qbs.installPrefix}
when constructing the actual installation directory.
@@ -67,3 +67,28 @@
\defaultvalue \c Applications if the app is a \l{bundle::isBundle}{bundle}, \c bin otherwise.
\since Qbs 1.13
*/
+
+/*!
+ \qmlproperty bool Application::installDebugInformation
+
+ If \c{true}, the debug information will be installed to
+ \l{Application::debugInformationInstallDir}{debugInformationInstallDir}.
+
+ \defaultvalue \c false
+ \since Qbs 1.16
+ \sa{How do I separate and install debugging symbols?}
+*/
+
+/*!
+ \qmlproperty string Application::debugInformationInstallDir
+
+ Where to install the debug information if \l installDebugInformation is enabled.
+
+ The value is appended to \l{qbs::installPrefix}{qbs.installPrefix}
+ when constructing the actual installation directory.
+
+ \defaultvalue \l installDir.
+ \since Qbs 1.16
+
+ \sa{How do I separate and install debugging symbols?}
+*/
diff --git a/doc/reference/items/convenience/applicationextension.qdoc b/doc/reference/items/convenience/applicationextension.qdoc
index bc65c5449..9ff0101ff 100644
--- a/doc/reference/items/convenience/applicationextension.qdoc
+++ b/doc/reference/items/convenience/applicationextension.qdoc
@@ -26,7 +26,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage Application
\nextpage AutotestRunner
\qmltype ApplicationExtension
diff --git a/doc/reference/items/convenience/autotestrunner.qdoc b/doc/reference/items/convenience/autotestrunner.qdoc
index e8690ffe2..c9fb217ef 100644
--- a/doc/reference/items/convenience/autotestrunner.qdoc
+++ b/doc/reference/items/convenience/autotestrunner.qdoc
@@ -26,7 +26,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage ApplicationExtension
\nextpage CppApplication
\qmltype AutotestRunner
@@ -69,6 +68,27 @@
\endcode
\endlist
+
+ \section2 Setting Properties for individual Tests
+ \target autotestrunner-autotest-module
+
+ To control the behavior of individual tests, some properties of the \c AutotestRunner
+ can be overridden by depending on the \l autotest module and setting its properties:
+
+ \code
+ CppApplication {
+ name: "tst_mytest"
+ type: ["application", "autotest"]
+
+ Depends { name: "autotest" }
+
+ autotest.timeout: 60
+
+ // ...
+ }
+ \endcode
+
+
\section2 Relevant Job Pools
\target autotestrunner-job-pools
@@ -147,3 +167,16 @@
\defaultvalue empty
*/
+
+/*!
+ \qmlproperty int AutotestRunner::timeout
+
+ Time limit for the execution of the individual tests. If a test does not finish within
+ the time limit, the test is cancelled and considered failed. A value below or equal to 0
+ means no timeout.
+ A test can override this by setting the \l{autotest::timeout}{timeout} property
+ of the \l autotest module.
+
+ \defaultvalue -1
+ \since Qbs 1.15
+*/
diff --git a/doc/reference/items/convenience/cppapplication.qdoc b/doc/reference/items/convenience/cppapplication.qdoc
index 13bd523b7..3518ba1e1 100644
--- a/doc/reference/items/convenience/cppapplication.qdoc
+++ b/doc/reference/items/convenience/cppapplication.qdoc
@@ -25,18 +25,17 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage AutotestRunner
\nextpage DynamicLibrary
\qmltype CppApplication
- \inherits Product
+ \inherits Application
\inqmlmodule QbsConvenienceItems
\ingroup list-of-items
\keyword QML.CppApplication
\brief C++ application.
- A CppApplication is a \l{Product}{product} that has a dependency on the
+ A CppApplication is an \l{Application}{application} that has a dependency on the
\l{cpp} module. It is entirely equivalent to the following:
\code
diff --git a/doc/reference/items/convenience/dynamiclibrary.qdoc b/doc/reference/items/convenience/dynamiclibrary.qdoc
index 488aef3eb..861d0509d 100644
--- a/doc/reference/items/convenience/dynamiclibrary.qdoc
+++ b/doc/reference/items/convenience/dynamiclibrary.qdoc
@@ -25,11 +25,10 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage CppApplication
\nextpage InnoSetup
\qmltype DynamicLibrary
- \inherits Product
+ \inherits Library
\inqmlmodule QbsConvenienceItems
\ingroup list-of-items
\keyword QML.DynamicLibrary
@@ -37,7 +36,7 @@
\brief Dynamic library.
- A DynamicLibrary item is a \l{Product} of the \l{Product::}{type}
+ A DynamicLibrary item is a \l{Library}{library} of the \l{Product::}{type}
\c "dynamiclibrary".
For Android targets, the following applies:
@@ -47,51 +46,3 @@
\li There is a dependency on the \l{cpp} and \l{Android.ndk} modules.
\endlist
*/
-
-/*!
- \qmlproperty bool DynamicLibrary::install
-
- If \c{true}, the library will be installed to \l installDir.
-
- \defaultvalue \c false
- \since Qbs 1.13
-*/
-
-/*!
- \qmlproperty string DynamicLibrary::installDir
-
- Where to install the library, if \l install is enabled. On Unix, the symbolic links
- are also installed to this location.
-
- The value is appended to \l{qbs::installPrefix}{qbs.installPrefix}
- when constructing the actual installation directory.
-
- \defaultvalue \c Library/Frameworks if the library is a \l{bundle::isBundle}{bundle},
- otherwise \c bin for Windows and \c lib for Unix-like targets.
- \since Qbs 1.13
-*/
-
-/*!
- \qmlproperty bool DynamicLibrary::installImportLib
-
- If \c{true}, the import library will be installed to \l importLibInstallDir.
- This property is only relevant for Windows targets.
- Enable it if you want to create a development package.
-
- \defaultvalue \c false
- \since Qbs 1.13
-*/
-
-/*!
- \qmlproperty string DynamicLibrary::importLibInstallDir
-
- Where to install the import library, if \l installImportLib is enabled.
-
- The value is appended to \l{qbs::installPrefix}{qbs.installPrefix}
- when constructing the actual installation directory.
-
- This property is only relevant for Windows targets.
-
- \defaultvalue \c lib
- \since Qbs 1.13
-*/
diff --git a/doc/reference/items/convenience/innosetup.qdoc b/doc/reference/items/convenience/innosetup.qdoc
index f4b52dafd..0f66243e9 100644
--- a/doc/reference/items/convenience/innosetup.qdoc
+++ b/doc/reference/items/convenience/innosetup.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage DynamicLibrary
\nextpage InstallPackage
\qmltype InnoSetup
diff --git a/doc/reference/items/convenience/installpackage.qdoc b/doc/reference/items/convenience/installpackage.qdoc
index 4f82f71b7..7c5e8ec9b 100644
--- a/doc/reference/items/convenience/installpackage.qdoc
+++ b/doc/reference/items/convenience/installpackage.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage InnoSetup
\nextpage JavaClassCollection
\qmltype InstallPackage
diff --git a/doc/reference/items/convenience/javaclasscollection.qdoc b/doc/reference/items/convenience/javaclasscollection.qdoc
index fb93eb29e..ae060135c 100644
--- a/doc/reference/items/convenience/javaclasscollection.qdoc
+++ b/doc/reference/items/convenience/javaclasscollection.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage InstallPackage
\nextpage JavaJarFile
\qmltype JavaClassCollection
diff --git a/doc/reference/items/convenience/javajarfile.qdoc b/doc/reference/items/convenience/javajarfile.qdoc
index 48643d80e..97c775f16 100644
--- a/doc/reference/items/convenience/javajarfile.qdoc
+++ b/doc/reference/items/convenience/javajarfile.qdoc
@@ -25,9 +25,8 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage JavaClassCollection
- \nextpage LoadableModule
+ \nextpage Library
\qmltype JavaJarFile
\inherits Product
\inqmlmodule QbsConvenienceItems
diff --git a/doc/reference/items/convenience/library.qdoc b/doc/reference/items/convenience/library.qdoc
new file mode 100644
index 000000000..409af3f19
--- /dev/null
+++ b/doc/reference/items/convenience/library.qdoc
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \previouspage JavaJarFile
+ \nextpage LoadableModule
+ \qmltype Library
+ \since Qbs 1.4
+ \inherits Product
+ \inqmlmodule QbsConvenienceItems
+ \ingroup list-of-items
+ \keyword QML.NativeBinary
+
+ \brief Generic library.
+
+ A Library item is a base item for native libraries and can have \l{Product::}{type} set to
+ one of the following values: \c "dynamiclibrary", \c "staticlibrary", \c "loadablemodule".
+
+ The default \l{Product::}{type} value is \c "dynamiclibrary" except for iOS prior to
+ version 8, in which case the default value is \c "staticlibrary".
+
+ This item can automatically install the library target (and library symlinks on Unix) and
+ separated debug information.
+
+ For Android targets, the following applies:
+ \list
+ \li The \l{Product::type}{Product.type} property value contains
+ \c "android.nativelibrary" in addition to \c "dynamiclibrary".
+ \li There is a dependency on the \l{cpp} and \l{Android.ndk} modules.
+ \endlist
+*/
+
+/*!
+ \qmlproperty bool Library::install
+
+ If \c{true}, the library will be installed to \l{Library::installDir}{installDir}.
+
+ \defaultvalue \c false
+ \since Qbs 1.13
+*/
+
+/*!
+ \qmlproperty string Library::installDir
+
+ Where to install the library, if \l{Library::install}{install} is enabled. On Unix,
+ the symbolic links are also installed to this location.
+
+ The value is appended to \l{qbs::installPrefix}{qbs.installPrefix}
+ when constructing the actual installation directory.
+
+ \defaultvalue \c Library/Frameworks if the library is a \l{bundle::isBundle}{bundle},
+ otherwise \c bin for Windows and \c lib for Unix-like targets.
+ \since Qbs 1.13
+*/
+
+/*!
+ \qmlproperty bool Library::installDebugInformation
+
+ If \c{true}, the debug information will be installed to
+ \l{Library::debugInformationInstallDir}{debugInformationInstallDir}.
+
+ \defaultvalue \c false
+ \since Qbs 1.16
+ \sa{How do I separate and install debugging symbols?}
+*/
+
+/*!
+ \qmlproperty string Library::debugInformationInstallDir
+
+ Where to install the debug information if \l installDebugInformation is enabled.
+
+ The value is appended to \l{qbs::installPrefix}{qbs.installPrefix}
+ when constructing the actual installation directory.
+
+ \defaultvalue \l installDir.
+ \since Qbs 1.16
+
+ \sa{How do I separate and install debugging symbols?}
+*/
+
+/*!
+ \qmlproperty bool Library::installImportLib
+
+ If \c{true}, the import library will be installed to \l importLibInstallDir.
+ This property is only relevant for Windows targets.
+ Enable it if you want to create a development package.
+
+ \defaultvalue \c false
+ \since Qbs 1.13
+*/
+
+/*!
+ \qmlproperty string Library::importLibInstallDir
+
+ Where to install the import library, if \l installImportLib is enabled.
+
+ The value is appended to \l{qbs::installPrefix}{qbs.installPrefix}
+ when constructing the actual installation directory.
+
+ This property is only relevant for Windows targets.
+
+ \defaultvalue \c lib
+ \since Qbs 1.13
+*/
+
diff --git a/doc/reference/items/convenience/loadablemodule.qdoc b/doc/reference/items/convenience/loadablemodule.qdoc
index d02dbae1c..2921051fd 100644
--- a/doc/reference/items/convenience/loadablemodule.qdoc
+++ b/doc/reference/items/convenience/loadablemodule.qdoc
@@ -26,11 +26,10 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
- \previouspage JavaJarFile
+ \previouspage Library
\nextpage QtApplication
\qmltype LoadableModule
- \inherits Product
+ \inherits Library
\inqmlmodule QbsConvenienceItems
\ingroup list-of-items
\keyword QML.LoadableModule
diff --git a/doc/reference/items/convenience/qtapplication.qdoc b/doc/reference/items/convenience/qtapplication.qdoc
index 09640a8c1..0cef3080b 100644
--- a/doc/reference/items/convenience/qtapplication.qdoc
+++ b/doc/reference/items/convenience/qtapplication.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage LoadableModule
\nextpage QtGuiApplication
\qmltype QtApplication
@@ -42,7 +41,7 @@
\code
CppApplication {
- Depends { name: "Qt.core" } }
+ Depends { name: "Qt.core" }
}
\endcode
diff --git a/doc/reference/items/convenience/qtguiapplication.qdoc b/doc/reference/items/convenience/qtguiapplication.qdoc
index e69b9214c..62fb5cee7 100644
--- a/doc/reference/items/convenience/qtguiapplication.qdoc
+++ b/doc/reference/items/convenience/qtguiapplication.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage QtApplication
\nextpage StaticLibrary
\qmltype QtGuiApplication
diff --git a/doc/reference/items/convenience/staticlibrary.qdoc b/doc/reference/items/convenience/staticlibrary.qdoc
index cd459cf6e..c2ff1e63b 100644
--- a/doc/reference/items/convenience/staticlibrary.qdoc
+++ b/doc/reference/items/convenience/staticlibrary.qdoc
@@ -25,39 +25,16 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage QtGuiApplication
\nextpage XPCService
\qmltype StaticLibrary
- \inherits Product
+ \inherits Library
\inqmlmodule QbsConvenienceItems
\ingroup list-of-items
\keyword QML.StaticLibrary
\brief Static library.
- A StaticLibrary item is a \l{Product}{product} of the \l{Product::}{type}
+ A StaticLibrary item is a \l{Library}{library} of the \l{Product::}{type}
\c "staticlibrary".
*/
-
-/*!
- \qmlproperty bool StaticLibrary::install
-
- If \c{true}, the library will be installed to \l installDir.
-
- \defaultvalue \c false
- \since Qbs 1.13
-*/
-
-/*!
- \qmlproperty string StaticLibrary::installDir
-
- Where to install the library, if \l install is enabled.
-
- The value is appended to \l{qbs::installPrefix}{qbs.installPrefix}
- when constructing the actual installation directory.
-
- \defaultvalue \c Library/Frameworks if the library is a \l{bundle::isBundle}{bundle},
- \c lib otherwise.
- \since Qbs 1.13
-*/
diff --git a/doc/reference/items/convenience/xpcservice.qdoc b/doc/reference/items/convenience/xpcservice.qdoc
index f786cf9ae..682d161b9 100644
--- a/doc/reference/items/convenience/xpcservice.qdoc
+++ b/doc/reference/items/convenience/xpcservice.qdoc
@@ -26,7 +26,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-convenience-items.html
\previouspage StaticLibrary
\qmltype XPCService
\inherits Application
diff --git a/doc/reference/items/language/artifact.qdoc b/doc/reference/items/language/artifact.qdoc
index f64e2f0c9..6a6d80cd2 100644
--- a/doc/reference/items/language/artifact.qdoc
+++ b/doc/reference/items/language/artifact.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\nextpage Depends
\ingroup list-of-language-items
\qmltype Artifact
diff --git a/doc/reference/items/language/depends.qdoc b/doc/reference/items/language/depends.qdoc
index 8a3e23ba9..937828b8c 100644
--- a/doc/reference/items/language/depends.qdoc
+++ b/doc/reference/items/language/depends.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Artifact
\nextpage Export
\qmltype Depends
@@ -128,7 +127,19 @@
The \l required and \l profiles properties are ignored if this property is
set.
- Product types attached via Module::additionalProductTypes are not considered.
+ \note This property does not work recursively. Consider this example:
+ \code
+ Product {
+ name: "A"
+ type: "x"
+ Depends { productTypes: "someTag" }
+ }
+ Product {
+ name: "B"
+ Depends { productTypes: "x" }
+ }
+ \endcode
+ It is not guaranteed that \c A will show up in \c B's dependencies.
\nodefaultvalue
*/
@@ -186,12 +197,3 @@
\nodefaultvalue
*/
-
-/*!
- \qmlproperty bool Depends::enableFallback
-
- Whether to fall back to a pkg-config based \l{Module Providers}{module provider}
- if the dependency is not found.
-
- \defaultvalue \c true
-*/
diff --git a/doc/reference/items/language/export.qdoc b/doc/reference/items/language/export.qdoc
index d8ed4c33d..e3648fc3e 100644
--- a/doc/reference/items/language/export.qdoc
+++ b/doc/reference/items/language/export.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Depends
\nextpage FileTagger
\qmltype Export
@@ -45,8 +44,8 @@
name: "A"
Export {
Depends { name: "cpp" }
- cpp.includePaths: product.sourceDirectory
- cpp.defines: ["USING_" + product.name.toUpperCase()]
+ cpp.includePaths: exportingProduct.sourceDirectory
+ cpp.defines: ["USING_" + exportingProduct.name.toUpperCase()]
}
}
@@ -56,16 +55,45 @@
}
\endcode
- The sources in product B will be able to use headers from product A without specifiying
+ The sources in product B will be able to use headers from product A without specifying
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.
+ The dependent \l{Product}'s modules are not exported unless explicitly specified within the
+ \l{Export} item:
+ \code
+ Product {
+ name: "B-Exporting-A"
+ Depends { name: "A" }
+ Export {
+ Depends { name: "A" }
+ }
+ }
+ \endcode
+
+ The relationship of the exported dependencies is transitive. A product C depending on a
+ product B-Exporting-A will also get a direct dependency on A and thus inherit include
+ paths and preprocessor macros exported by A.
+
+ \code
+ Product {
+ name: "C"
+ Depends { name: "B-Exporting-A" }
+ }
+ \endcode
- 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.
+ Within an Export item, you can use the \c exportingProduct variable to refer to
+ the product which defines the Export item. Use the \c{importingProduct} variable
+ to refer to the product that pulls in the resulting module:
+ \code
+ Product {
+ name: "D"
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory, importingProduct.buildDirectory]
+ }
+ }
+ \endcode
*/
/*!
@@ -81,10 +109,10 @@
project against it), you would write something like the following:
\code
Export {
- Depends { name: cpp" }
- cpp.includePaths: [product.sourceDirectory]
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
prefixMapping: [{
- prefix: product.sourceDirectory,
+ prefix: exportingProduct.sourceDirectory,
replacement: FileInfo.joinPaths(qbs.installPrefix, "include")
}]
}
diff --git a/doc/reference/items/language/filetagger.qdoc b/doc/reference/items/language/filetagger.qdoc
index 1f01037b5..5b2b4cb1f 100644
--- a/doc/reference/items/language/filetagger.qdoc
+++ b/doc/reference/items/language/filetagger.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Export
\nextpage Group
\qmltype FileTagger
diff --git a/doc/reference/items/language/group.qdoc b/doc/reference/items/language/group.qdoc
index caeffcfaa..fa6784ca6 100644
--- a/doc/reference/items/language/group.qdoc
+++ b/doc/reference/items/language/group.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage FileTagger
\nextpage JobLimit
\qmltype Group
@@ -49,21 +48,21 @@
}
Group {
name: "Windows files"
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
files: "myclass_win_impl.cpp"
}
Group {
name: "Unix files"
- condition: qbs.targetOS.contains("unix")
+ condition: qbs.targetOS.includes("unix")
files: "unixhelper.cpp"
Group {
name: "Linux files"
- condition: qbs.targetOS.contains("linux")
+ condition: qbs.targetOS.includes("linux")
files: "myclass_linux_impl.cpp"
}
Group {
name: "FreeBSD files"
- condition: qbs.targetOS.contains("freebsd")
+ condition: qbs.targetOS.includes("freebsd")
files: "myclass_freebsd_impl.cpp"
}
}
@@ -75,10 +74,13 @@
}
}
\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
+ When specifying files, you can use the wildcards \c "*", \c "?" and \c "[]", which
+ have their
+ \l{https://en.wikipedia.org/wiki/Wildcard_character#File_and_directory_patterns}{usual meaning}
+ as in Unix Shell. 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 \c "**" 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
@@ -115,13 +117,15 @@
*/
/*!
- \qmlproperty list Group::files
+ \qmlproperty pathList Group::files
The files in the group. Mutually exclusive with \l{fileTagsFilter}.
Relative paths are resolved using the parent directory of the file
that contains the Group item. However, if the \l{prefix} property is set to
an absolute path, then that one becomes the base directory.
+ The values can contain wildcards.
+
\defaultvalue An empty list
*/
@@ -147,7 +151,7 @@
*/
/*!
- \qmlproperty list Group::fileTagsFilter
+ \qmlproperty stringList Group::fileTagsFilter
List of \l{Artifact::fileTags}{artifact.fileTags} to match. Any properties
set in this group will be applied to the product's artifacts whose file tags
@@ -171,7 +175,7 @@
*/
/*!
- \qmlproperty list Group::fileTags
+ \qmlproperty stringList Group::fileTags
A list of file tags to attach to the group's files. These can then be
matched by a \l{Rule}{rule}.
@@ -197,10 +201,13 @@
*/
/*!
- \qmlproperty list Group::excludeFiles
+ \qmlproperty pathList Group::excludeFiles
+
+ A list of files that are \e subtracted from the \l{files} list.
- A list of files that are \e subtracted from the files list. Useful when
- using wildcards.
+ The values can contain wildcards.
+
+ This property is ignored if \l{fileTagsFilter} is set.
\defaultvalue An empty list
*/
diff --git a/doc/reference/items/language/joblimit.qdoc b/doc/reference/items/language/joblimit.qdoc
index cd68bae31..5ac3689e0 100644
--- a/doc/reference/items/language/joblimit.qdoc
+++ b/doc/reference/items/language/joblimit.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Group
\nextpage Module
\qmltype JobLimit
diff --git a/doc/reference/items/language/module.qdoc b/doc/reference/items/language/module.qdoc
index 18706b4b8..48ad583c8 100644
--- a/doc/reference/items/language/module.qdoc
+++ b/doc/reference/items/language/module.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage JobLimit
\nextpage ModuleProvider
\qmltype Module
@@ -170,64 +169,15 @@
\qml
Module {
property bool featureEnabled:
- (product.type.contains("application")) ? true : false
+ (product.type.includes("application")) ? true : false
}
\endqml
- \section1 Special Property Values
-
- For every property defined in a module, \QBS provides the following special
- built-in values:
-
- \list
- \li \l base
- \li \l original
- \li \l outer
- \endlist
-
- \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 only valid in \l{Group} and \l{Properties} items:
- \code
- Product {
- Depends { name: "mymodule" }
- mymodule.someProperty: ["value1"]
- Group {
- name: "special files"
- files: ["somefile1", "somefile2"]
- mymodule.someProperty: outer.concat(["value"]) // => ["value1", "value2"]
- }
- }
- \endcode
+ \section2 Special Property Values
+ For every property defined in a module, \QBS provides the special
+ \l{Special Property Values#original}{original} value containing the value of the property in
+ the module itself (possibly overridden from a profile).
\section1 Dependency Parameters
diff --git a/doc/reference/items/language/moduleprovider.qdoc b/doc/reference/items/language/moduleprovider.qdoc
index ddbc25959..09cabb39b 100644
--- a/doc/reference/items/language/moduleprovider.qdoc
+++ b/doc/reference/items/language/moduleprovider.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Module
\nextpage Parameter
\qmltype ModuleProvider
@@ -41,6 +40,8 @@
The actual module creation is done on the right-hand side of the
\l{ModuleProvider::relativeSearchPaths}{relativeSearchPaths} property.
+ A ModuleProvider item may contain \l{Probe} items.
+
Here is a complete minimal example of a module provider. It just creates an empty module.
If you put this item into the file \c {module-providers/mymodule/provider.qbs}
in your project source directory, you will be able to successfully build a product which
@@ -66,19 +67,46 @@
*/
/*!
+ \qmlproperty bool ModuleProvider::isEager
+
+ Holds whether provider is eager.
+
+ Eager provider is executed only once and should create multiple modules at once when executed).
+ A non-eager provider is executed multiple times, one time for each module \QBS tries to
+ instantiate.
+
+ \sa ModuleProvider::moduleName
+ \default true
+*/
+
+/*!
\qmlproperty string ModuleProvider::name
The name of the module provider.
- This property is set by \QBS. For simple dependency names, it is the name of the dependency
- as specified in the \l Depends item. If the dependency name consists of multiple components,
- the value is the name up until (and including) the component that corresponds to the directory
- the provider was found in. For instance, if the dependency is \c {x.m1} and the provider was
- found in \c {module-providers/x/m1/provider.qbs}, then \c name is \c {x.m1}.
+ This property is set by \QBS.
+
+ If provider is \l{How \QBS Uses Module Providers}{requested} via the
+ \l{Product::qbsModuleProviders}{qbsModuleProviders} property, it is the name specified in this
+ property and matches the provider file name, without the \c .qbs extension.
+ Otherwise, it is the name of the directory the provider was found in, relative to
+ the particular module-providers base directory. For instance, if the dependency is \c {x.m1} and
+ the provider was found in \c {module-providers/x/m1/provider.qbs}, then \c name is \c {x.m1}.
If the provider was found in \c {module-providers/x/provider.qbs}, then \c name is \c x.
*/
/*!
+ \qmlproperty string ModuleProvider::moduleName
+
+ This property is set by QBS for non-eager providers and contains the name of the module
+ that is currently being instantiated by the provider.
+
+ For eager providers, the value of this property is \c undefined.
+
+ \sa ModuleProvider::isEager
+*/
+
+/*!
\qmlproperty string ModuleProvider::outputBaseDir
The path under which the new modules should be created when \l relativeSearchPaths
diff --git a/doc/reference/items/language/parameter.qdoc b/doc/reference/items/language/parameter.qdoc
index c544af2d7..9a1b0345b 100644
--- a/doc/reference/items/language/parameter.qdoc
+++ b/doc/reference/items/language/parameter.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Module
\nextpage Parameters
\qmltype Parameter
diff --git a/doc/reference/items/language/parameters.qdoc b/doc/reference/items/language/parameters.qdoc
index bbccb222c..fb7653528 100644
--- a/doc/reference/items/language/parameters.qdoc
+++ b/doc/reference/items/language/parameters.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Parameter
\nextpage Probe
\qmltype Parameters
@@ -33,10 +32,10 @@
\ingroup list-of-items
\keyword QML.Parameters
- \brief Defines default values for dependency parameters within Export items.
+ \brief Defines default values for dependency parameters within Module and Export items.
The Parameters item defines default values for dependency parameters within
- \l{Export} items.
+ \l{Module} and \l{Export} items.
Example:
\code
diff --git a/doc/reference/items/language/probe.qdoc b/doc/reference/items/language/probe.qdoc
index 4ebfae319..8e31d8827 100644
--- a/doc/reference/items/language/probe.qdoc
+++ b/doc/reference/items/language/probe.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Parameters
\nextpage Product
\qmltype Probe
@@ -36,7 +35,7 @@
\brief Locates files outside the project.
- Probe items can appear inside \l{Product} and \l{Project} items.
+ Probe items can appear inside \l{Product}, \l{Project} and \l{ModuleProvider} items.
They are run prior to building, for instance to locate dependent headers, libraries, and other
files outside the project directory whose locations are not known ahead of time.
Probes can be parameterized via their properties and typically store results in
diff --git a/doc/reference/items/language/product.qdoc b/doc/reference/items/language/product.qdoc
index 6b87cffc0..1ae29741c 100644
--- a/doc/reference/items/language/product.qdoc
+++ b/doc/reference/items/language/product.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Probe
\nextpage Profile
\qmltype Product
@@ -207,9 +206,13 @@
/*!
\qmlproperty stringList Product::qbsSearchPaths
+ A list of paths that are searched for imports, modules and module providers.
+
The value set here will be merged with the value of
\l{Project::qbsSearchPaths}{project.qbsSearchPaths}.
+ For the details about how to add custom items, see the \l{Custom Modules and Items} page.
+
\defaultvalue \l{Project::qbsSearchPaths}{project.qbsSearchPaths}
*/
@@ -280,3 +283,15 @@
The value of this property is automatically set by \QBS and cannot be
changed by the user.
*/
+
+/*!
+ \qmlproperty stringList Product::qbsModuleProviders
+
+ The list of \l{Module Providers} to use for this product.
+
+ Overrides \l{Project::qbsModuleProviders}{Project.qbsModuleProviders}.
+
+ \sa Project::qbsModuleProviders
+
+ \since Qbs 1.21
+*/
diff --git a/doc/reference/items/language/profile.qdoc b/doc/reference/items/language/profile.qdoc
index b0eccc803..3c41b312c 100644
--- a/doc/reference/items/language/profile.qdoc
+++ b/doc/reference/items/language/profile.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Product
\nextpage Project
\qmltype Profile
diff --git a/doc/reference/items/language/project.qdoc b/doc/reference/items/language/project.qdoc
index d2db644f6..b1dc6e9de 100644
--- a/doc/reference/items/language/project.qdoc
+++ b/doc/reference/items/language/project.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Profile
\nextpage Properties
\qmltype Project
@@ -92,11 +91,13 @@
/*!
\qmlproperty stringList Project::qbsSearchPaths
- A list of paths that 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
+ A list of paths that are searched for imports, modules and module providers 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.
+ For the details about how to add custom items, see the \l{Custom Modules and Items} page.
+
\defaultvalue An empty list
*/
@@ -124,3 +125,19 @@
\defaultvalue \c "1.3.0"
*/
+
+/*!
+ \qmlproperty stringList Project::qbsModuleProviders
+
+ The list of \l{Module Providers} to use for this project.
+
+ Providers contribute to the \l{Product::}{qbsSearchPaths} in the order specified here, so
+ modules generated by providers specified earlier are prioritized.
+ Currently, this order also corresponds with the actual execution order of the providers, but
+ this should not be relied upon.
+
+ \sa Product::qbsModuleProviders
+
+ \since Qbs 1.21
+*/
+
diff --git a/doc/reference/items/language/properties.qdoc b/doc/reference/items/language/properties.qdoc
index a4d0d46a3..3a71a035b 100644
--- a/doc/reference/items/language/properties.qdoc
+++ b/doc/reference/items/language/properties.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Project
\nextpage PropertyOptions
\qmltype Properties
@@ -45,7 +44,7 @@
\code
Product {
Properties {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
cpp.defines: ["ON_WINDOWS"]
cpp.includePaths: ["extraWindowsIncludes"]
}
@@ -58,12 +57,12 @@
\code
Product {
Properties {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
cpp.defines: ["ON_WINDOWS"]
cpp.includePaths: ["myWindowsIncludes"]
}
Properties {
- condition: qbs.targetOS.contains("linux")
+ condition: qbs.targetOS.includes("linux")
cpp.defines: ["ON_LINUX"]
cpp.includePaths: ["myLinuxIncludes"]
}
@@ -74,32 +73,32 @@
\code
Product {
cpp.defines: {
- if (qbs.targetOS.contains("windows"))
+ if (qbs.targetOS.includes("windows"))
return ["ON_WINDOWS"];
- if (qbs.targetOS.contains("linux"))
+ if (qbs.targetOS.includes("linux"))
return ["ON_LINUX"];
return ["ON_UNKNOWN_PLATFORM"];
}
cpp.includePaths: {
- if (qbs.targetOS.contains("windows"))
+ if (qbs.targetOS.includes("windows"))
return ["myWindowsIncludes"];
- if (qbs.targetOS.contains("linux"))
+ if (qbs.targetOS.includes("linux"))
return ["myLinuxIncludes"];
return base;
}
}
\endcode
- In Properties items, one can access the \l{Module#outer}{outer value} of a
+ In Properties items, one can access the \l{Special Property Values#outer}{outer} value of a
property.
\code
Product {
Properties {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
cpp.defines: outer.concat("ON_WINDOWS") // === ["FOO", "ON_WINDOWS"]
}
Properties {
- condition: qbs.targetOS.contains("linux")
+ condition: qbs.targetOS.includes("linux")
cpp.defines: ["ON_LINUX"] // === ["ON_LINUX"]
}
cpp.defines: ["FOO"]
diff --git a/doc/reference/items/language/propertyoptions.qdoc b/doc/reference/items/language/propertyoptions.qdoc
index a953489a0..afbba7104 100644
--- a/doc/reference/items/language/propertyoptions.qdoc
+++ b/doc/reference/items/language/propertyoptions.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Properties
\nextpage Rule
\inqmlmodule QbsLanguageItems
diff --git a/doc/reference/items/language/rule.qdoc b/doc/reference/items/language/rule.qdoc
index 62a32caf5..ec5865444 100644
--- a/doc/reference/items/language/rule.qdoc
+++ b/doc/reference/items/language/rule.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage PropertyOptions
\nextpage Scanner
\qmltype Rule
diff --git a/doc/reference/items/language/scanner.qdoc b/doc/reference/items/language/scanner.qdoc
index 9ec575684..d3758ef61 100644
--- a/doc/reference/items/language/scanner.qdoc
+++ b/doc/reference/items/language/scanner.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Rule
\nextpage SubProject
\qmltype Scanner
diff --git a/doc/reference/items/language/subproject.qdoc b/doc/reference/items/language/subproject.qdoc
index 91c879b55..d2bf018a0 100644
--- a/doc/reference/items/language/subproject.qdoc
+++ b/doc/reference/items/language/subproject.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage list-of-language-items.html
\previouspage Scanner
\qmltype SubProject
\inqmlmodule QbsLanguageItems
@@ -94,6 +93,20 @@
*/
/*!
+ \qmlproperty bool SubProject::condition
+
+ Whether the sub-project is added. If \c false, the sub-project is not
+ included.
+
+ Setting this property has the same effect as setting the \c{condition}
+ property within a \l{Properties} item. If both this property and the
+ \c{condition} property within a \l{Properties} item are defined, the
+ sub-project is included only if both properties evaluate to \c{true}.
+
+ \defaultvalue \c true
+*/
+
+/*!
\qmlproperty path SubProject::filePath
The file path of the project to add as a sub-project. If the top-level item
diff --git a/doc/reference/items/probe/binary-probe.qdoc b/doc/reference/items/probe/binary-probe.qdoc
index 930043829..919d0b89e 100644
--- a/doc/reference/items/probe/binary-probe.qdoc
+++ b/doc/reference/items/probe/binary-probe.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2019 Ivan Komissarov
+** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com)
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
@@ -26,9 +26,8 @@
****************************************************************************/
/*!
- \contentspage list-of-probes.html
\qmltype BinaryProbe
- \ingroup list-of-probes
+ \inqmlmodule QbsProbes
\ingroup list-of-items
\keyword QML.BinaryProbe
\inherits PathProbe
@@ -51,6 +50,7 @@
For example, BinaryProbe can be used to search for a protobuf compiler executable as follows:
\code
+ // Assuming module is called "myproto"
import qbs.File
import qbs.Probes
@@ -71,10 +71,10 @@
Rule {
// rule input/outputs here...
- // run executable
+ // run executable for the module called "myproto":
prepare: {
var args = // initialize arguments...
- var cmd = new Command(executableFilePath, args);
+ var cmd = new Command(input.myproto.executableFilePath, args);
cmd.highlight = "codegen";
cmd.description = "generating protobuf files for " + input.fileName;
return [cmd];
diff --git a/doc/reference/items/probe/conanfile-probe.qdoc b/doc/reference/items/probe/conanfile-probe.qdoc
new file mode 100644
index 000000000..cc91b9237
--- /dev/null
+++ b/doc/reference/items/probe/conanfile-probe.qdoc
@@ -0,0 +1,257 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Richard Weickelt
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype ConanfileProbe
+ \inqmlmodule QbsProbes
+ \ingroup list-of-items
+ \keyword QML.ConanfileProbe
+
+ \brief Extracts information about dependencies from a Conan recipe file.
+
+ The \c ConanfileProbe interfaces \QBS to the \l{https://conan.io/}{Conan
+ package manager}. It runs
+ \l{https://docs.conan.io/en/latest/reference/commands/consumer/install.html}{conan
+ install} on a Conan recipe file such as \c conanfile.py or \c conanfile.txt
+ and extracts all available meta information about package dependencies using
+ the \l{https://docs.conan.io/en/latest/reference/generators/json.html}{json
+ generator}. The output may be used to set up \l{Profile} items or module
+ properties in products. \c ConanfileProbe can also be used to run other
+ Conan generators and to retrieve their output.
+
+ \section1 Examples
+
+ In the following examples we assume that our project contains a \c conanfile.py.
+ This file describes all dependencies of our project. The dependency packages are
+ expected to export meta information to be consumed by our project.
+
+ \section2 Including Files Generated by Conan
+
+ Conan has a built-in
+ \l{https://docs.conan.io/en/latest/reference/generators/qbs.html}{qbs
+ generator} that creates a project file containing dummy products. This is
+ the easiest way to access dependencies, but also the least flexible one. It
+ requires each Conan package to export correct meta information and works only
+ if the dependency is a library.
+
+ \qml
+ import qbs.Probes
+
+ Project {
+ Probes.ConanfileProbe {
+ id: conan
+ conanfilePath: project.sourceDirectory + "/conanfile.py"
+ generators: "qbs"
+ }
+
+ references: conan.generatedFilesPath + "/conanbuildinfo.qbs"
+
+ CppApplication {
+ type: "application"
+ files: "main.cpp"
+ Depends { name: "mylib" }
+ }
+ }
+ \endqml
+
+ \section2 Setting Module Properties in Products
+
+ When a product depends on a Conan package that does not have a
+ dedicated \l{List of Modules}{module}, package meta information may be
+ directly fed into the \l{cpp} module.
+
+ This approach is very flexible.
+
+ \qml
+ import qbs.Probes
+
+ CppApplication {
+ Probes.ConanfileProbe {
+ id: conan
+ conanfilePath: product.sourceDirectory + "/conanfile.py"
+ options: ({opt1: "True"; opt2: "TheValue"})
+ }
+ cpp.includePaths: conan.dependencies["myLib"].include_paths
+ cpp.libraryPaths: conan.dependencies["myLib"].lib_paths
+ cpp.dynamicLibraries: conan.dependencies["mylib"].libs
+ }
+ \endqml
+
+ \section2 Setting Up a Profile
+
+ When multiple products depend on one or more Conan packages, the dependency
+ information may be combined in a \l{Profile}. This is especially useful when
+ \QBS modules are available for some of the packages, but some of their
+ properties need to be initialized. Otherwise, it would have to be done
+ manually in global profiles.
+
+ \qml
+ import qbs.Probes
+
+ Project {
+ Probes.ConanfileProbe {
+ id: conan
+ conanfilePath: project.sourceDirectory + "/conanfile.py"
+ }
+ Profile {
+ name: "arm-gcc"
+ cpp.toolchainInstallPath: conan.dependencies["arm-none-eabi-gcc"].rootpath + "/bin"
+ cpp.toolchainPrefix: "arm-linux-gnueabi-"
+ qbs.toolchainType: "gcc"
+ }
+ }
+ \endqml
+
+ This allows fully automated dependency management, including compiler
+ toolchains and is very useful when teams work in heterougeneous
+ environments.
+
+*/
+
+/*!
+ \qmlproperty stringList ConanfileProbe::additionalArguments
+
+ Additional command line arguments that are appended to the \c{conan install}
+ command.
+
+ \defaultvalue []
+*/
+
+/*!
+ \qmlproperty path ConanfileProbe::conanfilePath
+
+ Path to a \c conanfile.py or \c conanfile.txt that is used by this probe.
+
+ This property cannot be set at the same time as \l{ConanfileProbe::}{packageReference}.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty var ConanfileProbe::dependencies
+
+ This property contains the same information as
+ \l{ConanfileProbe::}{json}.dependencies, but instead of an array, \c
+ dependencies is a map with package names as keys for convenient access.
+
+ \readonly
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty path ConanfileProbe::executable
+
+ The name of or the path to the Conan executable.
+
+ \defaultvalue "conan.exe" on Windows, "conan" otherwise
+*/
+
+/*!
+ \qmlproperty path ConanfileProbe::generatedFilesPath
+
+ The path of the folder where Conan generators store their files. Each
+ instance of this probe creates a unique folder under
+ \l{Project::buildDirectory}{Project.buildDirectory}. The folder name is a
+ hash of the arguments supplied to \c{conan install}.
+
+ \readonly
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty stringList ConanfileProbe::generators
+
+ Conan generators to be executed by this probe. The
+ \l{https://docs.conan.io/en/latest/reference/generators/json.html}{JSON
+ generator} is always enabled. Generated files are written to the
+ \l{ConanfileProbe::generatedFilesPath}{generatedFilesPath} folder.
+
+ \sa {https://docs.conan.io/en/latest/reference/generators.html}{Available
+ generators}
+
+ \defaultvalue ["json"]
+*/
+
+/*!
+ \qmlproperty var ConanfileProbe::json
+
+ The parsed output of Conan's
+ \l{https://docs.conan.io/en/latest/reference/generators/json.html}{JSON
+ generator} as a JavaScript object.
+
+ \readonly
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty var ConanfileProbe::options
+
+ Options applied to \c{conan install} via the \c{-o} flag.
+ This property is an object in the form \c{key:value}.
+
+ Example:
+ \qml
+ options: ({someOpt: "True", someOtherOpt: "TheValue"})
+ \endqml
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty string ConanfileProbe::packageReference
+
+ Reference of a Conan package in the form \c{name/version@user/channel}.
+ Use this property if you want to probe an existing package in the local
+ cache or on a remote.
+
+ This property cannot be set at the same time as \l{ConanfileProbe::}{conanfilePath}.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty var ConanfileProbe::settings
+
+ Settings applied to \c{conan install} via the \c{-s} flag.
+ This property is an object in the form \c{key:value}.
+
+ Example:
+ \qml
+ settings: ({os: "Linux", compiler: "gcc"})
+ \endqml
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty var ConanfileProbe::verbose
+
+ Set to \c{true} to print all output of `conan install`.
+ If this property is \c{false} only error output will be printed.
+
+ \defaultvalue \c{false}
+*/
diff --git a/doc/reference/items/probe/dmc-probe.qdoc b/doc/reference/items/probe/dmc-probe.qdoc
new file mode 100644
index 000000000..36d99cf93
--- /dev/null
+++ b/doc/reference/items/probe/dmc-probe.qdoc
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype DmcProbe
+ \inqmlmodule QbsProbes
+ \ingroup list-of-items
+ \keyword QML.DmcProbe
+ \inherits PathProbe
+ \brief Collects Digital Mars toolchain compiler information.
+ \since Qbs 1.13
+ \internal
+
+ Detects the version, supported architecture, the platform
+ endianness, and other stuff for the specified compiler executable
+ from the \l{https://www.digitalmars.com/}{Digital Mars} toolchain.
+*/
+
+/*!
+ \qmlproperty string DmcProbe::compilerFilePath
+
+ An input property which is a full path to the Digital Mars compiler
+ executable.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty string DmcProbe::architecture
+
+ Detected architecture of the target platform's processor.
+
+ The possible values are \c "x86" and \c "x86_16".
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty string DmcProbe::targetPlatform
+
+ Detected target platform.
+
+ The possible values are \c "windows" and \c "dos".
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty int DmcProbe::versionMajor
+
+ Detected major compiler version.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty int DmcProbe::versionMinor
+
+ Detected minor compiler version.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty int DmcProbe::versionPatch
+
+ Detected patch compiler version.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty stringList DmcProbe::includePaths
+
+ Detected compiler include paths.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty var DmcProbe::compilerDefinesByLanguage
+
+ Detected set of compiler pre-defined macros depending
+ on the \c "C" or \c "C++" language.
+
+ \nodefaultvalue
+*/
diff --git a/doc/reference/items/probe/framework-probe.qdoc b/doc/reference/items/probe/framework-probe.qdoc
index 23ee57b3b..89986f48f 100644
--- a/doc/reference/items/probe/framework-probe.qdoc
+++ b/doc/reference/items/probe/framework-probe.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2019 Ivan Komissarov
+** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com)
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
@@ -26,9 +26,8 @@
****************************************************************************/
/*!
- \contentspage list-of-probes.html
\qmltype FrameworkProbe
- \ingroup list-of-probes
+ \inqmlmodule QbsProbes
\ingroup list-of-items
\keyword QML.FrameworkProbe
\inherits PathProbe
diff --git a/doc/reference/items/probe/iar-probe.qdoc b/doc/reference/items/probe/iar-probe.qdoc
index c300f82b7..387e70b89 100644
--- a/doc/reference/items/probe/iar-probe.qdoc
+++ b/doc/reference/items/probe/iar-probe.qdoc
@@ -25,9 +25,8 @@
**
****************************************************************************/
/*!
- \contentspage list-of-probes.html
\qmltype IarProbe
- \ingroup list-of-probes
+ \inqmlmodule QbsProbes
\ingroup list-of-items
\keyword QML.IarProbe
\inherits PathProbe
@@ -53,7 +52,10 @@
Detected architecture of the target platform's processor.
- The possible values are \c "arm", \c "mcs51" and \c "avr".
+ The possible values are \c "arm", \c "mcs51", \c "avr", \c "avr32",
+ \c "stm8", \c "riscv", \c "rl78", \c "rx", \c "rh850", \c "v850",
+ \c "78k", \c "sh", \c "r32c", \c "m32c", \c "m16c", \c "msp430",
+ \c "hcs12", \c "hcs8", \c "m68k", and \c "cr16".
\nodefaultvalue
*/
diff --git a/doc/reference/items/probe/include-probe.qdoc b/doc/reference/items/probe/include-probe.qdoc
index f84df3f5f..eb8fcf8b9 100644
--- a/doc/reference/items/probe/include-probe.qdoc
+++ b/doc/reference/items/probe/include-probe.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2019 Ivan Komissarov
+** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com)
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
@@ -26,9 +26,8 @@
****************************************************************************/
/*!
- \contentspage list-of-probes.html
\qmltype IncludeProbe
- \ingroup list-of-probes
+ \inqmlmodule QbsProbes
\ingroup list-of-items
\keyword QML.IncludeProbe
\inherits PathProbe
@@ -40,7 +39,6 @@
For example, IncludeProbe can be used to search for a zlib header as follows:
\code
- import qbs
import qbs.Probes
CppApplication {
diff --git a/doc/reference/items/probe/keil-probe.qdoc b/doc/reference/items/probe/keil-probe.qdoc
index f5aa07d83..b4b56e0aa 100644
--- a/doc/reference/items/probe/keil-probe.qdoc
+++ b/doc/reference/items/probe/keil-probe.qdoc
@@ -25,9 +25,8 @@
**
****************************************************************************/
/*!
- \contentspage list-of-probes.html
\qmltype KeilProbe
- \ingroup list-of-probes
+ \inqmlmodule QbsProbes
\ingroup list-of-items
\keyword QML.KeilProbe
\inherits PathProbe
@@ -53,7 +52,7 @@
Detected architecture of the target platform's processor.
- The possible values are \c "arm" and \c "mcs51".
+ The possible values are \c "arm", \c "mcs51", \c "mcs251" and \c "c166".
\nodefaultvalue
*/
diff --git a/doc/reference/items/probe/library-probe.qdoc b/doc/reference/items/probe/library-probe.qdoc
new file mode 100644
index 000000000..458e3679d
--- /dev/null
+++ b/doc/reference/items/probe/library-probe.qdoc
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype LibraryProbe
+ \inqmlmodule QbsProbes
+ \ingroup list-of-items
+ \keyword QML.IncludeProbe
+ \inherits PathProbe
+
+ \brief Locates library files outside the project.
+
+ On Windows, searches for library files within directories specified by the \c PATH environment
+ variable.
+
+ On Unix, searches for library files within directories specified by the \c LIBRARY_PATH
+ environment variable, as well as in \c "/usr/lib" and \c "/usr/local/lib".
+
+ On Linux, also searches in platform-specific directories, such as \c "/usr/lib64" and
+ \c "/usr/lib/x86_64-linux-gnu".
+
+ For example, LibraryProbe can be used to search for a \c zlib library as follows:
+
+ \code
+ import qbs.Probes
+
+ CppApplication {
+ Probes.LibraryProbe {
+ id: zlibProbe
+ names: "z"
+ }
+ cpp.libraryPaths: zlibProbe.found ? [zlibProbe.path] : []
+ cpp.dynamicLibraries: zlibProbe.found ? [zlibProbe.names] : []
+ files: 'main.cpp'
+ }
+ \endcode
+*/
diff --git a/doc/reference/items/probe/path-probe.qdoc b/doc/reference/items/probe/path-probe.qdoc
index eaaee9e54..729f11dde 100644
--- a/doc/reference/items/probe/path-probe.qdoc
+++ b/doc/reference/items/probe/path-probe.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 Ivan Komissarov
+** Copyright (C) 2018 Ivan Komissarov (abbapoh@gmail.com)
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
@@ -25,9 +25,8 @@
**
****************************************************************************/
/*!
- \contentspage list-of-probes.html
\qmltype PathProbe
- \ingroup list-of-probes
+ \inqmlmodule QbsProbes
\ingroup list-of-items
\keyword QML.PathProbe
@@ -37,10 +36,11 @@
locations.
PathProbe takes as input lists of paths to search files from and file name patterns. The paths
- are specified by using the searchPaths, platformSearchPaths, environmentPaths, and
- platformEnvironmentPaths properties. The file name patterns are specified by the \l names and
- nameSuffixes properties. Returns the first file that matches the file name patterns. If no such
- file is found, the \l {Probe::found}{probe.found} property is set to \c false.
+ are specified by using the environmentPaths, searchPaths, platformEnvironmentPaths and
+ platformSearchPaths properties; the path are searched in the same order as listed. The file
+ name patterns are specified by the \l names and nameSuffixes properties. Returns the first file
+ that matches the file name patterns. If no such file is found, the
+ \l {Probe::found}{probe.found} property is set to \c false.
For example, a simple PathProbe that searches for the stdio.h header can be used as follows:
@@ -81,7 +81,7 @@
For example, when searching for a library, it might be necessary to check its architecture:
\code
PathProbe {
- pathSuffixes: ["*.so", *.dll]
+ pathSuffixes: [".so", ".dll", ".dylib"]
candidateFilter: {
function getLibraryArchitecture(file) { ... }
return function(file) {
@@ -148,7 +148,7 @@
The list of environment variables that contains paths where to search files. Paths in the
environment variable should be separated using
- \l{qbs::pathListSeparator}{qbs.pathListSeparator}.
+ \l{pathListSeparator}{FileInfo.pathListSeparator()}.
\nodefaultvalue
*/
@@ -158,7 +158,7 @@
The list of platform environment variables that contains paths where to search files. Paths in
the environment variable should be separated using
- \l{qbs::pathListSeparator}{qbs.pathListSeparator}.
+ \l{pathListSeparator}{FileInfo.pathListSeparator()}.
\nodefaultvalue
*/
@@ -169,6 +169,24 @@
This property contains the result list of all paths that were checked before a file was found
(including the file names).
+ This property is set even if the Probe didn't find anything and can be used to give the user
+ a hint what went wrong:
+ \code
+ Module {
+ Probes.BinaryProbe {
+ id: pythonProbe
+ names: "python"
+ }
+ validate: {
+ if (!pythonProbe.found) {
+ throw ModUtils.ModuleError(
+ "Could not find python binary at any of the following locations:\n\t" +
+ pythonProbe.candidatePaths.join("\n\t"));
+ }
+ }
+ }
+ \endcode
+
\nodefaultvalue
*/
diff --git a/doc/reference/items/probe/pkgconfig-probe.qdoc b/doc/reference/items/probe/pkgconfig-probe.qdoc
index 7bdaf4e19..66e008601 100644
--- a/doc/reference/items/probe/pkgconfig-probe.qdoc
+++ b/doc/reference/items/probe/pkgconfig-probe.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2019 Ivan Komissarov
+** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com)
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
@@ -26,9 +26,8 @@
****************************************************************************/
/*!
- \contentspage list-of-probes.html
\qmltype PkgConfigProbe
- \ingroup list-of-probes
+ \inqmlmodule QbsProbes
\ingroup list-of-items
\keyword QML.PkgConfigProbe
@@ -52,8 +51,12 @@
maxVersion: '5.99.99'
}
files: 'main.cpp'
- cpp.cxxFlags: pkgConfig.cflags
- cpp.linkerFlags: pkgConfig.libs
+ cpp.defines : pkgConfig.defines
+ cpp.includePaths: pkgConfig.includePaths
+ cpp.dynamicLibraries: pkgConfig.libraries
+ cpp.libraryPaths: pkgConfig.libraryPaths
+ cpp.commonCompilerFlags: pkgConfig.compilerFlags
+ cpp.linkerFlags: pkgConfig.linkerFlags
}
\endcode
*/
@@ -111,7 +114,7 @@
\qmlproperty string PkgConfigProbe::exactVersion
The exact version of the required package. If set, pkg-config will ignore packages with
- version greater than the value of this property.
+ version that is not equal to the value of this property.
\nodefaultvalue
*/
@@ -120,7 +123,7 @@
\qmlproperty string PkgConfigProbe::maxVersion
The maximum version of the required package. If set, pkg-config will ignore packages with
- version that is not equal to the value of this property.
+ version greater than the value of this property.
\nodefaultvalue
*/
@@ -195,9 +198,6 @@
This output property contains the list of library paths that should be passed to a linker when
using requested package.
- This property sets the value of the PKG_CONFIG_LIBDIR environment variable passed to
- the \c pkg-config binary.
-
\nodefaultvalue
*/
diff --git a/doc/reference/items/probe/sdcc-probe.qdoc b/doc/reference/items/probe/sdcc-probe.qdoc
index 1353eb736..06cb06891 100644
--- a/doc/reference/items/probe/sdcc-probe.qdoc
+++ b/doc/reference/items/probe/sdcc-probe.qdoc
@@ -25,9 +25,8 @@
**
****************************************************************************/
/*!
- \contentspage list-of-probes.html
\qmltype SdccProbe
- \ingroup list-of-probes
+ \inqmlmodule QbsProbes
\ingroup list-of-items
\keyword QML.SdccProbe
\inherits PathProbe
@@ -53,7 +52,7 @@
Detected architecture of the target platform's processor.
- The possible values are \c "mcs51".
+ The possible values are \c "mcs51", \c "stm8", and \c "hcs8".
\nodefaultvalue
*/
diff --git a/doc/reference/items/probe/watcom-probe.qdoc b/doc/reference/items/probe/watcom-probe.qdoc
new file mode 100644
index 000000000..8f06be974
--- /dev/null
+++ b/doc/reference/items/probe/watcom-probe.qdoc
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com>
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype WatcomProbe
+ \inqmlmodule QbsProbes
+ \ingroup list-of-items
+ \keyword QML.WatcomProbe
+ \inherits PathProbe
+ \brief Collects Oopen Watcom toolchain compiler information.
+ \since Qbs 1.22
+ \internal
+
+ Detects the version, supported architecture, the platform
+ endianness, and other stuff for the specified compiler executable
+ from the \l{https://github.com/open-watcom}{Open Watcom} toolchain.
+*/
+
+/*!
+ \qmlproperty string WatcomProbe::compilerFilePath
+
+ An input property which is a full path to the Digital Mars compiler
+ executable.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty string WatcomProbe::architecture
+
+ Detected architecture of the target platform's processor.
+
+ The possible values are \c "x86" and \c "x86_16".
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty string WatcomProbe::endianness
+
+ Detected endianness of the target platform's processor architecture.
+
+ The possible values are \c "big" and \c "little".
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty string WatcomProbe::targetPlatform
+
+ Detected target platform.
+
+ The possible values are \c "windows", \c "linux", \c "os2" and \c "dos".
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty int WatcomProbe::versionMajor
+
+ Detected major compiler version.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty int WatcomProbe::versionMinor
+
+ Detected minor compiler version.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty int WatcomProbe::versionPatch
+
+ Detected patch compiler version.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty stringList WatcomProbe::includePaths
+
+ Detected compiler include paths.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty var WatcomProbe::compilerDefinesByLanguage
+
+ Detected set of compiler pre-defined macros depending
+ on the \c "C" or \c "C++" language.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty var WatcomProbe::environment
+
+ Detected compiler run environment.
+
+ \nodefaultvalue
+*/
diff --git a/doc/reference/jsextensions/jsextension-binaryfile.qdoc b/doc/reference/jsextensions/jsextension-binaryfile.qdoc
index 59def1af4..c8c3bd4b1 100644
--- a/doc/reference/jsextensions/jsextension-binaryfile.qdoc
+++ b/doc/reference/jsextensions/jsextension-binaryfile.qdoc
@@ -28,7 +28,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page jsextension-binaryfile.html
\ingroup list-of-builtin-services
diff --git a/doc/reference/jsextensions/jsextension-environment.qdoc b/doc/reference/jsextensions/jsextension-environment.qdoc
index 8579ecee5..465a157a2 100644
--- a/doc/reference/jsextensions/jsextension-environment.qdoc
+++ b/doc/reference/jsextensions/jsextension-environment.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page jsextension-environment.html
\ingroup list-of-builtin-services
diff --git a/doc/reference/jsextensions/jsextension-file.qdoc b/doc/reference/jsextensions/jsextension-file.qdoc
index c12fcc4ef..f04055593 100644
--- a/doc/reference/jsextensions/jsextension-file.qdoc
+++ b/doc/reference/jsextensions/jsextension-file.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page jsextension-file.html
\ingroup list-of-builtin-services
@@ -48,6 +47,9 @@
\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.
+ \note The file is not copied if the source file timestamp is older than the destination file
+ timestamp. If you want to replace the newer file, you need to remove it first via
+ File.remove().
\section2 exists
\code
diff --git a/doc/reference/jsextensions/jsextension-fileinfo.qdoc b/doc/reference/jsextensions/jsextension-fileinfo.qdoc
index 967606ea7..d58c1a9e9 100644
--- a/doc/reference/jsextensions/jsextension-fileinfo.qdoc
+++ b/doc/reference/jsextensions/jsextension-fileinfo.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page jsextension-fileinfo.html
\ingroup list-of-builtin-services
@@ -150,4 +149,26 @@
FileInfo.toWindowsSeparators(filePath: string): string
\endcode
Returns \c filePath with all '/' characters replaced by '\\'.
+
+ \section2 pathListSeparator
+ \code
+ FileInfo.pathListSeparator(): string
+ \endcode
+ Returns the host operating system separator for a path list that is used in environment
+ variables or other contexts.
+ \funsince 1.22
+
+ \section2 pathSeparator
+ \code
+ FileInfo.pathSeparator(): string
+ \endcode
+ Returns the host operating system path separator.
+ \funsince 1.22
+
+ \section2 executableSuffix
+ \code
+ FileInfo.executableSuffix(): string
+ \endcode
+ Returns the host operating system executable suffix.
+ \funsince 1.23
*/
diff --git a/doc/reference/jsextensions/jsextension-host.qdoc b/doc/reference/jsextensions/jsextension-host.qdoc
new file mode 100644
index 000000000..c011e90eb
--- /dev/null
+++ b/doc/reference/jsextensions/jsextension-host.qdoc
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 Raphaël Cotty <raphael.cotty@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page jsextension-host.html
+ \ingroup list-of-builtin-services
+
+ \title Host Service
+ \brief Provides host information.
+
+ The \c Host service offers various information about the host environment, such as operating
+ system names, platform name.
+
+ \section1 Available Operations
+
+ \section2 architecture
+ \code
+ Host.architecture(): string
+ \endcode
+ Returns the host operating system architecture.
+
+ See \l{qbs::architecture}{qbs.architecture} for the target-specific equivalent property.
+ architecture.
+ \funsince 1.22
+
+ \section2 os
+ \code
+ Host.os(): string[]
+ \endcode
+ Returns the host operating system names.
+
+ See the \l{qbs::targetOS}{qbs.targetOS} for the target-specific equivalent property.
+ \funsince 1.22
+
+ \section2 platform
+ \code
+ Host.platform(): string
+ \endcode
+ Returns the host operating system platform.
+
+ See the \l{qbs::targetPlatform}{qbs.targetPlatform} for the target-specific equivalent property.
+ \funsince 1.22
+
+
+ \section2 osVersion
+ \code
+ Host.osVersion(): string
+ \endcode
+ Returns the host operating system version. Currently, only defined for Windows and Apple
+ platforms.
+
+ Consists of two or three numbers separated by dots. For example, \c "10.9" or \c "6.3.9600".
+ \funsince 1.22
+
+ \section2 osBuildVersion
+ \code
+ Host.osBuildVersion(): string
+ \endcode
+ Returns the host operating system 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 \l{qbs::}{versionPatch}. On Apple platforms, this is a standard build number in the Apple
+ versioning scheme. For example, \c "13C64".
+ \funsince 1.22
+
+ \section2 osVersionParts
+ \code
+ Host.osVersionParts(): string[]
+ \endcode
+ Returns the host operating system version as a list.
+
+ For example, Windows 8.1 (version 6.3.9600) would correspond to a value of \c[6, 3, 9600].
+ \funsince 1.22
+
+ \section2 osVersionMajor
+ \code
+ Host.osVersionMajor(): number
+ \endcode
+ Returns the host operating system major version.
+ \funsince 1.22
+
+ \section2 osVersionMinor
+ \code
+ Host.osVersionMinor(): number
+ \endcode
+ Returns the host operating system minor version.
+ \funsince 1.22
+
+ \section2 osVersionPatch
+ \code
+ Host.osVersionPatch(): number
+ \endcode
+ Returns the host operating system patch level.
+ \funsince 1.22
+
+ \section2 nullDevice
+ \code
+ Host.nullDevice(): string[]
+ \endcode
+ Returns the platform-specific file path corresponding to the null device.
+ \funsince 1.22
+*/
diff --git a/doc/reference/jsextensions/jsextension-process.qdoc b/doc/reference/jsextensions/jsextension-process.qdoc
index dd942ecff..49b47f4b2 100644
--- a/doc/reference/jsextensions/jsextension-process.qdoc
+++ b/doc/reference/jsextensions/jsextension-process.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page jsextension-process.html
\ingroup list-of-builtin-services
diff --git a/doc/reference/jsextensions/jsextension-propertylist.qdoc b/doc/reference/jsextensions/jsextension-propertylist.qdoc
index 869873aec..e1dad6e38 100644
--- a/doc/reference/jsextensions/jsextension-propertylist.qdoc
+++ b/doc/reference/jsextensions/jsextension-propertylist.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page jsextension-propertylist.html
\ingroup list-of-builtin-services
diff --git a/doc/reference/jsextensions/jsextension-temporarydir.qdoc b/doc/reference/jsextensions/jsextension-temporarydir.qdoc
index 750653143..29f62725d 100644
--- a/doc/reference/jsextensions/jsextension-temporarydir.qdoc
+++ b/doc/reference/jsextensions/jsextension-temporarydir.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page jsextension-temporarydir.html
\ingroup list-of-builtin-services
diff --git a/doc/reference/jsextensions/jsextension-textfile.qdoc b/doc/reference/jsextensions/jsextension-textfile.qdoc
index 65758b3bd..b2cf2c729 100644
--- a/doc/reference/jsextensions/jsextension-textfile.qdoc
+++ b/doc/reference/jsextensions/jsextension-textfile.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page jsextension-textfile.html
\ingroup list-of-builtin-services
diff --git a/doc/reference/jsextensions/jsextension-utilities.qdoc b/doc/reference/jsextensions/jsextension-utilities.qdoc
index 793581999..686e17a99 100644
--- a/doc/reference/jsextensions/jsextension-utilities.qdoc
+++ b/doc/reference/jsextensions/jsextension-utilities.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page jsextension-utilities.html
\ingroup list-of-builtin-services
diff --git a/doc/reference/jsextensions/jsextension-xml.qdoc b/doc/reference/jsextensions/jsextension-xml.qdoc
index b5d3891ae..26e2e6de8 100644
--- a/doc/reference/jsextensions/jsextension-xml.qdoc
+++ b/doc/reference/jsextensions/jsextension-xml.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page jsextension-xml.html
\ingroup list-of-builtin-services
diff --git a/doc/reference/jsextensions/jsextensions-general.qdoc b/doc/reference/jsextensions/jsextensions-general.qdoc
index 383f7b2bf..495e90f39 100644
--- a/doc/reference/jsextensions/jsextensions-general.qdoc
+++ b/doc/reference/jsextensions/jsextensions-general.qdoc
@@ -26,7 +26,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page jsextensions-general.html
\ingroup list-of-builtin-services
@@ -58,12 +57,6 @@
\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
@@ -86,24 +79,6 @@
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
diff --git a/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc b/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc
new file mode 100644
index 000000000..a7f6fe6b7
--- /dev/null
+++ b/doc/reference/module-providers/qbspkgconfig-module-provider.qdoc
@@ -0,0 +1,133 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype qbspkgconfig
+ \inqmlmodule QbsModuleProviders
+ \since 1.20
+
+ \brief Module provider based on the qbspkg-config library.
+
+ \QBS uses a built-in parser of the \c{*.pc} files and does not require the presence of the
+ \c pkg-config tool in the system. However, if the \c pkg-config tool is present, \QBS will
+ use the same libDirs as the system pkg-config uses by default; otherwise, a built-in list of
+ paths is used.
+
+ In order to enable usage of this provider in your Product, set the
+ \l{Product::qbsModuleProviders}{qbsModuleProviders} property as shown in the example below:
+ \snippet ../examples/pkgconfig-provider/pkgconfig-provider.qbs 0
+*/
+
+/*!
+ \qmlproperty string qbspkgconfig::executableFilePath
+
+ The path to the \c {pkg-config} executable. If not set, the pkg-config from PATH is used.
+
+ \defaultvalue undefined
+*/
+
+/*!
+ \qmlproperty stringList qbspkgconfig::libDirs
+
+ Set this if you need to overwrite the default search directories.
+ \note You do not need to set this for cross-compilation in order to point
+ to the sysroot. \QBS does that for you.
+
+ This property is the equivalent of the \c{PKG_CONFIG_LIBDIR} variable
+ for the \c{pkg-config} tool.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty stringList qbspkgconfig::extraPaths
+
+ Set this if you need to add extra search directories.
+
+ This property is the equivalent of the \c{PKG_CONFIG_PATH} variable
+ for the \c{pkg-config} tool.
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty bool qbspkgconfig::staticMode
+
+ If this property is \c true, then \QBS will include "private" libs and dependencies of the
+ package. This property is the equivalent of the
+ \c{--static} option for the \c{pkg-config} tool.
+
+ Set this if your product is to be linked statically.
+
+ \defaultvalue \c false
+*/
+
+/*!
+ \qmlproperty bool qbspkgconfig::definePrefix
+
+ If this property is \c true, then \QBS will override the ${prefix} variable in the packages
+ with a value that is guessed based on the location of the .pc file.
+
+ This option corresponds to the \c --define-prefix / \c --dont-define-prefix command line
+ options of the \c pkg-config tool.
+
+ \defaultvalue \c true on Windows, \c false otherwise
+*/
+
+/*!
+ \qmlproperty stringList qbspkgconfig::executableNames
+
+ The names of the \c pkg-config executable to search for.
+
+ Note that since newer distributions use \l{http://pkgconf.org}{pkgconf} by default, it has
+ higher priority over \c pkg-config.
+
+ \defaultvalue \c{["pkgconf", "pkg-config"]}
+*/
+
+/*!
+ \qmlproperty path qbspkgconfig::sysroot
+
+ Set this property if you need to overwrite the default search sysroot path used by
+ \c pkg-config.
+
+ This can be useful if \c pkg-config files are located in the directory other than qbs.sysroot.
+ This is the case on macOS platform - all XCode profiles are sysrooted to the SDK
+ directory, but \c pkg-config is typically intalled using Brew and resides in the
+ \c /usr/local directory.
+
+ Setting this property to \c undefined or empty (\c "") value will use pkg-config's default
+ search paths:
+ \code
+ qbs resolve moduleProviders.qbspkgconfig.sysroot:undefined
+ \endcode
+
+ This property is the equivalent of the \c{PKG_CONFIG_SYSROOT_DIR} variable for the
+ \c{pkg-config} tool.
+
+ \defaultvalue \c "" on macOS, \c qbs.sysroot on other platforms
+*/
diff --git a/doc/reference/module-providers/qt-module-provider.qdoc b/doc/reference/module-providers/qt-module-provider.qdoc
new file mode 100644
index 000000000..cd77a0d12
--- /dev/null
+++ b/doc/reference/module-providers/qt-module-provider.qdoc
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype Qt
+ \inqmlmodule QbsModuleProviders
+
+ \brief Module provider that generates Qt modules.
+
+ Looking up a Qt installation happens via a \l{Module Providers}{module provider}.
+ By default, if a dependency to a Qt module is encountered, \QBS collects all Qt installations
+ it can find. This lookup happens by searching for \c qmake executables in the \c PATH
+ environment variable. Alternatively, you can explicitly tell \QBS which Qt
+ installations it should consider by setting the qmakeFilePaths
+ module provider \l{Parameterizing Module Providers}{property}. In that case,
+ the environment will be ignored. For instance, with the following Linux command line,
+ \QBS will build the project against a custom Qt instead of the standard one in \c{/usr/bin}:
+ \code
+ $ qbs moduleProviders.Qt.qmakeFilePaths:/opt/myqt/bin/qmake
+ \endcode
+ You can also set the module provider property in a profile. The simplest way to do
+ this is via the \l setup-qt tool. For examples of how to use this tool, see the
+ \l{Managing Qt Versions} section.
+
+ This provider is activated automatically when encountering a dependency on the Qt
+ module and the \l{Product::qbsModuleProviders}{qbsModuleProviders} property
+ is \c undefined:
+ \code
+ CppApplication {
+ Depends { name: "Qt.core" }
+ files: "main.cpp"
+ }
+ \endcode
+
+ Alternatively, you can activate this provider explicitly via the
+ \l{Product::qbsModuleProviders}{qbsModuleProviders} property:
+ \code
+ CppApplication {
+ Depends { name: "Qt.core" }
+ files: "main.cpp"
+ qbsModuleProviders: "Qt"
+ }
+ \endcode
+*/
+
+/*!
+ \qmlproperty stringList Qt::qmakeFilePaths
+
+ List of paths to \c qmake executables.
+
+ \defaultvalue undefined
+*/
diff --git a/doc/reference/modules/android-ndk-module.qdoc b/doc/reference/modules/android-ndk-module.qdoc
index e3a7ca22d..d3b16e2bf 100644
--- a/doc/reference/modules/android-ndk-module.qdoc
+++ b/doc/reference/modules/android-ndk-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Android.ndk
\inqmlmodule QbsModules
\since Qbs 1.4
@@ -79,6 +78,17 @@
*/
/*!
+ \qmlproperty string Android.ndk::buildId
+
+ Value to pass to the --build-id linker flag.
+ Plain --build-id option is used when buildId property is empty.
+
+ \since Qbs 1.21
+
+ \defaultvalue \c "sha1"
+*/
+
+/*!
\qmlproperty string Android.ndk::appStl
The library to use for C++. The possible values are:
diff --git a/doc/reference/modules/android-sdk-module.qdoc b/doc/reference/modules/android-sdk-module.qdoc
index 7733ae75d..ce27b6302 100644
--- a/doc/reference/modules/android-sdk-module.qdoc
+++ b/doc/reference/modules/android-sdk-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Android.sdk
\inqmlmodule QbsModules
\since Qbs 1.4
@@ -119,7 +118,7 @@
*/
/*!
- \qmlproperty string Android.sdk::assetsDir
+ \qmlproperty path Android.sdk::assetsDir
The base directory for Android assets in the respective product.
@@ -201,7 +200,7 @@
*/
/*!
- \qmlproperty string Android.sdk::resourcesDir
+ \qmlproperty path Android.sdk::resourcesDir
The base directory for Android resources in the respective product.
@@ -233,3 +232,49 @@
\qmlproperty stringList Android.sdk::aidlSearchPaths
Search paths for import statements to pass to the \c aidl tool via the \c{-I} option.
*/
+
+/*!
+ \qmlproperty string Android.sdk::aaptName
+ \since Qbs 1.17
+
+ Name of the aapt binary. Allowed options: "aapt" and "aapt2".
+
+ \defaultvalue \c "aapt2"
+*/
+
+/*!
+ \qmlproperty string Android.sdk::packageType
+ \since Qbs 1.17
+
+ Type of the package. Allowed options: "apk" and "aab".
+ Type "apk" generates a runnable package whereas "aab" generates a package for Google Play.
+
+ \defaultvalue \c "apk"
+*/
+
+/*!
+ \qmlproperty string Android.sdk::dexCompilerName
+ \since Qbs 1.20
+
+ Name of the dex compiler binary. Allowed options: "dx" and "d8".
+
+ \defaultvalue \c "d8"
+*/
+
+/*!
+ \qmlproperty string Android.sdk::minimumVersion
+ \since Qbs 1.17
+
+ Minimum API Level required for the application to run.
+
+ \defaultvalue \c "21"
+*/
+
+/*!
+ \qmlproperty string Android.sdk::targetVersion
+ \since Qbs 1.17
+
+ API Level that the application targets.
+
+ \defaultvalue \c platformVersion
+*/
diff --git a/doc/reference/modules/archiver-module.qdoc b/doc/reference/modules/archiver-module.qdoc
index 8bec4993d..9a6c59ba3 100644
--- a/doc/reference/modules/archiver-module.qdoc
+++ b/doc/reference/modules/archiver-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype archiver
\inqmlmodule QbsModules
\since Qbs 1.4
diff --git a/doc/reference/modules/asan-module.qdoc b/doc/reference/modules/asan-module.qdoc
new file mode 100644
index 000000000..6f6ee66c0
--- /dev/null
+++ b/doc/reference/modules/asan-module.qdoc
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype Sanitizers.address
+ \inqmlmodule QbsModules
+ \brief Provides AddressSanitizer support.
+
+ Provides support for the \l{https://clang.llvm.org/docs/AddressSanitizer.html}{AddressSanitizer}
+ tool, which uses compiler instrumentation to detect memory corruption bugs at runtime.
+*/
+
+
+/*!
+ \qmlproperty string Sanitizers.address::detectUseAfterReturn
+
+ Controls whether support for detecting problems with stack use after returning
+ from a function should be built into the application.
+
+ The possible values are:
+ \list
+ \li \c "always": Check for this type of problem unconditionally.
+ \li \c "runtime": Build with this capability, but only do the respective checks if they
+ are explicitly requested at runtime.
+ \li \c "never": Do not build with support for this check.
+ \endlist
+
+ \note Only the clang compiler (starting at 13.0) supports the full set of values.
+ With GCC and MSVC, "runtime" maps to "always".
+
+ \defaultvalue \c "always"
+*/
+
+/*!
+ \qmlproperty bool Sanitizers.address::detectUseAfterScope
+
+ Controls whether to detect bugs relating the use of variables after they've gone out of scope.
+
+ \note This property has no effect when building with with GCC and MSVC.
+
+ \defaultvalue \c true
+*/
+
+/*!
+ \qmlproperty bool Sanitizers.address::enabled
+
+ Controls whether to enable AddressSanitizer.
+
+ \note You do not need to check if the current toolchain supports AddressSanitizer:
+ If \QBS detects that it doesn't, this property has no effect.
+
+ \defaultvalue \c true
+*/
diff --git a/doc/reference/modules/autotest-module.qdoc b/doc/reference/modules/autotest-module.qdoc
index ddd9e0078..730f2c3bf 100644
--- a/doc/reference/modules/autotest-module.qdoc
+++ b/doc/reference/modules/autotest-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype autotest
\inqmlmodule QbsModules
\since Qbs 1.13
@@ -67,3 +66,14 @@
\nodefaultvalue
*/
+/*!
+ \qmlproperty int autotest::timeout
+
+ The time limit for the execution of the autotest. If not specified, the
+ \l{AutotestRunner::timeout}{timeout} property of the \l AutotestRunner
+ that invokes the autotest is used.
+
+ \nodefaultvalue
+ \since Qbs 1.15
+*/
+
diff --git a/doc/reference/modules/bundle-module.qdoc b/doc/reference/modules/bundle-module.qdoc
index ec0e1083d..fe9bf1354 100644
--- a/doc/reference/modules/bundle-module.qdoc
+++ b/doc/reference/modules/bundle-module.qdoc
@@ -27,7 +27,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype bundle
\inqmlmodule QbsModules
\since Qbs 1.4
@@ -97,8 +96,9 @@
A prefix for the product's bundle identifier. If \l{bundle::}{identifier}
is left unset, the bundle identifier will be a concatenation of this value
- and the \l{bundle::}{identifier} property, separated by a period (.). This
- corresponds to the organization identifier in Xcode.
+ and the \l{Product::targetName}{Product.targetName} property formatted as
+ an RFC-1034 identifier, separated by a period (.). This corresponds to the
+ organization identifier in Xcode.
\defaultvalue \c{org.example}
*/
@@ -107,8 +107,9 @@
\qmlproperty string bundle::identifier
The bundle's identifier. If left unset, the bundle identifier will be a
- concatenation of this value and the \l{bundle::}{identifierPrefix} property,
- separated by a period (.).
+ concatenation of the \l{bundle::}{identifierPrefix} value and the
+ \l{Product::targetName}{Product.targetName} propertyy formatted as an
+ RFC-1034 identifier, separated by a period (.).
\defaultvalue A combination of \l{bundle::}{identifierPrefix} and the
product's target name formatted as an RFC-1034 identifier.
@@ -205,7 +206,7 @@
*/
/*!
- \qmlproperty bool bundle::resources
+ \qmlproperty pathList bundle::resources
A list of resources to copy to a bundle's \c Resources subdirectory. Files
will automatically be copied into \c lproj subdirectories corresponding to
@@ -464,6 +465,17 @@
*/
/*!
+ \qmlproperty bool bundle::useBuiltinXcodeBuildSpecs
+
+ Set this property to \c true to use Xcode \c .xcspec files shipped with \QBS.
+
+ This property can be used as a workaround when using a new Xcode version that is not supported
+ by \QBS yet.
+
+ \default false
+*/
+
+/*!
\qmlproperty string bundle::versionsFolderPath
\readonly
\since Qbs 1.5
diff --git a/doc/reference/modules/capnprotocpp-module.qdoc b/doc/reference/modules/capnprotocpp-module.qdoc
new file mode 100644
index 000000000..7635edd9e
--- /dev/null
+++ b/doc/reference/modules/capnprotocpp-module.qdoc
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype capnproto.cpp
+ \inqmlmodule QbsModules
+ \since Qbs 1.17
+
+ \brief Provides support for Cap'n Proto for the C++ language.
+
+ The \c capnproto.cpp module provides support for generating C++ headers
+ and sources from proto definition files using the \c capnpc tool.
+
+ A simple qbs file that uses Cap'n Proto can be written as follows:
+ \code
+ CppApplication {
+ Depends { name: "capnproto.cpp" }
+ files: ["foo.capnp", "main.cpp"]
+ }
+ \endcode
+ A generated header now can be included in the C++ sources:
+ \code
+ #include <foo.capnp.h>
+
+ int main(int argc, char* argv[]) {
+ ::capnp::MallocMessageBuilder message;
+
+ auto foo = message.initRoot<Foo>();
+ foo.setAnswer(42);
+ return 0;
+ }
+ \endcode
+
+ \section2 Relevant File Tags
+
+ \table
+ \header
+ \li Tag
+ \li Auto-tagged File Names
+ \li Since
+ \li Description
+ \row
+ \li \c{"capnproto.input"}
+ \li \c{*.capnp}
+ \li 1.17.0
+ \li Source files with this tag are considered inputs to the \c capnpc compiler.
+ \endtable
+
+ \section2 Dependencies
+ This module depends on the \c capnp module and on the \c capnp-rpc module if
+ \l{capnproto.cpp::useRpc}{useRpc} property is \c true. These modules are created by the
+ \l{Module Providers} via the \c pkg-config tool.
+*/
+
+/*!
+ \qmlproperty string capnproto.cpp::compilerName
+
+ The name of the capnp binary.
+
+ \defaultvalue \c "capnpc"
+*/
+
+/*!
+ \qmlproperty string capnproto.cpp::compilerPath
+
+ The path to the protoc binary.
+
+ Use this property to override the auto-detected location.
+
+ \defaultvalue \c auto-detected
+*/
+
+/*!
+ \qmlproperty pathList capnproto.cpp::importPaths
+
+ The list of import paths that are passed to the \c capnpc tool via the \c --import-path option.
+
+ \defaultvalue \c []
+*/
+
+/*!
+ \qmlproperty string capnproto.cpp::outputDir
+
+ The directory where the \c capnpc compiler generated files are placed.
+
+ \defaultvalue \c product.buildDirectory + "/capnp"
+*/
+
+/*!
+ \qmlproperty bool capnproto.cpp::useRpc
+
+ Use this property to enable support for the RPC framework.
+
+ A simple qbs file that uses rpc can be written as follows:
+
+ \quotefile ../examples/capnproto/calculator_cpp/calculator_cpp.qbs
+
+ \defaultvalue \c false
+*/
diff --git a/doc/reference/modules/codesign-module.qdoc b/doc/reference/modules/codesign-module.qdoc
new file mode 100644
index 000000000..45e247a6a
--- /dev/null
+++ b/doc/reference/modules/codesign-module.qdoc
@@ -0,0 +1,395 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype codesign
+ \inqmlmodule QbsModules
+ \since Qbs 1.19
+
+ \brief Provides code signing support.
+
+ The \c codesign module contains properties and rules for code signing on Apple and Android
+ platforms.
+
+ \section2 Relevant File Tags
+
+ \table
+ \header
+ \li Tag
+ \li Auto-tagged File Names
+ \li Since
+ \li Description
+ \row
+ \li \c{"codesign.entitlements"}
+ \li \c{*.entitlements}
+ \li 1.19.0
+ \li \l{https://developer.apple.com/documentation/bundleresources/entitlements}{Xcode entitlements}
+ \row
+ \li \c{"codesign.provisioningprofile"}
+ \li \c{*.mobileprovision, *.provisionprofile}
+ \li 1.19.0
+ \li Xcode provisioning profiles
+ \row
+ \li \c{"codesign.signed_artifact"}
+ \li n/a
+ \li 1.19.0
+ \li This tag is attached to all signed artifacts such as applications or libraries
+ \endtable
+*/
+
+/*!
+ \qmlproperty string codesign::signingTimestamp
+
+ URL of the timestamp authority RFC 3161 server to be contacted to authenticate code signing.
+ \c undefined or \c empty indicates that a system-specific default should be used;
+ \c{"none"} explicitly disables the use of timestamp services on Apple platforms.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "none" on Apple, \c undefined otherwise
+*/
+
+/*!
+ \qmlproperty stringList codesign::codesignFlags
+
+ Additional flags passed to the \c{codesign} tool.
+
+ \since Qbs 1.19
+
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty string codesign::codesignName
+
+ The name of the \c{codesign} binary.
+
+ \since Qbs 1.19
+
+ \defaultvalue Determined automatically
+*/
+
+/*!
+ \qmlproperty string codesign::codesignPath
+
+ Path to the \c{codesign} tool.
+
+ \since Qbs 1.19
+
+ \defaultvalue Determined automatically
+*/
+
+/*!
+ \qmlproperty bool codesign::enableCodeSigning
+
+ Whether to actually perform code signing.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c false
+*/
+
+/*!
+ \qmlproperty string codesign::provisioningProfile
+
+ Name or UUID of the provisioning profile to embed in the product.
+ Typically this should be left blank to allow \QBS to use automatic provisioning.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c undefined
+
+ \appleproperty
+*/
+
+/*!
+ \qmlproperty path codesign::provisioningProfilesPath
+
+ Path to directory containing provisioning profiles installed on the system.
+ This should not normally need to be changed.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c{"~/Library/MobileDevice/Provisioning Profiles"}
+
+ \appleproperty
+*/
+
+/*!
+ \qmlproperty string codesign::signingIdentity
+
+ Search string used to find the certificate to sign the product. This does not have to be
+ a full certificate name like "Mac Developer: John Doe (XXXXXXXXXX)", and can instead be
+ a partial string like "Mac Developer" or the certificate's SHA1 fingerprint.
+ The search string should generally be one of the following:
+ \list
+ \li 3rd Party Mac Developer Application
+ \li 3rd Party Mac Developer Installer
+ \li Developer ID Application
+ \li Developer ID Installer
+ \li iPhone Developer
+ \li iPhone Distribution
+ \li Mac Developer
+ \endlist
+
+ It is also possible to use the special \c "-" value to use the ad-hoc signing.
+
+ See \l{https://developer.apple.com/library/mac/documentation/IDEs/Conceptual/AppDistributionGuide/MaintainingCertificates/MaintainingCertificates.html#//apple_ref/doc/uid/TP40012582-CH31-SW41}{Maintaining Your Signing Identities and Certificates}
+ for complete documentation on the existing certificate types.
+ In general you should use \l{codesign::signingType}{signingType} instead.
+
+ \since Qbs 1.19
+
+ \defaultvalue Determined by \l{codesign::signingType}{signingType}
+
+ \appleproperty
+*/
+
+/*!
+ \qmlproperty string codesign::signingType
+
+ Type of code signing to use. This should generally be used in preference to an explicit
+ signing identity like "Mac Developer: John Doe (XXXXXXXXXX)" since it is not user
+ specific and can be set in a project file.
+ Possible values include: \c{"app-store"}, \c{"apple-id"}, \c{"ad-hoc"}, which sign for
+ the App Store or Mac App Store, Developer ID, and Ad-hoc code signing, respectively.
+
+ \section1 Relation between the signingType and signingIdentity
+
+ The following table shows how the signingIdentity's default value is calculated.
+
+ \table
+ \header
+ \li \c qbs.targetOS
+ \li \c codesign.signingType
+ \li \c qbs.buildVariant
+ \li \c codesign.signingIdentity
+ \row
+ \li {1, 4} \c "macos"
+ \li \c "ad-hoc"
+ \li any
+ \li \c "-"
+ \row
+ \li {1, 2} \c "app-store"
+ \li \c "debug", \c "profiling"
+ \li \c "Mac Developer"
+ \row
+ \li \c "release"
+ \li \c "3rd Party Mac Developer Application"
+ \row
+ \li \c "apple-id"
+ \li any
+ \li \c "Developer ID Application"
+ \row
+ \li {1, 2} \c "ios", \c "tvos", \c "watchos"
+ \li {1, 2} \c "app-store"
+ \li \c "debug", \c "profiling"
+ \li \c "iPhone Developer"
+ \row
+ \li \c "release"
+ \li \c "iPhone Distribution"
+ \endtable
+
+ \since Qbs 1.19
+
+ \defaultvalue Determined automatically
+
+ \appleproperty
+*/
+
+/*!
+ \qmlproperty string codesign::teamIdentifier
+
+ Human readable name or 10-digit identifier of the Apple development team that the
+ signing identity belongs to. This is used to disambiguate between multiple certificates
+ of the same type in different teams. Typically this can be left blank if the development
+ machine is only signed in to a single development team, and should be set in a profile
+ otherwise.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c undefined
+
+ \appleproperty
+*/
+
+/*!
+ \qmlproperty bool codesign::useApksigner
+
+ If \c true, the package is signed using apksignerFilePath binary.
+ Set this property to \c false to use the jarsignerFilePath one.
+ Set by the Android.sdk module.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c true
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keystorePath
+
+ The absolute path to the keystore file.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "${HOME}/.android/debug.keystore"
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keystorePassword
+
+ The keystore password.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "android"
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keyPassword
+
+ The key password.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "android"
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keyAlias
+
+ The key alias.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "androiddebugkey"
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::subjectName
+
+ Specifies the name of the subject of the signing certificate.
+ This value can be a substring of the entire subject name.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c undefined
+
+ \windowsproperty
+*/
+
+/*!
+ \qmlproperty string codesign::rootSubjectName
+
+ Specifies the name of the subject of the root certificate that
+ the signing certificate must chain to. This value may be a substring
+ of the entire subject name of the root certificate.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c undefined
+
+ \windowsproperty
+*/
+
+/*!
+ \qmlproperty string codesign::hashAlgorithm
+
+ Specifies the default hash algorithm used on the signing certificate.
+ The possible values are \c sha1, \c sha256, \c sha384, and \c sha512.
+
+ \note Only available in Windows 10 kit builds 20236 and later.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c undefined
+
+ \windowsproperty
+*/
+
+/*!
+ \qmlproperty string codesign::timestampAlgorithm
+
+ Specifies the default timestamp algorithm used together with the
+ \c signingTimestamp property. The possible values are \c sha1, \c sha256.
+
+ \note If this value is not set, then the default sha1 algorithm
+ will be used.
+
+ \since Qbs 1.19.2
+
+ \defaultvalue \c undefined
+
+ \windowsproperty
+*/
+
+/*!
+ \qmlproperty string codesign::certificatePath
+
+ Specifies the full path to the signing certificate file (*.pfx).
+
+ \since Qbs 1.19
+
+ \defaultvalue \c undefined
+
+ \windowsproperty
+*/
+
+/*!
+ \qmlproperty string codesign::certificatePassword
+
+ Specifies the password to use when opening a signing certificate file (*.pfx).
+
+ \since Qbs 1.19
+
+ \defaultvalue \c undefined
+
+ \windowsproperty
+*/
+
+/*!
+ \qmlproperty string codesign::crossCertificatePath
+
+ Specifies the full path to the additional certificate file (*.cer).
+
+ \since Qbs 1.19
+
+ \defaultvalue \c undefined
+
+ \windowsproperty
+*/
diff --git a/doc/reference/modules/cpp-module.qdoc b/doc/reference/modules/cpp-module.qdoc
index 19575a0f8..2b344aac2 100644
--- a/doc/reference/modules/cpp-module.qdoc
+++ b/doc/reference/modules/cpp-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype cpp
\inqmlmodule QbsModules
\since Qbs 1.0
@@ -81,6 +80,7 @@
May contain the values: "weak", "lazy", "reexport", and "upward"; refer to the Apple
ld64 man page for full details. \c{undefined} uses normal linking.
Currently only applies when linking for Apple platforms.
+ Note that \c "lazy" mode is deprecated and doesn't work with Xcode 15 and above.
\endtable
\section2 Relevant File Tags
@@ -144,6 +144,14 @@
and Objective-C++, respectively. There can be only one such file per product and
language.
\row
+ \li \c{"def"}
+ \li -
+ \li 1.17.0
+ \li This tag is used for a module-definition file (.def) to be passed to the linker.
+ Such a file provides the linker with information about exports, attributes,
+ and other information about the program to be linked. This file tag only has
+ an effect with the MSVC toolchain.
+ \row
\li \c{"dynamiclibrary"}
\li n/a
\li 1.0.1
@@ -357,6 +365,15 @@
*/
/*!
+ \qmlproperty bool cpp::createSymlinks
+ \unixproperty
+
+ Whether to create version alias symlinks when building a dynamic library.
+
+ \defaultvalue \c true
+*/
+
+/*!
\qmlproperty bool cpp::discardUnusedData
\since Qbs 1.10
@@ -390,6 +407,8 @@
\defaultvalue \c{true} for MSVC and with GCC or Clang on Apple platforms,
otherwise \c{false}.
+
+ \sa debugInformation, {How do I separate and install debugging symbols?}
*/
/*!
@@ -506,6 +525,132 @@
*/
/*!
+ \qmlproperty string cpp::executablePrefix
+
+ A string to prepend to the executable file \l{Product::targetName}{name}.
+
+ \defaultvalue \c ""
+*/
+
+/*!
+ \qmlproperty string cpp::dynamicLibraryPrefix
+
+ A string to prepend to the dynamic library file \l{Product::targetName}{name}.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical values are \c "" or \c "lib"
+*/
+
+/*!
+ \qmlproperty string cpp::loadableModulePrefix
+ \appleproperty
+
+ A string to prepend to the Darwin loadable module file \l{Product::targetName}{name}.
+
+ \defaultvalue \c ""
+*/
+
+/*!
+ \qmlproperty string cpp::staticLibraryPrefix
+
+ A string to prepend to the static library file \l{Product::targetName}{name}.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical values are \c "" or \c "lib"
+*/
+
+/*!
+ \qmlproperty string cpp::executableSuffix
+
+ A string to append to the executable file \l{Product::targetName}{name}.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical values are \c "" or \c ".exe"
+*/
+
+/*!
+ \qmlproperty string cpp::dynamicLibrarySuffix
+
+ A string to append to the dynamic library file \l{Product::targetName}{name}.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical values are \c ".so", \c ".dll"
+ or \c "dylib"
+*/
+
+/*!
+ \qmlproperty string cpp::dynamicLibraryImportSuffix
+ \windowsproperty
+
+ A string to append to the dynamic library import file \l{Product::targetName}{name}.
+
+ \defaultvalue \c ".lib"
+*/
+
+/*!
+ \qmlproperty string cpp::loadableModuleSuffix
+ \appleproperty
+
+ A string to append to the Darwin loadable module file \l{Product::targetName}{name}.
+
+ \defaultvalue \c ".bundle"
+*/
+
+/*!
+ \qmlproperty string cpp::staticLibrarySuffix
+
+ A string to append to the executable file \l{Product::targetName}{name}.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical values are \c ".a" or \c ".lib"
+*/
+
+/*!
+ \qmlproperty string cpp::debugInfoSuffix
+
+ A string to append to the debug information file name.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical values are \c ".debug",
+ \c ".pdb" or \c ".dwarf"
+*/
+
+/*!
+ \qmlproperty string cpp::debugInfoBundleSuffix
+ \appleproperty
+
+ A string to append to the debug information bundle name.
+
+ \defaultvalue \c ".dSYM"
+*/
+
+/*!
+ \qmlproperty string cpp::objectSuffix
+
+ A string to append to the generated object files.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical values are \c ".o" or \c ".obj"
+*/
+
+/*!
+ \qmlproperty string cpp::linkerMapSuffix
+
+ A string to append to the generated linker memory map files.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical value is \c ".map"
+*/
+
+/*!
+ \qmlproperty string cpp::compilerListingSuffix
+
+ A string to append to the generated compiler listing files.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical value is \c ".lst"
+*/
+
+/*!
+ \qmlproperty string cpp::assemblerListingSuffix
+
+ A string to append to the generated assembler listing files.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical value is \c ".lst"
+*/
+
+/*!
\qmlproperty pathList cpp::prefixHeaders
\since Qbs 1.0.1
@@ -613,6 +758,15 @@
*/
/*!
+ \qmlproperty string cpp::compilerVersion
+
+ Compiler version string consisting of major, minor and patch numbers,
+ separated by a dot.
+
+ \nodefaultvalue
+*/
+
+/*!
\qmlproperty int cpp::compilerVersionMajor
\since Qbs 1.4
@@ -684,7 +838,7 @@
If the value is left undefined, the compiler default will be used.
If the list contains more than one value, the highest version is chosen.
- Possible values include: \c{"c89"}, \c{"c99"}, \c{"c11"}.
+ Possible values include: \c{"c89"}, \c{"c99"}, \c{"c11"}, \c{"c17"}, \c{"c2x"}.
\nodefaultvalue
*/
@@ -701,7 +855,8 @@
If the value is left undefined, the compiler default will be used.
If the list contains more than one value, the highest version is chosen.
- Possible values include: \c{"c++98"}, \c{"c++11"}, \c{"c++14"}, \c{"c++17"}.
+ Possible values include: \c{"c++98"}, \c{"c++11"}, \c{"c++14"}, \c{"c++17"}, \c{"c++20"},
+ \c{"c++23"}.
\nodefaultvalue
*/
@@ -915,9 +1070,9 @@
If this property is set to \c{undefined}, the default runtime library of the
toolchain is used.
- \note This property is only functional for MSVC.
+ \note This property is only functional for MSVC and MinGW.
- \defaultvalue \c{"dynamic"} for MSVC, \c{undefined} for other compilers.
+ \defaultvalue \c{"dynamic"} for MSVC and MinGW, \c{undefined} for other compilers.
*/
/*!
@@ -930,6 +1085,15 @@
*/
/*!
+ \qmlproperty bool cpp::enableSuspiciousLinkerFlagWarnings
+ \since Qbs 1.8
+
+ Whether to print warnings about escaped linker flags (such as \c -Xlinker and \c -Wl).
+
+ \defaultvalue \c{true}
+*/
+
+/*!
\qmlproperty string cpp::exceptionHandlingModel
\since Qbs 1.5
@@ -1217,8 +1381,6 @@
\qmlproperty string cpp::linkerMode
\since Qbs 1.6
- \unixproperty
-
Controls whether to automatically use an appropriate compiler frontend
instead of the system linker when linking binaries.
@@ -1237,12 +1399,25 @@
\qmlproperty string cpp::linkerVariant
\since Qbs 1.13
- \unixproperty
+ Set this property to force the use of a specific linker. A non-empty value
+ will result in the \c {-fuse-ld} option being emitted when linking with \c gcc,
+ \c clang or \c clang-cl. Other toolchains do not support this property.
+
+ The possible values for \c clang and \c gcc are \c "bfd", \c "gold", \c "lld"
+ and \c "mold", the possible values for \c clang-cl are \c "link" and \c "lld".
- Set this property to force the use of a specific \c ld implementation. A non-empty value
- will result in the \c {-fuse-ld} option being emitted when linking with \c gcc or \c clang.
+ The following example demonstrates how to change the linker for different toolchains:
- The possible values are \c "bfd", \c "gold" and \c "lld".
+ \code
+ Properties {
+ condition: qbs.toolchain.includes("gcc")
+ cpp.linkerVariant: "gold"
+ }
+ Properties {
+ condition: qbs.toolchain.includes("clang-cl")
+ cpp.linkerVariant: "lld"
+ }
+ \endcode
\nodefaultvalue
*/
@@ -1353,6 +1528,8 @@
\l{cpp::}{systemRunPaths} are ignored.
\nodefaultvalue
+
+ \sa{How do I make use of rpaths?}
*/
/*!
@@ -1389,6 +1566,8 @@
install names.
\nodefaultvalue
+
+ \sa{How do I make use of rpaths?}
*/
/*!
@@ -1428,6 +1607,16 @@
*/
/*!
+ \qmlproperty string cpp::rpathLinkFlag
+ \since Qbs 1.9
+
+ The rpath link flag used by the linker.
+
+ \defaultvalue \l{qbs::toolchain}{toolchain}-dependent, typical values are \c "-rpath-link="
+ or \c "-L"
+*/
+
+/*!
\qmlproperty string cpp::variantSuffix
\since Qbs 1.10
@@ -1702,9 +1891,7 @@
\qmlproperty bool cpp::generateCompilerListingFiles
\since Qbs 1.15
- \baremetalproperty
-
- Whether to auto-generate a compiler listing files.
+ Whether to auto-generate compiler listing files.
\defaultvalue \c{false}
*/
@@ -1729,3 +1916,28 @@
\defaultvalue \c{true}
*/
+
+/*!
+ \qmlproperty string cpp::windowsSdkVersion
+ \since Qbs 1.19
+
+ Which Windows SDK version should be used with MSVC toolchain. By default,
+ the latest SDK is used.
+
+ \windowsproperty
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty string cpp::enableCxxLanguageMacro
+ \since Qbs 1.20
+
+ Whether to set the \c{/Zc:__cplusplus} macro
+ (\l{https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus})
+
+ This property only applies to MSVC toolchain. clang-cl also supports
+ this option, but it has no effect.
+
+ \windowsproperty
+ \defaultvalue \c{true} for MSVC, \c{false} for clang-cl
+*/
diff --git a/doc/reference/modules/cpufeatures-module.qdoc b/doc/reference/modules/cpufeatures-module.qdoc
index 8dcd117f1..c2358e388 100644
--- a/doc/reference/modules/cpufeatures-module.qdoc
+++ b/doc/reference/modules/cpufeatures-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype cpufeatures
\inqmlmodule QbsModules
\since Qbs 1.10
diff --git a/doc/reference/modules/dmg-module.qdoc b/doc/reference/modules/dmg-module.qdoc
index 56fd65960..5d18621ab 100644
--- a/doc/reference/modules/dmg-module.qdoc
+++ b/doc/reference/modules/dmg-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype dmg
\inqmlmodule QbsModules
\since Qbs 1.9
diff --git a/doc/reference/modules/exporter-cmake.qdoc b/doc/reference/modules/exporter-cmake.qdoc
new file mode 100644
index 000000000..2f4191a76
--- /dev/null
+++ b/doc/reference/modules/exporter-cmake.qdoc
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Raphael Cotty (raphael.cotty@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \qmltype Exporter.cmake
+ \inqmlmodule QbsModules
+ \since Qbs 2.3
+
+ \brief Provides support for generating \CMAKE packages from dynamic, static and header library
+ products.
+
+ The Exporter.cmake module contains the properties and rules to create a \CMAKE config
+ \l{https://cmake.org/cmake/help/v3.29/manual/cmake-packages.7.html#config-file-packages}{files}
+ from the \l Export item of a \l Product.
+
+ For instance, suppose you are creating a library. To allow exporting to \CMAKE, you would write
+ something like the following:
+ \code
+ DynamicLibrary {
+ name: "mylibrary"
+ qbs.installPrefix: "/opt/mylibrary"
+ Depends { name: "Exporter.cmake" }
+ Exporter.cmake.packageName: "MyLibrary"
+ property string headersInstallDir: "include"
+ // ...
+ Group {
+ name: "API headers"
+ files: ["mylib.h"]
+ qbs.install: true
+ qbs.installDir: headersInstallDir
+ }
+ Group {
+ fileTagsFilter: ["Exporter.cmake.package"]
+ qbs.installDir: "lib/cmake/MyLibrary"
+ }
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: FileInfo.joinPaths(
+ exportingProduct.qbs.installRoot,
+ exportingProduct.qbs.installPrefix,
+ exportingProduct.headersInstallDir)
+ }
+ }
+ \endcode
+ To build against this library, from within your \CMAKE project, you simply
+ use \l{https://cmake.org/cmake/help/v3.29/command/find_package.html}{find_package}:
+ \code
+ find_package(MyLibrary PATHS REQUIRED)
+ add_executable(Consumer main.cpp)
+ target_link_libraries(Consumer MyLibrary)
+ \endcode
+
+ \section2 Relevant File Tags
+ \target filetags-exporter-qbs
+
+ \table
+ \header
+ \li Tag
+ \li Since
+ \li Description
+ \row
+ \li \c{"Exporter.cmake.package"}
+ \li 2.3.0
+ \li This tag is attached to all generated module files.
+ \row
+ \li \c{"Exporter.cmake.configFile"}
+ \li 2.3.0
+ \li This tag is attached to the generated config file.
+ \row
+ \li \c{"Exporter.cmake.versionFile"}
+ \li 2.3.0
+ \li This tag is attached to the generated version file.
+ \endtable
+*/
+
+/*!
+ \qmlproperty string Exporter.cmake::configFileName
+
+ The name of the generated config file.
+
+ \defaultvalue \c{packageName + "Config.cmake"}
+*/
+
+/*!
+ \qmlproperty string Exporter.cmake::versionFileName
+
+ The name of the generated version file.
+
+ \defaultvalue \c{packageName + "ConfigVersion.cmake"}
+*/
+
+/*!
+ \qmlproperty string Exporter.cmake::packageName
+
+ The name of the \CMAKE package.
+
+ \defaultvalue \l{Product::targetName}{Product.targetName}
+*/
+
diff --git a/doc/reference/modules/exporter-pkgconfig-module.qdoc b/doc/reference/modules/exporter-pkgconfig-module.qdoc
index 9489b4622..1dce5ffd5 100644
--- a/doc/reference/modules/exporter-pkgconfig-module.qdoc
+++ b/doc/reference/modules/exporter-pkgconfig-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Exporter.pkgconfig
\inqmlmodule QbsModules
\since Qbs 1.12
diff --git a/doc/reference/modules/exporter-qbs-module.qdoc b/doc/reference/modules/exporter-qbs-module.qdoc
index 610b02dab..cea5aad70 100644
--- a/doc/reference/modules/exporter-qbs-module.qdoc
+++ b/doc/reference/modules/exporter-qbs-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Exporter.qbs
\inqmlmodule QbsModules
\since Qbs 1.12
@@ -58,10 +57,11 @@
}
Export {
Depends { name: "cpp" }
- cpp.includePaths: [product.sourceDirectory]
+ cpp.includePaths: [exportingProduct.sourceDirectory]
prefixMapping: [{
- prefix: product.sourceDirectory,
- replacement: FileInfo.joinPaths(qbs.installPrefix, product.headersInstallDir)
+ prefix: exportingProduct.sourceDirectory,
+ replacement: FileInfo.joinPaths(exportingProduct.qbs.installPrefix,
+ exportingProduct.headersInstallDir)
}]
}
}
diff --git a/doc/reference/modules/freedesktop-module.qdoc b/doc/reference/modules/freedesktop-module.qdoc
new file mode 100644
index 000000000..5b6b14d0f
--- /dev/null
+++ b/doc/reference/modules/freedesktop-module.qdoc
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Alberto Mardegan
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype freedesktop
+ \inqmlmodule QbsModules
+ \since Qbs 1.16
+
+ \brief Provides support for some freedesktop.org specifications.
+
+ The \c freedesktop module contains properties and rules for building and working with
+ applications compliant to freedesktop.org specifications on UNIX platforms.
+ The areas in which this module can be of use include:
+
+ \list
+ \li Creation or post-processing of
+ \l{https://specifications.freedesktop.org/desktop-entry-spec/latest/}{\c{.desktop}
+ files}
+ \li Installation of \l{https://www.freedesktop.org/software/appstream/docs/chap-Metadata.html}{AppStream} metadata
+ \li Defining the location for
+ \l{https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout}
+ {application icons}
+ \endlist
+
+ This module is available on all platforms but is currently only useful on UNIX platforms.
+
+ \section2 Example usage
+ \target example-freedesktop
+
+ \code
+ Application {
+ ...
+ Depends { name: "freedesktop" }
+
+ Group {
+ files: [
+ ...
+
+ // Declare the desktop and appstream files
+ "data/my-app.desktop",
+ "data/my-app.metainfo.xml",
+ ]
+ }
+
+ // Add/change some fields in the desktop file
+ freedesktop.desktopKeys: ({
+ 'Exec': FileInfo.joinPaths(qbs.installPrefix,
+ product.installDir,
+ product.targetName) + ' --argument',
+ 'X-Application-Version': product.version,
+ })
+
+ // Declare the application icon
+ Group {
+ files: "icons/my-application.svg"
+ fileTags: "freedesktop.appIcon"
+ }
+ }
+ \endcode
+
+ \section2 Relevant File Tags
+ \target filetags-freedesktop
+
+ \table
+ \header
+ \li Tag
+ \li Auto-tagged File Names
+ \li Since
+ \li Description
+ \row
+ \li \c{"freedesktop.desktopfile_source"}
+ \li \c{*.desktop}
+ \li 1.16
+ \li A source file with this tag is a \c{.desktop} file or fragment that
+ will be merged into the application's final \c{.desktop} file.
+ \row
+ \li \c{"freedesktop.desktopfile"}
+ \li -
+ \li 1.16
+ \li Attached to the output artifacts of the rule that produces the
+ merged \c{.desktop} file.
+ \row
+ \li \c{"freedesktop.appstream"}
+ \li \c{*.metainfo.xml}, \c{*.appdata.xml}
+ \li 1.16
+ \li Source files with this tag are AppStream metadata files which will
+ be installed under \l{qbs::}{installPrefix}\c{/share/metainfo}
+ \row
+ \li \c{"freedesktop.appIcon"}
+ \li -
+ \li 1.16
+ \li Source files with this tag are application icons and will be
+ installed under \l{qbs::}{installPrefix}\c{/share/icons/hicolor/scalable/apps}
+ \endtable
+*/
+
+/*!
+ \qmlproperty string freedesktop::appName
+
+ The display name of the application which will be stored in the \c{.desktop} file.
+
+ \defaultvalue \l{Product::name}{product.name}
+*/
+
+/*!
+ \qmlproperty var freedesktop::desktopKeys
+
+ A dictionary of key-value pairs to add to the application's \c{.desktop}
+ file.
+
+ The contents of this property will be aggregated with the values from any
+ \c{.desktop} file. If this property and any \c{.desktop} files contain the
+ same key, this property will take precedence.
+
+ \nodefaultvalue
+*/
diff --git a/doc/reference/modules/ib-module.qdoc b/doc/reference/modules/ib-module.qdoc
index d6f8d3248..b85f87c71 100644
--- a/doc/reference/modules/ib-module.qdoc
+++ b/doc/reference/modules/ib-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype ib
\inqmlmodule QbsModules
\since Qbs 1.1
diff --git a/doc/reference/modules/ico-module.qdoc b/doc/reference/modules/ico-module.qdoc
index ccff608ef..60e4e2958 100644
--- a/doc/reference/modules/ico-module.qdoc
+++ b/doc/reference/modules/ico-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype ico
\inqmlmodule QbsModules
\since Qbs 1.11
diff --git a/doc/reference/modules/innosetup-module.qdoc b/doc/reference/modules/innosetup-module.qdoc
index 0f61275fc..fe75ecf0d 100644
--- a/doc/reference/modules/innosetup-module.qdoc
+++ b/doc/reference/modules/innosetup-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype innosetup
\inqmlmodule QbsModules
\since Qbs 1.7
diff --git a/doc/reference/modules/java-module.qdoc b/doc/reference/modules/java-module.qdoc
index 28057e089..02ad3227a 100644
--- a/doc/reference/modules/java-module.qdoc
+++ b/doc/reference/modules/java-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype java
\inqmlmodule QbsModules
\since Qbs 1.4
diff --git a/doc/reference/modules/lexyacc-module.qdoc b/doc/reference/modules/lexyacc-module.qdoc
index 2d5117641..002e90f05 100644
--- a/doc/reference/modules/lexyacc-module.qdoc
+++ b/doc/reference/modules/lexyacc-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype lex_yacc
\inqmlmodule QbsModules
\since Qbs 1.6
diff --git a/doc/reference/modules/nodejs-module.qdoc b/doc/reference/modules/nodejs-module.qdoc
index 0b56ba636..8b3166a81 100644
--- a/doc/reference/modules/nodejs-module.qdoc
+++ b/doc/reference/modules/nodejs-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype nodejs
\inqmlmodule QbsModules
\since Qbs 1.3
diff --git a/doc/reference/modules/nsis-module.qdoc b/doc/reference/modules/nsis-module.qdoc
index 359fec46c..d2b979eaa 100644
--- a/doc/reference/modules/nsis-module.qdoc
+++ b/doc/reference/modules/nsis-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype nsis
\inqmlmodule QbsModules
\since Qbs 1.2
diff --git a/doc/reference/modules/pkgconfig-module.qdoc b/doc/reference/modules/pkgconfig-module.qdoc
index f30308895..cd24257fc 100644
--- a/doc/reference/modules/pkgconfig-module.qdoc
+++ b/doc/reference/modules/pkgconfig-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype pkgconfig
\inqmlmodule QbsModules
\since 1.13
diff --git a/doc/reference/modules/protobufcpp-module.qdoc b/doc/reference/modules/protobufcpp-module.qdoc
index b82ccc9f7..2c96eaeef 100644
--- a/doc/reference/modules/protobufcpp-module.qdoc
+++ b/doc/reference/modules/protobufcpp-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype protobuf.cpp
\inqmlmodule QbsModules
\since Qbs 1.13
@@ -73,32 +72,38 @@
\li
\li 1.14.0
\li Source files with this tag are considered as gRPC files.
+ \row
+ \li \c{"protobuf.hpp"}
+ \li
+ \li 1.18.0
+ \li This tag is attached to the header files generated by \c protoc compiler.
\endtable
-*/
-
-/*!
- \qmlproperty string protobuf.cpp::grpcIncludePath
- The path where grpc++ headers are located. Set this property to override the
- default location.
+ \section2 Dependencies
- \defaultvalue \c auto-detected
+ The \l protobuf.cpp module requires runtime libraries to be operational. It depends on the
+ \c "protobuflib" module which can be created by the \l qbspkgconfig or fallback module
+ providers (the corresponding packages are \c protobuf or \c protobuf-lite). If \l useGrpc is
+ set to true, the \l protobuf.cpp module also depends on the \c "grpc++" module.
*/
/*!
- \qmlproperty string protobuf.cpp::grpcLibraryPath
+ \qmlproperty string protobuf.cpp::compilerName
- The path where the grpc++ library is located. Set this property to override the
- default location.
+ The name of the protoc binary.
- \defaultvalue \c auto-detected
+ \since Qbs 1.17
+ \defaultvalue \c "protoc"
*/
/*!
- \qmlproperty string protobuf.cpp::protocBinary
+ \qmlproperty string protobuf.cpp::compilerPath
+
+ The path to the protoc binary.
- The command to invoke when compiling proto definition files.
+ Use this property to override the auto-detected location.
+ \since Qbs 1.17
\defaultvalue \c auto-detected
*/
@@ -115,21 +120,13 @@
*/
/*!
- \qmlproperty string protobuf.cpp::includePath
+ \qmlproperty string protobuf.cpp::outputDir
+ \readonly
- The path where protobuf C++ headers are located. Set this property to override the
- default location.
+ The directory where the \c protoc compiler generated files are placed.
- \defaultvalue \c auto-detected
-*/
-
-/*!
- \qmlproperty string protobuf.cpp::libraryPath
-
- The path where the protobuf C++ library is located. Set this property to override the
- default location.
-
- \defaultvalue \c auto-detected
+ The value of this property is automatically set by \QBS and cannot be
+ changed by the user.
*/
/*!
diff --git a/doc/reference/modules/protobufnanopb-module.qdoc b/doc/reference/modules/protobufnanopb-module.qdoc
new file mode 100644
index 000000000..75c87ba57
--- /dev/null
+++ b/doc/reference/modules/protobufnanopb-module.qdoc
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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$
+**
+****************************************************************************/
+
+/*!
+ \qmltype protobuf.nanopb
+ \inqmlmodule QbsModules
+ \since Qbs 1.18
+
+ \brief Provides support for protocol buffers for embedded systems.
+
+ The \c protobuf.nanopb module provides support for generating nanopb C headers
+ and sources from proto definition files using the \l nanopb plugin for the
+ \l protoc tool suitable for embedded systems.
+
+ A simple qbs file that uses protobuf can be written as follows:
+ \code
+ CppApplication {
+ Depends { name: "protobuf.nanopb" }
+ files: ["foo.proto", "main.cpp"]
+ }
+ \endcode
+ A generated header now can be included in the C++ sources:
+ \code
+ #include <foo.pb.h>
+
+ int main(int argc, char* argv[]) {
+ Foo bar;
+ bar.has_answer = true;
+ bar.answer = 42;
+ return 0;
+ }
+ \endcode
+
+ Nanopb .options files to define nanopb specific proto options are supported.
+
+ \section2 Relevant File Tags
+
+ \table
+ \header
+ \li Tag
+ \li Auto-tagged File Names
+ \li Since
+ \li Description
+ \row
+ \li \c{"protobuf.input"}
+ \li \c{*.proto}
+ \li 1.13.0
+ \li Source files with this tag are considered inputs to the \c protoc compiler.
+ \row
+ \li \c{"protobuf.hpp"}
+ \li
+ \li 1.18.0
+ \li This tag is attached to the header files generated by \c protoc compiler.
+ \endtable
+*/
+
+/*!
+ \qmlproperty string protobuf.nanopb::compilerName
+
+ The name of the protoc binary.
+
+ \defaultvalue \c "protoc"
+*/
+
+/*!
+ \qmlproperty string protobuf.nanopb::compilerPath
+
+ The path to the protoc binary.
+
+ Use this property to override the auto-detected location.
+
+ \defaultvalue \c auto-detected
+*/
+
+/*!
+ \qmlproperty pathList protobuf.nanopb::importPaths
+
+ The list of imports that are passed to the \c protoc tool via the \c --proto_path option.
+ These imports should contain the proto files. They are used to determine
+ the relative structure of the generated files.
+ \note The paths are passed to \c protoc in the same order as specified in this property and
+ \c protoc output may differ depending on that order.
+
+ \defaultvalue \c []
+*/
+
+/*!
+ \qmlproperty string protobuf.nanopb::includePath
+
+ The path where protobuf nanopb C headers are located. Set this property to override the
+ default location.
+
+ \defaultvalue \c auto-detected
+*/
+
+/*!
+ \qmlproperty string protobuf.nanopb::libraryPath
+
+ The path where the protobuf nanopb runtime library is located. Set this property to override the
+ default location.
+
+ \defaultvalue \c auto-detected
+*/
+
+/*!
+ \qmlproperty string protobuf.nanopb::outputDir
+ \readonly
+
+ The directory where the \c protoc compiler generated files are placed.
+
+ The value of this property is automatically set by \QBS and cannot be
+ changed by the user.
+*/
+
+/*!
+ \qmlproperty string protobuf.nanopb::pluginName
+
+ The name of the nanopb protoc plugin.
+
+ \defaultvalue \c "protoc-gen-nanopb"
+*/
+
+/*!
+ \qmlproperty string protobuf.nanopb::pluginPath
+
+ The path to the nanopb protoc plugin.
+
+ Use this property to override the auto-detected location.
+
+ \defaultvalue \c auto-detected
+*/
diff --git a/doc/reference/modules/protobufobjc-module.qdoc b/doc/reference/modules/protobufobjc-module.qdoc
index 07e6db759..3666cdb8a 100644
--- a/doc/reference/modules/protobufobjc-module.qdoc
+++ b/doc/reference/modules/protobufobjc-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype protobuf.objc
\inqmlmodule QbsModules
\since Qbs 1.13
@@ -49,14 +48,31 @@
\li \c{*.proto}
\li 1.13.0
\li Source files with this tag are considered inputs to the \c protoc compiler.
+ \row
+ \li \c{"protobuf.hpp"}
+ \li
+ \li 1.18.0
+ \li This tag is attached to the header files generated by \c protoc compiler.
\endtable
*/
/*!
- \qmlproperty string protobuf.objc::protocBinary
+ \qmlproperty string protobuf.objc::compilerName
+
+ The name of the protoc binary.
+
+ \since Qbs 1.17
+ \defaultvalue \c "protoc"
+*/
+
+/*!
+ \qmlproperty string protobuf.objc::compilerPath
- The command to invoke when compiling proto definition files.
+ The path to the protoc binary.
+ Use this property to override the auto-detected location.
+
+ \since Qbs 1.17
\defaultvalue \c auto-detected
*/
@@ -99,3 +115,13 @@
\defaultvalue \c auto-detected
*/
+
+/*!
+ \qmlproperty string protobuf.objc::outputDir
+ \readonly
+
+ The directory where the \c protoc compiler generated files are placed.
+
+ The value of this property is automatically set by \QBS and cannot be
+ changed by the user.
+*/
diff --git a/doc/reference/modules/qbs-module.qdoc b/doc/reference/modules/qbs-module.qdoc
index c1a0b2532..7fc1479d4 100644
--- a/doc/reference/modules/qbs-module.qdoc
+++ b/doc/reference/modules/qbs-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage list-of-modules.html
\qmltype qbs
\inqmlmodule QbsModules
\since Qbs 1.0
@@ -56,7 +55,7 @@
name: "Runtime resources"
files: "*.qml"
qbs.install: true
- qbs.installDir: condition: qbs.targetOS.contains("unix")
+ qbs.installDir: condition: qbs.targetOS.includes("unix")
? "share/myapp" : "resources"
}
Group {
@@ -65,7 +64,7 @@
qbs.install: true
qbs.installDir: "bin"
}
- qbs.installPrefix: condition: qbs.targetOS.contains("unix")
+ qbs.installPrefix: condition: qbs.targetOS.includes("unix")
? "usr/local" : "MyApp"
}
\endcode
@@ -113,11 +112,13 @@
The name of the build variant for the current build.
- Possible values are \c{"debug"} and \c{"release"}. A debug build usually
- contains additional debug symbols that are needed for debugging the
- application but that can be left out from the release version. Debug builds
- are used for testing and release builds for creating the final installation
- file.
+ Possible values are \c{"debug"}, \c{"release"} and \c{"profiling"}.
+ A debug build usually contains additional debug symbols that are needed for
+ debugging the application and has optimizations turned off. A profiling
+ build usually contains debug symbols and has optimizations turned on. This
+ is useful for profiling tools or when you need to retain debug symbols
+ in a release build. A release build is a build without debug information and
+ with optimizations enabled.
\defaultvalue \c{"release"} if
\l{qbs::configurationName}{qbs.configurationName} is \c{"release"}. Otherwise
@@ -129,7 +130,8 @@
Whether to generate debug information.
- \defaultvalue \c{true} for debug builds, \c{false} otherwise.
+ \defaultvalue \c{true} if \l{qbs::buildVariant}{qbs.buildVariant} is \c{"debug"} or
+ \c{"profiling"}. Otherwise \c{false}.
*/
/*!
@@ -151,7 +153,7 @@
\endlist
Typically, this property is enabled for debug builds and disabled for
- release builds.
+ release or profiling builds.
\defaultvalue \c{true} for debug builds, \c{false} otherwise.
*/
@@ -169,11 +171,12 @@
\li \c{"small"}
\endlist
- \defaultvalue \c{"none"} for debug builds, \c{"fast"} for release builds.
+ \defaultvalue \c{"none"} if \l{qbs::buildVariant}{qbs.buildVariant} is \c{"debug"}.
+ Otherwise \c{"fast"}.
*/
/*!
- \qmlproperty stringList qbs::targetPlatform
+ \qmlproperty string qbs::targetPlatform
\since 1.11
The OS you want to build the project for.
@@ -207,6 +210,9 @@
\li \c{"android"}
\li \c{["android", "linux", "unix"]}
\row
+ \li \c{"dos"}
+ \li \c{["dos"]}
+ \row
\li \c{"freebsd"}
\li \c{["freebsd", "bsd", "unix"]}
\row
@@ -243,6 +249,9 @@
\li \c{"openbsd"}
\li \c{["openbsd", "bsd", "unix"]}
\row
+ \li \c{"os2"}
+ \li \c{["os2"]}
+ \row
\li \c{"qnx"}
\li \c{["qnx", "unix"]}
\row
@@ -255,6 +264,9 @@
\li \c{"tvos-simulator"}
\li \c{["tvos-simulator", "tvos", "darwin", "bsd", "unix"]}
\row
+ \li \c{"unix"}
+ \li \c{["unix"]}
+ \row
\li \c{"vxworks"}
\li \c{["vxworks"]}
\row
@@ -266,11 +278,19 @@
\row
\li \c{"windows"}
\li \c{["windows"]}
+ \row
+ \li \c{"none"}
+ \li \c{["none"]}
+ \row
+ \li \c{undefined}
+ \li \c{[]}
\endtable
+ \note The "none" value is usually used for a bare-metal platforms.
+
\sa {Target Platforms}
- \nodefaultvalue
+ \defaultvalue \l{platform}{Host.platform()}
*/
/*!
@@ -285,77 +305,317 @@
Commonly used values are: \c{"x86"}, \c{"x86_64"}, and \c{"arm"}.
+ \section2 Supported Processor Architectures
+
+ This table describes the possible values of the \l{qbs::}{architecture} property:
+ \table
+ \header
+ \li Architecture
+ \li Description
+ \row
+ \li \c{"78k"}
+ \li 16- and 8-bit accumulator-based register-bank CISC architecture
+ microcontroller family manufactured by Renesas Electronics
+ \row
+ \li \c{"arm"}
+ \li 32-bit RISC architecture for computer processors
+ developed by Acorn RISC Machine
+ \note There are a lot of sub-variants of the ARM architecture.
+ Some specialized \QBS modules differentiate between them,
+ making use of values such as \c "armv7a". Please consult the
+ respective module-specific documentation for information
+ on what kind of value to use.
+ \row
+ \li \c{"arm64"}
+ \li 64-bit RISC architecture for computer processors
+ developed by Acorn RISC Machine
+ \row
+ \li \c{"avr"}
+ \li 8-bit modified Harvard RISC architecture microcontroller
+ family manufactured by Microchip Technology
+ \row
+ \li \c{"avr32"}
+ \li 32-bit RISC architecture microcontroller family developed by Atmel
+ \row
+ \li \c{"c166"}
+ \li 16-bit architecture microcontroller family developed by Infineon
+ \row
+ \li \c{"cr16"}
+ \li 16-bit compact RISC architecture microcontroller family
+ developed by National Semiconductor
+ \row
+ \li \c{"e2k"}
+ \li 512-bit Russian wide VLIW microprocessor developed by Moscow Center
+ of SPARC Technologies (MCST) and fabricated by TSMC
+ \row
+ \li \c{"hcs8"}
+ \li 8-bit HC08 and S08 microcontroller family from Freescale Semiconductor
+ \row
+ \li \c{"hcs12"}
+ \li 16-bit HC12 and S12 microcontroller family from Freescale Semiconductor
+ \row
+ \li \c{"hppa"}
+ \li 64-bit PA-RISC processor architecture developed by Hewlett-Packard
+ \row
+ \li \c{"ia64"}
+ \li 64-bit ISA architecture of the Itanium family processors
+ developed by Intel
+ \row
+ \li \c{"m16c"}
+ \li 16-bit CISC microcontrollers featuring high ROM code
+ efficiency manufactured by Renesas Electronics
+ \row
+ \li \c{"m32c"}
+ \li 32- and 16-bit CISC microcontrollers featuring high ROM code
+ efficiency manufactured by Renesas Electronics
+ \row
+ \li \c{"m32r"}
+ \li 32-bit RISC microcontrollers for general industrial and
+ car-mounted systems, digital AV equipment, digital imaging
+ equipment manufactured by Renesas Electronics
+ \row
+ \li \c{"m68k"}
+ \li 16- and 32-bit CISC microprocessor, developed by Motorola
+ Semiconductor Products Sector, and further improved as ColdFire
+ architecture developed by NXP
+ \row
+ \li \c{"mcs251"}
+ \li 8-, 16-, and 32-bit microcontroller family which is a next binary
+ compatible generation for the \c "mcs51" family
+ \row
+ \li \c{"mcs51"}
+ \li 8-bit Harvard architecture microcontroller family developed by Intel
+ \row
+ \li \c{"mips"}
+ \li 32-bit RISC microprocessor without interlocked pipelined stages
+ architecture developed by MIPS Computer Systems
+ \row
+ \li \c{"mips64"}
+ \li 64-bit RISC microprocessor without interlocked pipelined stages
+ architecture developed by MIPS Computer Systems
+ \row
+ \li \c{"msp430"}
+ \li 16-bit mixed-signal microcontroller family manufactured
+ by Texas Instruments
+ \row
+ \li \c{"ppc"}
+ \li 32-bit RISC architecture processor family developed by
+ Apple–IBM–Motorola alliance
+ \row
+ \li \c{"ppc64"}
+ \li 64-bit RISC architecture processor family developed by
+ Apple–IBM–Motorola alliance
+ \row
+ \li \c{"r32c"}
+ \li 32-bit CISC microcontrollers with improved code efficiency
+ and processing performance manufactured by Renesas Electronics
+ \row
+ \li \c{"rh850"}
+ \li 32-bit automotive microcontroller family manufactured
+ by Renesas Electronics
+ \row
+ \li \c{"riscv"}
+ \li Open and free standard instruction set architecture based on established
+ RISC principles
+ \row
+ \li \c{"rl78"}
+ \li 16- and 8-bit accumulator-based register-bank CISC architecture
+ with 3-stage instruction pipelining microcontroller family manufactured
+ by Renesas Electronics
+ \row
+ \li \c{"rx"}
+ \li High performance 32-bit CISC microcontroller family manufactured
+ by Renesas Electronics
+ \row
+ \li \c{"s390x"}
+ \li 64- and 32-bit System/390 processor architecture developed by IBM
+ \row
+ \li \c{"sh"}
+ \li 32-bit RISC architecture processor family developed by Hitachi and
+ currently manufactured by Renesas Electronics
+ \row
+ \li \c{"sparc"}
+ \li 32-bit RISC architecture processor family developed by
+ Sun Microsystems and Fujitsu
+ \row
+ \li \c{"sparc64"}
+ \li 64-bit RISC architecture processor family developed by
+ Sun Microsystems and Fujitsu
+ \row
+ \li \c{"stm8"}
+ \li 8-bit microcontroller family manufactured by STMicroelectronics
+ \row
+ \li \c{"v850"}
+ \li 32-bit RISC microcontroller family manufactured by Renesas Electronics
+ \row
+ \li \c{"x86"}
+ \li 32-bit ISA architecture processor family developed by Intel
+ \row
+ \li \c{"x86_16"}
+ \li 16-bit ISA architecture processor family developed by Intel
+ \row
+ \li \c{"x86_64"}
+ \li 64-bit ISA architecture processor family developed by AMD
+ \row
+ \li \c{"xtensa"}
+ \li 32-bit architecture with a compact 16- and 24-bit instruction set
+ developed by Tensilica
+ \endtable
+
\nodefaultvalue
*/
/*!
- \qmlproperty string qbs::toolchainType
- \since Qbs 1.11
+ \qmlproperty stringList qbs::toolchain
- The toolchain that is going to be used for this build.
+ Contains the list of string values describing the toolchain and toolchain
+ family that is used to build a project.
- You should generally treat this property as \e{write-only} and avoid using
- it to test for the current toolchain. Instead, use the \l{qbs::}{toolchain}
- property for conditionals.
-
- For example, instead of:
+ This property is deduced from the \l{qbs::}{toolchainType} property and is typically
+ used to test for a particular toolchain or toolchain family in conditionals:
\code
- (qbs.toolchainType === "xcode" || qbs.toolchainType === "clang" || qbs.toolchainType === "gcc")
+ Properties {
+ // flags for GCC
+ condition: qbs.toolchain.includes("gcc")
+ cpp.commonCompilerFlags: ...
+ }
+ Properties {
+ // flags for MSVC
+ condition: qbs.toolchain.includes("msvc")
+ cpp.commonCompilerFlags: ...
+ }
\endcode
- use:
+ Unlike \l{qbs::}{toolchainType}, which contains a single value, \c qbs.toolchain
+ is a string list which also contains the toolchain family. This allows to make
+ conditions and checks simpler. For example, instead of:
\code
- qbs.toolchain.contains("gcc")
+ (qbs.toolchainType === "xcode" || qbs.toolchainType === "clang" || qbs.toolchainType === "gcc")
\endcode
- However, there are some cases when using \c toolchainType would be
- acceptable, such as when the resulting condition would be simpler while
- still being correct:
+ use:
\code
- (qbs.toolchainType === "gcc")
+ qbs.toolchain.includes("gcc")
\endcode
- versus:
+ since XCode, GCC and Clang belong to the \c "gcc" family.
- \code
- (qbs.toolchain.contains("gcc") && !qbs.toolchain.contains("clang"))
- \endcode
+ \section2 Relation between toolchainType and toolchain
- Typical values include: \c{"gcc"}, \c{"clang"}, \c{"mingw"}, \c{"msvc"}, and
- \c{"xcode"}.
+ This table describes the possible values and matching between the \l{qbs::}{toolchainType}
+ and the \c toolchain properties:
+ \table
+ \header
+ \li Toolchain Type
+ \li Toolchain
+ \row
+ \li \c{"clang"}
+ \li \c{["clang", "llvm", "gcc"]}
+ \row
+ \li \c{"clang-cl"}
+ \li \c{["clang-cl", "msvc"]}
+ \row
+ \li \c{"cosmic"}
+ \li \c{["cosmic"]}
+ \row
+ \li \c{"dmc"}
+ \li \c{["dmc"]}
+ \row
+ \li \c{"gcc"}
+ \li \c{["gcc"]}
+ \row
+ \li \c{"iar"}
+ \li \c{["iar"]}
+ \row
+ \li \c{"keil"}
+ \li \c{["keil"]}
+ \row
+ \li \c{"llvm"}
+ \li \c{["llvm", "gcc"]}
+ \row
+ \li \c{"mingw"}
+ \li \c{["mingw", "gcc"]}
+ \row
+ \li \c{"msvc"}
+ \li \c{["msvc"]}
+ \row
+ \li \c{"qcc"}
+ \li \c{["qcc"]}
+ \row
+ \li \c{"sdcc"}
+ \li \c{["sdcc"]}
+ \row
+ \li \c{"watcom"}
+ \li \c{["watcom"]}
+ \row
+ \li \c{"xcode"}
+ \li \c{["xcode", "clang", "llvm", "gcc"]}
+ \endtable
- \defaultvalue Determined automatically.
+ \nodefaultvalue
*/
/*!
- \qmlproperty string qbs::sysroot
+ \qmlproperty string qbs::toolchainType
+ \since Qbs 1.11
- The \c sysroot of the target platform.
+ The toolchain that is going to be used for this build.
- This property is typically set in a profile for cross-compiling.
+ For example, to build a project using the \c "clang" toolchain, simply do
+ \code
+ qbs build qbs.toolchainType:clang
+ \endcode
- \nodefaultvalue
-*/
+ You should generally treat this property as \e{write-only} and avoid using
+ it to test for the current toolchain. Instead, use the \l{qbs::}{toolchain}
+ property for conditionals.
-/*!
- \qmlproperty string qbs::pathListSeparator
+ Typical values include: \c{"gcc"}, \c{"clang"}, \c{"clang-cl"}, \c{"mingw"},
+ \c{"msvc"}, and \c{"xcode"}. Also see \l{Relation between toolchainType and toolchain}.
- The platform-specific separator for a path list that is used in environment
- variables or other contexts.
+ By default, \c qbs.toolchainType is automatically detected based on the
+ \l{qbs::}{targetOS} property:
+ \table
+ \header
+ \li Target OS
+ \li Toolchain
+ \row
+ \li \c{"darwin"}
+ \li \c{"xcode"}
+ \row
+ \li \c{"freebsd"}
+ \li \c{"clang"}
+ \row
+ \li \c{"haiku"}
+ \li \c{"gcc"}
+ \row
+ \li \c{"qnx"}
+ \li \c{"qcc"}
+ \row
+ \li \c{"unix"}
+ \li \c{"gcc"}
+ \row
+ \li \c{"vxworks"}
+ \li \c{"gcc"}
+ \row
+ \li \c{"windows"}
+ \li \c{"msvc"}
+ \endtable
- \defaultvalue \c{";"} on Windows, \c{":"} on Unix.
+ \defaultvalue Determined automatically.
*/
/*!
- \qmlproperty string qbs::nullDevice
- \since Qbs 1.4.2
+ \qmlproperty string qbs::sysroot
- The platform-specific file path corresponding to the null device.
+ The \c sysroot of the target platform.
+
+ This property is typically set in a profile for cross-compiling.
- \defaultvalue \c{"NUL"} on Windows, \c{"/dev/null"} on Unix.
+ \nodefaultvalue
*/
/*!
@@ -374,98 +634,6 @@
*/
/*!
- \qmlproperty stringList qbs::hostOS
-
- 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 \l{qbs::}{targetOS},
- even though some of them may not be supported.
-
- \nodefaultvalue
-*/
-
-/*!
- \qmlproperty string qbs::hostOSVersion
- \readonly
- \since Qbs 1.2
-
- The host operating system version. Currently, only defined for Windows and
- Apple platforms.
-
- Consists of two or three numbers separated by dots. For example, \c "10.9"
- or \c "6.3.9600".
-
- \nodefaultvalue
-*/
-
-/*!
- \qmlproperty string qbs::hostOSBuildVersion
- \readonly
- \since Qbs 1.2
-
- 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 \l{qbs::}{versionPatch}. On Apple platforms, this is a standard build
- number in the Apple versioning scheme. For example, \c "13C64".
-
- \nodefaultvalue
-*/
-
-/*!
- \qmlproperty int qbs::hostOSVersionMajor
- \readonly
- \since Qbs 1.2
-
- The host operating system major version.
-
- \defaultvalue \c{hostOSVersionParts[0]}
-*/
-
-/*!
- \qmlproperty int qbs::hostOSVersionMinor
- \since Qbs 1.2
-
- The host operating system minor version.
-
- \defaultvalue \c{hostOSVersionParts[1]}
-*/
-
-/*!
- \qmlproperty list qbs::hostOSVersionParts
- \readonly
- \since Qbs 1.2
-
- The host operating system version as a list.
-
- For example, Windows 8.1 (version 6.3.9600) would correspond to a value of
- \c[6, 3, 9600].
-
- \defaultvalue \c []
-*/
-
-/*!
- \qmlproperty int qbs::hostOSVersionPatch
- \readonly
- \since Qbs 1.2
-
- The host operating system patch level.
-
- \defaultvalue \c{hostOSVersionParts[2]}
-*/
-
-/*!
- \qmlproperty string qbs::hostPlatform
- \since Qbs 1.11
-
- Do not use this property.
-
- \defaultvalue Determined automatically.
-*/
-
-/*!
\qmlproperty stringList qbs::targetOS
\readonly
@@ -477,17 +645,17 @@
\code
Group {
// Includes all Unix-like platforms, such as: Linux, BSD, Apple platforms and others.
- condition: qbs.targetOS.contains("unix")
+ condition: qbs.targetOS.includes("unix")
files: ...
}
Group {
// Includes all Apple platforms, such as macOS, iOS, and iOS Simulator.
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
files: ...
}
Group {
// Includes only macOS
- condition: qbs.targetOS.contains("macos")
+ condition: qbs.targetOS.includes("macos")
files: ...
}
\endcode
@@ -500,7 +668,7 @@
use
\code
- qbs.targetOS.contains("darwin")
+ qbs.targetOS.includes("darwin")
\endcode
@@ -513,7 +681,7 @@
versus
\code
- qbs.targetOS.contains("linux") && !qbs.targetOS.contains("android")
+ qbs.targetOS.includes("linux") && !qbs.targetOS.includes("android")
\endcode
For the complete list of possible values, see
@@ -523,17 +691,6 @@
*/
/*!
- \qmlproperty stringList qbs::toolchain
-
- The attributes of the toolchain that is going to be used for this build.
-
- Typical values include \c{"llvm"}, in addition to the possible values of
- \l{qbs::}{toolchainType}.
-
- \nodefaultvalue
-*/
-
-/*!
\qmlproperty string qbs::version
\readonly
\since Qbs 1.4.1
diff --git a/doc/reference/modules/qnx-module.qdoc b/doc/reference/modules/qnx-module.qdoc
index d14f44b8f..a8ba75717 100644
--- a/doc/reference/modules/qnx-module.qdoc
+++ b/doc/reference/modules/qnx-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype qnx
\inqmlmodule QbsModules
\since Qbs 1.8
diff --git a/doc/reference/modules/qt-android_support-module.qdoc b/doc/reference/modules/qt-android_support-module.qdoc
index 995f73ba5..01d43ab98 100644
--- a/doc/reference/modules/qt-android_support-module.qdoc
+++ b/doc/reference/modules/qt-android_support-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Qt.android_support
\inqmlmodule QbsModules
\brief Provides Qt support for the Android platform.
@@ -39,17 +38,6 @@
*/
/*!
- \qmlproperty bool Qt.android_support::useMinistro
-
- Whether or not to use the Ministro service. If this property is enabled, then
- the Qt libraries required by your application as well as some other resources
- will not be packaged into the APK file, but are expected to be present on the
- device at run time.
-
- \defaultvalue \c false
-*/
-
-/*!
\qmlproperty string Qt.android_support::qmlRootDir
The root directory of the product's QML files. This information is passed to
@@ -86,6 +74,15 @@
*/
/*!
+ \qmlproperty stringList Qt.android_support::qmlImportPaths
+
+ Additional directories to search for QML imports.
+
+ \defaultvalue \c undefined
+ \since 1.22
+*/
+
+/*!
\qmlproperty bool Qt.android_support::verboseAndroidDeployQt
Enable this property if you want verbose output from the
diff --git a/doc/reference/modules/qt-core-module.qdoc b/doc/reference/modules/qt-core-module.qdoc
index c8c3dc7de..6aedf2a69 100644
--- a/doc/reference/modules/qt-core-module.qdoc
+++ b/doc/reference/modules/qt-core-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Qt.core
\inqmlmodule QbsModules
\brief Provides Qt Core support.
@@ -100,6 +99,12 @@
\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.metatypes"}
+ \li n/a
+ \li 1.16
+ \li This tag is attached to the JSON files that are potentially created if
+ \l{Qt.core::generateMetaTypesFile}{generateMetaTypesFile} is enabled.
+ \row
\li \c{"qt.core.resource_data"}
\li -
\li 1.7
@@ -196,6 +201,54 @@
*/
/*!
+ \qmlproperty bool Qt.core::generateMetaTypesFile
+
+ If this property is enabled, a JSON file tagged as \c "qt.core.metatypes" will potentially
+ be generated from metatype information collected by \c moc.
+
+ \nodefaultvalue
+ \since Qbs 1.16
+*/
+
+/*!
+ \qmlproperty string Qt.core::generatedHeadersDir
+
+ The directory where tools that generate headers (such as \c moc or \c uic) put resulting
+ files.
+
+ Normally, you don't need to change this property. The one use-case is when there are several
+ files with the same file name in the Product. The file name produced by \c moc is based
+ only on the source file's base file name (without the directory path) which leads to a conflict
+ in the mentioned case. You can resolve the conflict by setting this property to a non-default
+ value for one of the files. For example:
+
+ \code
+ QtApplication {
+ files: ["my_cool_header.h", "main.cpp"]
+ Group {
+ name: "legacy"
+ files: "legacy/my_cool_header.h"
+ Qt.core.generatedHeadersDir: "qt.legacy.headers"
+ }
+ }
+ \endcode
+
+ \defaultvalue \c product.buildDirectory + "/qt.headers"
+*/
+
+/*!
+ \qmlproperty bool Qt.core::metaTypesInstallDir
+
+ The directory to install the metatypes file into. If this property is empty or undefined,
+ the metatypes file will not be installed. If the
+ \l{Qt.core::generateMetaTypesFile}{generateMetaTypesFile} property is not \c true, then
+ this property has no effect.
+
+ \nodefaultvalue
+ \since Qbs 1.16
+*/
+
+/*!
\qmlproperty path Qt.core::incPath
The base path of the Qt headers.
@@ -325,6 +378,14 @@
*/
/*!
+ \qmlproperty string Qt.core::qdocOutputDir
+
+ The directory name where the \c qdoc tool writes its output.
+
+ \defaultvalue \c{product.buildDirectory + "/qdoc_html"}
+*/
+
+/*!
\qmlproperty string Qt.core::qmBaseName
The base name of the \c .qm file to be built from the \c .ts files in the
@@ -336,6 +397,14 @@
*/
/*!
+ \qmlproperty string Qt.core::qmDir
+
+ The directory name where to put the \c .qm file to be built.
+
+ \defaultvalue \l{Product::destinationDirectory}{product.destinationDirectory}
+*/
+
+/*!
\qmlproperty string Qt.core::qtBuildVariant
Specifies the type of Qt libraries to build against: \c "debug" or
@@ -441,3 +510,11 @@
\defaultvalue \c{versionParts[2]}
*/
+
+/*!
+ \qmlproperty bool Qt.core::useRPaths
+
+ Whether to add \l{Qt.core::libPath}{Qt.core.libPath} to \l{cpp::rpaths}{cpp.rpaths}.
+
+ \defaultvalue \c true on Linux, \c false everywhere else.
+*/
diff --git a/doc/reference/modules/qt-dbus-module.qdoc b/doc/reference/modules/qt-dbus-module.qdoc
index dacf3f095..32e84f950 100644
--- a/doc/reference/modules/qt-dbus-module.qdoc
+++ b/doc/reference/modules/qt-dbus-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Qt.dbus
\inqmlmodule QbsModules
\brief Provides Qt D-Bus support.
diff --git a/doc/reference/modules/qt-declarative-module.qdoc b/doc/reference/modules/qt-declarative-module.qdoc
index f7996257f..f247f3347 100644
--- a/doc/reference/modules/qt-declarative-module.qdoc
+++ b/doc/reference/modules/qt-declarative-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Qt.declarative
\inqmlmodule QbsModules
\brief Provides Qt Quick 1 support.
diff --git a/doc/reference/modules/qt-gui-module.qdoc b/doc/reference/modules/qt-gui-module.qdoc
index 16d8cf344..ace3cce08 100644
--- a/doc/reference/modules/qt-gui-module.qdoc
+++ b/doc/reference/modules/qt-gui-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Qt.gui
\inqmlmodule QbsModules
\brief Provides Qt GUI support.
diff --git a/doc/reference/modules/qt-modules.qdoc b/doc/reference/modules/qt-modules.qdoc
index 099f4e3ce..e3fee3c72 100644
--- a/doc/reference/modules/qt-modules.qdoc
+++ b/doc/reference/modules/qt-modules.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\page qt-modules.html
\inmodule qbs-modules
@@ -36,6 +35,8 @@
The \c{Qt.*} modules contain properties and rules for Qt.
+ Qt modules are generated by the \l[QML]{Qt} module provider.
+
\section1 Creating Dependencies to Qt Modules
The Qt modules are grouped using the prefix \c Qt. If your product depends
@@ -55,22 +56,6 @@
The Qt modules that have properties and relevant file tags are described in
separate topics.
- \section1 Qt-specific Module Provider Properties
-
- Looking up a Qt installation happens via a \l{Module Providers}{module provider}.
- By default, if a dependency to a Qt module is encountered, \QBS collects all Qt installations
- it can find. This lookup happens by searching for \c qmake executables in the \c PATH
- environment variable. Alternatively, you can explicitly tell \QBS which Qt
- installations it should consider by setting the \c Qt.qmakeFilePaths
- \l{Parameterizing Module Providers}{module provider property}. In that case,
- the environment will be ignored. For instance, with the following Linux command line,
- \QBS will build the project against a custom Qt instead of the standard one in \c{/usr/bin}:
- \code
- $ qbs moduleProviders.Qt.qmakeFilePaths:/opt/myqt/bin/qmake
- \endcode
- You can also set the module provider property in a profile. The simplest way to do
- this is via the \l setup-qt tool.
-
\section1 List of Submodules
\table
diff --git a/doc/reference/modules/qt-plugin_support-module.qdoc b/doc/reference/modules/qt-plugin_support-module.qdoc
index 2f41e1cc0..057291820 100644
--- a/doc/reference/modules/qt-plugin_support-module.qdoc
+++ b/doc/reference/modules/qt-plugin_support-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Qt.plugin_support
\inqmlmodule QbsModules
\since Qbs 1.13.0
diff --git a/doc/reference/modules/qt-qml-module.qdoc b/doc/reference/modules/qt-qml-module.qdoc
index 722a2eb41..57b3661f0 100644
--- a/doc/reference/modules/qt-qml-module.qdoc
+++ b/doc/reference/modules/qt-qml-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Qt.qml
\inqmlmodule QbsModules
\brief Provides Qt QML support.
@@ -60,10 +59,81 @@
\li 1.8
\li Source files with this tag serve as inputs to the QML plugin
scanner.
+ \row
+ \li \c{"qt.qml.types"}
+ \li n/a
+ \li 1.16
+ \li This tag is attached to the files created by the \c qmltyperegistrar
+ tool if the \l importName property is set.
\endtable
*/
/*!
+ \qmlproperty string Qt.qml::importName
+
+ Setting this value triggers QML type registration via the \c qmltyperegistrar tool,
+ which results in the creation of a file with the tag \c{"qt.qml.types"}.
+ The given string is the name under which the registered types can be imported
+ by QML code that wants to use them.
+
+ \note This functionality is only available with Qt 5.15 or later.
+
+ \since Qbs 1.16
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty string Qt.qml::importVersion
+
+ Specifies the version of the types to be registered.
+ Values consist of a major and an optional minor number, separated by dots.
+ This property has no effect if \l importName is not set.
+
+ \since Qbs 1.16
+ \defaultvalue \l {Product::version}{The product version}
+*/
+
+/*!
+ \qmlproperty string Qt.qml::typesFileName
+
+ Specifies the name of the file that declares the types registered for this product.
+ For applications, the default value is \c{<name>.qmltypes},
+ where \c{<name>} is the product's \l{Product::targetName}{target name}.
+ Otherwise, the default value is "plugins.qmltypes".
+ \note The naming conventions are still in flux.
+ When in doubt, consult the Qt documentation.
+
+ This property has no effect if \l importName is not set.
+
+ \since Qbs 1.16
+*/
+
+/*!
+ \qmlproperty string Qt.qml::typesInstallDir
+
+ The directory to install the qmltypes file into. If this property is empty or undefined,
+ the file will not be installed.
+ This property has no effect if \l importName is not set.
+
+ \since Qbs 1.16
+ \nodefaultvalue
+*/
+
+/*!
+ \qmlproperty stringList Qt.qml::extraMetaTypesFiles
+
+ Specifies extra metatypes files to pass to the \c qmltyperegistrar tool
+ via the \c{--foreign-types} option when registering QML types.
+ \note This property is only needed for external libraries, not products or modules
+ pulled via \c Depends items. In particular, you don't need to (and should not)
+ use it to collect the metatypes files of Qt modules. These are found automatically.
+ This property has no effect if \l importName is not set.
+
+ \since Qbs 1.16
+ \nodefaultvalue
+*/
+
+/*!
\qmlproperty string Qt.qml::qmlImportScannerName
The base name of the QML import scanner.
diff --git a/doc/reference/modules/qt-quick-module.qdoc b/doc/reference/modules/qt-quick-module.qdoc
index 8a39b92fc..5e6fa694b 100644
--- a/doc/reference/modules/qt-quick-module.qdoc
+++ b/doc/reference/modules/qt-quick-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Qt.quick
\inqmlmodule QbsModules
\brief Provides Qt Quick 2 support.
diff --git a/doc/reference/modules/qt-scxml-module.qdoc b/doc/reference/modules/qt-scxml-module.qdoc
index 34f0461d6..b0092ae6e 100644
--- a/doc/reference/modules/qt-scxml-module.qdoc
+++ b/doc/reference/modules/qt-scxml-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype Qt.scxml
\inqmlmodule QbsModules
\brief Provides Qt SCXML support.
diff --git a/doc/reference/modules/texttemplate-module.qdoc b/doc/reference/modules/texttemplate-module.qdoc
index 652b92863..4c354b261 100644
--- a/doc/reference/modules/texttemplate-module.qdoc
+++ b/doc/reference/modules/texttemplate-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype texttemplate
\inqmlmodule QbsModules
\since Qbs 1.13
diff --git a/doc/reference/modules/typescript-module.qdoc b/doc/reference/modules/typescript-module.qdoc
index 4c65c2279..f0f967747 100644
--- a/doc/reference/modules/typescript-module.qdoc
+++ b/doc/reference/modules/typescript-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype typescript
\inqmlmodule QbsModules
\since Qbs 1.3
diff --git a/doc/reference/modules/vcs-module.qdoc b/doc/reference/modules/vcs-module.qdoc
index d8ae45b80..fac0979ae 100644
--- a/doc/reference/modules/vcs-module.qdoc
+++ b/doc/reference/modules/vcs-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype vcs
\inqmlmodule QbsModules
\since Qbs 1.10
diff --git a/doc/reference/modules/wix-module.qdoc b/doc/reference/modules/wix-module.qdoc
index a09119698..ab6a7497f 100644
--- a/doc/reference/modules/wix-module.qdoc
+++ b/doc/reference/modules/wix-module.qdoc
@@ -26,7 +26,6 @@
****************************************************************************/
/*!
- \contentspage index.html
\qmltype wix
\inqmlmodule QbsModules
\since Qbs 1.2
diff --git a/doc/reference/modules/xcode-module.qdoc b/doc/reference/modules/xcode-module.qdoc
index bf38feea1..748226c06 100644
--- a/doc/reference/modules/xcode-module.qdoc
+++ b/doc/reference/modules/xcode-module.qdoc
@@ -26,7 +26,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\qmltype xcode
\inqmlmodule QbsModules
\since Qbs 1.5
diff --git a/doc/reference/reference.qdoc b/doc/reference/reference.qdoc
index f1975bf3a..f2992b287 100644
--- a/doc/reference/reference.qdoc
+++ b/doc/reference/reference.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\previouspage howtos.html
\page reference.html
\nextpage building-qbs.html
@@ -47,7 +46,6 @@
*/
/*!
- \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
@@ -67,7 +65,6 @@
*/
/*!
- \contentspage reference.html
\qmlmodule QbsModules
\title List of Modules
These are the modules shipped with \QBS.
@@ -78,7 +75,12 @@
*/
/*!
- \contentspage reference.html
+ \qmlmodule QbsModuleProviders
+ \title List of Module Providers
+ These are the module providers shipped with \QBS.
+*/
+
+/*!
\group list-of-items
\title List of All Items
@@ -88,7 +90,6 @@
*/
/*!
- \contentspage reference.html
\qmlmodule QbsLanguageItems
\title List of Language Items
@@ -97,7 +98,6 @@
*/
/*!
- \contentspage reference.html
\qmlmodule QbsConvenienceItems
\title List of Convenience Items
@@ -105,8 +105,7 @@
*/
/*!
- \contentspage reference.html
- \group list-of-probes
+ \qmlmodule QbsProbes
\title List of Probes
These are the probes shipped with \QBS.
diff --git a/doc/targets/qbs-target-android.qdoc b/doc/targets/qbs-target-android.qdoc
index 46081db0d..b306a0ea2 100644
--- a/doc/targets/qbs-target-android.qdoc
+++ b/doc/targets/qbs-target-android.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page qbs-target-android.html
\ingroup platforms
diff --git a/doc/targets/qbs-target-integrity.qdoc b/doc/targets/qbs-target-integrity.qdoc
index 52418a86c..bef92ee82 100644
--- a/doc/targets/qbs-target-integrity.qdoc
+++ b/doc/targets/qbs-target-integrity.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page qbs-target-integrity.html
\ingroup platforms
diff --git a/doc/targets/qbs-target-ios.qdoc b/doc/targets/qbs-target-ios.qdoc
index 1b55a7d0e..56908f01d 100644
--- a/doc/targets/qbs-target-ios.qdoc
+++ b/doc/targets/qbs-target-ios.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page qbs-target-ios.html
\ingroup platforms
diff --git a/doc/targets/qbs-target-linux.qdoc b/doc/targets/qbs-target-linux.qdoc
index 8b15da444..025c77909 100644
--- a/doc/targets/qbs-target-linux.qdoc
+++ b/doc/targets/qbs-target-linux.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page qbs-target-linux.html
\ingroup platforms
diff --git a/doc/targets/qbs-target-macos.qdoc b/doc/targets/qbs-target-macos.qdoc
index 8d956fc63..82fa6ee3c 100644
--- a/doc/targets/qbs-target-macos.qdoc
+++ b/doc/targets/qbs-target-macos.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page qbs-target-macos.html
\ingroup platforms
diff --git a/doc/targets/qbs-target-platforms.qdoc b/doc/targets/qbs-target-platforms.qdoc
index 7e556aea9..92fc7b1f1 100644
--- a/doc/targets/qbs-target-platforms.qdoc
+++ b/doc/targets/qbs-target-platforms.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\previouspage installing-files.html
\group platforms
\nextpage shell.html
diff --git a/doc/targets/qbs-target-qnx.qdoc b/doc/targets/qbs-target-qnx.qdoc
index c7e28d905..a4d75de23 100644
--- a/doc/targets/qbs-target-qnx.qdoc
+++ b/doc/targets/qbs-target-qnx.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page qbs-target-qnx.html
\ingroup platforms
diff --git a/doc/targets/qbs-target-tvos.qdoc b/doc/targets/qbs-target-tvos.qdoc
index 76744637e..6529663ad 100644
--- a/doc/targets/qbs-target-tvos.qdoc
+++ b/doc/targets/qbs-target-tvos.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page qbs-target-tvos.html
\ingroup platforms
diff --git a/doc/targets/qbs-target-vxworks.qdoc b/doc/targets/qbs-target-vxworks.qdoc
index 100ecfcc3..299f4fe7a 100644
--- a/doc/targets/qbs-target-vxworks.qdoc
+++ b/doc/targets/qbs-target-vxworks.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page qbs-target-vxworks.html
\ingroup platforms
diff --git a/doc/targets/qbs-target-watchos.qdoc b/doc/targets/qbs-target-watchos.qdoc
index 9afec3f46..f0997b1cf 100644
--- a/doc/targets/qbs-target-watchos.qdoc
+++ b/doc/targets/qbs-target-watchos.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page qbs-target-watchos.html
\ingroup platforms
diff --git a/doc/targets/qbs-target-windows.qdoc b/doc/targets/qbs-target-windows.qdoc
index 258a5b445..8bd788877 100644
--- a/doc/targets/qbs-target-windows.qdoc
+++ b/doc/targets/qbs-target-windows.qdoc
@@ -25,7 +25,6 @@
**
****************************************************************************/
/*!
- \contentspage index.html
\page qbs-target-windows.html
\ingroup platforms
diff --git a/doc/tutorial.qdoc b/doc/tutorial.qdoc
new file mode 100644
index 000000000..7d336b5b3
--- /dev/null
+++ b/doc/tutorial.qdoc
@@ -0,0 +1,647 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com).
+** 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$
+**
+****************************************************************************/
+
+/*!
+ \previouspage module-providers.html
+ \page tutorial.html
+ \nextpage tutorial-1.html
+
+ \title Tutorial
+
+ The tutorial describes the process of creating a typical \QBS project and explains core
+ concepts.
+
+ \list
+ \li \l{tutorial-1.html}{Application}
+ \li \l{tutorial-2.html}{Static Library}
+ \li \l{tutorial-3.html}{Dynamic Library}
+ \li \l{tutorial-4.html}{Convenience Items}
+ \li \l{tutorial-5.html}{Autotest}
+ \li \l{tutorial-6.html}{Project Properties}
+ \li \l{tutorial-7.html}{Buildconfig Module}
+ \li \l{tutorial-8.html}{Configurable Library}
+ \li \l{tutorial-9.html}{Version Header}
+ \endlist
+*/
+
+/*!
+ \previouspage tutorial.html
+ \page tutorial-1.html
+ \nextpage tutorial-2.html
+
+ \title Application
+
+ Let's start with a mandatory Hello World example. There is a Hello World example in the
+ \l{overview.html#well-defined-language}{overview section}, but this time we will create a
+ real-world project.
+
+ We will start with a simple \QBS project file:
+ \snippet ../tutorial/chapter-1/myproject.qbs 0
+
+ Here, we set the \l{Project::name}{name} of the project to \c "My Project" - it is mainly
+ used for IDEs but can also be used to reference a project when setting properties from
+ command-line. We also set the \l{Project::minimumQbsVersion}{minimumQbsVersion} - this property
+ can be useful if the project depends on features that are not present in earlier \QBS
+ versions.
+
+ The \l{Project::references}{references} property contains the path to a file
+ that describes our application. This file is located in a separate \c app directory -
+ typically, projects tend to have quite a complicated structure but \QBS does not enforce any
+ specific layout, so we will simply put each product in a separate directory.
+
+ The application file sets several properties:
+ \snippet ../tutorial/chapter-1/app/app.qbs 0
+
+ The \l{Product::name}{name} property identifies the product.
+ The \l{Product::targetName}{targetName} property sets the name of the resulting binary
+ (without the \c .exe extension on Windows, which is appended automatically). By default,
+ \l{Product::targetName}{targetName} defaults to \l{Product::name}{name}. The
+ \l{Product::files}{files} property contains a single \c main.c file which is a trivial
+ \e{hello world} application:
+ \snippet ../tutorial/chapter-1/app/main.c 0
+
+ We set \l{Product::consoleApplication}{consoleApplication} to \c true to indicate that our
+ application is going to be used from a terminal. For example, on Windows, this will spawn a
+ new console window if you double-click the resulting binary, while on macOS it will create a
+ standalone binary file instead of an
+ \l{https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html}{application bundle}.
+
+ By default, the \l{Application::install}{CppApplication.install} property is \c false and thus
+ \QBS does not install the binary. If the \l{Application::install}{install} property is
+ \c true, when building a project, \QBS will also install it into an \e{installation root}
+ folder which by default is named \c install-root and located under the build directory. This
+ folder will contain only resulting artifacts without the build leftovers and files will have
+ the same layout as in the target system. The \l{Application::install}{install} property should
+ be set to \c true for all products that are to be distributed.
+ See the \l{installing-files.html}{Installing Files} section for more details.
+
+ We can now build and run our application from the project directory:
+ \code
+ chapter-1 $ qbs build
+ ...
+ Building for configuration default
+ compiling main.c [My Application]
+ ...
+ linking myapp [My Application]
+ ...
+ Build done for configuration default.
+
+ chapter-1 $ qbs run
+ ...
+ Starting target. Full command line: .../default/install-root/usr/local/bin/myapp
+ Hello, world
+ \endcode
+
+ The \QBS output to console and default installation location may vary between different
+ platforms.
+
+ In most cases, \QBS should be able to find the default compiler (for example, GCC or
+ Clang on Linux, Visual Studio on Windows, or Xcode on macOS), but if that's not the
+ case, see the \l{configuring.html}{configuring} section.
+
+ In the following chapters, we will continue to improve our project: we will add a library and
+ make it configurable.
+*/
+
+/*!
+ \previouspage tutorial-1.html
+ \page tutorial-2.html
+ \nextpage tutorial-3.html
+
+ \title Static Library
+
+ Let's add a static library to our project so we can reuse some code. Analogous to what we did
+ for the application, we put the file into the \c{lib} directory and add it to the
+ \l{Project::references}{references} property in our project. The modified project may look
+ like this:
+
+ \snippet ../tutorial/chapter-2/myproject.qbs 0
+
+ Let's take a look at the the library file now:
+
+ \snippet ../tutorial/chapter-2/lib/lib.qbs 0
+ It contains the \l{StaticLibrary} item which sets the \l{Product::type}{type} of the product
+ to \c "staticlibrary" and sets some defaults like where to install that library.
+ As before, we set the \l{Product::files}{files} property with a header:
+ \snippet ../tutorial/chapter-2/lib/lib.h 0
+ And we set the implementation file of our library:
+ \snippet ../tutorial/chapter-2/lib/lib.c 0
+
+ We will keep our library really simple - it only contains one function, which we will later use
+ in our application. The source file requires a \c "CRUCIAL_DEFINE" to be
+ passed to a preprocessor. That is why we set the \l{cpp::defines}{cpp.defines} property:
+ \snippet ../tutorial/chapter-2/lib/lib.qbs 1
+
+ Note that unlike the \l{CppApplication} item, the \l{StaticLibrary} does not pull in the
+ dependency on the \l{cpp} module automatically - that is why we have to pull it in manually
+ using the \l{Depends} item. Without the \l{cpp} module, \QBS would not know how to turn a
+ \c{.c} file into an object file and the object file into a library. See
+ \l{Rules and Product Types} for details.
+
+ Next, we need to tell \QBS where to look for public headers of our library when building
+ products that depend on it. By default, \QBS knows nothing about the layout of our
+ library, so we tell it to look for headers in the library's source directory using the
+ \l{Export} item:
+
+ \snippet ../tutorial/chapter-2/lib/lib.qbs 2
+ You can export any \l{Module} property within the \l{Export} item - it will be merged in the
+ depending product with other properties. For example, you can export
+ \l{cpp::defines}{cpp.defines} or specific \l{cpp::commonCompilerFlags}{compiler flags} that
+ are required to use this product.
+
+ We depend on the \l{cpp} module twice - once within the \l{StaticLibrary}
+ item and once in the \l{Export} item. This is because by default \QBS does not export anything
+ when depending on this product and the dependencies in this item (as well as
+ properties set in this item) are private to this product while dependencies and properties
+ set in the \l{Export} item are for export only.
+
+ Finally, we have some Apple-specific settings. You can skip this part of the tutorial if you
+ are using some other platform. We depend on the \l{bundle} module and set the
+ \l{bundle::isBundle}{bundle.isBundle} to false:
+ \snippet ../tutorial/chapter-2/lib/lib.qbs 3
+ By default, \QBS builds static and dynamic libraries as
+ \l{https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WhatAreFrameworks.html}{Frameworks}
+ on macOS. So, to keep things simple, we disable the framework build and build a plain old
+ static library file here.
+*/
+
+/*!
+ \previouspage tutorial-2.html
+ \page tutorial-3.html
+ \nextpage tutorial-4.html
+
+ \title Dynamic Library
+
+ Let's now turn our static library into a dynamic library. It is a bit trickier with dynamic
+ libraries since several things should be tweaked. First, we need to be able to mark methods
+ (or classes) in our library as exported. Second, we need to tell our application where to look
+ for our library at run time using \l{https://en.wikipedia.org/wiki/Rpath}{rpaths}.
+
+ Some compilers, like MSVC, require us to mark which symbols we want to export or import. The
+ \l{https://stackoverflow.com/a/6840659}{canonical} way would be to define some helper macros.
+ Let's start with a header that defines those helper macros:
+ \snippet ../tutorial/chapter-3/lib/lib_global.h 0
+
+ This header defines the \c MYLIB_EXPORT macro that expands either to an "export" or to an
+ "import" directive based on the presence of the \c MYLIB_LIBRARY macro. We can use this macro
+ to mark a function as follows:
+ \snippet ../tutorial/chapter-3/lib/lib.h 0
+
+ The modified library product may look like this:
+
+ \snippet ../tutorial/chapter-3/lib/lib.qbs 0
+
+ The most important change is that we changed the item type from \l{StaticLibrary} to
+ \l{DynamicLibrary}. We also added the \c MYLIB_LIBRARY to the list of
+ \l{cpp::defines}{defines}. We do this only when building the library but we are not exporting
+ it - that way the users of our library will have methods marked for import rather than export.
+
+ Finally, we set \l{cpp::sonamePrefix}{cpp.sonamePrefix} to \c "@rpath". This is required only
+ for Apple platforms, see
+ \l{https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/RunpathDependentLibraries.html}{Run-Path Dependent Libraries}
+ for details.
+
+ It is also required to set \l{cpp::rpaths}{cpp.rpaths} in our application file. Since the
+ library is installed to the \c{lib} directory and the application is installed to the \c{bin}
+ directory, we need to tell the loader to look in the \c{lib} directory. The
+ \l{jsextension-fileinfo.html#relativePath}{FileInfo.relativePath} method can help us:
+ \snippet ../tutorial/chapter-3/app/app.qbs 0
+
+ On Linux, this expression would be equivalent to this: \c{cpp.rpaths: ["$ORIGIN/../lib"]}.
+ Don't forget to \c{import qbs.FileInfo} in order to be able to use the
+ \l{jsextension-fileinfo.html} extension.
+
+ To make the example complete, here's how the full \c app/app.qbs file should look like:
+ \snippet ../tutorial/chapter-3/app/app.qbs 1
+*/
+
+/*!
+ \previouspage tutorial-3.html
+ \page tutorial-4.html
+ \nextpage tutorial-5.html
+
+ \title Convenience Items
+
+ As you might have noticed, we are repeating ourselves when setting the same properties in our
+ products - we set \l{Product::version}{version}, \l{Application::install}{install},
+ \l{cpp::rpaths}{cpp.rpaths}, and so on. For a single application and a library, that is not a
+ big deal, but what if we have a dozen libraries? Luckily, this can be achieved using item
+ \l{language-introduction.html#reusing-project-file-code}{inheritance} - we move the common code
+ to the base item and in the real product we will only set what is specific to that product (for
+ example, the list of \l{Product::files}{files}).
+
+ First, we need to tell \QBS where to look for our new base items. This can be achieved using
+ the \l{Project::qbsSearchPaths}{qbsSearchPaths} property. We set this property to \c "qbs" so
+ that \QBS will search our items in the \c{qbs} directory located in the project directory:
+ \snippet ../tutorial/chapter-4/myproject.qbs 0
+
+ \note This directory has a pre-defined structure: base items should be located under the
+ \c{imports} subdirectory. See \l{Custom Modules and Items} for details.
+
+ Let's create a base item for all our applications and move common code there:
+ \snippet ../tutorial/chapter-4/qbs/imports/MyApplication.qbs 0
+
+ As you see, we managed to extract most of the code here, and our application file now only
+ contains what's relevant to it:
+ \snippet ../tutorial/chapter-4/app/app.qbs 0
+
+ Now let's do the same for our library:
+ \snippet ../tutorial/chapter-4/qbs/imports/MyLibrary.qbs 0
+
+ Here, we introduce a helper property, \c libraryMacro, with a default value calculated based
+ on the capitalized product name. Since the name of out library product is \c "mylib", this
+ property will expand to \c "MYLIB_LIBRARY". We can also override the default value
+ for the macro in products that inherit our item like this:
+ \code
+ MyLibrary {
+ libraryMacro: "SOME_OTHER_LIBRARY_MACRO"
+ }
+ \endcode
+
+ Let's take a look at the refactored library file:
+ \snippet ../tutorial/chapter-4/lib/lib.qbs 0
+
+ We managed to extract the reusable parts to common base items leaving the actual products clean
+ and simple.
+
+ Unfortunately, item inheritance comes with a price - when both parent and child items set the
+ same property (\l{cpp::defines}{cpp.defines} in our case), the value in the child item wins.
+ To work around this, the special \l{special-property-values.html#base}{base} value
+ exists - it gives access to the base item's value of the current property and makes it possible
+ to extend its value rather than override it. Here, we concatenate the list of defines from the
+ base item \c{["MYLIB_LIBRARY"]} with a new list, specific to this product (namely,
+ \c{['CRUCIAL_DEFINE']}).
+*/
+
+/*!
+ \previouspage tutorial-4.html
+ \page tutorial-5.html
+ \nextpage tutorial-6.html
+
+ \title Autotest
+
+ Now that we can re-use our base items, let's add a simple autotest. We inherit the new item
+ from the \c MyApplication item and add an \c "autotest" type:
+
+ \snippet ../tutorial/chapter-5/qbs/imports/MyAutoTest.qbs 0
+
+ This additional type is required for the helper \l{AutotestRunner} item. This item runs all
+ products with the \c "autotest" type when it is being built. It also implicitly depends on all
+ such products, so they will be built before running as well.
+
+ Let's add this item to our top-level \l{Project} item:
+
+ \code
+ Project {
+ name: "My Project"
+ minimumQbsVersion: "2.0"
+ // ...
+ AutotestRunner {
+ timeout: 60
+ }
+ }
+ \endcode
+ Here we set the \l{AutotestRunner::timeout}{timeout} property to 60 seconds so that our runner
+ kills tests that run way too long.
+
+ Now we need to add our first test. Let's create a new product with the following content:
+
+ \snippet ../tutorial/chapter-5/test/test.qbs 0
+
+ Here we depend on our library (which we are going to test), set the product
+ \l{Product::name}{name}, and specify the source file, which looks like this:
+ \snippet ../tutorial/chapter-5/test/test.c 0
+
+ The test compares the value from the library with the value from the command line.
+
+ Don't forget to add the new test product to the references property in the Project:
+ \snippet ../tutorial/chapter-5/myproject.qbs 0
+
+ Now we can build the autotest product - this will also launch our test:
+ \code
+ $ qbs build -p "autotest-runner"
+ ...
+ running test mytest [autotest-runner]
+ Build done for configuration default.
+ \endcode
+*/
+
+/*!
+ \previouspage tutorial-5.html
+ \page tutorial-6.html
+ \nextpage tutorial-7.html
+
+ \title Project Properties
+
+ It would be nice if our project was configurable. Let's add some properties to our root project
+ file:
+ \snippet ../tutorial/chapter-6/myproject.qbs 0
+
+ Now we can use those properties in our helper items and in products:
+
+ \snippet ../tutorial/chapter-6/qbs/imports/MyApplication.qbs 0
+
+ Here, we access the project file using the special
+ \l{special-property-values.html#project}{project} value. If the nearest project in the project
+ tree does not have the desired property, \QBS looks it up in the parent project, potentially
+ all the way up to the top-level project.
+ We also use the \l{Application::installDebugInformation}{installDebugInformation}
+ property here. By default, it has the same value as \l{Application::install}{install} but we
+ want to make the debug information install optional.
+
+ Let's now disable the tests if \c project.withTests is \c false:
+
+ \snippet ../tutorial/chapter-6/myproject.qbs 1
+
+ Here we use the \l{Properties} item within the \l{SubProject} item. This item allows to
+ override a subproject's properties which can be useful when adding some other \QBS project as a
+ Git submodule. Of course, it is not very useful in our particular case since we only set the
+ \l{Project::condition}{Project.condition} property. We could achieve the same effect by
+ setting the \l{SubProject::condition}{condition} property of the \l{SubProject} item:
+ \code
+ SubProject {
+ filePath: "test/test.qbs"
+ condition: parent.withTests
+ }
+ \endcode
+ Another way would be to disable the test product. That way, an IDE would still show the whole
+ project tree including disabled tests:
+ \code
+ // qbs/imports/MyAutoTest.qbs
+ MyApplication {
+ condition: project.withTests
+ type: base.concat(["autotest"])
+ }
+ \endcode
+
+ Let's finally make our \l{AutotestRunner} configurable too:
+ \snippet ../tutorial/chapter-6/myproject.qbs 2
+
+ There are several ways to override project properties from the command line. First, the special
+ \c project variable can be used to set the properties of the top-level project:
+ \code
+ $ qbs resolve project.withTests:false
+ \endcode
+
+ You can also refer to properties using project's \l{Project::name}{name}:
+
+ \code
+ $ qbs resolve "projects.My Project.withTests:false"
+ \endcode
+
+ This can again be useful for accessing the properties of external sub-projects. Note that since
+ the project name contains spaces, we use quotes here.
+*/
+
+/*!
+ \previouspage tutorial-6.html
+ \page tutorial-7.html
+ \nextpage tutorial-8.html
+
+ \title Buildconfig Module
+
+ In the previous chapter, we added some properties to our main \l{Project} file. While this is a
+ perfect approach for \e public properties of the project, sometimes we want
+ to add some \e private properties for better tuning. Of course, we could put everything
+ in the \l{Project} file, but that would make it very convoluted. Also, accessing the top-level
+ project all the way from products makes things strongly tied.
+
+ You can also use a \l{Module} that \l{Product}{products} may depend on. That way, a
+ \l{Product} only uses properties of the module it depends on without the need to know about
+ the top-level project.
+
+ Let's create a file named \c{mybuildconfig.qbs} and put it into the
+ \c{qbs/modules/mybuildconfig} directory, near the \c{qbs/imports} directory:
+ \code
+ // qbs/modules/mybuildconfig.qbs
+ Module {
+ }
+ \endcode
+
+ So far, this is just an empty \l{Module} so let's add some properties to it:
+ \snippet ../tutorial/chapter-7/qbs/modules/mybuildconfig/mybuildconfig.qbs 0
+
+ We added the \c appInstallDir and \c libInstallDir properties that will allow us to configure
+ the installation location of the our application and library, respectively.
+
+ Now we can use our module in the \c{MyApplication.qbs} item:
+ \snippet ../tutorial/chapter-7/qbs/imports/MyApplication.qbs 0
+
+ We pull in the new module using the \l{Depends} item, similar to how we pulled in
+ the \l{cpp} module dependency earlier. We also set the \l{Application::installDir}{installDir}
+ property to the corresponding module property, namely to \c{mybuildconfig.appInstallDir}.
+
+ \QBS \l{Module}{modules} have the feature to automatically export properties of other modules.
+ Those exported properties are merged in the resulting product. We can use this feature to set
+ the \l{cpp::rpaths}{cpp.rpaths} in our module rather than in products:
+
+ \snippet ../tutorial/chapter-7/qbs/modules/mybuildconfig/mybuildconfig.qbs 1
+
+ Here, we inject the dependency on the \l{cpp} module and calculate the \c{libRPaths} property.
+ This is a relative path from the \c{product.installDir} (which is either \c{"bin"}
+ or \c{"lib"}, depending on product type to \c{libInstallDir}). Finally, we set
+ \l{cpp::rpaths}{cpp.rpaths} to this property. This way, those \c rpaths will be automatically
+ exported to all products that depend on the \c{mybuildconfig} module.
+
+ Now, we can also use our new module in the library item:
+ \snippet ../tutorial/chapter-7/qbs/imports/MyLibrary.qbs 0
+
+ Let's change the library folder name from \c{"lib"} to \c{"lib64"} when building our project:
+ \code
+ $ qbs modules.mybuildconfig.libDirName:lib64
+ ...
+ $ ls default/install-root/usr/local/
+ bin lib64
+ \endcode
+*/
+
+/*!
+ \previouspage tutorial-7.html
+ \page tutorial-8.html
+ \nextpage tutorial-9.html
+
+ \title Configurable Library
+
+ In this chapter, we will make our library more configurable. We will add configuration options
+ to be able to choose between static and dynamic builds.
+
+ We start with some new properties in the \c mybuildconfig module:
+ \code
+ Module {
+ ...
+ property bool staticBuild: false
+ property bool installStaticLib: true
+ ...
+ }
+ \endcode
+
+ We need to do some modifications to export macros in the \c lib/lib_global.h header:
+ \snippet ../tutorial/chapter-8/lib/lib_global.h 0
+
+ Here' we added a new branch when \c MYLIB_STATIC_LIBRARY is defined. In that case, we make
+ the \c MY_LIB_EXPORT macro empty.
+
+ Now, let's modify the \c qbs/imports/MyLibrary.qbs file as follows:
+ \snippet ../tutorial/chapter-8/qbs/imports/MyLibrary.qbs 0
+
+ First thing to notice is that we changed the type of the item from \l{DynamicLibrary} to the
+ \l{Library} item which is a common base item for dynamic and static libraries.
+
+ Second, we change the \l{Product::type}{type} of the product so it depends on the \c staticBuild
+ property:
+ \code
+ type: mybuildconfig.staticBuild ? "staticlibrary" : "dynamiclibrary"
+ \endcode
+
+ Third, we change our \e{export macro} to be different in the static and dynamic builds to
+ correspond with the changes we made to the \c lib_global.h:
+ \code
+ readonly property string _nameUpper : name.replace(" ", "_").toUpperCase()
+ property string libraryMacro: _nameUpper + "_LIBRARY"
+ property string staticLibraryMacro: _nameUpper + "_STATIC_LIBRARY"
+ cpp.defines: mybuildconfig.staticBuild ? [staticLibraryMacro] : [libraryMacro]
+ \endcode
+
+ Note that in a static build we export the \c MYLIB_STATIC_LIBRARY macro so that the dependent
+ products will know whether they link to a static or dynamic library:
+ \code
+ Export {
+ ...
+ cpp.defines: exportingProduct.mybuildconfig.staticBuild
+ ? [exportingProduct.staticLibraryMacro] : []
+ }
+ \endcode
+
+ Now we can build our project in dynamic or static mode:
+ \code
+ $ qbs resolve modules.mybuildconfig.staticBuild:false && qbs build
+ ...
+ $ ls default/install-root/usr/local/lib/
+ libmylib.1.0.0.so
+ libmylib.1.0.so
+ libmylib.1.so
+ libmylib.so
+ $ rm -r default
+ $ qbs resolve modules.mybuildconfig.staticBuild:true && qbs build
+ ...
+ $ $ ls default/install-root/usr/local/lib/
+ libmylib.a
+ \endcode
+*/
+
+/*!
+ \previouspage tutorial-8.html
+ \page tutorial-9.html
+ \nextpage howtos.html
+
+ \title Version Header
+
+ To create new files, such as version headers, during the build, use \l{Rules}. Every command
+ \QBS executes, such as compile or linker commands, is described by a corresponding Rule in a
+ Module or a Product.
+
+ In this section, we will create a simple header with a string constant based on the variable
+ in our project.
+
+ First, we add this variable to our \c mybuildconfig module:
+ \snippet ../tutorial/chapter-9/qbs/modules/mybuildconfig/mybuildconfig.qbs 0
+
+ Next, we create a new file, \c{version.h.in}, located in the \c{version-header} directory. This
+ file contains the template for our header:
+ \snippet ../tutorial/chapter-9/version-header/version.h.in 0
+
+ Now we create a file called \c{version-header.qbs} in the same directory. This file contains
+ a \l{Product} with a \l{Rule} that turns the \c{version.h.in} into a real header.
+ Let's go through the contents of the file:
+ \snippet ../tutorial/chapter-9/version-header/version-header.qbs 0
+
+ First, we import \l{TextFile Service}{TextFile}. We will need this class to read the template
+ and write the resulting header. Second, we declare a new \l{Product} named \c{"version_header"}
+ and with the \c{"hpp"} type. This is the type of the artifact we will create in the Rule.
+ Third, we add the dependency on the \c mybuildconfig module to use the new
+ \c mybuildconfig.productVersion variable.
+
+ We also add a \l{Group} with the template file and assign the \c{version_h_in} tag to the file:
+ \snippet ../tutorial/chapter-9/version-header/version-header.qbs 1
+
+ \QBS Rules work with file tags, instead of working with files directly, which makes
+ it easy to reuse rules. The name of the tag is chosen arbitrarily here. We could use the name
+ of the file as a tag, but to avoid confusion between file name and file tag, we use underscores
+ in the tag instead of dots.
+
+ Now we can create a Rule itself:
+ \snippet ../tutorial/chapter-9/version-header/version-header.qbs 2
+ Here, we specify that our Rule takes files tagged as \c "version_h_in" and produces an
+ \l{Artifact} with the name \c "version.h" and tagged \c "hpp". By default, files are created in
+ the \l{Product::destinationDirectory}{Product.destinationDirectory} folder. We add the \c "hpp"
+ tag for the header as this is the tag the \l{cpp}{cpp module} uses for headers. That way, \QBS
+ can track changes and process our generated file the same way it treats all other
+ headers. Note that earlier we set the product type to \c "hpp" as well. \QBS requires that
+ artifact type should match the product type directly or be accessible via the chain of Rules.
+ Otherwise, the Rule won't be executed. For details, see the \l{Rules and Product Types}
+ section.
+
+ The actual code generation happens in the \l{Rule::prepare}{Rule.prepare} script:
+ \snippet ../tutorial/chapter-9/version-header/version-header.qbs 3
+
+ In this script, we create a \l {JavaScriptCommand} object and set some meta properties, such as
+ the description and highlight. For details about Commands, see
+ \l{Command and JavaScriptCommand}. In the sourceCode variable, we create a JavaScript
+ function that opens the \l{The inputs and outputs Variables}{input file}, reads its content
+ using the \l{TextFile Service}{TextFile} object, replaces the \c "${PRODUCT_VERSION}"
+ placeholder with the actual value in the \c product.mybuildconfig.productVersion variable, and
+ writes the resulting content into the \l{The inputs and outputs Variables}{output file}.
+
+ Finally, we export the \l{Product::buildDirectory}{exportingProduct.buildDirectory} so that
+ products that depend on this product can include our generated header:
+
+ \snippet ../tutorial/chapter-9/version-header/version-header.qbs 4
+
+ The full content of the file should look like this:
+
+ \snippet ../tutorial/chapter-9/version-header/version-header.qbs 5
+
+ Let's now add our Product into the root project so \QBS will be aware of it:
+ \snippet ../tutorial/chapter-9/myproject.qbs 0
+
+ We also need to add the dependency on the \c "version_header" to our application:
+ \snippet ../tutorial/chapter-9/app/app.qbs 0
+
+ Now we can include the header in the \c main.c file and print the contents of the string
+ constant:
+
+ \snippet ../tutorial/chapter-9/app/main.c 0
+
+ Let's try and run our application. You should see something like this:
+ \code
+ $ qbs run -p "My Application"
+ Starting target. Full command line: .../default/install-root/usr/local/bin/myapp
+ Hello, world
+ Hello from library
+ ProductVersion = 1.0.0
+ \endcode
+*/
diff --git a/docker-compose.yml b/docker-compose.yml
index e5c7329c2..d852cc5e6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -4,6 +4,12 @@ x-default-service: &linux
working_dir: /qbs
environment:
- BUILD_OPTIONS
+ - QTEST_FUNCTION_TIMEOUT
+ - QBS_AUTOTEST_PROFILE
+ - QBS_TEST_SOURCE_ROOT
+ - WITH_ARCHIVE
+ - WITH_TESTS
+ - CLCACHE_DIR
volumes:
- .:/qbs
- ~/.ccache:/home/devel/.ccache
@@ -12,24 +18,149 @@ x-default-service: &linux
- SYS_PTRACE
services:
- bionic:
+ jammy-qt6: &jammy-qt6
<< : *linux
- hostname: bionic
- image: ${DOCKER_USER:-qbsbuild}/qbsdev:bionic-5.12.6_1.14.1-2
+ hostname: jammy-qt6
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-qt6-6.5.3_2.1.2-0
build:
- dockerfile: docker/bionic/Dockerfile
+ dockerfile: docker/jammy/Dockerfile
context: .
args:
- QT_VERSION: 5.12.6
- QTCREATOR_VERSION: 4.10.2
+ QT_VERSION: 6.5.3
+ QTCREATOR_VERSION: 11.0.3
- stretch:
+ jammy-qt5:
<< : *linux
- hostname: stretch
- image: ${DOCKER_USER:-qbsbuild}/qbsdev:stretch
+ hostname: jammy-qt5
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-5.15.2_2.1.2-0
build:
- dockerfile: docker/stretch/Dockerfile
+ dockerfile: docker/jammy/Dockerfile
context: .
args:
- QT_VERSION: 5.12.4
- QTCREATOR_VERSION: 4.9.2
+ QT_VERSION: 5.15.2
+ QTCREATOR_VERSION: 11.0.3
+
+ jammy:
+ << : *jammy-qt6
+
+ jammy-qt6-static:
+ << : *linux
+ hostname: jammy-qt6-static
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-qt6-static-6.5.0_2.1.2-0
+ build:
+ dockerfile: docker/jammy/test-qt6-static.Dockerfile
+ context: .
+ args:
+ QT_VERSION: 6.5.0
+ QTCREATOR_VERSION: 11.0.3
+
+ jammy-android-515:
+ << : *linux
+ hostname: jammy-android
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-android-5.15.2-0
+ build:
+ dockerfile: docker/jammy/test-android.Dockerfile
+ context: .
+ args:
+ QT_VERSION: 5.15.2
+ ANDROID_NDK_VERSION: 23.0.7599858
+
+ jammy-android-62:
+ << : *linux
+ hostname: jammy-android
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-android-6.2.4-0
+ build:
+ dockerfile: docker/jammy/test-android.Dockerfile
+ context: .
+ args:
+ QT_VERSION: 6.2.4
+ ANDROID_NDK_VERSION: 23.0.7599858
+
+ jammy-android-65:
+ << : *linux
+ hostname: jammy-android
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-android-6.5.0-0
+ build:
+ dockerfile: docker/jammy/test-android.Dockerfile
+ context: .
+ args:
+ QT_VERSION: 6.5.0
+ ANDROID_NDK_VERSION: 25.1.8937393
+
+ jammy-android-ndk-r19c:
+ << : *linux
+ hostname: jammy-android
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-android-ndk-r19c-0
+ build:
+ dockerfile: docker/jammy/test-android-no-qt.Dockerfile
+ context: .
+ args:
+ ANDROID_NDK_VERSION: 19.2.5345600
+
+ jammy-android-ndk-r21e:
+ << : *linux
+ hostname: jammy-android
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-android-ndk-r21e-0
+ build:
+ dockerfile: docker/jammy/test-android-no-qt.Dockerfile
+ context: .
+ args:
+ ANDROID_NDK_VERSION: 21.4.7075529
+
+ jammy-android-ndk-r23:
+ << : *linux
+ hostname: jammy-android
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-android-ndk-r23-0
+ build:
+ dockerfile: docker/jammy/test-android-no-qt.Dockerfile
+ context: .
+ args:
+ ANDROID_NDK_VERSION: 23.0.7599858
+
+ jammy-baremetal:
+ << : *linux
+ hostname: jammy-baremetal
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-baremetal-0
+ build:
+ dockerfile: docker/jammy/test-baremetal.Dockerfile
+ context: .
+
+ jammy-qt4:
+ << : *linux
+ hostname: jammy-qt4
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:jammy-qt4-0
+ build:
+ dockerfile: docker/jammy/test-qt4.Dockerfile
+ context: .
+
+ leap:
+ << : *linux
+ hostname: leap
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:leap-5.15.2_1.20.1-1
+ build:
+ dockerfile: docker/leap/Dockerfile
+ context: .
+ args:
+ QT_VERSION: 5.15.2
+ QTCREATOR_VERSION: 5.0.3
+
+ windows:
+ image: ${DOCKER_USER:-qbsbuild}/qbsdev:windowsservercore-6.6.0_1.24.0-0
+ build:
+ dockerfile: docker/windowsservercore/Dockerfile
+ context: .
+ args:
+ QT_VERSION: 6.6.0
+ QTCREATOR_VERSION: 9.0.1
+ working_dir: 'C:/qbs'
+ environment:
+ - BUILD_OPTIONS
+ - WITH_DOCS
+ volumes:
+ - type: bind
+ source: .
+ target: C:\qbs
+ - type: bind
+ source: ~/.ccache
+ target: C:\.ccache
+ network_mode: nat
diff --git a/docker/bionic/Dockerfile b/docker/bionic/Dockerfile
deleted file mode 100644
index 178186c67..000000000
--- a/docker/bionic/Dockerfile
+++ /dev/null
@@ -1,191 +0,0 @@
-#
-# Downloads and builds Qt from source. We do it in a
-# separate stage to keep the number of dependencies low in
-# the final Docker image.
-#
-FROM ubuntu:bionic as build-qt-mingw32_w64
-ARG QT_VERSION
-
-RUN apt-get update -qq && \
- apt-get install -qq -y --no-install-recommends \
- build-essential \
- ca-certificates \
- libclang-3.9-dev \
- libgl1-mesa-dev \
- mingw-w64 \
- python \
- xz-utils \
- wget
-
-ENV LLVM_INSTALL_DIR=/usr/lib/llvm-3.9
-
-RUN mkdir -p /qt/source && \
- wget -nv --continue --tries=20 --waitretry=10 --retry-connrefused \
- --no-dns-cache --timeout 300 -qO- \
- https://download.qt.io/official_releases/qt/${QT_VERSION%??}/${QT_VERSION}/single/qt-everywhere-src-${QT_VERSION}.tar.xz \
- | tar --strip-components=1 -C /qt/source -xJf-
-
-RUN mkdir -p qt/build && \
- cd qt/build && \
- ../source/configure \
- -prefix /opt/Qt/${QT_VERSION}/mingw32_w64 \
- -release \
- -shared \
- -opensource \
- -confirm-license \
- -nomake examples \
- -nomake tests \
- -xplatform win32-g++ \
- -opengl desktop \
- -device-option CROSS_COMPILE=/usr/bin/x86_64-w64-mingw32- \
- -qt-sqlite -qt-libpng \
- -no-cups -no-dbus -no-pch \
- -no-feature-accessibility \
- -skip qtactiveqt \
- -skip qt3d \
- -skip qtcanvas3d \
- -skip qtcharts \
- -skip qtconnectivity \
- -skip qtdatavis3d \
- -skip qtdeclarative \
- -skip qtdoc \
- -skip qtgamepad \
- -skip qtgraphicaleffects \
- -skip qtimageformats \
- -skip qtlocation \
- -skip qtmultimedia \
- -skip qtnetworkauth \
- -skip qtquickcontrols \
- -skip qtquickcontrols2 \
- -skip qtpurchasing \
- -skip qtremoteobjects \
- -skip qtscxml \
- -skip qtsensors \
- -skip qtserialbus \
- -skip qtserialport \
- -skip qtspeech \
- -skip qtsvg \
- -skip qttranslations \
- -skip qtwayland \
- -skip qtvirtualkeyboard \
- -skip qtwebchannel \
- -skip qtwebengine \
- -skip qtwebsockets \
- -skip qtwebview \
- -skip qtwinextras \
- -skip qtxmlpatterns \
- -skip qtx11extras
-
-# Build and transform stdout into . to reduce the noise
-RUN cd qt/build && \
- make -j $(nproc --all) | stdbuf -o0 tr -cd '\n' | stdbuf -o0 tr '\n' '.' && \
- make install
-
-#
-# Install Qt and Qbs for Linux and combine that with Qt for Windows from the
-# previous stage
-#
-FROM ubuntu:bionic
-LABEL Description="Ubuntu development environment for Qbs with Qt and various dependencies for testing Qbs modules and functionality"
-ARG QT_VERSION
-ARG QTCREATOR_VERSION
-
-# Allow colored output on command line.
-ENV TERM=xterm-color
-
-#
-# Make it possible to change UID/GID in the entrypoint script. The docker
-# container usually runs as root user on Linux hosts. When the Docker container
-# mounts a folder on the host and creates files there, those files would be
-# owned by root instead of the current user. Thus we create a user here who's
-# UID will be changed in the entrypoint script to match the UID of the current
-# host user.
-#
-ARG USER_UID=1000
-ARG USER_NAME=devel
-RUN apt-get update -qq && \
- apt-get install -qq -y \
- ca-certificates \
- gosu \
- sudo && \
- groupadd -g ${USER_UID} ${USER_NAME} && \
- useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \
- usermod -a -G sudo ${USER_NAME} && \
- echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers
-
-COPY docker/bionic/entrypoint.sh /sbin/entrypoint.sh
-ENTRYPOINT ["/sbin/entrypoint.sh"]
-
-# Qbs build dependencies
-RUN apt-get update -qq && \
- apt-get install -qq -y --no-install-recommends \
- build-essential \
- ca-certificates \
- ccache \
- curl \
- git \
- libclang-3.9-dev \
- clang-8 \
- clang-tidy-8 \
- libdbus-1-3 \
- libfreetype6 \
- libfontconfig1 \
- libgl1-mesa-dev \
- libgl1-mesa-glx \
- pkg-config \
- help2man \
- python-pip \
- p7zip-full && \
- update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 100 && \
- update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 100 && \
- pip install beautifulsoup4 lxml # for building the documentation
-
-ENV LLVM_INSTALL_DIR=/usr/lib/llvm-8
-
-
-#
-# Install Qt and Qbs for Linux from qt.io
-#
-COPY scripts/install-qt.sh install-qt.sh
-
-RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qtscript qttools qtx11extras icu && \
- ./install-qt.sh --version ${QTCREATOR_VERSION} qtcreator && \
- echo "export PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:\${PATH}" > /etc/profile.d/qt.sh
-
-ENV PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:${PATH}
-
-
-#
-# Install Qt installation from build stage
-#
-COPY --from=build-qt-mingw32_w64 /opt/Qt/${QT_VERSION}/mingw32_w64 /opt/Qt/${QT_VERSION}/mingw32_w64
-
-#
-# Install mingw toolchain to cross build for Windows and select
-# POSIX API to make use of threading support in the stl. That
-# is required by Qbs.
-#
-RUN apt-get install -qq -y --no-install-recommends \
- mingw-w64 && \
- printf "1\n" | update-alternatives --config x86_64-w64-mingw32-g++
-
-
-# Configure Qbs
-USER $USER_NAME
-RUN qbs-setup-toolchains /usr/bin/g++ gcc && \
- qbs-setup-toolchains /usr/bin/clang clang && \
- qbs-setup-qt /opt/Qt/${QT_VERSION}/gcc_64/bin/qmake qt-gcc_64 && \
- qbs config profiles.qt-gcc_64.baseProfile gcc && \
- qbs-setup-qt /opt/Qt/${QT_VERSION}/gcc_64/bin/qmake qt-clang_64 && \
- qbs config profiles.qt-clang_64.baseProfile clang && \
- qbs config defaultProfile qt-gcc_64 && \
- \
- qbs-setup-toolchains /usr/bin/x86_64-w64-mingw32-g++ mingw && \
- qbs-setup-qt /opt/Qt/${QT_VERSION}/mingw32_w64/bin/qmake qt-mingw32_w64 && \
- qbs config profiles.qt-mingw32_w64.baseProfile mingw
-
-# Switch back to root user for the entrypoint script.
-USER root
-
-# Work-around for QTBUG-79020
-RUN echo "export QT_NO_GLIB=1" >> /etc/profile.d/qt.sh
diff --git a/docker/docker.qbs b/docker/docker.qbs
index 5adc277e4..513a5fd8b 100644
--- a/docker/docker.qbs
+++ b/docker/docker.qbs
@@ -1,35 +1,8 @@
-import qbs
-Project {
- Product {
- Depends { name: "docker"; required: false }
-
- name: "qbs-docker-stretch"
- type: ["docker.docker-image"]
- builtByDefault: false
- condition: docker.present
-
- docker.imageTag: "qbsbuild/qbsdev:stretch"
-
- files: [
- "stretch/Dockerfile",
- "stretch/entrypoint.sh",
- ]
- }
-
- Product {
- Depends { name: "docker"; required: false }
-
- name: "qbs-docker-windowsservercore"
- type: ["docker.docker-image"]
- builtByDefault: false
- condition: docker.present
-
- docker.imageTag: "qbsbuild/qbsdev:windowsservercore"
-
- files: [
- "windowsservercore/Dockerfile",
- "windowsservercore/build-qt.bat",
- ]
- }
+// This is a convenience product to be able to use Qt Creator for editing the docker files.
+// For building and managing the images, use docker-compose as explained in
+// https://doc.qt.io/qbs/building-qbs.html#using-docker.
+Product {
+ name: "docker"
+ files: "**"
}
diff --git a/docker/bionic/entrypoint.sh b/docker/entrypoint.sh
index 40bc5acb9..40bc5acb9 100755
--- a/docker/bionic/entrypoint.sh
+++ b/docker/entrypoint.sh
diff --git a/docker/jammy/Dockerfile b/docker/jammy/Dockerfile
new file mode 100644
index 000000000..cdda42202
--- /dev/null
+++ b/docker/jammy/Dockerfile
@@ -0,0 +1,116 @@
+#
+# Install Qt and Qbs for Linux
+#
+FROM ubuntu:jammy
+LABEL Description="Ubuntu development environment for Qbs with Qt and various dependencies for testing Qbs modules and functionality"
+ARG QT_VERSION
+ARG QTCREATOR_VERSION
+
+# Allow colored output on command line.
+ENV TERM=xterm-color
+
+#
+# Make it possible to change UID/GID in the entrypoint script. The docker
+# container usually runs as root user on Linux hosts. When the Docker container
+# mounts a folder on the host and creates files there, those files would be
+# owned by root instead of the current user. Thus we create a user here who's
+# UID will be changed in the entrypoint script to match the UID of the current
+# host user.
+#
+ARG USER_UID=1000
+ARG USER_NAME=devel
+RUN apt-get update -qq && \
+ apt-get install -qq -y \
+ ca-certificates \
+ gosu \
+ sudo && \
+ groupadd -g ${USER_UID} ${USER_NAME} && \
+ useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \
+ usermod -a -G sudo ${USER_NAME} && \
+ echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers
+
+COPY docker/entrypoint.sh /sbin/entrypoint.sh
+ENTRYPOINT ["/sbin/entrypoint.sh"]
+
+# # Qbs build dependencies
+RUN apt-get update -qq && \
+ DEBIAN_FRONTEND="noninteractive" apt-get install -qq -y --no-install-recommends \
+ bison \
+ build-essential \
+ ca-certificates \
+ capnproto \
+ ccache \
+ clang-15 \
+ clang-tidy-15 \
+ cmake \
+ curl \
+ flex \
+ git \
+ help2man \
+ icoutils \
+ libcapnp-dev \
+ libdbus-1-3 \
+ libfreetype6 \
+ libfontconfig1 \
+ libgl1-mesa-dev \
+ libgl1-mesa-glx \
+ libnanopb-dev \
+ libprotobuf-dev \
+ libgrpc++-dev \
+ libxkbcommon-x11-0 \
+ locales \
+ nanopb \
+ ninja-build \
+ nsis \
+ pkg-config \
+ protobuf-compiler \
+ protobuf-compiler-grpc \
+ psmisc \
+ python3-pip \
+ python3-setuptools \
+ p7zip-full \
+ subversion \
+ unzip \
+ zip && \
+ update-alternatives --install /usr/bin/clang clang /usr/bin/clang-15 100 && \
+ update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-15 100 && \
+ update-alternatives --install /usr/bin/clang-check clang-check /usr/bin/clang-check-15 100 && \
+ update-alternatives --install /usr/bin/python python /usr/bin/python3 100 && \
+ pip install beautifulsoup4 lxml protobuf==3.19.1 pyyaml
+
+# Set the locale
+RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
+ locale-gen
+ENV LANG en_US.UTF-8
+ENV LANGUAGE en_US:en
+ENV LC_ALL en_US.UTF-8
+
+ENV LLVM_INSTALL_DIR=/usr/lib/llvm-15
+
+#
+# Install Qt and Qbs for Linux from qt.io
+#
+COPY scripts/install-qt.sh install-qt.sh
+
+RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qttools qtx11extras qtscxml qt5compat icu && \
+ ./install-qt.sh --version ${QTCREATOR_VERSION} qtcreator && \
+ echo "export PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:\${PATH}" > /etc/profile.d/qt.sh
+
+ENV PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:${PATH}
+
+
+# Configure Qbs
+USER $USER_NAME
+RUN qbs-setup-toolchains /usr/bin/g++ gcc && \
+ qbs-setup-toolchains /usr/bin/clang clang && \
+ qbs-setup-qt /opt/Qt/${QT_VERSION}/gcc_64/bin/qmake qt-gcc_64 && \
+ qbs config profiles.qt-gcc_64.baseProfile gcc && \
+ qbs-setup-qt /opt/Qt/${QT_VERSION}/gcc_64/bin/qmake qt-clang_64 && \
+ qbs config profiles.qt-clang_64.baseProfile clang && \
+ qbs config defaultProfile qt-gcc_64
+
+# Switch back to root user for the entrypoint script.
+USER root
+
+# Work-around for QTBUG-79020
+RUN echo "export QT_NO_GLIB=1" >> /etc/profile.d/qt.sh
diff --git a/docker/jammy/test-android-no-qt.Dockerfile b/docker/jammy/test-android-no-qt.Dockerfile
new file mode 100644
index 000000000..4e6dadf4f
--- /dev/null
+++ b/docker/jammy/test-android-no-qt.Dockerfile
@@ -0,0 +1,105 @@
+#
+# Android SDK/NDK for testing Qbs
+#
+FROM ubuntu:jammy
+LABEL Description="Ubuntu test environment for Qbs for Android"
+
+# Allow colored output on command line.
+ENV TERM=xterm-color
+
+#
+# Make it possible to change UID/GID in the entrypoint script. The docker
+# container usually runs as root user on Linux hosts. When the Docker container
+# mounts a folder on the host and creates files there, those files would be
+# owned by root instead of the current user. Thus we create a user here who's
+# UID will be changed in the entrypoint script to match the UID of the current
+# host user.
+#
+ARG USER_UID=1000
+ARG USER_NAME=devel
+RUN apt-get update -qq && \
+ apt-get install -qq -y \
+ ca-certificates \
+ gosu \
+ sudo && \
+ groupadd -g ${USER_UID} ${USER_NAME} && \
+ useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \
+ usermod -a -G sudo ${USER_NAME} && \
+ echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers
+
+COPY docker/entrypoint.sh /sbin/entrypoint.sh
+ENTRYPOINT ["/sbin/entrypoint.sh"]
+
+# Qbs build dependencies
+RUN apt-get update -qq && \
+ apt-get install -qq -y --no-install-recommends \
+ ca-certificates \
+ curl \
+ libasan5 \
+ libglib2.0-0 \
+ openjdk-8-jdk-headless \
+ p7zip-full \
+ unzip
+
+ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
+RUN echo "export JAVA_HOME=${JAVA_HOME}" > /etc/profile.d/android.sh && \
+ echo "export PATH=${JAVA_HOME}/bin:\${PATH}" >> /etc/profile.d/android.sh
+
+ARG ANDROID_NDK_VERSION
+
+ENV ANDROID_HOME="/home/${USER_NAME}/android"
+ENV ANDROID_SDK_ROOT=${ANDROID_HOME}
+ENV ANDROID_NDK_ROOT=${ANDROID_HOME}/"ndk"/${ANDROID_NDK_VERSION}
+ENV PATH="${JAVA_HOME}:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/cmdline-tools/bin:$PATH"
+RUN echo "export ANDROID_HOME=/home/${USER_NAME}/android" >> /etc/profile.d/android.sh && \
+ echo "export ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" >> /etc/profile.d/android.sh && \
+ echo "export ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" >> /etc/profile.d/android.sh && \
+ echo "export PATH=${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/cmdline-tools/bin:\$PATH" >> /etc/profile.d/android.sh
+
+#
+# We ned to run the following steps as the target user
+#
+USER ${USER_NAME}
+RUN mkdir ${ANDROID_HOME}
+
+# Get Android command line tools
+ARG COMMAND_LINE_TOOLS_VERSION="6858069"
+RUN curl -s https://dl.google.com/android/repository/commandlinetools-linux-${COMMAND_LINE_TOOLS_VERSION}_latest.zip > ${ANDROID_HOME}/commandlinetools.zip && \
+ unzip ${ANDROID_HOME}/commandlinetools.zip -d ${ANDROID_HOME} && \
+ rm -v ${ANDROID_HOME}/commandlinetools.zip
+
+# Accept SDK license
+ARG ANDROID_PLATFORM="android-29"
+ARG BUILD_TOOLS="29.0.2"
+RUN yes | sdkmanager "--sdk_root=${ANDROID_HOME}" --verbose --licenses && \
+ sdkmanager "--sdk_root=${ANDROID_HOME}" --update && \
+ sdkmanager "--sdk_root=${ANDROID_HOME}" "platforms;${ANDROID_PLATFORM}"
+
+RUN yes | sdkmanager "--sdk_root=${ANDROID_HOME}" "build-tools;${BUILD_TOOLS}"
+RUN yes | sdkmanager "--sdk_root=${ANDROID_HOME}" "platform-tools"
+RUN yes | sdkmanager "--sdk_root=${ANDROID_HOME}" "tools"
+RUN yes | sdkmanager "--sdk_root=${ANDROID_HOME}" "ndk;${ANDROID_NDK_VERSION}"
+
+RUN /usr/lib/jvm/java-8-openjdk-amd64/bin/keytool -genkey -keystore /home/${USER_NAME}/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname 'CN=Android Debug,O=Android,C=US'
+
+# Install ndk samples in ${ANDROID_NDK_ROOT}/samples
+RUN cd ${ANDROID_NDK_ROOT} && \
+ curl -sLO https://github.com/android/ndk-samples/archive/master.zip && \
+ unzip -q master.zip && \
+ rm -v master.zip && \
+ mv ndk-samples-master samples
+
+# Install android-BasicMediaDecoder in ${ANDROID_SDK_ROOT}/samples
+RUN mkdir ${ANDROID_SDK_ROOT}/samples && \
+ cd ${ANDROID_SDK_ROOT}/samples && \
+ curl -sLO https://github.com/googlearchive/android-BasicMediaDecoder/archive/master.zip && \
+ unzip -q master.zip && \
+ rm -v master.zip && \
+ mv android-BasicMediaDecoder-master android-BasicMediaDecoder
+
+# Download buildtool to generate aab packages in ${ANDROID_SDK_ROOT}
+RUN cd ${ANDROID_SDK_ROOT} && \
+ curl -sLO https://github.com/google/bundletool/releases/download/1.3.0/bundletool-all-1.3.0.jar
+
+USER root
+
diff --git a/docker/jammy/test-android.Dockerfile b/docker/jammy/test-android.Dockerfile
new file mode 100644
index 000000000..b17cee65b
--- /dev/null
+++ b/docker/jammy/test-android.Dockerfile
@@ -0,0 +1,137 @@
+#
+# Android SDK/NDK + Qt for Android for testing Qbs
+#
+FROM ubuntu:jammy
+LABEL Description="Ubuntu test environment for Qbs and Qt for Android"
+
+# Allow colored output on command line.
+ENV TERM=xterm-color
+
+#
+# Make it possible to change UID/GID in the entrypoint script. The docker
+# container usually runs as root user on Linux hosts. When the Docker container
+# mounts a folder on the host and creates files there, those files would be
+# owned by root instead of the current user. Thus we create a user here who's
+# UID will be changed in the entrypoint script to match the UID of the current
+# host user.
+#
+ARG USER_UID=1000
+ARG USER_NAME=devel
+RUN apt-get update -qq && \
+ apt-get install -qq -y \
+ ca-certificates \
+ gosu \
+ sudo && \
+ groupadd -g ${USER_UID} ${USER_NAME} && \
+ useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \
+ usermod -a -G sudo ${USER_NAME} && \
+ echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers
+
+COPY docker/entrypoint.sh /sbin/entrypoint.sh
+ENTRYPOINT ["/sbin/entrypoint.sh"]
+
+# Qbs build dependencies
+RUN apt-get update -qq && \
+ apt-get install -qq -y --no-install-recommends \
+ ca-certificates \
+ curl \
+ libasan5 \
+ libglib2.0-0 \
+ locales \
+ openjdk-8-jdk-headless \
+ p7zip-full \
+ unzip
+
+# Set the locale
+RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
+ locale-gen
+ENV LANG en_US.UTF-8
+ENV LANGUAGE en_US:en
+ENV LC_ALL en_US.UTF-8
+
+ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
+RUN echo "export JAVA_HOME=${JAVA_HOME}" > /etc/profile.d/android.sh && \
+ echo "export PATH=${JAVA_HOME}/bin:\${PATH}" >> /etc/profile.d/android.sh
+
+ARG ANDROID_NDK_VERSION
+
+ENV ANDROID_HOME="/home/${USER_NAME}/android"
+ENV ANDROID_SDK_ROOT=${ANDROID_HOME}
+ENV ANDROID_NDK_ROOT=${ANDROID_HOME}/"ndk"/${ANDROID_NDK_VERSION}
+ENV PATH="${JAVA_HOME}:${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/cmdline-tools/bin:$PATH"
+RUN echo "export ANDROID_HOME=/home/${USER_NAME}/android" >> /etc/profile.d/android.sh && \
+ echo "export ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" >> /etc/profile.d/android.sh && \
+ echo "export ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" >> /etc/profile.d/android.sh && \
+ echo "export PATH=${ANDROID_HOME}/platform-tools:${ANDROID_HOME}/cmdline-tools/bin:\$PATH" >> /etc/profile.d/android.sh
+
+#
+# We ned to run the following steps as the target user
+#
+USER ${USER_NAME}
+RUN mkdir ${ANDROID_HOME}
+
+# Get Android command line tools
+ARG COMMAND_LINE_TOOLS_VERSION="6858069"
+RUN curl -s https://dl.google.com/android/repository/commandlinetools-linux-${COMMAND_LINE_TOOLS_VERSION}_latest.zip > ${ANDROID_HOME}/commandlinetools.zip && \
+ unzip ${ANDROID_HOME}/commandlinetools.zip -d ${ANDROID_HOME} && \
+ rm -v ${ANDROID_HOME}/commandlinetools.zip
+
+# Accept SDK license
+ARG ANDROID_PLATFORM="android-30"
+ARG BUILD_TOOLS="29.0.2"
+RUN yes | sdkmanager "--sdk_root=${ANDROID_HOME}" --verbose --licenses && \
+ sdkmanager "--sdk_root=${ANDROID_HOME}" --update && \
+ sdkmanager "--sdk_root=${ANDROID_HOME}" "platforms;${ANDROID_PLATFORM}"
+
+RUN yes | sdkmanager "--sdk_root=${ANDROID_HOME}" "build-tools;${BUILD_TOOLS}"
+RUN yes | sdkmanager "--sdk_root=${ANDROID_HOME}" "platform-tools"
+RUN yes | sdkmanager "--sdk_root=${ANDROID_HOME}" "tools"
+RUN yes | sdkmanager "--sdk_root=${ANDROID_HOME}" "ndk;${ANDROID_NDK_VERSION}"
+
+RUN /usr/lib/jvm/java-8-openjdk-amd64/bin/keytool -genkey -keystore /home/${USER_NAME}/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android -keyalg RSA -keysize 2048 -validity 10000 -dname 'CN=Android Debug,O=Android,C=US'
+
+# Install ndk samples in ${ANDROID_NDK_ROOT}/samples
+RUN cd ${ANDROID_NDK_ROOT} && \
+ curl -sLO https://github.com/android/ndk-samples/archive/master.zip && \
+ unzip -q master.zip && \
+ rm -v master.zip && \
+ mv ndk-samples-master samples
+
+# Install android-BasicMediaDecoder in ${ANDROID_SDK_ROOT}/samples
+RUN mkdir ${ANDROID_SDK_ROOT}/samples && \
+ cd ${ANDROID_SDK_ROOT}/samples && \
+ curl -sLO https://github.com/googlearchive/android-BasicMediaDecoder/archive/master.zip && \
+ unzip -q master.zip && \
+ rm -v master.zip && \
+ mv android-BasicMediaDecoder-master android-BasicMediaDecoder
+
+# Download buildtool to generate aab packages in ${ANDROID_SDK_ROOT}
+RUN cd ${ANDROID_SDK_ROOT} && \
+ curl -sLO https://github.com/google/bundletool/releases/download/1.3.0/bundletool-all-1.3.0.jar
+
+USER root
+
+#
+# Install Qt and Qbs for Linux from qt.io
+#
+ARG QT_VERSION
+COPY scripts/install-qt.sh install-qt.sh
+RUN if [ "${QT_VERSION}" \< "5.14" ] || [ ! "${QT_VERSION}" \< "6.0.0" ]; then \
+ QT_ABIS="android_armv7 android_arm64_v8a android_x86 android_x86_64"; \
+ else \
+ QT_ABIS="any"; \
+ fi; \
+ if [ ! "${QT_VERSION}" \< "6.0.0" ]; then \
+ ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative icu; \
+ if [ "${QT_VERSION}" \< "6.1.0" ]; then \
+ QT_COMPONENTS="qtbase qtdeclarative qttools qtquickcontrols2 qtquicktimeline svg"; \
+ else \
+ QT_COMPONENTS="qtbase qtdeclarative qttools qtquicktimeline svg"; \
+ fi; \
+ else \
+ QT_COMPONENTS="qtbase qtdeclarative qttools qtimageformats"; \
+ fi; \
+ for abi in ${QT_ABIS}; do \
+ ./install-qt.sh --version ${QT_VERSION} --target android --toolchain ${abi} ${QT_COMPONENTS}; \
+ done && \
+ echo "export QT_VERSION=${QT_VERSION}" >> /etc/profile.d/qt.sh
diff --git a/docker/jammy/test-baremetal.Dockerfile b/docker/jammy/test-baremetal.Dockerfile
new file mode 100644
index 000000000..774885797
--- /dev/null
+++ b/docker/jammy/test-baremetal.Dockerfile
@@ -0,0 +1,50 @@
+#
+# Baremetal toolchains for testing Qbs
+#
+FROM ubuntu:jammy
+LABEL Description="Ubuntu baremetal test environment for Qbs"
+
+# Allow colored output on command line.
+ENV TERM=xterm-color
+
+#
+# Make it possible to change UID/GID in the entrypoint script. The docker
+# container usually runs as root user on Linux hosts. When the Docker container
+# mounts a folder on the host and creates files there, those files would be
+# owned by root instead of the current user. Thus we create a user here who's
+# UID will be changed in the entrypoint script to match the UID of the current
+# host user.
+#
+ARG USER_UID=1000
+ARG USER_NAME=devel
+RUN apt-get update -qq && \
+ apt-get install -qq -y \
+ ca-certificates \
+ gosu \
+ sudo && \
+ groupadd -g ${USER_UID} ${USER_NAME} && \
+ useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \
+ usermod -a -G sudo ${USER_NAME} && \
+ echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers
+
+COPY docker/entrypoint.sh /sbin/entrypoint.sh
+ENTRYPOINT ["/sbin/entrypoint.sh"]
+
+# Install baremetal toolchains and Qbs runtime dependencies.
+RUN apt-get update -qq && \
+ apt-get install -qq -y \
+ libasan5 \
+ libglib2.0-0 \
+ libgssapi-krb5-2 \
+ libgl1-mesa-glx \
+ gcc-arm-none-eabi \
+ gcc-avr \
+ avr-libc \
+ gcc-msp430 \
+ sdcc \
+ binutils-xtensa-lx106 \
+ gcc-xtensa-lx106 \
+ gcc-riscv64-unknown-elf
+
+# Work-around for QTBUG-79020.
+RUN echo "export QT_NO_GLIB=1" >> /etc/profile.d/qt.sh
diff --git a/docker/jammy/test-qt4.Dockerfile b/docker/jammy/test-qt4.Dockerfile
new file mode 100644
index 000000000..9fa8f5d6b
--- /dev/null
+++ b/docker/jammy/test-qt4.Dockerfile
@@ -0,0 +1,40 @@
+#
+# Testing Qbs with qt4
+#
+FROM ubuntu:jammy
+LABEL Description="Ubuntu qt4 test environment for Qbs"
+
+# Allow colored output on command line.
+ENV TERM=xterm-color
+
+#
+# Make it possible to change UID/GID in the entrypoint script. The docker
+# container usually runs as root user on Linux hosts. When the Docker container
+# mounts a folder on the host and creates files there, those files would be
+# owned by root instead of the current user. Thus we create a user here who's
+# UID will be changed in the entrypoint script to match the UID of the current
+# host user.
+#
+ARG USER_UID=1000
+ARG USER_NAME=devel
+RUN apt-get update -qq && \
+ apt-get install -qq -y \
+ ca-certificates \
+ gosu \
+ software-properties-common \
+ sudo && \
+ groupadd -g ${USER_UID} ${USER_NAME} && \
+ useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \
+ usermod -a -G sudo ${USER_NAME} && \
+ echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers
+
+COPY docker/entrypoint.sh /sbin/entrypoint.sh
+ENTRYPOINT ["/sbin/entrypoint.sh"]
+
+# Install baremetal toolchains and Qbs runtime dependencies.
+RUN sudo add-apt-repository ppa:ubuntuhandbook1/ppa -y && \
+ apt-get update -qq && \
+ apt-get install -qq -y \
+ build-essential \
+ libqt4-dev \
+ qt4-dev-tools
diff --git a/docker/jammy/test-qt6-static.Dockerfile b/docker/jammy/test-qt6-static.Dockerfile
new file mode 100644
index 000000000..37239fd9a
--- /dev/null
+++ b/docker/jammy/test-qt6-static.Dockerfile
@@ -0,0 +1,221 @@
+#
+# Testing Qbs with static qt6
+#
+FROM ubuntu:jammy
+LABEL Description="Ubuntu static qt6 test environment for Qbs"
+ARG QT_VERSION
+ARG QTCREATOR_VERSION
+
+# Allow colored output on command line.
+ENV TERM=xterm-color
+
+#
+# Make it possible to change UID/GID in the entrypoint script. The docker
+# container usually runs as root user on Linux hosts. When the Docker container
+# mounts a folder on the host and creates files there, those files would be
+# owned by root instead of the current user. Thus we create a user here who's
+# UID will be changed in the entrypoint script to match the UID of the current
+# host user.
+#
+ARG USER_UID=1000
+ARG USER_NAME=devel
+RUN apt-get update -qq && \
+ apt-get install -qq -y \
+ wget \
+ ca-certificates \
+ gosu \
+ software-properties-common \
+ sudo && \
+ groupadd -g ${USER_UID} ${USER_NAME} && \
+ useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \
+ usermod -a -G sudo ${USER_NAME} && \
+ echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers
+
+COPY docker/entrypoint.sh /sbin/entrypoint.sh
+ENTRYPOINT ["/sbin/entrypoint.sh"]
+
+RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
+RUN echo 'deb [signed-by=/usr/share/keyrings/kitware-archive-keyring.gpg] https://apt.kitware.com/ubuntu/ jammy main' | tee /etc/apt/sources.list.d/kitware.list >/dev/null
+RUN cat /etc/apt/sources.list.d/kitware.list
+
+RUN apt-get update -qq && \
+ DEBIAN_FRONTEND="noninteractive" apt-get install -qq -y --no-install-recommends \
+ build-essential \
+ git \
+ perl \
+ clang-15 \
+ cmake \
+ python3 \
+ zlib1g-dev \
+ libzstd-dev \
+ libdbus-1-dev \
+ libglib2.0-dev \
+ libssl-dev \
+ libdrm-dev \
+ libegl-dev \
+ libwayland-dev \
+ libvulkan-dev \
+ libicu-dev \
+ libb2-dev \
+ libclang-15-dev \
+ libsystemd-dev \
+ libfontconfig1-dev \
+ libfreetype6-dev \
+ libx11-dev \
+ libx11-xcb-dev \
+ libxext-dev \
+ libxfixes-dev \
+ libxi-dev \
+ libxrender-dev \
+ libxcb1-dev \
+ libxcb-glx0-dev \
+ libxcb-keysyms1-dev \
+ libxcb-image0-dev \
+ libxcb-shm0-dev \
+ libxcb-icccm4-dev \
+ libxcb-sync0-dev \
+ libxcb-xfixes0-dev \
+ libxcb-shape0-dev \
+ libxcb-randr0-dev \
+ libxcb-render-util0-dev \
+ libxcb-xinerama0-dev \
+ libxkbcommon-dev \
+ libxkbcommon-x11-dev \
+ libxcb-xinput-dev \
+ ninja-build \
+ libudev-dev \
+ libxcb-cursor-dev \
+ libxcb-composite0-dev \
+ libxcb-dri2-0-dev \
+ libxcb-dri3-dev \
+ libxcb-ewmh-dev \
+ libxcb-present-dev \
+ libxcb-present-dev \
+ libxcb-record0-dev \
+ libxcb-res0-dev \
+ libxcb-screensaver0-dev \
+ libxcb-sync-dev \
+ libxcb-util-dev \
+ libxcb-xf86dri0-dev \
+ libxcb-xtest0-dev \
+ libxcb-xv0-dev \
+ libxcb-xvmc0-dev \
+ libxcb-damage0-dev \
+ libxcb-dpms0-dev \
+ libgstreamer1.0-dev \
+ llvm-15-dev \
+ apt-transport-https
+
+ENV QT_HOME="/home/${USER_NAME}/qt"
+USER ${USER_NAME}
+RUN mkdir ${QT_HOME}
+
+RUN cd ${QT_HOME} && git clone https://code.qt.io/qt/qt5.git
+
+RUN cd ${QT_HOME}/qt5 && git checkout v${QT_VERSION} && perl init-repository
+
+RUN mkdir ${QT_HOME}/static-build && cd ${QT_HOME}/static-build && ../qt5/configure -prefix /opt/Qt/${QT_VERSION}/gcc_64
+
+RUN cd ${QT_HOME}/static-build && cmake --build . --parallel
+
+USER root
+
+RUN cd ${QT_HOME}/static-build && cmake --install .
+
+FROM ubuntu:jammy
+LABEL Description="Ubuntu static qt6 test environment for Qbs"
+ARG QT_VERSION
+ARG QTCREATOR_VERSION
+
+RUN mkdir -p /opt/Qt
+COPY --from=0 /opt/Qt/${QT_VERSION} /opt/Qt/${QT_VERSION}
+
+# Allow colored output on command line.
+ENV TERM=xterm-color
+ARG USER_UID=1000
+ARG USER_NAME=devel
+RUN apt-get update -qq && \
+ apt-get install -qq -y \
+ ca-certificates \
+ gosu \
+ software-properties-common \
+ sudo && \
+ groupadd -g ${USER_UID} ${USER_NAME} && \
+ useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \
+ usermod -a -G sudo ${USER_NAME} && \
+ echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers
+
+COPY docker/focal/entrypoint.sh /sbin/entrypoint.sh
+ENTRYPOINT ["/sbin/entrypoint.sh"]
+
+# Qbs build dependencies
+RUN apt-get update -qq && \
+ DEBIAN_FRONTEND="noninteractive" apt-get install -qq -y --no-install-recommends \
+ bison \
+ build-essential \
+ ca-certificates \
+ capnproto \
+ ccache \
+ clang-15 \
+ clang-tidy-15 \
+ cmake \
+ curl \
+ flex \
+ git \
+ help2man \
+ icoutils \
+ libcapnp-dev \
+ libdbus-1-3 \
+ libfreetype6 \
+ libfontconfig1 \
+ libgl1-mesa-dev \
+ libgl1-mesa-glx \
+ libnanopb-dev \
+ libprotobuf-dev \
+ libgrpc++-dev \
+ libxkbcommon-x11-0 \
+ nanopb \
+ ninja-build \
+ nsis \
+ pkg-config \
+ protobuf-compiler \
+ protobuf-compiler-grpc \
+ psmisc \
+ python3-pip \
+ python3-setuptools \
+ p7zip-full \
+ subversion \
+ unzip \
+ zip \
+ libb2-1 \
+ libpcre++ && \
+ update-alternatives --install /usr/bin/clang clang /usr/bin/clang-15 100 && \
+ update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-15 100 && \
+ update-alternatives --install /usr/bin/clang-check clang-check /usr/bin/clang-check-15 100 && \
+ update-alternatives --install /usr/bin/python python /usr/bin/python3 100 && \
+ pip install beautifulsoup4 lxml protobuf pyyaml
+
+ENV LLVM_INSTALL_DIR=/usr/lib/llvm-15
+
+#
+# Install Qbs for Linux from qt.io
+#
+COPY scripts/install-qt.sh install-qt.sh
+
+RUN ./install-qt.sh --version ${QTCREATOR_VERSION} qtcreator && \
+ echo "export PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:\${PATH}" > /etc/profile.d/qt.sh
+
+ENV PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:${PATH}
+
+# Configure Qbs
+USER $USER_NAME
+RUN qbs-setup-toolchains /usr/bin/g++ gcc && \
+ qbs-setup-qt /opt/Qt/${QT_VERSION}/gcc_64/bin/qmake qt-gcc_64 && \
+ qbs config profiles.qt-gcc_64.baseProfile gcc && \
+ qbs config defaultProfile qt-gcc_64
+
+# Switch back to root user for the entrypoint script.
+USER root
+
+# Work-around for QTBUG-79020
+RUN echo "export QT_NO_GLIB=1" >> /etc/profile.d/qt.sh
diff --git a/docker/leap/Dockerfile b/docker/leap/Dockerfile
new file mode 100644
index 000000000..12f789e21
--- /dev/null
+++ b/docker/leap/Dockerfile
@@ -0,0 +1,96 @@
+#
+# Install Qt and Qbs for Linux
+#
+FROM opensuse/leap:15.3
+LABEL Description="OpenSUSE development environment for Qbs with Qt and various dependencies for testing Qbs modules and functionality"
+ARG QT_VERSION
+ARG QTCREATOR_VERSION
+
+# Allow colored output on command line.
+ENV TERM=xterm-color
+
+#
+# Make it possible to change UID/GID in the entrypoint script. The docker
+# container usually runs as root user on Linux hosts. When the Docker container
+# mounts a folder on the host and creates files there, those files would be
+# owned by root instead of the current user. Thus we create a user here who's
+# UID will be changed in the entrypoint script to match the UID of the current
+# host user.
+#
+ARG USER_UID=1000
+ARG USER_NAME=devel
+RUN zypper in -y \
+ ca-certificates \
+ sudo \
+ system-user-mail \
+ system-group-wheel && \
+ groupadd -g ${USER_UID} ${USER_NAME} && \
+ useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \
+ usermod -a -G wheel ${USER_NAME} && \
+ echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers
+
+COPY docker/leap/entrypoint.sh /sbin/entrypoint.sh
+ENTRYPOINT ["/sbin/entrypoint.sh"]
+
+# # Qbs build dependencies
+RUN zypper install -y \
+ bison \
+ capnproto \
+ ccache \
+ cmake \
+ command-not-found \
+ curl \
+ gcc10 \
+ gcc10-c++ \
+ glibc-devel-static \
+ flex \
+ fontconfig \
+ git \
+ gzip \
+ help2man \
+ icoutils \
+ libcapnp-devel \
+ libgthread-2_0-0 \
+ libfreetype6 \
+ Mesa-libGL-devel \
+ Mesa-libGL1 \
+ nanopb-devel \
+ ninja \
+ perl \
+ pkg-config \
+ psmisc \
+ python3-pip \
+ p7zip-full \
+ subversion \
+ tar \
+ unzip \
+ which \
+ zip && \
+ update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100 && \
+ update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100 && \
+ pip install --upgrade pip && \
+ pip install beautifulsoup4 lxml
+
+#
+# Install Qt and Qbs for Linux from qt.io
+#
+COPY scripts/install-qt.sh install-qt.sh
+
+RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qttools qtx11extras qtscxml qt5compat icu && \
+ ./install-qt.sh --version ${QTCREATOR_VERSION} qtcreator && \
+ echo "export PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:\${PATH}" > /etc/profile.d/qt.sh
+
+ENV PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:${PATH}
+
+# Configure Qbs
+USER $USER_NAME
+RUN qbs-setup-toolchains /usr/bin/g++ gcc && \
+ qbs-setup-qt /opt/Qt/${QT_VERSION}/gcc_64/bin/qmake qt-gcc_64 && \
+ qbs config profiles.qt-gcc_64.baseProfile gcc && \
+ qbs config defaultProfile qt-gcc_64
+
+# Switch back to root user for the entrypoint script.
+USER root
+
+# Work-around for QTBUG-79020
+RUN echo "export QT_NO_GLIB=1" >> /etc/profile.d/qt.sh
diff --git a/docker/leap/entrypoint.sh b/docker/leap/entrypoint.sh
new file mode 100755
index 000000000..a34926ca4
--- /dev/null
+++ b/docker/leap/entrypoint.sh
@@ -0,0 +1,96 @@
+#!/usr/bin/env bash
+set -e
+
+#############################################################################
+##
+## Copyright (C) 2019 Richard Weickelt <richard@weickelt.de>
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+#
+# Entrypoint script when starting the container. The script checks the current
+# working directory and changes the uid/gid of developer/users to match whatever
+# is found in the working directory. This is useful to match the user and group
+# of mounted volumes into the container
+
+#
+# If not root, re-run script as root to fix ids
+#
+if [ "$(id -u)" != "0" ]; then
+ exec sudo -i /sbin/entrypoint.sh "$@"
+fi
+
+#
+# Try to determine the uid of the working directory and adjust the current
+# user's uid/gid accordingly.
+#
+USER_GID=${USER_GID:-$(stat -c "%g" .)}
+USER_UID=${USER_UID:-$(stat -c "%u" .)}
+USER_NAME=${USER_NAME:-devel}
+USER_GROUP=${USER_GROUP:-devel}
+EXEC=""
+export HOME=/home/${USER_NAME}
+
+#
+# This is a problem on Linux hosts when we mount a folder from the
+# user file system and write artifacts into that. Thus, we downgrade
+# the current user and make sure that the uid and gid matches the one
+# of the mounted project folder.
+#
+# This work-around is not needed on Windows hosts as Windows doesn't
+# have such a concept.
+#
+if [ "${USER_UID}" != "0" ]; then
+ if [ "$(id -u ${USER_NAME})" != "${USER_UID}" ]; then
+ usermod -o -u ${USER_UID} ${USER_NAME}
+ # After changing the user's uid, all files in user's home directory
+ # automatically get the new uid.
+ fi
+ current_gid=$(id -g ${USER_NAME})
+ if [ "$(id -g ${USER_NAME})" != "${USER_GID}" ]; then
+ groupmod -o -g ${USER_GID} ${USER_GROUP}
+ # Set the new gid on all files in the home directory that still have the
+ # old gid.
+ find /home/${USER_NAME} -gid "${current_gid}" ! -type l -exec chgrp ${USER_GID} {} \;
+ fi
+fi
+EXEC="exec sudo -u ${USER_NAME} -g ${USER_GROUP}"
+
+if [ -z "$1" ]; then
+ ${EXEC} bash -l
+else
+ ${EXEC} bash -l -c "$*"
+fi
diff --git a/docker/stretch/Dockerfile b/docker/stretch/Dockerfile
deleted file mode 100644
index 736bd1ef7..000000000
--- a/docker/stretch/Dockerfile
+++ /dev/null
@@ -1,73 +0,0 @@
-#
-# Downloads and builds Qt from source. This is simpler than using the Qt online
-# installer. We do it in a separate stage to keep the number of dependencies low
-# in the final Docker image.
-#
-FROM debian:9
-LABEL Description="Debian development environment for Qbs with Qt and various dependencies for testing Qbs modules and functionality"
-ARG QT_VERSION
-ARG QTCREATOR_VERSION
-
-# Allow colored output on command line.
-ENV TERM=xterm-color
-
-#
-# Make it possible to change UID/GID in the entrypoint script. The docker
-# container usually runs as root user on Linux hosts. When the Docker container
-# mounts a folder on the host and creates files there, those files would be
-# owned by root instead of the current user. Thus we create a user here who's
-# UID will be changed in the entrypoint script to match the UID of the current
-# host user.
-#
-ARG USER_UID=1000
-ARG USER_NAME=devel
-RUN apt-get update -qq && \
- apt-get install -qq -y --no-install-recommends \
- ca-certificates \
- gosu \
- sudo && \
- groupadd -g ${USER_UID} ${USER_NAME} && \
- useradd -s /bin/bash -u ${USER_UID} -g ${USER_NAME} -o -c "" -m ${USER_NAME} && \
- usermod -a -G sudo ${USER_NAME} && \
- echo "%devel ALL = (ALL) NOPASSWD: ALL" >> /etc/sudoers
-
-COPY docker/stretch/entrypoint.sh entrypoint.sh
-ENTRYPOINT ["/entrypoint.sh"]
-
-
-# Build and run dependencies for Qbs
-RUN apt-get install -qq -y --no-install-recommends \
- build-essential \
- curl \
- git \
- help2man \
- libclang-3.9 \
- libdbus-1-3 \
- libgl1-mesa-glx \
- libfreetype6 \
- libfontconfig1 \
- libgl1-mesa-dev \
- make \
- pkg-config \
- python-pip \
- p7zip-full && \
- pip install beautifulsoup4 lxml # for building the documentation
-
-ENV LLVM_INSTALL_DIR=/usr/lib/llvm-3.9
-
-COPY scripts/install-qt.sh install-qt.sh
-
-RUN ./install-qt.sh --version ${QT_VERSION} qtbase qtdeclarative qtscript qttools qtx11extras icu && \
- ./install-qt.sh --version ${QTCREATOR_VERSION} qtcreator && \
- echo "export PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:\${PATH}" > /etc/profile.d/qt.sh
-
-ENV PATH=/opt/Qt/${QT_VERSION}/gcc_64/bin:/opt/Qt/Tools/QtCreator/bin:${PATH}
-
-# Configure Qbs
-USER $USER_NAME
-RUN qbs-setup-toolchains --detect && \
- qbs-setup-qt /opt/Qt/${QT_VERSION}/gcc_64/bin/qmake qt && \
- qbs config defaultProfile qt
-
-# Switch back to root user for the entrypoint script.
-USER root
diff --git a/docker/stretch/entrypoint.sh b/docker/stretch/entrypoint.sh
deleted file mode 100755
index 04504ffcc..000000000
--- a/docker/stretch/entrypoint.sh
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env bash
-set -e
-
-#############################################################################
-##
-## Copyright (C) 2019 Richard Weickelt <richard@weickelt.de>
-## Contact: https://www.qt.io/licensing/
-##
-## This file is part of Qbs.
-##
-## $QT_BEGIN_LICENSE:LGPL$
-## Commercial License Usage
-## Licensees holding valid commercial Qt licenses may use this file in
-## accordance with the commercial license agreement provided with the
-## Software or, alternatively, in accordance with the terms contained in
-## a written agreement between you and The Qt Company. For licensing terms
-## and conditions see https://www.qt.io/terms-conditions. For further
-## information use the contact form at https://www.qt.io/contact-us.
-##
-## GNU Lesser General Public License Usage
-## Alternatively, this file may be used under the terms of the GNU Lesser
-## General Public License version 3 as published by the Free Software
-## Foundation and appearing in the file LICENSE.LGPL3 included in the
-## packaging of this file. Please review the following information to
-## ensure the GNU Lesser General Public License version 3 requirements
-## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-##
-## GNU General Public License Usage
-## Alternatively, this file may be used under the terms of the GNU
-## General Public License version 2.0 or (at your option) the GNU General
-## Public license version 3 or any later version approved by the KDE Free
-## Qt Foundation. The licenses are as published by the Free Software
-## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-## included in the packaging of this file. Please review the following
-## information to ensure the GNU General Public License requirements will
-## be met: https://www.gnu.org/licenses/gpl-2.0.html and
-## https://www.gnu.org/licenses/gpl-3.0.html.
-##
-## $QT_END_LICENSE$
-##
-#############################################################################
-
-#
-# Try to determine the uid of the working directory and adjust the current
-# user's uid/gid accordingly.
-#
-WORKDIR_GID=$(stat -c "%g" .)
-WORKDIR_UID=$(stat -c "%u" .)
-USER_NAME=${USER_NAME:-devel}
-EXEC=""
-
-if [ "$(id -u ${USER_NAME})" != "0" ] && [ "${WORKDIR_UID}" != "0" ] ; then
-
- export HOME=/home/${USER_NAME}
-
- if [ "$(id -u ${USER_NAME})" != "${WORKDIR_UID}" ]; then
- usermod -u ${WORKDIR_UID} ${USER_NAME}
- groupmod -g ${WORKDIR_GID} ${USER_NAME}
- chown -R -h ${WORKDIR_UID} /home;
- chgrp -R -h ${WORKDIR_GID} /home;
- fi
- EXEC="exec gosu ${USER_NAME}:${USER_NAME}"
-fi
-
-if [ -z "$1" ]; then
- ${EXEC} bash --login
-else
- ${EXEC} bash --login -c "$*"
-fi
diff --git a/docker/windowsservercore/Dockerfile b/docker/windowsservercore/Dockerfile
index 43cf93736..8fce9fa59 100644
--- a/docker/windowsservercore/Dockerfile
+++ b/docker/windowsservercore/Dockerfile
@@ -1,49 +1,54 @@
-FROM microsoft/windowsservercore:10.0.14393.1884 as build-env
+
+FROM mcr.microsoft.com/windows/servercore:1809
LABEL Description="Windows Server Core development environment for Qbs with Qt, Chocolatey and various dependencies for testing Qbs modules and functionality"
# Disable crash dialog for release-mode runtimes
RUN reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v Disabled /t REG_DWORD /d 1 /f
RUN reg add "HKLM\SOFTWARE\Microsoft\Windows\Windows Error Reporting" /v DontShowUI /t REG_DWORD /d 1 /f
-RUN @powershell -NoProfile -ExecutionPolicy Bypass -Command \
- $Env:chocolateyVersion = '0.10.8' ; \
+# Install VS 2019 from the website since chocolatey has broken .NET 4.8 (dotnetfx package) which is a
+# dependency for the visualstudio2019buildtools package
+RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \
+ Invoke-WebRequest "https://aka.ms/vs/16/release/vs_community.exe" \
+ -OutFile "%TEMP%\vs_community.exe" -UseBasicParsing
+
+RUN "%TEMP%\vs_community.exe" --quiet --wait --norestart --noUpdateInstaller \
+ --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 \
+ --add Microsoft.VisualStudio.Component.Windows10SDK.20348
+
+RUN powershell -NoProfile -ExecutionPolicy Bypass -Command \
+ $Env:chocolateyVersion = '0.10.15' ; \
$Env:chocolateyUseWindowsCompression = 'false' ; \
- "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
-RUN choco install -y python2 --version 2.7.14 && refreshenv && python --version && pip --version
-RUN choco install -y vcredist140
-RUN choco install -y qbs --version 1.12.1 && qbs --version
-RUN choco install -y visualcpp-build-tools --version 14.0.25420.1 && dir "%PROGRAMFILES(X86)%\Microsoft Visual C++ Build Tools"
-RUN choco install -y unzip --version 6.0
-RUN choco install -y zip --version 3.0
-
-
-FROM build-env as qt-build-env
-LABEL Description="Environment for Building Qt"
-RUN choco install -y jom --version 1.1.2 && jom /version
-
-# Download and extract Qt source archives
-WORKDIR C:/
-RUN md src
-WORKDIR src
-COPY build-qt.bat .
-ADD http://download.qt.io/official_releases/qt/5.12/5.12.5/submodules/qtbase-everywhere-src-5.12.5.zip qtbase.zip
-ADD http://download.qt.io/official_releases/qt/5.12/5.12.5/submodules/qtscript-everywhere-src-5.12.5.zip qtscript.zip
-ADD http://download.qt.io/official_releases/qt/5.12/5.12.5/submodules/qttools-everywhere-src-5.12.5.zip qttools.zip
-RUN unzip qtbase.zip
-RUN unzip qtscript.zip
-RUN unzip qttools.zip
-RUN rename qtbase-everywhere-src-5.12.5 qtbase
-RUN rename qtscript-everywhere-src-5.12.5 qtscript
-RUN rename qttools-everywhere-src-5.12.5 qttools
-RUN build-qt.bat x86
-RUN build-qt.bat x64
-
-
-FROM build-env as qbs-build-env
-WORKDIR C:/
-COPY --from=qt-build-env C:/Qt C:/Qt/
-ENV QTDIR C:\\Qt\\x86
-ENV QTDIR64 C:\\Qt\\x64
+ "[Net.ServicePointManager]::SecurityProtocol = \"tls12, tls11, tls\"; iex ((New-Object System.Net.WebClient).DownloadString('http://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
+
+RUN choco install -y python --version 3.9 && \
+ choco install -y 7zip --version 19.0 && \
+ choco install -y git --version 2.24.0 --params "/GitAndUnixToolsOnPath" && \
+ choco install -y vswhere
# for building the documentation
RUN pip install beautifulsoup4 lxml
+
+# clcache for speeding up MSVC builds
+ENV CLCACHE_DIR="C:/.ccache"
+RUN certutil -generateSSTFromWU roots.sst && \
+ certutil -addstore -f root roots.sst && \
+ del roots.sst && \
+ pip install --trusted-host=pypi.org \
+ git+https://github.com/frerich/clcache.git@cae73d8255d78db8ba11e23c51fd2c9a89e7475b
+
+########### Install Qt #############
+ARG QT_VERSION
+COPY scripts/install-qt.sh install-qt.sh
+
+RUN bash -c "./install-qt.sh -d /c/Qt --version ${QT_VERSION} --toolchain win64_msvc2019_64 qtbase qtdeclarative qttools qt5compat"
+ENV QTDIR64=C:\\Qt\\${QT_VERSION}\\msvc2019_64
+
+########### Install Qbs #############
+ARG QTCREATOR_VERSION
+RUN bash -c "./install-qt.sh -d /c/Qt --version ${QTCREATOR_VERSION} qtcreator"
+RUN setx PATH "C:\\Qt\\Tools\\QtCreator\\bin;%PATH%"
+
+RUN qbs setup-toolchains --detect && \
+ qbs setup-qt %QTDIR64%/bin/qmake.exe qt64 && \
+ qbs config defaultProfile qt64
diff --git a/docker/windowsservercore/build-qt.bat b/docker/windowsservercore/build-qt.bat
deleted file mode 100644
index 0d7f432e6..000000000
--- a/docker/windowsservercore/build-qt.bat
+++ /dev/null
@@ -1,23 +0,0 @@
-setlocal
-set ARCH=%1
-call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %ARCH%
-md %ARCH%
-cd %ARCH%
-md qtbase
-cd qtbase
-call C:\src\qtbase\configure -opensource -confirm-license -prefix C:\Qt\%ARCH% -opengl desktop -release -nomake tests -nomake examples
-jom
-jom install
-cd ..
-md qttools\src\windeployqt
-cd qttools\src\windeployqt
-C:\Qt\%ARCH%\bin\qmake C:\src\qttools\src\windeployqt\windeployqt.pro
-jom
-jom install
-cd ..
-md qtscript
-cd qtscript
-C:\Qt\%ARCH%\bin\qmake C:\src\qtscript\qtscript.pro
-jom
-jom install
-endlocal
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 000000000..8170f37a2
--- /dev/null
+++ b/examples/app-and-lib/app-and-lib.qbs
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+Project {
+ references: [
+ "app/app.qbs",
+ "lib/lib.qbs"
+ ]
+}
+
diff --git a/examples/app-and-lib/app/app.qbs b/examples/app-and-lib/app/app.qbs
index 26f9774af..1cdb95c39 100644
--- a/examples/app-and-lib/app/app.qbs
+++ b/examples/app-and-lib/app/app.qbs
@@ -48,13 +48,11 @@
**
****************************************************************************/
-import qbs 1.0
-
-Product {
- type: "application"
+Application {
consoleApplication: true
files : [ "main.cpp" ]
Depends { name: "cpp" }
Depends { name: "mylib" }
+ install: true
}
diff --git a/examples/app-and-lib/app/main.cpp b/examples/app-and-lib/app/main.cpp
index 17f4226f3..dc02fa8ef 100644
--- a/examples/app-and-lib/app/main.cpp
+++ b/examples/app-and-lib/app/main.cpp
@@ -53,6 +53,6 @@
int main()
{
- puts("Now calling a function from mylib:");
+ std::puts("Now calling a function from mylib:");
return bla();
}
diff --git a/examples/app-and-lib/app_and_lib.qbs b/examples/app-and-lib/app_and_lib.qbs
deleted file mode 100644
index baa42307f..000000000
--- a/examples/app-and-lib/app_and_lib.qbs
+++ /dev/null
@@ -1,59 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples 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$
-**
-****************************************************************************/
-
-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
index bfa62ecf7..a1b30a0d2 100644
--- a/examples/app-and-lib/lib/lib.cpp
+++ b/examples/app-and-lib/lib/lib.cpp
@@ -56,6 +56,6 @@
int bla()
{
- puts("Hello World!");
+ std::puts("Hello World!");
return 2;
}
diff --git a/examples/app-and-lib/lib/lib.qbs b/examples/app-and-lib/lib/lib.qbs
index 5c0098dae..a5fbc6fb8 100644
--- a/examples/app-and-lib/lib/lib.qbs
+++ b/examples/app-and-lib/lib/lib.qbs
@@ -48,10 +48,7 @@
**
****************************************************************************/
-import qbs 1.0
-
-Product {
- type: "staticlibrary"
+StaticLibrary {
name: "mylib"
files: [
"lib.cpp",
@@ -62,7 +59,7 @@ Product {
Export {
Depends { name: "cpp" }
- cpp.includePaths: [product.sourceDirectory]
+ cpp.includePaths: [exportingProduct.sourceDirectory]
}
}
diff --git a/examples/baremetal/at90can128olimex/at90can128olimex.qbs b/examples/baremetal/at90can128olimex/at90can128olimex.qbs
index dc1760fe9..2c206f7cc 100644
--- a/examples/baremetal/at90can128olimex/at90can128olimex.qbs
+++ b/examples/baremetal/at90can128olimex/at90can128olimex.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
Project {
name: "Examples for Olimex AVR-CAN board"
references: [
diff --git a/examples/baremetal/at90can128olimex/redblink/README.md b/examples/baremetal/at90can128olimex/redblink/README.md
index 0e9710e57..cbf51898d 100644
--- a/examples/baremetal/at90can128olimex/redblink/README.md
+++ b/examples/baremetal/at90can128olimex/redblink/README.md
@@ -1,4 +1,4 @@
-This examples demonstrates how to build a bare-metal application using
+This example demonstrates how to build a bare-metal application using
different AVR toolchains. It is designed for the OLIMEX AVR-CAN target
board (based on at90can128 chip) and simply flashes the red LED on the
board.
diff --git a/examples/baremetal/at90can128olimex/redblink/redblink.qbs b/examples/baremetal/at90can128olimex/redblink/redblink.qbs
index 84323e3c1..afdbe46f7 100644
--- a/examples/baremetal/at90can128olimex/redblink/redblink.qbs
+++ b/examples/baremetal/at90can128olimex/redblink/redblink.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
CppApplication {
condition: {
if (!qbs.architecture.contains("avr"))
diff --git a/examples/baremetal/baremetal.qbs b/examples/baremetal/baremetal.qbs
index eba504512..0d71968b6 100644
--- a/examples/baremetal/baremetal.qbs
+++ b/examples/baremetal/baremetal.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
Project {
name: "BareMetal"
references: [
@@ -58,6 +56,10 @@ Project {
"cc2540usbdongle/cc2540usbdongle.qbs",
"stm8s103f3/stm8s103f3.qbs",
"msp430f5529/msp430f5529.qbs",
- "cy7c68013a/cy7c68013a.qbs"
+ "cy7c68013a/cy7c68013a.qbs",
+ "stm32f103/stm32f103.qbs",
+ "pca10040/pca10040.qbs",
+ "pca10001/pca10001.qbs",
+ "esp8266/esp8266.qbs",
]
}
diff --git a/examples/baremetal/cc2540usbdongle/cc2540usbdongle.qbs b/examples/baremetal/cc2540usbdongle/cc2540usbdongle.qbs
index 52e1948eb..6cb99e6a4 100644
--- a/examples/baremetal/cc2540usbdongle/cc2540usbdongle.qbs
+++ b/examples/baremetal/cc2540usbdongle/cc2540usbdongle.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
Project {
name: "Examples for cc2540usbdongle board"
references: [
diff --git a/examples/baremetal/cc2540usbdongle/greenblink/greenblink.qbs b/examples/baremetal/cc2540usbdongle/greenblink/greenblink.qbs
index e8aa3d1fd..d1d3281a9 100644
--- a/examples/baremetal/cc2540usbdongle/greenblink/greenblink.qbs
+++ b/examples/baremetal/cc2540usbdongle/greenblink/greenblink.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
CppApplication {
condition: {
if (!qbs.architecture.contains("mcs51"))
diff --git a/examples/baremetal/cc2540usbdongle/greenblink/system.h b/examples/baremetal/cc2540usbdongle/greenblink/system.h
index cdc6302d4..4f105dbd0 100644
--- a/examples/baremetal/cc2540usbdongle/greenblink/system.h
+++ b/examples/baremetal/cc2540usbdongle/greenblink/system.h
@@ -66,6 +66,9 @@ extern "C" {
#elif defined (__SDCC_mcs51)
#include <mcs51/compiler.h>
# define DEFINE_SFR(name, addr) __sfr __at(addr) name;
+# ifndef NOP
+# define NOP() __asm NOP __endasm
+# endif
# define system_nop() NOP()
#else
#error "Unsupported toolchain"
diff --git a/examples/baremetal/cy7c68013a/cy7c68013a.qbs b/examples/baremetal/cy7c68013a/cy7c68013a.qbs
index 370844725..dca261d70 100644
--- a/examples/baremetal/cy7c68013a/cy7c68013a.qbs
+++ b/examples/baremetal/cy7c68013a/cy7c68013a.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
Project {
name: "Examples for cy7c68013a board"
references: [
diff --git a/examples/baremetal/cy7c68013a/nes-gamepads/nes-gamepads.qbs b/examples/baremetal/cy7c68013a/nes-gamepads/nes-gamepads.qbs
index 0d7994033..81566ff96 100644
--- a/examples/baremetal/cy7c68013a/nes-gamepads/nes-gamepads.qbs
+++ b/examples/baremetal/cy7c68013a/nes-gamepads/nes-gamepads.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
CppApplication {
condition: {
if (!qbs.architecture.contains("mcs51"))
diff --git a/examples/baremetal/esp8266/README.md b/examples/baremetal/esp8266/README.md
new file mode 100644
index 000000000..a9b4ef92d
--- /dev/null
+++ b/examples/baremetal/esp8266/README.md
@@ -0,0 +1,34 @@
+This example demonstrates how to build a bare-metal application using
+GCC toolchain for Espressif ESP8266-WROOM02 MCU.
+
+An application is designed to be flashed on the external SPI FLASH
+with the following parameters:
+
+ * SPI FLASH size: 16 MBit (2048 + 2048 KBytes)
+ * SPI FLASH mode: DIO
+ * SPI FLASH clock: 40 MHz
+ * SPI FLASH map type: 5
+
+An application is running as WiFi access point that discards all
+connection attempts, and has the following WiFi parameters:
+ * SSID: "HELLO FROM QBS"
+ * Authentication mode: "open"
+ * Beacon interval: 1000 milliseconds
+ * Channel number: 3
+
+An application requires the ESP8266 NON-OS SDK:
+
+ * https://github.com/espressif/ESP8266_NONOS_SDK
+
+the path to which should be set via the ESP8266_NON_OS_SDK_ROOT
+environment variable.
+
+To convert the ELF application to the final binary image (and also for
+re-program the ESP8266 MCU), you can use the open source 'esptool'
+utility (using the SPI FLASH parameters, provided above):
+
+ * https://github.com/espressif/esptool
+
+The following toolchains are supported:
+
+ * GNU ESP8266 Toolchain
diff --git a/examples/baremetal/esp8266/access-point/access-point.qbs b/examples/baremetal/esp8266/access-point/access-point.qbs
new file mode 100644
index 000000000..e9b06c340
--- /dev/null
+++ b/examples/baremetal/esp8266/access-point/access-point.qbs
@@ -0,0 +1,28 @@
+CppApplication {
+ condition: qbs.toolchain.contains("gcc") && qbs.architecture === "xtensa"
+
+ Depends { name: "esp8266_sdk" }
+
+ cpp.cLanguageVersion: "c99"
+ cpp.positionIndependentCode: false
+
+ // This required because ESP8266 SDK includes this 'user_config.h' internally.
+ cpp.includePaths: ["."]
+
+ cpp.staticLibraries: [
+ "c",
+ "crypto",
+ "gcc",
+ "lwip",
+ "main",
+ "net80211",
+ "phy",
+ "pp",
+ "wpa"
+ ]
+
+ files: [
+ "user_config.h",
+ "user_main.c"
+ ]
+}
diff --git a/examples/baremetal/esp8266/access-point/user_config.h b/examples/baremetal/esp8266/access-point/user_config.h
new file mode 100644
index 000000000..307f93dd5
--- /dev/null
+++ b/examples/baremetal/esp8266/access-point/user_config.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 ESP8266_USER_CONFIG_H
+#define ESP8266_USER_CONFIG_H
+
+// From ESP8266 SDK.
+#include <user_interface.h>
+
+#define ESP_SSID_CFG "HELLO FROM QBS\0"
+#define ESP_AUTO_MODE_CFG (AUTH_OPEN)
+#define ESP_BEACON_INTERVAL_MS_CFG (1000)
+#define ESP_CHANNEL_NO_CFG (3)
+
+#endif // ESP8266_USER_CONFIG_H
diff --git a/examples/baremetal/esp8266/access-point/user_main.c b/examples/baremetal/esp8266/access-point/user_main.c
new file mode 100644
index 000000000..be2a8e92d
--- /dev/null
+++ b/examples/baremetal/esp8266/access-point/user_main.c
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "user_config.h"
+
+// From ESP8266 SDK.
+#include <osapi.h>
+
+static const partition_item_t esp_partitions[] = {
+ {SYSTEM_PARTITION_BOOTLOADER, ESP_PART_BL_ADDR, ESP_PART_BOOTLOADER_SIZE},
+ {SYSTEM_PARTITION_OTA_1, ESP_PART_APP1_ADDR, ESP_PART_APPLICATION_SIZE},
+ {SYSTEM_PARTITION_OTA_2, ESP_PART_APP2_ADDR, ESP_PART_APPLICATION_SIZE},
+ {SYSTEM_PARTITION_RF_CAL, ESP_PART_RF_CAL_ADDR, ESP_PART_RF_CAL_SIZE},
+ {SYSTEM_PARTITION_PHY_DATA, ESP_PART_PHY_DATA_ADDR, ESP_PART_PHY_DATA_SIZE},
+ {SYSTEM_PARTITION_SYSTEM_PARAMETER, ESP_PART_SYS_PARAM_ADDR, ESP_PART_SYS_PARAM_SIZE}
+};
+
+enum {
+ ESP_PART_COUNT = sizeof(esp_partitions) / sizeof(esp_partitions[0])
+};
+
+LOCAL void ICACHE_FLASH_ATTR user_init_done(void)
+{
+ os_printf("esp: initialization completed\n");
+}
+
+void ICACHE_FLASH_ATTR user_pre_init(void)
+{
+ const bool ok = system_partition_table_regist(esp_partitions, ESP_PART_COUNT, SPI_FLASH_SIZE_MAP);
+ if (!ok) {
+ os_printf("esp: partitions registration failed\n");
+ while (true);
+ }
+}
+
+void ICACHE_FLASH_ATTR user_init(void)
+{
+ os_printf("esp: SDK version: %s\n", system_get_sdk_version());
+ os_printf("esp: reset reason: %u\n", system_get_rst_info()->reason);
+
+ const bool ok = wifi_set_opmode(SOFTAP_MODE);
+ if (!ok) {
+ os_printf("esp: set softap mode failed\n");
+ } else {
+ struct softap_config ap_cfg = {0};
+ ap_cfg.authmode = ESP_AUTO_MODE_CFG;
+ ap_cfg.beacon_interval = ESP_BEACON_INTERVAL_MS_CFG;
+ ap_cfg.channel = ESP_CHANNEL_NO_CFG;
+ ap_cfg.max_connection = 0;
+ ap_cfg.ssid_hidden = false;
+ os_memcpy(&ap_cfg.ssid, ESP_SSID_CFG, sizeof(ESP_SSID_CFG));
+ const bool ok = wifi_softap_set_config(&ap_cfg);
+ if (!ok)
+ os_printf("esp: set softap configuration failed\n");
+ }
+
+ system_init_done_cb(user_init_done);
+}
diff --git a/examples/baremetal/esp8266/esp8266.qbs b/examples/baremetal/esp8266/esp8266.qbs
new file mode 100644
index 000000000..faa74434e
--- /dev/null
+++ b/examples/baremetal/esp8266/esp8266.qbs
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+Project {
+ name: "Examples for esp8266-wroom-02 board"
+ qbsSearchPaths: "qbs"
+ references: [
+ "access-point/access-point.qbs"
+ ]
+}
diff --git a/examples/baremetal/esp8266/qbs/imports/Esp8266SdkProbe.qbs b/examples/baremetal/esp8266/qbs/imports/Esp8266SdkProbe.qbs
new file mode 100644
index 000000000..faa8b1efd
--- /dev/null
+++ b/examples/baremetal/esp8266/qbs/imports/Esp8266SdkProbe.qbs
@@ -0,0 +1,25 @@
+import qbs.Environment
+import qbs.File
+import qbs.FileInfo
+import qbs.Probes
+
+Probes.PathProbe {
+ // Inputs.
+ environmentPaths: Environment.getEnv("ESP8266_NON_OS_SDK_ROOT")
+ // Outputs.
+ property string includesPath;
+ property string libsPath;
+ property string linkerScriptsPath;
+ configure: {
+ for (var i in environmentPaths) {
+ var rootPath = environmentPaths[i];
+ if (!File.exists(rootPath))
+ continue;
+ includesPath = FileInfo.joinPaths(rootPath, "include");
+ libsPath = FileInfo.joinPaths(rootPath, "lib");
+ linkerScriptsPath = FileInfo.joinPaths(rootPath, "ld");
+ found = true;
+ return;
+ }
+ }
+}
diff --git a/examples/baremetal/esp8266/qbs/modules/esp8266_sdk/Esp8266SdkModule.qbs b/examples/baremetal/esp8266/qbs/modules/esp8266_sdk/Esp8266SdkModule.qbs
new file mode 100644
index 000000000..57e1f8572
--- /dev/null
+++ b/examples/baremetal/esp8266/qbs/modules/esp8266_sdk/Esp8266SdkModule.qbs
@@ -0,0 +1,66 @@
+Module {
+ Depends { name: "cpp" }
+
+ Esp8266SdkProbe { id: esp_sdk_probe }
+
+ validate: {
+ if (!esp_sdk_probe.found)
+ throw "ESP8266 NON OS SDK not found. Please set the ESP8266_NON_OS_SDK_ROOT env variable."
+ }
+
+ cpp.systemIncludePaths: [esp_sdk_probe.includesPath]
+ cpp.libraryPaths: [esp_sdk_probe.libsPath]
+ cpp.driverFlags: ["-nostdlib"]
+
+ cpp.cFlags: [
+ "-Wpointer-arith",
+ "-Wundef",
+ "-fno-inline-functions",
+ "-mlongcalls",
+ "-mtext-section-literals",
+ "-ffunction-sections",
+ "-fdata-sections",
+ "-fno-builtin-printf",
+ "-fno-guess-branch-probability",
+ "-freorder-blocks-and-partition",
+ "-fno-cse-follow-jumps"
+ ]
+
+ cpp.linkerFlags: [
+ "--no-check-sections",
+ "--gc-sections",
+ "--start-group"
+ ]
+
+ cpp.defines: [
+ "ICACHE_FLASH",
+ "USE_OPTIMIZE_PRINTF",
+
+ // Target specific defines for the external 40MHz DIO
+ // SPI 16 Mbit FLASH (2048 KByte + 2048 KByte) FOTA.
+ "SPI_FLASH_SIZE_MAP=5",
+ "ESP_PART_BL_ADDR=0x000000",
+ "ESP_PART_APP1_ADDR=0x001000",
+ "ESP_PART_APP2_ADDR=0x101000",
+ "ESP_PART_RF_CAL_ADDR=0x1FB000",
+ "ESP_PART_PHY_DATA_ADDR=0x1FC000",
+ "ESP_PART_SYS_PARAM_ADDR=0x1FD000",
+ "ESP_PART_BOOTLOADER_SIZE=0x001000",
+ "ESP_PART_APPLICATION_SIZE=0x0E0000",
+ "ESP_PART_RF_CAL_SIZE=0x001000",
+ "ESP_PART_PHY_DATA_SIZE=0x001000",
+ "ESP_PART_SYS_PARAM_SIZE=0x003000"
+ ]
+
+ property string _linker_scripts_path: esp_sdk_probe.linkerScriptsPath + "/"
+
+ Group {
+ name: "ESP8266 Linker Scripts"
+ prefix: esp8266_sdk._linker_scripts_path + "/"
+ fileTags: ["linkerscript"]
+ files: [
+ "eagle.rom.addr.v6.ld",
+ "eagle.app.v6.new.2048.ld"
+ ]
+ }
+}
diff --git a/examples/baremetal/msp430f5529/msp430f5529.qbs b/examples/baremetal/msp430f5529/msp430f5529.qbs
index f4a6f95b9..64e62dd57 100644
--- a/examples/baremetal/msp430f5529/msp430f5529.qbs
+++ b/examples/baremetal/msp430f5529/msp430f5529.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
Project {
name: "Examples for msp430f5529 board"
references: [
diff --git a/examples/baremetal/msp430f5529/nes-gamepads/nes-gamepads.qbs b/examples/baremetal/msp430f5529/nes-gamepads/nes-gamepads.qbs
index 37dfe8ca6..a06cfc6a2 100644
--- a/examples/baremetal/msp430f5529/nes-gamepads/nes-gamepads.qbs
+++ b/examples/baremetal/msp430f5529/nes-gamepads/nes-gamepads.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
CppApplication {
condition: {
if (!qbs.architecture.contains("msp430"))
diff --git a/examples/baremetal/msp430f5529/redblink/README.md b/examples/baremetal/msp430f5529/redblink/README.md
index 3affbaf7e..2f63b2349 100644
--- a/examples/baremetal/msp430f5529/redblink/README.md
+++ b/examples/baremetal/msp430f5529/redblink/README.md
@@ -1,4 +1,4 @@
-This examples demonstrates how to build a bare-metal application using
+This example demonstrates how to build a bare-metal application using
different MSP430 toolchains. It is designed for the MSP-EXP430F5529LP
target board (based on msp430f5529 chip) and simply flashes the red
LED on the board.
diff --git a/examples/baremetal/msp430f5529/redblink/redblink.qbs b/examples/baremetal/msp430f5529/redblink/redblink.qbs
index 0f056e101..d89d307a9 100644
--- a/examples/baremetal/msp430f5529/redblink/redblink.qbs
+++ b/examples/baremetal/msp430f5529/redblink/redblink.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
CppApplication {
condition: {
if (!qbs.architecture.contains("msp430"))
diff --git a/examples/baremetal/pca10001/greenblink/README.md b/examples/baremetal/pca10001/greenblink/README.md
new file mode 100644
index 000000000..0abfa46e6
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/README.md
@@ -0,0 +1,10 @@
+This example demonstrates how to build a bare-metal application using
+different ARM toolchains. It is designed for the Nordic pca100001/pca100024
+evaluation kit (based on nRF51822 MCU) and simply flashes the green
+LED on the board.
+
+The following toolchains are supported:
+
+ * GNU Arm Embedded Toolchain
+ * IAR Embedded Workbench
+ * KEIL Microcontroller Development Kit
diff --git a/examples/baremetal/pca10001/greenblink/gcc/flash.ld b/examples/baremetal/pca10001/greenblink/gcc/flash.ld
new file mode 100644
index 000000000..d65ee1464
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/gcc/flash.ld
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+ENTRY(reset_handler)
+
+MEMORY
+{
+ FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x40000
+ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x4000
+}
+
+SECTIONS {
+ .text : {
+ KEEP(*(.isr_vector))
+ *(.text*)
+ KEEP(*(.init))
+ KEEP(*(.fini))
+ /* .ctors */
+ *crtbegin.o(.ctors)
+ *crtbegin?.o(.ctors)
+ *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
+ *(SORT(.ctors.*))
+ *(.ctors)
+ /* .dtors */
+ *crtbegin.o(.dtors)
+ *crtbegin?.o(.dtors)
+ *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
+ *(SORT(.dtors.*))
+ *(.dtors)
+ *(.rodata*)
+ *(.eh_frame*)
+ . = ALIGN(4);
+ } > FLASH
+
+ .ARM.extab : {
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ . = ALIGN(4);
+ } > FLASH
+
+ _start_of_exidx_section = .;
+ .ARM.exidx : {
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ . = ALIGN(4);
+ } > FLASH
+ _end_of_exidx_section = .;
+
+ _end_of_code_section = .;
+
+ .data : AT (_end_of_code_section) {
+ _start_of_data_section = .;
+ *(vtable)
+ *(.data*)
+ . = ALIGN(4);
+ /* preinit data */
+ PROVIDE_HIDDEN(__preinit_array_start = .);
+ *(.preinit_array)
+ PROVIDE_HIDDEN(__preinit_array_end = .);
+ . = ALIGN(4);
+ /* init data */
+ PROVIDE_HIDDEN(__init_array_start = .);
+ *(SORT(.init_array.*))
+ *(.init_array)
+ PROVIDE_HIDDEN(__init_array_end = .);
+ . = ALIGN(4);
+ /* finit data */
+ PROVIDE_HIDDEN(__fini_array_start = .);
+ *(SORT(.fini_array.*))
+ *(.fini_array)
+ PROVIDE_HIDDEN(__fini_array_end = .);
+ *(.jcr)
+ . = ALIGN(4);
+ /* All data end */
+ _end_of_data_section = .;
+ } > RAM
+
+ .bss : {
+ . = ALIGN(4);
+ __bss_start__ = .;
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ } > RAM
+
+ .heap (COPY): {
+ __end__ = .;
+ end = __end__;
+ *(.heap*)
+ _heap_limit = .;
+ } > RAM
+
+ .stack_dummy (COPY): {
+ *(.stack*)
+ } > RAM
+
+ /* Set stack top to end of RAM, and stack limit move down by size of stack_dummy section. */
+ _end_of_stack = ORIGIN(RAM) + LENGTH(RAM);
+ _stack_limit = _end_of_stack - SIZEOF(.stack_dummy);
+ PROVIDE(__stack = _end_of_stack);
+
+ /* Check if data + heap + stack exceeds RAM limit */
+ ASSERT(_stack_limit >= _heap_limit, "region RAM overflowed with stack")
+}
diff --git a/examples/baremetal/pca10001/greenblink/gcc/startup.s b/examples/baremetal/pca10001/greenblink/gcc/startup.s
new file mode 100644
index 000000000..ec433ac03
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/gcc/startup.s
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+.syntax unified
+.arch armv7e-m
+.thumb
+
+// Define the stack.
+.section .stack
+.align 3
+.equ _size_of_stack, 2048
+.globl _end_of_stack
+.globl _stack_limit
+_stack_limit:
+.space _size_of_stack
+.size _stack_limit, . - _stack_limit
+_end_of_stack:
+.size _end_of_stack, . - _end_of_stack
+
+// Define the heap.
+.section .heap
+.align 3
+.equ _size_of_heap, 2048
+.globl _end_of_heap
+.globl _heap_limit
+_end_of_heap:
+.space _size_of_heap
+.size _end_of_heap, . - _end_of_heap
+_heap_limit:
+.size _heap_limit, . - _heap_limit
+
+// Define the empty vectors table.
+.section .isr_vector, "ax"
+.align 2
+.globl _vectors_table
+_vectors_table:
+ // Generic interrupts offset.
+ .word _end_of_stack // Initial stack pointer value.
+ .word reset_handler // Reset.
+ .word 0 // NMI.
+ .word 0 // Hard fault.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // SVC.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // PendSV.
+ .word 0 // SysTick.
+
+ // External interrupts offset.
+ .word 0 // POWER CLOCK.
+ .word 0 // RADIO.
+ .word 0 // UART0
+ .word 0 // SPI0/TWI0.
+ .word 0 // SPI1/TWI1.
+ .word 0 // Reserved.
+ .word 0 // GPIOTE.
+ .word 0 // ADC.
+ .word 0 // TIMER0.
+ .word 0 // TIMER1.
+ .word 0 // TIMER2.
+ .word 0 // RTC0.
+ .word 0 // TEMP.
+ .word 0 // RNG.
+ .word 0 // ECB.
+ .word 0 // CCM_AAR.
+ .word 0 // WDT.
+ .word 0 // RTC1.
+ .word 0 // QDEC.
+ .word 0 // LPCOMP.
+ .word 0 // SWI0.
+ .word 0 // SWI1.
+ .word 0 // SWI2.
+ .word 0 // SWI3.
+ .word 0 // SWI4.
+ .word 0 // SWI5.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+.size _vectors_table, . - _vectors_table
+
+// Define the 'reset_handler' function, which is an entry point.
+.text
+.thumb
+.thumb_func
+.align 1
+.globl reset_handler
+.type reset_handler, %function
+reset_handler:
+ // Loop to copy data from read only memory to RAM.
+ ldr r1, =_end_of_code_section
+ ldr r2, =_start_of_data_section
+ ldr r3, =__bss_start__
+ subs r3, r3, r2
+ ble _copy_data_init_done
+_copy_data_init:
+ subs r3, r3, #4
+ ldr r0, [r1,r3]
+ str r0, [r2,r3]
+ bgt _copy_data_init
+_copy_data_init_done:
+ // Zero fill the bss segment.
+ ldr r1, =__bss_start__
+ ldr r2, =__bss_end__
+ movs r0, 0
+ subs r2, r2, r1
+ ble _loop_fill_zero_bss_done
+
+_loop_fill_zero_bss:
+ subs r2, r2, #4
+ str r0, [r1, r2]
+ bgt _loop_fill_zero_bss
+
+_loop_fill_zero_bss_done:
+ // Call the application's entry point.
+ bl main
+ bx lr
+.size reset_handler, . - reset_handler
diff --git a/examples/baremetal/pca10001/greenblink/gpio.c b/examples/baremetal/pca10001/greenblink/gpio.c
new file mode 100644
index 000000000..5fc910e58
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/gpio.c
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "gpio.h"
+#include "system.h"
+
+#define GPIO_GREEN_LED_PIN_POS (18u)
+#define GPIO_GREEN_LED_PIN (1u << GPIO_GREEN_LED_PIN_POS)
+
+// Pin direction attributes.
+#define GPIO_PIN_CNF_DIR_POS (0u) // Position of DIR field.
+#define GPIO_PIN_CNF_DIR_OUT (1u) // Configure pin as an output pin.
+#define GPIO_PIN_CNF_DIR_MSK (GPIO_PIN_CNF_DIR_OUT << GPIO_PIN_CNF_DIR_POS)
+
+// Pin input buffer attributes.
+#define GPIO_PIN_CNF_INPUT_POS (1u) // Position of INPUT field.
+#define GPIO_PIN_CNF_INPUT_OFF (1u) // Disconnect input buffer.
+#define GPIO_PIN_CNF_INPUT_MSK (GPIO_PIN_CNF_INPUT_OFF << GPIO_PIN_CNF_INPUT_POS)
+
+// Pin pull attributes.
+#define GPIO_PIN_CNF_PULL_POS (2u) // Position of PULL field.
+#define GPIO_PIN_CNF_PULL_OFF (0u) // No pull.
+#define GPIO_PIN_CNF_PULL_MSK (GPIO_PIN_CNF_PULL_OFF << GPIO_PIN_CNF_PULL_POS)
+
+// Pin drive attributes.
+#define GPIO_PIN_CNF_DRIVE_POS (8u) // Position of DRIVE field.
+#define GPIO_PIN_CNF_DRIVE_S0S1 (0u) // Standard '0', standard '1'.
+#define GPIO_PIN_CNF_DRIVE_MSK (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_POS)
+
+// Pin sense attributes.
+#define GPIO_PIN_CNF_SENSE_POS (16u) // Position of SENSE field.
+#define GPIO_PIN_CNF_SENSE_OFF (0u) // Disabled.
+#define GPIO_PIN_CNF_SENSE_MSK (GPIO_PIN_CNF_SENSE_OFF << GPIO_PIN_CNF_SENSE_POS)
+
+void gpio_init_green_led(void)
+{
+ GPIOD_REGS_MAP->PIN_CNF[GPIO_GREEN_LED_PIN_POS] = (GPIO_PIN_CNF_DIR_MSK
+ | GPIO_PIN_CNF_INPUT_MSK
+ | GPIO_PIN_CNF_PULL_MSK
+ | GPIO_PIN_CNF_DRIVE_MSK
+ | GPIO_PIN_CNF_SENSE_MSK);
+}
+
+void gpio_toggle_green_led(void)
+{
+ const uint32_t gpio_state = GPIOD_REGS_MAP->OUT;
+ GPIOD_REGS_MAP->OUTSET = (GPIO_GREEN_LED_PIN & ~gpio_state);
+ GPIOD_REGS_MAP->OUTCLR = (GPIO_GREEN_LED_PIN & gpio_state);
+}
diff --git a/examples/baremetal/pca10001/greenblink/gpio.h b/examples/baremetal/pca10001/greenblink/gpio.h
new file mode 100644
index 000000000..9e931b715
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/gpio.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 GPIO_H
+#define GPIO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void gpio_init_green_led(void);
+void gpio_toggle_green_led(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GPIO_H
diff --git a/examples/baremetal/pca10001/greenblink/greenblink.qbs b/examples/baremetal/pca10001/greenblink/greenblink.qbs
new file mode 100644
index 000000000..1a4dabde7
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/greenblink.qbs
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+CppApplication {
+ condition: {
+ if (!qbs.architecture.startsWith("arm"))
+ return false;
+ return (qbs.toolchain.contains("gcc")
+ || qbs.toolchain.contains("iar")
+ || qbs.toolchain.contains("keil"))
+ && !qbs.toolchain.contains("xcode")
+ }
+ name: "pca10001-greenblink"
+ cpp.cLanguageVersion: "c99"
+ cpp.positionIndependentCode: false
+
+ //
+ // GCC-specific properties and sources.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("gcc")
+ cpp.assemblerFlags: [
+ "-mcpu=cortex-m0",
+ ]
+ cpp.driverFlags: [
+ "-mcpu=cortex-m0",
+ "-specs=nosys.specs"
+ ]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("gcc")
+ name: "GCC"
+ prefix: "gcc/"
+ Group {
+ name: "Startup"
+ fileTags: ["asm"]
+ files: ["startup.s"]
+ }
+ Group {
+ name: "Linker Script"
+ fileTags: ["linkerscript"]
+ files: ["flash.ld"]
+ }
+ }
+
+ //
+ // IAR-specific properties and sources.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("iar")
+ cpp.assemblerFlags: [
+ "--cpu", "cortex-m0"
+ ]
+ cpp.driverFlags: [
+ "--cpu", "cortex-m0"
+ ]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("iar")
+ name: "IAR"
+ prefix: "iar/"
+ Group {
+ name: "Startup"
+ fileTags: ["asm"]
+ files: ["startup.s"]
+ }
+ Group {
+ name: "Linker Script"
+ fileTags: ["linkerscript"]
+ files: ["flash.icf"]
+ }
+ }
+
+ //
+ // KEIL-specific properties and sources.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("keil")
+ cpp.assemblerFlags: [
+ "--cpu", "cortex-m0"
+ ]
+ cpp.driverFlags: [
+ "--cpu", "cortex-m0"
+ ]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("keil")
+ name: "KEIL"
+ prefix: "keil/"
+ Group {
+ name: "Startup"
+ fileTags: ["asm"]
+ files: ["startup.s"]
+ }
+ Group {
+ name: "Linker Script"
+ fileTags: ["linkerscript"]
+ files: ["flash.sct"]
+ }
+ }
+
+ //
+ // Common code.
+ //
+
+ Group {
+ name: "Gpio"
+ files: ["gpio.c", "gpio.h"]
+ }
+
+ Group {
+ name: "System"
+ files: ["system.h"]
+ }
+
+ files: ["main.c"]
+}
diff --git a/examples/baremetal/pca10001/greenblink/iar/flash.icf b/examples/baremetal/pca10001/greenblink/iar/flash.icf
new file mode 100644
index 000000000..8d1ef63f4
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/iar/flash.icf
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+define symbol _start_of_intvec_section = 0x00000000;
+
+define symbol _start_of_ram_section = 0x20000000;
+define symbol _end_of_ram_section = 0x20003FFF;
+define symbol _start_of_flash_section = 0x00000000;
+define symbol _end_of_flash_section = 0x0003FFFF;
+
+define symbol _min_stack_size = 0x800;
+define symbol _min_heap_size = 0x800;
+
+define memory mem with size = 4G;
+define region flash_section = mem:[from _start_of_flash_section to _end_of_flash_section];
+define region ram_section = mem:[from _start_of_ram_section to _end_of_ram_section];
+
+define block CSTACK with alignment = 8, size = _min_stack_size { };
+define block HEAP with alignment = 8, size = _min_heap_size { };
+
+initialize by copy { readwrite };
+
+place at address mem:_start_of_intvec_section { readonly section .intvec };
+
+place in flash_section { readonly };
+place in ram_section { readwrite, block CSTACK, block HEAP };
diff --git a/examples/baremetal/pca10001/greenblink/iar/startup.s b/examples/baremetal/pca10001/greenblink/iar/startup.s
new file mode 100644
index 000000000..d8041c5c1
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/iar/startup.s
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+ MODULE ?cstartup
+
+ SECTION CSTACK:DATA:NOROOT(3)
+ SECTION .intvec:CODE:NOROOT(2)
+
+ EXTERN __iar_program_start
+
+ PUBLIC _vectors_table
+ DATA
+
+_vectors_table
+ ;; Generic interrupts offset.
+ DCD sfe(CSTACK) ; Initial stack pointer value.
+ DCD reset_handler ; Reset.
+ DCD 0 ; NMI.
+ DCD 0 ; Hard fault.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; SVC.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; PendSV.
+ DCD 0 ; SysTick.
+ ;; External interrupts offset.
+ DCD 0 ; Power clock.
+ DCD 0 ; Radio.
+ DCD 0 ; UART0.
+ DCD 0 ; SPI0/TWI0
+ DCD 0 ; SPI1/TWI1
+ DCD 0 ; Reserved
+ DCD 0 ; GPIOTE
+ DCD 0 ; ADC
+ DCD 0 ; TIMER0
+ DCD 0 ; TIMER1
+ DCD 0 ; TIMER2
+ DCD 0 ; RTC0
+ DCD 0 ; TEMP
+ DCD 0 ; RNG
+ DCD 0 ; ECB
+ DCD 0 ; CCM/AAR
+ DCD 0 ; WDT
+ DCD 0 ; RTC1
+ DCD 0 ; QDEC
+ DCD 0 ; LPCOMP
+ DCD 0 ; SWI0
+ DCD 0 ; SWI1
+ DCD 0 ; SWI2
+ DCD 0 ; SWI3
+ DCD 0 ; SWI4
+ DCD 0 ; SWI5
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+
+ ;; Reset handler.
+ THUMB
+ PUBWEAK reset_handler
+ SECTION .text:CODE:REORDER:NOROOT(2)
+reset_handler
+ BLX R0
+ LDR R0, =__iar_program_start
+ BX R0
+
+ END
+
+
diff --git a/examples/baremetal/pca10001/greenblink/keil/flash.sct b/examples/baremetal/pca10001/greenblink/keil/flash.sct
new file mode 100644
index 000000000..6d5a1e42d
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/keil/flash.sct
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+;; Load region size_region.
+LR_IROM1 0x00000000 0x00040000 {
+ ;; Load address = execution address.
+ ER_IROM1 0x00000000 0x00040000 {
+ *.o (RESET, +First)
+ *(InRoot$$Sections)
+ .ANY (+RO)
+ .ANY (+XO)
+ }
+
+ ; RW data.
+ RW_IRAM1 0x20000000 0x00004000 {
+ .ANY (+RW +ZI)
+ }
+}
diff --git a/examples/baremetal/pca10001/greenblink/keil/startup.s b/examples/baremetal/pca10001/greenblink/keil/startup.s
new file mode 100644
index 000000000..4b63519db
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/keil/startup.s
@@ -0,0 +1,134 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+;; Contact: https://www.qt.io/licensing/
+;;
+;; This file is part of the examples 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$
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+_size_of_stack EQU 0x800
+_size_of_heap EQU 0x800
+
+;; Stack configuration.
+ AREA STACK, NOINIT, READWRITE, ALIGN=3
+_start_of_stack SPACE _size_of_stack
+_end_of_stack
+
+;; Heap configuration.
+ AREA HEAP, NOINIT, READWRITE, ALIGN=3
+_start_of_heap SPACE _size_of_heap
+_end_of_heap
+
+ PRESERVE8
+ THUMB
+
+;; Define the empty vectors table.
+ AREA RESET, DATA, READONLY
+_vectors_table
+ ;; Generic interrupts offset.
+ DCD _end_of_stack ; Initial stack pointer value.
+ DCD reset_handler ; Reset.
+ DCD 0 ; NMI.
+ DCD 0 ; Hard fault.
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; SVC.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; PendSV.
+ DCD 0 ; SysTick.
+ ;; External interrupts offset.
+ DCD 0 ; Power clock.
+ DCD 0 ; Radio.
+ DCD 0 ; UART0.
+ DCD 0 ; SPI0/TWI0
+ DCD 0 ; SPI1/TWI1
+ DCD 0 ; Reserved
+ DCD 0 ; GPIOTE
+ DCD 0 ; ADC
+ DCD 0 ; TIMER0
+ DCD 0 ; TIMER1
+ DCD 0 ; TIMER2
+ DCD 0 ; RTC0
+ DCD 0 ; TEMP
+ DCD 0 ; RNG
+ DCD 0 ; ECB
+ DCD 0 ; CCM/AAR
+ DCD 0 ; WDT
+ DCD 0 ; RTC1
+ DCD 0 ; QDEC
+ DCD 0 ; LPCOMP
+ DCD 0 ; SWI0
+ DCD 0 ; SWI1
+ DCD 0 ; SWI2
+ DCD 0 ; SWI3
+ DCD 0 ; SWI4
+ DCD 0 ; SWI5
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+ DCD 0 ; Reserved
+_end_of_vectors_table
+
+_size_of_vectors_table EQU _end_of_vectors_table - _vectors_table
+
+ AREA |.text|, CODE, READONLY
+;; Reset handler.
+reset_handler PROC
+ EXPORT reset_handler [WEAK]
+ IMPORT main
+ LDR R0, =main
+ BX R0
+ ENDP
+ ALIGN
+
+ END
diff --git a/examples/baremetal/pca10001/greenblink/main.c b/examples/baremetal/pca10001/greenblink/main.c
new file mode 100644
index 000000000..89d3daeb6
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/main.c
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "gpio.h"
+
+#include <stdint.h>
+
+static void some_delay(uint32_t counts)
+{
+ for (uint32_t index = 0u; index < counts; ++index)
+ __asm("nop");
+}
+
+int main(void)
+{
+ gpio_init_green_led();
+
+ while (1) {
+ gpio_toggle_green_led();
+ some_delay(1000000u);
+ }
+}
diff --git a/examples/baremetal/pca10001/greenblink/system.h b/examples/baremetal/pca10001/greenblink/system.h
new file mode 100644
index 000000000..37e6ad8ea
--- /dev/null
+++ b/examples/baremetal/pca10001/greenblink/system.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 SYSTEM_H
+#define SYSTEM_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define __IO volatile
+
+struct gpio_regs_map {
+ uint32_t RESERVED0[321u];
+ __IO uint32_t OUT;
+ __IO uint32_t OUTSET;
+ __IO uint32_t OUTCLR;
+ __IO uint32_t IN;
+ __IO uint32_t DIR;
+ __IO uint32_t DIRSET;
+ __IO uint32_t DIRCLR;
+ uint32_t RESERVED1[120u];
+ __IO uint32_t PIN_CNF[32u];
+};
+
+#define GPIO_REGS_ADDRESS (0x50000000u)
+#define GPIOD_REGS_MAP ((struct gpio_regs_map *)GPIO_REGS_ADDRESS)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SYSTEM_H
diff --git a/examples/baremetal/pca10001/pca10001.qbs b/examples/baremetal/pca10001/pca10001.qbs
new file mode 100644
index 000000000..db1303268
--- /dev/null
+++ b/examples/baremetal/pca10001/pca10001.qbs
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+Project {
+ name: "Examples for pca10001/pca10024 boards"
+ references: [
+ "greenblink/greenblink.qbs"
+ ]
+}
diff --git a/examples/baremetal/pca10040/greenblink/README.md b/examples/baremetal/pca10040/greenblink/README.md
new file mode 100644
index 000000000..7d759839b
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/README.md
@@ -0,0 +1,10 @@
+This example demonstrates how to build a bare-metal application using
+different ARM toolchains. It is designed for the Nordic pca10040
+evaluation kit (based on nRF52832 MCU) and simply flashes the green
+LED on the board.
+
+The following toolchains are supported:
+
+ * GNU Arm Embedded Toolchain
+ * IAR Embedded Workbench
+ * KEIL Microcontroller Development Kit
diff --git a/examples/baremetal/pca10040/greenblink/gcc/flash.ld b/examples/baremetal/pca10040/greenblink/gcc/flash.ld
new file mode 100644
index 000000000..b998618ae
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/gcc/flash.ld
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+ENTRY(reset_handler)
+
+MEMORY
+{
+ FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x80000
+ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x10000
+ CODE_RAM (rwx) : ORIGIN = 0x800000, LENGTH = 0x10000
+}
+
+SECTIONS {
+ .text : {
+ KEEP(*(.isr_vector))
+ *(.text*)
+ KEEP(*(.init))
+ KEEP(*(.fini))
+ /* .ctors */
+ *crtbegin.o(.ctors)
+ *crtbegin?.o(.ctors)
+ *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
+ *(SORT(.ctors.*))
+ *(.ctors)
+ /* .dtors */
+ *crtbegin.o(.dtors)
+ *crtbegin?.o(.dtors)
+ *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
+ *(SORT(.dtors.*))
+ *(.dtors)
+ *(.rodata*)
+ *(.eh_frame*)
+ . = ALIGN(4);
+ } > FLASH
+
+ .ARM.extab : {
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ . = ALIGN(4);
+ } > FLASH
+
+ _start_of_exidx_section = .;
+ .ARM.exidx : {
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ . = ALIGN(4);
+ } > FLASH
+ _end_of_exidx_section = .;
+
+ _end_of_code_section = .;
+
+ .data : AT (_end_of_code_section) {
+ _start_of_data_section = .;
+ *(vtable)
+ *(.data*)
+ . = ALIGN(4);
+ /* preinit data */
+ PROVIDE_HIDDEN(__preinit_array_start = .);
+ *(.preinit_array)
+ PROVIDE_HIDDEN(__preinit_array_end = .);
+ . = ALIGN(4);
+ /* init data */
+ PROVIDE_HIDDEN(__init_array_start = .);
+ *(SORT(.init_array.*))
+ *(.init_array)
+ PROVIDE_HIDDEN(__init_array_end = .);
+ . = ALIGN(4);
+ /* finit data */
+ PROVIDE_HIDDEN(__fini_array_start = .);
+ *(SORT(.fini_array.*))
+ *(.fini_array)
+ PROVIDE_HIDDEN(__fini_array_end = .);
+ *(.jcr)
+ . = ALIGN(4);
+ /* All data end */
+ _end_of_data_section = .;
+ } > RAM
+
+ .bss : {
+ . = ALIGN(4);
+ __bss_start__ = .;
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ } > RAM
+
+ .heap (COPY): {
+ __end__ = .;
+ end = __end__;
+ *(.heap*)
+ _heap_limit = .;
+ } > RAM
+
+ .stack_dummy (COPY): {
+ *(.stack*)
+ } > RAM
+
+ /* Set stack top to end of RAM, and stack limit move down by size of stack_dummy section. */
+ _end_of_stack = ORIGIN(RAM) + LENGTH(RAM);
+ _stack_limit = _end_of_stack - SIZEOF(.stack_dummy);
+ PROVIDE(__stack = _end_of_stack);
+
+ /* Check if data + heap + stack exceeds RAM limit */
+ ASSERT(_stack_limit >= _heap_limit, "region RAM overflowed with stack")
+}
diff --git a/examples/baremetal/pca10040/greenblink/gcc/startup.s b/examples/baremetal/pca10040/greenblink/gcc/startup.s
new file mode 100644
index 000000000..d1fe7e9de
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/gcc/startup.s
@@ -0,0 +1,253 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+.syntax unified
+.arch armv7e-m
+.thumb
+
+// Define the stack.
+.section .stack
+.align 3
+.equ _size_of_stack, 8192
+.globl _end_of_stack
+.globl _stack_limit
+_stack_limit:
+.space _size_of_stack
+.size _stack_limit, . - _stack_limit
+_end_of_stack:
+.size _end_of_stack, . - _end_of_stack
+
+// Define the heap.
+.section .heap
+.align 3
+.equ _size_of_heap, 8192
+.globl _end_of_heap
+.globl _heap_limit
+_end_of_heap:
+.space _size_of_heap
+.size _end_of_heap, . - _end_of_heap
+_heap_limit:
+.size _heap_limit, . - _heap_limit
+
+// Define the empty vectors table.
+.section .isr_vector, "ax"
+.align 2
+.globl _vectors_table
+_vectors_table:
+ // Generic interrupts offset.
+ .word _end_of_stack // Initial stack pointer value.
+ .word reset_handler // Reset.
+ .word 0 // NMI.
+ .word 0 // Hard fault.
+ .word 0 // Memory management fault.
+ .word 0 // Bus fault.
+ .word 0 // Usage fault.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // SVC.
+ .word 0 // Debug monitor.
+ .word 0 // Reserved.
+ .word 0 // PendSV.
+ .word 0 // SysTick.
+
+ // External interrupts offset.
+ .word 0 // POWER CLOCK.
+ .word 0 // RADIO.
+ .word 0 // UARTE0/UART0
+ .word 0 // SPIM0/SPIS0/TWIM0/TWIS0/SPI0/TWI0.
+ .word 0 // SPIM1/SPIS1/TWIM1/TWIS1/SPI1/TWI1.
+ .word 0 // NFCT.
+ .word 0 // GPIOTE.
+ .word 0 // SAADC.
+ .word 0 // TIMER0.
+ .word 0 // TIMER1.
+ .word 0 // TIMER2.
+ .word 0 // RTC0.
+ .word 0 // TEMP.
+ .word 0 // RNG.
+ .word 0 // ECB.
+ .word 0 // CCM_AAR.
+ .word 0 // WDT.
+ .word 0 // RTC1.
+ .word 0 // QDEC.
+ .word 0 // COMP_LPCOMP.
+ .word 0 // SWI0_EGU0.
+ .word 0 // SWI1_EGU1.
+ .word 0 // SWI2_EGU2.
+ .word 0 // SWI3_EGU3.
+ .word 0 // SWI4_EGU4.
+ .word 0 // SWI5_EGU5.
+ .word 0 // TIMER3.
+ .word 0 // TIMER4.
+ .word 0 // PWM0.
+ .word 0 // PDM.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // MWU.
+ .word 0 // PWM1.
+ .word 0 // PWM2.
+ .word 0 // SPIM2/SPIS2/SPI2.
+ .word 0 // RTC2.
+ .word 0 // I2S.
+ .word 0 // FPU.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+.size _vectors_table, . - _vectors_table
+
+// Define the 'reset_handler' function, which is an entry point.
+.text
+.thumb
+.thumb_func
+.align 1
+.globl reset_handler
+.type reset_handler, %function
+reset_handler:
+ // Loop to copy data from read only memory to RAM.
+ ldr r1, =_end_of_code_section
+ ldr r2, =_start_of_data_section
+ ldr r3, =__bss_start__
+ subs r3, r3, r2
+ ble _copy_data_init_done
+_copy_data_init:
+ subs r3, r3, #4
+ ldr r0, [r1,r3]
+ str r0, [r2,r3]
+ bgt _copy_data_init
+_copy_data_init_done:
+ // Zero fill the bss segment.
+ ldr r1, =__bss_start__
+ ldr r2, =__bss_end__
+ movs r0, 0
+ subs r2, r2, r1
+ ble _loop_fill_zero_bss_done
+
+_loop_fill_zero_bss:
+ subs r2, r2, #4
+ str r0, [r1, r2]
+ bgt _loop_fill_zero_bss
+
+_loop_fill_zero_bss_done:
+ // Call the application's entry point.
+ bl main
+ bx lr
+.size reset_handler, . - reset_handler
diff --git a/examples/baremetal/pca10040/greenblink/gpio.c b/examples/baremetal/pca10040/greenblink/gpio.c
new file mode 100644
index 000000000..c86f6ac5e
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/gpio.c
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "gpio.h"
+#include "system.h"
+
+#define GPIO_GREEN_LED_PIN_POS (17u)
+#define GPIO_GREEN_LED_PIN (1u << GPIO_GREEN_LED_PIN_POS)
+
+// Pin direction attributes.
+#define GPIO_PIN_CNF_DIR_POS (0u) // Position of DIR field.
+#define GPIO_PIN_CNF_DIR_OUT (1u) // Configure pin as an output pin.
+#define GPIO_PIN_CNF_DIR_MSK (GPIO_PIN_CNF_DIR_OUT << GPIO_PIN_CNF_DIR_POS)
+
+// Pin input buffer attributes.
+#define GPIO_PIN_CNF_INPUT_POS (1u) // Position of INPUT field.
+#define GPIO_PIN_CNF_INPUT_OFF (1u) // Disconnect input buffer.
+#define GPIO_PIN_CNF_INPUT_MSK (GPIO_PIN_CNF_INPUT_OFF << GPIO_PIN_CNF_INPUT_POS)
+
+// Pin pull attributes.
+#define GPIO_PIN_CNF_PULL_POS (2u) // Position of PULL field.
+#define GPIO_PIN_CNF_PULL_OFF (0u) // No pull.
+#define GPIO_PIN_CNF_PULL_MSK (GPIO_PIN_CNF_PULL_OFF << GPIO_PIN_CNF_PULL_POS)
+
+// Pin drive attributes.
+#define GPIO_PIN_CNF_DRIVE_POS (8u) // Position of DRIVE field.
+#define GPIO_PIN_CNF_DRIVE_S0S1 (0u) // Standard '0', standard '1'.
+#define GPIO_PIN_CNF_DRIVE_MSK (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_POS)
+
+// Pin sense attributes.
+#define GPIO_PIN_CNF_SENSE_POS (16u) // Position of SENSE field.
+#define GPIO_PIN_CNF_SENSE_OFF (0u) // Disabled.
+#define GPIO_PIN_CNF_SENSE_MSK (GPIO_PIN_CNF_SENSE_OFF << GPIO_PIN_CNF_SENSE_POS)
+
+void gpio_init_green_led(void)
+{
+ GPIOD_REGS_MAP->PIN_CNF[GPIO_GREEN_LED_PIN_POS] = (GPIO_PIN_CNF_DIR_MSK
+ | GPIO_PIN_CNF_INPUT_MSK
+ | GPIO_PIN_CNF_PULL_MSK
+ | GPIO_PIN_CNF_DRIVE_MSK
+ | GPIO_PIN_CNF_SENSE_MSK);
+}
+
+void gpio_toggle_green_led(void)
+{
+ const uint32_t gpio_state = GPIOD_REGS_MAP->OUT;
+ GPIOD_REGS_MAP->OUTSET = (GPIO_GREEN_LED_PIN & ~gpio_state);
+ GPIOD_REGS_MAP->OUTCLR = (GPIO_GREEN_LED_PIN & gpio_state);
+}
diff --git a/examples/baremetal/pca10040/greenblink/gpio.h b/examples/baremetal/pca10040/greenblink/gpio.h
new file mode 100644
index 000000000..9e931b715
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/gpio.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 GPIO_H
+#define GPIO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void gpio_init_green_led(void);
+void gpio_toggle_green_led(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GPIO_H
diff --git a/examples/baremetal/pca10040/greenblink/greenblink.qbs b/examples/baremetal/pca10040/greenblink/greenblink.qbs
new file mode 100644
index 000000000..2ae3dc2a1
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/greenblink.qbs
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+CppApplication {
+ condition: {
+ if (!qbs.architecture.startsWith("arm"))
+ return false;
+ return (qbs.toolchain.contains("gcc")
+ || qbs.toolchain.contains("iar")
+ || qbs.toolchain.contains("keil"))
+ && !qbs.toolchain.contains("xcode")
+ }
+ name: "pca10040-greenblink"
+ cpp.cLanguageVersion: "c99"
+ cpp.positionIndependentCode: false
+
+ //
+ // GCC-specific properties and sources.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("gcc")
+ cpp.assemblerFlags: [
+ "-mcpu=cortex-m4",
+ "-mfloat-abi=hard",
+ "-mfpu=fpv4-sp-d16",
+ ]
+ cpp.driverFlags: [
+ "-mcpu=cortex-m4",
+ "-mfloat-abi=hard",
+ "-mfpu=fpv4-sp-d16",
+ "-specs=nosys.specs"
+ ]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("gcc")
+ name: "GCC"
+ prefix: "gcc/"
+ Group {
+ name: "Startup"
+ fileTags: ["asm"]
+ files: ["startup.s"]
+ }
+ Group {
+ name: "Linker Script"
+ fileTags: ["linkerscript"]
+ files: ["flash.ld"]
+ }
+ }
+
+ //
+ // IAR-specific properties and sources.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("iar")
+ cpp.assemblerFlags: [
+ "--cpu", "cortex-m4",
+ "--fpu", "vfpv4_sp"
+ ]
+ cpp.driverFlags: [
+ "--cpu", "cortex-m4",
+ "--fpu", "vfpv4_sp"
+ ]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("iar")
+ name: "IAR"
+ prefix: "iar/"
+ Group {
+ name: "Startup"
+ fileTags: ["asm"]
+ files: ["startup.s"]
+ }
+ Group {
+ name: "Linker Script"
+ fileTags: ["linkerscript"]
+ files: ["flash.icf"]
+ }
+ }
+
+ //
+ // KEIL-specific properties and sources.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("keil")
+ cpp.assemblerFlags: [
+ "--cpu", "cortex-m4.fp.sp"
+ ]
+ cpp.driverFlags: [
+ "--cpu", "cortex-m4.fp.sp"
+ ]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("keil")
+ name: "KEIL"
+ prefix: "keil/"
+ Group {
+ name: "Startup"
+ fileTags: ["asm"]
+ files: ["startup.s"]
+ }
+ Group {
+ name: "Linker Script"
+ fileTags: ["linkerscript"]
+ files: ["flash.sct"]
+ }
+ }
+
+ //
+ // Common code.
+ //
+
+ Group {
+ name: "Gpio"
+ files: ["gpio.c", "gpio.h"]
+ }
+
+ Group {
+ name: "System"
+ files: ["system.h"]
+ }
+
+ files: ["main.c"]
+}
diff --git a/examples/baremetal/pca10040/greenblink/iar/flash.icf b/examples/baremetal/pca10040/greenblink/iar/flash.icf
new file mode 100644
index 000000000..d19640293
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/iar/flash.icf
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+define symbol _start_of_intvec_section = 0x00000000;
+
+define symbol _start_of_irom1_section = 0x00000000;
+define symbol _end_of_irom1_section = 0x0007FFFF;
+define symbol _start_of_irom2_section = 0x0;
+define symbol _end_of_irom2_section = 0x0;
+define symbol _start_of_erom1_section = 0x0;
+define symbol _end_of_erom1_section = 0x0;
+define symbol _start_of_erom2_section = 0x0;
+define symbol _end_of_erom2_section = 0x0;
+define symbol _start_of_erom3_section = 0x0;
+define symbol _end_of_erom3_section = 0x0;
+define symbol _start_of_iram1_section = 0x20000000;
+define symbol _end_of_iram1_section = 0x2000FFFF;
+define symbol _start_of_iram2_section = 0x0;
+define symbol _end_of_iram2_section = 0x0;
+define symbol _start_of_eram1_section = 0x0;
+define symbol _end_of_eram1_section = 0x0;
+define symbol _start_of_eram2_section = 0x0;
+define symbol _end_of_eram2_section = 0x0;
+define symbol _start_of_eram3_section = 0x0;
+define symbol _end_of_eram3_section = 0x0;
+
+define symbol _min_stack_size = 0x2000;
+define symbol _min_proc_stack_size = 0x0;
+define symbol _min_heap_size = 0x2000;
+
+define memory mem with size = 4G;
+define region irom_section = mem:[from _start_of_irom1_section to _end_of_irom1_section]
+ | mem:[from _start_of_irom2_section to _end_of_irom2_section];
+define region erom_section = mem:[from _start_of_erom1_section to _end_of_erom1_section]
+ | mem:[from _start_of_erom2_section to _end_of_erom2_section]
+ | mem:[from _start_of_erom3_section to _end_of_erom3_section];
+define region iram_section = mem:[from _start_of_iram1_section to _end_of_iram1_section]
+ | mem:[from _start_of_iram2_section to _end_of_iram2_section];
+define region eram_section = mem:[from _start_of_eram1_section to _end_of_eram1_section]
+ | mem:[from _start_of_eram2_section to _end_of_eram2_section]
+ | mem:[from _start_of_eram3_section to _end_of_eram3_section];
+
+
+define block CSTACK with alignment = 8, size = _min_stack_size { };
+define block PROC_STACK with alignment = 8, size = _min_proc_stack_size { };
+define block HEAP with alignment = 8, size = _min_heap_size { };
+
+initialize by copy { readwrite };
+
+place at address mem:_start_of_intvec_section { readonly section .intvec };
+
+place in irom_section { readonly };
+place in erom_section { readonly section application_specific_ro };
+place in iram_section { readwrite, block CSTACK, block HEAP, block PROC_STACK };
+place in eram_section { readwrite section application_specific_rw };
diff --git a/examples/baremetal/pca10040/greenblink/iar/startup.s b/examples/baremetal/pca10040/greenblink/iar/startup.s
new file mode 100644
index 000000000..209322ac0
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/iar/startup.s
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+ MODULE ?cstartup
+
+ SECTION CSTACK:DATA:NOROOT(3)
+ SECTION .intvec:CODE:NOROOT(2)
+
+ EXTERN __iar_program_start
+
+ PUBLIC _vectors_table
+ DATA
+
+_vectors_table
+ ;; Generic interrupts offset.
+ DCD sfe(CSTACK) ; Initial stack pointer value.
+ DCD reset_handler ; Reset.
+ DCD 0 ; NMI.
+ DCD 0 ; Hard fault.
+ DCD 0 ; Memory management fault.
+ DCD 0 ; Bus fault.
+ DCD 0 ; Usage fault.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; SVC.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; PendSV.
+ DCD 0 ; SysTick.
+ ;; External interrupts offset.
+ DCD 0 ; POWER CLOCK.
+ DCD 0 ; RADIO.
+ DCD 0 ; UARTE0/UART0
+ DCD 0 ; SPIM0/SPIS0/TWIM0/TWIS0/SPI0/TWI0.
+ DCD 0 ; SPIM1/SPIS1/TWIM1/TWIS1/SPI1/TWI1.
+ DCD 0 ; NFCT.
+ DCD 0 ; GPIOTE.
+ DCD 0 ; SAADC.
+ DCD 0 ; TIMER0.
+ DCD 0 ; TIMER1.
+ DCD 0 ; TIMER2.
+ DCD 0 ; RTC0.
+ DCD 0 ; TEMP.
+ DCD 0 ; RNG.
+ DCD 0 ; ECB.
+ DCD 0 ; CCM_AAR.
+ DCD 0 ; WDT.
+ DCD 0 ; RTC1.
+ DCD 0 ; QDEC.
+ DCD 0 ; COMP_LPCOMP.
+ DCD 0 ; SWI0_EGU0.
+ DCD 0 ; SWI1_EGU1.
+ DCD 0 ; SWI2_EGU2.
+ DCD 0 ; SWI3_EGU3.
+ DCD 0 ; SWI4_EGU4.
+ DCD 0 ; SWI5_EGU5.
+ DCD 0 ; TIMER3.
+ DCD 0 ; TIMER4.
+ DCD 0 ; PWM0.
+ DCD 0 ; PDM.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; MWU.
+ DCD 0 ; PWM1.
+ DCD 0 ; PWM2.
+ DCD 0 ; SPIM2/SPIS2/SPI2.
+ DCD 0 ; RTC2.
+ DCD 0 ; I2S.
+ DCD 0 ; FPU.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+
+ ;; Reset handler.
+ THUMB
+ PUBWEAK reset_handler
+ SECTION .text:CODE:REORDER:NOROOT(2)
+reset_handler
+ BLX R0
+ LDR R0, =__iar_program_start
+ BX R0
+
+ END
diff --git a/examples/baremetal/pca10040/greenblink/keil/flash.sct b/examples/baremetal/pca10040/greenblink/keil/flash.sct
new file mode 100644
index 000000000..29843331a
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/keil/flash.sct
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+;; Load region size_region.
+LR_IROM1 0x00000000 0x00080000 {
+ ;; Load address = execution address.
+ ER_IROM1 0x00000000 0x00080000 {
+ *.o (RESET, +First)
+ *(InRoot$$Sections)
+ .ANY (+RO)
+ .ANY (+XO)
+ }
+
+ ; RW data.
+ RW_IRAM1 0x20000000 0x00008000 {
+ .ANY (+RW +ZI)
+ }
+}
diff --git a/examples/baremetal/pca10040/greenblink/keil/startup.s b/examples/baremetal/pca10040/greenblink/keil/startup.s
new file mode 100644
index 000000000..369ca96f8
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/keil/startup.s
@@ -0,0 +1,215 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+;; Contact: https://www.qt.io/licensing/
+;;
+;; This file is part of the examples 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$
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+_size_of_stack EQU 0x2000
+_size_of_heap EQU 0x2000
+
+;; Stack configuration.
+ AREA STACK, NOINIT, READWRITE, ALIGN=3
+_start_of_stack SPACE _size_of_stack
+_end_of_stack
+
+;; Heap configuration.
+ AREA HEAP, NOINIT, READWRITE, ALIGN=3
+_start_of_heap SPACE _size_of_heap
+_end_of_heap
+
+ PRESERVE8
+ THUMB
+
+;; Define the empty vectors table.
+ AREA RESET, DATA, READONLY
+_vectors_table
+ ;; Generic interrupts offset.
+ DCD _end_of_stack ; Initial stack pointer value.
+ DCD reset_handler ; Reset.
+ DCD 0 ; NMI.
+ DCD 0 ; Hard fault.
+ DCD 0 ; Memory management fault.
+ DCD 0 ; Bus fault.
+ DCD 0 ; Usage fault.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; SVC.
+ DCD 0 ; Debug monitor.
+ DCD 0 ; Reserved.
+ DCD 0 ; PendSV.
+ DCD 0 ; SysTick.
+ ;; External interrupts offset.
+ DCD 0 ; Power clock.
+ DCD 0 ; Radio.
+ DCD 0 ; UARTE0/UART0.
+ DCD 0 ; SPIM0/SPIS0/TWIM0/TWIS0/SPI0/TWI0.
+ DCD 0 ; SPIM1/SPIS1/TWIM1/TWIS1/SPI1/TWI1.
+ DCD 0 ; NFCT.
+ DCD 0 ; GPIOTE.
+ DCD 0 ; SAADC.
+ DCD 0 ; TIMER0.
+ DCD 0 ; TIMER1.
+ DCD 0 ; TIMER2.
+ DCD 0 ; RTC0.
+ DCD 0 ; TEMP.
+ DCD 0 ; RNG.
+ DCD 0 ; ECB.
+ DCD 0 ; CCM/AAR.
+ DCD 0 ; WDT.
+ DCD 0 ; RTC1.
+ DCD 0 ; QDEC.
+ DCD 0 ; COMP/LPCOMP.
+ DCD 0 ; SWI0/EGU0.
+ DCD 0 ; SWI1/EGU1.
+ DCD 0 ; SWI2/EGU2.
+ DCD 0 ; SWI3/EGU3.
+ DCD 0 ; SWI4/EGU4.
+ DCD 0 ; SWI5/EGU5.
+ DCD 0 ; TIMER3.
+ DCD 0 ; TIMER4.
+ DCD 0 ; PWM0.
+ DCD 0 ; PDM.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; MWU.
+ DCD 0 ; PWM1.
+ DCD 0 ; PWM2.
+ DCD 0 ; SPIM2/SPIS2/SPI2.
+ DCD 0 ; RTC2.
+ DCD 0 ; I2S.
+ DCD 0 ; FPU.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+
+_end_of_vectors_table
+
+_size_of_vectors_table EQU _end_of_vectors_table - _vectors_table
+
+ AREA |.text|, CODE, READONLY
+;; Reset handler.
+reset_handler PROC
+ EXPORT reset_handler [WEAK]
+ IMPORT main
+ LDR R0, =main
+ BX R0
+ ENDP
+ ALIGN
+
+ END
diff --git a/examples/baremetal/pca10040/greenblink/main.c b/examples/baremetal/pca10040/greenblink/main.c
new file mode 100644
index 000000000..89d3daeb6
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/main.c
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "gpio.h"
+
+#include <stdint.h>
+
+static void some_delay(uint32_t counts)
+{
+ for (uint32_t index = 0u; index < counts; ++index)
+ __asm("nop");
+}
+
+int main(void)
+{
+ gpio_init_green_led();
+
+ while (1) {
+ gpio_toggle_green_led();
+ some_delay(1000000u);
+ }
+}
diff --git a/examples/baremetal/pca10040/greenblink/system.h b/examples/baremetal/pca10040/greenblink/system.h
new file mode 100644
index 000000000..bf9e4b352
--- /dev/null
+++ b/examples/baremetal/pca10040/greenblink/system.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 SYSTEM_H
+#define SYSTEM_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define __IO volatile
+
+struct gpio_regs_map {
+ uint32_t RESERVED0[321u];
+ __IO uint32_t OUT;
+ __IO uint32_t OUTSET;
+ __IO uint32_t OUTCLR;
+ __IO uint32_t IN;
+ __IO uint32_t DIR;
+ __IO uint32_t DIRSET;
+ __IO uint32_t DIRCLR;
+ __IO uint32_t LATCH;
+ __IO uint32_t DETECTMODE;
+ uint32_t RESERVED1[118u];
+ __IO uint32_t PIN_CNF[32u];
+};
+
+#define GPIO_REGS_ADDRESS (0x50000000u)
+#define GPIOD_REGS_MAP ((struct gpio_regs_map *)GPIO_REGS_ADDRESS)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SYSTEM_H
diff --git a/examples/baremetal/pca10040/pca10040.qbs b/examples/baremetal/pca10040/pca10040.qbs
new file mode 100644
index 000000000..e5e516c3f
--- /dev/null
+++ b/examples/baremetal/pca10040/pca10040.qbs
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+Project {
+ name: "Examples for pca10040 board"
+ references: [
+ "greenblink/greenblink.qbs"
+ ]
+}
diff --git a/examples/baremetal/stm32f103/greenblink/README.md b/examples/baremetal/stm32f103/greenblink/README.md
new file mode 100644
index 000000000..8d19ac73a
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/README.md
@@ -0,0 +1,10 @@
+This example demonstrates how to build a bare-metal application using
+different ARM toolchains. It is designed for the stm32f103 "Blue Pill"
+evaluation kit (based on stm32f103c8 MCU) and simply flashes the green
+LED on the board.
+
+The following toolchains are supported:
+
+ * GNU Arm Embedded Toolchain
+ * IAR Embedded Workbench
+ * KEIL Microcontroller Development Kit
diff --git a/examples/baremetal/stm32f103/greenblink/gcc/flash.ld b/examples/baremetal/stm32f103/greenblink/gcc/flash.ld
new file mode 100644
index 000000000..3fe5c1563
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/gcc/flash.ld
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+/* Entry point (defined in assembled file). */
+ENTRY(reset_handler)
+
+/* End of RAM, it is the user mode stack pointer address. */
+_end_of_stack = 0x20005000;
+
+/* Generate a link error if heap and stack don't fit into RAM. */
+_size_of_heap = 0x200; /* Required amount of heap. */
+_size_of_stack = 0x400; /* Required amount of stack. */
+
+MEMORY {
+ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
+ FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 64K
+}
+
+SECTIONS {
+ /* The vectors table goes into FLASH. */
+ .isr_vector : {
+ . = ALIGN(4);
+ KEEP(*(.isr_vector)) /* Startup code. */
+ . = ALIGN(4);
+ } > FLASH
+
+ /* The program code and other data goes into FLASH. */
+ .text : {
+ . = ALIGN(4);
+ *(.text)
+ *(.text*)
+ *(.glue_7) /* Glue arm to thumb code. */
+ *(.glue_7t) /* Glue thumb to arm code. */
+ *(.eh_frame)
+ KEEP(*(.init))
+ KEEP(*(.fini))
+ . = ALIGN(4);
+ _end_of_text_section = .; /* Export global symbol at end of code. */
+ } > FLASH
+
+ /* Constant data goes into FLASH. */
+ .rodata : {
+ . = ALIGN(4);
+ *(.rodata)
+ *(.rodata*)
+ . = ALIGN(4);
+ } > FLASH
+
+ .ARM.extab : {
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ } > FLASH
+
+ .ARM : {
+ __exidx_start = .;
+ *(.ARM.exidx*)
+ __exidx_end = .;
+ } > FLASH
+
+ .preinit_array : {
+ PROVIDE_HIDDEN(__preinit_array_start = .);
+ KEEP(*(.preinit_array*))
+ PROVIDE_HIDDEN(__preinit_array_end = .);
+ } > FLASH
+
+ .init_array : {
+ PROVIDE_HIDDEN(__init_array_start = .);
+ KEEP(*(SORT(.init_array.*)))
+ KEEP(*(.init_array*))
+ PROVIDE_HIDDEN(__init_array_end = .);
+ } > FLASH
+
+ .fini_array : {
+ PROVIDE_HIDDEN(__fini_array_start = .);
+ KEEP(*(SORT(.fini_array.*)))
+ KEEP(*(.fini_array*))
+ PROVIDE_HIDDEN(__fini_array_end = .);
+ } > FLASH
+
+ _start_of_init_data_section = LOADADDR(.data);
+
+ /* Initialized data sections goes into RAM, load LMA copy after code. */
+ .data : {
+ . = ALIGN(4);
+ _start_of_data_section = .; /* Export global symbol at data start. */
+ *(.data)
+ *(.data*)
+ . = ALIGN(4);
+ _end_of_data_section = .; /* Export global symbol at data end. */
+ } > RAM AT > FLASH
+
+ /* Uninitialized data section. */
+ . = ALIGN(4);
+ .bss : {
+ /* This is used by the startup in order to initialize the .bss secion. */
+ _start_of_bss_section = .; /* Export global symbol at bss start. */
+ __bss_start__ = _start_of_bss_section;
+ *(.bss)
+ *(.bss*)
+ *(COMMON)
+ . = ALIGN(4);
+ _end_of_bss_section = .; /* Export global symbol at bss end. */
+ __bss_end__ = _end_of_bss_section;
+ } > RAM
+
+ /* Used to check that there is enough RAM left. */
+ ._user_heap_stack : {
+ . = ALIGN(4);
+ PROVIDE(end = .);
+ PROVIDE(_end = .);
+ . = . + _size_of_heap;
+ . = . + _size_of_stack;
+ . = ALIGN(4);
+ } > RAM
+
+ /* Remove information from the standard libraries. */
+ /DISCARD/ : {
+ libc.a ( * )
+ libm.a ( * )
+ libgcc.a ( * )
+ }
+
+ .ARM.attributes 0 : {
+ *(.ARM.attributes)
+ }
+}
diff --git a/examples/baremetal/stm32f103/greenblink/gcc/startup.s b/examples/baremetal/stm32f103/greenblink/gcc/startup.s
new file mode 100644
index 000000000..5bb262f16
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/gcc/startup.s
@@ -0,0 +1,177 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+.syntax unified
+.cpu cortex-m3
+.fpu softvfp
+.thumb
+
+// These symbols are exported from the linker script.
+.word _start_of_init_data_section
+.word _start_of_data_section
+.word _end_of_data_section
+.word _start_of_bss_section
+.word _end_of_bss_section
+
+.equ _boot_ram, 0xF108F85F
+
+.section .text.reset_handler
+.weak reset_handler
+.type reset_handler, %function
+reset_handler:
+ // Copy the data segment initializers from flash to SRAM.
+ movs r1, #0
+ b _loop_copy_data_init
+_copy_data_init:
+ ldr r3, =_start_of_init_data_section
+ ldr r3, [r3, r1]
+ str r3, [r0, r1]
+ adds r1, r1, #4
+_loop_copy_data_init:
+ ldr r0, =_start_of_data_section
+ ldr r3, =_end_of_data_section
+ adds r2, r0, r1
+ cmp r2, r3
+ bcc _copy_data_init
+ ldr r2, =_start_of_bss_section
+ b _loop_fill_zero_bss
+
+ // Zero fill the bss segment.
+_fill_zero_bss:
+ movs r3, #0
+ str r3, [r2], #4
+_loop_fill_zero_bss:
+ ldr r3, = _end_of_bss_section
+ cmp r2, r3
+ bcc _fill_zero_bss
+
+ // Call the static constructors.
+ bl __libc_init_array
+
+ // Call the application entry point.
+ bl main
+ bx lr
+.size reset_handler, .-reset_handler
+
+// Define the empty vectors table.
+.section .isr_vector,"a",%progbits
+.type _vectors_table, %object
+.size _vectors_table, .-_vectors_table
+_vectors_table:
+ // Generic interrupts offset.
+ .word _end_of_stack // Initial stack pointer value.
+ .word reset_handler // Reset.
+ .word 0 // NMI.
+ .word 0 // Hard fault.
+ .word 0 // Memory management fault.
+ .word 0 // Bus fault.
+ .word 0 // Usage fault.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // Reserved.
+ .word 0 // SVC.
+ .word 0 // Debug monitor.
+ .word 0 // Reserved.
+ .word 0 // PendSV.
+ .word 0 // SysTick.
+
+ // External interrupts offset.
+ .word 0 // Window watchdog.
+ .word 0 // PVD through EXTI Line detection.
+ .word 0 // Tamper and timestamps through the EXTI line.
+ .word 0 // RTC wakeup through the EXTI line.
+ .word 0 // FLASH.
+ .word 0 // RCC.
+ .word 0 // EXTI line0.
+ .word 0 // EXTI line1.
+ .word 0 // EXTI line2.
+ .word 0 // EXTI line3.
+ .word 0 // EXTI line4.
+ .word 0 // DMA1 stream 0.
+ .word 0 // DMA1 stream 1.
+ .word 0 // DMA1 stream 2.
+ .word 0 // DMA1 stream 3.
+ .word 0 // DMA1 stream 4.
+ .word 0 // DMA1 stream 5.
+ .word 0 // DMA1 stream 6.
+ .word 0 // ADC1/2.
+ .word 0 // USB HP/CAN1 TX.
+ .word 0 // USB LP / CAN1 RX0.
+ .word 0 // CAN1 RX1.
+ .word 0 // CAN1 SCE.
+ .word 0 // EXTI line [9:5]s.
+ .word 0 // TIM1 break.
+ .word 0 // TIM1 update.
+ .word 0 // TIM1 trigger and communication.
+ .word 0 // TIM1 capture compare.
+ .word 0 // TIM2.
+ .word 0 // TIM3.
+ .word 0 // TIM4.
+ .word 0 // I2C1 event.
+ .word 0 // I2C1 error.
+ .word 0 // I2C2 event.
+ .word 0 // I2C2 error.
+ .word 0 // SPI1.
+ .word 0 // SPI2.
+ .word 0 // USART1.
+ .word 0 // USART2.
+ .word 0 // USART3.
+ .word 0 // EXTI line [15:10]s.
+ .word 0 // RTC alarm.
+ .word 0 // USB wakeup.
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word 0
+ .word _boot_ram // Boot RAM mode for stm32f10x medium density devices.
diff --git a/examples/baremetal/stm32f103/greenblink/gpio.c b/examples/baremetal/stm32f103/greenblink/gpio.c
new file mode 100644
index 000000000..84c87a2b9
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/gpio.c
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "gpio.h"
+#include "system.h"
+
+#define GPIO_GREEN_LED_PIN_POS (13u)
+#define GPIO_GREEN_LED_PIN (1u << GPIO_GREEN_LED_PIN_POS)
+
+// Bit definition for RCC_APB2ENR register.
+#define RCC_APB2ENR_IOPCEN_POS (4u)
+#define RCC_APB2ENR_IOPCEN (0x1u << RCC_APB2ENR_IOPCEN_POS)
+
+// Bit definition for GPIO_CRL register.
+#define GPIO_CRL_MODE_POS (0u) // MODE field position.
+#define GPIO_CRL_MODE_MSK (0x3u << GPIO_CRL_MODE_POS) // MODE field mask.
+#define GPIO_CRL_MODE_OUT_FREQ_LOW (0x2u << GPIO_CRL_MODE_POS) // As output with low frequency.
+
+#define GPIO_CRL_CNF_POS (2u) // CNF field position.
+#define GPIO_CRL_CNF_MSK (0x3u << GPIO_CRL_CNF_POS) // CNF field mask.
+#define GPIO_CRL_CNF_OUTPUT_PP (0x00000000u) // General purpose output push-pull.
+
+void gpio_init_green_led(void)
+{
+ // Enable RCC clock on GPIOC port.
+ RCC_REGS_MAP->APB2ENR |= RCC_APB2ENR_IOPCEN;
+ // Configure GPIOC pin #13.
+ const uint32_t offset = ((GPIO_GREEN_LED_PIN_POS - 8u) << 2u);
+ GPIOC_REGS_MAP->CRH &= ~((GPIO_CRL_MODE_MSK | GPIO_CRL_CNF_MSK) << offset);
+ GPIOC_REGS_MAP->CRH |= ((GPIO_CRL_MODE_OUT_FREQ_LOW | GPIO_CRL_CNF_OUTPUT_PP) << offset);
+}
+
+void gpio_toggle_green_led(void)
+{
+ GPIOC_REGS_MAP->ODR ^= GPIO_GREEN_LED_PIN;
+}
diff --git a/examples/baremetal/stm32f103/greenblink/gpio.h b/examples/baremetal/stm32f103/greenblink/gpio.h
new file mode 100644
index 000000000..9e931b715
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/gpio.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 GPIO_H
+#define GPIO_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void gpio_init_green_led(void);
+void gpio_toggle_green_led(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // GPIO_H
diff --git a/examples/baremetal/stm32f103/greenblink/greenblink.qbs b/examples/baremetal/stm32f103/greenblink/greenblink.qbs
new file mode 100644
index 000000000..18fe8e536
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/greenblink.qbs
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+CppApplication {
+ condition: {
+ if (!qbs.architecture.startsWith("arm"))
+ return false;
+ return (qbs.toolchain.contains("gcc")
+ || qbs.toolchain.contains("iar")
+ || qbs.toolchain.contains("keil"))
+ && !qbs.toolchain.contains("xcode")
+ }
+ name: "stm32f103-greenblink"
+ cpp.cLanguageVersion: "c99"
+ cpp.positionIndependentCode: false
+
+ //
+ // GCC-specific properties and sources.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("gcc")
+ cpp.assemblerFlags: [
+ "-mcpu=cortex-m3"
+ ]
+ cpp.driverFlags: [
+ "-mcpu=cortex-m3",
+ "-specs=nosys.specs"
+ ]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("gcc")
+ name: "GCC"
+ prefix: "gcc/"
+ Group {
+ name: "Startup"
+ fileTags: ["asm"]
+ files: ["startup.s"]
+ }
+ Group {
+ name: "Linker Script"
+ fileTags: ["linkerscript"]
+ files: ["flash.ld"]
+ }
+ }
+
+ //
+ // IAR-specific properties and sources.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("iar")
+ cpp.assemblerFlags: [
+ "--cpu", "cortex-m3",
+ "--fpu", "none"
+ ]
+ cpp.driverFlags: [
+ "--cpu", "cortex-m3",
+ "--fpu", "none"
+ ]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("iar")
+ name: "IAR"
+ prefix: "iar/"
+ Group {
+ name: "Startup"
+ fileTags: ["asm"]
+ files: ["startup.s"]
+ }
+ Group {
+ name: "Linker Script"
+ fileTags: ["linkerscript"]
+ files: ["flash.icf"]
+ }
+ }
+
+ //
+ // KEIL-specific properties and sources.
+ //
+
+ Properties {
+ condition: qbs.toolchain.contains("keil")
+ cpp.assemblerFlags: [
+ "--cpu", "cortex-m3"
+ ]
+ cpp.driverFlags: [
+ "--cpu", "cortex-m3"
+ ]
+ }
+
+ Group {
+ condition: qbs.toolchain.contains("keil")
+ name: "KEIL"
+ prefix: "keil/"
+ Group {
+ name: "Startup"
+ fileTags: ["asm"]
+ files: ["startup.s"]
+ }
+ Group {
+ name: "Linker Script"
+ fileTags: ["linkerscript"]
+ files: ["flash.sct"]
+ }
+ }
+
+ //
+ // Common code.
+ //
+
+ Group {
+ name: "Gpio"
+ files: ["gpio.c", "gpio.h"]
+ }
+
+ Group {
+ name: "System"
+ files: ["system.h"]
+ }
+
+ files: ["main.c"]
+}
diff --git a/examples/baremetal/stm32f103/greenblink/iar/flash.icf b/examples/baremetal/stm32f103/greenblink/iar/flash.icf
new file mode 100644
index 000000000..1b424d29f
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/iar/flash.icf
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+define symbol _start_of_intvec_section = 0x08000000;
+
+define symbol _start_of_flash_section = 0x08000000;
+define symbol _end_of_flash_section = 0x0801FFFF;
+define symbol _start_of_ram_section = 0x20000000;
+define symbol _end_of_ram_section = 0x20004FFF;
+
+define symbol _min_stack_size = 0x400;
+define symbol _min_heap_size = 0x200;
+
+define memory mem with size = 4G;
+define region flash_section = mem:[from _start_of_flash_section to _end_of_flash_section];
+define region ram_section = mem:[from _start_of_ram_section to _end_of_ram_section];
+
+define block CSTACK with alignment = 8, size = _min_stack_size { };
+define block HEAP with alignment = 8, size = _min_heap_size { };
+
+initialize by copy { readwrite };
+do not initialize { section .noinit };
+
+place at address mem:_start_of_intvec_section { readonly section .intvec };
+
+place in flash_section { readonly };
+place in ram_section { readwrite, block CSTACK, block HEAP };
diff --git a/examples/baremetal/stm32f103/greenblink/iar/startup.s b/examples/baremetal/stm32f103/greenblink/iar/startup.s
new file mode 100644
index 000000000..cda2a4e7b
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/iar/startup.s
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+ MODULE ?cstartup
+
+ SECTION CSTACK:DATA:NOROOT(3)
+ SECTION .intvec:CODE:NOROOT(2)
+
+ EXTERN __iar_program_start
+
+ PUBLIC _vectors_table
+ DATA
+_vectors_table
+ ;; Generic interrupts offset.
+ DCD sfe(CSTACK) ; Initial stack pointer value.
+ DCD reset_handler ; Reset.
+ DCD 0 ; NMI.
+ DCD 0 ; Hard fault.
+ DCD 0 ; MPU fault.
+ DCD 0 ; Bus fault.
+ DCD 0 ; Usage fault.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; SVC.
+ DCD 0 ; Debug monitor.
+ DCD 0 ; Reserved.
+ DCD 0 ; PendSV.
+ DCD 0 ; SysTick.
+ ;; External interrupts offset.
+ DCD 0 ; Window watchdog.
+ DCD 0 ; PVD through EXTI line detection.
+ DCD 0 ; Tamper.
+ DCD 0 ; RTC.
+ DCD 0 ; Flash.
+ DCD 0 ; RCC.
+ DCD 0 ; EXTI line 0.
+ DCD 0 ; EXTI line 1.
+ DCD 0 ; EXTI line 2.
+ DCD 0 ; EXTI line 3.
+ DCD 0 ; EXTI line 4.
+ DCD 0 ; DMA1 channel 1.
+ DCD 0 ; DMA1 channel 2.
+ DCD 0 ; DMA1 channel 3.
+ DCD 0 ; DMA1 channel 4.
+ DCD 0 ; DMA1 channel 5.
+ DCD 0 ; DMA1 channel 6.
+ DCD 0 ; DMA1 channel 7.
+ DCD 0 ; ADC1/2.
+ DCD 0 ; USB high priority or CAN1 TX.
+ DCD 0 ; USB low priority or CAN1 RX0.
+ DCD 0 ; CAN1 RX1.
+ DCD 0 ; CAN1 SCE.
+ DCD 0 ; EXTI line 9..5.
+ DCD 0 ; TIM1 break.
+ DCD 0 ; TIM1 update.
+ DCD 0 ; TIM1 trigger and commutation.
+ DCD 0 ; TIM1 capture compare.
+ DCD 0 ; TIM2.
+ DCD 0 ; TIM3.
+ DCD 0 ; TIM4.
+ DCD 0 ; I2C1 event.
+ DCD 0 ; I2C1 error.
+ DCD 0 ; I2C2 event.
+ DCD 0 ; I2C2 error.
+ DCD 0 ; SPI1.
+ DCD 0 ; SPI2.
+ DCD 0 ; USART1.
+ DCD 0 ; USART2.
+ DCD 0 ; USART3.
+ DCD 0 ; EXTI line 15..10.
+ DCD 0 ; RTC alarm through EXTI line.
+ DCD 0 ; USB wakeup from suspend.
+
+ ;; Reset handler.
+ THUMB
+ PUBWEAK reset_handler
+ SECTION .text:CODE:REORDER:NOROOT(2)
+reset_handler
+ BLX R0
+ LDR R0, =__iar_program_start
+ BX R0
+
+ END
diff --git a/examples/baremetal/stm32f103/greenblink/keil/flash.sct b/examples/baremetal/stm32f103/greenblink/keil/flash.sct
new file mode 100644
index 000000000..3547ca2c0
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/keil/flash.sct
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+;; Load region size_region.
+LR_IROM1 0x08000000 0x00010000 {
+ ;; Load address = execution address.
+ ER_IROM1 0x08000000 0x00010000 {
+ *.o (RESET, +First)
+ *(InRoot$$Sections)
+ .ANY (+RO)
+ .ANY (+XO)
+ }
+
+ ; RW data.
+ RW_IRAM1 0x20000000 0x00005000 {
+ .ANY (+RW +ZI)
+ }
+}
diff --git a/examples/baremetal/stm32f103/greenblink/keil/startup.s b/examples/baremetal/stm32f103/greenblink/keil/startup.s
new file mode 100644
index 000000000..d80de94c6
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/keil/startup.s
@@ -0,0 +1,145 @@
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+;; Contact: https://www.qt.io/licensing/
+;;
+;; This file is part of the examples 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$
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+_size_of_stack EQU 0x400
+_size_of_heap EQU 0x200
+
+;; Stack configuration.
+ AREA STACK, NOINIT, READWRITE, ALIGN=3
+_start_of_stack SPACE _size_of_stack
+_end_of_stack
+
+;; Heap configuration.
+ AREA HEAP, NOINIT, READWRITE, ALIGN=3
+_start_of_heap SPACE _size_of_heap
+_end_of_heap
+
+ PRESERVE8
+ THUMB
+
+;; Define the empty vectors table.
+ AREA RESET, DATA, READONLY
+_vectors_table
+ ;; Generic interrupts offset.
+ DCD _end_of_stack ; Initial stack pointer value.
+ DCD reset_handler ; Reset.
+ DCD 0 ; NMI.
+ DCD 0 ; Hard fault.
+ DCD 0 ; Memory management fault.
+ DCD 0 ; Bus fault.
+ DCD 0 ; Usage fault.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; Reserved.
+ DCD 0 ; SVC.
+ DCD 0 ; Debug monitor.
+ DCD 0 ; Reserved.
+ DCD 0 ; PendSV.
+ DCD 0 ; SysTick.
+ ;; External interrupts offset.
+ DCD 0 ; Window watchdog.
+ DCD 0 ; PVD through EXTI line detection.
+ DCD 0 ; Tamper and TimeStamps through the EXTI line.
+ DCD 0 ; RTC wakeup through the EXTI line.
+ DCD 0 ; FLASH.
+ DCD 0 ; RCC.
+ DCD 0 ; EXTI line0.
+ DCD 0 ; EXTI line1.
+ DCD 0 ; EXTI line2.
+ DCD 0 ; EXTI line3.
+ DCD 0 ; EXTI line4.
+ DCD 0 ; DMA1 channel 1.
+ DCD 0 ; DMA1 channel 2.
+ DCD 0 ; DMA1 channel 3.
+ DCD 0 ; DMA1 channel 4.
+ DCD 0 ; DMA1 channel 5.
+ DCD 0 ; DMA1 channel 6.
+ DCD 0 ; DMA1 channel 7.
+ DCD 0 ; ADC1/2.
+ DCD 0 ; USB high priority or CAN1 TX.
+ DCD 0 ; USB low priority or CAN1 RX0.
+ DCD 0 ; CAN1 RX1.
+ DCD 0 ; CAN1 SCE.
+ DCD 0 ; EXTI line 9..5.
+ DCD 0 ; TIM1 break.
+ DCD 0 ; TIM1 update.
+ DCD 0 ; TIM1 trigger and commutation.
+ DCD 0 ; IM1 capture compare.
+ DCD 0 ; TIM2.
+ DCD 0 ; TIM3.
+ DCD 0 ; TIM4.
+ DCD 0 ; I2C1 event.
+ DCD 0 ; I2C1 error.
+ DCD 0 ; I2C2 event.
+ DCD 0 ; I2C2 error.
+ DCD 0 ; SPI1.
+ DCD 0 ; SPI2.
+ DCD 0 ; USART1.
+ DCD 0 ; USART2.
+ DCD 0 ; USART3.
+ DCD 0 ; EXTI line 15..10.
+ DCD 0 ; RTC alarm through EXTI line.
+ DCD 0 ; USB wakeup from suspend.
+_end_of_vectors_table
+
+_size_of_vectors_table EQU _end_of_vectors_table - _vectors_table
+
+ AREA |.text|, CODE, READONLY
+;; Reset handler.
+reset_handler PROC
+ EXPORT reset_handler [WEAK]
+ IMPORT main
+ LDR R0, =main
+ BX R0
+ ENDP
+ ALIGN
+
+ END
diff --git a/examples/baremetal/stm32f103/greenblink/main.c b/examples/baremetal/stm32f103/greenblink/main.c
new file mode 100644
index 000000000..21bc5badc
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/main.c
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "gpio.h"
+
+#include <stdint.h>
+
+static void some_delay(uint32_t counts)
+{
+ for (uint32_t index = 0u; index < counts; ++index)
+ __asm("nop");
+}
+
+int main(void)
+{
+ gpio_init_green_led();
+
+ while (1) {
+ gpio_toggle_green_led();
+ some_delay(100000u);
+ }
+}
diff --git a/examples/baremetal/stm32f103/greenblink/system.h b/examples/baremetal/stm32f103/greenblink/system.h
new file mode 100644
index 000000000..d18854bea
--- /dev/null
+++ b/examples/baremetal/stm32f103/greenblink/system.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 SYSTEM_H
+#define SYSTEM_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define __IO volatile
+
+// General purpose input/output registers map.
+struct gpio_regs_map {
+ __IO uint32_t CRL;
+ __IO uint32_t CRH;
+ __IO uint32_t IDR;
+ __IO uint32_t ODR;
+ __IO uint32_t BSRR;
+ __IO uint32_t BRR;
+ __IO uint32_t LCKR;
+};
+
+// Reset and clock control registers map.
+struct rcc_regs_map {
+ __IO uint32_t CR;
+ __IO uint32_t CFGR;
+ __IO uint32_t CIR;
+ __IO uint32_t APB2RSTR;
+ __IO uint32_t APB1RSTR;
+ __IO uint32_t AHBENR;
+ __IO uint32_t APB2ENR;
+ __IO uint32_t APB1ENR;
+ __IO uint32_t BDCR;
+ __IO uint32_t CSR;
+};
+
+#define PERIPH_ADDRESS (0x40000000u)
+
+#define APB2PERIPH_ADDRESS (PERIPH_ADDRESS + 0x00010000u)
+#define AHBPERIPH_ADDRESS (PERIPH_ADDRESS + 0x00020000u)
+
+#define GPIOC_REGS_ADDRESS (APB2PERIPH_ADDRESS + 0x00001000u)
+#define RCC_REGS_ADDRESS (AHBPERIPH_ADDRESS + 0x00001000u)
+
+#define GPIOC_REGS_MAP ((struct gpio_regs_map *)GPIOC_REGS_ADDRESS)
+#define RCC_REGS_MAP ((struct rcc_regs_map *)RCC_REGS_ADDRESS)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // SYSTEM_H
diff --git a/examples/baremetal/stm32f103/stm32f103.qbs b/examples/baremetal/stm32f103/stm32f103.qbs
new file mode 100644
index 000000000..705502c52
--- /dev/null
+++ b/examples/baremetal/stm32f103/stm32f103.qbs
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+Project {
+ name: "Examples for stm32f103 board"
+ references: [
+ "greenblink/greenblink.qbs"
+ ]
+}
diff --git a/examples/baremetal/stm32f4discovery/blueblink/blueblink.qbs b/examples/baremetal/stm32f4discovery/blueblink/blueblink.qbs
index 5ccf8b3b0..f2e1dfcc9 100644
--- a/examples/baremetal/stm32f4discovery/blueblink/blueblink.qbs
+++ b/examples/baremetal/stm32f4discovery/blueblink/blueblink.qbs
@@ -48,15 +48,14 @@
**
****************************************************************************/
-import qbs
-
CppApplication {
condition: {
- if (!qbs.architecture.contains("arm"))
+ if (!qbs.architecture.startsWith("arm"))
return false;
- return qbs.toolchain.contains("gcc")
- || qbs.toolchain.contains("iar")
- || qbs.toolchain.contains("keil")
+ return (qbs.toolchain.contains("gcc")
+ || qbs.toolchain.contains("iar")
+ || qbs.toolchain.contains("keil"))
+ && !qbs.toolchain.contains("xcode")
}
name: "stm32f4discovery-blueblink"
cpp.cLanguageVersion: "c99"
@@ -68,6 +67,11 @@ CppApplication {
Properties {
condition: qbs.toolchain.contains("gcc")
+ cpp.assemblerFlags: [
+ "-mcpu=cortex-m4",
+ "-mfloat-abi=hard",
+ "-mfpu=fpv4-sp-d16",
+ ]
cpp.driverFlags: [
"-mcpu=cortex-m4",
"-mfloat-abi=hard",
@@ -98,6 +102,10 @@ CppApplication {
Properties {
condition: qbs.toolchain.contains("iar")
+ cpp.assemblerFlags: [
+ "--cpu", "cortex-m4",
+ "--fpu", "vfpv4_sp"
+ ]
cpp.driverFlags: [
"--cpu", "cortex-m4",
"--fpu", "vfpv4_sp"
@@ -126,6 +134,9 @@ CppApplication {
Properties {
condition: qbs.toolchain.contains("keil")
+ cpp.assemblerFlags: [
+ "--cpu", "cortex-m4.fp"
+ ]
cpp.driverFlags: [
"--cpu", "cortex-m4.fp"
]
diff --git a/examples/baremetal/stm32f4discovery/stm32f4discovery.qbs b/examples/baremetal/stm32f4discovery/stm32f4discovery.qbs
index a402848ca..cbb3bdc3a 100644
--- a/examples/baremetal/stm32f4discovery/stm32f4discovery.qbs
+++ b/examples/baremetal/stm32f4discovery/stm32f4discovery.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
Project {
name: "Examples for stm32f4discovery board"
references: [
diff --git a/examples/baremetal/stm8s103f3/redblink/redblink.qbs b/examples/baremetal/stm8s103f3/redblink/redblink.qbs
index 197bae8ae..c01f323cd 100644
--- a/examples/baremetal/stm8s103f3/redblink/redblink.qbs
+++ b/examples/baremetal/stm8s103f3/redblink/redblink.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
CppApplication {
condition: {
if (!qbs.architecture.contains("stm8"))
diff --git a/examples/baremetal/stm8s103f3/stm8s103f3.qbs b/examples/baremetal/stm8s103f3/stm8s103f3.qbs
index 922c4c1f0..cfbfc87a8 100644
--- a/examples/baremetal/stm8s103f3/stm8s103f3.qbs
+++ b/examples/baremetal/stm8s103f3/stm8s103f3.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs
-
Project {
name: "Examples for stm8s103f3 board"
references: [
diff --git a/examples/capnproto/addressbook_cpp/addressbook.capnp b/examples/capnproto/addressbook_cpp/addressbook.capnp
new file mode 100644
index 000000000..1a6c60937
--- /dev/null
+++ b/examples/capnproto/addressbook_cpp/addressbook.capnp
@@ -0,0 +1,55 @@
+# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
+# Licensed under the MIT License:
+#
+# 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.
+
+@0x9eb32e19f86ee174;
+
+using Cxx = import "/capnp/c++.capnp";
+$Cxx.namespace("addressbook");
+
+struct Person {
+ id @0 :UInt32;
+ name @1 :Text;
+ email @2 :Text;
+ phones @3 :List(PhoneNumber);
+
+ struct PhoneNumber {
+ number @0 :Text;
+ type @1 :Type;
+
+ enum Type {
+ mobile @0;
+ home @1;
+ work @2;
+ }
+ }
+
+ employment :union {
+ unemployed @4 :Void;
+ employer @5 :Text;
+ school @6 :Text;
+ selfEmployed @7 :Void;
+ # We assume that a person is only one of these.
+ }
+}
+
+struct AddressBook {
+ people @0 :List(Person);
+}
diff --git a/examples/capnproto/addressbook_cpp/addressbook.cpp b/examples/capnproto/addressbook_cpp/addressbook.cpp
new file mode 100644
index 000000000..b2bece947
--- /dev/null
+++ b/examples/capnproto/addressbook_cpp/addressbook.cpp
@@ -0,0 +1,288 @@
+// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
+// Licensed under the MIT License:
+//
+// 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.
+
+// This sample code appears in the documentation for the C++ implementation.
+//
+// If Cap'n Proto is installed, build the sample like:
+// capnp compile -oc++ addressbook.capnp
+// c++ -std=c++14 -Wall addressbook.c++ addressbook.capnp.c++ `pkg-config --cflags --libs capnp` -o addressbook
+//
+// If Cap'n Proto is not installed, but the source is located at $SRC and has been
+// compiled in $BUILD (often both are simply ".." from here), you can do:
+// $BUILD/capnp compile -I$SRC/src -o$BUILD/capnpc-c++ addressbook.capnp
+// c++ -std=c++14 -Wall addressbook.c++ addressbook.capnp.c++ -I$SRC/src -L$BUILD/.libs -lcapnp -lkj -o addressbook
+//
+// Run like:
+// ./addressbook write | ./addressbook read
+// Use "dwrite" and "dread" to use dynamic code instead.
+
+// TODO(test): Needs cleanup.
+
+#include "addressbook.capnp.h"
+#include <capnp/message.h>
+#include <capnp/serialize-packed.h>
+#include <iostream>
+
+using addressbook::Person;
+using addressbook::AddressBook;
+
+void writeAddressBook(int fd) {
+ ::capnp::MallocMessageBuilder message;
+
+ AddressBook::Builder addressBook = message.initRoot<AddressBook>();
+ ::capnp::List<Person>::Builder people = addressBook.initPeople(2);
+
+ Person::Builder alice = people[0];
+ alice.setId(123);
+ alice.setName("Alice");
+ alice.setEmail("alice@example.com");
+ // Type shown for explanation purposes; normally you'd use auto.
+ ::capnp::List<Person::PhoneNumber>::Builder alicePhones =
+ alice.initPhones(1);
+ alicePhones[0].setNumber("555-1212");
+ alicePhones[0].setType(Person::PhoneNumber::Type::MOBILE);
+ alice.getEmployment().setSchool("MIT");
+
+ Person::Builder bob = people[1];
+ bob.setId(456);
+ bob.setName("Bob");
+ bob.setEmail("bob@example.com");
+ auto bobPhones = bob.initPhones(2);
+ bobPhones[0].setNumber("555-4567");
+ bobPhones[0].setType(Person::PhoneNumber::Type::HOME);
+ bobPhones[1].setNumber("555-7654");
+ bobPhones[1].setType(Person::PhoneNumber::Type::WORK);
+ bob.getEmployment().setUnemployed();
+
+ writePackedMessageToFd(fd, message);
+}
+
+void printAddressBook(int fd) {
+ ::capnp::PackedFdMessageReader message(fd);
+
+ AddressBook::Reader addressBook = message.getRoot<AddressBook>();
+
+ for (Person::Reader person : addressBook.getPeople()) {
+ std::cout << person.getName().cStr() << ": "
+ << person.getEmail().cStr() << std::endl;
+ for (Person::PhoneNumber::Reader phone: person.getPhones()) {
+ const char* typeName = "UNKNOWN";
+ switch (phone.getType()) {
+ case Person::PhoneNumber::Type::MOBILE: typeName = "mobile"; break;
+ case Person::PhoneNumber::Type::HOME: typeName = "home"; break;
+ case Person::PhoneNumber::Type::WORK: typeName = "work"; break;
+ }
+ std::cout << " " << typeName << " phone: "
+ << phone.getNumber().cStr() << std::endl;
+ }
+ Person::Employment::Reader employment = person.getEmployment();
+ switch (employment.which()) {
+ case Person::Employment::UNEMPLOYED:
+ std::cout << " unemployed" << std::endl;
+ break;
+ case Person::Employment::EMPLOYER:
+ std::cout << " employer: "
+ << employment.getEmployer().cStr() << std::endl;
+ break;
+ case Person::Employment::SCHOOL:
+ std::cout << " student at: "
+ << employment.getSchool().cStr() << std::endl;
+ break;
+ case Person::Employment::SELF_EMPLOYED:
+ std::cout << " self-employed" << std::endl;
+ break;
+ }
+ }
+}
+
+#if !CAPNP_LITE
+
+#include "addressbook.capnp.h"
+#include <capnp/message.h>
+#include <capnp/serialize-packed.h>
+#include <iostream>
+#include <capnp/schema.h>
+#include <capnp/dynamic.h>
+
+using ::capnp::DynamicValue;
+using ::capnp::DynamicStruct;
+using ::capnp::DynamicEnum;
+using ::capnp::DynamicList;
+using ::capnp::List;
+using ::capnp::Schema;
+using ::capnp::StructSchema;
+using ::capnp::EnumSchema;
+
+using ::capnp::Void;
+using ::capnp::Text;
+using ::capnp::MallocMessageBuilder;
+using ::capnp::PackedFdMessageReader;
+
+void dynamicWriteAddressBook(int fd, StructSchema schema) {
+ // Write a message using the dynamic API to set each
+ // field by text name. This isn't something you'd
+ // normally want to do; it's just for illustration.
+
+ MallocMessageBuilder message;
+
+ // Types shown for explanation purposes; normally you'd
+ // use auto.
+ DynamicStruct::Builder addressBook =
+ message.initRoot<DynamicStruct>(schema);
+
+ DynamicList::Builder people =
+ addressBook.init("people", 2).as<DynamicList>();
+
+ DynamicStruct::Builder alice =
+ people[0].as<DynamicStruct>();
+ alice.set("id", 123);
+ alice.set("name", "Alice");
+ alice.set("email", "alice@example.com");
+ auto alicePhones = alice.init("phones", 1).as<DynamicList>();
+ auto phone0 = alicePhones[0].as<DynamicStruct>();
+ phone0.set("number", "555-1212");
+ phone0.set("type", "mobile");
+ alice.get("employment").as<DynamicStruct>()
+ .set("school", "MIT");
+
+ auto bob = people[1].as<DynamicStruct>();
+ bob.set("id", 456);
+ bob.set("name", "Bob");
+ bob.set("email", "bob@example.com");
+
+ // Some magic: We can convert a dynamic sub-value back to
+ // the native type with as<T>()!
+ List<Person::PhoneNumber>::Builder bobPhones =
+ bob.init("phones", 2).as<List<Person::PhoneNumber>>();
+ bobPhones[0].setNumber("555-4567");
+ bobPhones[0].setType(Person::PhoneNumber::Type::HOME);
+ bobPhones[1].setNumber("555-7654");
+ bobPhones[1].setType(Person::PhoneNumber::Type::WORK);
+ bob.get("employment").as<DynamicStruct>()
+ .set("unemployed", ::capnp::VOID);
+
+ writePackedMessageToFd(fd, message);
+}
+
+void dynamicPrintValue(DynamicValue::Reader value) {
+ // Print an arbitrary message via the dynamic API by
+ // iterating over the schema. Look at the handling
+ // of STRUCT in particular.
+
+ switch (value.getType()) {
+ case DynamicValue::VOID:
+ std::cout << "";
+ break;
+ case DynamicValue::BOOL:
+ std::cout << (value.as<bool>() ? "true" : "false");
+ break;
+ case DynamicValue::INT:
+ std::cout << value.as<int64_t>();
+ break;
+ case DynamicValue::UINT:
+ std::cout << value.as<uint64_t>();
+ break;
+ case DynamicValue::FLOAT:
+ std::cout << value.as<double>();
+ break;
+ case DynamicValue::TEXT:
+ std::cout << '\"' << value.as<Text>().cStr() << '\"';
+ break;
+ case DynamicValue::LIST: {
+ std::cout << "[";
+ bool first = true;
+ for (auto element: value.as<DynamicList>()) {
+ if (first) {
+ first = false;
+ } else {
+ std::cout << ", ";
+ }
+ dynamicPrintValue(element);
+ }
+ std::cout << "]";
+ break;
+ }
+ case DynamicValue::ENUM: {
+ auto enumValue = value.as<DynamicEnum>();
+ KJ_IF_MAYBE(enumerant, enumValue.getEnumerant()) {
+ std::cout <<
+ enumerant->getProto().getName().cStr();
+ } else {
+ // Unknown enum value; output raw number.
+ std::cout << enumValue.getRaw();
+ }
+ break;
+ }
+ case DynamicValue::STRUCT: {
+ std::cout << "(";
+ auto structValue = value.as<DynamicStruct>();
+ bool first = true;
+ for (auto field: structValue.getSchema().getFields()) {
+ if (!structValue.has(field)) continue;
+ if (first) {
+ first = false;
+ } else {
+ std::cout << ", ";
+ }
+ std::cout << field.getProto().getName().cStr()
+ << " = ";
+ dynamicPrintValue(structValue.get(field));
+ }
+ std::cout << ")";
+ break;
+ }
+ default:
+ // There are other types, we aren't handling them.
+ std::cout << "?";
+ break;
+ }
+}
+
+void dynamicPrintMessage(int fd, StructSchema schema) {
+ PackedFdMessageReader message(fd);
+ dynamicPrintValue(message.getRoot<DynamicStruct>(schema));
+ std::cout << std::endl;
+}
+
+#endif // !CAPNP_LITE
+
+int main(int argc, char* argv[]) {
+ if (argc != 2) {
+ std::cerr << "Missing arg." << std::endl;
+ return 1;
+ } else if (strcmp(argv[1], "write") == 0) {
+ writeAddressBook(1);
+ } else if (strcmp(argv[1], "read") == 0) {
+ printAddressBook(0);
+#if !CAPNP_LITE
+ } else if (strcmp(argv[1], "dwrite") == 0) {
+ StructSchema schema = Schema::from<AddressBook>();
+ dynamicWriteAddressBook(1, schema);
+ } else if (strcmp(argv[1], "dread") == 0) {
+ StructSchema schema = Schema::from<AddressBook>();
+ dynamicPrintMessage(0, schema);
+#endif
+ } else {
+ std::cerr << "Invalid arg: " << argv[1] << std::endl;
+ return 1;
+ }
+ return 0;
+}
diff --git a/examples/capnproto/addressbook_cpp/addressbook_cpp.qbs b/examples/capnproto/addressbook_cpp/addressbook_cpp.qbs
new file mode 100644
index 000000000..33f78d5a5
--- /dev/null
+++ b/examples/capnproto/addressbook_cpp/addressbook_cpp.qbs
@@ -0,0 +1,11 @@
+CppApplication {
+ Depends { name: "capnproto.cpp"; required: false }
+ condition: capnproto.cpp.present && qbs.targetPlatform === qbs.hostPlatform
+ consoleApplication: true
+ cpp.minimumMacosVersion: "10.8"
+
+ files: [
+ "addressbook.capnp",
+ "addressbook.cpp"
+ ]
+}
diff --git a/examples/capnproto/calculator_cpp/calculator-client.cpp b/examples/capnproto/calculator_cpp/calculator-client.cpp
new file mode 100644
index 000000000..5d8452921
--- /dev/null
+++ b/examples/capnproto/calculator_cpp/calculator-client.cpp
@@ -0,0 +1,367 @@
+// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
+// Licensed under the MIT License:
+//
+// 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 "calculator.capnp.h"
+#include <capnp/ez-rpc.h>
+#include <kj/debug.h>
+#include <math.h>
+#include <iostream>
+
+class PowerFunction final: public Calculator::Function::Server {
+ // An implementation of the Function interface wrapping pow(). Note that
+ // we're implementing this on the client side and will pass a reference to
+ // the server. The server will then be able to make calls back to the client.
+
+public:
+ kj::Promise<void> call(CallContext context) {
+ auto params = context.getParams().getParams();
+ KJ_REQUIRE(params.size() == 2, "Wrong number of parameters.");
+ context.getResults().setValue(pow(params[0], params[1]));
+ return kj::READY_NOW;
+ }
+};
+
+int main(int argc, const char* argv[]) {
+ if (argc != 2) {
+ std::cerr << "usage: " << argv[0] << " HOST:PORT\n"
+ "Connects to the Calculator server at the given address and "
+ "does some RPCs." << std::endl;
+ return 1;
+ }
+
+ capnp::EzRpcClient client(argv[1]);
+ Calculator::Client calculator = client.getMain<Calculator>();
+
+ // Keep an eye on `waitScope`. Whenever you see it used is a place where we
+ // stop and wait for the server to respond. If a line of code does not use
+ // `waitScope`, then it does not block!
+ auto& waitScope = client.getWaitScope();
+
+ {
+ // Make a request that just evaluates the literal value 123.
+ //
+ // What's interesting here is that evaluate() returns a "Value", which is
+ // another interface and therefore points back to an object living on the
+ // server. We then have to call read() on that object to read it.
+ // However, even though we are making two RPC's, this block executes in
+ // *one* network round trip because of promise pipelining: we do not wait
+ // for the first call to complete before we send the second call to the
+ // server.
+
+ std::cout << "Evaluating a literal... ";
+ std::cout.flush();
+
+ // Set up the request.
+ auto request = calculator.evaluateRequest();
+ request.getExpression().setLiteral(123);
+
+ // Send it, which returns a promise for the result (without blocking).
+ auto evalPromise = request.send();
+
+ // Using the promise, create a pipelined request to call read() on the
+ // returned object, and then send that.
+ auto readPromise = evalPromise.getValue().readRequest().send();
+
+ // Now that we've sent all the requests, wait for the response. Until this
+ // point, we haven't waited at all!
+ auto response = readPromise.wait(waitScope);
+ KJ_ASSERT(response.getValue() == 123);
+
+ std::cout << "PASS" << std::endl;
+ }
+
+ {
+ // Make a request to evaluate 123 + 45 - 67.
+ //
+ // The Calculator interface requires that we first call getOperator() to
+ // get the addition and subtraction functions, then call evaluate() to use
+ // them. But, once again, we can get both functions, call evaluate(), and
+ // then read() the result -- four RPCs -- in the time of *one* network
+ // round trip, because of promise pipelining.
+
+ std::cout << "Using add and subtract... ";
+ std::cout.flush();
+
+ Calculator::Function::Client add = nullptr;
+ Calculator::Function::Client subtract = nullptr;
+
+ {
+ // Get the "add" function from the server.
+ auto request = calculator.getOperatorRequest();
+ request.setOp(Calculator::Operator::ADD);
+ add = request.send().getFunc();
+ }
+
+ {
+ // Get the "subtract" function from the server.
+ auto request = calculator.getOperatorRequest();
+ request.setOp(Calculator::Operator::SUBTRACT);
+ subtract = request.send().getFunc();
+ }
+
+ // Build the request to evaluate 123 + 45 - 67.
+ auto request = calculator.evaluateRequest();
+
+ auto subtractCall = request.getExpression().initCall();
+ subtractCall.setFunction(subtract);
+ auto subtractParams = subtractCall.initParams(2);
+ subtractParams[1].setLiteral(67);
+
+ auto addCall = subtractParams[0].initCall();
+ addCall.setFunction(add);
+ auto addParams = addCall.initParams(2);
+ addParams[0].setLiteral(123);
+ addParams[1].setLiteral(45);
+
+ // Send the evaluate() request, read() the result, and wait for read() to
+ // finish.
+ auto evalPromise = request.send();
+ auto readPromise = evalPromise.getValue().readRequest().send();
+
+ auto response = readPromise.wait(waitScope);
+ KJ_ASSERT(response.getValue() == 101);
+
+ std::cout << "PASS" << std::endl;
+ }
+
+ {
+ // Make a request to evaluate 4 * 6, then use the result in two more
+ // requests that add 3 and 5.
+ //
+ // Since evaluate() returns its result wrapped in a `Value`, we can pass
+ // that `Value` back to the server in subsequent requests before the first
+ // `evaluate()` has actually returned. Thus, this example again does only
+ // one network round trip.
+
+ std::cout << "Pipelining eval() calls... ";
+ std::cout.flush();
+
+ Calculator::Function::Client add = nullptr;
+ Calculator::Function::Client multiply = nullptr;
+
+ {
+ // Get the "add" function from the server.
+ auto request = calculator.getOperatorRequest();
+ request.setOp(Calculator::Operator::ADD);
+ add = request.send().getFunc();
+ }
+
+ {
+ // Get the "multiply" function from the server.
+ auto request = calculator.getOperatorRequest();
+ request.setOp(Calculator::Operator::MULTIPLY);
+ multiply = request.send().getFunc();
+ }
+
+ // Build the request to evaluate 4 * 6
+ auto request = calculator.evaluateRequest();
+
+ auto multiplyCall = request.getExpression().initCall();
+ multiplyCall.setFunction(multiply);
+ auto multiplyParams = multiplyCall.initParams(2);
+ multiplyParams[0].setLiteral(4);
+ multiplyParams[1].setLiteral(6);
+
+ auto multiplyResult = request.send().getValue();
+
+ // Use the result in two calls that add 3 and add 5.
+
+ auto add3Request = calculator.evaluateRequest();
+ auto add3Call = add3Request.getExpression().initCall();
+ add3Call.setFunction(add);
+ auto add3Params = add3Call.initParams(2);
+ add3Params[0].setPreviousResult(multiplyResult);
+ add3Params[1].setLiteral(3);
+ auto add3Promise = add3Request.send().getValue().readRequest().send();
+
+ auto add5Request = calculator.evaluateRequest();
+ auto add5Call = add5Request.getExpression().initCall();
+ add5Call.setFunction(add);
+ auto add5Params = add5Call.initParams(2);
+ add5Params[0].setPreviousResult(multiplyResult);
+ add5Params[1].setLiteral(5);
+ auto add5Promise = add5Request.send().getValue().readRequest().send();
+
+ // Now wait for the results.
+ KJ_ASSERT(add3Promise.wait(waitScope).getValue() == 27);
+ KJ_ASSERT(add5Promise.wait(waitScope).getValue() == 29);
+
+ std::cout << "PASS" << std::endl;
+ }
+
+ {
+ // Our calculator interface supports defining functions. Here we use it
+ // to define two functions and then make calls to them as follows:
+ //
+ // f(x, y) = x * 100 + y
+ // g(x) = f(x, x + 1) * 2;
+ // f(12, 34)
+ // g(21)
+ //
+ // Once again, the whole thing takes only one network round trip.
+
+ std::cout << "Defining functions... ";
+ std::cout.flush();
+
+ Calculator::Function::Client add = nullptr;
+ Calculator::Function::Client multiply = nullptr;
+ Calculator::Function::Client f = nullptr;
+ Calculator::Function::Client g = nullptr;
+
+ {
+ // Get the "add" function from the server.
+ auto request = calculator.getOperatorRequest();
+ request.setOp(Calculator::Operator::ADD);
+ add = request.send().getFunc();
+ }
+
+ {
+ // Get the "multiply" function from the server.
+ auto request = calculator.getOperatorRequest();
+ request.setOp(Calculator::Operator::MULTIPLY);
+ multiply = request.send().getFunc();
+ }
+
+ {
+ // Define f.
+ auto request = calculator.defFunctionRequest();
+ request.setParamCount(2);
+
+ {
+ // Build the function body.
+ auto addCall = request.getBody().initCall();
+ addCall.setFunction(add);
+ auto addParams = addCall.initParams(2);
+ addParams[1].setParameter(1); // y
+
+ auto multiplyCall = addParams[0].initCall();
+ multiplyCall.setFunction(multiply);
+ auto multiplyParams = multiplyCall.initParams(2);
+ multiplyParams[0].setParameter(0); // x
+ multiplyParams[1].setLiteral(100);
+ }
+
+ f = request.send().getFunc();
+ }
+
+ {
+ // Define g.
+ auto request = calculator.defFunctionRequest();
+ request.setParamCount(1);
+
+ {
+ // Build the function body.
+ auto multiplyCall = request.getBody().initCall();
+ multiplyCall.setFunction(multiply);
+ auto multiplyParams = multiplyCall.initParams(2);
+ multiplyParams[1].setLiteral(2);
+
+ auto fCall = multiplyParams[0].initCall();
+ fCall.setFunction(f);
+ auto fParams = fCall.initParams(2);
+ fParams[0].setParameter(0);
+
+ auto addCall = fParams[1].initCall();
+ addCall.setFunction(add);
+ auto addParams = addCall.initParams(2);
+ addParams[0].setParameter(0);
+ addParams[1].setLiteral(1);
+ }
+
+ g = request.send().getFunc();
+ }
+
+ // OK, we've defined all our functions. Now create our eval requests.
+
+ // f(12, 34)
+ auto fEvalRequest = calculator.evaluateRequest();
+ auto fCall = fEvalRequest.initExpression().initCall();
+ fCall.setFunction(f);
+ auto fParams = fCall.initParams(2);
+ fParams[0].setLiteral(12);
+ fParams[1].setLiteral(34);
+ auto fEvalPromise = fEvalRequest.send().getValue().readRequest().send();
+
+ // g(21)
+ auto gEvalRequest = calculator.evaluateRequest();
+ auto gCall = gEvalRequest.initExpression().initCall();
+ gCall.setFunction(g);
+ gCall.initParams(1)[0].setLiteral(21);
+ auto gEvalPromise = gEvalRequest.send().getValue().readRequest().send();
+
+ // Wait for the results.
+ KJ_ASSERT(fEvalPromise.wait(waitScope).getValue() == 1234);
+ KJ_ASSERT(gEvalPromise.wait(waitScope).getValue() == 4244);
+
+ std::cout << "PASS" << std::endl;
+ }
+
+ {
+ // Make a request that will call back to a function defined locally.
+ //
+ // Specifically, we will compute 2^(4 + 5). However, exponent is not
+ // defined by the Calculator server. So, we'll implement the Function
+ // interface locally and pass it to the server for it to use when
+ // evaluating the expression.
+ //
+ // This example requires two network round trips to complete, because the
+ // server calls back to the client once before finishing. In this
+ // particular case, this could potentially be optimized by using a tail
+ // call on the server side -- see CallContext::tailCall(). However, to
+ // keep the example simpler, we haven't implemented this optimization in
+ // the sample server.
+
+ std::cout << "Using a callback... ";
+ std::cout.flush();
+
+ Calculator::Function::Client add = nullptr;
+
+ {
+ // Get the "add" function from the server.
+ auto request = calculator.getOperatorRequest();
+ request.setOp(Calculator::Operator::ADD);
+ add = request.send().getFunc();
+ }
+
+ // Build the eval request for 2^(4+5).
+ auto request = calculator.evaluateRequest();
+
+ auto powCall = request.getExpression().initCall();
+ powCall.setFunction(kj::heap<PowerFunction>());
+ auto powParams = powCall.initParams(2);
+ powParams[0].setLiteral(2);
+
+ auto addCall = powParams[1].initCall();
+ addCall.setFunction(add);
+ auto addParams = addCall.initParams(2);
+ addParams[0].setLiteral(4);
+ addParams[1].setLiteral(5);
+
+ // Send the request and wait.
+ auto response = request.send().getValue().readRequest()
+ .send().wait(waitScope);
+ KJ_ASSERT(response.getValue() == 512);
+
+ std::cout << "PASS" << std::endl;
+ }
+
+ return 0;
+}
diff --git a/examples/capnproto/calculator_cpp/calculator-server.cpp b/examples/capnproto/calculator_cpp/calculator-server.cpp
new file mode 100644
index 000000000..c2593be3a
--- /dev/null
+++ b/examples/capnproto/calculator_cpp/calculator-server.cpp
@@ -0,0 +1,215 @@
+// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
+// Licensed under the MIT License:
+//
+// 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 "calculator.capnp.h"
+#include <kj/debug.h>
+#include <capnp/ez-rpc.h>
+#include <capnp/message.h>
+#include <iostream>
+
+typedef unsigned int uint;
+
+kj::Promise<double> readValue(Calculator::Value::Client value) {
+ // Helper function to asynchronously call read() on a Calculator::Value and
+ // return a promise for the result. (In the future, the generated code might
+ // include something like this automatically.)
+
+ return value.readRequest().send()
+ .then([](capnp::Response<Calculator::Value::ReadResults> result) {
+ return result.getValue();
+ });
+}
+
+kj::Promise<double> evaluateImpl(
+ Calculator::Expression::Reader expression,
+ capnp::List<double>::Reader params = capnp::List<double>::Reader()) {
+ // Implementation of CalculatorImpl::evaluate(), also shared by
+ // FunctionImpl::call(). In the latter case, `params` are the parameter
+ // values passed to the function; in the former case, `params` is just an
+ // empty list.
+
+ switch (expression.which()) {
+ case Calculator::Expression::LITERAL:
+ return expression.getLiteral();
+
+ case Calculator::Expression::PREVIOUS_RESULT:
+ return readValue(expression.getPreviousResult());
+
+ case Calculator::Expression::PARAMETER: {
+ KJ_REQUIRE(expression.getParameter() < params.size(),
+ "Parameter index out-of-range.");
+ return params[expression.getParameter()];
+ }
+
+ case Calculator::Expression::CALL: {
+ auto call = expression.getCall();
+ auto func = call.getFunction();
+
+ // Evaluate each parameter.
+ kj::Array<kj::Promise<double>> paramPromises =
+ KJ_MAP(param, call.getParams()) {
+ return evaluateImpl(param, params);
+ };
+
+ // Join the array of promises into a promise for an array.
+ kj::Promise<kj::Array<double>> joinedParams =
+ kj::joinPromises(kj::mv(paramPromises));
+
+ // When the parameters are complete, call the function.
+ return joinedParams.then([KJ_CPCAP(func)](kj::Array<double>&& paramValues) mutable {
+ auto request = func.callRequest();
+ request.setParams(paramValues);
+ return request.send().then(
+ [](capnp::Response<Calculator::Function::CallResults>&& result) {
+ return result.getValue();
+ });
+ });
+ }
+
+ default:
+ // Throw an exception.
+ KJ_FAIL_REQUIRE("Unknown expression type.");
+ }
+}
+
+class ValueImpl final: public Calculator::Value::Server {
+ // Simple implementation of the Calculator.Value Cap'n Proto interface.
+
+public:
+ ValueImpl(double value): value(value) {}
+
+ kj::Promise<void> read(ReadContext context) {
+ context.getResults().setValue(value);
+ return kj::READY_NOW;
+ }
+
+private:
+ double value;
+};
+
+class FunctionImpl final: public Calculator::Function::Server {
+ // Implementation of the Calculator.Function Cap'n Proto interface, where the
+ // function is defined by a Calculator.Expression.
+
+public:
+ FunctionImpl(uint paramCount, Calculator::Expression::Reader body)
+ : paramCount(paramCount) {
+ this->body.setRoot(body);
+ }
+
+ kj::Promise<void> call(CallContext context) {
+ auto params = context.getParams().getParams();
+ KJ_REQUIRE(params.size() == paramCount, "Wrong number of parameters.");
+
+ return evaluateImpl(body.getRoot<Calculator::Expression>(), params)
+ .then([KJ_CPCAP(context)](double value) mutable {
+ context.getResults().setValue(value);
+ });
+ }
+
+private:
+ uint paramCount;
+ // The function's arity.
+
+ capnp::MallocMessageBuilder body;
+ // Stores a permanent copy of the function body.
+};
+
+class OperatorImpl final: public Calculator::Function::Server {
+ // Implementation of the Calculator.Function Cap'n Proto interface, wrapping
+ // basic binary arithmetic operators.
+
+public:
+ OperatorImpl(Calculator::Operator op): op(op) {}
+
+ kj::Promise<void> call(CallContext context) {
+ auto params = context.getParams().getParams();
+ KJ_REQUIRE(params.size() == 2, "Wrong number of parameters.");
+
+ double result;
+ switch (op) {
+ case Calculator::Operator::ADD: result = params[0] + params[1]; break;
+ case Calculator::Operator::SUBTRACT:result = params[0] - params[1]; break;
+ case Calculator::Operator::MULTIPLY:result = params[0] * params[1]; break;
+ case Calculator::Operator::DIVIDE: result = params[0] / params[1]; break;
+ default:
+ KJ_FAIL_REQUIRE("Unknown operator.");
+ }
+
+ context.getResults().setValue(result);
+ return kj::READY_NOW;
+ }
+
+private:
+ Calculator::Operator op;
+};
+
+class CalculatorImpl final: public Calculator::Server {
+ // Implementation of the Calculator Cap'n Proto interface.
+
+public:
+ kj::Promise<void> evaluate(EvaluateContext context) override {
+ return evaluateImpl(context.getParams().getExpression())
+ .then([KJ_CPCAP(context)](double value) mutable {
+ context.getResults().setValue(kj::heap<ValueImpl>(value));
+ });
+ }
+
+ kj::Promise<void> defFunction(DefFunctionContext context) override {
+ auto params = context.getParams();
+ context.getResults().setFunc(kj::heap<FunctionImpl>(
+ params.getParamCount(), params.getBody()));
+ return kj::READY_NOW;
+ }
+
+ kj::Promise<void> getOperator(GetOperatorContext context) override {
+ context.getResults().setFunc(kj::heap<OperatorImpl>(
+ context.getParams().getOp()));
+ return kj::READY_NOW;
+ }
+};
+
+int main(int argc, const char* argv[]) {
+ if (argc != 2) {
+ std::cerr << "usage: " << argv[0] << " ADDRESS[:PORT]\n"
+ "Runs the server bound to the given address/port.\n"
+ "ADDRESS may be '*' to bind to all local addresses.\n"
+ ":PORT may be omitted to choose a port automatically." << std::endl;
+ return 1;
+ }
+
+ // Set up a server.
+ capnp::EzRpcServer server(kj::heap<CalculatorImpl>(), argv[1]);
+
+ // Write the port number to stdout, in case it was chosen automatically.
+ auto& waitScope = server.getWaitScope();
+ uint port = server.getPort().wait(waitScope);
+ if (port == 0) {
+ // The address format "unix:/path/to/socket" opens a unix domain socket,
+ // in which case the port will be zero.
+ std::cout << "Listening on Unix socket..." << std::endl;
+ } else {
+ std::cout << "Listening on port " << port << "..." << std::endl;
+ }
+
+ // Run forever, accepting connections and handling requests.
+ kj::NEVER_DONE.wait(waitScope);
+}
diff --git a/examples/capnproto/calculator_cpp/calculator.capnp b/examples/capnproto/calculator_cpp/calculator.capnp
new file mode 100644
index 000000000..adc8294e5
--- /dev/null
+++ b/examples/capnproto/calculator_cpp/calculator.capnp
@@ -0,0 +1,118 @@
+# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
+# Licensed under the MIT License:
+#
+# 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.
+
+@0x85150b117366d14b;
+
+interface Calculator {
+ # A "simple" mathematical calculator, callable via RPC.
+ #
+ # But, to show off Cap'n Proto, we add some twists:
+ #
+ # - You can use the result from one call as the input to the next
+ # without a network round trip. To accomplish this, evaluate()
+ # returns a `Value` object wrapping the actual numeric value.
+ # This object may be used in a subsequent expression. With
+ # promise pipelining, the Value can actually be used before
+ # the evaluate() call that creates it returns!
+ #
+ # - You can define new functions, and then call them. This again
+ # shows off pipelining, but it also gives the client the
+ # opportunity to define a function on the client side and have
+ # the server call back to it.
+ #
+ # - The basic arithmetic operators are exposed as Functions, and
+ # you have to call getOperator() to obtain them from the server.
+ # This again demonstrates pipelining -- using getOperator() to
+ # get each operator and then using them in evaluate() still
+ # only takes one network round trip.
+
+ evaluate @0 (expression :Expression) -> (value :Value);
+ # Evaluate the given expression and return the result. The
+ # result is returned wrapped in a Value interface so that you
+ # may pass it back to the server in a pipelined request. To
+ # actually get the numeric value, you must call read() on the
+ # Value -- but again, this can be pipelined so that it incurs
+ # no additional latency.
+
+ struct Expression {
+ # A numeric expression.
+
+ union {
+ literal @0 :Float64;
+ # A literal numeric value.
+
+ previousResult @1 :Value;
+ # A value that was (or, will be) returned by a previous
+ # evaluate().
+
+ parameter @2 :UInt32;
+ # A parameter to the function (only valid in function bodies;
+ # see defFunction).
+
+ call :group {
+ # Call a function on a list of parameters.
+ function @3 :Function;
+ params @4 :List(Expression);
+ }
+ }
+ }
+
+ interface Value {
+ # Wraps a numeric value in an RPC object. This allows the value
+ # to be used in subsequent evaluate() requests without the client
+ # waiting for the evaluate() that returns the Value to finish.
+
+ read @0 () -> (value :Float64);
+ # Read back the raw numeric value.
+ }
+
+ defFunction @1 (paramCount :Int32, body :Expression)
+ -> (func :Function);
+ # Define a function that takes `paramCount` parameters and returns the
+ # evaluation of `body` after substituting these parameters.
+
+ interface Function {
+ # An algebraic function. Can be called directly, or can be used inside
+ # an Expression.
+ #
+ # A client can create a Function that runs on the server side using
+ # `defFunction()` or `getOperator()`. Alternatively, a client can
+ # implement a Function on the client side and the server will call back
+ # to it. However, a function defined on the client side will require a
+ # network round trip whenever the server needs to call it, whereas
+ # functions defined on the server and then passed back to it are called
+ # locally.
+
+ call @0 (params :List(Float64)) -> (value :Float64);
+ # Call the function on the given parameters.
+ }
+
+ getOperator @2 (op :Operator) -> (func :Function);
+ # Get a Function representing an arithmetic operator, which can then be
+ # used in Expressions.
+
+ enum Operator {
+ add @0;
+ subtract @1;
+ multiply @2;
+ divide @3;
+ }
+}
diff --git a/examples/capnproto/calculator_cpp/calculator_cpp.qbs b/examples/capnproto/calculator_cpp/calculator_cpp.qbs
new file mode 100644
index 000000000..a884117b2
--- /dev/null
+++ b/examples/capnproto/calculator_cpp/calculator_cpp.qbs
@@ -0,0 +1,28 @@
+import qbs.Host
+
+Project {
+ CppApplication {
+ Depends { name: "capnproto.cpp"; required: false }
+ name: "server"
+ condition: capnproto.cpp.present && qbs.targetPlatform === Host.platform()
+ consoleApplication: true
+ capnproto.cpp.useRpc: true
+
+ files: [
+ "calculator.capnp",
+ "calculator-server.cpp"
+ ]
+ }
+ CppApplication {
+ Depends { name: "capnproto.cpp"; required: false }
+ name: "client"
+ condition: capnproto.cpp.present && qbs.targetPlatform === Host.platform()
+ consoleApplication: true
+ capnproto.cpp.useRpc: true
+
+ files: [
+ "calculator.capnp",
+ "calculator-client.cpp"
+ ]
+ }
+}
diff --git a/examples/cocoa-application/CocoaApplication.qbs b/examples/cocoa-application/CocoaApplication.qbs
deleted file mode 100644
index 472758764..000000000
--- a/examples/cocoa-application/CocoaApplication.qbs
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Petroules Corporation.
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples 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$
-**
-****************************************************************************/
-
-import qbs
-import qbs.Utilities
-
-Project {
- references: [
- "app.qbs"
- ]
-
- SubProject {
- filePath: "dmg.qbs"
- Properties {
- condition: Utilities.versionCompare(qbs.version, "1.9") >= 0
- }
- }
-}
diff --git a/examples/cocoa-application/CocoaApplication/en_US.lproj b/examples/cocoa-application/CocoaApplication/en_US.lproj
deleted file mode 120000
index 545e7ac76..000000000
--- a/examples/cocoa-application/CocoaApplication/en_US.lproj
+++ /dev/null
@@ -1 +0,0 @@
-en.lproj \ No newline at end of file
diff --git a/examples/cocoa-application/CocoaApplication/en_US.lproj/Credits.rtf b/examples/cocoa-application/CocoaApplication/en_US.lproj/Credits.rtf
new file mode 100644
index 000000000..9caf316e0
--- /dev/null
+++ b/examples/cocoa-application/CocoaApplication/en_US.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_US.lproj/InfoPlist.strings b/examples/cocoa-application/CocoaApplication/en_US.lproj/InfoPlist.strings
new file mode 100644
index 000000000..b92732c79
--- /dev/null
+++ b/examples/cocoa-application/CocoaApplication/en_US.lproj/InfoPlist.strings
@@ -0,0 +1 @@
+/* Localized versions of Info.plist keys */
diff --git a/examples/cocoa-application/CocoaApplication/en_US.lproj/LICENSE b/examples/cocoa-application/CocoaApplication/en_US.lproj/LICENSE
new file mode 100644
index 000000000..44650fe73
--- /dev/null
+++ b/examples/cocoa-application/CocoaApplication/en_US.lproj/LICENSE
@@ -0,0 +1,27 @@
+Copyright (C) 2017 The Qt Company Ltd.
+
+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.
diff --git a/examples/cocoa-application/CocoaApplication/en_US.lproj/MainMenu.xib b/examples/cocoa-application/CocoaApplication/en_US.lproj/MainMenu.xib
new file mode 100644
index 000000000..faedd4637
--- /dev/null
+++ b/examples/cocoa-application/CocoaApplication/en_US.lproj/MainMenu.xib
@@ -0,0 +1,4666 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="8.00">
+ <data>
+ <int key="IBDocument.SystemTarget">1080</int>
+ <string key="IBDocument.SystemVersion">11D50</string>
+ <string key="IBDocument.InterfaceBuilderVersion">2457</string>
+ <string key="IBDocument.AppKitVersion">1138.32</string>
+ <string key="IBDocument.HIToolboxVersion">568.00</string>
+ <object class="NSMutableDictionary" key="IBDocument.PluginVersions">
+ <string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="NS.object.0">2457</string>
+ </object>
+ <array key="IBDocument.IntegratedClassDependencies">
+ <string>NSWindowTemplate</string>
+ <string>NSView</string>
+ <string>NSMenu</string>
+ <string>NSMenuItem</string>
+ <string>NSCustomObject</string>
+ </array>
+ <array key="IBDocument.PluginDependencies">
+ <string>com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </array>
+ <object class="NSMutableDictionary" key="IBDocument.Metadata">
+ <string key="NS.key.0">PluginDependencyRecalculationVersion</string>
+ <integer value="1" key="NS.object.0"/>
+ </object>
+ <array class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
+ <object class="NSCustomObject" id="1021">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSCustomObject" id="1014">
+ <string key="NSClassName">FirstResponder</string>
+ </object>
+ <object class="NSCustomObject" id="1050">
+ <string key="NSClassName">NSApplication</string>
+ </object>
+ <object class="NSMenu" id="649796088">
+ <string key="NSTitle">AMainMenu</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="694149608">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">CocoaApplication</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <object class="NSCustomResource" key="NSOnImage" id="35465992">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuCheckmark</string>
+ </object>
+ <object class="NSCustomResource" key="NSMixedImage" id="502551668">
+ <string key="NSClassName">NSImage</string>
+ <string key="NSResourceName">NSMenuMixedState</string>
+ </object>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="110575045">
+ <string key="NSTitle">CocoaApplication</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="238522557">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">About CocoaApplication</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="304266470">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="609285721">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Preferences…</string>
+ <string key="NSKeyEquiv">,</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="481834944">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1046388886">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Services</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="752062318">
+ <string key="NSTitle">Services</string>
+ <array class="NSMutableArray" key="NSMenuItems"/>
+ <string key="NSName">_NSServicesMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="646227648">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="755159360">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide CocoaApplication</string>
+ <string key="NSKeyEquiv">h</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="342932134">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Hide Others</string>
+ <string key="NSKeyEquiv">h</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="908899353">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Show All</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1056857174">
+ <reference key="NSMenu" ref="110575045"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="632727374">
+ <reference key="NSMenu" ref="110575045"/>
+ <string key="NSTitle">Quit CocoaApplication</string>
+ <string key="NSKeyEquiv">q</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ <string key="NSName">_NSAppleMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="379814623">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">File</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="720053764">
+ <string key="NSTitle">File</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="705341025">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">New</string>
+ <string key="NSKeyEquiv">n</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="722745758">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Open…</string>
+ <string key="NSKeyEquiv">o</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1025936716">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Open Recent</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="1065607017">
+ <string key="NSTitle">Open Recent</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="759406840">
+ <reference key="NSMenu" ref="1065607017"/>
+ <string key="NSTitle">Clear Menu</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ <string key="NSName">_NSRecentDocumentsMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="425164168">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="776162233">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Close</string>
+ <string key="NSKeyEquiv">w</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1023925487">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Save…</string>
+ <string key="NSKeyEquiv">s</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="579971712">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Revert to Saved</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1010469920">
+ <reference key="NSMenu" ref="720053764"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="294629803">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Page Setup...</string>
+ <string key="NSKeyEquiv">P</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSToolTip"/>
+ </object>
+ <object class="NSMenuItem" id="49223823">
+ <reference key="NSMenu" ref="720053764"/>
+ <string key="NSTitle">Print…</string>
+ <string key="NSKeyEquiv">p</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="952259628">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Edit</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="789758025">
+ <string key="NSTitle">Edit</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="1058277027">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Undo</string>
+ <string key="NSKeyEquiv">z</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="790794224">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Redo</string>
+ <string key="NSKeyEquiv">Z</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1040322652">
+ <reference key="NSMenu" ref="789758025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="296257095">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Cut</string>
+ <string key="NSKeyEquiv">x</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="860595796">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Copy</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="29853731">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Paste</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="82994268">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Paste and Match Style</string>
+ <string key="NSKeyEquiv">V</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="437104165">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Delete</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="583158037">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Select All</string>
+ <string key="NSKeyEquiv">a</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="212016141">
+ <reference key="NSMenu" ref="789758025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="892235320">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Find</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="963351320">
+ <string key="NSTitle">Find</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="447796847">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find…</string>
+ <string key="NSKeyEquiv">f</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="738670835">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find and Replace…</string>
+ <string key="NSKeyEquiv">f</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">12</int>
+ </object>
+ <object class="NSMenuItem" id="326711663">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find Next</string>
+ <string key="NSKeyEquiv">g</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="270902937">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Find Previous</string>
+ <string key="NSKeyEquiv">G</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="159080638">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Use Selection for Find</string>
+ <string key="NSKeyEquiv">e</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">7</int>
+ </object>
+ <object class="NSMenuItem" id="88285865">
+ <reference key="NSMenu" ref="963351320"/>
+ <string key="NSTitle">Jump to Selection</string>
+ <string key="NSKeyEquiv">j</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="972420730">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Spelling and Grammar</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="769623530">
+ <string key="NSTitle">Spelling and Grammar</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="679648819">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Show Spelling and Grammar</string>
+ <string key="NSKeyEquiv">:</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="96193923">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Document Now</string>
+ <string key="NSKeyEquiv">;</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="859480356">
+ <reference key="NSMenu" ref="769623530"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="948374510">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Spelling While Typing</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="967646866">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Check Grammar With Spelling</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="795346622">
+ <reference key="NSMenu" ref="769623530"/>
+ <string key="NSTitle">Correct Spelling Automatically</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="507821607">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Substitutions</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="698887838">
+ <string key="NSTitle">Substitutions</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="65139061">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Show Substitutions</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="19036812">
+ <reference key="NSMenu" ref="698887838"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="605118523">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Copy/Paste</string>
+ <string key="NSKeyEquiv">f</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="197661976">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Quotes</string>
+ <string key="NSKeyEquiv">g</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="672708820">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Dashes</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="708854459">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Smart Links</string>
+ <string key="NSKeyEquiv">G</string>
+ <int key="NSKeyEquivModMask">1179648</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="537092702">
+ <reference key="NSMenu" ref="698887838"/>
+ <string key="NSTitle">Text Replacement</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="288088188">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Transformations</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="579392910">
+ <string key="NSTitle">Transformations</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="1060694897">
+ <reference key="NSMenu" ref="579392910"/>
+ <string key="NSTitle">Make Upper Case</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="879586729">
+ <reference key="NSMenu" ref="579392910"/>
+ <string key="NSTitle">Make Lower Case</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="56570060">
+ <reference key="NSMenu" ref="579392910"/>
+ <string key="NSTitle">Capitalize</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="676164635">
+ <reference key="NSMenu" ref="789758025"/>
+ <string key="NSTitle">Speech</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="785027613">
+ <string key="NSTitle">Speech</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="731782645">
+ <reference key="NSMenu" ref="785027613"/>
+ <string key="NSTitle">Start Speaking</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="680220178">
+ <reference key="NSMenu" ref="785027613"/>
+ <string key="NSTitle">Stop Speaking</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="302598603">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Format</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="941447902">
+ <string key="NSTitle">Format</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="792887677">
+ <reference key="NSMenu" ref="941447902"/>
+ <string key="NSTitle">Font</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="786677654">
+ <string key="NSTitle">Font</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="159677712">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Show Fonts</string>
+ <string key="NSKeyEquiv">t</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="305399458">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Bold</string>
+ <string key="NSKeyEquiv">b</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">2</int>
+ </object>
+ <object class="NSMenuItem" id="814362025">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Italic</string>
+ <string key="NSKeyEquiv">i</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">1</int>
+ </object>
+ <object class="NSMenuItem" id="330926929">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Underline</string>
+ <string key="NSKeyEquiv">u</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="533507878">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="158063935">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Bigger</string>
+ <string key="NSKeyEquiv">+</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">3</int>
+ </object>
+ <object class="NSMenuItem" id="885547335">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Smaller</string>
+ <string key="NSKeyEquiv">-</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <int key="NSTag">4</int>
+ </object>
+ <object class="NSMenuItem" id="901062459">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="767671776">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Kern</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="175441468">
+ <string key="NSTitle">Kern</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="252969304">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Use Default</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="766922938">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Use None</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="677519740">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Tighten</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="238351151">
+ <reference key="NSMenu" ref="175441468"/>
+ <string key="NSTitle">Loosen</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="691570813">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Ligatures</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="1058217995">
+ <string key="NSTitle">Ligatures</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="706297211">
+ <reference key="NSMenu" ref="1058217995"/>
+ <string key="NSTitle">Use Default</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="568384683">
+ <reference key="NSMenu" ref="1058217995"/>
+ <string key="NSTitle">Use None</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="663508465">
+ <reference key="NSMenu" ref="1058217995"/>
+ <string key="NSTitle">Use All</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="769124883">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Baseline</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="18263474">
+ <string key="NSTitle">Baseline</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="257962622">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Use Default</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="644725453">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Superscript</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1037576581">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Subscript</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="941806246">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Raise</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1045724900">
+ <reference key="NSMenu" ref="18263474"/>
+ <string key="NSTitle">Lower</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="739652853">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="1012600125">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Show Colors</string>
+ <string key="NSKeyEquiv">C</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="214559597">
+ <reference key="NSMenu" ref="786677654"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="596732606">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Copy Style</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="393423671">
+ <reference key="NSMenu" ref="786677654"/>
+ <string key="NSTitle">Paste Style</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ <string key="NSName">_NSFontMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="215659978">
+ <reference key="NSMenu" ref="941447902"/>
+ <string key="NSTitle">Text</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="446991534">
+ <string key="NSTitle">Text</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="875092757">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Align Left</string>
+ <string key="NSKeyEquiv">{</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="630155264">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Center</string>
+ <string key="NSKeyEquiv">|</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="945678886">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Justify</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="512868991">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Align Right</string>
+ <string key="NSKeyEquiv">}</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="163117631">
+ <reference key="NSMenu" ref="446991534"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="31516759">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Writing Direction</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="956096989">
+ <string key="NSTitle">Writing Direction</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="257099033">
+ <reference key="NSMenu" ref="956096989"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <string key="NSTitle">Paragraph</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="551969625">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="249532473">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="607364498">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="508151438">
+ <reference key="NSMenu" ref="956096989"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="981751889">
+ <reference key="NSMenu" ref="956096989"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <string key="NSTitle">Selection</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="380031999">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CURlZmF1bHQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="825984362">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CUxlZnQgdG8gUmlnaHQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="560145579">
+ <reference key="NSMenu" ref="956096989"/>
+ <string type="base64-UTF8" key="NSTitle">CVJpZ2h0IHRvIExlZnQ</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="908105787">
+ <reference key="NSMenu" ref="446991534"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="644046920">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Show Ruler</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="231811626">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Copy Ruler</string>
+ <string key="NSKeyEquiv">c</string>
+ <int key="NSKeyEquivModMask">1310720</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="883618387">
+ <reference key="NSMenu" ref="446991534"/>
+ <string key="NSTitle">Paste Ruler</string>
+ <string key="NSKeyEquiv">v</string>
+ <int key="NSKeyEquivModMask">1310720</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="586577488">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">View</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="466310130">
+ <string key="NSTitle">View</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="102151532">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Show Toolbar</string>
+ <string key="NSKeyEquiv">t</string>
+ <int key="NSKeyEquivModMask">1572864</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="237841660">
+ <reference key="NSMenu" ref="466310130"/>
+ <string key="NSTitle">Customize Toolbar…</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="713487014">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Window</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="835318025">
+ <string key="NSTitle">Window</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="1011231497">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Minimize</string>
+ <string key="NSKeyEquiv">m</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="575023229">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Zoom</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="299356726">
+ <reference key="NSMenu" ref="835318025"/>
+ <bool key="NSIsDisabled">YES</bool>
+ <bool key="NSIsSeparator">YES</bool>
+ <string key="NSTitle"/>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ <object class="NSMenuItem" id="625202149">
+ <reference key="NSMenu" ref="835318025"/>
+ <string key="NSTitle">Bring All to Front</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ <string key="NSName">_NSWindowsMenu</string>
+ </object>
+ </object>
+ <object class="NSMenuItem" id="448692316">
+ <reference key="NSMenu" ref="649796088"/>
+ <string key="NSTitle">Help</string>
+ <string key="NSKeyEquiv"/>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ <string key="NSAction">submenuAction:</string>
+ <object class="NSMenu" key="NSSubmenu" id="992780483">
+ <string key="NSTitle">Help</string>
+ <array class="NSMutableArray" key="NSMenuItems">
+ <object class="NSMenuItem" id="105068016">
+ <reference key="NSMenu" ref="992780483"/>
+ <string key="NSTitle">CocoaApplication Help</string>
+ <string key="NSKeyEquiv">?</string>
+ <int key="NSKeyEquivModMask">1048576</int>
+ <int key="NSMnemonicLoc">2147483647</int>
+ <reference key="NSOnImage" ref="35465992"/>
+ <reference key="NSMixedImage" ref="502551668"/>
+ </object>
+ </array>
+ <string key="NSName">_NSHelpMenu</string>
+ </object>
+ </object>
+ </array>
+ <string key="NSName">_NSMainMenu</string>
+ </object>
+ <object class="NSWindowTemplate" id="972006081">
+ <int key="NSWindowStyleMask">15</int>
+ <int key="NSWindowBacking">2</int>
+ <string key="NSWindowRect">{{335, 390}, {480, 360}}</string>
+ <int key="NSWTFlags">1954021376</int>
+ <string key="NSWindowTitle">CocoaApplication</string>
+ <string key="NSWindowClass">NSWindow</string>
+ <nil key="NSViewClass"/>
+ <nil key="NSUserInterfaceItemIdentifier"/>
+ <object class="NSView" key="NSWindowView" id="439893737">
+ <nil key="NSNextResponder"/>
+ <int key="NSvFlags">256</int>
+ <string key="NSFrameSize">{480, 360}</string>
+ </object>
+ <string key="NSScreenRect">{{0, 0}, {2560, 1418}}</string>
+ <string key="NSMaxSize">{10000000000000, 10000000000000}</string>
+ <bool key="NSWindowIsRestorable">YES</bool>
+ </object>
+ <object class="NSCustomObject" id="976324537">
+ <string key="NSClassName">AppDelegate</string>
+ </object>
+ <object class="NSCustomObject" id="755631768">
+ <string key="NSClassName">NSFontManager</string>
+ </object>
+ </array>
+ <object class="IBObjectContainer" key="IBDocument.Objects">
+ <array class="NSMutableArray" key="connectionRecords">
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">terminate:</string>
+ <reference key="source" ref="1050"/>
+ <reference key="destination" ref="632727374"/>
+ </object>
+ <int key="connectionID">449</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontStandardAboutPanel:</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="238522557"/>
+ </object>
+ <int key="connectionID">142</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">delegate</string>
+ <reference key="source" ref="1021"/>
+ <reference key="destination" ref="976324537"/>
+ </object>
+ <int key="connectionID">495</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performMiniaturize:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1011231497"/>
+ </object>
+ <int key="connectionID">37</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">arrangeInFront:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="625202149"/>
+ </object>
+ <int key="connectionID">39</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">print:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="49223823"/>
+ </object>
+ <int key="connectionID">86</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runPageLayout:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="294629803"/>
+ </object>
+ <int key="connectionID">87</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">clearRecentDocuments:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="759406840"/>
+ </object>
+ <int key="connectionID">127</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performClose:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="776162233"/>
+ </object>
+ <int key="connectionID">193</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleContinuousSpellChecking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="948374510"/>
+ </object>
+ <int key="connectionID">222</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">undo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1058277027"/>
+ </object>
+ <int key="connectionID">223</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copy:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="860595796"/>
+ </object>
+ <int key="connectionID">224</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">checkSpelling:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="96193923"/>
+ </object>
+ <int key="connectionID">225</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">paste:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="29853731"/>
+ </object>
+ <int key="connectionID">226</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">stopSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="680220178"/>
+ </object>
+ <int key="connectionID">227</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">cut:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="296257095"/>
+ </object>
+ <int key="connectionID">228</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showGuessPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="679648819"/>
+ </object>
+ <int key="connectionID">230</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">redo:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="790794224"/>
+ </object>
+ <int key="connectionID">231</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">selectAll:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="583158037"/>
+ </object>
+ <int key="connectionID">232</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">startSpeaking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="731782645"/>
+ </object>
+ <int key="connectionID">233</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">delete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="437104165"/>
+ </object>
+ <int key="connectionID">235</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performZoom:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="575023229"/>
+ </object>
+ <int key="connectionID">240</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="447796847"/>
+ </object>
+ <int key="connectionID">241</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">centerSelectionInVisibleArea:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="88285865"/>
+ </object>
+ <int key="connectionID">245</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleGrammarChecking:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="967646866"/>
+ </object>
+ <int key="connectionID">347</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleSmartInsertDelete:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="605118523"/>
+ </object>
+ <int key="connectionID">355</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticQuoteSubstitution:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="197661976"/>
+ </object>
+ <int key="connectionID">356</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticLinkDetection:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="708854459"/>
+ </object>
+ <int key="connectionID">357</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">saveDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1023925487"/>
+ </object>
+ <int key="connectionID">362</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">revertDocumentToSaved:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="579971712"/>
+ </object>
+ <int key="connectionID">364</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">runToolbarCustomizationPalette:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="237841660"/>
+ </object>
+ <int key="connectionID">365</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleToolbarShown:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="102151532"/>
+ </object>
+ <int key="connectionID">366</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hide:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="755159360"/>
+ </object>
+ <int key="connectionID">367</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">hideOtherApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="342932134"/>
+ </object>
+ <int key="connectionID">368</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">unhideAllApplications:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="908899353"/>
+ </object>
+ <int key="connectionID">370</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">newDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="705341025"/>
+ </object>
+ <int key="connectionID">373</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">openDocument:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="722745758"/>
+ </object>
+ <int key="connectionID">374</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">raiseBaseline:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="941806246"/>
+ </object>
+ <int key="connectionID">426</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">lowerBaseline:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1045724900"/>
+ </object>
+ <int key="connectionID">427</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copyFont:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="596732606"/>
+ </object>
+ <int key="connectionID">428</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">subscript:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1037576581"/>
+ </object>
+ <int key="connectionID">429</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">superscript:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="644725453"/>
+ </object>
+ <int key="connectionID">430</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">tightenKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="677519740"/>
+ </object>
+ <int key="connectionID">431</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">underline:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="330926929"/>
+ </object>
+ <int key="connectionID">432</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontColorPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1012600125"/>
+ </object>
+ <int key="connectionID">433</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">useAllLigatures:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="663508465"/>
+ </object>
+ <int key="connectionID">434</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">loosenKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="238351151"/>
+ </object>
+ <int key="connectionID">435</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">pasteFont:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="393423671"/>
+ </object>
+ <int key="connectionID">436</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">unscript:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="257962622"/>
+ </object>
+ <int key="connectionID">437</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">useStandardKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="252969304"/>
+ </object>
+ <int key="connectionID">438</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">useStandardLigatures:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="706297211"/>
+ </object>
+ <int key="connectionID">439</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">turnOffLigatures:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="568384683"/>
+ </object>
+ <int key="connectionID">440</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">turnOffKerning:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="766922938"/>
+ </object>
+ <int key="connectionID">441</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticSpellingCorrection:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="795346622"/>
+ </object>
+ <int key="connectionID">456</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontSubstitutionsPanel:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="65139061"/>
+ </object>
+ <int key="connectionID">458</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticDashSubstitution:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="672708820"/>
+ </object>
+ <int key="connectionID">461</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleAutomaticTextReplacement:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="537092702"/>
+ </object>
+ <int key="connectionID">463</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">uppercaseWord:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="1060694897"/>
+ </object>
+ <int key="connectionID">464</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">capitalizeWord:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="56570060"/>
+ </object>
+ <int key="connectionID">467</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">lowercaseWord:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="879586729"/>
+ </object>
+ <int key="connectionID">468</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">pasteAsPlainText:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="82994268"/>
+ </object>
+ <int key="connectionID">486</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="326711663"/>
+ </object>
+ <int key="connectionID">487</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="270902937"/>
+ </object>
+ <int key="connectionID">488</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="159080638"/>
+ </object>
+ <int key="connectionID">489</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">showHelp:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="105068016"/>
+ </object>
+ <int key="connectionID">493</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignCenter:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="630155264"/>
+ </object>
+ <int key="connectionID">518</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">pasteRuler:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="883618387"/>
+ </object>
+ <int key="connectionID">519</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">toggleRuler:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="644046920"/>
+ </object>
+ <int key="connectionID">520</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignRight:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="512868991"/>
+ </object>
+ <int key="connectionID">521</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">copyRuler:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="231811626"/>
+ </object>
+ <int key="connectionID">522</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignJustified:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="945678886"/>
+ </object>
+ <int key="connectionID">523</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">alignLeft:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="875092757"/>
+ </object>
+ <int key="connectionID">524</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeBaseWritingDirectionNatural:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="551969625"/>
+ </object>
+ <int key="connectionID">525</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeBaseWritingDirectionLeftToRight:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="249532473"/>
+ </object>
+ <int key="connectionID">526</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeBaseWritingDirectionRightToLeft:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="607364498"/>
+ </object>
+ <int key="connectionID">527</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeTextWritingDirectionNatural:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="380031999"/>
+ </object>
+ <int key="connectionID">528</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeTextWritingDirectionLeftToRight:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="825984362"/>
+ </object>
+ <int key="connectionID">529</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">makeTextWritingDirectionRightToLeft:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="560145579"/>
+ </object>
+ <int key="connectionID">530</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">performFindPanelAction:</string>
+ <reference key="source" ref="1014"/>
+ <reference key="destination" ref="738670835"/>
+ </object>
+ <int key="connectionID">535</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">addFontTrait:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="305399458"/>
+ </object>
+ <int key="connectionID">421</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">addFontTrait:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="814362025"/>
+ </object>
+ <int key="connectionID">422</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">modifyFont:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="885547335"/>
+ </object>
+ <int key="connectionID">423</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">orderFrontFontPanel:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="159677712"/>
+ </object>
+ <int key="connectionID">424</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBActionConnection" key="connection">
+ <string key="label">modifyFont:</string>
+ <reference key="source" ref="755631768"/>
+ <reference key="destination" ref="158063935"/>
+ </object>
+ <int key="connectionID">425</int>
+ </object>
+ <object class="IBConnectionRecord">
+ <object class="IBOutletConnection" key="connection">
+ <string key="label">window</string>
+ <reference key="source" ref="976324537"/>
+ <reference key="destination" ref="972006081"/>
+ </object>
+ <int key="connectionID">532</int>
+ </object>
+ </array>
+ <object class="IBMutableOrderedSet" key="objectRecords">
+ <array key="orderedObjects">
+ <object class="IBObjectRecord">
+ <int key="objectID">0</int>
+ <array key="object" id="0"/>
+ <reference key="children" ref="1048"/>
+ <nil key="parent"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-2</int>
+ <reference key="object" ref="1021"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">File's Owner</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-1</int>
+ <reference key="object" ref="1014"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">First Responder</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">-3</int>
+ <reference key="object" ref="1050"/>
+ <reference key="parent" ref="0"/>
+ <string key="objectName">Application</string>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">29</int>
+ <reference key="object" ref="649796088"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="713487014"/>
+ <reference ref="694149608"/>
+ <reference ref="952259628"/>
+ <reference ref="379814623"/>
+ <reference ref="586577488"/>
+ <reference ref="302598603"/>
+ <reference ref="448692316"/>
+ </array>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">19</int>
+ <reference key="object" ref="713487014"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="835318025"/>
+ </array>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">56</int>
+ <reference key="object" ref="694149608"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="110575045"/>
+ </array>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">217</int>
+ <reference key="object" ref="952259628"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="789758025"/>
+ </array>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">83</int>
+ <reference key="object" ref="379814623"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="720053764"/>
+ </array>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">81</int>
+ <reference key="object" ref="720053764"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="1023925487"/>
+ <reference ref="49223823"/>
+ <reference ref="722745758"/>
+ <reference ref="705341025"/>
+ <reference ref="1025936716"/>
+ <reference ref="294629803"/>
+ <reference ref="776162233"/>
+ <reference ref="425164168"/>
+ <reference ref="579971712"/>
+ <reference ref="1010469920"/>
+ </array>
+ <reference key="parent" ref="379814623"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">75</int>
+ <reference key="object" ref="1023925487"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">78</int>
+ <reference key="object" ref="49223823"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">72</int>
+ <reference key="object" ref="722745758"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">82</int>
+ <reference key="object" ref="705341025"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">124</int>
+ <reference key="object" ref="1025936716"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="1065607017"/>
+ </array>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">77</int>
+ <reference key="object" ref="294629803"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">73</int>
+ <reference key="object" ref="776162233"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">79</int>
+ <reference key="object" ref="425164168"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">112</int>
+ <reference key="object" ref="579971712"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">74</int>
+ <reference key="object" ref="1010469920"/>
+ <reference key="parent" ref="720053764"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">125</int>
+ <reference key="object" ref="1065607017"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="759406840"/>
+ </array>
+ <reference key="parent" ref="1025936716"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">126</int>
+ <reference key="object" ref="759406840"/>
+ <reference key="parent" ref="1065607017"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">205</int>
+ <reference key="object" ref="789758025"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="437104165"/>
+ <reference ref="583158037"/>
+ <reference ref="1058277027"/>
+ <reference ref="212016141"/>
+ <reference ref="296257095"/>
+ <reference ref="29853731"/>
+ <reference ref="860595796"/>
+ <reference ref="1040322652"/>
+ <reference ref="790794224"/>
+ <reference ref="892235320"/>
+ <reference ref="972420730"/>
+ <reference ref="676164635"/>
+ <reference ref="507821607"/>
+ <reference ref="288088188"/>
+ <reference ref="82994268"/>
+ </array>
+ <reference key="parent" ref="952259628"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">202</int>
+ <reference key="object" ref="437104165"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">198</int>
+ <reference key="object" ref="583158037"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">207</int>
+ <reference key="object" ref="1058277027"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">214</int>
+ <reference key="object" ref="212016141"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">199</int>
+ <reference key="object" ref="296257095"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">203</int>
+ <reference key="object" ref="29853731"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">197</int>
+ <reference key="object" ref="860595796"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">206</int>
+ <reference key="object" ref="1040322652"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">215</int>
+ <reference key="object" ref="790794224"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">218</int>
+ <reference key="object" ref="892235320"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="963351320"/>
+ </array>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">216</int>
+ <reference key="object" ref="972420730"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="769623530"/>
+ </array>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">200</int>
+ <reference key="object" ref="769623530"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="948374510"/>
+ <reference ref="96193923"/>
+ <reference ref="679648819"/>
+ <reference ref="967646866"/>
+ <reference ref="859480356"/>
+ <reference ref="795346622"/>
+ </array>
+ <reference key="parent" ref="972420730"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">219</int>
+ <reference key="object" ref="948374510"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">201</int>
+ <reference key="object" ref="96193923"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">204</int>
+ <reference key="object" ref="679648819"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">220</int>
+ <reference key="object" ref="963351320"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="270902937"/>
+ <reference ref="88285865"/>
+ <reference ref="159080638"/>
+ <reference ref="326711663"/>
+ <reference ref="447796847"/>
+ <reference ref="738670835"/>
+ </array>
+ <reference key="parent" ref="892235320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">213</int>
+ <reference key="object" ref="270902937"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">210</int>
+ <reference key="object" ref="88285865"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">221</int>
+ <reference key="object" ref="159080638"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">208</int>
+ <reference key="object" ref="326711663"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">209</int>
+ <reference key="object" ref="447796847"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">57</int>
+ <reference key="object" ref="110575045"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="238522557"/>
+ <reference ref="755159360"/>
+ <reference ref="908899353"/>
+ <reference ref="632727374"/>
+ <reference ref="646227648"/>
+ <reference ref="609285721"/>
+ <reference ref="481834944"/>
+ <reference ref="304266470"/>
+ <reference ref="1046388886"/>
+ <reference ref="1056857174"/>
+ <reference ref="342932134"/>
+ </array>
+ <reference key="parent" ref="694149608"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">58</int>
+ <reference key="object" ref="238522557"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">134</int>
+ <reference key="object" ref="755159360"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">150</int>
+ <reference key="object" ref="908899353"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">136</int>
+ <reference key="object" ref="632727374"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">144</int>
+ <reference key="object" ref="646227648"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">129</int>
+ <reference key="object" ref="609285721"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">143</int>
+ <reference key="object" ref="481834944"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">236</int>
+ <reference key="object" ref="304266470"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">131</int>
+ <reference key="object" ref="1046388886"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="752062318"/>
+ </array>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">149</int>
+ <reference key="object" ref="1056857174"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">145</int>
+ <reference key="object" ref="342932134"/>
+ <reference key="parent" ref="110575045"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">130</int>
+ <reference key="object" ref="752062318"/>
+ <reference key="parent" ref="1046388886"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">24</int>
+ <reference key="object" ref="835318025"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="299356726"/>
+ <reference ref="625202149"/>
+ <reference ref="575023229"/>
+ <reference ref="1011231497"/>
+ </array>
+ <reference key="parent" ref="713487014"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">92</int>
+ <reference key="object" ref="299356726"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">5</int>
+ <reference key="object" ref="625202149"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">239</int>
+ <reference key="object" ref="575023229"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">23</int>
+ <reference key="object" ref="1011231497"/>
+ <reference key="parent" ref="835318025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">295</int>
+ <reference key="object" ref="586577488"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="466310130"/>
+ </array>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">296</int>
+ <reference key="object" ref="466310130"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="102151532"/>
+ <reference ref="237841660"/>
+ </array>
+ <reference key="parent" ref="586577488"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">297</int>
+ <reference key="object" ref="102151532"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">298</int>
+ <reference key="object" ref="237841660"/>
+ <reference key="parent" ref="466310130"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">211</int>
+ <reference key="object" ref="676164635"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="785027613"/>
+ </array>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">212</int>
+ <reference key="object" ref="785027613"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="680220178"/>
+ <reference ref="731782645"/>
+ </array>
+ <reference key="parent" ref="676164635"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">195</int>
+ <reference key="object" ref="680220178"/>
+ <reference key="parent" ref="785027613"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">196</int>
+ <reference key="object" ref="731782645"/>
+ <reference key="parent" ref="785027613"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">346</int>
+ <reference key="object" ref="967646866"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">348</int>
+ <reference key="object" ref="507821607"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="698887838"/>
+ </array>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">349</int>
+ <reference key="object" ref="698887838"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="605118523"/>
+ <reference ref="197661976"/>
+ <reference ref="708854459"/>
+ <reference ref="65139061"/>
+ <reference ref="19036812"/>
+ <reference ref="672708820"/>
+ <reference ref="537092702"/>
+ </array>
+ <reference key="parent" ref="507821607"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">350</int>
+ <reference key="object" ref="605118523"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">351</int>
+ <reference key="object" ref="197661976"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">354</int>
+ <reference key="object" ref="708854459"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">371</int>
+ <reference key="object" ref="972006081"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="439893737"/>
+ </array>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">372</int>
+ <reference key="object" ref="439893737"/>
+ <reference key="parent" ref="972006081"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">375</int>
+ <reference key="object" ref="302598603"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="941447902"/>
+ </array>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">376</int>
+ <reference key="object" ref="941447902"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="792887677"/>
+ <reference ref="215659978"/>
+ </array>
+ <reference key="parent" ref="302598603"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">377</int>
+ <reference key="object" ref="792887677"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="786677654"/>
+ </array>
+ <reference key="parent" ref="941447902"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">388</int>
+ <reference key="object" ref="786677654"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="159677712"/>
+ <reference ref="305399458"/>
+ <reference ref="814362025"/>
+ <reference ref="330926929"/>
+ <reference ref="533507878"/>
+ <reference ref="158063935"/>
+ <reference ref="885547335"/>
+ <reference ref="901062459"/>
+ <reference ref="767671776"/>
+ <reference ref="691570813"/>
+ <reference ref="769124883"/>
+ <reference ref="739652853"/>
+ <reference ref="1012600125"/>
+ <reference ref="214559597"/>
+ <reference ref="596732606"/>
+ <reference ref="393423671"/>
+ </array>
+ <reference key="parent" ref="792887677"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">389</int>
+ <reference key="object" ref="159677712"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">390</int>
+ <reference key="object" ref="305399458"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">391</int>
+ <reference key="object" ref="814362025"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">392</int>
+ <reference key="object" ref="330926929"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">393</int>
+ <reference key="object" ref="533507878"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">394</int>
+ <reference key="object" ref="158063935"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">395</int>
+ <reference key="object" ref="885547335"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">396</int>
+ <reference key="object" ref="901062459"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">397</int>
+ <reference key="object" ref="767671776"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="175441468"/>
+ </array>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">398</int>
+ <reference key="object" ref="691570813"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="1058217995"/>
+ </array>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">399</int>
+ <reference key="object" ref="769124883"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="18263474"/>
+ </array>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">400</int>
+ <reference key="object" ref="739652853"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">401</int>
+ <reference key="object" ref="1012600125"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">402</int>
+ <reference key="object" ref="214559597"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">403</int>
+ <reference key="object" ref="596732606"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">404</int>
+ <reference key="object" ref="393423671"/>
+ <reference key="parent" ref="786677654"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">405</int>
+ <reference key="object" ref="18263474"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="257962622"/>
+ <reference ref="644725453"/>
+ <reference ref="1037576581"/>
+ <reference ref="941806246"/>
+ <reference ref="1045724900"/>
+ </array>
+ <reference key="parent" ref="769124883"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">406</int>
+ <reference key="object" ref="257962622"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">407</int>
+ <reference key="object" ref="644725453"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">408</int>
+ <reference key="object" ref="1037576581"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">409</int>
+ <reference key="object" ref="941806246"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">410</int>
+ <reference key="object" ref="1045724900"/>
+ <reference key="parent" ref="18263474"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">411</int>
+ <reference key="object" ref="1058217995"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="706297211"/>
+ <reference ref="568384683"/>
+ <reference ref="663508465"/>
+ </array>
+ <reference key="parent" ref="691570813"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">412</int>
+ <reference key="object" ref="706297211"/>
+ <reference key="parent" ref="1058217995"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">413</int>
+ <reference key="object" ref="568384683"/>
+ <reference key="parent" ref="1058217995"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">414</int>
+ <reference key="object" ref="663508465"/>
+ <reference key="parent" ref="1058217995"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">415</int>
+ <reference key="object" ref="175441468"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="252969304"/>
+ <reference ref="766922938"/>
+ <reference ref="677519740"/>
+ <reference ref="238351151"/>
+ </array>
+ <reference key="parent" ref="767671776"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">416</int>
+ <reference key="object" ref="252969304"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">417</int>
+ <reference key="object" ref="766922938"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">418</int>
+ <reference key="object" ref="677519740"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">419</int>
+ <reference key="object" ref="238351151"/>
+ <reference key="parent" ref="175441468"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">420</int>
+ <reference key="object" ref="755631768"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">450</int>
+ <reference key="object" ref="288088188"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="579392910"/>
+ </array>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">451</int>
+ <reference key="object" ref="579392910"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="1060694897"/>
+ <reference ref="879586729"/>
+ <reference ref="56570060"/>
+ </array>
+ <reference key="parent" ref="288088188"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">452</int>
+ <reference key="object" ref="1060694897"/>
+ <reference key="parent" ref="579392910"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">453</int>
+ <reference key="object" ref="859480356"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">454</int>
+ <reference key="object" ref="795346622"/>
+ <reference key="parent" ref="769623530"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">457</int>
+ <reference key="object" ref="65139061"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">459</int>
+ <reference key="object" ref="19036812"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">460</int>
+ <reference key="object" ref="672708820"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">462</int>
+ <reference key="object" ref="537092702"/>
+ <reference key="parent" ref="698887838"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">465</int>
+ <reference key="object" ref="879586729"/>
+ <reference key="parent" ref="579392910"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">466</int>
+ <reference key="object" ref="56570060"/>
+ <reference key="parent" ref="579392910"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">485</int>
+ <reference key="object" ref="82994268"/>
+ <reference key="parent" ref="789758025"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">490</int>
+ <reference key="object" ref="448692316"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="992780483"/>
+ </array>
+ <reference key="parent" ref="649796088"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">491</int>
+ <reference key="object" ref="992780483"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="105068016"/>
+ </array>
+ <reference key="parent" ref="448692316"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">492</int>
+ <reference key="object" ref="105068016"/>
+ <reference key="parent" ref="992780483"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">494</int>
+ <reference key="object" ref="976324537"/>
+ <reference key="parent" ref="0"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">496</int>
+ <reference key="object" ref="215659978"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="446991534"/>
+ </array>
+ <reference key="parent" ref="941447902"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">497</int>
+ <reference key="object" ref="446991534"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="875092757"/>
+ <reference ref="630155264"/>
+ <reference ref="945678886"/>
+ <reference ref="512868991"/>
+ <reference ref="163117631"/>
+ <reference ref="31516759"/>
+ <reference ref="908105787"/>
+ <reference ref="644046920"/>
+ <reference ref="231811626"/>
+ <reference ref="883618387"/>
+ </array>
+ <reference key="parent" ref="215659978"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">498</int>
+ <reference key="object" ref="875092757"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">499</int>
+ <reference key="object" ref="630155264"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">500</int>
+ <reference key="object" ref="945678886"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">501</int>
+ <reference key="object" ref="512868991"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">502</int>
+ <reference key="object" ref="163117631"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">503</int>
+ <reference key="object" ref="31516759"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="956096989"/>
+ </array>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">504</int>
+ <reference key="object" ref="908105787"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">505</int>
+ <reference key="object" ref="644046920"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">506</int>
+ <reference key="object" ref="231811626"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">507</int>
+ <reference key="object" ref="883618387"/>
+ <reference key="parent" ref="446991534"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">508</int>
+ <reference key="object" ref="956096989"/>
+ <array class="NSMutableArray" key="children">
+ <reference ref="257099033"/>
+ <reference ref="551969625"/>
+ <reference ref="249532473"/>
+ <reference ref="607364498"/>
+ <reference ref="508151438"/>
+ <reference ref="981751889"/>
+ <reference ref="380031999"/>
+ <reference ref="825984362"/>
+ <reference ref="560145579"/>
+ </array>
+ <reference key="parent" ref="31516759"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">509</int>
+ <reference key="object" ref="257099033"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">510</int>
+ <reference key="object" ref="551969625"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">511</int>
+ <reference key="object" ref="249532473"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">512</int>
+ <reference key="object" ref="607364498"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">513</int>
+ <reference key="object" ref="508151438"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">514</int>
+ <reference key="object" ref="981751889"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">515</int>
+ <reference key="object" ref="380031999"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">516</int>
+ <reference key="object" ref="825984362"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">517</int>
+ <reference key="object" ref="560145579"/>
+ <reference key="parent" ref="956096989"/>
+ </object>
+ <object class="IBObjectRecord">
+ <int key="objectID">534</int>
+ <reference key="object" ref="738670835"/>
+ <reference key="parent" ref="963351320"/>
+ </object>
+ </array>
+ </object>
+ <dictionary class="NSMutableDictionary" key="flattenedProperties">
+ <string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="-3.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="112.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="124.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="125.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="126.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="129.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="130.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="131.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="134.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="136.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="143.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="144.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="145.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="149.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="150.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="19.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="195.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="196.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="197.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="198.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="199.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="200.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="201.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="202.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="203.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="204.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="205.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="206.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="207.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="208.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="209.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="210.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="211.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="212.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="213.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="214.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="215.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="216.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="217.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="218.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="219.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="220.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="221.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="23.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="236.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="239.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="24.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="29.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="295.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="296.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="297.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="298.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="346.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="348.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="349.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="350.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="351.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="354.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="371.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="371.IBWindowTemplateEditedContentRect">{{380, 496}, {480, 360}}</string>
+ <integer value="1" key="371.NSWindowTemplate.visibleAtLaunch"/>
+ <string key="372.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="375.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="376.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="377.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="388.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="389.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="390.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="391.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="392.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="393.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="394.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="395.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="396.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="397.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="398.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="399.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="400.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="401.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="402.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="403.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="404.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="405.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="406.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="407.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="408.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="409.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="410.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="411.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="412.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="413.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="414.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="415.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="416.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="417.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="418.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="419.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="420.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="450.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="451.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="452.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="453.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="454.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="457.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="459.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="460.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="462.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="465.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="466.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="485.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="490.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="491.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="492.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="494.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="496.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="497.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="498.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="499.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="5.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="500.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="501.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="502.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="503.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="504.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="505.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="506.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="507.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="508.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="509.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="510.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="511.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="512.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="513.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="514.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="515.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="516.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="517.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="534.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="56.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="57.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="58.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="72.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="73.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="74.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="75.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="77.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="78.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="79.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="81.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="82.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="83.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ <string key="92.IBPluginDependency">com.apple.InterfaceBuilder.CocoaPlugin</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
+ <nil key="activeLocalization"/>
+ <dictionary class="NSMutableDictionary" key="localizations"/>
+ <nil key="sourceID"/>
+ <int key="maxID">535</int>
+ </object>
+ <object class="IBClassDescriber" key="IBDocument.Classes">
+ <array class="NSMutableArray" key="referencedPartialClassDescriptions">
+ <object class="IBPartialClassDescription">
+ <string key="className">ABCardController</string>
+ <string key="superclassName">NSObject</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="addCardViewField:">id</string>
+ <string key="copy:">id</string>
+ <string key="cut:">id</string>
+ <string key="doDelete:">id</string>
+ <string key="find:">id</string>
+ <string key="paste:">id</string>
+ <string key="saveChanges:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="addCardViewField:">
+ <string key="name">addCardViewField:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="copy:">
+ <string key="name">copy:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="cut:">
+ <string key="name">cut:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="doDelete:">
+ <string key="name">doDelete:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="find:">
+ <string key="name">find:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="paste:">
+ <string key="name">paste:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="saveChanges:">
+ <string key="name">saveChanges:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="mCardView">ABCardView</string>
+ <string key="mEditButton">NSButton</string>
+ <string key="mManagedObjectContext">NSManagedObjectContext</string>
+ <string key="mSearchField">NSSearchField</string>
+ <string key="mStatusTextField">NSTextField</string>
+ <string key="mWindow">NSWindow</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="mCardView">
+ <string key="name">mCardView</string>
+ <string key="candidateClassName">ABCardView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mEditButton">
+ <string key="name">mEditButton</string>
+ <string key="candidateClassName">NSButton</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mManagedObjectContext">
+ <string key="name">mManagedObjectContext</string>
+ <string key="candidateClassName">NSManagedObjectContext</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mSearchField">
+ <string key="name">mSearchField</string>
+ <string key="candidateClassName">NSSearchField</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mStatusTextField">
+ <string key="name">mStatusTextField</string>
+ <string key="candidateClassName">NSTextField</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mWindow">
+ <string key="name">mWindow</string>
+ <string key="candidateClassName">NSWindow</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/ABCardController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">ABCardView</string>
+ <string key="superclassName">NSView</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="commitAndSave:">id</string>
+ <string key="statusImageClicked:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="commitAndSave:">
+ <string key="name">commitAndSave:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="statusImageClicked:">
+ <string key="name">statusImageClicked:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="mBindingsController">NSObjectController</string>
+ <string key="mBuddyStatusImage">NSImageView</string>
+ <string key="mHeaderView">NSView</string>
+ <string key="mNameView">ABNameFrameView</string>
+ <string key="mNextKeyView">NSView</string>
+ <string key="mUserImage">NSImage</string>
+ <string key="mUserImageView">ABImageView</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="mBindingsController">
+ <string key="name">mBindingsController</string>
+ <string key="candidateClassName">NSObjectController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mBuddyStatusImage">
+ <string key="name">mBuddyStatusImage</string>
+ <string key="candidateClassName">NSImageView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mHeaderView">
+ <string key="name">mHeaderView</string>
+ <string key="candidateClassName">NSView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mNameView">
+ <string key="name">mNameView</string>
+ <string key="candidateClassName">ABNameFrameView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mNextKeyView">
+ <string key="name">mNextKeyView</string>
+ <string key="candidateClassName">NSView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mUserImage">
+ <string key="name">mUserImage</string>
+ <string key="candidateClassName">NSImage</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="mUserImageView">
+ <string key="name">mUserImageView</string>
+ <string key="candidateClassName">ABImageView</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/ABCardView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">ABImageView</string>
+ <string key="superclassName">NSImageView</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="copy:">id</string>
+ <string key="cut:">id</string>
+ <string key="delete:">id</string>
+ <string key="paste:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="copy:">
+ <string key="name">copy:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="cut:">
+ <string key="name">cut:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="delete:">
+ <string key="name">delete:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="paste:">
+ <string key="name">paste:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/ABImageView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTBorderedView</string>
+ <string key="superclassName">DVTLayoutView_ML</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">contentView</string>
+ <string key="NS.object.0">NSView</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">contentView</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">contentView</string>
+ <string key="candidateClassName">NSView</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTBorderedView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTDelayedMenuButton</string>
+ <string key="superclassName">NSButton</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTDelayedMenuButton.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTGradientImageButton</string>
+ <string key="superclassName">NSButton</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTGradientImageButton.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTImageAndTextCell</string>
+ <string key="superclassName">NSTextFieldCell</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTImageAndTextCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTImageAndTextColumn</string>
+ <string key="superclassName">NSTableColumn</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTImageAndTextColumn.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTLayoutView_ML</string>
+ <string key="superclassName">NSView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTLayoutView_ML.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTOutlineView</string>
+ <string key="superclassName">NSOutlineView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTOutlineView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTSplitView</string>
+ <string key="superclassName">NSSplitView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTSplitView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTStackView_ML</string>
+ <string key="superclassName">DVTLayoutView_ML</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTStackView_ML.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTTableView</string>
+ <string key="superclassName">NSTableView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTTableView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">DVTViewController</string>
+ <string key="superclassName">NSViewController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/DVTViewController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">HFController</string>
+ <string key="superclassName">NSObject</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">selectAll:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <string key="NS.key.0">selectAll:</string>
+ <object class="IBActionInfo" key="NS.object.0">
+ <string key="name">selectAll:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/HFController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">HFRepresenterTextView</string>
+ <string key="superclassName">NSView</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">selectAll:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <string key="NS.key.0">selectAll:</string>
+ <object class="IBActionInfo" key="NS.object.0">
+ <string key="name">selectAll:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/HFRepresenterTextView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IBEditor</string>
+ <string key="superclassName">NSObject</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="changeFont:">id</string>
+ <string key="performCopy:">id</string>
+ <string key="performCut:">id</string>
+ <string key="selectAll:">id</string>
+ <string key="sizeSelectionToFit:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="changeFont:">
+ <string key="name">changeFont:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="performCopy:">
+ <string key="name">performCopy:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="performCut:">
+ <string key="name">performCut:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="selectAll:">
+ <string key="name">selectAll:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="sizeSelectionToFit:">
+ <string key="name">sizeSelectionToFit:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IBEditor.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDECapsuleListView</string>
+ <string key="superclassName">DVTStackView_ML</string>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">dataSource</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">dataSource</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">dataSource</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDECapsuleListView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDMArrayController</string>
+ <string key="superclassName">NSArrayController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDMArrayController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDMEditor</string>
+ <string key="superclassName">IDEEditor</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="bottomToolbarBorderView">DVTBorderedView</string>
+ <string key="sourceListSplitViewPane">NSView</string>
+ <string key="sourceListViewController">IDEDMEditorSourceListController</string>
+ <string key="splitView">DVTSplitView</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="bottomToolbarBorderView">
+ <string key="name">bottomToolbarBorderView</string>
+ <string key="candidateClassName">DVTBorderedView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="sourceListSplitViewPane">
+ <string key="name">sourceListSplitViewPane</string>
+ <string key="candidateClassName">NSView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="sourceListViewController">
+ <string key="name">sourceListViewController</string>
+ <string key="candidateClassName">IDEDMEditorSourceListController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="splitView">
+ <string key="name">splitView</string>
+ <string key="candidateClassName">DVTSplitView</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDMEditor.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDMEditorController</string>
+ <string key="superclassName">IDEViewController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDMEditorController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDMEditorSourceListController</string>
+ <string key="superclassName">IDEDMEditorController</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="borderedView">DVTBorderedView</string>
+ <string key="parentEditor">IDEDMEditor</string>
+ <string key="primaryColumn">DVTImageAndTextColumn</string>
+ <string key="sourceListOutlineView">DVTOutlineView</string>
+ <string key="sourceListTreeController">NSTreeController</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="borderedView">
+ <string key="name">borderedView</string>
+ <string key="candidateClassName">DVTBorderedView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="parentEditor">
+ <string key="name">parentEditor</string>
+ <string key="candidateClassName">IDEDMEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="primaryColumn">
+ <string key="name">primaryColumn</string>
+ <string key="candidateClassName">DVTImageAndTextColumn</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="sourceListOutlineView">
+ <string key="name">sourceListOutlineView</string>
+ <string key="candidateClassName">DVTOutlineView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="sourceListTreeController">
+ <string key="name">sourceListTreeController</string>
+ <string key="candidateClassName">NSTreeController</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDMEditorSourceListController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDMHighlightImageAndTextCell</string>
+ <string key="superclassName">DVTImageAndTextCell</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDMHighlightImageAndTextCell.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDataModelBrowserEditor</string>
+ <string key="superclassName">IDEDMEditorController</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="attributesTableViewController">IDEDataModelPropertiesTableController</string>
+ <string key="capsuleView">IDECapsuleListView</string>
+ <string key="entityArrayController">NSArrayController</string>
+ <string key="fetchedPropertiesTableViewController">IDEDataModelPropertiesTableController</string>
+ <string key="parentEditor">IDEDataModelEntityContentsEditor</string>
+ <string key="relationshipsTableViewController">IDEDataModelPropertiesTableController</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="attributesTableViewController">
+ <string key="name">attributesTableViewController</string>
+ <string key="candidateClassName">IDEDataModelPropertiesTableController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="capsuleView">
+ <string key="name">capsuleView</string>
+ <string key="candidateClassName">IDECapsuleListView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="entityArrayController">
+ <string key="name">entityArrayController</string>
+ <string key="candidateClassName">NSArrayController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="fetchedPropertiesTableViewController">
+ <string key="name">fetchedPropertiesTableViewController</string>
+ <string key="candidateClassName">IDEDataModelPropertiesTableController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="parentEditor">
+ <string key="name">parentEditor</string>
+ <string key="candidateClassName">IDEDataModelEntityContentsEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="relationshipsTableViewController">
+ <string key="name">relationshipsTableViewController</string>
+ <string key="candidateClassName">IDEDataModelPropertiesTableController</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDataModelBrowserEditor.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDataModelConfigurationEditor</string>
+ <string key="superclassName">IDEDMEditorController</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="capsuleListView">IDECapsuleListView</string>
+ <string key="parentEditor">IDEDataModelEditor</string>
+ <string key="tableController">IDEDataModelConfigurationTableController</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="capsuleListView">
+ <string key="name">capsuleListView</string>
+ <string key="candidateClassName">IDECapsuleListView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="parentEditor">
+ <string key="name">parentEditor</string>
+ <string key="candidateClassName">IDEDataModelEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="tableController">
+ <string key="name">tableController</string>
+ <string key="candidateClassName">IDEDataModelConfigurationTableController</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDataModelConfigurationEditor.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDataModelConfigurationTableController</string>
+ <string key="superclassName">IDEDMEditorController</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="configurationsArrayController">NSArrayController</string>
+ <string key="entitiesArrayController">NSArrayController</string>
+ <string key="parentEditor">IDEDataModelConfigurationEditor</string>
+ <string key="tableView">XDTableView</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="configurationsArrayController">
+ <string key="name">configurationsArrayController</string>
+ <string key="candidateClassName">NSArrayController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="entitiesArrayController">
+ <string key="name">entitiesArrayController</string>
+ <string key="candidateClassName">NSArrayController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="parentEditor">
+ <string key="name">parentEditor</string>
+ <string key="candidateClassName">IDEDataModelConfigurationEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="tableView">
+ <string key="name">tableView</string>
+ <string key="candidateClassName">XDTableView</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDataModelConfigurationTableController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDataModelDiagramEditor</string>
+ <string key="superclassName">IDEDMEditorController</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="diagramView">XDDiagramView</string>
+ <string key="parentEditor">IDEDataModelEntityContentsEditor</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="diagramView">
+ <string key="name">diagramView</string>
+ <string key="candidateClassName">XDDiagramView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="parentEditor">
+ <string key="name">parentEditor</string>
+ <string key="candidateClassName">IDEDataModelEntityContentsEditor</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDataModelDiagramEditor.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDataModelEditor</string>
+ <string key="superclassName">IDEDMEditor</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="addEntityButton">DVTDelayedMenuButton</string>
+ <string key="addPropertyButton">DVTDelayedMenuButton</string>
+ <string key="browserDiagramSegmentControl">NSSegmentedControl</string>
+ <string key="configurationViewController">IDEDataModelConfigurationEditor</string>
+ <string key="entityContentsViewController">IDEDataModelEntityContentsEditor</string>
+ <string key="fetchRequestViewController">IDEDataModelFetchRequestEditor</string>
+ <string key="hierarchySegmentControl">NSSegmentedControl</string>
+ <string key="tabView">NSTabView</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="addEntityButton">
+ <string key="name">addEntityButton</string>
+ <string key="candidateClassName">DVTDelayedMenuButton</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="addPropertyButton">
+ <string key="name">addPropertyButton</string>
+ <string key="candidateClassName">DVTDelayedMenuButton</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="browserDiagramSegmentControl">
+ <string key="name">browserDiagramSegmentControl</string>
+ <string key="candidateClassName">NSSegmentedControl</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="configurationViewController">
+ <string key="name">configurationViewController</string>
+ <string key="candidateClassName">IDEDataModelConfigurationEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="entityContentsViewController">
+ <string key="name">entityContentsViewController</string>
+ <string key="candidateClassName">IDEDataModelEntityContentsEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="fetchRequestViewController">
+ <string key="name">fetchRequestViewController</string>
+ <string key="candidateClassName">IDEDataModelFetchRequestEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="hierarchySegmentControl">
+ <string key="name">hierarchySegmentControl</string>
+ <string key="candidateClassName">NSSegmentedControl</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="tabView">
+ <string key="name">tabView</string>
+ <string key="candidateClassName">NSTabView</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDataModelEditor.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDataModelEntityContentsEditor</string>
+ <string key="superclassName">IDEDMEditorController</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="browserViewController">IDEDataModelBrowserEditor</string>
+ <string key="diagramViewController">IDEDataModelDiagramEditor</string>
+ <string key="parentEditor">IDEDataModelEditor</string>
+ <string key="tabView">NSTabView</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="browserViewController">
+ <string key="name">browserViewController</string>
+ <string key="candidateClassName">IDEDataModelBrowserEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="diagramViewController">
+ <string key="name">diagramViewController</string>
+ <string key="candidateClassName">IDEDataModelDiagramEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="parentEditor">
+ <string key="name">parentEditor</string>
+ <string key="candidateClassName">IDEDataModelEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="tabView">
+ <string key="name">tabView</string>
+ <string key="candidateClassName">NSTabView</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDataModelEntityContentsEditor.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDataModelFetchRequestEditor</string>
+ <string key="superclassName">IDEDMEditorController</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="entityController">NSArrayController</string>
+ <string key="parentEditor">IDEDataModelEditor</string>
+ <string key="tableView">IDECapsuleListView</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="entityController">
+ <string key="name">entityController</string>
+ <string key="candidateClassName">NSArrayController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="parentEditor">
+ <string key="name">parentEditor</string>
+ <string key="candidateClassName">IDEDataModelEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="tableView">
+ <string key="name">tableView</string>
+ <string key="candidateClassName">IDECapsuleListView</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDataModelFetchRequestEditor.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDataModelPropertiesTableController</string>
+ <string key="superclassName">IDEDMEditorController</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="arrayController">IDEDMArrayController</string>
+ <string key="entitiesColumn">NSTableColumn</string>
+ <string key="entityArrayController">NSArrayController</string>
+ <string key="parentEditor">IDEDataModelBrowserEditor</string>
+ <string key="propertyNameAndImageCell">IDEDMHighlightImageAndTextCell</string>
+ <string key="tableView">XDTableView</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="arrayController">
+ <string key="name">arrayController</string>
+ <string key="candidateClassName">IDEDMArrayController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="entitiesColumn">
+ <string key="name">entitiesColumn</string>
+ <string key="candidateClassName">NSTableColumn</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="entityArrayController">
+ <string key="name">entityArrayController</string>
+ <string key="candidateClassName">NSArrayController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="parentEditor">
+ <string key="name">parentEditor</string>
+ <string key="candidateClassName">IDEDataModelBrowserEditor</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="propertyNameAndImageCell">
+ <string key="name">propertyNameAndImageCell</string>
+ <string key="candidateClassName">IDEDMHighlightImageAndTextCell</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="tableView">
+ <string key="name">tableView</string>
+ <string key="candidateClassName">XDTableView</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDataModelPropertiesTableController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDocDownloadsTableViewController</string>
+ <string key="superclassName">NSObject</string>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="_downloadButtonCell">NSButtonCell</string>
+ <string key="_tableView">DVTTableView</string>
+ <string key="prefPaneController">IDEDocViewingPrefPaneController</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="_downloadButtonCell">
+ <string key="name">_downloadButtonCell</string>
+ <string key="candidateClassName">NSButtonCell</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="_tableView">
+ <string key="name">_tableView</string>
+ <string key="candidateClassName">DVTTableView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="prefPaneController">
+ <string key="name">prefPaneController</string>
+ <string key="candidateClassName">IDEDocViewingPrefPaneController</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDocDownloadsTableViewController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDocSetOutlineView</string>
+ <string key="superclassName">NSOutlineView</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDocSetOutlineView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDocSetOutlineViewController</string>
+ <string key="superclassName">NSObject</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="getDocSetAction:">id</string>
+ <string key="showProblemInfoForUpdate:">id</string>
+ <string key="subscribeToPublisherAction:">id</string>
+ <string key="unsubscribeFromPublisher:">id</string>
+ <string key="updateDocSetAction:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="getDocSetAction:">
+ <string key="name">getDocSetAction:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="showProblemInfoForUpdate:">
+ <string key="name">showProblemInfoForUpdate:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="subscribeToPublisherAction:">
+ <string key="name">subscribeToPublisherAction:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="unsubscribeFromPublisher:">
+ <string key="name">unsubscribeFromPublisher:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="updateDocSetAction:">
+ <string key="name">updateDocSetAction:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">docSetOutlineView</string>
+ <string key="NS.object.0">IDEDocSetOutlineView</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">docSetOutlineView</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">docSetOutlineView</string>
+ <string key="candidateClassName">IDEDocSetOutlineView</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDocSetOutlineViewController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEDocViewingPrefPaneController</string>
+ <string key="superclassName">IDEViewController</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="addSubscription:">id</string>
+ <string key="checkForAndInstallUpdatesNow:">id</string>
+ <string key="deleteDocSet:">id</string>
+ <string key="downloadAction:">id</string>
+ <string key="minimumFontSizeComboBoxAction:">id</string>
+ <string key="minimumFontSizeEnabledAction:">id</string>
+ <string key="showHelp:">id</string>
+ <string key="showSubscriptionSheet:">id</string>
+ <string key="subscriptionCancelAction:">id</string>
+ <string key="toggleAutoCheckForAndInstallUpdates:">id</string>
+ <string key="toggleDocSetInfo:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="addSubscription:">
+ <string key="name">addSubscription:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="checkForAndInstallUpdatesNow:">
+ <string key="name">checkForAndInstallUpdatesNow:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="deleteDocSet:">
+ <string key="name">deleteDocSet:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="downloadAction:">
+ <string key="name">downloadAction:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="minimumFontSizeComboBoxAction:">
+ <string key="name">minimumFontSizeComboBoxAction:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="minimumFontSizeEnabledAction:">
+ <string key="name">minimumFontSizeEnabledAction:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="showHelp:">
+ <string key="name">showHelp:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="showSubscriptionSheet:">
+ <string key="name">showSubscriptionSheet:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="subscriptionCancelAction:">
+ <string key="name">subscriptionCancelAction:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="toggleAutoCheckForAndInstallUpdates:">
+ <string key="name">toggleAutoCheckForAndInstallUpdates:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="toggleDocSetInfo:">
+ <string key="name">toggleDocSetInfo:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="outlets">
+ <string key="_addButton">DVTGradientImageButton</string>
+ <string key="_deleteButton">DVTGradientImageButton</string>
+ <string key="_showInfoAreaButton">DVTGradientImageButton</string>
+ <string key="_splitView">NSSplitView</string>
+ <string key="_splitViewDocSetInfoSubview">NSView</string>
+ <string key="_splitViewDocSetsListSubview">NSView</string>
+ <string key="borderedViewAroundSplitView">DVTBorderedView</string>
+ <string key="borderedViewBelowTable">DVTBorderedView</string>
+ <string key="checkAndInstallNowButton">NSButton</string>
+ <string key="docSetInfoTextView">NSTextView</string>
+ <string key="docSetOutlineViewController">IDEDocSetOutlineViewController</string>
+ <string key="downloadsTableViewController">IDEDocDownloadsTableViewController</string>
+ <string key="minimumFontSizeControl">NSComboBox</string>
+ <string key="noUpdatesAvailableMessage">NSTextField</string>
+ <string key="showInfoButton">NSButton</string>
+ <string key="subscriptionTextField">NSTextField</string>
+ <string key="subscriptionWindow">NSWindow</string>
+ <string key="validateAddSubscriptionButton">NSButton</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <object class="IBToOneOutletInfo" key="_addButton">
+ <string key="name">_addButton</string>
+ <string key="candidateClassName">DVTGradientImageButton</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="_deleteButton">
+ <string key="name">_deleteButton</string>
+ <string key="candidateClassName">DVTGradientImageButton</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="_showInfoAreaButton">
+ <string key="name">_showInfoAreaButton</string>
+ <string key="candidateClassName">DVTGradientImageButton</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="_splitView">
+ <string key="name">_splitView</string>
+ <string key="candidateClassName">NSSplitView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="_splitViewDocSetInfoSubview">
+ <string key="name">_splitViewDocSetInfoSubview</string>
+ <string key="candidateClassName">NSView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="_splitViewDocSetsListSubview">
+ <string key="name">_splitViewDocSetsListSubview</string>
+ <string key="candidateClassName">NSView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="borderedViewAroundSplitView">
+ <string key="name">borderedViewAroundSplitView</string>
+ <string key="candidateClassName">DVTBorderedView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="borderedViewBelowTable">
+ <string key="name">borderedViewBelowTable</string>
+ <string key="candidateClassName">DVTBorderedView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="checkAndInstallNowButton">
+ <string key="name">checkAndInstallNowButton</string>
+ <string key="candidateClassName">NSButton</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="docSetInfoTextView">
+ <string key="name">docSetInfoTextView</string>
+ <string key="candidateClassName">NSTextView</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="docSetOutlineViewController">
+ <string key="name">docSetOutlineViewController</string>
+ <string key="candidateClassName">IDEDocSetOutlineViewController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="downloadsTableViewController">
+ <string key="name">downloadsTableViewController</string>
+ <string key="candidateClassName">IDEDocDownloadsTableViewController</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="minimumFontSizeControl">
+ <string key="name">minimumFontSizeControl</string>
+ <string key="candidateClassName">NSComboBox</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="noUpdatesAvailableMessage">
+ <string key="name">noUpdatesAvailableMessage</string>
+ <string key="candidateClassName">NSTextField</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="showInfoButton">
+ <string key="name">showInfoButton</string>
+ <string key="candidateClassName">NSButton</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="subscriptionTextField">
+ <string key="name">subscriptionTextField</string>
+ <string key="candidateClassName">NSTextField</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="subscriptionWindow">
+ <string key="name">subscriptionWindow</string>
+ <string key="candidateClassName">NSWindow</string>
+ </object>
+ <object class="IBToOneOutletInfo" key="validateAddSubscriptionButton">
+ <string key="name">validateAddSubscriptionButton</string>
+ <string key="candidateClassName">NSButton</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEDocViewingPrefPaneController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEEditor</string>
+ <string key="superclassName">IDEViewController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEEditor.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IDEViewController</string>
+ <string key="superclassName">DVTViewController</string>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IDEViewController.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">IKImageView</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="copy:">id</string>
+ <string key="crop:">id</string>
+ <string key="cut:">id</string>
+ <string key="paste:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="copy:">
+ <string key="name">copy:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="crop:">
+ <string key="name">crop:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="cut:">
+ <string key="name">cut:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="paste:">
+ <string key="name">paste:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/IKImageView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSDocument</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="printDocument:">id</string>
+ <string key="revertDocumentToSaved:">id</string>
+ <string key="runPageLayout:">id</string>
+ <string key="saveDocument:">id</string>
+ <string key="saveDocumentAs:">id</string>
+ <string key="saveDocumentTo:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="printDocument:">
+ <string key="name">printDocument:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="revertDocumentToSaved:">
+ <string key="name">revertDocumentToSaved:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="runPageLayout:">
+ <string key="name">runPageLayout:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="saveDocument:">
+ <string key="name">saveDocument:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="saveDocumentAs:">
+ <string key="name">saveDocumentAs:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="saveDocumentTo:">
+ <string key="name">saveDocumentTo:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/NSDocument.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">NSResponder</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">_insertFindPattern:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <string key="NS.key.0">_insertFindPattern:</string>
+ <object class="IBActionInfo" key="NS.object.0">
+ <string key="name">_insertFindPattern:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/NSResponder.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">QLPreviewBubble</string>
+ <string key="superclassName">NSObject</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="hide:">id</string>
+ <string key="show:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="hide:">
+ <string key="name">hide:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="show:">
+ <string key="name">show:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">parentWindow</string>
+ <string key="NS.object.0">NSWindow</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">parentWindow</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">parentWindow</string>
+ <string key="candidateClassName">NSWindow</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/QLPreviewBubble.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">QTMovieView</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="showAll:">id</string>
+ <string key="showCustomButton:">id</string>
+ <string key="toggleLoops:">id</string>
+ <string key="zoomIn:">id</string>
+ <string key="zoomOut:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="showAll:">
+ <string key="name">showAll:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="showCustomButton:">
+ <string key="name">showCustomButton:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="toggleLoops:">
+ <string key="name">toggleLoops:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="zoomIn:">
+ <string key="name">zoomIn:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="zoomOut:">
+ <string key="name">zoomOut:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/QTMovieView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">WebView</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="reloadFromOrigin:">id</string>
+ <string key="resetPageZoom:">id</string>
+ <string key="zoomPageIn:">id</string>
+ <string key="zoomPageOut:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="reloadFromOrigin:">
+ <string key="name">reloadFromOrigin:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="resetPageZoom:">
+ <string key="name">resetPageZoom:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="zoomPageIn:">
+ <string key="name">zoomPageIn:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="zoomPageOut:">
+ <string key="name">zoomPageOut:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/WebView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">XDDiagramView</string>
+ <string key="superclassName">NSView</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="_graphLayouterMenuItemAction:">id</string>
+ <string key="_zoomPopUpButtonAction:">id</string>
+ <string key="alignBottomEdges:">id</string>
+ <string key="alignCentersHorizontallyInContainer:">id</string>
+ <string key="alignCentersVerticallyInContainer:">id</string>
+ <string key="alignHorizontalCenters:">id</string>
+ <string key="alignLeftEdges:">id</string>
+ <string key="alignRightEdges:">id</string>
+ <string key="alignTopEdges:">id</string>
+ <string key="alignVerticalCenters:">id</string>
+ <string key="bringToFront:">id</string>
+ <string key="collapseAllCompartments:">id</string>
+ <string key="copy:">id</string>
+ <string key="cut:">id</string>
+ <string key="delete:">id</string>
+ <string key="deleteBackward:">id</string>
+ <string key="deleteForward:">id</string>
+ <string key="deselectAll:">id</string>
+ <string key="diagramZoomIn:">id</string>
+ <string key="diagramZoomOut:">id</string>
+ <string key="expandAllCompartments:">id</string>
+ <string key="flipHorizontally:">id</string>
+ <string key="flipVertically:">id</string>
+ <string key="layoutGraphicsConcentrically:">id</string>
+ <string key="layoutGraphicsHierarchically:">id</string>
+ <string key="lock:">id</string>
+ <string key="makeSameHeight:">id</string>
+ <string key="makeSameWidth:">id</string>
+ <string key="moveDown:">id</string>
+ <string key="moveDownAndModifySelection:">id</string>
+ <string key="moveLeft:">id</string>
+ <string key="moveLeftAndModifySelection:">id</string>
+ <string key="moveRight:">id</string>
+ <string key="moveRightAndModifySelection:">id</string>
+ <string key="moveUp:">id</string>
+ <string key="moveUpAndModifySelection:">id</string>
+ <string key="paste:">id</string>
+ <string key="rollDownAllCompartments:">id</string>
+ <string key="rollUpAllCompartments:">id</string>
+ <string key="selectAll:">id</string>
+ <string key="sendToBack:">id</string>
+ <string key="sizeToFit:">id</string>
+ <string key="toggleGridShown:">id</string>
+ <string key="toggleHiddenGraphicsShown:">id</string>
+ <string key="togglePageBreaksShown:">id</string>
+ <string key="toggleRuler:">id</string>
+ <string key="toggleSnapsToGrid:">id</string>
+ <string key="unlock:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="_graphLayouterMenuItemAction:">
+ <string key="name">_graphLayouterMenuItemAction:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="_zoomPopUpButtonAction:">
+ <string key="name">_zoomPopUpButtonAction:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="alignBottomEdges:">
+ <string key="name">alignBottomEdges:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="alignCentersHorizontallyInContainer:">
+ <string key="name">alignCentersHorizontallyInContainer:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="alignCentersVerticallyInContainer:">
+ <string key="name">alignCentersVerticallyInContainer:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="alignHorizontalCenters:">
+ <string key="name">alignHorizontalCenters:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="alignLeftEdges:">
+ <string key="name">alignLeftEdges:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="alignRightEdges:">
+ <string key="name">alignRightEdges:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="alignTopEdges:">
+ <string key="name">alignTopEdges:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="alignVerticalCenters:">
+ <string key="name">alignVerticalCenters:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="bringToFront:">
+ <string key="name">bringToFront:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="collapseAllCompartments:">
+ <string key="name">collapseAllCompartments:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="copy:">
+ <string key="name">copy:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="cut:">
+ <string key="name">cut:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="delete:">
+ <string key="name">delete:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="deleteBackward:">
+ <string key="name">deleteBackward:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="deleteForward:">
+ <string key="name">deleteForward:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="deselectAll:">
+ <string key="name">deselectAll:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="diagramZoomIn:">
+ <string key="name">diagramZoomIn:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="diagramZoomOut:">
+ <string key="name">diagramZoomOut:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="expandAllCompartments:">
+ <string key="name">expandAllCompartments:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="flipHorizontally:">
+ <string key="name">flipHorizontally:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="flipVertically:">
+ <string key="name">flipVertically:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="layoutGraphicsConcentrically:">
+ <string key="name">layoutGraphicsConcentrically:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="layoutGraphicsHierarchically:">
+ <string key="name">layoutGraphicsHierarchically:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="lock:">
+ <string key="name">lock:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="makeSameHeight:">
+ <string key="name">makeSameHeight:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="makeSameWidth:">
+ <string key="name">makeSameWidth:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="moveDown:">
+ <string key="name">moveDown:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="moveDownAndModifySelection:">
+ <string key="name">moveDownAndModifySelection:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="moveLeft:">
+ <string key="name">moveLeft:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="moveLeftAndModifySelection:">
+ <string key="name">moveLeftAndModifySelection:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="moveRight:">
+ <string key="name">moveRight:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="moveRightAndModifySelection:">
+ <string key="name">moveRightAndModifySelection:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="moveUp:">
+ <string key="name">moveUp:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="moveUpAndModifySelection:">
+ <string key="name">moveUpAndModifySelection:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="paste:">
+ <string key="name">paste:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="rollDownAllCompartments:">
+ <string key="name">rollDownAllCompartments:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="rollUpAllCompartments:">
+ <string key="name">rollUpAllCompartments:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="selectAll:">
+ <string key="name">selectAll:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="sendToBack:">
+ <string key="name">sendToBack:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="sizeToFit:">
+ <string key="name">sizeToFit:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="toggleGridShown:">
+ <string key="name">toggleGridShown:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="toggleHiddenGraphicsShown:">
+ <string key="name">toggleHiddenGraphicsShown:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="togglePageBreaksShown:">
+ <string key="name">togglePageBreaksShown:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="toggleRuler:">
+ <string key="name">toggleRuler:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="toggleSnapsToGrid:">
+ <string key="name">toggleSnapsToGrid:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="unlock:">
+ <string key="name">unlock:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <object class="NSMutableDictionary" key="outlets">
+ <string key="NS.key.0">_diagramController</string>
+ <string key="NS.object.0">IDEDataModelDiagramEditor</string>
+ </object>
+ <object class="NSMutableDictionary" key="toOneOutletInfosByName">
+ <string key="NS.key.0">_diagramController</string>
+ <object class="IBToOneOutletInfo" key="NS.object.0">
+ <string key="name">_diagramController</string>
+ <string key="candidateClassName">IDEDataModelDiagramEditor</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/XDDiagramView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">XDTableView</string>
+ <string key="superclassName">NSTableView</string>
+ <object class="NSMutableDictionary" key="actions">
+ <string key="NS.key.0">showAllTableColumns:</string>
+ <string key="NS.object.0">id</string>
+ </object>
+ <object class="NSMutableDictionary" key="actionInfosByName">
+ <string key="NS.key.0">showAllTableColumns:</string>
+ <object class="IBActionInfo" key="NS.object.0">
+ <string key="name">showAllTableColumns:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </object>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/XDTableView.h</string>
+ </object>
+ </object>
+ <object class="IBPartialClassDescription">
+ <string key="className">AppDelegate</string>
+ <string key="superclassName">NSObject</string>
+ <dictionary class="NSMutableDictionary" key="actions">
+ <string key="applicationShouldTerminate:">id</string>
+ <string key="applicationWillFinishLaunching:">id</string>
+ </dictionary>
+ <dictionary class="NSMutableDictionary" key="actionInfosByName">
+ <object class="IBActionInfo" key="applicationShouldTerminate:">
+ <string key="name">applicationShouldTerminate:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ <object class="IBActionInfo" key="applicationWillFinishLaunching:">
+ <string key="name">applicationWillFinishLaunching:</string>
+ <string key="candidateClassName">id</string>
+ </object>
+ </dictionary>
+ <object class="IBClassDescriptionSource" key="sourceIdentifier">
+ <string key="majorKey">IBProjectSource</string>
+ <string key="minorKey">./Classes/AppDelegate.h</string>
+ </object>
+ </object>
+ </array>
+ </object>
+ <int key="IBDocument.localizationMode">0</int>
+ <string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
+ <bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
+ <int key="IBDocument.defaultPropertyAccessControl">3</int>
+ <dictionary class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
+ <string key="NSMenuCheckmark">{11, 11}</string>
+ <string key="NSMenuMixedState">{10, 3}</string>
+ </dictionary>
+ <bool key="IBDocument.UseAutolayout">YES</bool>
+ </data>
+</archive>
diff --git a/examples/cocoa-application/app.qbs b/examples/cocoa-application/app.qbs
index f684786ad..67558e8a5 100644
--- a/examples/cocoa-application/app.qbs
+++ b/examples/cocoa-application/app.qbs
@@ -48,11 +48,11 @@
**
****************************************************************************/
-import qbs
+import qbs.Utilities
CppApplication {
Depends { condition: product.condition; name: "ib" }
- condition: qbs.hostOS.contains("macos")
+ condition: qbs.targetOS.contains("macos")
name: "Cocoa Application"
cpp.useObjcPrecompiledHeader: true
@@ -100,4 +100,11 @@ CppApplication {
}
ib.appIconName: "AppIcon"
+
+ Properties {
+ // codesign module only present starting from 1.19
+ condition: Utilities.versionCompare(qbs.version, "1.19") >= 0
+ codesign.enableCodeSigning: true
+ codesign.signingType: "ad-hoc"
+ }
}
diff --git a/examples/cocoa-application/cocoa-application.qbs b/examples/cocoa-application/cocoa-application.qbs
new file mode 100644
index 000000000..9c1ad1370
--- /dev/null
+++ b/examples/cocoa-application/cocoa-application.qbs
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Petroules Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+import qbs.Utilities
+
+Project {
+ references: [
+ "app.qbs"
+ ]
+
+ SubProject {
+ filePath: "dmg.qbs"
+ Properties {
+ condition: Utilities.versionCompare(qbs.version, "1.9") >= 0
+ }
+ }
+}
diff --git a/examples/cocoa-application/dmg.qbs b/examples/cocoa-application/dmg.qbs
index 610c5d2a3..0ae68b297 100644
--- a/examples/cocoa-application/dmg.qbs
+++ b/examples/cocoa-application/dmg.qbs
@@ -48,13 +48,12 @@
**
****************************************************************************/
-import qbs
-
AppleApplicationDiskImage {
condition: qbs.targetOS.contains("macos")
name: "Cocoa Application DMG"
targetName: "cocoa-application-" + version
version: "1.0"
+ builtByDefault: false
Depends { name: "Cocoa Application" }
Depends { name: "ib" }
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication.qbs b/examples/cocoa-touch-application/CocoaTouchApplication.qbs
deleted file mode 100644
index cf0a273d5..000000000
--- a/examples/cocoa-touch-application/CocoaTouchApplication.qbs
+++ /dev/null
@@ -1,107 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Petroules Corporation.
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples 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$
-**
-****************************************************************************/
-
-import qbs 1.0
-
-CppApplication {
- Depends { name: "xcode"; required: false }
- Depends { condition: product.condition; name: "ib" }
- condition: qbs.hostOS.contains("macos") && xcode.present
- name: "Cocoa Touch Application"
-
- qbs.targetPlatform: "ios"
- qbs.architecture: "arm64"
-
- cpp.useObjcPrecompiledHeader: true
- cpp.minimumIosVersion: "8.0"
- cpp.frameworks: [ "UIKit", "Foundation", "CoreGraphics" ]
-
- Group {
- prefix: "CocoaTouchApplication/"
- files: [
- "AppDelegate.h",
- "AppDelegate.m",
- "CocoaTouchApplication-Info.plist",
- "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"
- ]
- }
-
- Group {
- name: "Xcode Project"
- files: [
- "CocoaTouchApplication.xcodeproj/project.pbxproj"
- ]
- }
-
- Group {
- files: ["CocoaTouchApplication/CocoaTouchApplication-Prefix.pch"]
- fileTags: ["objc_pch_src"]
- }
-}
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication.xcodeproj/project.pbxproj b/examples/cocoa-touch-application/CocoaTouchApplication.xcodeproj/project.pbxproj
index 07f3a1a46..62d4425c9 100644
--- a/examples/cocoa-touch-application/CocoaTouchApplication.xcodeproj/project.pbxproj
+++ b/examples/cocoa-touch-application/CocoaTouchApplication.xcodeproj/project.pbxproj
@@ -13,15 +13,13 @@
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 */; };
+ AC83486F258674C900A9CBBB /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AC83486E258674C900A9CBBB /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -35,9 +33,6 @@
14E3FEB7175FB2E800C857C6 /* CocoaTouchApplication-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CocoaTouchApplication-Prefix.pch"; sourceTree = "<group>"; };
14E3FEB8175FB2E800C857C6 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
14E3FEB9175FB2E800C857C6 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
- 14E3FEBB175FB2E800C857C6 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
- 14E3FEBD175FB2E800C857C6 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = "<group>"; };
- 14E3FEBF175FB2E800C857C6 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
14E3FEC1175FB2E800C857C6 /* MasterViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MasterViewController.h; sourceTree = "<group>"; };
14E3FEC2175FB2E800C857C6 /* MasterViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MasterViewController.m; sourceTree = "<group>"; };
14E3FEC4175FB2E800C857C6 /* DetailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DetailViewController.h; sourceTree = "<group>"; };
@@ -46,6 +41,7 @@
14E3FECB175FB2E900C857C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MasterViewController_iPad.xib; sourceTree = "<group>"; };
14E3FECE175FB2E900C857C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/DetailViewController_iPhone.xib; sourceTree = "<group>"; };
14E3FED1175FB2E900C857C6 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/DetailViewController_iPad.xib; sourceTree = "<group>"; };
+ AC83486E258674C900A9CBBB /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -103,6 +99,7 @@
14E3FECD175FB2E900C857C6 /* DetailViewController_iPhone.xib */,
14E3FED0175FB2E900C857C6 /* DetailViewController_iPad.xib */,
14E3FEB0175FB2E800C857C6 /* Supporting Files */,
+ AC83486E258674C900A9CBBB /* LaunchScreen.storyboard */,
);
path = CocoaTouchApplication;
sourceTree = "<group>";
@@ -114,9 +111,6 @@
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 = "<group>";
@@ -147,14 +141,15 @@
14E3FE9E175FB2E800C857C6 /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 0460;
- ORGANIZATIONNAME = "Petroules Corporation";
+ LastUpgradeCheck = 1220;
+ ORGANIZATIONNAME = io.qbs;
};
buildConfigurationList = 14E3FEA1175FB2E800C857C6 /* Build configuration list for PBXProject "CocoaTouchApplication" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
+ English,
en,
);
mainGroup = 14E3FE9D175FB2E800C857C6;
@@ -172,10 +167,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ AC83486F258674C900A9CBBB /* LaunchScreen.storyboard in Resources */,
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 */,
@@ -247,27 +240,48 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = 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_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -278,20 +292,40 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -305,6 +339,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "CocoaTouchApplication/CocoaTouchApplication-Prefix.pch";
INFOPLIST_FILE = "CocoaTouchApplication/CocoaTouchApplication-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "io.qbs.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -316,6 +351,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "CocoaTouchApplication/CocoaTouchApplication-Prefix.pch";
INFOPLIST_FILE = "CocoaTouchApplication/CocoaTouchApplication-Info.plist";
+ PRODUCT_BUNDLE_IDENTIFIER = "io.qbs.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.m b/examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.m
index ed4d9f310..b93bcf2c7 100644
--- a/examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.m
+++ b/examples/cocoa-touch-application/CocoaTouchApplication/AppDelegate.m
@@ -40,32 +40,24 @@
@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];
+ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// 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];
+ MasterViewController *masterViewController = [[MasterViewController alloc] initWithNibName:@"MasterViewController_iPhone" bundle:nil];
+ self.navigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
self.window.rootViewController = self.navigationController;
} else {
- MasterViewController *masterViewController = [[[MasterViewController alloc] initWithNibName:@"MasterViewController_iPad" bundle:nil] autorelease];
- UINavigationController *masterNavigationController = [[[UINavigationController alloc] initWithRootViewController:masterViewController] autorelease];
+ MasterViewController *masterViewController = [[MasterViewController alloc] initWithNibName:@"MasterViewController_iPad" bundle:nil];
+ UINavigationController *masterNavigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
- DetailViewController *detailViewController = [[[DetailViewController alloc] initWithNibName:@"DetailViewController_iPad" bundle:nil] autorelease];
- UINavigationController *detailNavigationController = [[[UINavigationController alloc] initWithRootViewController:detailViewController] autorelease];
+ DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailViewController_iPad" bundle:nil];
+ UINavigationController *detailNavigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
masterViewController.detailViewController = detailViewController;
- self.splitViewController = [[[UISplitViewController alloc] init] autorelease];
+ self.splitViewController = [[UISplitViewController alloc] init];
self.splitViewController.delegate = detailViewController;
self.splitViewController.viewControllers = [NSArray arrayWithObjects:masterNavigationController, detailNavigationController, nil];
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Info.plist b/examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Info.plist
index f95cb4555..8075a9d96 100644
--- a/examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Info.plist
+++ b/examples/cocoa-touch-application/CocoaTouchApplication/CocoaTouchApplication-Info.plist
@@ -9,7 +9,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
- <string>org.example.${PRODUCT_NAME:rfc1034identifier}</string>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@@ -24,6 +24,8 @@
<string>1.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/Default-568h@2x.png b/examples/cocoa-touch-application/CocoaTouchApplication/Default-568h@2x.png
deleted file mode 100644
index 0891b7aab..000000000
--- a/examples/cocoa-touch-application/CocoaTouchApplication/Default-568h@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/Default.png b/examples/cocoa-touch-application/CocoaTouchApplication/Default.png
deleted file mode 100644
index 4c8ca6f69..000000000
--- a/examples/cocoa-touch-application/CocoaTouchApplication/Default.png
+++ /dev/null
Binary files differ
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/Default@2x.png b/examples/cocoa-touch-application/CocoaTouchApplication/Default@2x.png
deleted file mode 100644
index 35b84cffe..000000000
--- a/examples/cocoa-touch-application/CocoaTouchApplication/Default@2x.png
+++ /dev/null
Binary files differ
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.m b/examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.m
index 85a5d6ddb..dbe821650 100644
--- a/examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.m
+++ b/examples/cocoa-touch-application/CocoaTouchApplication/DetailViewController.m
@@ -41,21 +41,12 @@
@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];
+ _detailItem = newDetailItem;
// Update the view.
[self configureView];
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/LaunchScreen.storyboard b/examples/cocoa-touch-application/CocoaTouchApplication/LaunchScreen.storyboard
new file mode 100644
index 000000000..344bbca5c
--- /dev/null
+++ b/examples/cocoa-touch-application/CocoaTouchApplication/LaunchScreen.storyboard
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17506" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <device id="retina3_5" orientation="portrait" appearance="light"/>
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17505"/>
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Copyright © 2020 io.qbs. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
+ <rect key="frame" x="0.0" y="439.5" width="320" height="20.5"/>
+ <fontDescription key="fontDescription" type="system" pointSize="17"/>
+ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <nil key="highlightedColor"/>
+ </label>
+ <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Cocoa Touch Application" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
+ <rect key="frame" x="0.0" y="139.5" width="320" height="43"/>
+ <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
+ <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <nil key="highlightedColor"/>
+ </label>
+ </subviews>
+ <viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/>
+ <constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
+ <constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" constant="20" symbolic="YES" id="SfN-ll-jLj"/>
+ <constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/>
+ <constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
+ <constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="x7j-FC-K8j"/>
+ </constraints>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.m b/examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.m
index ebac52dcd..fe8afba09 100644
--- a/examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.m
+++ b/examples/cocoa-touch-application/CocoaTouchApplication/MasterViewController.m
@@ -49,20 +49,13 @@
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];
+ UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)];
self.navigationItem.rightBarButtonItem = addButton;
}
@@ -101,7 +94,7 @@
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
- cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
+ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
@@ -150,7 +143,7 @@
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 = [[DetailViewController alloc] initWithNibName:@"DetailViewController_iPhone" bundle:nil];
}
self.detailViewController.detailItem = object;
[self.navigationController pushViewController:self.detailViewController animated:YES];
diff --git a/examples/cocoa-touch-application/CocoaTouchApplication/main.m b/examples/cocoa-touch-application/CocoaTouchApplication/main.m
index 7665efcf1..0d92018b6 100644
--- a/examples/cocoa-touch-application/CocoaTouchApplication/main.m
+++ b/examples/cocoa-touch-application/CocoaTouchApplication/main.m
@@ -33,8 +33,8 @@
int main(int argc, char *argv[])
{
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
- [pool release];
- return retVal;
+ @autoreleasepool
+ {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+ }
}
diff --git a/examples/cocoa-touch-application/cocoa-touch-application.qbs b/examples/cocoa-touch-application/cocoa-touch-application.qbs
new file mode 100644
index 000000000..c837cacf1
--- /dev/null
+++ b/examples/cocoa-touch-application/cocoa-touch-application.qbs
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Petroules Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+CppApplication {
+ Depends { name: "xcode"; required: false }
+ Depends { condition: product.condition; name: "ib" }
+ condition: qbs.hostOS.contains("macos") && xcode.present && qbs.targetPlatform.contains("ios")
+ name: "Cocoa Touch Application"
+ install: true
+
+ bundle.identifierPrefix: "io.qbs"
+ bundle.infoPlist: ({"UILaunchStoryboardName": "LaunchScreen"})
+
+ cpp.useObjcPrecompiledHeader: true
+ cpp.minimumIosVersion: "9.0"
+ cpp.automaticReferenceCounting: true
+ cpp.frameworks: [ "UIKit", "Foundation", "CoreGraphics" ]
+
+ // sample code signing settings:
+ // codesign.enableCodeSigning: true
+ // codesign.provisioningProfile: "my provisioning profile name"
+ // codesign.signingIdentity: "Apple Development: My Team Name"
+
+ Group {
+ prefix: "CocoaTouchApplication/"
+ files: [
+ "AppDelegate.h",
+ "AppDelegate.m",
+ "CocoaTouchApplication-Info.plist",
+ "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",
+ "MasterViewController_iPad.xib",
+ "MasterViewController_iPhone.xib"
+ ]
+ }
+
+ Group {
+ id: bundle_resources
+ files: [
+ "CocoaTouchApplication/LaunchScreen.storyboard",
+ "CocoaTouchApplication/en.lproj/InfoPlist.strings"
+ ]
+ }
+
+ bundle.resources: bundle_resources.files
+
+ Group {
+ name: "Xcode Project"
+ files: [
+ "CocoaTouchApplication.xcodeproj/project.pbxproj"
+ ]
+ }
+
+ Group {
+ files: ["CocoaTouchApplication/CocoaTouchApplication-Prefix.pch"]
+ fileTags: ["objc_pch_src"]
+ }
+}
diff --git a/examples/code-generator/code-generator.qbs b/examples/code-generator/code-generator.qbs
index df15dec5e..fdb0e1aac 100644
--- a/examples/code-generator/code-generator.qbs
+++ b/examples/code-generator/code-generator.qbs
@@ -47,7 +47,6 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-import qbs
Project {
// A code generator that outputs a "Hello World" C++ program.
@@ -55,10 +54,15 @@ Project {
name: "hwgen"
consoleApplication: true
files: ["hwgen.cpp"]
+ Properties {
+ condition: qbs.toolchain.contains("gcc") || qbs.toolchain.contains("clang-cl")
+ cpp.cxxFlags: ["-Wno-deprecated-declarations"]
+ }
}
// Generate and build a hello-world application.
CppApplication {
+ condition: qbs.targetPlatform === qbs.hostPlatform
name: "hello-world"
Depends { name: "hwgen" }
Rule {
diff --git a/examples/code-generator/hwgen.cpp b/examples/code-generator/hwgen.cpp
index 1e2556648..90f2eadf4 100644
--- a/examples/code-generator/hwgen.cpp
+++ b/examples/code-generator/hwgen.cpp
@@ -54,12 +54,12 @@ int main(int argc, char **argv)
{
if (argc < 2)
return 1;
- FILE *f = fopen(argv[1], "w");
+ std::FILE *f = std::fopen(argv[1], "w");
if (!f)
return 2;
- fprintf(f, "#include <cstdio>\n\n"
- "int main()\n"
- "{\n printf(\"Hello World!\\n\");\n return 0;\n}\n");
- fclose(f);
+ std::fprintf(f, "#include <cstdio>\n\n"
+ "int main()\n"
+ "{\n std::printf(\"Hello World!\\n\");\n return 0;\n}\n");
+ std::fclose(f);
return 0;
}
diff --git a/examples/collidingmice/collidingmice.qbs b/examples/collidingmice/collidingmice.qbs
index 52231882e..8f93a3230 100644
--- a/examples/collidingmice/collidingmice.qbs
+++ b/examples/collidingmice/collidingmice.qbs
@@ -48,8 +48,6 @@
**
****************************************************************************/
-import qbs 1.0
-
CppApplication {
name : "CollidingMice"
Depends { name: "Qt.widgets" }
diff --git a/examples/collidingmice/main.cpp b/examples/collidingmice/main.cpp
index ffddf4c81..2d0514427 100644
--- a/examples/collidingmice/main.cpp
+++ b/examples/collidingmice/main.cpp
@@ -64,7 +64,6 @@ static const int MouseCount = 7;
int main(int argc, char **argv)
{
QApplication app(argc, argv);
- qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
//! [0]
//! [1]
diff --git a/examples/collidingmice/mouse.cpp b/examples/collidingmice/mouse.cpp
index 7075ada38..bbc718649 100644
--- a/examples/collidingmice/mouse.cpp
+++ b/examples/collidingmice/mouse.cpp
@@ -71,9 +71,9 @@ static qreal normalizeAngle(qreal angle)
//! [0]
Mouse::Mouse()
: angle(0), speed(0), mouseEyeDirection(0),
- color(qrand() % 256, qrand() % 256, qrand() % 256)
+ color(m_rand.generate() % 256, m_rand.generate() % 256, m_rand.generate() % 256)
{
- setRotation(qrand() % (360 * 16));
+ setRotation(m_rand.generate() % (360 * 16));
}
//! [0]
@@ -189,16 +189,16 @@ void Mouse::advance(int step)
// Add some random movement
//! [10]
- if (dangerMice.size() > 1 && (qrand() % 10) == 0) {
- if (qrand() % 1)
- angle += (qrand() % 100) / 500.0;
+ if (dangerMice.size() > 1 && (m_rand.generate() % 10) == 0) {
+ if (m_rand.generate() % 1)
+ angle += (m_rand.generate() % 100) / 500.0;
else
- angle -= (qrand() % 100) / 500.0;
+ angle -= (m_rand.generate() % 100) / 500.0;
}
//! [10]
//! [11]
- speed += (-50 + qrand() % 100) / 100.0;
+ speed += (-50 + m_rand.generate() % 100) / 100.0;
qreal dx = ::sin(angle) * 10;
mouseEyeDirection = (qAbs(dx / 5) < 1) ? 0 : dx / 5;
diff --git a/examples/collidingmice/mouse.h b/examples/collidingmice/mouse.h
index a98618ea5..23b9faad5 100644
--- a/examples/collidingmice/mouse.h
+++ b/examples/collidingmice/mouse.h
@@ -52,6 +52,7 @@
#define MOUSE_H
#include <QGraphicsItem>
+#include <QRandomGenerator>
//! [0]
class Mouse : public QGraphicsItem
@@ -68,6 +69,7 @@ protected:
void advance(int step) override;
private:
+ QRandomGenerator m_rand = QRandomGenerator::securelySeeded();
qreal angle;
qreal speed;
qreal mouseEyeDirection;
diff --git a/examples/compiled-qml/compiled-qml.qbs b/examples/compiled-qml/compiled-qml.qbs
new file mode 100644
index 000000000..5d5dd3a8c
--- /dev/null
+++ b/examples/compiled-qml/compiled-qml.qbs
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+CppApplication {
+ Depends { name: "Qt.quick" }
+
+ files: [
+ "cheese.jpg",
+ "main.cpp",
+ "qml.qrc"
+ ]
+
+ Group {
+ name: "QML Files"
+ files: ["*.qml"]
+ }
+}
diff --git a/examples/compiled-qml/myapp.qbs b/examples/compiled-qml/myapp.qbs
deleted file mode 100644
index 3a900fac9..000000000
--- a/examples/compiled-qml/myapp.qbs
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples 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$
-**
-****************************************************************************/
-
-import qbs
-
-CppApplication {
- Depends { name: "Qt.quick" }
-
- files: [
- "cheese.jpg",
- "main.cpp",
- "qml.qrc"
- ]
-
- Group {
- name: "QML Files"
- files: ["*.qml"]
- }
-}
diff --git a/examples/examples.qbs b/examples/examples.qbs
index ff6e7a191..fa8ed0f5c 100644
--- a/examples/examples.qbs
+++ b/examples/examples.qbs
@@ -48,24 +48,6 @@
**
****************************************************************************/
-import qbs
-
-Project {
- references: [
- "app-and-lib/app_and_lib.qbs",
- "cocoa-application/CocoaApplication.qbs",
- "cocoa-touch-application/CocoaTouchApplication.qbs",
- "code-generator/code-generator.qbs",
- "collidingmice/collidingmice.qbs",
- "compiled-qml/myapp.qbs",
- "grpc/ping-pong-grpc.qbs",
- "helloworld-complex/hello.qbs",
- "helloworld-minimal/hello.qbs",
- "helloworld-qt/hello.qbs",
- "install-bundle/install-bundle.qbs",
- "protobuf/cpp/addressbook.qbs",
- "protobuf/objc/addressbook.qbs",
- "baremetal/baremetal.qbs",
- "rule/rule.qbs",
- ]
+Product {
+ files: "**/*"
}
diff --git a/examples/exporters/exporters.qbs b/examples/exporters/exporters.qbs
new file mode 100644
index 000000000..bae62e17c
--- /dev/null
+++ b/examples/exporters/exporters.qbs
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+Project {
+ references: [
+ "lib_a/lib_a.qbs",
+ "lib_b/lib_b.qbs"
+ ]
+ qbsSearchPaths: "qbs"
+}
+
diff --git a/examples/exporters/lib_a/lib_a.cpp b/examples/exporters/lib_a/lib_a.cpp
new file mode 100644
index 000000000..e3e7f17d9
--- /dev/null
+++ b/examples/exporters/lib_a/lib_a.cpp
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "lib_a.h"
+
+const char *bla()
+{
+ return "Hello World!";
+}
diff --git a/examples/exporters/lib_a/lib_a.h b/examples/exporters/lib_a/lib_a.h
new file mode 100644
index 000000000..f583421b9
--- /dev/null
+++ b/examples/exporters/lib_a/lib_a.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 LIB_A_H
+#define LIB_A_H
+
+const char *bla();
+
+#endif // LIB_A_H
diff --git a/examples/exporters/lib_a/lib_a.qbs b/examples/exporters/lib_a/lib_a.qbs
new file mode 100644
index 000000000..fedbb7074
--- /dev/null
+++ b/examples/exporters/lib_a/lib_a.qbs
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+MyLibrary {
+ name: "lib_a"
+ files: ["lib_a.cpp"]
+
+ Group {
+ name: "API headers"
+ files: ["lib_a.h"]
+ qbs.install: true
+ qbs.installDir: headersInstallDir
+ }
+ Export {
+ Depends { name: "cpp" }
+ cpp.dynamicLibraries: "z"
+ cpp.libraryPaths: "/opt/local/lib"
+ }
+}
+
diff --git a/examples/exporters/lib_b/lib_b.cpp b/examples/exporters/lib_b/lib_b.cpp
new file mode 100644
index 000000000..b29dc57b6
--- /dev/null
+++ b/examples/exporters/lib_b/lib_b.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "lib_b.h"
+
+#include <lib_a.h>
+
+#include <cstdio>
+
+int foo()
+{
+ std::puts(bla());
+ return 2;
+}
diff --git a/examples/exporters/lib_b/lib_b.h b/examples/exporters/lib_b/lib_b.h
new file mode 100644
index 000000000..a3d3e3aee
--- /dev/null
+++ b/examples/exporters/lib_b/lib_b.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 LIB_B_H
+#define LIB_B_H
+
+int foo();
+
+#endif // LIB_B_H
diff --git a/examples/exporters/lib_b/lib_b.qbs b/examples/exporters/lib_b/lib_b.qbs
new file mode 100644
index 000000000..0c680f3e0
--- /dev/null
+++ b/examples/exporters/lib_b/lib_b.qbs
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+MyLibrary {
+ Depends { name: "lib_a" }
+ name: "lib_b"
+ files: ["lib_b.cpp"]
+ Group {
+ name: "API headers"
+ files: ["lib_b.h"]
+ qbs.install: true
+ qbs.installDir: headersInstallDir
+ }
+ Export {
+ Depends { name: 'lib_a' }
+ }
+}
diff --git a/examples/exporters/qbs/imports/MyLibrary.qbs b/examples/exporters/qbs/imports/MyLibrary.qbs
new file mode 100644
index 000000000..4607776b3
--- /dev/null
+++ b/examples/exporters/qbs/imports/MyLibrary.qbs
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+import qbs.FileInfo
+
+StaticLibrary {
+ Depends { name: 'cpp' }
+
+ property string headersInstallDir: "include"
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ prefixMapping: [{
+ prefix: exportingProduct.sourceDirectory,
+ replacement: FileInfo.joinPaths(exportingProduct.qbs.installPrefix,
+ exportingProduct.headersInstallDir)
+ }]
+ }
+ install: true
+
+ Depends { name: "Exporter.qbs" }
+
+ Group {
+ fileTagsFilter: "Exporter.qbs.module"
+ qbs.install: install
+ qbs.installDir: FileInfo.joinPaths(installDir, "qbs", "modules", product.name)
+ }
+
+ Depends { name: "Exporter.pkgconfig" }
+ Exporter.pkgconfig.versionEntry: "1.0"
+
+ Group {
+ fileTagsFilter: ["Exporter.pkgconfig.pc"]
+ qbs.install: install
+ qbs.installDir: FileInfo.joinPaths(installDir, "pkgconfig")
+ }
+
+ Depends { name: "Exporter.cmake" }
+ Group {
+ fileTagsFilter: ["Exporter.cmake.package"]
+ qbs.install: install
+ qbs.installDir: FileInfo.joinPaths(installDir, "cmake", product.name)
+ }
+
+ Depends { name: 'bundle' }
+ bundle.isBundle: false
+}
+
diff --git a/examples/grpc/client.cpp b/examples/grpc/client.cpp
index 55bcd866d..e6af8e5ec 100644
--- a/examples/grpc/client.cpp
+++ b/examples/grpc/client.cpp
@@ -1,30 +1,50 @@
/****************************************************************************
**
-** Copyright (C) 2019 Ivan Komissarov
-** Contact: abbapoh@gmail.com
+** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
**
-** This file is part of Qbs.
+** This file is part of the examples 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 http://www.qt.io/terms-conditions. For further information
-** use the contact form at http://www.qt.io/contact-us.
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
-** GNU 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.
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
**
-** 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.
+** "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$
**
****************************************************************************/
@@ -51,7 +71,7 @@
class Client
{
public:
- Client(std::shared_ptr<grpc::Channel> channel)
+ Client(const std::shared_ptr<grpc::Channel> &channel)
: m_stub(PP::MyApi::NewStub(channel)) {}
int ping(int count) {
diff --git a/examples/grpc/grpc.qbs b/examples/grpc/grpc.qbs
new file mode 100644
index 000000000..b6d169508
--- /dev/null
+++ b/examples/grpc/grpc.qbs
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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.Utilities
+
+Project {
+ condition: Utilities.versionCompare(qbs.version, "1.14") >= 0
+ qbsModuleProviders: "qbspkgconfig"
+
+ Application {
+ Depends { name: "cpp" }
+ Depends { name: "protobuf.cpp"; required: false }
+ condition: protobuf.cpp.present && qbs.targetPlatform === qbs.hostPlatform
+ protobuf.cpp.useGrpc: true
+ consoleApplication: true
+ cpp.cxxLanguageVersion: "c++17"
+ cpp.minimumMacosVersion: "10.15"
+ name: "client"
+ files: "client.cpp"
+ Properties {
+ condition: qbs.toolchain.contains("gcc")
+ cpp.cxxFlags: "-Wno-deprecated-declarations"
+ }
+ Group {
+ files: "ping-pong-grpc.proto"
+ fileTags: "protobuf.grpc"
+ }
+ }
+
+ Application {
+ Depends { name: "cpp" }
+ Depends { name: "protobuf.cpp"; required: false }
+ condition: protobuf.cpp.present && qbs.targetPlatform === qbs.hostPlatform
+ protobuf.cpp.useGrpc: true
+ consoleApplication: true
+ cpp.cxxLanguageVersion: "c++17"
+ cpp.minimumMacosVersion: "10.15"
+ name: "server"
+ files: "server.cpp"
+ Properties {
+ condition: qbs.toolchain.contains("gcc")
+ cpp.cxxFlags: "-Wno-deprecated-declarations"
+ }
+ Group {
+ files: "ping-pong-grpc.proto"
+ fileTags: "protobuf.grpc"
+ }
+ }
+}
diff --git a/examples/grpc/ping-pong-grpc.qbs b/examples/grpc/ping-pong-grpc.qbs
deleted file mode 100644
index 8d8909350..000000000
--- a/examples/grpc/ping-pong-grpc.qbs
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Ivan Komissarov
-** Contact: abbapoh@gmail.com
-**
-** 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.Utilities
-
-Project {
- condition: Utilities.versionCompare(qbs.version, "1.14") >= 0
-
- Application {
- Depends { name: "cpp" }
- Depends { name: "protobuf.cpp"; required: false }
- condition: protobuf.cpp.present
- protobuf.cpp.useGrpc: true
- consoleApplication: true
- cpp.cxxLanguageVersion: "c++17"
- name: "client"
- files: "client.cpp"
- Group {
- files: "ping-pong-grpc.proto"
- fileTags: "protobuf.grpc"
- }
- }
-
- Application {
- Depends { name: "cpp" }
- Depends { name: "protobuf.cpp"; required: false }
- condition: protobuf.cpp.present
- protobuf.cpp.useGrpc: true
- consoleApplication: true
- cpp.cxxLanguageVersion: "c++17"
- name: "server"
- files: "client.cpp"
- Group {
- files: "ping-pong-grpc.proto"
- fileTags: "protobuf.grpc"
- }
- }
-}
diff --git a/examples/grpc/server.cpp b/examples/grpc/server.cpp
index 89d6f0096..74460d29d 100644
--- a/examples/grpc/server.cpp
+++ b/examples/grpc/server.cpp
@@ -1,30 +1,50 @@
/****************************************************************************
**
-** Copyright (C) 2019 Ivan Komissarov
-** Contact: abbapoh@gmail.com
+** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
**
-** This file is part of Qbs.
+** This file is part of the examples 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 http://www.qt.io/terms-conditions. For further information
-** use the contact form at http://www.qt.io/contact-us.
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
-** GNU 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.
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
**
-** 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.
+** "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$
**
****************************************************************************/
diff --git a/examples/helloworld-complex/hello.qbs b/examples/helloworld-complex/hello.qbs
deleted file mode 100644
index 725424e49..000000000
--- a/examples/helloworld-complex/hello.qbs
+++ /dev/null
@@ -1,89 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples 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$
-**
-****************************************************************************/
-
-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/helloworld-complex.qbs b/examples/helloworld-complex/helloworld-complex.qbs
new file mode 100644
index 000000000..fab3fd516
--- /dev/null
+++ b/examples/helloworld-complex/helloworld-complex.qbs
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+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 ? 'HAS_DEBUG' : 'HAS_RELEASE'
+ ]);
+ if (project.hasSpecialFeature)
+ defines.push("HAS_SPECIAL_FEATURE");
+ return defines;
+ }
+ prefix: "src/"
+ files: [
+ 'main.cpp'
+ ]
+ }
+ }
+}
diff --git a/examples/helloworld-complex/src/main.cpp b/examples/helloworld-complex/src/main.cpp
index 691c401e5..54e215d13 100644
--- a/examples/helloworld-complex/src/main.cpp
+++ b/examples/helloworld-complex/src/main.cpp
@@ -67,10 +67,10 @@
int main()
{
someUsefulFunction();
-#ifdef _DEBUG
- puts("Hello World! (debug version)");
-#else
- puts("Hello World! (release version)");
+#if defined(HAS_DEBUG)
+ std::puts("Hello World! (debug version)");
+#elif defined(HAS_RELEASE)
+ std::puts("Hello World! (release version)");
#endif
#ifdef HAS_SPECIAL_FEATURE
bragAboutSpecialFeature();
diff --git a/examples/helloworld-minimal/hello.qbs b/examples/helloworld-minimal/hello.qbs
deleted file mode 100644
index 5cad9f4bf..000000000
--- a/examples/helloworld-minimal/hello.qbs
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples 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$
-**
-****************************************************************************/
-
-import qbs
-
-CppApplication {
- name: "HelloWorld-minimal"
- files: "main.cpp"
-}
diff --git a/examples/helloworld-minimal/helloworld-minimal.qbs b/examples/helloworld-minimal/helloworld-minimal.qbs
new file mode 100644
index 000000000..40149d01f
--- /dev/null
+++ b/examples/helloworld-minimal/helloworld-minimal.qbs
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+CppApplication {
+ name: "HelloWorld-minimal"
+ files: "main.cpp"
+}
diff --git a/examples/helloworld-qt/hello.qbs b/examples/helloworld-qt/hello.qbs
deleted file mode 100644
index 4c6e070af..000000000
--- a/examples/helloworld-qt/hello.qbs
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the examples 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$
-**
-****************************************************************************/
-
-import qbs
-
-QtApplication {
- name: "HelloWorld-Qt"
- files: "main.cpp"
-}
diff --git a/examples/helloworld-qt/helloworld-qt.qbs b/examples/helloworld-qt/helloworld-qt.qbs
new file mode 100644
index 000000000..8d8b6e493
--- /dev/null
+++ b/examples/helloworld-qt/helloworld-qt.qbs
@@ -0,0 +1,54 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+QtApplication {
+ name: "HelloWorld-Qt"
+ files: "main.cpp"
+}
diff --git a/examples/helloworld-qt/main.cpp b/examples/helloworld-qt/main.cpp
index 217fc12d0..e1e822591 100644
--- a/examples/helloworld-qt/main.cpp
+++ b/examples/helloworld-qt/main.cpp
@@ -55,5 +55,7 @@
int main()
{
- QTextStream(stdout) << QCoreApplication::translate("hello", "Hello, World!") << endl;
+ QTextStream ts(stdout);
+ ts << QCoreApplication::translate("hello", "Hello, World!\n");
+ ts.flush();
}
diff --git a/examples/install-bundle/install-bundle.qbs b/examples/install-bundle/install-bundle.qbs
index ac5a6c255..cedfc2105 100644
--- a/examples/install-bundle/install-bundle.qbs
+++ b/examples/install-bundle/install-bundle.qbs
@@ -1,14 +1,17 @@
-import qbs
import qbs.FileInfo
Project {
CppApplication {
+ Depends { name: "bundle" }
Depends { name: "windowutils" }
Depends { name: "ib"; condition: qbs.targetOS.contains("darwin") }
Depends { name: "Qt"; submodules: ["core", "gui", "widgets"] }
+ condition: qbs.targetOS.contains("macos")
+ || qbs.targetOS.contains("linux")
+ || qbs.targetOS.contains("windows")
name: "window"
- property bool isBundle: qbs.targetOS.contains("darwin") && bundle.isBundle
+ property bool isBundle: bundle.isBundle
targetName: isBundle ? "Window" : "window"
files: [
"main.cpp",
@@ -22,7 +25,7 @@ Project {
cpp.minimumMacosVersion: "10.10"
Group {
- fileTagsFilter: isBundle ? ["bundle.content"] : ["application"]
+ fileTagsFilter: qbs.targetOS.contains("darwin") && isBundle ? ["bundle.content"] : ["application"]
qbs.install: true
qbs.installDir: isBundle ? "Applications" : (qbs.targetOS.contains("windows") ? "" : "bin")
qbs.installSourceBase: product.buildDirectory
@@ -30,6 +33,7 @@ Project {
}
DynamicLibrary {
+ Depends { name: "bundle" }
Depends { name: "cpp" }
name: "windowutils"
diff --git a/examples/pkgconfig-provider/main.c b/examples/pkgconfig-provider/main.c
new file mode 100644
index 000000000..398b06842
--- /dev/null
+++ b/examples/pkgconfig-provider/main.c
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** MIT License
+
+** Copyright (c) 2017 Aleksander Alekseev
+
+** 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <errno.h>
+#include <limits.h>
+#include <zlib.h>
+
+int main(int argc, char* argv[])
+{
+ int res;
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "Usage: %s <fname>\n", argv[0]);
+ return 1;
+ }
+
+ char* fname = argv[1];
+
+ struct stat file_stat;
+ res = stat(fname, &file_stat);
+ if (res == -1)
+ {
+ fprintf(stderr, "stat(...) failed, errno = %d\n", errno);
+ return 1;
+ }
+
+ size_t temp_file_size = (size_t)file_stat.st_size;
+ if (temp_file_size >= INT_MAX)
+ {
+ fprintf(stderr, "Error: filze_size >= INT_MAX (%d)\n", INT_MAX);
+ return 1;
+ }
+
+ int file_size = (int)temp_file_size;
+ int buff_size = file_size + 1;
+ void* file_buff = malloc(buff_size);
+ if (file_buff == NULL)
+ {
+ fprintf(stderr, "malloc(buff_size) failed, buff_size = %d\n",
+ file_size);
+ return 1;
+ }
+
+ int fid = open(fname, O_RDONLY);
+ if (fid == -1)
+ {
+ fprintf(stderr, "open(...) failed, errno = %d\n", errno);
+ free(file_buff);
+ return 1;
+ }
+
+ if (read(fid, file_buff, file_size) != file_size)
+ {
+ fprintf(stderr, "read(...) failed, errno = %d\n", errno);
+ free(file_buff);
+ close(fid);
+ return 1;
+ }
+
+ close(fid);
+
+ uLongf compress_buff_size = compressBound(file_size);
+ void* compress_buff = malloc(compress_buff_size);
+ if (compress_buff == NULL)
+ {
+ fprintf(stderr,
+ "malloc(compress_buff_size) failed, "
+ "compress_buff_size = %lu\n",
+ compress_buff_size);
+ free(file_buff);
+ return 1;
+ }
+
+ uLongf compressed_size = compress_buff_size;
+ res = compress(compress_buff, &compressed_size, file_buff, file_size);
+ if (res != Z_OK)
+ {
+ fprintf(stderr, "compress(...) failed, res = %d\n", res);
+ free(compress_buff);
+ free(file_buff);
+ return 1;
+ }
+
+ memset(file_buff, 0, buff_size);
+ uLongf decompressed_size = (uLongf)file_size;
+ res = uncompress(file_buff, &decompressed_size,
+ compress_buff, compressed_size);
+ if (res != Z_OK)
+ {
+ fprintf(stderr, "uncompress(...) failed, res = %d\n", res);
+ free(compress_buff);
+ free(file_buff);
+ return 1;
+ }
+
+ printf(
+ "%s\n----------------\n"
+ "File size: %d, compress_buff_size: %lu, compressed_size: %lu, "
+ "decompressed_size: %lu\n",
+ (char*)file_buff, file_size, compress_buff_size, compressed_size,
+ decompressed_size);
+
+ free(compress_buff);
+ free(file_buff);
+}
diff --git a/examples/pkgconfig-provider/pkgconfig-provider.qbs b/examples/pkgconfig-provider/pkgconfig-provider.qbs
new file mode 100644
index 000000000..34ba32d03
--- /dev/null
+++ b/examples/pkgconfig-provider/pkgconfig-provider.qbs
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+//! [0]
+CppApplication {
+ consoleApplication: true
+ Depends { name: "zlib"; required: false }
+ condition: zlib.present
+ name: "PkgConfigProviderExample"
+ files: "main.c"
+ qbsModuleProviders: ["qbspkgconfig"]
+}
+//! [0]
diff --git a/examples/protobuf/cpp/README.md b/examples/protobuf/addressbook_cpp/README.md
index f0e1b1a93..f0e1b1a93 100644
--- a/examples/protobuf/cpp/README.md
+++ b/examples/protobuf/addressbook_cpp/README.md
diff --git a/examples/protobuf/addressbook_cpp/addressbook_cpp.qbs b/examples/protobuf/addressbook_cpp/addressbook_cpp.qbs
new file mode 100644
index 000000000..5a3b27652
--- /dev/null
+++ b/examples/protobuf/addressbook_cpp/addressbook_cpp.qbs
@@ -0,0 +1,18 @@
+import qbs.Host
+
+CppApplication {
+ consoleApplication: true
+ condition: protobuf.cpp.present && qbs.targetPlatform === Host.platform()
+
+ Depends { name: "cpp" }
+ cpp.minimumMacosVersion: "10.15"
+
+ Depends { name: "protobuf.cpp"; required: false }
+
+ files: [
+ "../shared/addressbook.proto",
+ "main.cpp",
+ "README.md",
+ ]
+ qbsModuleProviders: "qbspkgconfig"
+}
diff --git a/examples/protobuf/addressbook_cpp/main.cpp b/examples/protobuf/addressbook_cpp/main.cpp
new file mode 100644
index 000000000..31baba3d1
--- /dev/null
+++ b/examples/protobuf/addressbook_cpp/main.cpp
@@ -0,0 +1,179 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// 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 Google Inc. 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.
+
+#include <ctime>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#ifdef __GNUC__
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-parameter"
+# include <google/protobuf/util/time_util.h>
+# pragma GCC diagnostic pop
+#else
+# include <google/protobuf/util/time_util.h>
+#endif // __GNUC__
+
+#include "addressbook.pb.h"
+
+using google::protobuf::util::TimeUtil;
+
+int printUsage(char *argv0)
+{
+ std::cerr << "Usage: " << argv0 << "add|list ADDRESS_BOOK_FILE" << std::endl;
+ return -1;
+}
+
+std::string readString(const std::string &promt)
+{
+ std::string result;
+ std::cout << promt;
+ std::getline(std::cin, result);
+ return result;
+}
+
+// This function fills in a Person message based on user input.
+void promptForAddress(tutorial::Person* person)
+{
+ std::cout << "Enter person ID number: ";
+ int id;
+ std::cin >> id;
+ person->set_id(id);
+ std::cin.ignore(256, '\n');
+
+ *person->mutable_name() = readString("Enter name: ");
+
+ const auto email = readString("Enter email address (blank for none): ");
+ if (!email.empty())
+ person->set_email(email);
+
+ while (true) {
+ const auto number = readString("Enter a phone number (or leave blank to finish): ");
+ if (number.empty())
+ break;
+
+ tutorial::Person::PhoneNumber *phone_number = person->add_phones();
+ phone_number->set_number(number);
+
+ const auto type = readString("Is this a mobile, home, or work phone? ");
+ if (type == "mobile")
+ phone_number->set_type(tutorial::Person::MOBILE);
+ else if (type == "home")
+ phone_number->set_type(tutorial::Person::HOME);
+ else if (type == "work")
+ phone_number->set_type(tutorial::Person::WORK);
+ else
+ std::cout << "Unknown phone type. Using default." << std::endl;
+ }
+ *person->mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(NULL));
+}
+
+// Iterates though all people in the AddressBook and prints info about them.
+void listPeople(const tutorial::AddressBook& address_book)
+{
+ for (int i = 0; i < address_book.people_size(); i++) {
+ const tutorial::Person& person = address_book.people(i);
+
+ std::cout << "Person ID: " << person.id() << std::endl;
+ std::cout << " Name: " << person.name() << std::endl;
+ if (!person.email().empty()) {
+ std::cout << " E-mail address: " << person.email() << std::endl;
+ }
+
+ for (int j = 0; j < person.phones_size(); j++) {
+ const tutorial::Person::PhoneNumber& phone_number = person.phones(j);
+
+ switch (phone_number.type()) {
+ case tutorial::Person::MOBILE:
+ std::cout << " Mobile phone #: ";
+ break;
+ case tutorial::Person::HOME:
+ std::cout << " Home phone #: ";
+ break;
+ case tutorial::Person::WORK:
+ std::cout << " Work phone #: ";
+ break;
+ default:
+ std::cout << " Unknown phone #: ";
+ break;
+ }
+ std::cout << phone_number.number() << std::endl;
+ }
+ if (person.has_last_updated()) {
+ std::cout << " Updated: " << TimeUtil::ToString(person.last_updated()) << std::endl;
+ }
+ }
+}
+
+int main(int argc, char* argv[]) {
+ // Verify that the version of the library that we linked against is
+ // compatible with the version of the headers we compiled against.
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ if (argc != 3)
+ return printUsage(argv[0]);
+
+ tutorial::AddressBook address_book;
+
+ // Read the existing address book.
+ std::fstream input(argv[2], std::ios::in | std::ios::binary);
+ if (!input) {
+ std::cout << argv[2] << ": File not found." << std::endl;
+ } else if (!address_book.ParseFromIstream(&input)) {
+ std::cerr << "Failed to parse address book." << std::endl;
+ return -1;
+ }
+
+ const std::string mode(argv[1]);
+ if (mode == "add") {
+ // Add an address.
+ promptForAddress(address_book.add_people());
+
+ if (!input)
+ std::cout << "Creating a new file." << std::endl;
+
+ // Write the new address book back to disk.
+ std::fstream output(argv[2], std::ios::out | std::ios::trunc | std::ios::binary);
+ if (!address_book.SerializeToOstream(&output)) {
+ std::cerr << "Failed to write address book." << std::endl;
+ return -1;
+ }
+ } else if (mode == "list") {
+ listPeople(address_book);
+ } else {
+ return printUsage(argv[0]);
+ }
+
+ // Optional: Delete all global objects allocated by libprotobuf.
+ google::protobuf::ShutdownProtobufLibrary();
+
+ return 0;
+}
diff --git a/examples/protobuf/objc/README.md b/examples/protobuf/addressbook_objc/README.md
index c0fc7c0e5..c0fc7c0e5 100644
--- a/examples/protobuf/objc/README.md
+++ b/examples/protobuf/addressbook_objc/README.md
diff --git a/examples/protobuf/addressbook_objc/addressbook_objc.qbs b/examples/protobuf/addressbook_objc/addressbook_objc.qbs
new file mode 100644
index 000000000..a573b9015
--- /dev/null
+++ b/examples/protobuf/addressbook_objc/addressbook_objc.qbs
@@ -0,0 +1,13 @@
+CppApplication {
+ consoleApplication: true
+ condition: protobuf.objc.present && qbs.targetOS.contains("macos")
+
+ Depends { name: "cpp" }
+ Depends { name: "protobuf.objc"; required: false }
+
+ Group {
+ cpp.automaticReferenceCounting: true
+ files: "main.m"
+ }
+ files: "../shared/addressbook.proto"
+}
diff --git a/examples/protobuf/addressbook_objc/main.m b/examples/protobuf/addressbook_objc/main.m
new file mode 100644
index 000000000..5e21fa060
--- /dev/null
+++ b/examples/protobuf/addressbook_objc/main.m
@@ -0,0 +1,183 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+#import "Addressbook.pbobjc.h"
+
+#import <Foundation/Foundation.h>
+
+int printUsage(char *argv0)
+{
+ NSString *programName = [[NSString alloc] initWithUTF8String:argv0];
+ NSLog(@"Usage: %@ add|list ADDRESS_BOOK_FILE", programName);
+ return -1;
+}
+
+NSString *readString(NSString *promt)
+{
+ NSLog(@"%@", promt);
+ NSFileHandle *inputFile = [NSFileHandle fileHandleWithStandardInput];
+ NSData *inputData = [inputFile availableData];
+ NSString *result = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
+ result = [result stringByTrimmingCharactersInSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ return result;
+}
+
+// This function fills in a Person message based on user input.
+void promptForAddress(Person* person)
+{
+ person.id_p = [readString(@"Enter person ID number:") intValue];
+ person.name = readString(@"Enter name:");
+
+ NSString *email = readString(@"Enter email address (blank for none):");
+ if ([email length] != 0)
+ person.email = email;
+
+ while (true) {
+ NSString *number = readString(@"Enter a phone number (or leave blank to finish):");
+ if ([number length] == 0)
+ break;
+
+ Person_PhoneNumber* phoneNumber = [[Person_PhoneNumber alloc] init];
+ phoneNumber.number = number;
+
+ NSString *type = readString(@"Is this a mobile, home, or work phone?:");
+ NSLog(@"\"%@\"", type);
+ if ([type compare:@"mobile"] == NSOrderedSame)
+ phoneNumber.type = Person_PhoneType_Mobile;
+ else if ([type compare:@"home"] == NSOrderedSame)
+ phoneNumber.type = Person_PhoneType_Home;
+ else if ([type compare:@"work"] == NSOrderedSame)
+ phoneNumber.type = Person_PhoneType_Work;
+ else
+ NSLog(@"Unknown phone type. Using default.");
+
+ [person.phonesArray addObject:phoneNumber];
+ }
+}
+
+// Iterates though all people in the AddressBook and prints info about them.
+void listPeople(AddressBook *addressBook)
+{
+ for (Person *person in addressBook.peopleArray) {
+ NSLog(@"Person ID: %d", person.id_p);
+ NSLog(@"Person name: %@", person.name);
+
+ if ([person.email length] != 0) {
+ NSLog(@"E-mail address: %@", person.email);
+ }
+
+ for (Person_PhoneNumber *phoneNumber in person.phonesArray) {
+ NSString *phonePrefix;
+
+ switch (phoneNumber.type) {
+ case Person_PhoneType_Mobile:
+ phonePrefix = @"Mobile phone";
+ break;
+ case Person_PhoneType_Home:
+ phonePrefix = @"Home phone";
+ break;
+ case Person_PhoneType_Work:
+ phonePrefix = @"Work phone";
+ break;
+ default:
+ phonePrefix = @"Unknown phone";
+ break;
+ }
+
+ NSLog(@" %@ #: %@", phonePrefix, phoneNumber.number);
+ }
+ printf("\n");
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 3)
+ return printUsage(argv[0]);
+
+ @autoreleasepool
+ {
+ AddressBook *addressBook;
+ NSString *filePath = [[NSString alloc] initWithUTF8String:argv[2]];
+
+ // Read the existing address book.
+ NSData *data = [NSData dataWithContentsOfFile:filePath];
+ if (!data) {
+ NSLog(@"%@ : File not found.", filePath);
+ addressBook = [[AddressBook alloc] init];
+ } else {
+ NSError *error;
+ addressBook = [AddressBook parseFromData:data error:&error];
+ if (!addressBook) {
+ NSLog(@"Failed to parse address book.");
+ return -1;
+ }
+ }
+
+ if (strcmp(argv[1], "add") == 0) {
+ // Add an address.
+ Person *person = [[Person alloc] init];
+ promptForAddress(person);
+ [addressBook.peopleArray addObject:person];
+
+ if (!data) {
+ NSLog(@"Creating a new file.");
+ }
+ [[addressBook data] writeToFile:filePath atomically:YES];
+ } else if (strcmp(argv[1], "list") == 0) {
+ listPeople(addressBook);
+ } else {
+ return printUsage(argv[0]);
+ }
+
+ return 0;
+ }
+}
diff --git a/examples/protobuf/cpp/addressbook.qbs b/examples/protobuf/cpp/addressbook.qbs
deleted file mode 100644
index 35f8ee7fb..000000000
--- a/examples/protobuf/cpp/addressbook.qbs
+++ /dev/null
@@ -1,19 +0,0 @@
-import qbs
-
-CppApplication {
- name: "addressbook_cpp"
- consoleApplication: true
- condition: protobuf.present
-
- Depends { name: "cpp" }
- cpp.cxxLanguageVersion: "c++11"
- cpp.warningLevel: "none"
-
- Depends { id: protobuf; name: "protobuf.cpp"; required: false }
-
- files: [
- "../shared/addressbook.proto",
- "main.cpp",
- "README.md",
- ]
-}
diff --git a/examples/protobuf/cpp/main.cpp b/examples/protobuf/cpp/main.cpp
deleted file mode 100644
index 8d2ca9763..000000000
--- a/examples/protobuf/cpp/main.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// 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 Google Inc. 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.
-
-#include <ctime>
-#include <fstream>
-#include <google/protobuf/util/time_util.h>
-#include <iostream>
-#include <string>
-
-#include "addressbook.pb.h"
-
-using google::protobuf::util::TimeUtil;
-
-int printUsage(char *argv0)
-{
- std::cerr << "Usage: " << argv0 << "add|list ADDRESS_BOOK_FILE" << std::endl;
- return -1;
-}
-
-std::string readString(const std::string &promt)
-{
- std::string result;
- std::cout << promt;
- std::getline(std::cin, result);
- return result;
-}
-
-// This function fills in a Person message based on user input.
-void promptForAddress(tutorial::Person* person)
-{
- std::cout << "Enter person ID number: ";
- int id;
- std::cin >> id;
- person->set_id(id);
- std::cin.ignore(256, '\n');
-
- *person->mutable_name() = readString("Enter name: ");
-
- const auto email = readString("Enter email address (blank for none): ");
- if (!email.empty())
- person->set_email(email);
-
- while (true) {
- const auto number = readString("Enter a phone number (or leave blank to finish): ");
- if (number.empty())
- break;
-
- tutorial::Person::PhoneNumber *phone_number = person->add_phones();
- phone_number->set_number(number);
-
- const auto type = readString("Is this a mobile, home, or work phone? ");
- if (type == "mobile")
- phone_number->set_type(tutorial::Person::MOBILE);
- else if (type == "home")
- phone_number->set_type(tutorial::Person::HOME);
- else if (type == "work")
- phone_number->set_type(tutorial::Person::WORK);
- else
- std::cout << "Unknown phone type. Using default." << std::endl;
- }
- *person->mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(NULL));
-}
-
-// Iterates though all people in the AddressBook and prints info about them.
-void listPeople(const tutorial::AddressBook& address_book)
-{
- for (int i = 0; i < address_book.people_size(); i++) {
- const tutorial::Person& person = address_book.people(i);
-
- std::cout << "Person ID: " << person.id() << std::endl;
- std::cout << " Name: " << person.name() << std::endl;
- if (!person.email().empty()) {
- std::cout << " E-mail address: " << person.email() << std::endl;
- }
-
- for (int j = 0; j < person.phones_size(); j++) {
- const tutorial::Person::PhoneNumber& phone_number = person.phones(j);
-
- switch (phone_number.type()) {
- case tutorial::Person::MOBILE:
- std::cout << " Mobile phone #: ";
- break;
- case tutorial::Person::HOME:
- std::cout << " Home phone #: ";
- break;
- case tutorial::Person::WORK:
- std::cout << " Work phone #: ";
- break;
- default:
- std::cout << " Unknown phone #: ";
- break;
- }
- std::cout << phone_number.number() << std::endl;
- }
- if (person.has_last_updated()) {
- std::cout << " Updated: " << TimeUtil::ToString(person.last_updated()) << std::endl;
- }
- }
-}
-
-int main(int argc, char* argv[]) {
- // Verify that the version of the library that we linked against is
- // compatible with the version of the headers we compiled against.
- GOOGLE_PROTOBUF_VERIFY_VERSION;
-
- if (argc != 3)
- return printUsage(argv[0]);
-
- tutorial::AddressBook address_book;
-
- // Read the existing address book.
- std::fstream input(argv[2], std::ios::in | std::ios::binary);
- if (!input) {
- std::cout << argv[2] << ": File not found." << std::endl;
- } else if (!address_book.ParseFromIstream(&input)) {
- std::cerr << "Failed to parse address book." << std::endl;
- return -1;
- }
-
- const std::string mode(argv[1]);
- if (mode == "add") {
- // Add an address.
- promptForAddress(address_book.add_people());
-
- if (!input)
- std::cout << "Creating a new file." << std::endl;
-
- // Write the new address book back to disk.
- std::fstream output(argv[2], std::ios::out | std::ios::trunc | std::ios::binary);
- if (!address_book.SerializeToOstream(&output)) {
- std::cerr << "Failed to write address book." << std::endl;
- return -1;
- }
- } else if (mode == "list") {
- listPeople(address_book);
- } else {
- return printUsage(argv[0]);
- }
-
- // Optional: Delete all global objects allocated by libprotobuf.
- google::protobuf::ShutdownProtobufLibrary();
-
- return 0;
-}
diff --git a/examples/protobuf/objc/addressbook.qbs b/examples/protobuf/objc/addressbook.qbs
deleted file mode 100644
index f087113d9..000000000
--- a/examples/protobuf/objc/addressbook.qbs
+++ /dev/null
@@ -1,15 +0,0 @@
-import qbs
-
-CppApplication {
- name: "addressbook_objc"
- consoleApplication: true
- condition: protobuf.present && qbs.targetOS.contains("darwin")
-
- Depends { name: "cpp" }
- Depends { id: protobuf; name: "protobuf.objc"; required: false }
-
- files: [
- "../shared/addressbook.proto",
- "main.m",
- ]
-}
diff --git a/examples/protobuf/objc/main.m b/examples/protobuf/objc/main.m
deleted file mode 100644
index 903799fca..000000000
--- a/examples/protobuf/objc/main.m
+++ /dev/null
@@ -1,171 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 Ivan Komissarov
-** Contact: abbapoh@gmail.com
-**
-** 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 "Addressbook.pbobjc.h"
-
-#import <Foundation/Foundation.h>
-
-int printUsage(char *argv0)
-{
- NSString *programName = [[NSString alloc] initWithUTF8String:argv0];
- NSLog(@"%@", [[NSString alloc] initWithFormat:@"Usage: %@ add|list ADDRESS_BOOK_FILE", programName]);
- [programName release];
- return -1;
-}
-
-NSString *readString(NSString *promt)
-{
- NSLog(@"%@", promt);
- NSFileHandle *inputFile = [NSFileHandle fileHandleWithStandardInput];
- NSData *inputData = [inputFile availableData];
- NSString *result = [[[NSString alloc]initWithData:inputData encoding:NSUTF8StringEncoding] autorelease];
- result = [[result stringByTrimmingCharactersInSet:
- [NSCharacterSet whitespaceAndNewlineCharacterSet]] autorelease];
- return result;
-}
-
-// This function fills in a Person message based on user input.
-void promptForAddress(Person* person)
-{
- person.id_p = [readString(@"Enter person ID number:") intValue];
- person.name = readString(@"Enter name:");
-
- NSString *email = readString(@"Enter email address (blank for none):");
- if ([email length] != 0)
- person.email = email;
-
- while (true) {
- NSString *number = readString(@"Enter a phone number (or leave blank to finish):");
- if ([number length] == 0)
- break;
-
- Person_PhoneNumber* phoneNumber = [[Person_PhoneNumber alloc] init];
- phoneNumber.number = number;
-
- NSString *type = readString(@"Is this a mobile, home, or work phone?:");
- NSLog(@"\"%@\"", type);
- if ([type compare:@"mobile"] == NSOrderedSame)
- phoneNumber.type = Person_PhoneType_Mobile;
- else if ([type compare:@"home"] == NSOrderedSame)
- phoneNumber.type = Person_PhoneType_Home;
- else if ([type compare:@"work"] == NSOrderedSame)
- phoneNumber.type = Person_PhoneType_Work;
- else
- NSLog(@"%@", @"Unknown phone type. Using default.");
-
- [person.phonesArray addObject:phoneNumber];
- }
-}
-
-// Iterates though all people in the AddressBook and prints info about them.
-void listPeople(AddressBook *addressBook)
-{
- NSArray *people = addressBook.peopleArray;
- for (unsigned i = 0; i < [people count]; i++) {
- Person *person = [people objectAtIndex:i];
-
- NSLog(@"%@", [[[NSString alloc] initWithFormat:@"Person ID: %d", person.id_p] autorelease]);
- NSLog(@"%@", [[[NSString alloc] initWithFormat:@"Person name: %@", person.name] autorelease]);
-
- if ([person.email length] != 0) {
- NSLog(@"%@", [[[NSString alloc] initWithFormat:@"E-mail address: %@", person.email] autorelease]);
- }
-
- NSArray *phones = person.phonesArray;
- for (unsigned j = 0; j < [phones count]; j++) {
- Person_PhoneNumber *phoneNumber = [phones objectAtIndex:j];
- NSString *phonePrefix;
-
- switch (phoneNumber.type) {
- case Person_PhoneType_Mobile:
- phonePrefix = @"Mobile phone";
- break;
- case Person_PhoneType_Home:
- phonePrefix = @"Home phone";
- break;
- case Person_PhoneType_Work:
- phonePrefix = @"Work phone";
- break;
- default:
- phonePrefix = @"Unknown phone";
- break;
- }
-
- NSLog(@"%@", [[[NSString alloc] initWithFormat:@" %@ #: %@", phonePrefix, phoneNumber.number] autorelease]);
- }
- printf("\n");
- }
-}
-
-int main(int argc, char *argv[])
-{
- if (argc != 3)
- return printUsage(argv[0]);
-
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
- AddressBook *addressBook;// = [AddressBook alloc];
- NSString *filePath = [[[NSString alloc] initWithUTF8String:argv[2]] autorelease];
-
- // Read the existing address book.
- NSData *data = [NSData dataWithContentsOfFile:filePath];
- if (!data) {
- NSLog(@"%@", [[NSString alloc] initWithFormat:@"%@ : File not found.", filePath]);
- addressBook = [[[AddressBook alloc] init] autorelease];
- } else {
- NSError *error;
- addressBook = [AddressBook parseFromData:data error:&error];
- if (!addressBook) {
- NSLog(@"%@", @"Failed to parse address book.");
- [pool drain];
- return -1;
- }
- }
-
- if (strcmp(argv[1], "add") == 0) {
- // Add an address.
- Person *person = [[Person alloc] init];
- promptForAddress(person);
- [addressBook.peopleArray addObject:person];
-
- if (!data) {
- NSLog(@"%@", @"Creating a new file.");
- }
- [[addressBook data] writeToFile:filePath atomically:YES];
- } else if (strcmp(argv[1], "list") == 0) {
- listPeople(addressBook);
- } else {
- [pool drain];
- return printUsage(argv[0]);
- }
-
- [pool drain];
- return 0;
-}
diff --git a/examples/rpaths/main.cpp b/examples/rpaths/main.cpp
new file mode 100644
index 000000000..b1b9a6d2b
--- /dev/null
+++ b/examples/rpaths/main.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "objecta.h"
+#include "objectb.h"
+
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+ (void)argc;
+ (void)argv;
+
+ std::cout << ObjectA::className() << std::endl;
+ std::cout << ObjectB::className() << std::endl;
+
+ ObjectB b;
+ std::cout << b.getA().name() << std::endl;
+
+ return 0;
+}
diff --git a/examples/rpaths/objecta.cpp b/examples/rpaths/objecta.cpp
new file mode 100644
index 000000000..7fbce6d70
--- /dev/null
+++ b/examples/rpaths/objecta.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "objecta.h"
+
+ObjectA::ObjectA(std::string name) :
+ m_name(std::move(name))
+{
+}
+
+std::string ObjectA::className()
+{
+ return "ObjectA";
+}
+
+const std::string &ObjectA::name() const
+{
+ return m_name;
+}
+
+void ObjectA::setName(std::string name)
+{
+ m_name = std::move(name);
+}
diff --git a/examples/rpaths/objecta.h b/examples/rpaths/objecta.h
new file mode 100644
index 000000000..e2bf145e5
--- /dev/null
+++ b/examples/rpaths/objecta.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 OBJECTA_H
+#define OBJECTA_H
+
+#include <string>
+
+class ObjectA
+{
+public:
+ explicit ObjectA(std::string name);
+
+ static std::string className();
+
+ const std::string &name() const;
+ void setName(std::string name);
+
+private:
+ std::string m_name;
+};
+
+#endif // OBJECTA_H
diff --git a/examples/rpaths/objectb.cpp b/examples/rpaths/objectb.cpp
new file mode 100644
index 000000000..228a68b0e
--- /dev/null
+++ b/examples/rpaths/objectb.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 "objectb.h"
+
+ObjectB::ObjectB() = default;
+
+std::string ObjectB::className()
+{
+ return "ObjectB";
+}
+
+const ObjectA &ObjectB::getA() const
+{
+ return objectA;
+}
+
diff --git a/examples/rpaths/objectb.h b/examples/rpaths/objectb.h
new file mode 100644
index 000000000..7c0602a1b
--- /dev/null
+++ b/examples/rpaths/objectb.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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 OBJECTB_H
+#define OBJECTB_H
+
+#include <string>
+
+#include "objecta.h"
+
+class ObjectB
+{
+public:
+ ObjectB();
+
+ static std::string className();
+
+ const ObjectA &getA() const;
+
+private:
+ ObjectA objectA{"A"};
+};
+
+#endif // OBJECTB_H
diff --git a/examples/rpaths/rpaths.qbs b/examples/rpaths/rpaths.qbs
new file mode 100644
index 000000000..59d48174a
--- /dev/null
+++ b/examples/rpaths/rpaths.qbs
@@ -0,0 +1,61 @@
+import qbs.FileInfo
+
+Project {
+ condition: qbs.targetOS.contains("unix")
+
+ //! [0]
+ DynamicLibrary {
+ Depends { name: "cpp" }
+ Depends { name: "bundle" }
+ name: "LibraryA"
+ bundle.isBundle: false
+ cpp.sonamePrefix: qbs.targetOS.contains("macos") ? "@rpath" : undefined
+ cpp.rpaths: cpp.rpathOrigin
+ cpp.cxxLanguageVersion: "c++11"
+ cpp.minimumMacosVersion: "10.8"
+ files: [
+ "objecta.cpp",
+ "objecta.h",
+ ]
+ install: true
+ installDir: "examples/lib"
+ }
+ //! [0]
+
+ //! [1]
+ DynamicLibrary {
+ Depends { name: "cpp" }
+ Depends { name: "bundle" }
+ Depends { name: "LibraryA" }
+ name: "LibraryB"
+ bundle.isBundle: false
+ cpp.cxxLanguageVersion: "c++11"
+ cpp.minimumMacosVersion: "10.8"
+ cpp.sonamePrefix: qbs.targetOS.contains("macos") ? "@rpath" : undefined
+ cpp.rpaths: cpp.rpathOrigin
+ files: [
+ "objectb.cpp",
+ "objectb.h",
+ ]
+ install: true
+ installDir: "examples/lib"
+ }
+ //! [1]
+
+ //! [2]
+ CppApplication {
+ Depends { name: "bundle" }
+ Depends { name: "LibraryA" }
+ Depends { name: "LibraryB" }
+ name: "rpaths-app"
+ files: "main.cpp"
+ consoleApplication: true
+ bundle.isBundle: false
+ cpp.rpaths: FileInfo.joinPaths(cpp.rpathOrigin, "..", "lib")
+ cpp.cxxLanguageVersion: "c++11"
+ cpp.minimumMacosVersion: "10.8"
+ install: true
+ installDir: "examples/bin"
+ }
+ //! [2]
+}
diff --git a/examples/rule/rule.qbs b/examples/rule/rule.qbs
index 8ec14ee40..f462a3708 100644
--- a/examples/rule/rule.qbs
+++ b/examples/rule/rule.qbs
@@ -19,7 +19,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = input.fileName + "->" + output.fileName;
+ cmd.description = "generating" + output.fileName + " from " + input.fileName;
cmd.highlight = "codegen";
cmd.sourceCode = function() {
var file = new TextFile(input.filePath);
diff --git a/qbs-resources/imports/QbsApp.qbs b/qbs-resources/imports/QbsApp.qbs
index caec90f85..5aad28650 100644
--- a/qbs-resources/imports/QbsApp.qbs
+++ b/qbs-resources/imports/QbsApp.qbs
@@ -1,32 +1,18 @@
-import qbs
import qbs.FileInfo
QbsProduct {
Depends { name: "qbscore" }
- Depends { name: "cpp" }
Depends { name: "qbsversion" }
+ Depends { name: "qbsconsolelogger" }
type: ["application", "qbsapplication"]
version: qbsversion.version
consoleApplication: true
- cpp.includePaths: [
- "../shared", // for the logger
- ]
Group {
fileTagsFilter: product.type
- .concat(qbs.buildVariant === "debug" ? ["debuginfo_app"] : [])
+ .concat(qbs.debugInformation ? ["debuginfo_app"] : [])
qbs.install: true
qbs.installDir: targetInstallDir
qbs.installSourceBase: buildDirectory
}
targetInstallDir: 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
index 5d4143a41..8624f65e7 100644
--- a/qbs-resources/imports/QbsAutotest.qbs
+++ b/qbs-resources/imports/QbsAutotest.qbs
@@ -1,8 +1,7 @@
-import qbs
import qbs.FileInfo
import qbs.Utilities
-QtApplication {
+QbsProduct {
type: ["application", "autotest"]
consoleApplication: true
property string testName
@@ -11,25 +10,12 @@ QtApplication {
Depends { name: "Qt.testlib" }
Depends { name: "qbscore" }
Depends { name: "qbsbuildconfig" }
- cpp.defines: [
+ cpp.defines: [ // deliberately override base defines
"QBS_TEST_SUITE_NAME=" + Utilities.cStringQuote(testName.toUpperCase().replace("-", "_"))
]
cpp.includePaths: [
"../../../src",
- "../../../src/app/shared", // for the logger
]
- cpp.cxxLanguageVersion: "c++14"
- Group {
- name: "logging"
- prefix: FileInfo.joinPaths(product.sourceDirectory, "../../../src/app/shared/logging") + '/'
- files: [
- "coloredoutput.cpp",
- "coloredoutput.h",
- "consolelogger.cpp",
- "consolelogger.h"
- ]
- }
- cpp.rpaths: qbsbuildconfig.libRPaths
qbs.commonRunEnvironment: ({
"QBS_INSTALL_DIR": FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix)
diff --git a/qbs-resources/imports/QbsLibrary.qbs b/qbs-resources/imports/QbsLibrary.qbs
index bf39cb8d6..f0769dd6c 100644
--- a/qbs-resources/imports/QbsLibrary.qbs
+++ b/qbs-resources/imports/QbsLibrary.qbs
@@ -1,33 +1,34 @@
-import qbs
import qbs.FileInfo
import qbs.Utilities
QbsProduct {
Depends { name: "cpp" }
+ Depends { name: "Exporter.pkgconfig"; condition: generatePkgConfigFile }
+ Depends { name: "Exporter.qbs"; condition: generateQbsModule }
+ Depends { name: "cpp" }
+
+ property string visibilityType: staticBuild ? "static" : "dynamic"
+ property string headerInstallPrefix: "/include/qbs"
+ property bool hasExporter: Utilities.versionCompare(qbs.version, "1.12") >= 0
+ property bool generatePkgConfigFile: qbsbuildconfig.generatePkgConfigFiles && hasExporter
+ property bool generateQbsModule: install && qbsbuildconfig.generateQbsModules && hasExporter
+ property bool staticBuild: Qt.core.staticBuild || qbsbuildconfig.staticBuild
+ property stringList libType: [staticBuild ? "staticlibrary" : "dynamiclibrary"]
+
version: qbsversion.version
type: libType
targetName: (qbs.enableDebugCode && qbs.targetOS.contains("windows")) ? (name + 'd') : name
+ cpp.visibility: "minimal"
cpp.defines: base.concat(visibilityType === "static" ? ["QBS_STATIC_LIB"] : ["QBS_LIBRARY"])
cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
Properties {
condition: qbs.toolchain.contains("gcc")
cpp.soVersion: version.replace(/\.\d+$/, '')
}
- cpp.visibility: "minimal"
- property string visibilityType: staticBuild ? "static" : "dynamic"
- property string headerInstallPrefix: "/include/qbs"
- property bool hasExporter: Utilities.versionCompare(qbs.version, "1.12") >= 0
- property bool generatePkgConfigFile: qbsbuildconfig.generatePkgConfigFiles && hasExporter
- property bool generateQbsModule: install && qbsbuildconfig.generateQbsModules && hasExporter
- property bool staticBuild: Qt.core.staticBuild || qbsbuildconfig.staticBuild
- property stringList libType: [staticBuild ? "staticlibrary" : "dynamiclibrary"]
-
- Depends { name: "Exporter.pkgconfig"; condition: generatePkgConfigFile }
- Depends { name: "Exporter.qbs"; condition: generateQbsModule }
Group {
fileTagsFilter: libType.concat("dynamiclibrary_symlink")
- .concat(qbs.buildVariant === "debug" ? ["debuginfo_dll"] : [])
+ .concat(qbs.debugInformation ? ["debuginfo_dll"] : [])
qbs.install: install
qbs.installDir: targetInstallDir
qbs.installSourceBase: buildDirectory
@@ -60,15 +61,15 @@ QbsProduct {
Depends { name: "Qt"; submodules: ["core"] }
Properties {
- condition: product.hasExporter
+ condition: exportingProduct.hasExporter
prefixMapping: [{
- prefix: product.sourceDirectory,
- replacement: FileInfo.joinPaths(product.qbs.installPrefix,
- product.headerInstallPrefix)
+ prefix: exportingProduct.sourceDirectory,
+ replacement: FileInfo.joinPaths(exportingProduct.qbs.installPrefix,
+ exportingProduct.headerInstallPrefix)
}]
}
- cpp.includePaths: [product.sourceDirectory]
- cpp.defines: product.visibilityType === "static" ? ["QBS_STATIC_LIB"] : []
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ cpp.defines: exportingProduct.visibilityType === "static" ? ["QBS_STATIC_LIB"] : []
}
}
diff --git a/qbs-resources/imports/QbsProduct.qbs b/qbs-resources/imports/QbsProduct.qbs
index b3a166e42..ea8516736 100644
--- a/qbs-resources/imports/QbsProduct.qbs
+++ b/qbs-resources/imports/QbsProduct.qbs
@@ -1,21 +1,23 @@
-import qbs
-
Product {
Depends { name: "qbsbuildconfig" }
Depends { name: "qbsversion" }
Depends { name: "Qt.core"; versionAtLeast: minimumQtVersion }
- property string minimumQtVersion: "5.11.0"
+ property string minimumQtVersion: "5.15.2"
property bool install: true
property string targetInstallDir
cpp.defines: {
- var res = ["QT_NO_CAST_FROM_ASCII", "QT_NO_PROCESS_COMBINED_ARGUMENT_START"];
+ var res = [
+ "QT_NO_CAST_FROM_ASCII",
+ "QT_NO_CAST_FROM_BYTEARRAY",
+ "QT_NO_PROCESS_COMBINED_ARGUMENT_START"
+ ];
if (qbs.toolchain.contains("msvc"))
res.push("_SCL_SECURE_NO_WARNINGS");
if (qbs.enableDebugCode)
res.push("QT_STRICT_ITERATORS");
return res;
}
- cpp.cxxLanguageVersion: "c++14"
+ cpp.cxxLanguageVersion: "c++17"
cpp.enableExceptions: true
cpp.rpaths: qbsbuildconfig.libRPaths
}
diff --git a/qbs-resources/imports/QbsStaticLibrary.qbs b/qbs-resources/imports/QbsStaticLibrary.qbs
new file mode 100644
index 000000000..6fa9fde31
--- /dev/null
+++ b/qbs-resources/imports/QbsStaticLibrary.qbs
@@ -0,0 +1,9 @@
+QbsProduct {
+ type: "staticlibrary"
+ Export {
+ Depends { name: "cpp" }
+ Depends { name: "Qt"; submodules: ["core"] }
+
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ }
+}
diff --git a/qbs-resources/imports/QbsUnittest.qbs b/qbs-resources/imports/QbsUnittest.qbs
new file mode 100644
index 000000000..c956d5480
--- /dev/null
+++ b/qbs-resources/imports/QbsUnittest.qbs
@@ -0,0 +1,10 @@
+import qbs.FileInfo
+import qbs.Utilities
+
+QbsAutotest {
+ Depends {
+ name: "Qt.core5compat";
+ condition: Utilities.versionCompare(Qt.core.version, "6.0.0") >= 0
+ }
+ Depends { name: "quickjs"; cpp.link: false }
+}
diff --git a/qbs-resources/modules/docker/docker.qbs b/qbs-resources/modules/docker/docker.qbs
deleted file mode 100644
index 1460cff3c..000000000
--- a/qbs-resources/modules/docker/docker.qbs
+++ /dev/null
@@ -1,50 +0,0 @@
-import qbs
-import qbs.FileInfo
-import qbs.Probes
-import qbs.Utilities
-
-Module {
- Probes.BinaryProbe {
- id: dockercli
- names: ["docker"]
- }
-
- property string dockerFilePath: dockercli.filePath
- property string imageTag
- property stringList buildFlags
-
- FileTagger {
- patterns: ["Dockerfile"]
- fileTags: ["docker.dockerfile"]
- }
-
- Rule {
- inputs: ["docker.dockerfile"]
-
- Artifact {
- // Let Docker handle the dependency management
- filePath: FileInfo.joinPaths(product.buildDirectory,
- Utilities.getHash(input.filePath), ".docker-image-dummy")
- fileTags: ["docker.docker-image"]
- }
-
- prepare: {
- var args = ["build"];
- var tag = product.docker.imageTag;
- if (tag)
- args.push("-t", tag);
- Array.prototype.push.apply(args, product.docker.buildFlags);
- args.push(".");
- var cmd = new Command(product.docker.dockerFilePath, args);
- cmd.workingDirectory = FileInfo.path(input.filePath);
- cmd.description = "building docker image "
- + FileInfo.fileName(cmd.workingDirectory) + (tag ? " (" + tag + ")" : "");
- return [cmd];
- }
- }
-
- validate: {
- if (!dockerFilePath)
- throw ModUtils.ModuleError("Could not find Docker.");
- }
-}
diff --git a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
index 10ba78045..4c22b5eb7 100644
--- a/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
+++ b/qbs-resources/modules/qbsbuildconfig/qbsbuildconfig.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.FileInfo
import qbs.Utilities
@@ -11,11 +10,10 @@ Module {
property bool enableUbSanitizer: false
property bool enableThreadSanitizer: false
property bool enableUnitTests: false
- property bool enableProjectFileUpdates: false
+ property bool enableProjectFileUpdates: true
property bool enableRPath: true
property bool installApiHeaders: true
property bool enableBundledQt: false
- property bool useBundledQtScript: false
property bool staticBuild: false
property string libDirName: "lib"
property string appInstallDir: "bin"
@@ -48,16 +46,23 @@ Module {
property string pluginsInstallDir: libDirName + "/qbs/plugins"
property string qmlTypeDescriptionsInstallDir: FileInfo.joinPaths(resourcesInstallDir,
"share/qbs/qml-type-descriptions")
+ property bool dumpJsLeaks: qbs.buildVariant === "debug"
Properties {
condition: project.withCode && qbs.toolchain.contains("gcc")
cpp.cxxFlags: {
- var flags = [];
+ var flags = ["-Wno-missing-field-initializers"];
if (enableAddressSanitizer)
flags.push("-fno-omit-frame-pointer");
- if (!qbs.toolchain.contains("clang")
- && Utilities.versionCompare(cpp.compilerVersion, "9") >= 0) {
- flags.push("-Wno-deprecated-copy", "-Wno-init-list-lifetime");
+ function isClang() { return qbs.toolchain.contains("clang"); }
+ function versionAtLeast(v) {
+ return Utilities.versionCompare(cpp.compilerVersion, v) >= 0;
+ };
+ if (isClang())
+ flags.push("-Wno-constant-logical-operand");
+ if ((!isClang() && versionAtLeast("9"))
+ || (isClang() && !qbs.hostOS.contains("darwin") && versionAtLeast("10"))) {
+ flags.push("-Wno-deprecated-copy");
}
return flags;
}
@@ -65,11 +70,20 @@ Module {
var flags = [];
if (enableAddressSanitizer)
flags.push("-fsanitize=address");
- if (enableUbSanitizer)
+ if (enableUbSanitizer) {
flags.push("-fsanitize=undefined");
+ flags.push("-fno-sanitize=vptr");
+ }
if (enableThreadSanitizer)
flags.push("-fsanitize=thread");
return flags;
}
}
+ Properties {
+ condition: project.withCode && qbs.toolchain.contains("msvc") && product.Qt
+ && Utilities.versionCompare(product.Qt.core.version, "6.3") >= 0
+ && Utilities.versionCompare(cpp.compilerVersion, "19.10") >= 0
+ && Utilities.versionCompare(qbs.version, "1.23") < 0
+ cpp.cxxFlags: "/permissive-"
+ }
}
diff --git a/qbs-resources/modules/qbsversion/qbsversion.qbs b/qbs-resources/modules/qbsversion/qbsversion.qbs
index ff1fd18f0..2e9a2ae42 100644
--- a/qbs-resources/modules/qbsversion/qbsversion.qbs
+++ b/qbs-resources/modules/qbsversion/qbsversion.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.File
import qbs.FileInfo
import qbs.TextFile
diff --git a/qbs.pro b/qbs.pro
deleted file mode 100644
index b8faa2ab0..000000000
--- a/qbs.pro
+++ /dev/null
@@ -1,71 +0,0 @@
-requires(!cross_compile)
-include(src/lib/bundledlibs.pri)
-
-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, 11, 0) {
- message("Cannot build qbs with Qt version $${QT_VERSION}.")
- error("Use at least Qt 5.11.0.")
-}
-
-TEMPLATE = subdirs
-corelib.file = src/lib/corelib/corelib.pro
-src_app.subdir = src/app
-src_app.depends = corelib
-src_libexec.subdir = src/libexec
-src_plugins.subdir = src/plugins
-CONFIG(shared, static|shared) {
- src_plugins.depends = corelib
- src_app.depends += src_plugins
-}
-tests.depends = static_res
-static_res.file = static-res.pro
-static_res.depends = src_app src_libexec src_plugins static.pro
-qbs_use_bundled_qtscript {
- scriptenginelib.file = src/lib/scriptengine/scriptengine.pro
- corelib.depends = scriptenginelib
- SUBDIRS += scriptenginelib
-}
-SUBDIRS += \
- corelib\
- src_app\
- src_libexec\
- src_plugins\
- static.pro\
- static_res\
- tests
-
-OTHER_FILES += \
- doc/*.qdoc \
- doc/reference/*.qdoc \
- doc/reference/items/convenience/*.qdoc \
- doc/reference/items/language/*.qdoc \
- doc/reference/jsextensions/*.qdoc \
- doc/reference/modules/*.qdoc \
- doc/targets/*.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
index f36bd0ee4..4b2a184db 100644
--- a/qbs.qbs
+++ b/qbs.qbs
@@ -1,29 +1,21 @@
-import qbs 1.0
-
Project {
minimumQbsVersion: "1.6"
qbsSearchPaths: ["qbs-resources"]
property bool withCode: true
- property bool withDocker: true
property bool withDocumentation: true
- property bool withExamples: false
property bool withTests: withCode
property stringList autotestArguments: []
property stringList autotestWrapper: []
references: [
+ "docker/docker.qbs",
+ "examples/examples.qbs",
"share/share.qbs",
"scripts/scripts.qbs",
+ "tutorial/tutorial.qbs",
]
SubProject {
- filePath: "docker/docker.qbs"
- Properties {
- condition: parent.withDocker
- }
- }
-
- SubProject {
filePath: "doc/doc.qbs"
Properties {
condition: parent.withDocumentation
@@ -31,13 +23,6 @@ Project {
}
SubProject {
- filePath: "examples/examples.qbs"
- Properties {
- condition: parent.withExamples
- }
- }
-
- SubProject {
filePath: "src/src.qbs"
Properties {
condition: parent.withCode
@@ -57,7 +42,16 @@ Project {
}
Product {
- name: "qmake project files for qbs"
- files: ["**/*.pr[io]"]
+ name: "cmake project files for qbs"
+ files: ["**/CMakeLists.txt"]
+ }
+
+ Product {
+ name: "continuous integration files"
+ files: [
+ ".clang-tidy",
+ "docker-compose.yml",
+ ".github/**/*.yml",
+ ]
}
}
diff --git a/qbs_version.pri b/qbs_version.pri
deleted file mode 100644
index 97c4c799e..000000000
--- a/qbs_version.pri
+++ /dev/null
@@ -1,4 +0,0 @@
-QBS_VERSION = $$cat($$PWD/VERSION)
-QBS_VERSION_MAJ = $$section(QBS_VERSION, ., 0, 0)
-QBS_VERSION_MIN = $$section(QBS_VERSION, ., 1, 1)
-DEFINES += QBS_VERSION=\\\"$$QBS_VERSION\\\"
diff --git a/scripts/address-sanitizer-suppressions.txt b/scripts/address-sanitizer-suppressions.txt
index 9381cc58e..c4731b213 100644
--- a/scripts/address-sanitizer-suppressions.txt
+++ b/scripts/address-sanitizer-suppressions.txt
@@ -1 +1,2 @@
-leak:libQt5Script.so.5
+leak:QThreadPrivate::QThreadPrivate
+leak:QArrayData::allocate
diff --git a/scripts/build-qbs-doc.sh b/scripts/build-qbs-doc.sh
new file mode 100755
index 000000000..82a84269c
--- /dev/null
+++ b/scripts/build-qbs-doc.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+#############################################################################
+##
+## Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com).
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+set -e
+
+if [ -z "${QBS_BUILD_PROFILE}" ]; then
+ QBS_BUILD_PROFILE=$(qbs config defaultProfile | cut -d: -f2 | tr -d '[:space:]')
+fi
+if [ -z "${QBS_BUILD_PROFILE}" ]; then
+ echo "Either QBS_BUILD_PROFILE or a defaultProfile must be set."
+ exit 1
+fi
+
+#
+# Additional build options
+#
+BUILD_OPTIONS="\
+ profile:${QBS_BUILD_PROFILE} \
+ ${BUILD_OPTIONS} \
+ config:documentation \
+"
+
+qbs build -p "qbs documentation" ${BUILD_OPTIONS}
diff --git a/scripts/build-qbs-with-cmake.sh b/scripts/build-qbs-with-cmake.sh
new file mode 100755
index 000000000..7f3fe3f5e
--- /dev/null
+++ b/scripts/build-qbs-with-cmake.sh
@@ -0,0 +1,105 @@
+#!/usr/bin/env bash
+#############################################################################
+##
+## Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com).
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+set -e
+
+#
+# It might be desired to keep settings for Qbs testing
+# in a separate folder.
+#
+export QBS_AUTOTEST_SETTINGS_DIR="${QBS_AUTOTEST_SETTINGS_DIR:-/tmp/qbs-settings}"
+
+BUILD_OPTIONS="\
+ -DWITH_UNIT_TESTS=1 \
+ -DQBS_INSTALL_HTML_DOCS=1 \
+ -DQBS_INSTALL_QCH_DOCS=1 \
+ -DCMAKE_C_COMPILER_LAUNCHER=ccache \
+ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
+ ${BUILD_OPTIONS} \
+"
+
+QMAKE_PATH="${QMAKE_PATH:-$(which qmake)}"
+QT_DIR=$(dirname ${QMAKE_PATH})/../
+
+# Use shadow build
+mkdir -p build
+pushd build
+
+#
+# Build all default products of Qbs
+#
+cmake -GNinja -DQt5_DIR=${QT_DIR}/lib/cmake/Qt5/ ${BUILD_OPTIONS} ..
+cmake --build .
+cmake --install . --prefix "install-root"
+
+#
+# Set up profiles for the freshly built Qbs if not
+# explicitly specified otherwise
+#
+if [ -z "${QBS_AUTOTEST_PROFILE}" ]; then
+
+ export QBS_AUTOTEST_PROFILE=autotestprofile
+ RUN_OPTIONS="\
+ --settings-dir ${QBS_AUTOTEST_SETTINGS_DIR} \
+ "
+
+ ./bin/qbs setup-toolchains \
+ ${RUN_OPTIONS} \
+ --detect
+
+ ./bin/qbs setup-qt \
+ ${RUN_OPTIONS} \
+ "${QMAKE_PATH}" ${QBS_AUTOTEST_PROFILE}
+
+ ./bin/qbs config \
+ ${RUN_OPTIONS} \
+ ${QBS_AUTOTEST_PROFILE}.baseProfile gcc
+
+fi
+
+#
+# Run all autotests with QBS_AUTOTEST_PROFILE. Some test cases might run for
+# over 10 minutes. Output an empty line every 9:50 minutes to prevent a 10min
+# timeout on Travis CI.
+#
+(while true; do echo "" && sleep 590; done) &
+trap "kill $!; wait $! 2>/dev/null || true; killall sleep || true" EXIT
+ctest -j $(nproc --all) --output-on-failure
+
+popd
diff --git a/scripts/build-qbs-with-qbs.sh b/scripts/build-qbs-with-qbs.sh
index f26d2222a..b26dcc7b5 100755
--- a/scripts/build-qbs-with-qbs.sh
+++ b/scripts/build-qbs-with-qbs.sh
@@ -45,16 +45,22 @@ set -e
#
export LSAN_OPTIONS="suppressions=$( cd "$(dirname "$0")" ; pwd -P )/address-sanitizer-suppressions.txt:print_suppressions=0"
+if [ -z "${QBS_BUILD_PROFILE}" ]; then
+ QBS_BUILD_PROFILE=$(qbs config defaultProfile | cut -d: -f2 | tr -d '[:space:]')
+fi
+if [ -z "${QBS_BUILD_PROFILE}" ]; then
+ echo "Either QBS_BUILD_PROFILE or a defaultProfile must be set."
+ exit 1
+fi
+
#
# Additional build options
#
BUILD_OPTIONS="\
- ${QBS_BUILD_PROFILE:+profile:${QBS_BUILD_PROFILE}} \
- modules.qbsbuildconfig.enableAddressSanitizer:true \
- modules.qbsbuildconfig.enableProjectFileUpdates:true \
+ profile:${QBS_BUILD_PROFILE} \
modules.qbsbuildconfig.enableUnitTests:true \
modules.cpp.treatWarningsAsErrors:true \
- project.withExamples:true \
+ modules.cpp.separateDebugInformation:true \
${BUILD_OPTIONS} \
config:release \
"
@@ -70,6 +76,18 @@ if [ "$WITH_DOCS" -ne 0 ]; then
qbs build -p "qbs documentation" ${BUILD_OPTIONS}
fi
+WITH_ARCHIVE=${WITH_ARCHIVE:-1}
+if [ "$WITH_ARCHIVE" -ne 0 ]; then
+ qbs build -p "qbs_archive" ${BUILD_OPTIONS}
+fi
+
+WITH_TESTS=${WITH_TESTS:-1}
+if [ "$WITH_TESTS" -eq 0 ]; then
+ exit 0
+fi
+
+QMAKE_PATH=${QMAKE_PATH:-$(which qmake)}
+
#
# Set up profiles for the freshly built Qbs if not
# explicitly specified otherwise
@@ -79,6 +97,8 @@ if [ -z "${QBS_AUTOTEST_PROFILE}" ]; then
export QBS_AUTOTEST_PROFILE=autotestprofile
export QBS_AUTOTEST_SETTINGS_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'qbs-settings'`
+ QBS_AUTOTEST_QMAKE_PATH=${QBS_AUTOTEST_QMAKE_PATH:-${QMAKE_PATH}}
+
RUN_OPTIONS="\
--settings-dir ${QBS_AUTOTEST_SETTINGS_DIR} \
"
@@ -89,21 +109,25 @@ if [ -z "${QBS_AUTOTEST_PROFILE}" ]; then
qbs run -p qbs_app ${BUILD_OPTIONS} -- setup-qt \
${RUN_OPTIONS} \
- "${QMAKE_PATH:-$(which qmake)}" ${QBS_AUTOTEST_PROFILE}
+ "${QBS_AUTOTEST_QMAKE_PATH}" ${QBS_AUTOTEST_PROFILE}
# Make sure that the Qt profile uses the same toolchain profile
# that was used for building in case a custom QBS_BUILD_PROFILE
# was set. Otherwise setup-qt automatically uses the default
# toolchain profile.
- if [ ! -z "${QBS_BUILD_PROFILE}" ]; then
- QBS_BUILD_BASE_PROFILE=$(qbs config profiles.${QBS_BUILD_PROFILE}.baseProfile | cut -d: -f2)
- if [ ! -z "${QBS_BUILD_BASE_PROFILE}" ]; then
- echo "Setting base profile for ${QBS_AUTOTEST_PROFILE} to ${QBS_BUILD_BASE_PROFILE}"
- qbs run -p qbs_app ${BUILD_OPTIONS} -- config \
- ${RUN_OPTIONS} \
- profiles.${QBS_AUTOTEST_PROFILE}.baseProfile ${QBS_BUILD_BASE_PROFILE}
- fi
+ if [ -z "${QBS_AUTOTEST_BASE_PROFILE}" ]; then
+ QBS_AUTOTEST_BASE_PROFILE=$(qbs config profiles.${QBS_BUILD_PROFILE}.baseProfile | cut -d: -f2)
fi
+ if [ ! -z "${QBS_AUTOTEST_BASE_PROFILE}" ]; then
+ echo "Setting base profile for ${QBS_AUTOTEST_PROFILE} to ${QBS_AUTOTEST_BASE_PROFILE}"
+ qbs run -p qbs_app ${BUILD_OPTIONS} -- config \
+ ${RUN_OPTIONS} \
+ profiles.${QBS_AUTOTEST_PROFILE}.baseProfile ${QBS_AUTOTEST_BASE_PROFILE}
+ fi
+
+ qbs run -p qbs_app ${BUILD_OPTIONS} -- config \
+ ${RUN_OPTIONS} \
+ --list
# QBS_AUTOTEST_PROFILE has been added to the environment
# which requires a resolve step
diff --git a/scripts/build-qbs-with-qmake.sh b/scripts/build-qbs-with-qmake.sh
deleted file mode 100755
index 1e97d7695..000000000
--- a/scripts/build-qbs-with-qmake.sh
+++ /dev/null
@@ -1,84 +0,0 @@
-#!/usr/bin/env bash
-#############################################################################
-##
-## Copyright (C) 2019 Richard Weickelt.
-## Contact: https://www.qt.io/licensing/
-##
-## This file is part of Qbs.
-##
-## $QT_BEGIN_LICENSE:LGPL$
-## Commercial License Usage
-## Licensees holding valid commercial Qt licenses may use this file in
-## accordance with the commercial license agreement provided with the
-## Software or, alternatively, in accordance with the terms contained in
-## a written agreement between you and The Qt Company. For licensing terms
-## and conditions see https://www.qt.io/terms-conditions. For further
-## information use the contact form at https://www.qt.io/contact-us.
-##
-## GNU Lesser General Public License Usage
-## Alternatively, this file may be used under the terms of the GNU Lesser
-## General Public License version 3 as published by the Free Software
-## Foundation and appearing in the file LICENSE.LGPL3 included in the
-## packaging of this file. Please review the following information to
-## ensure the GNU Lesser General Public License version 3 requirements
-## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-##
-## GNU General Public License Usage
-## Alternatively, this file may be used under the terms of the GNU
-## General Public License version 2.0 or (at your option) the GNU General
-## Public license version 3 or any later version approved by the KDE Free
-## Qt Foundation. The licenses are as published by the Free Software
-## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-## included in the packaging of this file. Please review the following
-## information to ensure the GNU General Public License requirements will
-## be met: https://www.gnu.org/licenses/gpl-2.0.html and
-## https://www.gnu.org/licenses/gpl-3.0.html.
-##
-## $QT_END_LICENSE$
-##
-#############################################################################
-set -e
-
-#
-# It might be desired to keep settings for Qbs testing
-# in a separate folder.
-#
-export QBS_AUTOTEST_SETTINGS_DIR="${QBS_AUTOTEST_SETTINGS_DIR:-/tmp/qbs-settings}"
-
-#
-# Build all default products of Qbs
-#
-qmake -r qbs.pro \
- CONFIG+=qbs_enable_unit_tests \
- CONFIG+=qbs_enable_project_file_updates \
- ${BUILD_OPTIONS}
-make -j $(nproc --all)
-make docs
-
-#
-# Set up profiles for the freshly built Qbs if not
-# explicitly specified otherwise
-#
-if [ -z "${QBS_AUTOTEST_PROFILE}" ]; then
-
- export QBS_AUTOTEST_PROFILE=autotestprofile
- RUN_OPTIONS="\
- --settings-dir ${QBS_AUTOTEST_SETTINGS_DIR} \
- "
-
- ./bin/qbs setup-toolchains \
- ${RUN_OPTIONS} \
- --detect
-
- ./bin/qbs setup-qt \
- ${RUN_OPTIONS} \
- "${QMAKE_PATH:-$(which qmake)}" ${QBS_AUTOTEST_PROFILE}
-
- ./bin/qbs config \
- ${RUN_OPTIONS} \
- ${QBS_AUTOTEST_PROFILE}.baseProfile gcc
-
-fi
-
-
-make check -j $(nproc --all)
diff --git a/scripts/cpu-count.sh b/scripts/cpu-count.sh
new file mode 100755
index 000000000..ce6b1c82f
--- /dev/null
+++ b/scripts/cpu-count.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+#############################################################################
+##
+## Copyright (C) 2020 Ivan Komissarov <abbapoh@gmail.com>
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+NPROC=`which nproc`
+SYSCTL=`which sysctl`
+CPU_COUNT=2
+if [ ! -z "$NPROC" ]; then # Linux
+ CPU_COUNT=`$NPROC --all`
+elif [ ! -z "$SYSCTL" ]; then # macOS
+ CPU_COUNT=`$SYSCTL -n hw.physicalcpu_max`
+fi
+
+echo $CPU_COUNT
diff --git a/scripts/git-clang-format b/scripts/git-clang-format
new file mode 100755
index 000000000..d33fd478d
--- /dev/null
+++ b/scripts/git-clang-format
@@ -0,0 +1,683 @@
+#!/usr/bin/env python3
+#
+#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===#
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+#===------------------------------------------------------------------------===#
+
+r"""
+clang-format git integration
+============================
+
+This file provides a clang-format integration for git. Put it somewhere in your
+path and ensure that it is executable. Then, "git clang-format" will invoke
+clang-format on the changes in current files or a specific commit.
+
+For further details, run:
+git clang-format -h
+
+Requires Python 2.7 or Python 3
+"""
+
+from __future__ import absolute_import, division, print_function
+import argparse
+import collections
+import contextlib
+import errno
+import os
+import re
+import subprocess
+import sys
+
+usage = ('git clang-format [OPTIONS] [<commit>] [<commit>|--staged] '
+ '[--] [<file>...]')
+
+desc = '''
+If zero or one commits are given, run clang-format on all lines that differ
+between the working directory and <commit>, which defaults to HEAD. Changes are
+only applied to the working directory, or in the stage/index.
+
+Examples:
+ To format staged changes, i.e everything that's been `git add`ed:
+ git clang-format
+
+ To also format everything touched in the most recent commit:
+ git clang-format HEAD~1
+
+ If you're on a branch off main, to format everything touched on your branch:
+ git clang-format main
+
+If two commits are given (requires --diff), run clang-format on all lines in the
+second <commit> that differ from the first <commit>.
+
+The following git-config settings set the default of the corresponding option:
+ clangFormat.binary
+ clangFormat.commit
+ clangFormat.extensions
+ clangFormat.style
+'''
+
+# Name of the temporary index file in which save the output of clang-format.
+# This file is created within the .git directory.
+temp_index_basename = 'clang-format-index'
+
+
+Range = collections.namedtuple('Range', 'start, count')
+
+
+def main():
+ config = load_git_config()
+
+ # In order to keep '--' yet allow options after positionals, we need to
+ # check for '--' ourselves. (Setting nargs='*' throws away the '--', while
+ # nargs=argparse.REMAINDER disallows options after positionals.)
+ argv = sys.argv[1:]
+ try:
+ idx = argv.index('--')
+ except ValueError:
+ dash_dash = []
+ else:
+ dash_dash = argv[idx:]
+ argv = argv[:idx]
+
+ default_extensions = ','.join([
+ # From clang/lib/Frontend/FrontendOptions.cpp, all lower case
+ 'c', 'h', # C
+ 'm', # ObjC
+ 'mm', # ObjC++
+ 'cc', 'cp', 'cpp', 'c++', 'cxx', 'hh', 'hpp', 'hxx', 'inc', # C++
+ 'ccm', 'cppm', 'cxxm', 'c++m', # C++ Modules
+ 'cu', 'cuh', # CUDA
+ # Other languages that clang-format supports
+ 'proto', 'protodevel', # Protocol Buffers
+ 'java', # Java
+ 'js', # JavaScript
+ 'ts', # TypeScript
+ 'cs', # C Sharp
+ 'json', # Json
+ 'sv', 'svh', 'v', 'vh', # Verilog
+ ])
+
+ p = argparse.ArgumentParser(
+ usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter,
+ description=desc)
+ p.add_argument('--binary',
+ default=config.get('clangformat.binary', 'clang-format'),
+ help='path to clang-format'),
+ p.add_argument('--commit',
+ default=config.get('clangformat.commit', 'HEAD'),
+ help='default commit to use if none is specified'),
+ p.add_argument('--diff', action='store_true',
+ help='print a diff instead of applying the changes')
+ p.add_argument('--diffstat', action='store_true',
+ help='print a diffstat instead of applying the changes')
+ p.add_argument('--extensions',
+ default=config.get('clangformat.extensions',
+ default_extensions),
+ help=('comma-separated list of file extensions to format, '
+ 'excluding the period and case-insensitive')),
+ p.add_argument('-f', '--force', action='store_true',
+ help='allow changes to unstaged files')
+ p.add_argument('-p', '--patch', action='store_true',
+ help='select hunks interactively')
+ p.add_argument('-q', '--quiet', action='count', default=0,
+ help='print less information')
+ p.add_argument('--staged', '--cached', action='store_true',
+ help='format lines in the stage instead of the working dir')
+ p.add_argument('--style',
+ default=config.get('clangformat.style', None),
+ help='passed to clang-format'),
+ p.add_argument('-v', '--verbose', action='count', default=0,
+ help='print extra information')
+ p.add_argument('--diff_from_common_commit', action='store_true',
+ help=('diff from the last common commit for commits in '
+ 'separate branches rather than the exact point of the '
+ 'commits'))
+ # We gather all the remaining positional arguments into 'args' since we need
+ # to use some heuristics to determine whether or not <commit> was present.
+ # However, to print pretty messages, we make use of metavar and help.
+ p.add_argument('args', nargs='*', metavar='<commit>',
+ help='revision from which to compute the diff')
+ p.add_argument('ignored', nargs='*', metavar='<file>...',
+ help='if specified, only consider differences in these files')
+ opts = p.parse_args(argv)
+
+ opts.verbose -= opts.quiet
+ del opts.quiet
+
+ commits, files = interpret_args(opts.args, dash_dash, opts.commit)
+ if len(commits) > 2:
+ die('at most two commits allowed; %d given' % len(commits))
+ if len(commits) == 2:
+ if opts.staged:
+ die('--staged is not allowed when two commits are given')
+ if not opts.diff:
+ die('--diff is required when two commits are given')
+ elif opts.diff_from_common_commit:
+ die('--diff_from_common_commit is only allowed when two commits are given')
+
+ if os.path.dirname(opts.binary):
+ opts.binary = os.path.abspath(opts.binary)
+
+ changed_lines = compute_diff_and_extract_lines(commits,
+ files,
+ opts.staged,
+ opts.diff_from_common_commit)
+ if opts.verbose >= 1:
+ ignored_files = set(changed_lines)
+ filter_by_extension(changed_lines, opts.extensions.lower().split(','))
+ # The computed diff outputs absolute paths, so we must cd before accessing
+ # those files.
+ cd_to_toplevel()
+ filter_symlinks(changed_lines)
+ if opts.verbose >= 1:
+ ignored_files.difference_update(changed_lines)
+ if ignored_files:
+ print(
+ 'Ignoring changes in the following files (wrong extension or symlink):')
+ for filename in ignored_files:
+ print(' %s' % filename)
+ if changed_lines:
+ print('Running clang-format on the following files:')
+ for filename in changed_lines:
+ print(' %s' % filename)
+
+ if not changed_lines:
+ if opts.verbose >= 0:
+ print('no modified files to format')
+ return 0
+
+ if len(commits) > 1:
+ old_tree = commits[1]
+ revision = old_tree
+ elif opts.staged:
+ old_tree = create_tree_from_index(changed_lines)
+ revision = ''
+ else:
+ old_tree = create_tree_from_workdir(changed_lines)
+ revision = None
+ new_tree = run_clang_format_and_save_to_tree(changed_lines,
+ revision,
+ binary=opts.binary,
+ style=opts.style)
+ if opts.verbose >= 1:
+ print('old tree: %s' % old_tree)
+ print('new tree: %s' % new_tree)
+
+ if old_tree == new_tree:
+ if opts.verbose >= 0:
+ print('clang-format did not modify any files')
+ return 0
+
+ if opts.diff:
+ return print_diff(old_tree, new_tree)
+ if opts.diffstat:
+ return print_diffstat(old_tree, new_tree)
+
+ changed_files = apply_changes(old_tree, new_tree, force=opts.force,
+ patch_mode=opts.patch)
+ if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
+ print('changed files:')
+ for filename in changed_files:
+ print(' %s' % filename)
+
+ return 1
+
+
+def load_git_config(non_string_options=None):
+ """Return the git configuration as a dictionary.
+
+ All options are assumed to be strings unless in `non_string_options`, in which
+ is a dictionary mapping option name (in lower case) to either "--bool" or
+ "--int"."""
+ if non_string_options is None:
+ non_string_options = {}
+ out = {}
+ for entry in run('git', 'config', '--list', '--null').split('\0'):
+ if entry:
+ if '\n' in entry:
+ name, value = entry.split('\n', 1)
+ else:
+ # A setting with no '=' ('\n' with --null) is implicitly 'true'
+ name = entry
+ value = 'true'
+ if name in non_string_options:
+ value = run('git', 'config', non_string_options[name], name)
+ out[name] = value
+ return out
+
+
+def interpret_args(args, dash_dash, default_commit):
+ """Interpret `args` as "[commits] [--] [files]" and return (commits, files).
+
+ It is assumed that "--" and everything that follows has been removed from
+ args and placed in `dash_dash`.
+
+ If "--" is present (i.e., `dash_dash` is non-empty), the arguments to its
+ left (if present) are taken as commits. Otherwise, the arguments are checked
+ from left to right if they are commits or files. If commits are not given,
+ a list with `default_commit` is used."""
+ if dash_dash:
+ if len(args) == 0:
+ commits = [default_commit]
+ else:
+ commits = args
+ for commit in commits:
+ object_type = get_object_type(commit)
+ if object_type not in ('commit', 'tag'):
+ if object_type is None:
+ die("'%s' is not a commit" % commit)
+ else:
+ die("'%s' is a %s, but a commit was expected" % (commit, object_type))
+ files = dash_dash[1:]
+ elif args:
+ commits = []
+ while args:
+ if not disambiguate_revision(args[0]):
+ break
+ commits.append(args.pop(0))
+ if not commits:
+ commits = [default_commit]
+ files = args
+ else:
+ commits = [default_commit]
+ files = []
+ return commits, files
+
+
+def disambiguate_revision(value):
+ """Returns True if `value` is a revision, False if it is a file, or dies."""
+ # If `value` is ambiguous (neither a commit nor a file), the following
+ # command will die with an appropriate error message.
+ run('git', 'rev-parse', value, verbose=False)
+ object_type = get_object_type(value)
+ if object_type is None:
+ return False
+ if object_type in ('commit', 'tag'):
+ return True
+ die('`%s` is a %s, but a commit or filename was expected' %
+ (value, object_type))
+
+
+def get_object_type(value):
+ """Returns a string description of an object's type, or None if it is not
+ a valid git object."""
+ cmd = ['git', 'cat-file', '-t', value]
+ p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = p.communicate()
+ if p.returncode != 0:
+ return None
+ return convert_string(stdout.strip())
+
+
+def compute_diff_and_extract_lines(commits, files, staged, diff_common_commit):
+ """Calls compute_diff() followed by extract_lines()."""
+ diff_process = compute_diff(commits, files, staged, diff_common_commit)
+ changed_lines = extract_lines(diff_process.stdout)
+ diff_process.stdout.close()
+ diff_process.wait()
+ if diff_process.returncode != 0:
+ # Assume error was already printed to stderr.
+ sys.exit(2)
+ return changed_lines
+
+
+def compute_diff(commits, files, staged, diff_common_commit):
+ """Return a subprocess object producing the diff from `commits`.
+
+ The return value's `stdin` file object will produce a patch with the
+ differences between the working directory (or stage if --staged is used) and
+ the first commit if a single one was specified, or the difference between
+ both specified commits, filtered on `files` (if non-empty).
+ Zero context lines are used in the patch."""
+ git_tool = 'diff-index'
+ extra_args = []
+ if len(commits) == 2:
+ git_tool = 'diff-tree'
+ if diff_common_commit:
+ commits = [f'{commits[0]}...{commits[1]}']
+ elif staged:
+ extra_args += ['--cached']
+
+ cmd = ['git', git_tool, '-p', '-U0'] + extra_args + commits + ['--']
+ cmd.extend(files)
+ p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+ p.stdin.close()
+ return p
+
+
+def extract_lines(patch_file):
+ """Extract the changed lines in `patch_file`.
+
+ The return value is a dictionary mapping filename to a list of (start_line,
+ line_count) pairs.
+
+ The input must have been produced with ``-U0``, meaning unidiff format with
+ zero lines of context. The return value is a dict mapping filename to a
+ list of line `Range`s."""
+ matches = {}
+ for line in patch_file:
+ line = convert_string(line)
+ match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
+ if match:
+ filename = match.group(1).rstrip('\r\n\t')
+ match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
+ if match:
+ start_line = int(match.group(1))
+ line_count = 1
+ if match.group(3):
+ line_count = int(match.group(3))
+ if line_count == 0:
+ line_count = 1
+ if start_line == 0:
+ continue
+ matches.setdefault(filename, []).append(Range(start_line, line_count))
+ return matches
+
+
+def filter_by_extension(dictionary, allowed_extensions):
+ """Delete every key in `dictionary` that doesn't have an allowed extension.
+
+ `allowed_extensions` must be a collection of lowercase file extensions,
+ excluding the period."""
+ allowed_extensions = frozenset(allowed_extensions)
+ for filename in list(dictionary.keys()):
+ base_ext = filename.rsplit('.', 1)
+ if len(base_ext) == 1 and '' in allowed_extensions:
+ continue
+ if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
+ del dictionary[filename]
+
+
+def filter_symlinks(dictionary):
+ """Delete every key in `dictionary` that is a symlink."""
+ for filename in list(dictionary.keys()):
+ if os.path.islink(filename):
+ del dictionary[filename]
+
+
+def cd_to_toplevel():
+ """Change to the top level of the git repository."""
+ toplevel = run('git', 'rev-parse', '--show-toplevel')
+ os.chdir(toplevel)
+
+
+def create_tree_from_workdir(filenames):
+ """Create a new git tree with the given files from the working directory.
+
+ Returns the object ID (SHA-1) of the created tree."""
+ return create_tree(filenames, '--stdin')
+
+
+def create_tree_from_index(filenames):
+ # Copy the environment, because the files have to be read from the original
+ # index.
+ env = os.environ.copy()
+ def index_contents_generator():
+ for filename in filenames:
+ git_ls_files_cmd = ['git', 'ls-files', '--stage', '-z', '--', filename]
+ git_ls_files = subprocess.Popen(git_ls_files_cmd, env=env,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+ stdout = git_ls_files.communicate()[0]
+ yield convert_string(stdout.split(b'\0')[0])
+ return create_tree(index_contents_generator(), '--index-info')
+
+
+def run_clang_format_and_save_to_tree(changed_lines, revision=None,
+ binary='clang-format', style=None):
+ """Run clang-format on each file and save the result to a git tree.
+
+ Returns the object ID (SHA-1) of the created tree."""
+ # Copy the environment when formatting the files in the index, because the
+ # files have to be read from the original index.
+ env = os.environ.copy() if revision == '' else None
+ def iteritems(container):
+ try:
+ return container.iteritems() # Python 2
+ except AttributeError:
+ return container.items() # Python 3
+ def index_info_generator():
+ for filename, line_ranges in iteritems(changed_lines):
+ if revision is not None:
+ if len(revision) > 0:
+ git_metadata_cmd = ['git', 'ls-tree',
+ '%s:%s' % (revision, os.path.dirname(filename)),
+ os.path.basename(filename)]
+ else:
+ git_metadata_cmd = ['git', 'ls-files', '--stage', '--', filename]
+ git_metadata = subprocess.Popen(git_metadata_cmd, env=env,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+ stdout = git_metadata.communicate()[0]
+ mode = oct(int(stdout.split()[0], 8))
+ else:
+ mode = oct(os.stat(filename).st_mode)
+ # Adjust python3 octal format so that it matches what git expects
+ if mode.startswith('0o'):
+ mode = '0' + mode[2:]
+ blob_id = clang_format_to_blob(filename, line_ranges,
+ revision=revision,
+ binary=binary,
+ style=style,
+ env=env)
+ yield '%s %s\t%s' % (mode, blob_id, filename)
+ return create_tree(index_info_generator(), '--index-info')
+
+
+def create_tree(input_lines, mode):
+ """Create a tree object from the given input.
+
+ If mode is '--stdin', it must be a list of filenames. If mode is
+ '--index-info' is must be a list of values suitable for "git update-index
+ --index-info", such as "<mode> <SP> <sha1> <TAB> <filename>". Any other mode
+ is invalid."""
+ assert mode in ('--stdin', '--index-info')
+ cmd = ['git', 'update-index', '--add', '-z', mode]
+ with temporary_index_file():
+ p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
+ for line in input_lines:
+ p.stdin.write(to_bytes('%s\0' % line))
+ p.stdin.close()
+ if p.wait() != 0:
+ die('`%s` failed' % ' '.join(cmd))
+ tree_id = run('git', 'write-tree')
+ return tree_id
+
+
+def clang_format_to_blob(filename, line_ranges, revision=None,
+ binary='clang-format', style=None, env=None):
+ """Run clang-format on the given file and save the result to a git blob.
+
+ Runs on the file in `revision` if not None, or on the file in the working
+ directory if `revision` is None. Revision can be set to an empty string to run
+ clang-format on the file in the index.
+
+ Returns the object ID (SHA-1) of the created blob."""
+ clang_format_cmd = [binary]
+ if style:
+ clang_format_cmd.extend(['-style='+style])
+ clang_format_cmd.extend([
+ '-lines=%s:%s' % (start_line, start_line+line_count-1)
+ for start_line, line_count in line_ranges])
+ if revision is not None:
+ clang_format_cmd.extend(['-assume-filename='+filename])
+ git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)]
+ git_show = subprocess.Popen(git_show_cmd, env=env, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+ git_show.stdin.close()
+ clang_format_stdin = git_show.stdout
+ else:
+ clang_format_cmd.extend([filename])
+ git_show = None
+ clang_format_stdin = subprocess.PIPE
+ try:
+ clang_format = subprocess.Popen(clang_format_cmd, stdin=clang_format_stdin,
+ stdout=subprocess.PIPE)
+ if clang_format_stdin == subprocess.PIPE:
+ clang_format_stdin = clang_format.stdin
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ die('cannot find executable "%s"' % binary)
+ else:
+ raise
+ clang_format_stdin.close()
+ hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin']
+ hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout,
+ stdout=subprocess.PIPE)
+ clang_format.stdout.close()
+ stdout = hash_object.communicate()[0]
+ if hash_object.returncode != 0:
+ die('`%s` failed' % ' '.join(hash_object_cmd))
+ if clang_format.wait() != 0:
+ die('`%s` failed' % ' '.join(clang_format_cmd))
+ if git_show and git_show.wait() != 0:
+ die('`%s` failed' % ' '.join(git_show_cmd))
+ return convert_string(stdout).rstrip('\r\n')
+
+
+@contextlib.contextmanager
+def temporary_index_file(tree=None):
+ """Context manager for setting GIT_INDEX_FILE to a temporary file and deleting
+ the file afterward."""
+ index_path = create_temporary_index(tree)
+ old_index_path = os.environ.get('GIT_INDEX_FILE')
+ os.environ['GIT_INDEX_FILE'] = index_path
+ try:
+ yield
+ finally:
+ if old_index_path is None:
+ del os.environ['GIT_INDEX_FILE']
+ else:
+ os.environ['GIT_INDEX_FILE'] = old_index_path
+ os.remove(index_path)
+
+
+def create_temporary_index(tree=None):
+ """Create a temporary index file and return the created file's path.
+
+ If `tree` is not None, use that as the tree to read in. Otherwise, an
+ empty index is created."""
+ gitdir = run('git', 'rev-parse', '--git-dir')
+ path = os.path.join(gitdir, temp_index_basename)
+ if tree is None:
+ tree = '--empty'
+ run('git', 'read-tree', '--index-output='+path, tree)
+ return path
+
+
+def print_diff(old_tree, new_tree):
+ """Print the diff between the two trees to stdout."""
+ # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output
+ # is expected to be viewed by the user, and only the former does nice things
+ # like color and pagination.
+ #
+ # We also only print modified files since `new_tree` only contains the files
+ # that were modified, so unmodified files would show as deleted without the
+ # filter.
+ return subprocess.run(['git', 'diff', '--diff-filter=M',
+ '--exit-code', old_tree, new_tree]).returncode
+
+def print_diffstat(old_tree, new_tree):
+ """Print the diffstat between the two trees to stdout."""
+ # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output
+ # is expected to be viewed by the user, and only the former does nice things
+ # like color and pagination.
+ #
+ # We also only print modified files since `new_tree` only contains the files
+ # that were modified, so unmodified files would show as deleted without the
+ # filter.
+ return subprocess.run(['git', 'diff', '--diff-filter=M', '--exit-code',
+ '--stat', old_tree, new_tree]).returncode
+
+def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
+ """Apply the changes in `new_tree` to the working directory.
+
+ Bails if there are local changes in those files and not `force`. If
+ `patch_mode`, runs `git checkout --patch` to select hunks interactively."""
+ changed_files = run('git', 'diff-tree', '--diff-filter=M', '-r', '-z',
+ '--name-only', old_tree,
+ new_tree).rstrip('\0').split('\0')
+ if not force:
+ unstaged_files = run('git', 'diff-files', '--name-status', *changed_files)
+ if unstaged_files:
+ print('The following files would be modified but '
+ 'have unstaged changes:', file=sys.stderr)
+ print(unstaged_files, file=sys.stderr)
+ print('Please commit, stage, or stash them first.', file=sys.stderr)
+ sys.exit(2)
+ if patch_mode:
+ # In patch mode, we could just as well create an index from the new tree
+ # and checkout from that, but then the user will be presented with a
+ # message saying "Discard ... from worktree". Instead, we use the old
+ # tree as the index and checkout from new_tree, which gives the slightly
+ # better message, "Apply ... to index and worktree". This is not quite
+ # right, since it won't be applied to the user's index, but oh well.
+ with temporary_index_file(old_tree):
+ subprocess.run(['git', 'checkout', '--patch', new_tree], check=True)
+ index_tree = old_tree
+ else:
+ with temporary_index_file(new_tree):
+ run('git', 'checkout-index', '-f', '--', *changed_files)
+ return changed_files
+
+
+def run(*args, **kwargs):
+ stdin = kwargs.pop('stdin', '')
+ verbose = kwargs.pop('verbose', True)
+ strip = kwargs.pop('strip', True)
+ for name in kwargs:
+ raise TypeError("run() got an unexpected keyword argument '%s'" % name)
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ stdin=subprocess.PIPE)
+ stdout, stderr = p.communicate(input=stdin)
+
+ stdout = convert_string(stdout)
+ stderr = convert_string(stderr)
+
+ if p.returncode == 0:
+ if stderr:
+ if verbose:
+ print('`%s` printed to stderr:' % ' '.join(args), file=sys.stderr)
+ print(stderr.rstrip(), file=sys.stderr)
+ if strip:
+ stdout = stdout.rstrip('\r\n')
+ return stdout
+ if verbose:
+ print('`%s` returned %s' % (' '.join(args), p.returncode), file=sys.stderr)
+ if stderr:
+ print(stderr.rstrip(), file=sys.stderr)
+ sys.exit(2)
+
+
+def die(message):
+ print('error:', message, file=sys.stderr)
+ sys.exit(2)
+
+
+def to_bytes(str_input):
+ # Encode to UTF-8 to get binary data.
+ if isinstance(str_input, bytes):
+ return str_input
+ return str_input.encode('utf-8')
+
+
+def to_string(bytes_input):
+ if isinstance(bytes_input, str):
+ return bytes_input
+ return bytes_input.encode('utf-8')
+
+
+def convert_string(bytes_input):
+ try:
+ return to_string(bytes_input.decode('utf-8'))
+ except AttributeError: # 'str' object has no attribute 'decode'.
+ return str(bytes_input)
+ except UnicodeError:
+ return str(bytes_input)
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/scripts/install-dm.sh b/scripts/install-dm.sh
new file mode 100644
index 000000000..a13c8c8ea
--- /dev/null
+++ b/scripts/install-dm.sh
@@ -0,0 +1,135 @@
+#!/usr/bin/env bash
+#############################################################################
+##
+## Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com>
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+set -eu
+
+function show_help() {
+ cat <<EOF
+usage: install-dm [options] [components]
+
+Examples
+ ./install-dm.sh --version 8.57
+
+Options
+ -d, --directory <directory>
+ Root directory where to install the components.
+ Maps to c:/dm on Windows by default.
+
+ --version <version>
+ The desired toolchain version.
+ Currently supported only 8.57 version.
+
+EOF
+}
+
+VERSION="8.57"
+INSTALL_DIR="/c/dm"
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --directory|-d)
+ INSTALL_DIR="$2"
+ shift
+ ;;
+ --version)
+ VERSION="$2"
+ shift
+ ;;
+ --help|-h)
+ show_help
+ exit 0
+ ;;
+ *)
+ ;;
+ esac
+ shift
+done
+
+if [ -z "${INSTALL_DIR}" ]; then
+ echo "No --directory specified or auto-detection failed." >&2
+ exit 1
+fi
+
+if [ -z "${VERSION}" ]; then
+ echo "No --version specified." >&2
+ exit 1
+fi
+
+DOWNLOAD_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'dm-tmp'`
+
+DM_URL="http://ftp.digitalmars.com/Digital_Mars_C++/Patch/dm${VERSION//./}c.zip"
+UTILS_URL="http://ftp.digitalmars.com/bup.zip"
+DOS_LIBS_URL="http://ftp.digitalmars.com/Digital_Mars_C++/Patch/dm850dos.zip"
+DOSX_LIBS_URL="http://ftp.digitalmars.com/Digital_Mars_C++/Patch/dm831x.zip"
+
+DM_ZIP="${DOWNLOAD_DIR}/dm.zip"
+UTILS_ZIP="${DOWNLOAD_DIR}/utils.zip"
+DOS_LIBS_ZIP="${DOWNLOAD_DIR}/doslibs.zip"
+DOSX_LIBS_ZIP="${DOWNLOAD_DIR}/dosxlibs.zip"
+
+echo "Downloading compiler from ${DM_URL}..." >&2
+curl --progress-bar -L -o ${DM_ZIP} ${DM_URL} >&2
+
+echo "Downloading utils from ${UTILS_URL}..." >&2
+curl --progress-bar -L -o ${UTILS_ZIP} ${UTILS_URL} >&2
+
+echo "Downloading DOS libs from ${DOS_LIBS_URL}..." >&2
+curl --progress-bar -L -o ${DOS_LIBS_ZIP} ${DOS_LIBS_URL} >&2
+
+echo "Downloading DOSX libs from ${DOSX_LIBS_URL}..." >&2
+curl --progress-bar -L -o ${DOSX_LIBS_ZIP} ${DOSX_LIBS_URL} >&2
+
+echo "Unpacking compiler to ${INSTALL_DIR}..." >&2
+7z x -y -o${INSTALL_DIR} ${DM_ZIP} >/dev/null 2>&1
+
+echo "Unpacking utils to ${INSTALL_DIR}..." >&2
+7z x -y -o${INSTALL_DIR} ${UTILS_ZIP} >/dev/null 2>&1
+
+echo "Unpacking DOS libs to ${INSTALL_DIR}..." >&2
+7z x -y -o${INSTALL_DIR} ${DOS_LIBS_ZIP} >/dev/null 2>&1
+
+echo "Unpacking DOSX libs to ${INSTALL_DIR}..." >&2
+7z x -y -o${INSTALL_DIR} ${DOSX_LIBS_ZIP} >/dev/null 2>&1
+
+echo "${INSTALL_DIR}/dm/bin"
+
+rm -f ${DM_ZIP}
+rm -f ${UTILS_ZIP}
+rm -f ${DOS_LIBS_ZIP}
+rm -f ${DOSX_LIBS_ZIP}
diff --git a/scripts/install-ow.sh b/scripts/install-ow.sh
new file mode 100755
index 000000000..c8db30b8f
--- /dev/null
+++ b/scripts/install-ow.sh
@@ -0,0 +1,129 @@
+#!/usr/bin/env bash
+#############################################################################
+##
+## Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com>
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+set -eu
+set -o pipefail
+
+function show_help() {
+ cat <<EOF
+usage: install-ow [options] [components]
+
+Examples
+ ./install-ow.sh --version 2.0
+
+Options
+ -d, --directory <directory>
+ Root directory where to install the components.
+ Maps to c:/watcom on Windows, /opt/watcom on Linux
+ by default.
+
+ --version <version>
+ The desired toolchain version.
+ Currently supported only 2.0 version.
+
+EOF
+}
+
+VERSION=2.0
+INSTALL_DIR=
+BIN_DIR=
+
+if [[ "$OSTYPE" =~ "linux" ]]; then
+ INSTALL_DIR="/opt/watcom"
+ if [[ "$HOSTTYPE" == "x86_64" ]]; then
+ BIN_DIR="binl64"
+ elif [[ "$HOSTTYPE" == "x86" ]]; then
+ BIN_DIR="binl"
+ fi
+elif [[ "$OSTYPE" == "msys" ]]; then
+ INSTALL_DIR="/c/watcom"
+ if [[ "$HOSTTYPE" == "x86_64" ]]; then
+ BIN_DIR="binnt64"
+ elif [[ "$HOSTTYPE" == "x86" ]]; then
+ BIN_DIR="binnt"
+ fi
+fi
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --directory|-d)
+ INSTALL_DIR="$2"
+ shift
+ ;;
+ --version)
+ VERSION="$2"
+ shift
+ ;;
+ --help|-h)
+ show_help
+ exit 0
+ ;;
+ *)
+ ;;
+ esac
+ shift
+done
+
+if [ -z "${INSTALL_DIR}" ]; then
+ echo "No --directory specified or auto-detection failed." >&2
+ exit 1
+fi
+
+if [ -z "${VERSION}" ]; then
+ echo "No --version specified." >&2
+ exit 1
+fi
+
+DOWNLOAD_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'ow-tmp'`
+
+VERSION_MAJOR=`echo $VERSION | cut -d. -f1`
+VERSION_MINOR=`echo $VERSION | cut -d. -f2`
+
+OW_URL="https://github.com/open-watcom/open-watcom-v${VERSION_MAJOR}/releases/download/Current-build/ow-snapshot.tar.xz"
+OW_TAR="${DOWNLOAD_DIR}/ow.tar.xz"
+
+echo "Downloading compiler from ${OW_URL}..." >&2
+curl --progress-bar -L -o ${OW_TAR} ${OW_URL} >&2
+
+echo "Unpacking compiler to ${INSTALL_DIR}..." >&2
+7z x "${OW_TAR}" -so | 7z x -aoa -si -ttar -o"${INSTALL_DIR}" >/dev/null 2>&1
+
+echo "${INSTALL_DIR}/${BIN_DIR}"
+
+rm -f ${OW_TAR}
diff --git a/scripts/install-qt.sh b/scripts/install-qt.sh
index 3024c7417..5799fee02 100755
--- a/scripts/install-qt.sh
+++ b/scripts/install-qt.sh
@@ -45,7 +45,7 @@ usage: install-qt [options] [components]
Examples
./install-qt.sh --version 5.13.1 qtbase
- ./install-qt.sh --version 5.14.0 --target android --toolchain any qtbase qtscript
+ ./install-qt.sh --version 5.14.0 --target android --toolchain any qtbase
Positional arguments
components
@@ -59,11 +59,14 @@ Positional arguments
target and toolchain.
Options
- --directory <directory>
+ -d, --directory <directory>
Root directory where to install the components.
Maps to C:/Qt on Windows, /opt/Qt on Linux, /usr/local/Qt on Mac
by default.
+ -f, --force
+ Force download and do not attempt to re-use an existing installation.
+
--host <host-os>
The host operating system. Can be one of linux_x64, mac_x64,
windows_x86. Auto-detected by default.
@@ -86,7 +89,7 @@ Options
android
any, android_armv7, android_arm64_v8a
desktop
- clang_64 (default),
+ clang_64 (default)
ios
ios
@@ -106,6 +109,8 @@ EOF
TARGET_PLATFORM=desktop
COMPONENTS=
VERSION=
+FORCE_DOWNLOAD=false
+MD5_TOOL=md5sum
case "$OSTYPE" in
*linux*)
@@ -117,6 +122,7 @@ case "$OSTYPE" in
HOST_OS=mac_x64
INSTALL_DIR=/usr/local/Qt
TOOLCHAIN=clang_64
+ MD5_TOOL="md5 -r"
;;
msys)
HOST_OS=windows_x86
@@ -135,6 +141,9 @@ while [ $# -gt 0 ]; do
INSTALL_DIR="$2"
shift
;;
+ --force|-f)
+ FORCE_DOWNLOAD=true
+ ;;
--host)
HOST_OS="$2"
shift
@@ -195,6 +204,39 @@ case "$TARGET_PLATFORM" in
;;
esac
+HASH=$(echo "${OSTYPE} ${TARGET_PLATFORM} ${TOOLCHAIN} ${VERSION} ${INSTALL_DIR}" | ${MD5_TOOL} | head -c 16)
+HASH_FILEPATH="${INSTALL_DIR}/${HASH}.manifest"
+INSTALLATION_IS_VALID=false
+if ! ${FORCE_DOWNLOAD} && [ -f "${HASH_FILEPATH}" ]; then
+ INSTALLATION_IS_VALID=true
+ while read filepath; do
+ if [ ! -e "${filepath}" ]; then
+ INSTALLATION_IS_VALID=false
+ break
+ fi
+ done <"${HASH_FILEPATH}"
+fi
+
+if ${INSTALLATION_IS_VALID}; then
+ echo "Already installed. Skipping download." >&2
+ exit 0
+fi
+
+MIRRORS="\
+ http://ftp.acc.umu.se/mirror/qt.io/qtproject \
+ http://ftp.fau.de/qtproject \
+ http://download.qt.io \
+"
+
+for MIRROR in ${MIRRORS}; do
+ if curl "${MIRROR}/online" -s -f -o /dev/null; then
+ break;
+ else
+ echo "Server ${MIRROR} not availabe. Trying next alternative..." >&2
+ MIRROR=""
+ fi
+done
+
DOWNLOAD_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'install-qt'`
#
@@ -203,20 +245,36 @@ DOWNLOAD_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'install-qt'`
function compute_url(){
local COMPONENT=$1
local CURL="curl -s -L"
- local BASE_URL="http://download.qt.io/online/qtsdkrepository/${HOST_OS}/${TARGET_PLATFORM}"
+ local BASE_URL="${MIRROR}/online/qtsdkrepository/${HOST_OS}/${TARGET_PLATFORM}"
+ local ANDROID_ARCH=$(echo ${TOOLCHAIN##android_})
if [[ "${COMPONENT}" =~ "qtcreator" ]]; then
- REMOTE_BASE="tools_qtcreator/qt.tools.qtcreator"
- REMOTE_PATH="$(${CURL} ${BASE_URL}/${REMOTE_BASE}/ | grep -o -E "${VERSION}[0-9\-]*${COMPONENT}\.7z" | tail -1)"
+ if [[ "${HOST_OS}" == "windows_x86" ]]; then
+ # newer QtC versions do not supported x86 version anymore
+ HOST_OS="windows_x64"
+ fi
+
+ SHORT_VERSION=${VERSION%??}
+ BASE_URL="${MIRROR}/official_releases/qtcreator"
+ REMOTE_PATH="${SHORT_VERSION}/${VERSION}/installer_source/${HOST_OS}/qtcreator.7z"
+ echo "${BASE_URL}/${REMOTE_PATH}"
+ return 0
+ elif [[ "${COMPONENT}" =~ "mingw" ]]; then
+ REMOTE_BASE="tools_mingw90/qt.tools.${TOOLCHAIN}${VERSION//./}"
+ REMOTE_PATH="$(${CURL} ${BASE_URL}/${REMOTE_BASE}/ | grep -o -E "[[:alnum:]_.\-]*7z" | grep -v "meta" | head -1)"
if [ ! -z "${REMOTE_PATH}" ]; then
echo "${BASE_URL}/${REMOTE_BASE}/${REMOTE_PATH}"
return 0
fi
-
else
REMOTE_BASES=(
+ # New repository format (>=6.0.0)
+ "qt6_${VERSION//./}/qt.qt6.${VERSION//./}.${TOOLCHAIN}"
+ "qt6_${VERSION//./}/qt.qt6.${VERSION//./}.${COMPONENT}.${TOOLCHAIN}"
+ "qt6_${VERSION//./}_${ANDROID_ARCH}/qt.qt6.${VERSION//./}.${TOOLCHAIN}"
+ "qt6_${VERSION//./}_${ANDROID_ARCH}/qt.qt6.${VERSION//./}.${COMPONENT}.${TOOLCHAIN}"
# New repository format (>=5.9.6)
"qt5_${VERSION//./}/qt.qt5.${VERSION//./}.${TOOLCHAIN}"
"qt5_${VERSION//./}/qt.qt5.${VERSION//./}.${COMPONENT}.${TOOLCHAIN}"
@@ -241,14 +299,39 @@ function compute_url(){
exit 1
}
+function version {
+ echo "$@" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }';
+}
+
mkdir -p ${INSTALL_DIR}
+rm -f "${HASH_FILEPATH}"
for COMPONENT in ${COMPONENTS}; do
+ if [[ "${COMPONENT}" =~ "qtcreator" ]] && [[ "${HOST_OS}" != "mac_x64" ]]; then
+ UNPACK_DIR="${INSTALL_DIR}/Tools/QtCreator"
+ mkdir -p ${UNPACK_DIR}
+ else
+ UNPACK_DIR="${INSTALL_DIR}"
+ fi
+
+ if [ "$(version "${VERSION}")" -ge "$(version "6.0.0")" ]; then
+ if [[ "${COMPONENT}" =~ "qtscript" ]] || [[ "${COMPONENT}" =~ "qtscxml" ]] || [[ "${COMPONENT}" =~ "qtx11extras" ]]; then
+ echo "Component ${COMPONENT} was removed in Qt6, skipping" >&2
+ continue
+ fi
+ else
+ if [[ "${COMPONENT}" =~ "qt5compat" ]]; then
+ echo "Component ${COMPONENT} is not present in Qt ${VERSION}, skipping" >&2
+ continue
+ fi
+ fi
+
URL="$(compute_url ${COMPONENT})"
- echo "Downloading ${COMPONENT}..." >&2
+ echo "Downloading ${COMPONENT} ${URL}..." >&2
curl --progress-bar -L -o ${DOWNLOAD_DIR}/package.7z ${URL} >&2
- 7z x -y -o${INSTALL_DIR} ${DOWNLOAD_DIR}/package.7z >/dev/null 2>&1
+ 7z x -y -o${UNPACK_DIR} ${DOWNLOAD_DIR}/package.7z >/dev/null 2>&1
+ 7z l -ba -slt -y ${DOWNLOAD_DIR}/package.7z | tr '\\' '/' | sed -n -e "s|^Path\ =\ |${UNPACK_DIR}/|p" >> "${HASH_FILEPATH}" 2>/dev/null
rm -f ${DOWNLOAD_DIR}/package.7z
#
@@ -265,17 +348,42 @@ for COMPONENT in ${COMPONENTS}; do
SUBDIR="${TOOLCHAIN/win32_/}"
elif [[ "${TOOLCHAIN}" =~ "any" ]] && [[ "${TARGET_PLATFORM}" == "android" ]]; then
SUBDIR="android"
+ elif [[ "${HOST_OS}" == "mac_x64" ]] && [[ ! "${VERSION}" < "6.1.2" ]] && [[ "${TARGET_PLATFORM}" == "desktop" ]]; then
+ SUBDIR="macos"
else
SUBDIR="${TOOLCHAIN}"
fi
- CONF_FILE="${INSTALL_DIR}/${VERSION}/${SUBDIR}/bin/qt.conf"
- echo "[Paths]" > ${CONF_FILE}
- echo "Prefix = .." >> ${CONF_FILE}
+ if [ "${TARGET_PLATFORM}" == "android" ] && [ ! "${VERSION}" \< "6.0.0" ]; then
+ CONF_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/target_qt.conf"
+ ANDROID_QMAKE_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/qmake"
+ if [ "${TOOLCHAIN}" == "android_armv7" ] && [ ! "${VERSION}" \< "6.4.2" ]; then
+ sed -i "s/\r//" "${CONF_FILE}"
+ sed -i "s|HostLibraryExecutables=.\/bin|HostLibraryExecutables=.\/libexec|g" "${CONF_FILE}"
+ chmod +x "${ANDROID_QMAKE_FILE}"
+ sed -i "s|\\\|\/|g" "${ANDROID_QMAKE_FILE}"
+ fi
+ sed -i "s|target|../$TOOLCHAIN|g" "${CONF_FILE}"
+ sed -i "/HostPrefix/ s|$|gcc_64|g" "${CONF_FILE}"
+ QMAKE_FILE="${UNPACK_DIR}/${VERSION}/gcc_64/bin/qmake"
+ sed -i "s|\/home\/qt\/work\/install\/bin\/qmake|$QMAKE_FILE|g" "${ANDROID_QMAKE_FILE}"
+ sed -i "s|\/Users\/qt\/work\/install\/bin\/qmake|$QMAKE_FILE|g" "${ANDROID_QMAKE_FILE}"
+ elif [ "${TARGET_PLATFORM}" == "ios" ] && [ ! "${VERSION}" \< "6.0.0" ]; then
+ CONF_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/target_qt.conf"
+ sed -i.bak "s|HostData=target|HostData=../$TOOLCHAIN|g" "${CONF_FILE}"
+ sed -i.bak "s|HostPrefix=..\/..\/|HostPrefix=..\/..\/macos|g" "${CONF_FILE}"
+ IOS_QMAKE_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/qmake"
+ QMAKE_FILE="${UNPACK_DIR}/${VERSION}/macos/bin/qmake"
+ sed -i.bak "s|\/Users\/qt\/work\/install\/bin\/qmake|${QMAKE_FILE}|g" "${IOS_QMAKE_FILE}"
+ else
+ CONF_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/bin/qt.conf"
+ echo "[Paths]" > ${CONF_FILE}
+ echo "Prefix = .." >> ${CONF_FILE}
+ fi
# Adjust the license to be able to run qmake
# sed with -i requires intermediate file on Mac OS
- PRI_FILE="${INSTALL_DIR}/${VERSION}/${SUBDIR}/mkspecs/qconfig.pri"
+ PRI_FILE="${UNPACK_DIR}/${VERSION}/${SUBDIR}/mkspecs/qconfig.pri"
sed -i.bak 's/Enterprise/OpenSource/g' "${PRI_FILE}"
sed -i.bak 's/licheck.*//g' "${PRI_FILE}"
rm "${PRI_FILE}.bak"
@@ -283,8 +391,22 @@ for COMPONENT in ${COMPONENTS}; do
# Print the directory so that the caller can
# adjust the PATH variable.
echo $(dirname "${CONF_FILE}")
+ elif [[ "${COMPONENT}" =~ "mingw" ]]; then
+ VERSION_DIR="${VERSION//./}"
+ if [[ "${TOOLCHAIN}" =~ "win64_mingw" ]]; then
+ if [[ "${VERSION}" == "9.0.0" ]]; then
+ VERSION_DIR="1120"
+ fi
+ echo "${UNPACK_DIR}/Tools/mingw${VERSION_DIR}_64/bin"
+ elif [[ "${TOOLCHAIN}" =~ "win32_mingw" ]]; then
+ echo "${UNPACK_DIR}/Tools/mingw${VERSION_DIR}_32/bin"
+ fi
elif [[ "${COMPONENT}" =~ "qtcreator" ]]; then
- echo "${INSTALL_DIR}/Tools/QtCreator/bin"
+ if [ "${HOST_OS}" == "mac_x64" ]; then
+ echo "${UNPACK_DIR}/Qt Creator.app/Contents/MacOS"
+ else
+ echo "${UNPACK_DIR}/bin"
+ fi
fi
done
diff --git a/scripts/make-release-archives.bat b/scripts/make-release-archives.bat
index 867fa773c..ce889a9ca 100644
--- a/scripts/make-release-archives.bat
+++ b/scripts/make-release-archives.bat
@@ -67,7 +67,8 @@ subst Q: "%CD%" && Q:
qbs build --settings-dir "%builddir%\.settings"^
-f qbs.qbs -d "%builddir%\build"^
- -p dist qbs.buildVariant:release project.withDocumentation:false "products.qbs archive.includeTopLevelDir:true"^
+ -p dist qbs.buildVariant:release project.withDocumentation:false products.qbs_archive.includeTopLevelDir:true^
+ modules.qbsbuildconfig.enableBundledQt:true^
config:release "qbs.installRoot:%builddir%\qbs-windows-x86-%version%" profile:qt^
config:release-64 "qbs.installRoot:%builddir%\qbs-windows-x86_64-%version%" profile:qt64 || exit /b
diff --git a/scripts/run-analyzer.sh b/scripts/run-analyzer.sh
index c623aed1c..9e53e50dd 100755
--- a/scripts/run-analyzer.sh
+++ b/scripts/run-analyzer.sh
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
#############################################################################
##
-## Copyright (C) 2019 Ivan Komissarov
-## Contact: abbapoh@gmail.com
+## Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com)
+## Contact: https://www.qt.io/licensing/
##
## This file is part of Qbs.
##
@@ -45,30 +45,23 @@ LLVM_INSTALL_DIR=${LLVM_INSTALL_DIR:-""}
# update-alternatives --install /usr/bin/run-clang-tidy.py run-clang-tidy.py /usr/bin/run-clang-tidy-4.0.py 1
CLANG_TIDY=`which clang-tidy`
-RUN_CLANG_TIDY=`which run-clang-tidy.py`
+RUN_CLANG_TIDY=`which run-clang-tidy`
if [ -z "$RUN_CLANG_TIDY" ] || [ -z "$CLANG_TIDY" ]; then
if [ ! -z "$LLVM_INSTALL_DIR" ]; then
CLANG_TIDY="$LLVM_INSTALL_DIR/bin/clang-tidy"
- RUN_CLANG_TIDY="$LLVM_INSTALL_DIR/share/clang/run-clang-tidy.py"
+ RUN_CLANG_TIDY="$LLVM_INSTALL_DIR/bin/run-clang-tidy"
else
echo "Can't find clang-tidy and/or run-clang-tidy.py in PATH, try setting LLVM_INSTALL_DIR"
exit 1
fi
fi
-NPROC=`which nproc`
-SYSCTL=`which sysctl`
-CPU_COUNT=2
-if [ ! -z "$NPROC" ]; then # Linux
- CPU_COUNT=`$NPROC --all`
-elif [ ! -z "$SYSCTL" ]; then # macOS
- CPU_COUNT=`$SYSCTL -n hw.ncpu`
-fi
+SCRIPT_DIR=$(dirname "$0")
+CPU_COUNT=$("${SCRIPT_DIR}/cpu-count.sh")
BUILD_OPTIONS="\
${QBS_BUILD_PROFILE:+profile:${QBS_BUILD_PROFILE}} \
- modules.qbsbuildconfig.enableProjectFileUpdates:true \
modules.cpp.treatWarningsAsErrors:true \
modules.qbs.buildVariant:release \
project.withTests:false \
@@ -84,6 +77,7 @@ if [ ! -f "$QBS_SRC_DIR/qbs.qbs" ]; then
fi
set -e
+set -o pipefail
qbs resolve -f "$QBS_SRC_DIR/qbs.qbs" $BUILD_OPTIONS
qbs build -f "$QBS_SRC_DIR/qbs.qbs" $BUILD_OPTIONS
@@ -92,12 +86,11 @@ qbs generate -g clangdb -f "$QBS_SRC_DIR/qbs.qbs" $BUILD_OPTIONS
SCRIPT="
import json
import os
-import sets
import sys
dbFile = sys.argv[1]
-blacklist = ['json.cpp']
-seenFiles = sets.Set()
+blacklist = ['json.cpp', 'qmljsgrammar.cpp', 'qmljsparser.cpp']
+seenFiles = set()
patched_db = []
with open(dbFile, 'r') as f:
db = json.load(f)
@@ -110,6 +103,8 @@ with open(dbFile, 'r') as f:
with open(dbFile, 'w') as f:
f.write(json.dumps(patched_db, indent=2))
"
-python -c "${SCRIPT}" analyzer/compile_commands.json
+python3 -c "${SCRIPT}" analyzer/compile_commands.json
-"$RUN_CLANG_TIDY" -p analyzer -clang-tidy-binary "$CLANG_TIDY" -j $CPU_COUNT -header-filter=".*qbs/.*\.h$" -quiet
+RUN_CLANG_TIDY+=" -p analyzer -clang-tidy-binary ${CLANG_TIDY} -j ${CPU_COUNT} -header-filter=\".*qbs.*\.h$\" -quiet"
+${RUN_CLANG_TIDY} 2>/dev/null | tee results.txt
+echo "$(grep -c 'warning:' results.txt) warnings in total"
diff --git a/scripts/scripts.qbs b/scripts/scripts.qbs
index 9a631ed10..8c6f77a6f 100644
--- a/scripts/scripts.qbs
+++ b/scripts/scripts.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Product {
name: "qbs dev scripts"
files: [
diff --git a/scripts/test-baremetal.sh b/scripts/test-baremetal.sh
new file mode 100755
index 000000000..985a5eac4
--- /dev/null
+++ b/scripts/test-baremetal.sh
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+set -eu
+
+#############################################################################
+##
+## Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+export LSAN_OPTIONS="suppressions=$( cd "$(dirname "$0")" ; pwd -P )/address-sanitizer-suppressions.txt:print_suppressions=0"
+
+export PATH="$1:$PATH"
+export QBS_AUTOTEST_ALWAYS_LOG_STDERR=true
+
+echo "Auto-detecting the Qbs profiles."
+qbs-setup-toolchains --detect
+qbs-config --list profiles
+
+echo "Testing the Qbs toolchains."
+export QBS_AUTOTEST_PROFILE=${QBS_AUTOTEST_PROFILE:-arm-none-eabi-gcc-9_2}
+tst_blackbox-baremetal
diff --git a/scripts/test-for-android.sh b/scripts/test-for-android.sh
new file mode 100755
index 000000000..34a7c0526
--- /dev/null
+++ b/scripts/test-for-android.sh
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+set -eu
+
+#############################################################################
+##
+## Copyright (C) 2019 Richard Weickelt.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+export PATH="$1:$PATH"
+
+export LSAN_OPTIONS="suppressions=$( cd "$(dirname "$0")" ; pwd -P )/address-sanitizer-suppressions.txt:print_suppressions=0"
+
+#
+# These are set outside of this script, for instance in the Docker image
+#
+echo "Android SDK installed at ${ANDROID_SDK_ROOT}"
+echo "Android NDK installed at ${ANDROID_NDK_ROOT}"
+
+# Cleaning profiles
+qbs config --unset profiles.qbs_autotests-android
+
+# Setting auto test profiles
+qbs setup-android --ndk-dir ${ANDROID_NDK_ROOT} --sdk-dir ${ANDROID_HOME} qbs_autotests-android
+
+export QBS_AUTOTEST_PROFILE=qbs_autotests-android
+export QBS_AUTOTEST_ALWAYS_LOG_STDERR=true
+export QTEST_FUNCTION_TIMEOUT=9000000
+
+qbs config --list
+
+tst_blackbox-android
+
diff --git a/scripts/test-qbs.sh b/scripts/test-qbs.sh
new file mode 100755
index 000000000..3996c63bf
--- /dev/null
+++ b/scripts/test-qbs.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+#############################################################################
+##
+## Copyright (C) 2020 Ivan Komissarov <abbapoh@gmail.com>
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+set -eu -o pipefail
+
+#
+# Qbs is built with the address sanitizer enabled.
+# Suppress findings in some parts of Qbs / dependencies.
+#
+export LSAN_OPTIONS="suppressions=$( cd "$(dirname "$0")" ; pwd -P )/address-sanitizer-suppressions.txt:print_suppressions=0"
+
+export PATH="$1:$PATH"
+
+export QBS_AUTOTEST_PROFILE=${QBS_AUTOTEST_PROFILE:-qt}
+echo "Running Qbs tests."
+find $1 -name "tst*" | xargs -I{} -n1 bash -c "{}"
diff --git a/scripts/test-qt-for-android.sh b/scripts/test-qt-for-android.sh
new file mode 100755
index 000000000..dcc52bc27
--- /dev/null
+++ b/scripts/test-qt-for-android.sh
@@ -0,0 +1,86 @@
+#!/usr/bin/env bash
+set -eu
+
+#############################################################################
+##
+## Copyright (C) 2019 Richard Weickelt.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+export PATH="$1:$PATH"
+
+export LSAN_OPTIONS="suppressions=$( cd "$(dirname "$0")" ; pwd -P )/address-sanitizer-suppressions.txt:print_suppressions=0"
+
+#
+# These are set outside of this script, for instance in the Docker image
+#
+QT_INSTALL_DIR=/opt/Qt/${QT_VERSION}
+echo "Android SDK installed at ${ANDROID_SDK_ROOT}"
+echo "Android NDK installed at ${ANDROID_NDK_ROOT}"
+echo "Qt installed at ${QT_INSTALL_DIR}"
+
+# Cleaning profiles
+qbs config --unset profiles.qbs_autotests-android
+qbs config --unset profiles.qbs_autotests-android-qt
+
+# Setting auto test profiles
+qbs setup-android --ndk-dir ${ANDROID_NDK_ROOT} --sdk-dir ${ANDROID_SDK_ROOT} qbs_autotests-android
+qbs setup-android --ndk-dir ${ANDROID_NDK_ROOT} --sdk-dir ${ANDROID_SDK_ROOT} --qt-dir ${QT_INSTALL_DIR} qbs_autotests-android-qt
+
+export QBS_AUTOTEST_PROFILE=qbs_autotests-android
+export QBS_AUTOTEST_ALWAYS_LOG_STDERR=true
+export QTEST_FUNCTION_TIMEOUT=9000000
+
+if [ ! "${QT_VERSION}" \< "5.14.0" ] && [ "${QT_VERSION}" \< "6.0.0" ]; then
+ echo "Using multi-arch data sets for qml tests (only for qt version >= 5.14 and < 6.0.0) with all architectures"
+ qbs config --list
+ tst_blackbox-android
+fi;
+
+if [ ! "${QT_VERSION}" \< "6.3.0" ]; then
+ echo "Using multi-arch data sets for qml tests (only for qt version >= 6.3.0) with all architectures"
+ qbs config --list
+ tst_blackbox-android
+fi;
+
+echo "Using single-arch (armv7a) data sets for qml tests"
+qbs config --unset profiles.qbs_autotests-android-qt.qbs.architectures
+qbs config profiles.qbs_autotests-android-qt.qbs.architecture armv7a
+
+qbs config --list
+
+tst_blackbox-android
+
diff --git a/scripts/test-qt.sh b/scripts/test-qt.sh
new file mode 100755
index 000000000..944d8941d
--- /dev/null
+++ b/scripts/test-qt.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+#############################################################################
+##
+## Copyright (C) 2021 Ivan Komissarov <abbapoh@gmail.com>
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+set -eu
+
+#
+# Qbs is built with the address sanitizer enabled.
+# Suppress findings in some parts of Qbs / dependencies.
+#
+export LSAN_OPTIONS="suppressions=$( cd "$(dirname "$0")" ; pwd -P )/address-sanitizer-suppressions.txt:print_suppressions=0"
+
+export PATH="$1:$PATH"
+
+export QBS_AUTOTEST_PROFILE=${QBS_AUTOTEST_PROFILE:-qt}
+
+tst_blackbox-qt
diff --git a/scripts/test-qt4.sh b/scripts/test-qt4.sh
new file mode 100755
index 000000000..b4b0078e4
--- /dev/null
+++ b/scripts/test-qt4.sh
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+set -eu
+
+#############################################################################
+##
+## Copyright (C) 2020 Raphael Cotty.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qbs.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+export PATH="$1:$PATH"
+
+export LSAN_OPTIONS="suppressions=$( cd "$(dirname "$0")" ; pwd -P )/address-sanitizer-suppressions.txt:print_suppressions=0"
+
+# Creating the qt4 profile
+
+qbs-setup-toolchains /usr/bin/g++ gcc
+qbs-setup-qt /usr/lib/x86_64-linux-gnu/qt4/bin/qmake qt4
+# To avoid warnings due to qt4 include files
+qbs config profiles.gcc.cpp.cxxFlags "-Wno-deprecated-copy"
+qbs config profiles.qt4.baseProfile gcc
+qbs config --list
+
+export QBS_AUTOTEST_PROFILE=qt4
+
+tst_blackbox-qt
diff --git a/share/CMakeLists.txt b/share/CMakeLists.txt
new file mode 100644
index 000000000..e47bd74f0
--- /dev/null
+++ b/share/CMakeLists.txt
@@ -0,0 +1,99 @@
+if(WIN32)
+ install(
+ FILES ../bin/ibmsvc.xml ../bin/ibqbs.bat
+ DESTINATION "${QBS_APP_INSTALL_DIR}"
+ )
+endif()
+
+# below we copy some files that are required to run qbs from the build directory
+# emulating the same layout we will have after the installation
+
+# copy & install python packages
+set(_SITE_PACKAGES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../src/3rdparty/python/lib/python3.9/site-packages/)
+file(GLOB_RECURSE _SITE_PACKAGES_RELATIVE RELATIVE "${_SITE_PACKAGES_DIR}" "${_SITE_PACKAGES_DIR}/*.py")
+
+set(_SITE_PACKAGES_SRC ${_SITE_PACKAGES_RELATIVE})
+list_transform_prepend(_SITE_PACKAGES_SRC "${_SITE_PACKAGES_DIR}/")
+
+set(_SITE_PACKAGES_DST ${_SITE_PACKAGES_RELATIVE})
+list_transform_prepend(_SITE_PACKAGES_DST "${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_RESOURCES_INSTALL_DIR}/qbs/python/")
+
+add_custom_command(
+ OUTPUT ${_SITE_PACKAGES_DST}
+ DEPENDS ${_SITE_PACKAGES_SRC}
+ COMMAND ${CMAKE_COMMAND} -E copy_directory
+ "${_SITE_PACKAGES_DIR}"
+ ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_RESOURCES_INSTALL_DIR}/qbs/python
+ COMMENT "Copying Python packages"
+ )
+install(
+ # trailing slash avoid copying the 'site-packages' dir and only copies its content
+ DIRECTORY "${_SITE_PACKAGES_DIR}"
+ DESTINATION "${QBS_RESOURCES_INSTALL_DIR}/qbs/python"
+ FILES_MATCHING PATTERN "*.py"
+ )
+
+# copy & install the "share/qbs" dir
+file(GLOB_RECURSE _QBS_RESOURCES_RELATIVE RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/qbs/*")
+
+set(_QBS_RESOURCES_SRC ${_QBS_RESOURCES_RELATIVE})
+list_transform_prepend(_QBS_RESOURCES_SRC "${CMAKE_CURRENT_SOURCE_DIR}/")
+
+set(_QBS_RESOURCES_DST ${_QBS_RESOURCES_RELATIVE})
+list_transform_prepend(_QBS_RESOURCES_DST "${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_RESOURCES_INSTALL_DIR}/")
+
+add_custom_command(
+ OUTPUT ${_QBS_RESOURCES_DST}
+ DEPENDS ${_QBS_RESOURCES_SRC}
+ COMMAND ${CMAKE_COMMAND} -E copy_directory
+ ${CMAKE_CURRENT_SOURCE_DIR}/qbs
+ ${CMAKE_BINARY_DIR}/${QBS_OUTPUT_PREFIX}${QBS_RESOURCES_INSTALL_DIR}/qbs
+ COMMENT "Copying QBS resources"
+ )
+install(
+ DIRECTORY qbs
+ DESTINATION "${QBS_RESOURCES_INSTALL_DIR}")
+
+# install examples dir
+install(
+ DIRECTORY ../examples
+ DESTINATION "${QBS_RESOURCES_INSTALL_DIR}/qbs"
+ )
+
+set(_QMLTYPES_INSTALL_PATH "${PROJECT_BINARY_DIR}/resources-build/install-root/share/qbs/qml-type-descriptions/qbs.qmltypes")
+get_update_path_command(UPDATE_PATH_COMMAND)
+get_target_property(_QBS_OUTPUT_DIR qbs RUNTIME_OUTPUT_DIRECTORY)
+add_custom_command(
+ OUTPUT "${_QMLTYPES_INSTALL_PATH}"
+ COMMAND ${CMAKE_COMMAND} -E env "${UPDATE_PATH_COMMAND}" ${_QBS_OUTPUT_DIR}/qbs
+ resolve
+ --settings-dir ${PROJECT_BINARY_DIR}/settings
+ -f ${PROJECT_SOURCE_DIR}/qbs.qbs
+ -d ${PROJECT_BINARY_DIR}/
+ config:resources-build
+ qbs.installPrefix:""
+ project.withCode:false
+ project.withDocumentation:false
+ profile:none
+ COMMAND ${CMAKE_COMMAND} -E env "${UPDATE_PATH_COMMAND}" ${_QBS_OUTPUT_DIR}/qbs
+ build
+ --settings-dir ${PROJECT_BINARY_DIR}/settings
+ -f ${PROJECT_SOURCE_DIR}/qbs.qbs
+ -d ${PROJECT_BINARY_DIR}/
+ config:resources-build
+ -p "qbs resources"
+ DEPENDS qbs "${_QBS_RESOURCES_DST}" qbs_processlauncher
+ )
+
+add_custom_target(
+ BuildQbsResources ALL
+ DEPENDS
+ ${_SITE_PACKAGES_DST}
+ ${_QBS_RESOURCES_DST}
+ "${_QMLTYPES_INSTALL_PATH}"
+ )
+
+install(
+ DIRECTORY ${PROJECT_BINARY_DIR}/resources-build/install-root/share/qbs/qml-type-descriptions
+ DESTINATION "${QBS_RESOURCES_INSTALL_DIR}/qbs"
+ )
diff --git a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js
index 5f9076004..9f7d92e59 100644
--- a/share/qbs/imports/qbs/DarwinTools/darwin-tools.js
+++ b/share/qbs/imports/qbs/DarwinTools/darwin-tools.js
@@ -166,7 +166,14 @@ var PropertyListVariableExpander = (function () {
var syntax;
var idx = -1;
for (var i in syntaxes) {
- var j = str.lastIndexOf(syntaxes[i].open);
+ var j;
+ // normal case - we search for the last occurrence to do a correct replacement
+ // for nested variables, e.g. ${VAR1_${VAR2}}. This doesn't work in case
+ // when start == end, e.g. @VAR@ - in that case we search from the start
+ if (syntaxes[i].open !== syntaxes[i].close)
+ j = str.lastIndexOf(syntaxes[i].open);
+ else
+ j = str.indexOf(syntaxes[i].open);
if (j > idx) {
syntax = syntaxes[i];
idx = j;
@@ -175,6 +182,48 @@ var PropertyListVariableExpander = (function () {
return { "syntax": syntax, "index": idx };
}
+ function expandString(key, str, env, seenVars) {
+ if (!str)
+ return str;
+ var repl = indexOfReplacementStart(syntaxes, str);
+ var i = repl.index;
+ while (i !== -1) {
+ var j = str.indexOf(repl.syntax.close, i + repl.syntax.open.length);
+ if (j === -1)
+ return str;
+ var varParts = str.slice(i + repl.syntax.open.length, j).split(':');
+ var varName = varParts[0];
+ var varFormatter = varParts[1];
+ var envValue = env[varName];
+ // if we end up expanding the same variable again, break the recursion
+ if (seenVars.indexOf(varName) !== -1)
+ envValue = "";
+ else
+ seenVars.push(varName);
+ var varValue = expandString(key, envValue, env, seenVars);
+ seenVars.pop();
+ if (undefined === varValue) {
+ // skip replacement
+ if ($this.undefinedVariableFunction)
+ $this.undefinedVariableFunction(key, varName);
+ varValue = "";
+ }
+ varValue = String(varValue);
+ if (varFormatter !== undefined) {
+ // TODO: XCode supports multiple formatters separated by a comma
+ var varFormatterLower = varFormatter.toLowerCase();
+ if (varFormatterLower === "rfc1034identifier" || varFormatterLower === "identifier")
+ varValue = Utilities.rfc1034Identifier(varValue);
+ if (varValue === "" && varFormatterLower.startsWith("default="))
+ varValue = varFormatter.split("=")[1];
+ }
+ str = str.slice(0, i) + varValue + str.slice(j + repl.syntax.close.length);
+ repl = indexOfReplacementStart(syntaxes, str);
+ i = repl.index;
+ }
+ return str;
+ }
+
function expandRecursive(obj, env, checked) {
checked.push(obj);
for (var key in obj) {
@@ -187,35 +236,9 @@ var PropertyListVariableExpander = (function () {
}
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 ($this.undefinedVariableFunction)
- $this.undefinedVariableFunction(key, varName);
- varValue = "";
- }
- 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);
- changes = true;
- repl = indexOfReplacementStart(syntaxes, value);
- i = repl.index;
- }
- if (changes)
- obj[key] = value;
+ var expandedValue = expandString(key, value, env, []);
+ if (expandedValue !== value)
+ obj[key] = expandedValue;
}
}
expandRecursive(obj, env, []);
@@ -253,19 +276,3 @@ function cleanPropertyList(plist) {
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
index b48b5eb94..a1ede5344 100644
--- a/share/qbs/imports/qbs/ModUtils/utils.js
+++ b/share/qbs/imports/qbs/ModUtils/utils.js
@@ -48,7 +48,7 @@ function mergeCFiles(inputs, outputFilePath)
}
function sanitizedList(list, product, fullPropertyName) {
- if (!Array.isArray(list))
+ if (!(list instanceof Array))
return list;
var filterFunc = function(elem) {
if (typeof elem === "string" && elem.length === 0) {
@@ -87,7 +87,7 @@ function artifactInstalledFilePath(artifact) {
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.filePath.substr(installSourceBase.length));
}
return FileInfo.joinPaths(targetDir, artifact.fileName);
}
@@ -408,7 +408,7 @@ var PropertyValidator = (function () {
errorMessage += "The following properties have invalid values:\n";
lines = [];
for (i in invalidProperties) {
- for (j in invalidProperties[i]) {
+ for (j = 0; j < invalidProperties[i].length; ++j) {
lines.push(this.moduleName + "." + i + ": " + invalidProperties[i][j]);
}
}
@@ -538,8 +538,13 @@ function guessArchitecture(m) {
break;
}
}
- } else if (hasAnyOf(m, ["__i386", "__i386__", "_M_IX86"])) {
+ } else if (hasAnyOf(m, ["__i386", "__i386__", "__386__"])) {
architecture = "x86";
+ } else if (hasAnyOf(m, ["_M_IX86"])) {
+ var code = parseInt(m["_M_IX86"]);
+ architecture = (code < 300) ? "x86_16" : "x86";
+ } else if (hasAnyOf(m, ["_M_I86"])) {
+ architecture = "x86_16";
} else if (hasAnyOf(m, ["__x86_64", "__x86_64__", "__amd64", "_M_X64", "_M_AMD64"])) {
architecture = "x86_64";
if (hasAnyOf(m, ["__x86_64h", "__x86_64h__"]))
@@ -570,6 +575,30 @@ function guessArchitecture(m) {
architecture = "msp430";
} else if (hasAnyOf(m, ["__RL78__"])) {
architecture = "rl78";
+ } else if (hasAnyOf(m, ["__RX__"])) {
+ architecture = "rx";
+ } else if (hasAnyOf(m, ["__v850__"])) {
+ architecture = "v850";
+ } else if (hasAnyOf(m, ["__riscv"])) {
+ architecture = "riscv";
+ } else if (hasAnyOf(m, ["__xtensa__", "__XTENSA__"])) {
+ architecture = "xtensa";
+ } else if (hasAnyOf(m, ["__m68k__"])) {
+ architecture = "m68k";
+ } else if (hasAnyOf(m, ["__m32c__"])) {
+ architecture = "m32c";
+ } else if (hasAnyOf(m, ["__m32r__", "__M32R__"])) {
+ architecture = "m32r";
+ } else if (hasAnyOf(m, ["__sh__", "__SH__"])) {
+ architecture = "sh";
+ } else if (hasAnyOf(m, ["__CR16__"])) {
+ architecture = "cr16";
+ } else if (hasAnyOf(m, ["__mc68hc1x__", "__mc68hc1x"])) {
+ architecture = "hcs12";
+ } else if (hasAnyOf(m, ["__e2k__"])) {
+ architecture = "e2k";
+ } else if (hasAnyOf(m, ["__hppa__"])) {
+ architecture = "hppa";
}
}
@@ -588,15 +617,19 @@ function guessTargetPlatform(m) {
return "vxworks";
if (hasAnyOf(m, ["__APPLE__"]))
return "darwin";
- if (hasAnyOf(m, ["WIN32", "_WIN32", "__WIN32__", "__NT__"]))
+ if (hasAnyOf(m, ["WIN32", "_WIN32", "__WIN32__", "__NT__", "__WINDOWS__", "_WINDOWS"]))
return "windows";
+ if (hasAnyOf(m, ["MSDOS", "__DOS__", "DOS386"]))
+ return "dos";
+ if (hasAnyOf(m, ["__OS2__"]))
+ return "os2";
if (hasAnyOf(m, ["_AIX"]))
return "aix";
if (hasAnyOf(m, ["hpux", "__hpux"]))
return "hpux";
if (hasAnyOf(m, ["__sun", "sun"]))
return "solaris";
- if (hasAnyOf(m, ["__linux__", "__linux"]))
+ if (hasAnyOf(m, ["__linux__", "__linux", "__LINUX__"]))
return "linux";
if (hasAnyOf(m, ["__FreeBSD__", "__DragonFly__", "__FreeBSD_kernel__"]))
return "freebsd";
@@ -610,3 +643,9 @@ function guessTargetPlatform(m) {
return "haiku";
}
}
+
+function toJSLiteral(v) {
+ if (v === undefined)
+ return "undefined";
+ return JSON.stringify(v);
+}
diff --git a/share/qbs/imports/qbs/PathTools/path-tools.js b/share/qbs/imports/qbs/PathTools/path-tools.js
index b2cb63e39..a857a7139 100644
--- a/share/qbs/imports/qbs/PathTools/path-tools.js
+++ b/share/qbs/imports/qbs/PathTools/path-tools.js
@@ -107,6 +107,8 @@ function dynamicLibraryFilePath(product, variantSuffix, version, maxParts) {
version = undefined;
}
+ fileName += product.moduleProperty("cpp", "archSuffix");
+
// Append the suffix (i.e. libqbs.1.0.0.dylib, libqbs.so, qbs.dll)
fileName += product.moduleProperty("cpp", "dynamicLibrarySuffix");
diff --git a/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs b/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs
index 4a86ee935..e50d4251a 100644
--- a/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/AndroidNdkProbe.qbs
@@ -32,12 +32,12 @@
import qbs.Environment
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.TextFile
import "../../../modules/Android/android-utils.js" as AndroidUtils
PathProbe {
// Inputs
- property stringList hostOS: qbs.hostOS
property path sdkPath
environmentPaths: Environment.getEnv("ANDROID_NDK_ROOT")
@@ -45,13 +45,13 @@ PathProbe {
var paths = [];
if (sdkPath)
paths.push(FileInfo.joinPaths(sdkPath, "ndk-bundle"));
- if (qbs.hostOS.contains("windows"))
+ if (Host.os().contains("windows"))
paths.push(FileInfo.joinPaths(Environment.getEnv("LOCALAPPDATA"),
"Android", "sdk", "ndk-bundle"));
- if (qbs.hostOS.contains("macos"))
+ if (Host.os().contains("macos"))
paths.push(FileInfo.joinPaths(Environment.getEnv("HOME"),
"Library", "Android", "sdk", "ndk-bundle"));
- if (qbs.hostOS.contains("linux"))
+ if (Host.os().contains("linux"))
paths.push(FileInfo.joinPaths(Environment.getEnv("HOME"),
"Android", "Sdk", "ndk-bundle"));
return paths;
@@ -83,11 +83,11 @@ PathProbe {
candidatePaths = allPaths;
for (i in allPaths) {
var platforms = [];
- if (hostOS.contains("windows"))
+ if (Host.os().contains("windows"))
platforms.push("windows-x86_64", "windows");
- if (hostOS.contains("darwin"))
+ if (Host.os().contains("darwin"))
platforms.push("darwin-x86_64", "darwin-x86");
- if (hostOS.contains("linux"))
+ if (Host.os().contains("linux"))
platforms.push("linux-x86_64", "linux-x86");
for (j in platforms) {
if (File.exists(FileInfo.joinPaths(allPaths[i], "prebuilt", platforms[j]))) {
diff --git a/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs b/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs
index 38feecdfa..5b777d3e2 100644
--- a/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/AndroidSdkProbe.qbs
@@ -31,17 +31,18 @@
import qbs.Environment
import qbs.File
import qbs.FileInfo
+import qbs.Host
import "../../../modules/Android/sdk/utils.js" as SdkUtils
import "../../../modules/Android/android-utils.js" as AndroidUtils
-BinaryProbe {
+PathProbe {
environmentPaths: Environment.getEnv("ANDROID_HOME")
platformSearchPaths: {
- if (qbs.hostOS.contains("windows"))
+ if (Host.os().contains("windows"))
return [FileInfo.joinPaths(Environment.getEnv("LOCALAPPDATA"), "Android", "sdk")];
- if (qbs.hostOS.contains("macos"))
+ if (Host.os().contains("macos"))
return [FileInfo.joinPaths(Environment.getEnv("HOME"), "Library", "Android", "sdk")];
- if (qbs.hostOS.contains("linux"))
+ if (Host.os().contains("linux"))
return [FileInfo.joinPaths(Environment.getEnv("HOME"), "Android", "Sdk")];
}
@@ -53,21 +54,17 @@ BinaryProbe {
property string platform
configure: {
- var suffixes = nameSuffixes || [""];
var i, allPaths = (environmentPaths || []).concat(platformSearchPaths || []);
candidatePaths = allPaths;
for (i in allPaths) {
- for (var j in suffixes) {
- if (File.exists(FileInfo.joinPaths(allPaths[i],
- "tools", "android" + suffixes[j]))) {
- path = allPaths[i];
- buildToolsVersions = SdkUtils.availableBuildToolsVersions(path)
- buildToolsVersion = buildToolsVersions[buildToolsVersions.length - 1];
- platforms = AndroidUtils.availablePlatforms(path)
- platform = platforms[platforms.length - 1];
- found = true;
- return;
- }
+ if (File.exists(FileInfo.joinPaths(allPaths[i], "build-tools"))) {
+ path = allPaths[i];
+ buildToolsVersions = SdkUtils.availableBuildToolsVersions(path)
+ buildToolsVersion = buildToolsVersions[buildToolsVersions.length - 1];
+ platforms = AndroidUtils.availablePlatforms(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
index 0bb264bd6..52b78df03 100644
--- a/share/qbs/imports/qbs/Probes/BinaryProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/BinaryProbe.qbs
@@ -28,8 +28,10 @@
**
****************************************************************************/
+import qbs.Host
+
PathProbe {
- nameSuffixes: qbs.hostOS.contains("windows") ? [".com", ".exe", ".bat", ".cmd"] : undefined
- platformSearchPaths: hostOS.contains("unix") ? ["/usr/bin", "/usr/local/bin"] : []
+ nameSuffixes: Host.os().contains("windows") ? [".com", ".exe", ".bat", ".cmd"] : undefined
+ platformSearchPaths: Host.os().contains("unix") ? ["/usr/bin", "/usr/local/bin"] : []
platformEnvironmentPaths: [ "PATH" ]
}
diff --git a/share/qbs/imports/qbs/Probes/ClBinaryProbe.qbs b/share/qbs/imports/qbs/Probes/ClBinaryProbe.qbs
new file mode 100644
index 000000000..3b3959017
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/ClBinaryProbe.qbs
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** 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.FileInfo
+import qbs.ModUtils
+import qbs.Utilities
+import "path-probe.js" as PathProbeConfigure
+
+BinaryProbe {
+ // input
+ property string preferredArchitecture
+
+ configure: {
+ var _selectors;
+ var results = PathProbeConfigure.configure(_selectors, names, nameSuffixes, nameFilter,
+ candidateFilter, searchPaths, pathSuffixes,
+ platformSearchPaths, environmentPaths,
+ platformEnvironmentPaths);
+ if (!results.found) {
+ var msvcs = Utilities.installedMSVCs(preferredArchitecture);
+ if (msvcs.length >= 1) {
+ var result = {};
+ result.fileName = "cl.exe";
+ result.path = msvcs[0].binPath;
+ result.filePath = FileInfo.joinPaths(path, fileName);
+ result.candidatePaths = result.filePath;
+ results.found = true;
+ results.files = [result];
+ }
+ }
+
+ found = results.found;
+ allResults = results.files;
+
+ if (allResults.length === 1) {
+ var result = allResults[0];
+ candidatePaths = result.candidatePaths;
+ path = result.path;
+ filePath = result.filePath;
+ fileName = result.fileName;
+ }
+
+ }
+}
diff --git a/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs b/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs
new file mode 100644
index 000000000..8dc01d376
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** 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.FileInfo
+import qbs.ModUtils
+import qbs.Utilities
+import "path-probe.js" as PathProbeConfigure
+
+BinaryProbe {
+ // output
+ property string vcvarsallPath
+
+ configure: {
+ var _selectors;
+ var results = PathProbeConfigure.configure(_selectors, names, nameSuffixes, nameFilter,
+ candidateFilter, searchPaths, pathSuffixes,
+ platformSearchPaths, environmentPaths,
+ platformEnvironmentPaths);
+ var compilerPath;
+ if (results.found)
+ compilerPath = results.files[0].filePath;
+ var compilers = Utilities.installedClangCls(compilerPath);
+ if (compilers.length >= 1) {
+ var result = {};
+ result.fileName = "clang-cl.exe";
+ result.path = compilers[0].toolchainInstallPath;
+ result.filePath = FileInfo.joinPaths(result.path, result.fileName);
+ result.candidatePaths = result.filePath;
+ result.vcvarsallPath = compilers[0].vcvarsallPath;
+ results.found = true;
+ results.files = [result];
+ }
+
+ found = results.found;
+ allResults = results.files;
+
+ if (allResults.length === 1) {
+ var result = allResults[0];
+ candidatePaths = result.candidatePaths;
+ path = result.path;
+ filePath = result.filePath;
+ fileName = result.fileName;
+ vcvarsallPath = result.vcvarsallPath;
+ }
+ }
+}
diff --git a/share/qbs/imports/qbs/Probes/ClangClProbe.qbs b/share/qbs/imports/qbs/Probes/ClangClProbe.qbs
index c7687f0e9..f0d930be5 100644
--- a/share/qbs/imports/qbs/Probes/ClangClProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/ClangClProbe.qbs
@@ -30,6 +30,7 @@
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.Utilities
import "../../../modules/cpp/gcc.js" as Gcc
@@ -39,15 +40,15 @@ PathProbe {
property string compilerFilePath
property string vcvarsallFilePath
property stringList enableDefinesByLanguage
- property string architecture
- property string _nullDevice: qbs.nullDevice
- property string _pathListSeparator: qbs.pathListSeparator
+ property string preferredArchitecture
+ property string winSdkVersion
// Outputs
property int versionMajor
property int versionMinor
property int versionPatch
property stringList includePaths
+ property string architecture
property var buildEnv
property var compilerDefinesByLanguage
@@ -57,9 +58,21 @@ PathProbe {
languages = ["c"];
var info = languages.contains("c")
- ? Utilities.clangClCompilerInfo(compilerFilePath, architecture, vcvarsallFilePath, "c") : {};
+ ? Utilities.clangClCompilerInfo(
+ compilerFilePath,
+ preferredArchitecture,
+ vcvarsallFilePath,
+ "c",
+ winSdkVersion)
+ : {};
var infoCpp = languages.contains("cpp")
- ? Utilities.clangClCompilerInfo(compilerFilePath, architecture, vcvarsallFilePath, "cpp") : {};
+ ? Utilities.clangClCompilerInfo(
+ compilerFilePath,
+ preferredArchitecture,
+ vcvarsallFilePath,
+ "cpp",
+ winSdkVersion)
+ : {};
found = (!languages.contains("c") ||
(!!info && !!info.macros && !!info.buildEnvironment))
&& (!languages.contains("cpp") ||
@@ -81,8 +94,9 @@ PathProbe {
var clangPath = FileInfo.joinPaths(FileInfo.path(compilerFilePath), "clang.exe");
var defaultPaths = Gcc.dumpDefaultPaths(buildEnv, clangPath,
- [], _nullDevice,
- _pathListSeparator, "", "");
+ [], Host.nullDevice(),
+ FileInfo.pathListSeparator(), "", "");
includePaths = defaultPaths.includePaths;
+ architecture = ModUtils.guessArchitecture(macros);
}
}
diff --git a/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs b/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs
new file mode 100644
index 000000000..097b1b6f1
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/ConanfileProbe.qbs
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Richard Weickelt
+** 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.Process
+import qbs.File
+import qbs.FileInfo
+import qbs.TextFile
+import qbs.Utilities
+
+Probe {
+ // Inputs
+ property stringList additionalArguments: []
+ property path conanfilePath
+ property path packageReference
+ property path executable: "conan" + FileInfo.executableSuffix()
+ property stringList generators: ["json"]
+ property var options
+ property var settings
+ property bool verbose: false
+
+ // Output
+ property var dependencies
+ property path generatedFilesPath
+ property var json
+
+ // Internal
+ // Ensure that the probe is re-run automatically whenever conanfile changes
+ // by making a file system property part of the probe's signature.
+ property int _conanfileLastModified: conanfilePath ? File.lastModified(conanfilePath) : 0
+ property path _projectBuildDirectory: project.buildDirectory
+
+ configure: {
+ if (conanfilePath && packageReference)
+ throw("conanfilePath and packageReference must not be defined at the same time.");
+
+ if (!conanfilePath && !packageReference)
+ throw("Either conanfilePath or packageReference must be defined.");
+
+ var reference = packageReference || FileInfo.cleanPath(conanfilePath);
+ console.info("Probing '" + reference + "'. This might take a while...");
+ if (conanfilePath && !File.exists(reference))
+ throw("The conanfile '" + reference + "' does not exist.");
+
+ var args = [
+ "install", reference,
+ ];
+
+ if (options) {
+ if (typeof options !== "object")
+ throw("The property 'options' must be an object.");
+ Object.keys(options).forEach(function(key,index) {
+ args.push("-o");
+ args.push(key + "=" + options[key]);
+ });
+ }
+
+ if (settings) {
+ if (typeof settings !== "object")
+ throw("The property 'settings' must be an object.");
+ Object.keys(settings).forEach(function(key,index) {
+ args.push("-s");
+ args.push(key + "=" + settings[key]);
+ });
+ }
+
+ if (!generators.contains("json"))
+ generators.push("json");
+
+ for (var i = 0; i < generators.length; i++)
+ args = args.concat(["-g", generators[i]]);
+
+ for (var i = 0; i < additionalArguments.length; i++)
+ args.push(additionalArguments[i]);
+
+ generatedFilesPath = FileInfo.cleanPath(_projectBuildDirectory +
+ "/genconan/" +
+ Utilities.getHash(args.join()));
+
+ args = args.concat(["-if", generatedFilesPath]);
+ var p = new Process();
+ p.start(executable, args);
+ while (!p.waitForFinished(500)) {
+ const output = p.readStdOut();
+ if (verbose && output) {
+ console.info(output);
+ }
+ }
+ while (!p.atEnd()) {
+ const output = p.readStdOut();
+ if (verbose && output) {
+ console.info(output);
+ }
+ }
+ if (p.exitCode()) {
+ const errorOutput = p.readStdErr();
+ p.close();
+ throw errorOutput;
+ }
+ p.close();
+
+ if (generators.contains("json")) {
+ if (!File.exists(generatedFilesPath + "/conanbuildinfo.json"))
+ throw("No conanbuildinfo.json has been generated.");
+
+ var jsonFile = new TextFile(generatedFilesPath + "/conanbuildinfo.json", TextFile.ReadOnly);
+ json = JSON.parse(jsonFile.readAll());
+ jsonFile.close();
+
+ dependencies = {};
+ for (var i = 0; i < json.dependencies.length; ++i) {
+ var dep = json.dependencies[i];
+ dependencies[dep.name] = dep;
+ }
+ }
+
+ found = true;
+ }
+}
diff --git a/share/qbs/imports/qbs/Probes/CosmicProbe.qbs b/share/qbs/imports/qbs/Probes/CosmicProbe.qbs
new file mode 100644
index 000000000..7de781e6e
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/CosmicProbe.qbs
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** 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.File
+import "../../../modules/cpp/cosmic.js" as COSMIC
+
+PathProbe {
+ // Inputs
+ property string compilerFilePath
+ property stringList enableDefinesByLanguage
+
+ // Outputs
+ property string architecture
+ property string endianness
+ property int versionMajor
+ property int versionMinor
+ property int versionPatch
+ property stringList includePaths
+ property var compilerDefinesByLanguage
+
+ configure: {
+ compilerDefinesByLanguage = {};
+
+ if (!File.exists(compilerFilePath)) {
+ found = false;
+ return;
+ }
+
+ var languages = enableDefinesByLanguage;
+ if (!languages || languages.length === 0)
+ languages = ["c"];
+
+ // COSMIC compiler support only the C-language.
+ if (!languages.contains("c")) {
+ found = false;
+ return;
+ }
+
+ var macros = COSMIC.dumpMacros(compilerFilePath);
+ if (!macros) {
+ found = false;
+ return;
+ }
+
+ compilerDefinesByLanguage["c"] = macros;
+
+ architecture = COSMIC.guessArchitecture(compilerFilePath);
+ endianness = COSMIC.guessEndianness(architecture);
+ var defaultPaths = COSMIC.dumpDefaultPaths(compilerFilePath, architecture);
+ includePaths = defaultPaths.includePaths;
+
+ var version = COSMIC.dumpVersion(compilerFilePath);
+ if (version) {
+ versionMajor = version.major;
+ versionMinor = version.minor;
+ versionPatch = version.patch;
+ found = !!architecture && !!endianness;
+ }
+ }
+}
diff --git a/share/qbs/imports/qbs/Probes/DmcProbe.qbs b/share/qbs/imports/qbs/Probes/DmcProbe.qbs
new file mode 100644
index 000000000..6a8723a3b
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/DmcProbe.qbs
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** 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.File
+import qbs.ModUtils
+import "../../../modules/cpp/dmc.js" as DMC
+
+PathProbe {
+ // Inputs
+ property string compilerFilePath
+ property stringList enableDefinesByLanguage
+
+ property string _targetPlatform
+ property string _targetArchitecture
+ property string _targetExtender // Only for DOS 16/32 bit.
+
+ // Outputs
+ property string architecture
+ property string targetPlatform
+ property int versionMajor
+ property int versionMinor
+ property int versionPatch
+ property stringList includePaths
+ property var compilerDefinesByLanguage
+
+ configure: {
+ compilerDefinesByLanguage = {};
+
+ if (!File.exists(compilerFilePath)) {
+ found = false;
+ return;
+ }
+
+ var languages = enableDefinesByLanguage;
+ if (!languages || languages.length === 0)
+ languages = ["c"];
+
+ var defaultPathsByLanguage = {};
+ for (var i = 0; i < languages.length; ++i) {
+ var tag = languages[i];
+ compilerDefinesByLanguage[tag] = DMC.dumpMacros(
+ compilerFilePath,
+ _targetPlatform,
+ _targetArchitecture,
+ _targetExtender,
+ tag);
+ var paths = DMC.dumpDefaultPaths(compilerFilePath, tag);
+ defaultPathsByLanguage[tag] = paths;
+ }
+
+ var macros = compilerDefinesByLanguage["c"]
+ || compilerDefinesByLanguage["cpp"];
+
+ architecture = ModUtils.guessArchitecture(macros);
+ targetPlatform = ModUtils.guessTargetPlatform(macros);
+
+ var defaultPaths = defaultPathsByLanguage["cpp"]
+ || defaultPathsByLanguage["c"];
+
+ includePaths = defaultPaths.includePaths;
+
+ var version = DMC.guessVersion(macros);
+ if (version) {
+ versionMajor = version.major;
+ versionMinor = version.minor;
+ versionPatch = version.patch;
+ found = !!architecture;
+ }
+ }
+}
diff --git a/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs b/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs
index e0fe73b40..c3d98a49f 100644
--- a/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/FrameworkProbe.qbs
@@ -35,10 +35,5 @@ PathProbe {
"/Library/Frameworks",
"/System/Library/Frameworks"
])
-
- nameFilter: {
- return function(name) {
- return name + ".framework";
- }
- }
+ nameSuffixes: ".framework"
}
diff --git a/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs b/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs
index 693fb6a01..0872e6cc0 100644
--- a/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/GccBinaryProbe.qbs
@@ -1,8 +1,10 @@
import qbs.Environment
import qbs.FileInfo
+import qbs.Host
import "path-probe.js" as PathProbeConfigure
BinaryProbe {
+ nameSuffixes: undefined // _compilerName already contains ".exe" suffix on Windows
// Inputs
property string _compilerName
property string _toolchainPrefix
@@ -12,7 +14,7 @@ BinaryProbe {
platformSearchPaths: {
var paths = base;
- if (qbs.targetOS.contains("windows") && qbs.hostOS.contains("windows"))
+ if (qbs.targetOS.contains("windows") && Host.os().contains("windows"))
paths.push(FileInfo.joinPaths(
Environment.getEnv("SystemDrive"), "MinGW", "bin"));
return paths;
@@ -48,20 +50,29 @@ BinaryProbe {
configure: {
var selectors;
- var _results = PathProbeConfigure.configure(
+ var results = PathProbeConfigure.configure(
selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths,
- pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths,
- pathListSeparator);
- found = _results.found;
- var resultFile = _results.files[0];
- candidatePaths = resultFile.candidatePaths;
- path = resultFile.path;
- filePath = resultFile.filePath;
- fileName = resultFile.fileName;
- (nameSuffixes || [""]).forEach(function(suffix) {
- var end = _compilerName + suffix;
- if (fileName.endsWith(end))
- tcPrefix = fileName.slice(0, -end.length);
- });
+ pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths);
+
+ found = results.found;
+ if (!found)
+ return;
+
+ var resultsMapper = function(result) {
+ (nameSuffixes || [""]).forEach(function(suffix) {
+ var end = _compilerName + suffix;
+ if (result.fileName.endsWith(end))
+ result.tcPrefix = result.fileName.slice(0, -end.length);
+ });
+ return result;
+ };
+ results.files = results.files.map(resultsMapper);
+ allResults = results.files;
+ var result = results.files[0];
+ candidatePaths = result.candidatePaths;
+ path = result.path;
+ filePath = result.filePath;
+ fileName = result.fileName;
+ tcPrefix = result.tcPrefix;
}
}
diff --git a/share/qbs/imports/qbs/Probes/GccProbe.qbs b/share/qbs/imports/qbs/Probes/GccProbe.qbs
index 9106ff27b..5c6dc1936 100644
--- a/share/qbs/imports/qbs/Probes/GccProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/GccProbe.qbs
@@ -29,6 +29,8 @@
****************************************************************************/
import qbs.File
+import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import "../../../modules/cpp/gcc.js" as Gcc
@@ -39,8 +41,6 @@ PathProbe {
property stringList flags: []
property var environment
- property string _nullDevice: qbs.nullDevice
- property string _pathListSeparator: qbs.pathListSeparator
property string _sysroot: qbs.sysroot
property stringList _targetOS: qbs.targetOS
@@ -63,7 +63,8 @@ PathProbe {
if (fp && File.exists(fp)) {
try {
compilerDefinesByLanguage[languages[i]] = Gcc.dumpMacros(environment, fp,
- flags, _nullDevice,
+ flags,
+ Host.nullDevice(),
languages[i]);
} catch (e) {
// Only throw errors when determining the compiler defines for the C language;
@@ -84,8 +85,8 @@ PathProbe {
|| compilerDefinesByLanguage["objcpp"];
var defaultPaths = Gcc.dumpDefaultPaths(environment, compilerFilePathByLanguage["cpp"] ||
compilerFilePathByLanguage["c"],
- flags, _nullDevice,
- _pathListSeparator, _sysroot, _targetOS);
+ flags, Host.nullDevice(),
+ FileInfo.pathListSeparator(), _sysroot, _targetOS);
found = !!macros && !!defaultPaths;
includePaths = defaultPaths.includePaths;
diff --git a/share/qbs/imports/qbs/Probes/GccVersionProbe.qbs b/share/qbs/imports/qbs/Probes/GccVersionProbe.qbs
index 528719e5b..497835479 100644
--- a/share/qbs/imports/qbs/Probes/GccVersionProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/GccVersionProbe.qbs
@@ -29,6 +29,7 @@
****************************************************************************/
import qbs.File
+import qbs.Host
import "../../../modules/cpp/gcc.js" as Gcc
PathProbe {
@@ -36,7 +37,6 @@ PathProbe {
property string compilerFilePath
property var environment
- property string _nullDevice: qbs.nullDevice
property stringList _toolchain: qbs.toolchain
// Outputs
@@ -50,7 +50,7 @@ PathProbe {
return;
}
- var macros = Gcc.dumpMacros(environment, compilerFilePath, undefined, _nullDevice);
+ var macros = Gcc.dumpMacros(environment, compilerFilePath, undefined, Host.nullDevice());
if (_toolchain.contains("clang")) {
versionMajor = parseInt(macros["__clang_major__"], 10);
diff --git a/share/qbs/imports/qbs/Probes/IarProbe.qbs b/share/qbs/imports/qbs/Probes/IarProbe.qbs
index d261e9065..a0008be47 100644
--- a/share/qbs/imports/qbs/Probes/IarProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/IarProbe.qbs
@@ -33,19 +33,17 @@ import "../../../modules/cpp/iar.js" as IAR
PathProbe {
// Inputs
- property string compilerFilePath;
- property stringList enableDefinesByLanguage;
-
- property string _nullDevice: qbs.nullDevice
+ property string compilerFilePath
+ property stringList enableDefinesByLanguage
// Outputs
- property string architecture;
- property string endianness;
- property int versionMajor;
- property int versionMinor;
- property int versionPatch;
- property stringList includePaths;
- property var compilerDefinesByLanguage;
+ property string architecture
+ property string endianness
+ property int versionMajor
+ property int versionMinor
+ property int versionPatch
+ property stringList includePaths
+ property var compilerDefinesByLanguage
configure: {
compilerDefinesByLanguage = {};
@@ -59,10 +57,13 @@ PathProbe {
if (!languages || languages.length === 0)
languages = ["c"];
+ var defaultPathsByLanguage = {};
for (var i = 0; i < languages.length; ++i) {
var tag = languages[i];
compilerDefinesByLanguage[tag] = IAR.dumpMacros(
compilerFilePath, tag);
+ var paths = IAR.dumpDefaultPaths(compilerFilePath, tag);
+ defaultPathsByLanguage[tag] = paths;
}
var macros = compilerDefinesByLanguage["c"]
@@ -71,10 +72,8 @@ PathProbe {
architecture = IAR.guessArchitecture(macros);
endianness = IAR.guessEndianness(macros);
- // FIXME: Do we need dump the default paths for both C
- // and C++ languages?
- var defaultPaths = IAR.dumpDefaultPaths(
- compilerFilePath, languages[0]);
+ var defaultPaths = defaultPathsByLanguage["cpp"]
+ || defaultPathsByLanguage["c"];
includePaths = defaultPaths.includePaths;
@@ -83,7 +82,7 @@ PathProbe {
versionMajor = version.major;
versionMinor = version.minor;
versionPatch = version.patch;
- found = version && architecture && endianness;
+ found = !!architecture && !!endianness;
}
}
}
diff --git a/share/qbs/imports/qbs/Probes/IncludeProbe.qbs b/share/qbs/imports/qbs/Probes/IncludeProbe.qbs
index 3c1059e64..6201cbc55 100644
--- a/share/qbs/imports/qbs/Probes/IncludeProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/IncludeProbe.qbs
@@ -32,6 +32,8 @@ PathProbe {
platformSearchPaths: qbs.targetOS.contains("unix") ? [
"/usr/include",
"/usr/local/include",
+ "/include",
+ "/app/include",
] : []
platformEnvironmentPaths: {
if (qbs.toolchain.contains('msvc'))
diff --git a/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs b/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs
index 2c06a6a0b..87475cb53 100644
--- a/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/InnoSetupProbe.qbs
@@ -35,18 +35,25 @@ PathProbe {
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
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\",
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\"
];
- 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;
+ var keySuffixes = [
+ "Microsoft\\Windows\\CurrentVersion\\Uninstall\\Inno Setup 5_is1",
+ "Microsoft\\Windows\\CurrentVersion\\Uninstall\\Inno Setup 6_is1"
+ ];
+ for (var i = 0; i < keys.length; ++i) {
+ for (var j = 0; j < keySuffixes.length; ++j) {
+ var key = keys[i] + keySuffixes[j];
+
+ var v = Utilities.getNativeSetting(key, "DisplayVersion");
+ if (v) {
+ path = Utilities.getNativeSetting(key, "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
index 1f414b0fa..efb5a5336 100644
--- a/share/qbs/imports/qbs/Probes/JdkProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/JdkProbe.qbs
@@ -30,14 +30,14 @@
****************************************************************************/
import qbs.Environment
+import qbs.Host
import "../../../modules/java/utils.js" as JavaUtils
PathProbe {
// Inputs
- property stringList hostOS: qbs.hostOS
property string architecture: !_androidCrossCompiling ? qbs.architecture : undefined
property bool _androidCrossCompiling: qbs.targetOS.contains("android")
- && !qbs.hostOS.contains("android")
+ && !Host.os().contains("android")
environmentPaths: Environment.getEnv("JAVA_HOME")
platformSearchPaths: [
@@ -47,7 +47,8 @@ PathProbe {
]
configure: {
- path = JavaUtils.findJdkPath(hostOS, architecture, environmentPaths, platformSearchPaths);
+ path = JavaUtils.findJdkPath(Host.os(), architecture, environmentPaths,
+ platformSearchPaths);
found = !!path;
}
}
diff --git a/share/qbs/imports/qbs/Probes/KeilProbe.qbs b/share/qbs/imports/qbs/Probes/KeilProbe.qbs
index 34afecb64..b123584ad 100644
--- a/share/qbs/imports/qbs/Probes/KeilProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/KeilProbe.qbs
@@ -29,23 +29,22 @@
****************************************************************************/
import qbs.File
+import qbs.Host
import "../../../modules/cpp/keil.js" as KEIL
PathProbe {
// Inputs
- property string compilerFilePath;
- property stringList enableDefinesByLanguage;
-
- property string _nullDevice: qbs.nullDevice
+ property string compilerFilePath
+ property stringList enableDefinesByLanguage
// Outputs
- property string architecture;
- property string endianness;
- property int versionMajor;
- property int versionMinor;
- property int versionPatch;
- property stringList includePaths;
- property var compilerDefinesByLanguage;
+ property string architecture
+ property string endianness
+ property int versionMajor
+ property int versionMinor
+ property int versionPatch
+ property stringList includePaths
+ property var compilerDefinesByLanguage
configure: {
compilerDefinesByLanguage = {};
@@ -62,7 +61,7 @@ PathProbe {
for (var i = 0; i < languages.length; ++i) {
var tag = languages[i];
compilerDefinesByLanguage[tag] = KEIL.dumpMacros(
- compilerFilePath, tag, _nullDevice);
+ compilerFilePath, tag, Host.nullDevice());
}
var macros = compilerDefinesByLanguage["c"]
@@ -72,7 +71,7 @@ PathProbe {
endianness = KEIL.guessEndianness(macros);
var defaultPaths = KEIL.dumpDefaultPaths(
- compilerFilePath, architecture);
+ compilerFilePath, Host.nullDevice());
includePaths = defaultPaths.includePaths;
@@ -81,7 +80,7 @@ PathProbe {
versionMajor = version.major;
versionMinor = version.minor;
versionPatch = version.patch;
- found = version.found && architecture && endianness;
+ found = !!architecture && !!endianness;
}
}
}
diff --git a/share/qbs/imports/qbs/Probes/LibraryProbe.qbs b/share/qbs/imports/qbs/Probes/LibraryProbe.qbs
index 26787d1b4..7631eb441 100644
--- a/share/qbs/imports/qbs/Probes/LibraryProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/LibraryProbe.qbs
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2018 Ivan Komissarov.
+** Copyright (C) 2018 Ivan Komissarov (abbapoh@gmail.com)
** Contact: http://www.qt.io/licensing
**
** This file is part of Qbs.
@@ -29,6 +29,7 @@
****************************************************************************/
PathProbe {
+ property string endianness
nameSuffixes: {
if (qbs.targetOS.contains("windows"))
return [".lib"];
@@ -36,10 +37,34 @@ PathProbe {
return [".dylib", ".a"];
return [".so", ".a"];
}
- platformSearchPaths: qbs.targetOS.contains("unix") ? [
- "/usr/lib",
- "/usr/local/lib",
- ] : []
+ platformSearchPaths: {
+ var result = [];
+ if (qbs.targetOS.contains("unix")) {
+ if (qbs.targetOS.contains("linux") && qbs.architecture) {
+ if (qbs.architecture === "armv7")
+ result = ["/usr/lib/arm-linux-gnueabihf"]
+ else if (qbs.architecture === "arm64")
+ result = ["/usr/lib/aarch64-linux-gnu"]
+ else if (qbs.architecture === "mips" && endianness === "big")
+ result = ["/usr/lib/mips-linux-gnu"]
+ else if (qbs.architecture === "mips" && endianness === "little")
+ result = ["/usr/lib/mipsel-linux-gnu"]
+ else if (qbs.architecture === "mips64")
+ result = ["/usr/lib/mips64el-linux-gnuabi64"]
+ else if (qbs.architecture === "ppc")
+ result = ["/usr/lib/powerpc-linux-gnu"]
+ else if (qbs.architecture === "ppc64")
+ result = ["/usr/lib/powerpc64le-linux-gnu"]
+ else if (qbs.architecture === "x86_64")
+ result = ["/usr/lib64", "/usr/lib/x86_64-linux-gnu"]
+ else if (qbs.architecture === "x86")
+ result = ["/usr/lib32", "/usr/lib/i386-linux-gnu"]
+ }
+ result = result.concat(["/usr/lib", "/usr/local/lib", "/lib", "/app/lib"]);
+ }
+
+ return result;
+ }
nameFilter: {
if (qbs.targetOS.contains("unix")) {
return function(name) {
diff --git a/share/qbs/imports/qbs/Probes/MsvcProbe.qbs b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs
index 2d5faecdd..d3624e010 100644
--- a/share/qbs/imports/qbs/Probes/MsvcProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/MsvcProbe.qbs
@@ -38,6 +38,7 @@ PathProbe {
property string compilerFilePath
property stringList enableDefinesByLanguage
property string preferredArchitecture
+ property string winSdkVersion
// Outputs
property string architecture
@@ -54,9 +55,9 @@ PathProbe {
languages = ["c"];
var info = languages.contains("c")
- ? Utilities.msvcCompilerInfo(compilerFilePath, "c") : {};
+ ? Utilities.msvcCompilerInfo(compilerFilePath, "c", winSdkVersion) : {};
var infoCpp = languages.contains("cpp")
- ? Utilities.msvcCompilerInfo(compilerFilePath, "cpp") : {};
+ ? Utilities.msvcCompilerInfo(compilerFilePath, "cpp", winSdkVersion) : {};
found = (!languages.contains("c") ||
(!!info && !!info.macros && !!info.buildEnvironment))
&& (!languages.contains("cpp") ||
diff --git a/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs b/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs
index b0162c715..520e57f56 100644
--- a/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/NodeJsProbe.qbs
@@ -30,12 +30,13 @@
import qbs.Environment
import qbs.FileInfo
+import qbs.Host
BinaryProbe {
names: ["node", "nodejs"]
platformSearchPaths: {
var paths = base;
- if (qbs.hostOS.contains("windows")) {
+ if (Host.os().contains("windows")) {
var env32 = Environment.getEnv("PROGRAMFILES(X86)");
var env64 = Environment.getEnv("PROGRAMFILES");
if (env64 === env32 && env64.endsWith(" (x86)"))
diff --git a/share/qbs/imports/qbs/Probes/NpmProbe.qbs b/share/qbs/imports/qbs/Probes/NpmProbe.qbs
index f6a99e826..08490065a 100644
--- a/share/qbs/imports/qbs/Probes/NpmProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/NpmProbe.qbs
@@ -28,6 +28,8 @@
**
****************************************************************************/
+import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import "path-probe.js" as PathProbeConfigure
import "../../../modules/nodejs/nodejs.js" as NodeJs
@@ -50,25 +52,30 @@ NodeJsProbe {
var selectors;
var results = PathProbeConfigure.configure(
selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths,
- pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths,
- pathListSeparator);
+ pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths);
- var v = new ModUtils.EnvironmentVariable("PATH", pathListSeparator,
- hostOS.contains("windows"));
+ var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(),
+ Host.os().contains("windows"));
v.prepend(interpreterPath);
- var result = results.files[0];
- result.npmBin = results.found
- ? NodeJs.findLocation(result.filePath, "bin", v.value)
- : undefined;
- result.npmRoot = results.found
- ? NodeJs.findLocation(result.filePath, "root", v.value)
- : undefined;
- result.npmPrefix = results.found
- ? NodeJs.findLocation(result.filePath, "prefix", v.value)
- : undefined;
+ var resultsMapper = function(result) {
+ result.npmBin = result.found
+ ? NodeJs.findLocation(result.filePath, "bin", v.value)
+ : undefined;
+ result.npmRoot = result.found
+ ? NodeJs.findLocation(result.filePath, "root", v.value)
+ : undefined;
+ result.npmPrefix = result.found
+ ? NodeJs.findLocation(result.filePath, "prefix", v.value)
+ : undefined;
+ return result;
+ };
+ results.files = results.files.map(resultsMapper);
found = results.found;
+ allResults = results.files;
+
+ var result = results.files[0];
candidatePaths = result.candidatePaths;
path = result.path;
filePath = result.filePath;
diff --git a/share/qbs/imports/qbs/Probes/PathProbe.qbs b/share/qbs/imports/qbs/Probes/PathProbe.qbs
index d0edea682..dc3b32ab7 100644
--- a/share/qbs/imports/qbs/Probes/PathProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/PathProbe.qbs
@@ -29,6 +29,7 @@
****************************************************************************/
import "path-probe.js" as PathProbeConfigure
+import qbs.Host
import qbs.ModUtils
Probe {
@@ -38,15 +39,11 @@ Probe {
property var nameFilter
property var candidateFilter
property varList selectors
- property pathList pathPrefixes
property pathList searchPaths
property stringList pathSuffixes
- property pathList platformSearchPaths: hostOS.contains("unix") ? ['/usr', '/usr/local'] : []
- property pathList platformPaths
+ property pathList platformSearchPaths: Host.os().contains("unix") ? ['/usr', '/usr/local'] : []
property stringList environmentPaths
property stringList platformEnvironmentPaths
- property stringList hostOS: qbs.hostOS
- property string pathListSeparator: qbs.pathListSeparator
// Output
property stringList candidatePaths
@@ -57,23 +54,19 @@ Probe {
property varList allResults
configure: {
- if (pathPrefixes)
- console.warn("PathProbe.pathPrefixes is deprecated, use searchPaths instead");
- if (platformPaths)
- console.warn("PathProbe.platformPaths is deprecated, use platformSearchPaths instead");
- var _searchPaths = ModUtils.concatAll(pathPrefixes, searchPaths);
- var _platformSearchPaths = ModUtils.concatAll(platformPaths, platformSearchPaths);
var results = PathProbeConfigure.configure(selectors, names, nameSuffixes, nameFilter,
- candidateFilter, _searchPaths, pathSuffixes,
- _platformSearchPaths, environmentPaths,
- platformEnvironmentPaths, pathListSeparator);
+ candidateFilter, searchPaths, pathSuffixes,
+ platformSearchPaths, environmentPaths,
+ platformEnvironmentPaths);
found = results.found;
allResults = results.files;
- var result = allResults[0];
- candidatePaths = result.candidatePaths;
- path = result.path;
- filePath = result.filePath;
- fileName = result.fileName;
+ if (allResults.length === 1) {
+ var result = allResults[0];
+ candidatePaths = result.candidatePaths;
+ 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
index b295c7441..0fe81c3cc 100644
--- a/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/PkgConfigProbe.qbs
@@ -28,6 +28,7 @@
**
****************************************************************************/
+import qbs.Host
import qbs.Process
import qbs.FileInfo
@@ -42,7 +43,6 @@ Probe {
property string maxVersion
property bool forStaticBuild: false
property stringList libDirs // Full, non-sysrooted paths, mirroring the environment variable
- property string pathListSeparator: qbs.pathListSeparator
// Output
property stringList cflags // Unmodified --cflags output
@@ -60,6 +60,7 @@ Probe {
if (!packageNames || packageNames.length === 0)
throw 'PkgConfigProbe.packageNames must be specified.';
var p = new Process();
+ var stdout;
try {
var libDirsToSet = libDirs;
if (sysroot) {
@@ -72,7 +73,7 @@ Probe {
}
}
if (libDirsToSet)
- p.setEnv("PKG_CONFIG_LIBDIR", libDirsToSet.join(pathListSeparator));
+ p.setEnv("PKG_CONFIG_LIBDIR", libDirsToSet.join(FileInfo.pathListSeparator()));
var versionArgs = [];
if (minVersion !== undefined)
versionArgs.push("--atleast-version=" + minVersion);
@@ -84,16 +85,22 @@ Probe {
&& p.exec(executable, versionArgs.concat(packageNames)) !== 0) {
return;
}
+
+ // protobuf is reserved as qbs module name, which depends on the protobuflib module
+ packageNames = packageNames.map(function(name) {
+ return name === "protobuflib" ? "protobuf" : name;
+ });
+
var args = packageNames;
if (p.exec(executable, args.concat([ '--cflags' ])) === 0) {
- cflags = p.readStdOut().trim();
- cflags = cflags ? cflags.split(/\s/) : [];
+ stdout = p.readStdOut().trim();
+ cflags = stdout ? stdout.split(/\s/): [];
var libsArgs = args.concat("--libs");
if (forStaticBuild)
libsArgs.push("--static");
if (p.exec(executable, libsArgs) === 0) {
- libs = p.readStdOut().trim();
- libs = libs ? libs.split(/\s/) : [];
+ stdout = p.readStdOut().trim();
+ libs = stdout ? stdout.split(/\s/): [];
if (p.exec(executable, [packageNames[0]].concat([ '--modversion' ])) === 0) {
modversion = p.readStdOut().trim();
found = true;
diff --git a/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs b/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs
new file mode 100644
index 000000000..5acec6bc0
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/QbsPkgConfigProbe.qbs
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** 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-pkg-config-probe.js" as PkgConfigProbeConfigure
+
+Probe {
+ // Inputs
+
+ property string _executableFilePath
+ property stringList _extraPaths
+ property stringList _libDirs
+ property bool _staticMode: false
+ property bool _definePrefix: false
+
+ property path _sysroot
+
+ // Output
+ property var packages
+ property var packagesByModuleName
+ property var brokenPackages
+ property stringList qmakePaths
+
+ configure: {
+ var result = PkgConfigProbeConfigure.configure(
+ _executableFilePath,
+ _extraPaths,
+ _libDirs,
+ _staticMode,
+ _definePrefix,
+ _sysroot);
+ packages = result.packages;
+ packagesByModuleName = result.packagesByModuleName;
+ brokenPackages = result.brokenPackages;
+ qmakePaths = result.qmakePaths;
+ found = true;
+ }
+}
diff --git a/share/qbs/imports/qbs/Probes/QmakeProbe.qbs b/share/qbs/imports/qbs/Probes/QmakeProbe.qbs
new file mode 100644
index 000000000..c50c6c851
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/QmakeProbe.qbs
@@ -0,0 +1,41 @@
+
+/****************************************************************************
+**
+** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com)
+** 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 "qmake-probe.js" as QmakeProbeConfigure
+
+Probe {
+ property stringList qmakePaths
+ property varList qtInfos
+
+ configure: {
+ qtInfos = QmakeProbeConfigure.configure(qmakePaths);
+ }
+}
diff --git a/share/qbs/imports/qbs/Probes/SdccProbe.qbs b/share/qbs/imports/qbs/Probes/SdccProbe.qbs
index 3595bb158..ae616fa27 100644
--- a/share/qbs/imports/qbs/Probes/SdccProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/SdccProbe.qbs
@@ -33,17 +33,18 @@ import "../../../modules/cpp/sdcc.js" as SDCC
PathProbe {
// Inputs
- property string compilerFilePath;
- property string preferredArchitecture;
+ property string compilerFilePath
+ property stringList enableDefinesByLanguage
+ property string preferredArchitecture
// Outputs
- property string architecture;
- property string endianness;
- property int versionMajor;
- property int versionMinor;
- property int versionPatch;
- property stringList includePaths;
- property var compilerDefinesByLanguage;
+ property string architecture
+ property string endianness
+ property int versionMajor
+ property int versionMinor
+ property int versionPatch
+ property stringList includePaths
+ property var compilerDefinesByLanguage
configure: {
compilerDefinesByLanguage = {};
@@ -53,9 +54,22 @@ PathProbe {
return;
}
+ var languages = enableDefinesByLanguage;
+ if (!languages || languages.length === 0)
+ languages = ["c"];
+
+ // SDCC compiler support only the C-language.
+ if (!languages.contains("c")) {
+ found = false;
+ return;
+ }
+
var macros = SDCC.dumpMacros(compilerFilePath, preferredArchitecture);
+ if (!macros) {
+ found = false;
+ return;
+ }
- // SDCC it is only the C language compiler.
compilerDefinesByLanguage["c"] = macros;
architecture = SDCC.guessArchitecture(macros);
@@ -65,9 +79,11 @@ PathProbe {
includePaths = defaultPaths.includePaths;
var version = SDCC.guessVersion(macros);
- versionMajor = version.major;
- versionMinor = version.minor;
- versionPatch = version.patch;
- found = version.found;
+ if (version) {
+ versionMajor = version.major;
+ versionMinor = version.minor;
+ versionPatch = version.patch;
+ found = !!architecture && !!endianness;
+ }
}
}
diff --git a/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs b/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs
index a35e555cc..f494d6012 100644
--- a/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/TypeScriptProbe.qbs
@@ -30,6 +30,7 @@
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import "path-probe.js" as PathProbeConfigure
import "../../../modules/typescript/typescript.js" as TypeScript
@@ -60,23 +61,29 @@ BinaryProbe {
var selectors;
var results = PathProbeConfigure.configure(
selectors, names, nameSuffixes, nameFilter, candidateFilter, searchPaths,
- pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths,
- pathListSeparator);
+ pathSuffixes, platformSearchPaths, environmentPaths, platformEnvironmentPaths);
- var v = new ModUtils.EnvironmentVariable("PATH", pathListSeparator,
- hostOS.contains("windows"));
+ var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(),
+ Host.os().contains("windows"));
v.prepend(interpreterPath);
- var result = results.files[0];
- result.version = results.found
- ? TypeScript.findTscVersion(result.filePath, v.value)
- : undefined;
- if (FileInfo.fromNativeSeparators(packageManagerBinPath) !== result.path ||
- !File.exists(FileInfo.fromNativeSeparators(packageManagerRootPath, "typescript"))) {
- result = { found: false };
- }
+ var resultsMapper = function(result) {
+ result.version = result.found
+ ? TypeScript.findTscVersion(result.filePath, v.value)
+ : undefined;
+ if (FileInfo.fromNativeSeparators(packageManagerBinPath) !== result.path ||
+ !File.exists(
+ FileInfo.fromNativeSeparators(packageManagerRootPath, "typescript"))) {
+ result = { found: false };
+ }
+ return result;
+ };
+ results.files = results.files.map(resultsMapper);
found = results.found;
+ allResults = results.files;
+
+ var result = results.files[0];
candidatePaths = result.candidatePaths;
path = result.path;
filePath = result.filePath;
diff --git a/share/qbs/imports/qbs/Probes/WatcomProbe.qbs b/share/qbs/imports/qbs/Probes/WatcomProbe.qbs
new file mode 100644
index 000000000..3ea22acb3
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/WatcomProbe.qbs
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com>
+** 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.File
+import qbs.Host
+import qbs.ModUtils
+import "../../../modules/cpp/watcom.js" as WATCOM
+
+PathProbe {
+ // Inputs
+ property string compilerFilePath
+ property stringList enableDefinesByLanguage
+
+ property string _pathListSeparator
+ property string _toolchainInstallPath
+ property string _targetPlatform
+ property string _targetArchitecture
+
+ // Outputs
+ property string architecture
+ property string endianness
+ property string targetPlatform
+ property int versionMajor
+ property int versionMinor
+ property int versionPatch
+ property stringList includePaths
+ property var compilerDefinesByLanguage
+ property var environment
+
+ configure: {
+ compilerDefinesByLanguage = {};
+
+ if (!File.exists(compilerFilePath)) {
+ found = false;
+ return;
+ }
+
+ var languages = enableDefinesByLanguage;
+ if (!languages || languages.length === 0)
+ languages = ["c"];
+
+ environment = WATCOM.guessEnvironment(Host.os(), _targetPlatform, _targetArchitecture,
+ _toolchainInstallPath, _pathListSeparator);
+
+ includePaths = environment["INCLUDE"].split(_pathListSeparator).filter(function(path) {
+ return File.exists(path);
+ });
+
+ for (var i = 0; i < languages.length; ++i) {
+ var tag = languages[i];
+ compilerDefinesByLanguage[tag] = WATCOM.dumpMacros(
+ environment, compilerFilePath,
+ _targetPlatform, _targetArchitecture, tag);
+ }
+
+ var macros = compilerDefinesByLanguage["c"]
+ || compilerDefinesByLanguage["cpp"];
+
+ endianness = macros["__BIG_ENDIAN"] ? "big" : "little";
+ architecture = ModUtils.guessArchitecture(macros);
+ targetPlatform = ModUtils.guessTargetPlatform(macros);
+
+ var version = WATCOM.guessVersion(macros);
+ if (version) {
+ versionMajor = version.major;
+ versionMinor = version.minor;
+ versionPatch = version.patch;
+ found = !!architecture && !!targetPlatform;
+ }
+ }
+}
diff --git a/share/qbs/imports/qbs/Probes/XcodeLocationProbe.qbs b/share/qbs/imports/qbs/Probes/XcodeLocationProbe.qbs
new file mode 100644
index 000000000..3401315f7
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/XcodeLocationProbe.qbs
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** 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.Process
+
+Probe {
+ property path developerPath
+ configure: {
+ var p = new Process();
+ try {
+ p.exec("/usr/bin/xcode-select", ["--print-path"], true);
+ developerPath = p.readStdOut().trim();
+ } catch (e) {
+ developerPath = "/Applications/Xcode.app/Contents/Developer";
+ } finally {
+ p.close();
+ found = true;
+ }
+ }
+}
diff --git a/share/qbs/imports/qbs/Probes/XcodeProbe.qbs b/share/qbs/imports/qbs/Probes/XcodeProbe.qbs
index e0ed99346..9a0d01072 100644
--- a/share/qbs/imports/qbs/Probes/XcodeProbe.qbs
+++ b/share/qbs/imports/qbs/Probes/XcodeProbe.qbs
@@ -48,6 +48,7 @@ Probe {
// Outputs
property var architectureSettings
property var availableSdks
+ property var platformSettings
property string xcodeVersion
configure: {
@@ -88,7 +89,7 @@ Probe {
architectureSettings = {};
var archSpecsPath = Xcode.archsSpecsPath(xcodeVersion, targetOS, platformType,
- platformPath, devicePlatformPath);
+ platformPath, devicePlatformPath, developerPath);
var archSpecsReader = new Xcode.XcodeArchSpecsReader(archSpecsPath);
archSpecsReader.getArchitectureSettings().map(function (setting) {
var val = archSpecsReader.getArchitectureSettingValue(setting);
@@ -97,6 +98,8 @@ Probe {
});
availableSdks = Xcode.sdkInfoList(sdksPath);
+ var platformInfoPlist = FileInfo.joinPaths(platformPath, "Info.plist");
+ platformSettings = Xcode.platformInfo(platformInfoPlist);
found = !!xcodeVersion;
}
}
diff --git a/share/qbs/imports/qbs/Probes/path-probe.js b/share/qbs/imports/qbs/Probes/path-probe.js
index a997f77f2..1858f5222 100644
--- a/share/qbs/imports/qbs/Probes/path-probe.js
+++ b/share/qbs/imports/qbs/Probes/path-probe.js
@@ -36,16 +36,16 @@ var ModUtils = require("qbs.ModUtils");
function asStringList(key, value) {
if (typeof(value) === "string")
return [value];
- if (Array.isArray(value))
+ if (value instanceof Array)
return value;
throw key + " must be a string or a stringList";
}
-function canonicalSelectors(selectors) {
+function canonicalSelectors(selectors, nameSuffixes) {
var mapper = function(selector) {
if (typeof(selector) === "string")
return {names : [selector]};
- if (Array.isArray(selector))
+ if (selector instanceof Array)
return {names : selector};
// dict
if (!selector.names)
@@ -53,14 +53,27 @@ function canonicalSelectors(selectors) {
selector.names = asStringList("names", selector.names);
if (selector.nameSuffixes)
selector.nameSuffixes = asStringList("nameSuffixes", selector.nameSuffixes);
+ else
+ selector.nameSuffixes = nameSuffixes;
return selector;
};
return selectors.map(mapper);
}
+function pathsFromEnvs(envs, pathListSeparator) {
+ envs = envs || [];
+ var result = [];
+ for (var i = 0; i < envs.length; ++i) {
+ var value = Environment.getEnv(envs[i]) || '';
+ if (value.length > 0)
+ result = result.concat(value.split(pathListSeparator));
+ }
+ return result;
+}
+
function configure(selectors, names, nameSuffixes, nameFilter, candidateFilter,
searchPaths, pathSuffixes, platformSearchPaths, environmentPaths,
- platformEnvironmentPaths, pathListSeparator) {
+ platformEnvironmentPaths) {
var result = { found: false, files: [] };
if (!selectors && !names)
throw '"names" or "selectors" must be specified';
@@ -70,7 +83,7 @@ function configure(selectors, names, nameSuffixes, nameFilter, candidateFilter,
{names: names, nameSuffixes: nameSuffixes}
];
} else {
- selectors = canonicalSelectors(selectors);
+ selectors = canonicalSelectors(selectors, nameSuffixes);
}
if (nameFilter) {
@@ -88,14 +101,11 @@ function configure(selectors, names, nameSuffixes, nameFilter, candidateFilter,
});
// FIXME: Suggest how to obtain paths from system
- var _paths = ModUtils.concatAll(searchPaths, platformSearchPaths);
- // 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 _paths = ModUtils.concatAll(
+ pathsFromEnvs(environmentPaths, FileInfo.pathListSeparator()),
+ searchPaths,
+ pathsFromEnvs(platformEnvironmentPaths, FileInfo.pathListSeparator()),
+ platformSearchPaths);
var _suffixes = ModUtils.concatAll('', pathSuffixes);
_paths = _paths.map(function(p) { return FileInfo.fromNativeSeparators(p); });
_suffixes = _suffixes.map(function(p) { return FileInfo.fromNativeSeparators(p); });
diff --git a/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js b/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js
new file mode 100644
index 000000000..d382dfb02
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/qbs-pkg-config-probe.js
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com)
+** 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 = require("qbs.Environment");
+var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var PkgConfig = require("qbs.PkgConfig");
+var ProviderUtils = require("qbs.ProviderUtils");
+var Process = require("qbs.Process");
+
+function getQmakePaths(pkg) {
+ var packageName = pkg.baseFileName;
+ if (packageName === "QtCore"
+ || packageName === "Qt5Core"
+ || packageName === "Qt6Core") {
+ var binDir = pkg.variables["bindir"] || pkg.variables["host_bins"];
+ if (!binDir) {
+ if (packageName === "QtCore") { // Qt4 does not have host_bins
+ var mocLocation = pkg.variables["moc_location"];
+ if (!mocLocation) {
+ console.warn("No moc_location variable in " + packageName);
+ return;
+ }
+ binDir = FileInfo.path(mocLocation);
+ } else {
+ console.warn("No 'bindir' or 'host_bins' variable in " + packageName);
+ return;
+ }
+ }
+ var suffix = FileInfo.executableSuffix();
+ return [FileInfo.joinPaths(binDir, "qmake" + suffix)];
+ }
+}
+
+function configure(
+ executableFilePath, extraPaths, libDirs, staticMode, definePrefix, sysroot) {
+
+ var result = {};
+ result.packages = [];
+ result.packagesByModuleName = {};
+ result.brokenPackages = [];
+ result.qtInfos = [];
+
+ var options = {};
+ options.libDirs = libDirs;
+ options.sysroot = sysroot;
+ options.definePrefix = definePrefix;
+ if (options.sysroot)
+ options.allowSystemLibraryPaths = true;
+ options.staticMode = staticMode;
+ options.extraPaths = extraPaths;
+ if (options.sysroot && !options.libDirs) {
+ options.libDirs = [
+ options.sysroot + "/usr/lib/pkgconfig",
+ options.sysroot + "/usr/share/pkgconfig"
+ ];
+ }
+ if (!options.libDirs) {
+ // if we have pkg-config/pkgconf installed, let's ask it for its search paths (since
+ // built-in search paths can differ between platforms)
+ if (executableFilePath) {
+ var p = new Process()
+ if (p.exec(executableFilePath, ['pkg-config', '--variable=pc_path']) === 0) {
+ var stdout = p.readStdOut().trim();
+ options.libDirs = stdout ? stdout.split(FileInfo.pathListSeparator()): [];
+ var installDir = FileInfo.path(executableFilePath);
+ options.libDirs = options.libDirs.map(function(path){
+ if (FileInfo.isAbsolutePath(path))
+ return path;
+ return FileInfo.cleanPath(FileInfo.joinPaths(installDir, path));
+ });
+ }
+ }
+ }
+ var pkgConfig = new PkgConfig(options);
+ result.packages = pkgConfig.packages();
+ for (var packageName in result.packages) {
+ var pkg = result.packages[packageName];
+ var moduleName = ProviderUtils.pkgConfigToModuleName(packageName);
+ result.packagesByModuleName[moduleName] = pkg;
+
+ if (packageName.startsWith("Qt")) {
+ if (!sysroot) {
+ var qmakePaths = getQmakePaths(pkg);
+ if (qmakePaths !== undefined)
+ result.qmakePaths = qmakePaths;
+ }
+ }
+ }
+ return result;
+}
diff --git a/share/qbs/imports/qbs/Probes/qmake-probe.js b/share/qbs/imports/qbs/Probes/qmake-probe.js
new file mode 100644
index 000000000..746914e20
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/qmake-probe.js
@@ -0,0 +1,1256 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+var Environment = require("qbs.Environment");
+var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var Host = require("qbs.Host");
+var Process = require("qbs.Process");
+var ProviderUtils = require("qbs.ProviderUtils");
+var TextFile = require("qbs.TextFile");
+var Utilities = require("qbs.Utilities");
+
+function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }); }
+
+function getQmakeFilePaths(qmakeFilePaths) {
+ if (qmakeFilePaths && qmakeFilePaths.length > 0)
+ return qmakeFilePaths;
+ console.info("Detecting Qt installations...");
+ var filePaths = [];
+ var pathValue = Environment.getEnv("PATH");
+ if (pathValue) {
+ var dirs = splitNonEmpty(pathValue, FileInfo.pathListSeparator());
+ for (var i = 0; i < dirs.length; ++i) {
+ var candidate = FileInfo.joinPaths(dirs[i], "qmake" + FileInfo.executableSuffix());
+ var canonicalCandidate = FileInfo.canonicalPath(candidate);
+ if (!canonicalCandidate || !File.exists(canonicalCandidate))
+ continue;
+ if (FileInfo.completeBaseName(canonicalCandidate) !== "qtchooser")
+ candidate = canonicalCandidate;
+ if (!filePaths.contains(candidate)) {
+ console.info("Found Qt at '" + FileInfo.toNativeSeparators(candidate) + "'.");
+ filePaths.push(candidate);
+ }
+ }
+ }
+ if (filePaths.length === 0) {
+ console.warn("Could not find any qmake executables in PATH. Either make sure a qmake "
+ + "executable is present in PATH or set the moduleProviders.Qt.qmakeFilePaths property "
+ + "to point to a qmake executable.");
+ }
+ return filePaths;
+}
+
+function queryQmake(qmakeFilePath) {
+ var qmakeProcess = new Process;
+ qmakeProcess.exec(qmakeFilePath, ["-query"]);
+ if (qmakeProcess.exitCode() !== 0) {
+ throw "The qmake executable '" + FileInfo.toNativeSeparators(qmakeFilePath)
+ + "' failed with exit code " + qmakeProcess.exitCode() + ".";
+ }
+ var queryResult = {};
+ while (!qmakeProcess.atEnd()) {
+ var line = qmakeProcess.readLine();
+ var index = (line || "").indexOf(":");
+ if (index !== -1)
+ queryResult[line.slice(0, index)] = line.slice(index + 1).trim();
+ }
+ return queryResult;
+}
+
+function pathQueryValue(queryResult, key) {
+ var p = queryResult[key];
+ if (p)
+ return FileInfo.fromNativeSeparators(p);
+}
+
+function readFileContent(filePath) {
+ var f = new TextFile(filePath, TextFile.ReadOnly);
+ var content = f.readAll();
+ f.close();
+ return content;
+}
+
+// TODO: Don't do the split every time...
+function configVariable(configContent, key) {
+ var configContentLines = configContent.split('\n');
+ var regexp = new RegExp("^\\s*" + key + "\\s*\\+{0,1}=(.*)");
+ for (var i = 0; i < configContentLines.length; ++i) {
+ var line = configContentLines[i];
+ var match = regexp.exec(line);
+ if (match)
+ return match[1].trim();
+ }
+}
+
+function configVariableItems(configContent, key) {
+ return splitNonEmpty(configVariable(configContent, key), ' ');
+}
+
+function msvcCompilerVersionForYear(year) {
+ var mapping = {
+ "2005": "14", "2008": "15", "2010": "16", "2012": "17", "2013": "18", "2015": "19",
+ "2017": "19.1", "2019": "19.2"
+ };
+ return mapping[year];
+}
+
+function msvcCompilerVersionFromMkspecName(mkspecName) {
+ return msvcCompilerVersionForYear(mkspecName.slice(msvcPrefix().length));
+}
+
+function addQtBuildVariant(qtProps, buildVariantName) {
+ if (qtProps.qtConfigItems.contains(buildVariantName))
+ qtProps.buildVariant.push(buildVariantName);
+}
+
+function checkForStaticBuild(qtProps) {
+ if (qtProps.qtMajorVersion >= 5)
+ return qtProps.qtConfigItems.contains("static");
+ if (qtProps.frameworkBuild)
+ return false; // there are no Qt4 static frameworks
+ var isWin = qtProps.mkspecName.startsWith("win");
+ var libDir = isWin ? qtProps.binaryPath : qtProps.libraryPath;
+ var coreLibFiles = File.directoryEntries(libDir, File.Files)
+ .filter(function(fp) { return fp.contains("Core"); });
+ if (coreLibFiles.length === 0)
+ throw "Could not determine whether Qt is a static build.";
+ for (var i = 0; i < coreLibFiles.length; ++i) {
+ if (Utilities.isSharedLibrary(coreLibFiles[i]))
+ return false;
+ }
+ return true;
+}
+
+function guessMinimumWindowsVersion(qtProps) {
+ if (qtProps.mkspecName.startsWith("winrt-"))
+ return "10.0";
+ if (!ProviderUtils.isDesktopWindowsQt(qtProps))
+ return "";
+ if (qtProps.qtMajorVersion >= 6)
+ return "10.0";
+ if (qtProps.architecture === "x86_64" || qtProps.architecture === "ia64")
+ return "5.2"
+ var match = qtProps.mkspecName.match(/^win32-msvc(\d+)$/);
+ if (match) {
+ var msvcVersion = match[1];
+ if (msvcVersion < 2012)
+ return "5.0";
+ return "5.1";
+ }
+ return qtProps.qtMajorVersion < 5 ? "5.0" : "5.1";
+}
+
+function fillEntryPointLibs(qtProps, debug) {
+ result = [];
+ var isMinGW = ProviderUtils.isMinGwQt(qtProps);
+
+ // Some Linux distributions rename the qtmain library.
+ var qtMainCandidates = ["qtmain"];
+ if (isMinGW && qtProps.qtMajorVersion === 5)
+ qtMainCandidates.push("qt5main");
+ if (qtProps.qtMajorVersion === 6)
+ qtMainCandidates.push("Qt6EntryPoint");
+
+ for (var i = 0; i < qtMainCandidates.length; ++i) {
+ var baseNameCandidate = qtMainCandidates[i];
+ var qtmain = qtProps.libraryPath + '/';
+ if (isMinGW)
+ qtmain += "lib";
+ qtmain += baseNameCandidate + qtProps.qtLibInfix;
+ if (debug && ProviderUtils.qtNeedsDSuffix(qtProps))
+ qtmain += 'd';
+ if (isMinGW) {
+ qtmain += ".a";
+ } else {
+ qtmain += ".lib";
+ if (Utilities.versionCompare(qtProps.qtVersion, "5.4.0") >= 0)
+ result.push("Shell32.lib");
+ }
+ if (File.exists(qtmain)) {
+ result.push(qtmain);
+ break;
+ }
+ }
+ if (result.length === 0) {
+ console.warn("Could not find the qtmain library at '"
+ + FileInfo.toNativeSeparators(qtProps.libraryPath)
+ + "'. You will not be able to link Qt applications.");
+ }
+ return result;
+}
+
+function getQtProperties(qmakeFilePath) {
+ var queryResult = queryQmake(qmakeFilePath);
+ var qtProps = {};
+ qtProps.installPrefixPath = pathQueryValue(queryResult, "QT_INSTALL_PREFIX");
+ qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS");
+ qtProps.includePath = pathQueryValue(queryResult, "QT_INSTALL_HEADERS");
+ qtProps.libraryPath = pathQueryValue(queryResult, "QT_INSTALL_LIBS");
+ qtProps.hostLibraryPath = pathQueryValue(queryResult, "QT_HOST_LIBS");
+ qtProps.binaryPath = pathQueryValue(queryResult, "QT_HOST_BINS")
+ || pathQueryValue(queryResult, "QT_INSTALL_BINS");
+ qtProps.installPath = pathQueryValue(queryResult, "QT_INSTALL_BINS");
+ qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS");
+ qtProps.pluginPath = pathQueryValue(queryResult, "QT_INSTALL_PLUGINS");
+ qtProps.qmlPath = pathQueryValue(queryResult, "QT_INSTALL_QML");
+ qtProps.qmlImportPath = pathQueryValue(queryResult, "QT_INSTALL_IMPORTS");
+ qtProps.qtVersion = queryResult.QT_VERSION;
+
+ var mkspecsBaseSrcPath;
+ if (Utilities.versionCompare(qtProps.qtVersion, "5") >= 0) {
+ qtProps.mkspecBasePath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA"),
+ "mkspecs");
+ mkspecsBaseSrcPath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA/src"),
+ "mkspecs");
+ } else {
+ qtProps.mkspecBasePath = FileInfo.joinPaths
+ (pathQueryValue(queryResult, "QT_INSTALL_DATA"), "mkspecs");
+ }
+
+ if (Utilities.versionCompare(qtProps.qtVersion, "6") >= 0) {
+ qtProps.libExecPath = pathQueryValue(queryResult, "QT_HOST_LIBEXECS")
+ || pathQueryValue(queryResult, "QT_INSTALL_LIBEXECS");
+ }
+
+ // QML tools were only moved in Qt 6.2.
+ qtProps.qmlLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0
+ ? qtProps.libExecPath : qtProps.binaryPath;
+
+ // qhelpgenerator was only moved in Qt 6.3.
+ qtProps.helpGeneratorLibExecPath = Utilities.versionCompare(qtProps.qtVersion, "6.3") >= 0
+ ? qtProps.libExecPath : qtProps.binaryPath;
+
+ if (!File.exists(qtProps.mkspecBasePath))
+ throw "Cannot extract the mkspecs directory.";
+
+ var qconfigContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath,
+ "qconfig.pri"));
+ qtProps.qtMajorVersion = parseInt(configVariable(qconfigContent, "QT_MAJOR_VERSION"));
+ qtProps.qtMinorVersion = parseInt(configVariable(qconfigContent, "QT_MINOR_VERSION"));
+ qtProps.qtPatchVersion = parseInt(configVariable(qconfigContent, "QT_PATCH_VERSION"));
+ qtProps.qtNameSpace = configVariable(qconfigContent, "QT_NAMESPACE");
+ qtProps.qtLibInfix = configVariable(qconfigContent, "QT_LIBINFIX") || "";
+ qtProps.architecture = configVariable(qconfigContent, "QT_TARGET_ARCH")
+ || configVariable(qconfigContent, "QT_ARCH") || "x86";
+ qtProps.configItems = configVariableItems(qconfigContent, "CONFIG");
+ qtProps.qtConfigItems = configVariableItems(qconfigContent, "QT_CONFIG");
+
+ // retrieve the mkspec
+ if (qtProps.qtMajorVersion >= 5) {
+ qtProps.mkspecName = queryResult.QMAKE_XSPEC;
+ qtProps.mkspecPath = FileInfo.joinPaths(qtProps.mkspecBasePath, qtProps.mkspecName);
+ if (mkspecsBaseSrcPath && !File.exists(qtProps.mkspecPath))
+ qtProps.mkspecPath = FileInfo.joinPaths(mkspecsBaseSrcPath, qtProps.mkspecName);
+ } else {
+ if (Host.os().contains("windows")) {
+ var baseDirPath = FileInfo.joinPaths(qtProps.mkspecBasePath, "default");
+ var fileContent = readFileContent(FileInfo.joinPaths(baseDirPath, "qmake.conf"));
+ qtProps.mkspecPath = configVariable(fileContent, "QMAKESPEC_ORIGINAL");
+ if (!File.exists(qtProps.mkspecPath)) {
+ // Work around QTBUG-28792.
+ // The value of QMAKESPEC_ORIGINAL is wrong for MinGW packages. Y u h8 me?
+ var match = fileContent.exec(/\binclude\(([^)]+)\/qmake\.conf\)/m);
+ if (match) {
+ qtProps.mkspecPath = FileInfo.cleanPath(FileInfo.joinPaths(
+ baseDirPath, match[1]));
+ }
+ }
+ } else {
+ qtProps.mkspecPath = FileInfo.canonicalPath(
+ FileInfo.joinPaths(qtProps.mkspecBasePath, "default"));
+ }
+
+ // 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++
+ qtProps.mkspecPath = FileInfo.cleanPath(qtProps.mkspecPath);
+
+ qtProps.mkspecName = qtProps.mkspecPath;
+ var idx = qtProps.mkspecName.lastIndexOf('/');
+ if (idx !== -1)
+ qtProps.mkspecName = qtProps.mkspecName.slice(idx + 1);
+ }
+ if (!File.exists(qtProps.mkspecPath))
+ throw "mkspec '" + FileInfo.toNativeSeparators(qtProps.mkspecPath) + "' does not exist";
+
+ // Starting with qt 5.14, android sdk provides multi-abi
+ if (Utilities.versionCompare(qtProps.qtVersion, "5.14.0") >= 0
+ && qtProps.mkspecPath.contains("android")) {
+ var qdeviceContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath,
+ "qdevice.pri"));
+ qtProps.androidAbis = configVariable(qdeviceContent, "DEFAULT_ANDROID_ABIS").split(' ');
+ }
+
+ // determine MSVC version
+ if (ProviderUtils.isMsvcQt(qtProps)) {
+ var msvcMajor = configVariable(qconfigContent, "QT_MSVC_MAJOR_VERSION");
+ var msvcMinor = configVariable(qconfigContent, "QT_MSVC_MINOR_VERSION");
+ var msvcPatch = configVariable(qconfigContent, "QT_MSVC_PATCH_VERSION");
+ if (msvcMajor && msvcMinor && msvcPatch)
+ qtProps.msvcVersion = msvcMajor + "." + msvcMinor + "." + msvcPatch;
+ else
+ qtProps.msvcVersion = msvcCompilerVersionFromMkspecName(qtProps.mkspecName);
+ }
+
+ // determine whether we have a framework build
+ qtProps.frameworkBuild = qtProps.mkspecPath.contains("macx")
+ && qtProps.configItems.contains("qt_framework");
+
+ // determine whether Qt is built with debug, release or both
+ qtProps.buildVariant = [];
+ addQtBuildVariant(qtProps, "debug");
+ addQtBuildVariant(qtProps, "release");
+
+ qtProps.staticBuild = checkForStaticBuild(qtProps);
+
+ // determine whether user apps require C++11
+ if (qtProps.qtConfigItems.contains("c++11") && qtProps.staticBuild)
+ qtProps.configItems.push("c++11");
+
+ // Set the minimum operating system versions appropriate for this Qt version
+ qtProps.windowsVersion = guessMinimumWindowsVersion(qtProps);
+ if (qtProps.windowsVersion) { // Is target OS Windows?
+ if (qtProps.buildVariant.contains("debug"))
+ qtProps.entryPointLibsDebug = fillEntryPointLibs(qtProps, true);
+ if (qtProps.buildVariant.contains("release"))
+ qtProps.entryPointLibsRelease = fillEntryPointLibs(qtProps, false);
+ } else if (qtProps.mkspecPath.contains("macx")) {
+ if (qtProps.qtMajorVersion >= 5) {
+ // Since Qt 6.7.1, QMAKE_MACOSX|IOS_DEPLOYMENT_TARGET is no longer present in
+ // qmake.conf. But it is also present in qconfig.pri, so first try to read it from there
+ qtProps.macosVersion = configVariable(qconfigContent, "QMAKE_MACOSX_DEPLOYMENT_TARGET");
+ qtProps.iosVersion = configVariable(qconfigContent, "QMAKE_IOS_DEPLOYMENT_TARGET");
+
+ // Next, we override the value from qmake.conf, if present there
+ // Note, that TVOS/WATCHOS variables are only present in qmake.conf (as of Qt 6.7.1)
+ var lines = getFileContentsRecursively(FileInfo.joinPaths(qtProps.mkspecPath,
+ "qmake.conf"));
+ for (var i = 0; i < lines.length; ++i) {
+ var line = lines[i].trim();
+ match = line.match
+ (/^QMAKE_(MACOSX|IOS|TVOS|WATCHOS)_DEPLOYMENT_TARGET\s*=\s*(.*)\s*$/);
+ if (match) {
+ var platform = match[1];
+ var version = match[2];
+ if (platform === "MACOSX")
+ qtProps.macosVersion = version;
+ else if (platform === "IOS")
+ qtProps.iosVersion = version;
+ else if (platform === "TVOS")
+ qtProps.tvosVersion = version;
+ else if (platform === "WATCHOS")
+ qtProps.watchosVersion = version;
+ }
+ }
+ var isMac = qtProps.mkspecName !== "macx-ios-clang"
+ && qtProps.mkspecName !== "macx-tvos-clang"
+ && qtProps.mkspecName !== "macx-watchos-clang";
+ if (isMac) {
+ // Qt 5.0.x placed the minimum version in a different file
+ if (!qtProps.macosVersion)
+ qtProps.macosVersion = "10.6";
+
+ // If we're using C++11 with libc++, make sure the deployment target is >= 10.7
+ if (Utilities.versionCompare(qtProps.macosVersion, "10, 7") < 0
+ && qtProps.qtConfigItems.contains("c++11")) {
+ qtProps.macosVersion = "10.7";
+ }
+ }
+ } else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 6) {
+ var qconfigDir = qtProps.frameworkBuild
+ ? FileInfo.joinPaths(qtProps.libraryPath, "QtCore.framework", "Headers")
+ : FileInfo.joinPaths(qtProps.includePath, "Qt");
+ try {
+ var qconfig = new TextFile(FileInfo.joinPaths(qconfigDir, "qconfig.h"),
+ TextFile.ReadOnly);
+ var qtCocoaBuild = false;
+ var ok = true;
+ do {
+ line = qconfig.readLine();
+ if (line.match(/\s*#define\s+QT_MAC_USE_COCOA\s+1\s*/)) {
+ qtCocoaBuild = true;
+ break;
+ }
+ } while (!qconfig.atEof());
+ qtProps.macosVersion = qtCocoaBuild ? "10.5" : "10.4";
+ }
+ catch (e) {}
+ finally {
+ if (qconfig)
+ qconfig.close();
+ }
+ if (!qtProps.macosVersion) {
+ throw "Could not determine whether Qt is using Cocoa or Carbon from '"
+ + FileInfo.toNativeSeparators(qconfig.filePath()) + "'.";
+ }
+ }
+ } else if (qtProps.mkspecPath.contains("android")) {
+ if (qtProps.qtMajorVersion >= 5)
+ qtProps.androidVersion = "2.3";
+ else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 8)
+ qtProps.androidVersion = "1.6"; // Necessitas
+ }
+ return qtProps;
+}
+
+function makePluginData() {
+ var pluginData = {};
+ pluginData.type = undefined;
+ pluginData.className = undefined;
+ pluginData.autoLoad = true;
+ pluginData["extends"] = [];
+ return pluginData;
+}
+
+function makeQtModuleInfo(name, qbsName, deps) {
+ var moduleInfo = {};
+ moduleInfo.name = name; // As in the path to the headers and ".name" in the pri files.
+ if (moduleInfo.name === undefined)
+ moduleInfo.name = "";
+ moduleInfo.qbsName = qbsName; // Lower-case version without "qt" prefix.
+ moduleInfo.dependencies = deps || []; // qbs names.
+ if (moduleInfo.qbsName && moduleInfo.qbsName !== "core"
+ && !moduleInfo.dependencies.contains("core")) {
+ moduleInfo.dependencies.unshift("core");
+ }
+ moduleInfo.isPrivate = qbsName && qbsName.endsWith("-private");
+ moduleInfo.hasLibrary = !moduleInfo.isPrivate;
+ moduleInfo.isStaticLibrary = false;
+ moduleInfo.isPlugin = false;
+ moduleInfo.mustExist = true;
+ moduleInfo.modulePrefix = ""; // empty value means "Qt".
+ moduleInfo.version = undefined;
+ moduleInfo.includePaths = [];
+ moduleInfo.compilerDefines = [];
+ moduleInfo.staticLibrariesDebug = [];
+ moduleInfo.staticLibrariesRelease = [];
+ moduleInfo.dynamicLibrariesDebug = [];
+ moduleInfo.dynamicLibrariesRelease = [];
+ moduleInfo.linkerFlagsDebug = [];
+ moduleInfo.linkerFlagsRelease = [];
+ moduleInfo.libFilePathDebug = undefined;
+ moduleInfo.libFilePathRelease = undefined;
+ moduleInfo.frameworksDebug = [];
+ moduleInfo.frameworksRelease = [];
+ moduleInfo.frameworkPathsDebug = [];
+ moduleInfo.frameworkPathsRelease = [];
+ moduleInfo.libraryPaths = [];
+ moduleInfo.libDir = "";
+ moduleInfo.config = [];
+ moduleInfo.supportedPluginTypes = [];
+ moduleInfo.pluginData = makePluginData();
+ return moduleInfo;
+}
+
+function frameworkHeadersPath(qtModuleInfo, qtProps) {
+ return FileInfo.joinPaths(qtProps.libraryPath, qtModuleInfo.name + ".framework", "Headers");
+}
+
+function qt4ModuleIncludePaths(qtModuleInfo, qtProps) {
+ var paths = [];
+ if (ProviderUtils.qtIsFramework(qtModuleInfo, qtProps))
+ paths.push(frameworkHeadersPath(qtModuleInfo, qtProps));
+ else
+ paths.push(qtProps.includePath, FileInfo.joinPaths(qtProps.includePath, qtModuleInfo.name));
+ return paths;
+}
+
+// We erroneously called the "testlib" module "test" for quite a while. Let's not punish users
+// for that.
+function addTestModule(modules) {
+ var testModule = makeQtModuleInfo("QtTest", "test", ["testlib"]);
+ testModule.hasLibrary = false;
+ modules.push(testModule);
+}
+
+// See above.
+function addDesignerComponentsModule(modules) {
+ var module = makeQtModuleInfo("QtDesignerComponents", "designercomponents",
+ ["designercomponents-private"]);
+ module.hasLibrary = false;
+ modules.push(module);
+}
+
+function guessLibraryFilePath(prlFilePath, libDir, qtProps) {
+ var baseName = FileInfo.baseName(prlFilePath);
+ var prefixCandidates = ["", "lib"];
+ var suffixCandidates = ["so." + qtProps.qtVersion, "so", "a", "lib", "dll.a"];
+ for (var i = 0; i < prefixCandidates.length; ++i) {
+ var prefix = prefixCandidates[i];
+ for (var j = 0; j < suffixCandidates.length; ++j) {
+ var suffix = suffixCandidates[j];
+ var candidate = FileInfo.joinPaths(libDir, prefix + baseName + '.' + suffix);
+ if (File.exists(candidate))
+ return candidate;
+ }
+ }
+}
+
+function doReplaceQtLibNamesWithFilePath(namePathMap, libList) {
+ for (var i = 0; i < libList.length; ++i) {
+ var lib = libList[i];
+ var path = namePathMap[lib];
+ if (path)
+ libList[i] = path;
+ }
+}
+
+function replaceQtLibNamesWithFilePath(modules, qtProps) {
+ // 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.
+ var linkerNamesToFilePathsDebug = {};
+ var linkerNamesToFilePathsRelease = {};
+ for (var i = 0; i < modules.length; ++i) {
+ var m = modules[i];
+ linkerNamesToFilePathsDebug[
+ ProviderUtils.qtLibNameForLinker(m, qtProps, true)] = m.libFilePathDebug;
+ linkerNamesToFilePathsRelease[
+ ProviderUtils.qtLibNameForLinker(m, qtProps, false)] = m.libFilePathRelease;
+ }
+ for (i = 0; i < modules.length; ++i) {
+ var module = modules[i];
+ doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.dynamicLibrariesDebug);
+ doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.staticLibrariesDebug);
+ doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease,
+ module.dynamicLibrariesRelease);
+ doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease,
+ module.staticLibrariesRelease);
+ }
+}
+
+function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles, androidAbi) {
+ if (!modInfo.hasLibrary)
+ return; // Can happen for Qt4 convenience modules, like "widgets".
+
+ if (debugBuild) {
+ if (!qtProps.buildVariant.contains("debug"))
+ return;
+ var modulesNeverBuiltAsDebug = ["bootstrap", "qmldevtools"];
+ for (var i = 0; i < modulesNeverBuiltAsDebug.length; ++i) {
+ var m = modulesNeverBuiltAsDebug[i];
+ if (modInfo.qbsName === m || modInfo.qbsName === m + "-private")
+ return;
+ }
+ } else if (!qtProps.buildVariant.contains("release")) {
+ return;
+ }
+
+ var libs = modInfo.isStaticLibrary
+ ? (debugBuild ? modInfo.staticLibrariesDebug : modInfo.staticLibrariesRelease)
+ : (debugBuild ? modInfo.dynamicLibrariesDebug : modInfo.dynamicLibrariesRelease);
+ var frameworks = debugBuild ? modInfo.frameworksDebug : modInfo.frameworksRelease;
+ var frameworkPaths = debugBuild ? modInfo.frameworkPathsDebug : modInfo.frameworkPathsRelease;
+ var flags = debugBuild ? modInfo.linkerFlagsDebug : modInfo.linkerFlagsRelease;
+ var libFilePath;
+
+ if (qtProps.mkspecName.contains("ios") && modInfo.isStaticLibrary) {
+ libs.push("z", "m");
+ if (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 8) {
+ var platformSupportModule = makeQtModuleInfo("QtPlatformSupport", "platformsupport");
+ libs.push(ProviderUtils.qtLibNameForLinker(platformSupportModule, qtProps, debugBuild));
+ }
+ if (modInfo.name === "qios") {
+ flags.push("-force_load", FileInfo.joinPaths(
+ qtProps.pluginPath, "platforms",
+ ProviderUtils.qtLibBaseName(
+ modInfo, "libqios", debugBuild, qtProps) + ".a"));
+ }
+ }
+ var prlFilePath = modInfo.isPlugin
+ ? FileInfo.joinPaths(qtProps.pluginPath, modInfo.pluginData.type)
+ : (modInfo.libDir ? modInfo.libDir : qtProps.libraryPath);
+ var libDir = prlFilePath;
+ if (ProviderUtils.qtIsFramework(modInfo, qtProps)) {
+ prlFilePath = FileInfo.joinPaths(
+ prlFilePath,
+ ProviderUtils.qtLibraryBaseName(modInfo, qtProps, false) + ".framework");
+ libDir = prlFilePath;
+ if (Utilities.versionCompare(qtProps.qtVersion, "5.14") >= 0)
+ prlFilePath = FileInfo.joinPaths(prlFilePath, "Resources");
+ }
+ var baseName = ProviderUtils.qtLibraryBaseName(modInfo, qtProps, debugBuild);
+ if (!qtProps.mkspecName.startsWith("win") && !ProviderUtils.qtIsFramework(modInfo, qtProps))
+ baseName = "lib" + baseName;
+ prlFilePath = FileInfo.joinPaths(prlFilePath, baseName);
+ var isNonStaticQt4OnWindows = qtProps.mkspecName.startsWith("win")
+ && !modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5;
+ if (isNonStaticQt4OnWindows)
+ prlFilePath = prlFilePath.slice(0, prlFilePath.length - 1); // The prl file base name does *not* contain the version number...
+ // qt for android versions 6.0 and 6.1 don't have the architecture suffix in the prl file
+ if (androidAbi.length > 0
+ && modInfo.name !== "QtBootstrap"
+ && (modInfo.name !== "QtQmlDevTools" || modInfo.name === "QtQmlDevTools"
+ && Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0)
+ && (Utilities.versionCompare(qtProps.qtVersion, "6.0") < 0
+ || Utilities.versionCompare(qtProps.qtVersion, "6.2") >= 0)) {
+ prlFilePath += "_";
+ prlFilePath += androidAbi;
+ }
+
+ prlFilePath += ".prl";
+
+ try {
+ var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly);
+ while (!prlFile.atEof()) {
+ var line = prlFile.readLine().trim();
+ var equalsOffset = line.indexOf('=');
+ if (equalsOffset === -1)
+ continue;
+ if (line.startsWith("QMAKE_PRL_TARGET")) {
+ var isMingw = qtProps.mkspecName.startsWith("win")
+ && qtProps.mkspecName.contains("g++");
+ var isQtVersionBefore56 = qtProps.qtMajorVersion < 5
+ || (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 6);
+
+ // 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.
+ var libFileName = "";
+ if (isQtVersionBefore56 && qtProps.qtMajorVersion === 5 && isMingw
+ && !modInfo.isStaticLibrary) {
+ libFileName += "lib";
+ }
+
+ libFileName += line.slice(equalsOffset + 1).trim();
+ if (isNonStaticQt4OnWindows)
+ libFileName += 4; // This is *not* part of QMAKE_PRL_TARGET...
+ if (isQtVersionBefore56) {
+ if (qtProps.mkspecName.contains("msvc")) {
+ libFileName += ".lib";
+ } else if (isMingw) {
+ libFileName += ".a";
+ if (!File.exists(FileInfo.joinPaths(libDir, libFileName)))
+ libFileName = libFileName.slice(0, -2) + ".dll";
+ }
+ }
+ libFilePath = FileInfo.joinPaths(libDir, libFileName);
+ continue;
+ }
+ if (line.startsWith("QMAKE_PRL_CONFIG")) {
+ modInfo.config = splitNonEmpty(line.slice(equalsOffset + 1).trim(), ' ');
+ continue;
+ }
+ if (!line.startsWith("QMAKE_PRL_LIBS ="))
+ continue;
+
+ var parts = extractPaths(line.slice(equalsOffset + 1).trim(), prlFilePath);
+ for (i = 0; i < parts.length; ++i) {
+ var part = parts[i];
+ part = part.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath);
+ part = part.replace("$$[QT_INSTALL_PLUGINS]", qtProps.pluginPath);
+ part = part.replace("$$[QT_INSTALL_PREFIX]", qtProps.installPrefixPath);
+ if (part.startsWith("-l")) {
+ libs.push(part.slice(2));
+ } else if (part.startsWith("-L")) {
+ modInfo.libraryPaths.push(part.slice(2));
+ } else if (part.startsWith("-F")) {
+ frameworkPaths.push(part.slice(2));
+ } else if (part === "-framework") {
+ if (++i < parts.length)
+ frameworks.push(parts[i]);
+ } else if (part === "-pthread") {
+ // prl files for android have QMAKE_PRL_LIBS = -llog -pthread but the pthread
+ // functionality is included in libc.
+ if (androidAbi.length === 0)
+ libs.push("pthread");
+ } else if (part.startsWith('-')) { // Some other option
+ console.debug("QMAKE_PRL_LIBS contains non-library option '" + part
+ + "' in file '" + prlFilePath + "'");
+ flags.push(part);
+ } else if (part.startsWith("/LIBPATH:")) {
+ libraryPaths.push(part.slice(9).replace(/\\/g, '/'));
+ } else { // Assume it's a file path/name.
+ libs.push(part.replace(/\\/g, '/'));
+ }
+ }
+ }
+ } catch (e) {
+ // qt_ext_lib_extX.pri (usually) don't have a corresponding prl file.
+ // So the pri file variable QMAKE_LIBS_LIBX points to the library
+ if (modInfo.isExternal) {
+ libFilePath = debugBuild ? modInfo.staticLibrariesDebug[0] :
+ modInfo.staticLibrariesRelease[0];
+ }
+ if (!libFilePath || !File.exists(libFilePath))
+ libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtProps);
+ if (nonExistingPrlFiles.contains(prlFilePath))
+ return;
+ nonExistingPrlFiles.push(prlFilePath);
+ if (modInfo.mustExist) {
+ console.warn("Could not open prl file '"
+ + FileInfo.toNativeSeparators(prlFilePath) + "' for module '"
+ + modInfo.name
+ + "' (" + e + "), and failed to deduce the library file path. "
+ + " This module will likely not be usable by qbs.");
+ }
+ }
+ finally {
+ if (prlFile)
+ prlFile.close();
+ }
+
+ if (debugBuild)
+ modInfo.libFilePathDebug = libFilePath;
+ else
+ modInfo.libFilePathRelease = libFilePath;
+}
+
+function setupLibraries(qtModuleInfo, qtProps, nonExistingPrlFiles, androidAbi) {
+ doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles, androidAbi);
+ doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles, androidAbi);
+}
+
+function allQt4Modules(qtProps) {
+ // as per http://doc.qt.io/qt-4.8/modules.html + private stuff.
+ var modules = [];
+
+ var core = makeQtModuleInfo("QtCore", "core");
+ core.compilerDefines.push("QT_CORE_LIB");
+ if (qtProps.qtNameSpace)
+ core.compilerDefines.push("QT_NAMESPACE=" + qtProps.qtNameSpace);
+ modules.push(core,
+ makeQtModuleInfo("QtCore", "core-private", ["core"]),
+ makeQtModuleInfo("QtGui", "gui"),
+ makeQtModuleInfo("QtGui", "gui-private", ["gui"]),
+ makeQtModuleInfo("QtMultimedia", "multimedia", ["gui", "network"]),
+ makeQtModuleInfo("QtMultimedia", "multimedia-private", ["multimedia"]),
+ makeQtModuleInfo("QtNetwork", "network"),
+ makeQtModuleInfo("QtNetwork", "network-private", ["network"]),
+ makeQtModuleInfo("QtOpenGL", "opengl", ["gui"]),
+ makeQtModuleInfo("QtOpenGL", "opengl-private", ["opengl"]),
+ makeQtModuleInfo("QtOpenVG", "openvg", ["gui"]),
+ makeQtModuleInfo("QtScript", "script"),
+ makeQtModuleInfo("QtScript", "script-private", ["script"]),
+ makeQtModuleInfo("QtScriptTools", "scripttools", ["script", "gui"]),
+ makeQtModuleInfo("QtScriptTools", "scripttools-private", ["scripttools"]),
+ makeQtModuleInfo("QtSql", "sql"),
+ makeQtModuleInfo("QtSql", "sql-private", ["sql"]),
+ makeQtModuleInfo("QtSvg", "svg", ["gui"]),
+ makeQtModuleInfo("QtSvg", "svg-private", ["svg"]),
+ makeQtModuleInfo("QtWebKit", "webkit", ["gui", "network"]),
+ makeQtModuleInfo("QtWebKit", "webkit-private", ["webkit"]),
+ makeQtModuleInfo("QtXml", "xml"),
+ makeQtModuleInfo("QtXml", "xml-private", ["xml"]),
+ makeQtModuleInfo("QtXmlPatterns", "xmlpatterns", ["network"]),
+ makeQtModuleInfo("QtXmlPatterns", "xmlpatterns-private", ["xmlpatterns"]),
+ makeQtModuleInfo("QtDeclarative", "declarative", ["gui", "script"]),
+ makeQtModuleInfo("QtDeclarative", "declarative-private", ["declarative"]),
+ makeQtModuleInfo("QtDesigner", "designer", ["gui", "xml"]),
+ makeQtModuleInfo("QtDesigner", "designer-private", ["designer"]),
+ makeQtModuleInfo("QtUiTools", "uitools"),
+ makeQtModuleInfo("QtUiTools", "uitools-private", ["uitools"]),
+ makeQtModuleInfo("QtHelp", "help", ["network", "sql"]),
+ makeQtModuleInfo("QtHelp", "help-private", ["help"]),
+ makeQtModuleInfo("QtTest", "testlib"),
+ makeQtModuleInfo("QtTest", "testlib-private", ["testlib"]));
+ if (qtProps.mkspecName.startsWith("win")) {
+ var axcontainer = makeQtModuleInfo("QAxContainer", "axcontainer");
+ axcontainer.modulePrefix = "Q";
+ axcontainer.isStaticLibrary = true;
+ axcontainer.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt"));
+ modules.push(axcontainer);
+
+ var axserver = makeQtModuleInfo("QAxServer", "axserver");
+ axserver.modulePrefix = "Q";
+ axserver.isStaticLibrary = true;
+ axserver.compilerDefines.push("QAXSERVER");
+ axserver.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt"));
+ modules.push(axserver);
+ } else {
+ modules.push(makeQtModuleInfo("QtDBus", "dbus"));
+ modules.push(makeQtModuleInfo("QtDBus", "dbus-private", ["dbus"]));
+ }
+
+ var designerComponentsPrivate = makeQtModuleInfo(
+ "QtDesignerComponents", "designercomponents-private",
+ ["gui-private", "designer-private"]);
+ designerComponentsPrivate.hasLibrary = true;
+ modules.push(designerComponentsPrivate);
+
+ var phonon = makeQtModuleInfo("Phonon", "phonon");
+ phonon.includePaths = qt4ModuleIncludePaths(phonon, qtProps);
+ modules.push(phonon);
+
+ // Set up include paths that haven't been set up before this point.
+ for (i = 0; i < modules.length; ++i) {
+ var module = modules[i];
+ if (module.includePaths.length > 0)
+ continue;
+ module.includePaths = qt4ModuleIncludePaths(module, qtProps);
+ }
+
+ // Set up compiler defines haven't been set up before this point.
+ for (i = 0; i < modules.length; ++i) {
+ module = modules[i];
+ if (module.compilerDefines.length > 0)
+ continue;
+ module.compilerDefines.push("QT_" + module.qbsName.toUpperCase() + "_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.
+ var virtualModule = makeQtModuleInfo(undefined, "widgets", ["core", "gui"]);
+ virtualModule.hasLibrary = false;
+ modules.push(virtualModule);
+ virtualModule = makeQtModuleInfo(undefined, "quick", ["declarative"]);
+ virtualModule.hasLibrary = false;
+ modules.push(virtualModule);
+ virtualModule = makeQtModuleInfo(undefined, "concurrent");
+ virtualModule.hasLibrary = false;
+ modules.push(virtualModule);
+ virtualModule = makeQtModuleInfo(undefined, "printsupport", ["core", "gui"]);
+ virtualModule.hasLibrary = false;
+ modules.push(virtualModule);
+
+ addTestModule(modules);
+ addDesignerComponentsModule(modules);
+
+ var modulesThatCanBeDisabled = [
+ "xmlpatterns", "multimedia", "phonon", "svg", "webkit", "script", "scripttools",
+ "declarative", "gui", "dbus", "opengl", "openvg"];
+ var nonExistingPrlFiles = [];
+ for (i = 0; i < modules.length; ++i) {
+ module = modules[i];
+ var name = module.qbsName;
+ var privateIndex = name.indexOf("-private");
+ if (privateIndex !== -1)
+ name = name.slice(0, privateIndex);
+ if (modulesThatCanBeDisabled.contains(name))
+ module.mustExist = false;
+ if (qtProps.staticBuild)
+ module.isStaticLibrary = true;
+ setupLibraries(module, qtProps, nonExistingPrlFiles, "");
+ }
+ replaceQtLibNamesWithFilePath(modules, qtProps);
+
+ return modules;
+}
+
+function getFileContentsRecursively(filePath) {
+ var file = new TextFile(filePath, TextFile.ReadOnly);
+ var lines = splitNonEmpty(file.readAll(), '\n');
+ for (var i = 0; i < lines.length; ++i) {
+ var includeString = "include(";
+ var line = lines[i].trim();
+ if (!line.startsWith(includeString))
+ continue;
+ var offset = includeString.length;
+ var closingParenPos = line.indexOf(')', offset);
+ if (closingParenPos === -1) {
+ console.warn("Invalid include statement in '"
+ + FileInfo.toNativeSeparators(filePath) + "'");
+ continue;
+ }
+ var includedFilePath = line.slice(offset, closingParenPos);
+ if (!FileInfo.isAbsolutePath(includedFilePath))
+ includedFilePath = FileInfo.joinPaths(FileInfo.path(filePath), includedFilePath);
+ var includedContents = getFileContentsRecursively(includedFilePath);
+ var j = i;
+ for (var k = 0; k < includedContents.length; ++k)
+ lines.splice(++j, 0, includedContents[k]);
+ lines.splice(i--, 1);
+ }
+ file.close();
+ return lines;
+}
+
+function extractPaths(rhs, filePath) {
+ var paths = [];
+ var startIndex = 0;
+ for (;;) {
+ while (startIndex < rhs.length && rhs.charAt(startIndex) === ' ')
+ ++startIndex;
+ if (startIndex >= rhs.length)
+ break;
+ var endIndex;
+ if (rhs.charAt(startIndex) === '"') {
+ ++startIndex;
+ endIndex = rhs.indexOf('"', startIndex);
+ if (endIndex === -1) {
+ console.warn("Unmatched quote in file '"
+ + FileInfo.toNativeSeparators(filePath) + "'");
+ break;
+ }
+ } else {
+ endIndex = rhs.indexOf(' ', startIndex + 1);
+ if (endIndex === -1)
+ endIndex = rhs.length;
+ }
+ paths.push(FileInfo.cleanPath(rhs.slice(startIndex, endIndex)
+ .replace("$$PWD", FileInfo.path(filePath))));
+ startIndex = endIndex + 1;
+ }
+ return paths;
+}
+
+function removeDuplicatedDependencyLibs(modules) {
+ var revDeps = {};
+ var currentPath = [];
+ var getLibraries;
+ var getLibFilePath;
+
+ function setupReverseDependencies(modules) {
+ var moduleByName = {};
+ for (var i = 0; i < modules.length; ++i)
+ moduleByName[modules[i].qbsName] = modules[i];
+ for (i = 0; i < modules.length; ++i) {
+ var module = modules[i];
+ for (var j = 0; j < module.dependencies.length; ++j) {
+ var depmod = moduleByName[module.dependencies[j]];
+ if (!depmod)
+ continue;
+ if (!revDeps[depmod.qbsName])
+ revDeps[depmod.qbsName] = [];
+ revDeps[depmod.qbsName].push(module);
+ }
+ }
+ }
+
+ function roots(modules) {
+ var result = [];
+ for (i = 0; i < modules.length; ++i) {
+ var module = modules[i]
+ if (module.dependencies.length === 0)
+ result.push(module);
+ }
+ return result;
+ }
+
+ function traverse(module, libs) {
+ if (currentPath.contains(module))
+ return;
+ currentPath.push(module);
+ var moduleLibraryLists = getLibraries(module);
+ for (var i = 0; i < moduleLibraryLists.length; ++i) {
+ var modLibList = moduleLibraryLists[i];
+ for (j = modLibList.length - 1; j >= 0; --j) {
+ if (libs.contains(modLibList[j]))
+ modLibList.splice(j, 1);
+ }
+ }
+
+ var libFilePath = getLibFilePath(module);
+ if (libFilePath)
+ libs.push(libFilePath);
+ for (i = 0; i < moduleLibraryLists.length; ++i)
+ libs = libs.concat(moduleLibraryLists[i]);
+ libs.sort();
+
+ var deps = revDeps[module.qbsName];
+ for (i = 0; i < (deps || []).length; ++i)
+ traverse(deps[i], libs);
+
+ currentPath.pop();
+ }
+
+ setupReverseDependencies(modules);
+
+ // Traverse the debug variants of modules.
+ getLibraries = function(module) {
+ return [module.dynamicLibrariesDebug, module.staticLibrariesDebug];
+ };
+ getLibFilePath = function(module) { return module.libFilePathDebug; };
+ var rootModules = roots(modules);
+ for (var i = 0; i < rootModules.length; ++i)
+ traverse(rootModules[i], []);
+
+ // Traverse the release variants of modules.
+ getLibraries = function(module) {
+ return [module.dynamicLibrariesRelease, module.staticLibrariesRelease];
+ };
+ getLibFilePath = function(module) { return module.libFilePathRelease; };
+ for (i = 0; i < rootModules.length; ++i)
+ traverse(rootModules[i], []);
+}
+
+function allQt5Modules(qtProps, androidAbi) {
+ var nonExistingPrlFiles = [];
+ var modules = [];
+ var modulesDir = FileInfo.joinPaths(qtProps.mkspecBasePath, "modules");
+ var modulePriFiles = File.directoryEntries(modulesDir, File.Files);
+ for (var i = 0; i < modulePriFiles.length; ++i) {
+ var priFileName = modulePriFiles[i];
+ var priFilePath = FileInfo.joinPaths(modulesDir, priFileName);
+ var externalFileNamePrefix = "qt_ext_";
+ var moduleFileNamePrefix = "qt_lib_";
+ var pluginFileNamePrefix = "qt_plugin_";
+ var moduleFileNameSuffix = ".pri";
+ var fileHasExternalPrefix = priFileName.startsWith(externalFileNamePrefix);
+ var fileHasModulePrefix = priFileName.startsWith(moduleFileNamePrefix);
+ var fileHasPluginPrefix = priFileName.startsWith(pluginFileNamePrefix);
+ if (!fileHasPluginPrefix && !fileHasModulePrefix && !fileHasExternalPrefix
+ || !priFileName.endsWith(moduleFileNameSuffix)) {
+ continue;
+ }
+ var moduleInfo = makeQtModuleInfo();
+ moduleInfo.isPlugin = fileHasPluginPrefix;
+ moduleInfo.isExternal = !moduleInfo.isPlugin && !fileHasModulePrefix;
+ var fileNamePrefix = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleInfo.isExternal
+ ? externalFileNamePrefix : moduleFileNamePrefix;
+ moduleInfo.qbsName = priFileName.slice(fileNamePrefix.length, -moduleFileNameSuffix.length);
+ if (moduleInfo.isPlugin) {
+ moduleInfo.name = moduleInfo.qbsName;
+ moduleInfo.isStaticLibrary = true;
+ }
+ var moduleKeyPrefix = (moduleInfo.isPlugin ? "QT_PLUGIN" : "QT")
+ + '.' + moduleInfo.qbsName + '.';
+ moduleInfo.qbsName = moduleInfo.qbsName.replace("_private", "-private");
+ var hasV2 = false;
+ var hasModuleEntry = false;
+ var lines = getFileContentsRecursively(priFilePath);
+ if (moduleInfo.isExternal) {
+ moduleInfo.name = "qt" + moduleInfo.qbsName;
+ moduleInfo.isStaticLibrary = true;
+ for (var k = 0; k < lines.length; ++k) {
+ var extLine = lines[k].trim();
+ var extFirstEqualsOffset = extLine.indexOf('=');
+ if (extFirstEqualsOffset === -1)
+ continue;
+ var extKey = extLine.slice(0, extFirstEqualsOffset).trim();
+ var extValue = extLine.slice(extFirstEqualsOffset + 1).trim();
+ if (!extKey.startsWith("QMAKE_") || !extValue)
+ continue;
+
+ var elements = extKey.split('_');
+ if (elements.length >= 3) {
+ if (elements[1] === "LIBS") {
+ extValue = extValue.replace("/home/qt/work/qt/qtbase/lib",
+ qtProps.libraryPath);
+ extValue = extValue.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath);
+ extValue = extValue.replace("$$[QT_INSTALL_LIBS/get]", qtProps.libraryPath);
+ if (elements.length === 4 ) {
+ if (elements[3] === androidAbi) {
+ moduleInfo.staticLibrariesRelease.push(extValue);
+ moduleInfo.staticLibrariesDebug.push(extValue);
+ }
+ } else if (elements.length === 5 ) {
+ // That's for "x86_64"
+ var abi = elements[3] + '_' + elements[4];
+ if (abi === androidAbi) {
+ moduleInfo.staticLibrariesRelease.push(extValue);
+ moduleInfo.staticLibrariesDebug.push(extValue);
+ }
+ } else {
+ moduleInfo.staticLibrariesRelease.push(extValue);
+ moduleInfo.staticLibrariesDebug.push(extValue);
+ }
+ } else if (elements[1] === "INCDIR") {
+ moduleInfo.includePaths.push(extValue.replace("$$[QT_INSTALL_HEADERS]",
+ qtProps.includePath));
+ }
+ }
+ }
+ moduleInfo.compilerDefines.push("QT_" + moduleInfo.qbsName.toUpperCase() + "_LIB");
+ moduleInfo.mustExist = false;
+ } else {
+ for (var j = 0; j < lines.length; ++j) {
+ var line = lines[j].trim();
+ var firstEqualsOffset = line.indexOf('=');
+ if (firstEqualsOffset === -1)
+ continue;
+ var key = line.slice(0, firstEqualsOffset).trim();
+ var value = line.slice(firstEqualsOffset + 1).trim();
+ if (!key.startsWith(moduleKeyPrefix) || !value)
+ continue;
+ if (key.endsWith(".name")) {
+ moduleInfo.name = value;
+ } else if (key.endsWith(".module")) {
+ hasModuleEntry = true;
+ } else if (key.endsWith(".depends")) {
+ moduleInfo.dependencies = splitNonEmpty(value, ' ');
+ for (var k = 0; k < moduleInfo.dependencies.length; ++k) {
+ moduleInfo.dependencies[k]
+ = moduleInfo.dependencies[k].replace("_private", "-private");
+ }
+ } else if (key.endsWith(".module_config")) {
+ var elems = splitNonEmpty(value, ' ');
+ for (k = 0; k < elems.length; ++k) {
+ var elem = elems[k];
+ 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 = extractPaths(value, priFilePath);
+ for (k = 0; k < moduleInfo.includePaths.length; ++k) {
+ moduleInfo.includePaths[k] = moduleInfo.includePaths[k]
+ .replace("$$QT_MODULE_INCLUDE_BASE", qtProps.includePath)
+ .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath)
+ .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath);
+ }
+ } else if (key.endsWith(".libs")) {
+ var libDirs = extractPaths(value, priFilePath);
+ if (libDirs.length === 1) {
+ moduleInfo.libDir = libDirs[0]
+ .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath)
+ .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath);
+ } else {
+ moduleInfo.libDir = qtProps.libraryPath;
+ }
+ } else if (key.endsWith(".DEFINES")) {
+ moduleInfo.compilerDefines = splitNonEmpty(value, ' ');
+ } else if (key.endsWith(".VERSION")) {
+ moduleInfo.version = value;
+ } else if (key.endsWith(".plugin_types")) {
+ moduleInfo.supportedPluginTypes = splitNonEmpty(value, ' ');
+ } else if (key.endsWith(".TYPE")) {
+ moduleInfo.pluginData.type = value;
+ } else if (key.endsWith(".EXTENDS")) {
+ moduleInfo.pluginData["extends"] = splitNonEmpty(value, ' ');
+ for (k = 0; k < moduleInfo.pluginData["extends"].length; ++k) {
+ if (moduleInfo.pluginData["extends"][k] === "-") {
+ moduleInfo.pluginData["extends"].splice(k, 1);
+ moduleInfo.pluginData.autoLoad = false;
+ break;
+ }
+ }
+ } else if (key.endsWith(".CLASS_NAME")) {
+ moduleInfo.pluginData.className = value;
+ }
+ }
+ }
+ if (hasV2 && !hasModuleEntry && !moduleInfo.isStaticLibrary)
+ moduleInfo.hasLibrary = false;
+
+ // Fix include paths for Apple frameworks.
+ // The qt_lib_XXX.pri files contain wrong values for versions < 5.6.
+ if (!hasV2 && ProviderUtils.qtIsFramework(moduleInfo, qtProps)) {
+ moduleInfo.includePaths = [];
+ var baseIncDir = frameworkHeadersPath(moduleInfo, qtProps);
+ if (moduleInfo.isPrivate) {
+ baseIncDir = FileInfo.joinPaths(baseIncDir, moduleInfo.version);
+ moduleInfo.includePaths.push(baseIncDir,
+ FileInfo.joinPaths(baseIncDir, moduleInfo.name));
+ } else {
+ moduleInfo.includePaths.push(baseIncDir);
+ }
+ }
+
+ setupLibraries(moduleInfo, qtProps, nonExistingPrlFiles, androidAbi);
+
+ modules.push(moduleInfo);
+ if (moduleInfo.qbsName === "testlib")
+ addTestModule(modules);
+ if (moduleInfo.qbsName === "designercomponents-private")
+ addDesignerComponentsModule(modules);
+ }
+
+ replaceQtLibNamesWithFilePath(modules, qtProps);
+ removeDuplicatedDependencyLibs(modules);
+
+ return modules;
+}
+
+function getQtInfo(qmakeFilePath) {
+ if (!File.exists(qmakeFilePath)) {
+ throw "The specified qmake file path '"
+ + FileInfo.toNativeSeparators(qmakeFilePath) + "' does not exist.";
+ }
+ var qtProps = getQtProperties(qmakeFilePath);
+ var androidAbis = [];
+ if (qtProps.androidAbis !== undefined) {
+ // Multiple androidAbis detected: Qt >= 5.14
+ androidAbis = qtProps.androidAbis;
+ } else {
+ // Single abi detected: Qt < 5.14
+ androidAbis.push('');
+ }
+ if (androidAbis.length > 1)
+ console.info("Qt with multiple abi detected: '" + androidAbis + "'");
+
+ var result = {};
+ result.qtProps = qtProps;
+ result.abiInfos = [];
+ result.qmakeFilePath = qmakeFilePath;
+ for (a = 0; a < androidAbis.length; ++a) {
+ var abiInfo = {};
+ if (androidAbis.length > 1)
+ console.info("Found abi '" + androidAbis[a] + "'...");
+ abiInfo.androidAbi = androidAbis[a];
+ var allModules = qtProps.qtMajorVersion < 5
+ ? allQt4Modules(qtProps) : allQt5Modules(qtProps, androidAbis[a]);;
+ abiInfo.modules = {};
+ for (var i = 0; i < allModules.length; ++i) {
+ var module = allModules[i];
+ abiInfo.modules[module.qbsName] = module;
+ }
+ abiInfo.pluginsByType = {};
+ abiInfo.nonEssentialPlugins = [];
+ for (var moduleName in abiInfo.modules) {
+ var m = abiInfo.modules[moduleName];
+ if (m.isPlugin) {
+ if (!abiInfo.pluginsByType[m.pluginData.type])
+ abiInfo.pluginsByType[m.pluginData.type] = [];
+ abiInfo.pluginsByType[m.pluginData.type].push(m.name);
+ if (!m.pluginData.autoLoad)
+ abiInfo.nonEssentialPlugins.push(m.name);
+ }
+ }
+
+ result.abiInfos.push(abiInfo);
+ }
+ return result;
+}
+
+function configure(qmakeFilePaths) {
+ var result = [];
+ qmakeFilePaths = getQmakeFilePaths(qmakeFilePaths);
+ if (!qmakeFilePaths || qmakeFilePaths.length === 0)
+ return result;
+ for (var i = 0; i < qmakeFilePaths.length; ++i) {
+ var qtInfo = {};
+ try {
+ console.info("Getting info about Qt at '"
+ + FileInfo.toNativeSeparators(qmakeFilePaths[i]) + "'...");
+ qtInfo = getQtInfo(qmakeFilePaths[i]);
+ result.push(qtInfo);
+ } catch (e) {
+ console.warn("Error getting info about Qt for '"
+ + FileInfo.toNativeSeparators(qmakeFilePaths[i]) + "': " + e);
+ throw e;
+ }
+ }
+ return result;
+}
diff --git a/share/qbs/imports/qbs/ProviderUtils/provider-utils.js b/share/qbs/imports/qbs/ProviderUtils/provider-utils.js
new file mode 100644
index 000000000..06e703a7a
--- /dev/null
+++ b/share/qbs/imports/qbs/ProviderUtils/provider-utils.js
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com)
+** 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 Utilities = require("qbs.Utilities");
+
+function pkgConfigToModuleName(packageName) {
+ return packageName.replace(/\./g, '-');
+}
+
+function msvcPrefix() { return "win32-msvc"; }
+
+function isMsvcQt(qtProps) { return qtProps.mkspecName.startsWith(msvcPrefix()); }
+
+function isMinGwQt(qtProps) {
+ return qtProps.mkspecName.startsWith("win32-g++") || qtProps.mkspecName.startsWith("mingw");
+}
+
+function isDesktopWindowsQt(qtProps) {
+ return qtProps.mkspecName.startsWith("win32-") || isMinGwQt(qtProps);
+}
+
+function qtNeedsDSuffix(qtProps) {
+ return !isMinGwQt(qtProps)
+ || Utilities.versionCompare(qtProps.qtVersion, "5.14.0") < 0
+ || qtProps.configItems.contains("debug_and_release");
+}
+
+function qtIsFramework(modInfo, qtProps) {
+ if (!qtProps.frameworkBuild || modInfo.isStaticLibrary)
+ return false;
+ var modulesNeverBuiltAsFrameworks = [
+ "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "harfbuzzng"
+ ];
+
+ if (qtProps.qtMajorVersion <= 5) {
+ modulesNeverBuiltAsFrameworks.push("uitools"); // is framework since qt6
+ }
+
+ return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName);
+}
+
+function qtLibBaseName(modInfo, libName, debugBuild, qtProps) {
+ var name = libName;
+ if (qtProps.mkspecName.startsWith("win")) {
+ if (debugBuild && qtNeedsDSuffix(qtProps))
+ name += 'd';
+ if (!modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5)
+ name += qtProps.qtMajorVersion;
+ }
+ if (qtProps.mkspecName.contains("macx")
+ || qtProps.mkspecName.contains("ios")
+ || qtProps.mkspecName.contains("darwin")) {
+ if (!qtIsFramework(modInfo, qtProps)
+ && qtProps.buildVariant.contains("debug")
+ && (!qtProps.buildVariant.contains("release") || debugBuild)) {
+ name += "_debug";
+ }
+ }
+ return name;
+}
+
+function qtModuleNameWithoutPrefix(modInfo) {
+ if (modInfo.name === "Phonon")
+ return "phonon";
+ if (!modInfo.modulePrefix && modInfo.name.startsWith("Qt"))
+ return modInfo.name.slice(2); // Strip off "Qt".
+ if (modInfo.name.startsWith(modInfo.modulePrefix))
+ return modInfo.name.slice(modInfo.modulePrefix.length);
+ return modInfo.name;
+}
+
+function qtLibraryBaseName(modInfo, qtProps, debugBuild) {
+ if (modInfo.isPlugin)
+ return qtLibBaseName(modInfo, modInfo.name, debugBuild, qtProps);
+
+ // Some modules use a different naming scheme, so it doesn't get boring.
+ var libNameBroken = modInfo.name === "Enginio"
+ || modInfo.name === "DataVisualization"
+ || modInfo.name === "Phonon";
+
+ var libName = "";
+ if (!modInfo.isExternal) {
+ libName += !modInfo.modulePrefix && !libNameBroken ? "Qt" : modInfo.modulePrefix;
+ if (qtProps.qtMajorVersion >= 5 && !qtIsFramework(modInfo, qtProps) && !libNameBroken)
+ libName += qtProps.qtMajorVersion;
+ }
+ libName += qtModuleNameWithoutPrefix(modInfo);
+ if (!modInfo.isExternal)
+ libName += qtProps.qtLibInfix;
+ return qtLibBaseName(modInfo, libName, debugBuild, qtProps);
+}
+
+function qtLibNameForLinker(modInfo, qtProps, debugBuild) {
+ if (!modInfo.hasLibrary)
+ return undefined;
+ var libName = qtLibraryBaseName(modInfo, qtProps, debugBuild);
+ if (qtProps.mkspecName.contains("msvc"))
+ libName += ".lib";
+ return libName;
+}
diff --git a/share/qbs/imports/qbs/base/AndroidApk.qbs b/share/qbs/imports/qbs/base/AndroidApk.qbs
index 5f86d8457..70cf6aa93 100644
--- a/share/qbs/imports/qbs/base/AndroidApk.qbs
+++ b/share/qbs/imports/qbs/base/AndroidApk.qbs
@@ -32,7 +32,7 @@ import qbs.File
import qbs.FileInfo
Product {
- type: ["android.apk"]
+ type: ["android.package"]
qbs.targetPlatform: "android"
Depends { name: "Android.sdk" }
}
diff --git a/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs b/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs
index 134f4dee0..462b96b7c 100644
--- a/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs
+++ b/share/qbs/imports/qbs/base/AppleApplicationDiskImage.qbs
@@ -34,7 +34,8 @@ import qbs.ModUtils
AppleDiskImage {
property string sourceBase: "/Applications"
- readonly property string absoluteSourceBase: FileInfo.joinPaths(qbs.installRoot, sourceBase)
+ readonly property string absoluteSourceBase:
+ FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, sourceBase)
property stringList symlinks: ["/Applications:Applications"]
readonly property string stageDirectory: FileInfo.joinPaths(destinationDirectory, "Volumes", dmg.volumeName)
@@ -56,7 +57,7 @@ AppleDiskImage {
prepare: Array.prototype.map.call(outputs["dmg.input"], function (symlink) {
var cmd = new Command("ln", ["-sfn", symlink.dmg.symlinkTarget, symlink.filePath]);
cmd.workingDirectory = product.stageDirectory;
- cmd.description = "symlinking " + symlink.fileName + " => " + symlink.dmg.symlinkTarget;
+ cmd.description = "symlinking " + symlink.fileName + " to " + symlink.dmg.symlinkTarget;
return cmd;
});
}
diff --git a/share/qbs/imports/qbs/base/Application.qbs b/share/qbs/imports/qbs/base/Application.qbs
index 694cfb83b..76536c68e 100644
--- a/share/qbs/imports/qbs/base/Application.qbs
+++ b/share/qbs/imports/qbs/base/Application.qbs
@@ -29,7 +29,7 @@
****************************************************************************/
NativeBinary {
- type: isForAndroid && !consoleApplication ? ["android.apk"] : ["application"]
+ type: isForAndroid && !consoleApplication ? ["android.package"] : ["application"]
property bool usesNativeCode
@@ -60,10 +60,18 @@ NativeBinary {
installDir: isBundle ? "Applications" : "bin"
Group {
- condition: install
- fileTagsFilter: isBundle ? "bundle.content" : "application";
+ condition: install && _installable
+ fileTagsFilter: isBundle ? "bundle.content" : "application"
qbs.install: true
qbs.installDir: installDir
qbs.installSourceBase: isBundle ? destinationDirectory : outer
}
+
+ Group {
+ condition: installDebugInformation && _installable
+ fileTagsFilter: ["debuginfo_app"]
+ qbs.install: true
+ qbs.installDir: debugInformationInstallDir
+ qbs.installSourceBase: destinationDirectory
+ }
}
diff --git a/share/qbs/imports/qbs/base/ApplicationExtension.qbs b/share/qbs/imports/qbs/base/ApplicationExtension.qbs
index 140475909..3d25d5f73 100644
--- a/share/qbs/imports/qbs/base/ApplicationExtension.qbs
+++ b/share/qbs/imports/qbs/base/ApplicationExtension.qbs
@@ -34,14 +34,21 @@ XPCService {
type: base.concat(["applicationextension"])
property bool _useLegacyExtensionLibraries:
- qbs.targetOS.contains("macos") && parseInt(xcode.sdkVersion.split(".")[1], 10) < 11 ||
- qbs.targetOS.contains("ios") && parseInt(xcode.sdkVersion.split(".")[0], 10) < 9
+ qbs.targetOS.contains("macos")
+ && xcode.present
+ && parseInt(xcode.sdkVersion.split(".")[1], 10) < 11
+ || qbs.targetOS.contains("ios")
+ && xcode.present
+ && parseInt(xcode.sdkVersion.split(".")[0], 10) < 9
cpp.entryPoint: "_NSExtensionMain"
+ cpp.frameworkPaths: base.concat(_useLegacyExtensionLibraries
+ ? qbs.sysroot + "/System/Library/PrivateFrameworks/"
+ : [])
cpp.frameworks: {
var frameworks = base.concat(["Foundation"]);
if (_useLegacyExtensionLibraries)
- frameworks.push(qbs.sysroot + "/System/Library/PrivateFrameworks/PlugInKit.framework");
+ frameworks.push("PlugInKit");
return frameworks;
}
diff --git a/share/qbs/imports/qbs/base/AutotestRunner.qbs b/share/qbs/imports/qbs/base/AutotestRunner.qbs
index 62ba7740b..30921eb38 100644
--- a/share/qbs/imports/qbs/base/AutotestRunner.qbs
+++ b/share/qbs/imports/qbs/base/AutotestRunner.qbs
@@ -94,7 +94,7 @@ Product {
.concat([commandFilePath])
.concat(arguments);
var cmd = new Command(fullCommandLine[0], fullCommandLine.slice(1));
- cmd.description = "Running test " + input.fileName;
+ cmd.description = "running test " + input.fileName;
cmd.environment = product.environment;
cmd.workingDirectory = workingDir;
cmd.timeout = timeout;
diff --git a/share/qbs/imports/qbs/base/DynamicLibrary.qbs b/share/qbs/imports/qbs/base/DynamicLibrary.qbs
index 267519a42..818665e38 100644
--- a/share/qbs/imports/qbs/base/DynamicLibrary.qbs
+++ b/share/qbs/imports/qbs/base/DynamicLibrary.qbs
@@ -30,24 +30,4 @@
Library {
type: ["dynamiclibrary"].concat(isForAndroid ? ["android.nativelibrary"] : [])
-
- installDir: isBundle ? "Library/Frameworks" : qbs.targetOS.contains("windows")
- ? "bin" : "lib"
- property bool installImportLib: false
- property string importLibInstallDir: "lib"
-
- Group {
- condition: install
- fileTagsFilter: isBundle ? "bundle.content" : ["dynamiclibrary", "dynamiclibrary_symlink"]
- qbs.install: true
- qbs.installDir: installDir
- qbs.installSourceBase: isBundle ? destinationDirectory : outer
- }
-
- Group {
- condition: installImportLib
- fileTagsFilter: "dynamiclibrary_import"
- qbs.install: true
- qbs.installDir: importLibInstallDir
- }
}
diff --git a/share/qbs/imports/qbs/base/InnoSetup.qbs b/share/qbs/imports/qbs/base/InnoSetup.qbs
index 5ea076eb3..e40a627f5 100644
--- a/share/qbs/imports/qbs/base/InnoSetup.qbs
+++ b/share/qbs/imports/qbs/base/InnoSetup.qbs
@@ -29,6 +29,7 @@
****************************************************************************/
Product {
- Depends { name: "innosetup"; condition: qbs.targetOS.contains("windows") }
+ condition: qbs.targetOS.contains("windows") && innosetup.present
+ Depends { name: "innosetup"; condition: qbs.targetOS.contains("windows"); required: false }
type: ["innosetup.exe"]
}
diff --git a/share/qbs/imports/qbs/base/Library.qbs b/share/qbs/imports/qbs/base/Library.qbs
index 615c2319f..914d79bcb 100644
--- a/share/qbs/imports/qbs/base/Library.qbs
+++ b/share/qbs/imports/qbs/base/Library.qbs
@@ -34,4 +34,61 @@ NativeBinary {
return ["staticlibrary"];
return ["dynamiclibrary"].concat(isForAndroid ? ["android.nativelibrary"] : []);
}
+
+ readonly property bool isDynamicLibrary: type.contains("dynamiclibrary")
+ readonly property bool isStaticLibrary: type.contains("staticlibrary")
+ readonly property bool isLoadableModule: type.contains("loadablemodule")
+
+ installDir: {
+ if (isBundle)
+ return "Library/Frameworks";
+ if (isDynamicLibrary)
+ return qbs.targetOS.contains("windows") ? "bin" : "lib";
+ if (isStaticLibrary)
+ return "lib";
+ }
+
+ property bool installImportLib: false
+ property string importLibInstallDir: "lib"
+
+ Group {
+ condition: install && _installable
+ fileTagsFilter: {
+ if (isBundle)
+ return ["bundle.content"];
+ if (isDynamicLibrary)
+ return ["dynamiclibrary", "dynamiclibrary_symlink"];
+ if (isStaticLibrary)
+ return ["staticlibrary"];
+ if (isLoadableModule)
+ return ["loadablemodule"];
+ return [];
+ }
+ qbs.install: true
+ qbs.installDir: installDir
+ qbs.installSourceBase: isBundle ? destinationDirectory : outer
+ }
+
+ Group {
+ condition: installImportLib
+ && type.contains("dynamiclibrary")
+ && _installable
+ fileTagsFilter: "dynamiclibrary_import"
+ qbs.install: true
+ qbs.installDir: importLibInstallDir
+ }
+
+ Group {
+ condition: installDebugInformation && _installable
+ fileTagsFilter: {
+ if (isDynamicLibrary)
+ return ["debuginfo_dll"];
+ else if (isLoadableModule)
+ return ["debuginfo_loadablemodule"];
+ return [];
+ }
+ qbs.install: true
+ qbs.installDir: debugInformationInstallDir
+ qbs.installSourceBase: destinationDirectory
+ }
}
diff --git a/share/qbs/imports/qbs/base/NativeBinary.qbs b/share/qbs/imports/qbs/base/NativeBinary.qbs
index 3597f348f..900ed9faa 100644
--- a/share/qbs/imports/qbs/base/NativeBinary.qbs
+++ b/share/qbs/imports/qbs/base/NativeBinary.qbs
@@ -35,6 +35,12 @@ Product {
property bool install: false
property string installDir
+ // Product artifacts should be installed if it's not multiplexed or aggregated,
+ // or if it is multiplexed and it's the aggregate product
+ readonly property bool _installable: !multiplexed || !aggregate || !multiplexConfigurationId
+
+ property bool installDebugInformation: install
+ property string debugInformationInstallDir: installDir
Depends { name: "bundle"; condition: isForDarwin }
diff --git a/share/qbs/imports/qbs/base/QtGuiApplication.qbs b/share/qbs/imports/qbs/base/QtGuiApplication.qbs
index 61bc69752..7b2abf017 100644
--- a/share/qbs/imports/qbs/base/QtGuiApplication.qbs
+++ b/share/qbs/imports/qbs/base/QtGuiApplication.qbs
@@ -28,6 +28,6 @@
**
****************************************************************************/
-CppApplication {
+QtApplication {
Depends { name: "Qt.gui" }
}
diff --git a/share/qbs/imports/qbs/base/QtModule.qbs b/share/qbs/imports/qbs/base/QtModule.qbs
new file mode 100644
index 000000000..35421436f
--- /dev/null
+++ b/share/qbs/imports/qbs/base/QtModule.qbs
@@ -0,0 +1,87 @@
+import qbs.FileInfo
+
+Module {
+ condition: (qbs.targetPlatform === targetPlatform || isCombinedUIKitBuild)
+ && (!qbs.architecture
+ || architectures.length === 0
+ || architectures.contains(qbs.architecture))
+
+ readonly property bool isCombinedUIKitBuild: ["ios", "tvos", "watchos"].contains(targetPlatform)
+ && ["x86", "x86_64"].contains(qbs.architecture)
+ && qbs.targetPlatform === targetPlatform + "-simulator"
+
+ Depends { name: "cpp" }
+ Depends { name: "Qt.core" }
+
+ Depends { name: "Qt.plugin_support" }
+ property stringList pluginTypes
+ Qt.plugin_support.pluginTypes: pluginTypes
+ Depends {
+ condition: Qt.core.staticBuild && !isPlugin
+ name: "Qt";
+ submodules: {
+ // We have to pull in all plugins here, because dependency resolving happens
+ // before module merging, and we don't know yet if someone set
+ // Qt.pluginSupport.pluginsByType in the product.
+ // TODO: We might be able to use Qt.pluginSupport.pluginsByType now.
+ // The real filtering is done later by the plugin module files themselves.
+ var list = [];
+ var allPlugins = Qt.plugin_support.allPluginsByType;
+ for (var i = 0; i < (pluginTypes || []).length; ++i)
+ Array.prototype.push.apply(list, allPlugins[pluginTypes[i]])
+ return list;
+ }
+ }
+
+ 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 architectures
+ property string targetPlatform
+ 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
+ property bool enableLinking: qtModuleName != undefined && hasLibrary
+ property stringList moduleConfig
+
+ Properties {
+ condition: enableLinking
+ cpp.staticLibraries: staticLibs
+ cpp.dynamicLibraries: dynamicLibs
+ cpp.frameworks: mFrameworks.concat(!isStaticLibrary && Qt.core.frameworkBuild
+ ? [libNameForLinker] : [])
+ cpp.systemFrameworkPaths: mFrameworkPaths
+ }
+}
diff --git a/share/qbs/imports/qbs/base/QtPlugin.qbs b/share/qbs/imports/qbs/base/QtPlugin.qbs
new file mode 100644
index 000000000..883e34465
--- /dev/null
+++ b/share/qbs/imports/qbs/base/QtPlugin.qbs
@@ -0,0 +1,51 @@
+import qbs.FileInfo
+import qbs.TextFile
+
+QtModule {
+ isPlugin: true
+
+ property string className
+ property stringList extendsModules
+
+ enableLinking: {
+ if (!base)
+ return false;
+ if (!isStaticLibrary)
+ return false;
+ if (!Qt.plugin_support.linkPlugins)
+ return false;
+ if (!(Qt.plugin_support.enabledPlugins || []).contains(qtModuleName))
+ return false;
+ if (!extendsModules || extendsModules.length === 0)
+ return true;
+ for (var i = 0; i < extendsModules.length; ++i) {
+ var moduleName = extendsModules[i];
+ if (product.Qt[moduleName] && product.Qt[moduleName].present)
+ return true;
+ }
+ return false;
+ }
+
+ Rule {
+ condition: enableLinking
+ multiplex: true
+ Artifact {
+ filePath: product.targetName + "_qt_plugin_import_"
+ + product.moduleProperty(product.moduleName, "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 <QtPlugin>\n\nQ_IMPORT_PLUGIN(" + className + ")");
+ f.close();
+ };
+ return cmd;
+ }
+ }
+}
diff --git a/share/qbs/imports/qbs/base/StaticLibrary.qbs b/share/qbs/imports/qbs/base/StaticLibrary.qbs
index 4eea3c991..5a78a83b0 100644
--- a/share/qbs/imports/qbs/base/StaticLibrary.qbs
+++ b/share/qbs/imports/qbs/base/StaticLibrary.qbs
@@ -30,13 +30,4 @@
Library {
type: ["staticlibrary"]
-
- installDir: isBundle ? "Library/Frameworks" : "lib"
- Group {
- condition: install
- fileTagsFilter: isBundle ? "bundle.content" : "staticlibrary";
- qbs.install: true
- qbs.installDir: installDir
- qbs.installSourceBase: isBundle ? destinationDirectory : outer
- }
}
diff --git a/share/qbs/module-providers/Qt/provider.qbs b/share/qbs/module-providers/Qt/provider.qbs
index 33083c51d..0d036c04d 100644
--- a/share/qbs/module-providers/Qt/provider.qbs
+++ b/share/qbs/module-providers/Qt/provider.qbs
@@ -1,6 +1,14 @@
import "setup-qt.js" as SetupQt
+import qbs.Probes
ModuleProvider {
+ Probes.QmakeProbe {
+ id: probe
+ qmakePaths: qmakeFilePaths
+ }
property stringList qmakeFilePaths
- relativeSearchPaths: SetupQt.doSetup(qmakeFilePaths, outputBaseDir, path, qbs)
+ readonly property varList _qtInfos: probe.qtInfos
+ condition: moduleName.startsWith("Qt.")
+ isEager: false
+ relativeSearchPaths: SetupQt.doSetup(moduleName, _qtInfos, outputBaseDir, path)
}
diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js
index 3ddc214d3..9a314822b 100644
--- a/share/qbs/module-providers/Qt/setup-qt.js
+++ b/share/qbs/module-providers/Qt/setup-qt.js
@@ -37,1103 +37,13 @@
**
****************************************************************************/
-var Environment = require("qbs.Environment");
var File = require("qbs.File");
var FileInfo = require("qbs.FileInfo");
-var Process = require("qbs.Process");
+var ModUtils = require("qbs.ModUtils");
+var ProviderUtils = require("qbs.ProviderUtils");
var TextFile = require("qbs.TextFile");
var Utilities = require("qbs.Utilities");
-function splitNonEmpty(s, c) { return s.split(c).filter(function(e) { return e; }); }
-function toNative(p) { return FileInfo.toNativeSeparators(p); }
-function exeSuffix(qbs) { return qbs.hostOS.contains("windows") ? ".exe" : ""; }
-
-function getQmakeFilePaths(qmakeFilePaths, qbs) {
- if (qmakeFilePaths && qmakeFilePaths.length > 0)
- return qmakeFilePaths;
- console.info("Detecting Qt installations...");
- var pathValue = Environment.getEnv("PATH");
- if (!pathValue)
- return [];
- var dirs = splitNonEmpty(pathValue, qbs.pathListSeparator);
- var suffix = exeSuffix(qbs);
- var filePaths = [];
- for (var i = 0; i < dirs.length; ++i) {
- var candidate = FileInfo.joinPaths(dirs[i], "qmake" + suffix);
- var canonicalCandidate = FileInfo.canonicalPath(candidate);
- if (!canonicalCandidate || !File.exists(canonicalCandidate))
- continue;
- if (FileInfo.completeBaseName(canonicalCandidate) !== "qtchooser")
- candidate = canonicalCandidate;
- if (!filePaths.contains(candidate)) {
- console.info("Found Qt at '" + toNative(candidate) + "'.");
- filePaths.push(candidate);
- }
- }
- return filePaths;
-}
-
-function queryQmake(qmakeFilePath) {
- var qmakeProcess = new Process;
- qmakeProcess.exec(qmakeFilePath, ["-query"]);
- if (qmakeProcess.exitCode() !== 0) {
- throw "The qmake executable '" + toNative(qmakeFilePath) + "' failed with exit code "
- + qmakeProcess.exitCode() + ".";
- }
- var queryResult = {};
- while (!qmakeProcess.atEnd()) {
- var line = qmakeProcess.readLine();
- var index = (line || "").indexOf(":");
- if (index !== -1)
- queryResult[line.slice(0, index)] = line.slice(index + 1).trim();
- }
- return queryResult;
-}
-
-function pathQueryValue(queryResult, key) {
- var p = queryResult[key];
- if (p)
- return FileInfo.fromNativeSeparators(p);
-}
-
-function readFileContent(filePath) {
- var f = new TextFile(filePath, TextFile.ReadOnly);
- var content = f.readAll();
- f.close();
- return content;
-}
-
-// TODO: Don't do the split every time...
-function configVariable(configContent, key) {
- var configContentLines = configContent.split('\n');
- var regexp = new RegExp("\\s*" + key + "\\s*\\+{0,1}=(.*)");
- for (var i = 0; i < configContentLines.length; ++i) {
- var line = configContentLines[i];
- var match = regexp.exec(line);
- if (match)
- return match[1].trim();
- }
-}
-
-function configVariableItems(configContent, key) {
- return splitNonEmpty(configVariable(configContent, key), ' ');
-}
-
-function msvcPrefix() { return "win32-msvc"; }
-
-function isMsvcQt(qtProps) { return qtProps.mkspecName.startsWith(msvcPrefix()); }
-
-function msvcCompilerVersionForYear(year) {
- var mapping = {
- "2005": "14", "2008": "15", "2010": "16", "2012": "17", "2013": "18", "2015": "19",
- "2017": "19.1", "2019": "19.2"
- };
- return mapping[year];
-}
-
-function msvcCompilerVersionFromMkspecName(mkspecName) {
- return msvcCompilerVersionForYear(mkspecName.slice(msvcPrefix().length));
-}
-
-function addQtBuildVariant(qtProps, buildVariantName) {
- if (qtProps.qtConfigItems.contains(buildVariantName))
- qtProps.buildVariant.push(buildVariantName);
-}
-
-function checkForStaticBuild(qtProps) {
- if (qtProps.qtMajorVersion >= 5)
- return qtProps.qtConfigItems.contains("static");
- if (qtProps.frameworkBuild)
- return false; // there are no Qt4 static frameworks
- var isWin = qtProps.mkspecName.startsWith("win");
- var libDir = isWin ? qtProps.binaryPath : qtProps.libraryPath;
- var coreLibFiles = File.directoryEntries(libDir, File.Files)
- .filter(function(fp) { return fp.contains("Core"); });
- if (coreLibFiles.length === 0)
- throw "Could not determine whether Qt is a static build.";
- for (var i = 0; i < coreLibFiles.length; ++i) {
- if (Utilities.isSharedLibrary(coreLibFiles[i]))
- return false;
- }
- return true;
-}
-
-function isForMinGw(qtProps) {
- return qtProps.mkspecName.startsWith("win32-g++") || qtProps.mkspecName.startsWith("mingw");
-}
-
-function targetsDesktopWindows(qtProps) {
- return qtProps.mkspecName.startsWith("win32-") || isForMinGw(qtProps);
-}
-
-function guessMinimumWindowsVersion(qtProps) {
- if (qtProps.mkspecName.startsWith("winrt-"))
- return "10.0";
- if (!targetsDesktopWindows(qtProps))
- return "";
- if (qtProps.architecture === "x86_64" || qtProps.architecture === "ia64")
- return "5.2"
- var match = qtProps.mkspecName.match(/^win32-msvc(\d+)$/);
- if (match) {
- var msvcVersion = match[1];
- if (msvcVersion < 2012)
- return "5.0";
- return "5.1";
- }
- return qtProps.qtMajorVersion < 5 ? "5.0" : "5.1";
-}
-
-function fillEntryPointLibs(qtProps, debug) {
- result = [];
- var isMinGW = isForMinGw(qtProps);
-
- // Some Linux distributions rename the qtmain library.
- var qtMainCandidates = ["qtmain"];
- if (isMinGW && qtProps.qtMajorVersion === 5)
- qtMainCandidates.push("qt5main");
-
- for (var i = 0; i < qtMainCandidates.length; ++i) {
- var baseNameCandidate = qtMainCandidates[i];
- var qtmain = qtProps.libraryPath + '/';
- if (isMinGW)
- qtmain += "lib";
- qtmain += baseNameCandidate + qtProps.qtLibInfix;
- if (debug)
- qtmain += 'd';
- if (isMinGW) {
- qtmain += ".a";
- } else {
- qtmain += ".lib";
- if (Utilities.versionCompare(qtProps.qtVersion, "5.4.0") >= 0)
- result.push("Shell32.lib");
- }
- if (File.exists(qtmain)) {
- result.push(qtmain);
- break;
- }
- }
- if (result.length === 0) {
- console.warn("Could not find the qtmain library at '" + toNative(qtProps.libraryPath)
- + "'. You will not be able to link Qt applications.");
- }
- return result;
-}
-
-function getQtProperties(qmakeFilePath, qbs) {
- var queryResult = queryQmake(qmakeFilePath);
- var qtProps = {};
- qtProps.installPrefixPath = pathQueryValue(queryResult, "QT_INSTALL_PREFIX");
- qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS");
- qtProps.includePath = pathQueryValue(queryResult, "QT_INSTALL_HEADERS");
- qtProps.libraryPath = pathQueryValue(queryResult, "QT_INSTALL_LIBS");
- qtProps.hostLibraryPath = pathQueryValue(queryResult, "QT_HOST_LIBS");
- qtProps.binaryPath = pathQueryValue(queryResult, "QT_HOST_BINS")
- || pathQueryValue(queryResult, "QT_INSTALL_BINS");
- qtProps.documentationPath = pathQueryValue(queryResult, "QT_INSTALL_DOCS");
- qtProps.pluginPath = pathQueryValue(queryResult, "QT_INSTALL_PLUGINS");
- qtProps.qmlPath = pathQueryValue(queryResult, "QT_INSTALL_QML");
- qtProps.qmlImportPath = pathQueryValue(queryResult, "QT_INSTALL_IMPORTS");
- qtProps.qtVersion = queryResult.QT_VERSION;
-
- var mkspecsBaseSrcPath;
- if (Utilities.versionCompare(qtProps.qtVersion, "5") >= 0) {
- qtProps.mkspecBasePath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA"),
- "mkspecs");
- mkspecsBaseSrcPath = FileInfo.joinPaths(pathQueryValue(queryResult, "QT_HOST_DATA/src"),
- "mkspecs");
- } else {
- qtProps.mkspecBasePath = FileInfo.joinPaths
- (pathQueryValue(queryResult, "QT_INSTALL_DATA"), "mkspecs");
- }
- if (!File.exists(qtProps.mkspecBasePath))
- throw "Cannot extract the mkspecs directory.";
-
- var qconfigContent = readFileContent(FileInfo.joinPaths(qtProps.mkspecBasePath,
- "qconfig.pri"));
- qtProps.qtMajorVersion = parseInt(configVariable(qconfigContent, "QT_MAJOR_VERSION"));
- qtProps.qtMinorVersion = parseInt(configVariable(qconfigContent, "QT_MINOR_VERSION"));
- qtProps.qtPatchVersion = parseInt(configVariable(qconfigContent, "QT_PATCH_VERSION"));
- qtProps.qtNameSpace = configVariable(qconfigContent, "QT_NAMESPACE");
- qtProps.qtLibInfix = configVariable(qconfigContent, "QT_LIBINFIX") || "";
- qtProps.architecture = configVariable(qconfigContent, "QT_TARGET_ARCH")
- || configVariable(qconfigContent, "QT_ARCH") || "x86";
- qtProps.configItems = configVariableItems(qconfigContent, "CONFIG");
- qtProps.qtConfigItems = configVariableItems(qconfigContent, "QT_CONFIG");
-
- // retrieve the mkspec
- if (qtProps.qtMajorVersion >= 5) {
- qtProps.mkspecName = queryResult.QMAKE_XSPEC;
- qtProps.mkspecPath = FileInfo.joinPaths(qtProps.mkspecBasePath, qtProps.mkspecName);
- if (mkspecsBaseSrcPath && !File.exists(qtProps.mkspecPath))
- qtProps.mkspecPath = FileInfo.joinPaths(mkspecsBaseSrcPath, qtProps.mkspecName);
- } else {
- if (qbs.hostOS.contains("windows")) {
- var baseDirPath = FileInfo.joinPaths(qtProps.mkspecBasePath, "default");
- var fileContent = readFileContent(FileInfo.joinPaths(baseDirPath, "qmake.conf"));
- qtProps.mkspecPath = configVariable(fileContent, "QMAKESPEC_ORIGINAL");
- if (!File.exists(qtProps.mkspecPath)) {
- // Work around QTBUG-28792.
- // The value of QMAKESPEC_ORIGINAL is wrong for MinGW packages. Y u h8 me?
- var match = fileContent.exec(/\binclude\(([^)]+)\/qmake\.conf\)/m);
- if (match) {
- qtProps.mkspecPath = FileInfo.cleanPath(FileInfo.joinPaths(
- baseDirPath, match[1]));
- }
- }
- } else {
- qtProps.mkspecPath = FileInfo.canonicalPath(
- FileInfo.joinPaths(qtProps.mkspecBasePath, "default"));
- }
-
- // 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++
- qtProps.mkspecPath = FileInfo.cleanPath(qtProps.mkspecPath);
-
- qtProps.mkspecName = qtProps.mkspecPath;
- var idx = qtProps.mkspecName.lastIndexOf('/');
- if (idx !== -1)
- qtProps.mkspecName = qtProps.mkspecName.slice(idx + 1);
- }
- if (!File.exists(qtProps.mkspecPath))
- throw "mkspec '" + toNative(qtProps.mkspecPath) + "' does not exist";
-
- // determine MSVC version
- if (isMsvcQt(qtProps)) {
- var msvcMajor = configVariable(qconfigContent, "QT_MSVC_MAJOR_VERSION");
- var msvcMinor = configVariable(qconfigContent, "QT_MSVC_MINOR_VERSION");
- var msvcPatch = configVariable(qconfigContent, "QT_MSVC_PATCH_VERSION");
- if (msvcMajor && msvcMinor && msvcPatch)
- qtProps.msvcVersion = msvcMajor + "." + msvcMinor + "." + msvcPatch;
- else
- qtProps.msvcVersion = msvcCompilerVersionFromMkspecName(qtProps.mkspecName);
- }
-
- // determine whether we have a framework build
- qtProps.frameworkBuild = qtProps.mkspecPath.contains("macx")
- && qtProps.configItems.contains("qt_framework");
-
- // determine whether Qt is built with debug, release or both
- qtProps.buildVariant = [];
- addQtBuildVariant(qtProps, "debug");
- addQtBuildVariant(qtProps, "release");
-
- qtProps.staticBuild = checkForStaticBuild(qtProps);
-
- // determine whether user apps require C++11
- if (qtProps.qtConfigItems.contains("c++11") && qtProps.staticBuild)
- qtProps.configItems.push("c++11");
-
- // Set the minimum operating system versions appropriate for this Qt version
- qtProps.windowsVersion = guessMinimumWindowsVersion(qtProps);
- if (qtProps.windowsVersion) { // Is target OS Windows?
- if (qtProps.buildVariant.contains("debug"))
- qtProps.entryPointLibsDebug = fillEntryPointLibs(qtProps, true);
- if (qtProps.buildVariant.contains("release"))
- qtProps.entryPointLibsRelease = fillEntryPointLibs(qtProps, false);
- } else if (qtProps.mkspecPath.contains("macx")) {
- if (qtProps.qtMajorVersion >= 5) {
- var lines = getFileContentsRecursively(FileInfo.joinPaths(qtProps.mkspecPath,
- "qmake.conf"));
- for (var i = 0; i < lines.length; ++i) {
- var line = lines[i].trim();
- match = line.match
- (/^QMAKE_(MACOSX|IOS|TVOS|WATCHOS)_DEPLOYMENT_TARGET\s*=\s*(.*)\s*$/);
- if (match) {
- var platform = match[1];
- var version = match[2];
- if (platform === "MACOSX")
- qtProps.macosVersion = version;
- else if (platform === "IOS")
- qtProps.iosVersion = version;
- else if (platform === "TVOS")
- qtProps.tvosVersion = version;
- else if (platform === "WATCHOS")
- qtProps.watchosVersion = version;
- }
- }
- var isMac = qtProps.mkspecName !== "macx-ios-clang"
- && qtProps.mkspecName !== "macx-tvos-clang"
- && qtProps.mkspecName !== "macx-watchos-clang";
- if (isMac) {
- // Qt 5.0.x placed the minimum version in a different file
- if (!qtProps.macosVersion)
- qtProps.macosVersion = "10.6";
-
- // If we're using C++11 with libc++, make sure the deployment target is >= 10.7
- if (Utilities.versionCompare(qtProps.macosVersion, "10, 7") < 0
- && qtProps.qtConfigItems.contains("c++11")) {
- qtProps.macosVersion = "10.7";
- }
- }
- } else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 6) {
- var qconfigDir = qtProps.frameworkBuild
- ? FileInfo.joinPaths(qtProps.libraryPath, "QtCore.framework", "Headers")
- : FileInfo.joinPaths(qtProps.includePath, "Qt");
- try {
- var qconfig = new TextFile(FileInfo.joinPaths(qconfigDir, "qconfig.h"),
- TextFile.ReadOnly);
- var qtCocoaBuild = false;
- var ok = true;
- do {
- line = qconfig.readLine();
- if (line.match(/\s*#define\s+QT_MAC_USE_COCOA\s+1\s*/)) {
- qtCocoaBuild = true;
- break;
- }
- } while (!qconfig.atEof());
- qtProps.macosVersion = qtCocoaBuild ? "10.5" : "10.4";
- }
- catch (e) {}
- finally {
- if (qconfig)
- qconfig.close();
- }
- if (!qtProps.macosVersion) {
- throw "Could not determine whether Qt is using Cocoa or Carbon from '"
- + toNative(qconfig.filePath()) + "'.";
- }
- }
- } else if (qtProps.mkspecPath.contains("android")) {
- if (qtProps.qtMajorVersion >= 5)
- qtProps.androidVersion = "2.3";
- else if (qtProps.qtMajorVersion === 4 && qtProps.qtMinorVersion >= 8)
- qtProps.androidVersion = "1.6"; // Necessitas
- }
- return qtProps;
-}
-
-function makePluginData() {
- var pluginData = {};
- pluginData.type = undefined;
- pluginData.className = undefined;
- pluginData.autoLoad = true;
- pluginData["extends"] = [];
- return pluginData;
-}
-
-function makeQtModuleInfo(name, qbsName, deps) {
- var moduleInfo = {};
- moduleInfo.name = name; // As in the path to the headers and ".name" in the pri files.
- if (moduleInfo.name === undefined)
- moduleInfo.name = "";
- moduleInfo.qbsName = qbsName; // Lower-case version without "qt" prefix.
- moduleInfo.dependencies = deps || []; // qbs names.
- if (moduleInfo.qbsName && moduleInfo.qbsName !== "core"
- && !moduleInfo.dependencies.contains("core")) {
- moduleInfo.dependencies.unshift("core");
- }
- moduleInfo.isPrivate = qbsName && qbsName.endsWith("-private");
- moduleInfo.hasLibrary = !moduleInfo.isPrivate;
- moduleInfo.isStaticLibrary = false;
- moduleInfo.isPlugin = false;
- moduleInfo.mustExist = true;
- moduleInfo.modulePrefix = ""; // empty value means "Qt".
- moduleInfo.version = undefined;
- moduleInfo.includePaths = [];
- moduleInfo.compilerDefines = [];
- moduleInfo.staticLibrariesDebug = [];
- moduleInfo.staticLibrariesRelease = [];
- moduleInfo.dynamicLibrariesDebug = [];
- moduleInfo.dynamicLibrariesRelease = [];
- moduleInfo.linkerFlagsDebug = [];
- moduleInfo.linkerFlagsRelease = [];
- moduleInfo.libFilePathDebug = undefined;
- moduleInfo.libFilePathRelease = undefined;
- moduleInfo.frameworksDebug = [];
- moduleInfo.frameworksRelease = [];
- moduleInfo.frameworkPathsDebug = [];
- moduleInfo.frameworkPathsRelease = [];
- moduleInfo.libraryPaths = [];
- moduleInfo.libDir = "";
- moduleInfo.config = [];
- moduleInfo.supportedPluginTypes = [];
- moduleInfo.pluginData = makePluginData();
- return moduleInfo;
-}
-
-function frameworkHeadersPath(qtModuleInfo, qtProps) {
- return FileInfo.joinPaths(qtProps.libraryPath, qtModuleInfo.name + ".framework", "Headers");
-}
-
-function qt4ModuleIncludePaths(qtModuleInfo, qtProps) {
- var paths = [];
- if (isFramework(qtModuleInfo, qtProps))
- paths.push(frameworkHeadersPath(qtModuleInfo, qtProps));
- else
- paths.push(qtProps.includePath, FileInfo.joinPaths(qtProps.includePath, qtModuleInfo.name));
- return paths;
-}
-
-// We erroneously called the "testlib" module "test" for quite a while. Let's not punish users
-// for that.
-function addTestModule(modules) {
- var testModule = makeQtModuleInfo("QtTest", "test", ["testlib"]);
- testModule.hasLibrary = false;
- modules.push(testModule);
-}
-
-// See above.
-function addDesignerComponentsModule(modules) {
- var module = makeQtModuleInfo("QtDesignerComponents", "designercomponents",
- ["designercomponents-private"]);
- module.hasLibrary = false;
- modules.push(module);
-}
-
-function isFramework(modInfo, qtProps) {
- if (!qtProps.frameworkBuild || modInfo.isStaticLibrary)
- return false;
- var modulesNeverBuiltAsFrameworks = [
- "bootstrap", "openglextensions", "platformsupport", "qmldevtools", "uitools", "harfbuzzng"
- ];
- return !modulesNeverBuiltAsFrameworks.contains(modInfo.qbsName);
-}
-
-function libBaseName(modInfo, libName, debugBuild, qtProps) {
- var name = libName;
- if (qtProps.mkspecName.startsWith("win")) {
- if (debugBuild)
- name += 'd';
- if (!modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5)
- name += qtProps.qtMajorVersion;
- }
- if (qtProps.mkspecName.contains("macx")
- || qtProps.mkspecName.contains("ios")
- || qtProps.mkspecName.contains("darwin")) {
- if (!isFramework(modInfo, qtProps)
- && qtProps.buildVariant.contains("debug")
- && (!qtProps.buildVariant.contains("release") || debugBuild)) {
- name += "_debug";
- }
- }
- return name;
-}
-
-function moduleNameWithoutPrefix(modInfo) {
- if (modInfo.name === "Phonon")
- return "phonon";
- if (!modInfo.modulePrefix && modInfo.name.startsWith("Qt"))
- return modInfo.name.slice(2); // Strip off "Qt".
- if (modInfo.name.startsWith(modInfo.modulePrefix))
- return modInfo.name.slice(modInfo.modulePrefix.length);
- return modInfo.name;
-}
-
-function libraryBaseName(modInfo, qtProps, debugBuild) {
- if (modInfo.isPlugin)
- return libBaseName(modInfo, modInfo.name, debugBuild, qtProps);
-
- // Some modules use a different naming scheme, so it doesn't get boring.
- var libNameBroken = modInfo.name === "Enginio"
- || modInfo.name === "DataVisualization"
- || modInfo.name === "Phonon";
-
- var libName = !modInfo.modulePrefix && !libNameBroken ? "Qt" : modInfo.modulePrefix;
- if (qtProps.qtMajorVersion >= 5 && !isFramework(modInfo, qtProps) && !libNameBroken)
- libName += qtProps.qtMajorVersion;
- libName += moduleNameWithoutPrefix(modInfo);
- libName += qtProps.qtLibInfix;
- return libBaseName(modInfo, libName, debugBuild, qtProps);
-}
-
-function libNameForLinker(modInfo, qtProps, debugBuild) {
- if (!modInfo.hasLibrary)
- return undefined;
- var libName = libraryBaseName(modInfo, qtProps, debugBuild);
- if (qtProps.mkspecName.contains("msvc"))
- libName += ".lib";
- return libName;
-}
-
-function guessLibraryFilePath(prlFilePath, libDir, qtProps) {
- var baseName = FileInfo.baseName(prlFilePath);
- var prefixCandidates = ["", "lib"];
- var suffixCandidates = ["so." + qtProps.qtVersion, "so", "a", "lib", "dll.a"];
- for (var i = 0; i < prefixCandidates.length; ++i) {
- var prefix = prefixCandidates[i];
- for (var j = 0; j < suffixCandidates.length; ++j) {
- var suffix = suffixCandidates[j];
- var candidate = FileInfo.joinPaths(libDir, prefix + baseName + '.' + suffix);
- if (File.exists(candidate))
- return candidate;
- }
- }
-}
-
-function doReplaceQtLibNamesWithFilePath(namePathMap, libList) {
- for (var i = 0; i < libList.length; ++i) {
- var lib = libList[i];
- var path = namePathMap[lib];
- if (path)
- libList[i] = path;
- }
-}
-
-function replaceQtLibNamesWithFilePath(modules, qtProps) {
- // 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.
- var linkerNamesToFilePathsDebug = {};
- var linkerNamesToFilePathsRelease = {};
- for (var i = 0; i < modules.length; ++i) {
- var m = modules[i];
- linkerNamesToFilePathsDebug[libNameForLinker(m, qtProps, true)] = m.libFilePathDebug;
- linkerNamesToFilePathsRelease[libNameForLinker(m, qtProps, false)] = m.libFilePathRelease;
- }
- for (i = 0; i < modules.length; ++i) {
- var module = modules[i];
- doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.dynamicLibrariesDebug);
- doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsDebug, module.staticLibrariesDebug);
- doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease,
- module.dynamicLibrariesRelease);
- doReplaceQtLibNamesWithFilePath(linkerNamesToFilePathsRelease,
- module.staticLibrariesRelease);
- }
-}
-
-function doSetupLibraries(modInfo, qtProps, debugBuild, nonExistingPrlFiles) {
- if (!modInfo.hasLibrary)
- return; // Can happen for Qt4 convenience modules, like "widgets".
-
- if (debugBuild) {
- if (!qtProps.buildVariant.contains("debug"))
- return;
- var modulesNeverBuiltAsDebug = ["bootstrap", "qmldevtools"];
- for (var i = 0; i < modulesNeverBuiltAsDebug.length; ++i) {
- var m = modulesNeverBuiltAsDebug[i];
- if (modInfo.qbsName === m || modInfo.qbsName === m + "-private")
- return;
- }
- } else if (!qtProps.buildVariant.contains("release")) {
- return;
- }
-
- var libs = modInfo.isStaticLibrary
- ? (debugBuild ? modInfo.staticLibrariesDebug : modInfo.staticLibrariesRelease)
- : (debugBuild ? modInfo.dynamicLibrariesDebug : modInfo.dynamicLibrariesRelease);
- var frameworks = debugBuild ? modInfo.frameworksDebug : modInfo.frameworksRelease;
- var frameworkPaths = debugBuild ? modInfo.frameworkPathsDebug : modInfo.frameworkPathsRelease;
- var flags = debugBuild ? modInfo.linkerFlagsDebug : modInfo.linkerFlagsRelease;
- var libFilePath;
-
- if (qtProps.mkspecName.contains("ios") && modInfo.isStaticLibrary) {
- libs.push("z", "m");
- if (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 8) {
- var platformSupportModule = makeQtModuleInfo("QtPlatformSupport", "platformsupport");
- libs.push(libNameForLinker(platformSupportModule, qtProps, debugBuild));
- }
- if (modInfo.name === "qios") {
- flags.push("-force_load", FileInfo.joinPaths(
- qtProps.pluginPath, "platforms",
- libBaseName(modInfo, "libqios", debugBuild, qtProps) + ".a"));
- }
- }
- var prlFilePath = modInfo.isPlugin
- ? FileInfo.joinPaths(qtProps.pluginPath, modInfo.pluginData.type)
- : (modInfo.libDir ? modInfo.libDir : qtProps.libraryPath);
- if (isFramework(modInfo, qtProps)) {
- prlFilePath = FileInfo.joinPaths(prlFilePath,
- libraryBaseName(modInfo, qtProps, false) + ".framework");
- }
- var libDir = prlFilePath;
- var baseName = libraryBaseName(modInfo, qtProps, debugBuild);
- if (!qtProps.mkspecName.startsWith("win") && !isFramework(modInfo, qtProps))
- baseName = "lib" + baseName;
- prlFilePath = FileInfo.joinPaths(prlFilePath, baseName);
- var isNonStaticQt4OnWindows = qtProps.mkspecName.startsWith("win")
- && !modInfo.isStaticLibrary && qtProps.qtMajorVersion < 5;
- if (isNonStaticQt4OnWindows)
- prlFilePath = prlFilePath.slice(0, prlFilePath.length - 1); // The prl file base name does *not* contain the version number...
- prlFilePath += ".prl";
- try {
- var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly);
- while (!prlFile.atEof()) {
- var line = prlFile.readLine().trim();
- var equalsOffset = line.indexOf('=');
- if (equalsOffset === -1)
- continue;
- if (line.startsWith("QMAKE_PRL_TARGET")) {
- var isMingw = qtProps.mkspecName.startsWith("win")
- && qtProps.mkspecName.contains("g++");
- var isQtVersionBefore56 = qtProps.qtMajorVersion < 5
- || (qtProps.qtMajorVersion === 5 && qtProps.qtMinorVersion < 6);
-
- // 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.
- var libFileName = "";
- if (isQtVersionBefore56 && qtProps.qtMajorVersion === 5 && isMingw
- && !modInfo.isStaticLibrary) {
- libFileName += "lib";
- }
-
- libFileName += line.slice(equalsOffset + 1).trim();
- if (isNonStaticQt4OnWindows)
- libFileName += 4; // This is *not* part of QMAKE_PRL_TARGET...
- if (isQtVersionBefore56) {
- if (qtProps.mkspecName.contains("msvc")) {
- libFileName += ".lib";
- } else if (isMingw) {
- libFileName += ".a";
- if (!File.exists(FileInfo.joinPaths(libDir, libFileName)))
- libFileName = libFileName.slice(0, -2) + ".dll";
- }
- }
- libFilePath = FileInfo.joinPaths(libDir, libFileName);
- continue;
- }
- if (line.startsWith("QMAKE_PRL_CONFIG")) {
- modInfo.config = splitNonEmpty(line.slice(equalsOffset + 1).trim(), ' ');
- continue;
- }
- if (!line.startsWith("QMAKE_PRL_LIBS ="))
- continue;
-
- var parts = extractPaths(line.slice(equalsOffset + 1).trim(), prlFilePath);
- for (i = 0; i < parts.length; ++i) {
- var part = parts[i];
- part = part.replace("$$[QT_INSTALL_LIBS]", qtProps.libraryPath);
- if (part.startsWith("-l")) {
- libs.push(part.slice(2));
- } else if (part.startsWith("-L")) {
- modInfo.libraryPaths.push(part.slice(2));
- } else if (part.startsWith("-F")) {
- frameworkPaths.push(part.slice(2));
- } else if (part === "-framework") {
- if (++i < parts.length)
- frameworks.push(parts[i]);
- } else if (part === "-pthread") {
- libs.push("pthread");
- } else if (part.startsWith('-')) { // Some other option
- console.debug("QMAKE_PRL_LIBS contains non-library option '" + part
- + "' in file '" + prlFilePath + "'");
- flags.push(part);
- } else if (part.startsWith("/LIBPATH:")) {
- libraryPaths.push(part.slice(9).replace(/\\/g, '/'));
- } else { // Assume it's a file path/name.
- libs.push(part.replace(/\\/g, '/'));
- }
- }
- }
- } catch (e) {
- libFilePath = guessLibraryFilePath(prlFilePath, libDir, qtProps);
- if (nonExistingPrlFiles.contains(prlFilePath))
- return;
- nonExistingPrlFiles.push(prlFilePath);
- if (!libFilePath && modInfo.mustExist) {
- console.warn("Could not open prl file '" + toNative(prlFilePath) + "' for module '"
- + modInfo.name
- + "' (" + e + "), and failed to deduce the library file path. "
- + " This module will likely not be usable by qbs.");
- }
- }
- finally {
- if (prlFile)
- prlFile.close();
- }
-
- if (debugBuild)
- modInfo.libFilePathDebug = libFilePath;
- else
- modInfo.libFilePathRelease = libFilePath;
-}
-
-function setupLibraries(qtModuleInfo, qtProps, nonExistingPrlFiles) {
- doSetupLibraries(qtModuleInfo, qtProps, true, nonExistingPrlFiles);
- doSetupLibraries(qtModuleInfo, qtProps, false, nonExistingPrlFiles);
-}
-
-function allQt4Modules(qtProps) {
- // as per http://doc.qt.io/qt-4.8/modules.html + private stuff.
- var modules = [];
-
- var core = makeQtModuleInfo("QtCore", "core");
- core.compilerDefines.push("QT_CORE_LIB");
- if (qtProps.qtNameSpace)
- core.compilerDefines.push("QT_NAMESPACE=" + qtProps.qtNameSpace);
- modules.push(core,
- makeQtModuleInfo("QtCore", "core-private", ["core"]),
- makeQtModuleInfo("QtGui", "gui"),
- makeQtModuleInfo("QtGui", "gui-private", ["gui"]),
- makeQtModuleInfo("QtMultimedia", "multimedia", ["gui", "network"]),
- makeQtModuleInfo("QtMultimedia", "multimedia-private", ["multimedia"]),
- makeQtModuleInfo("QtNetwork", "network"),
- makeQtModuleInfo("QtNetwork", "network-private", ["network"]),
- makeQtModuleInfo("QtOpenGL", "opengl", ["gui"]),
- makeQtModuleInfo("QtOpenGL", "opengl-private", ["opengl"]),
- makeQtModuleInfo("QtOpenVG", "openvg", ["gui"]),
- makeQtModuleInfo("QtScript", "script"),
- makeQtModuleInfo("QtScript", "script-private", ["script"]),
- makeQtModuleInfo("QtScriptTools", "scripttools", ["script", "gui"]),
- makeQtModuleInfo("QtScriptTools", "scripttools-private", ["scripttools"]),
- makeQtModuleInfo("QtSql", "sql"),
- makeQtModuleInfo("QtSql", "sql-private", ["sql"]),
- makeQtModuleInfo("QtSvg", "svg", ["gui"]),
- makeQtModuleInfo("QtSvg", "svg-private", ["svg"]),
- makeQtModuleInfo("QtWebKit", "webkit", ["gui", "network"]),
- makeQtModuleInfo("QtWebKit", "webkit-private", ["webkit"]),
- makeQtModuleInfo("QtXml", "xml"),
- makeQtModuleInfo("QtXml", "xml-private", ["xml"]),
- makeQtModuleInfo("QtXmlPatterns", "xmlpatterns", ["network"]),
- makeQtModuleInfo("QtXmlPatterns", "xmlpatterns-private", ["xmlpatterns"]),
- makeQtModuleInfo("QtDeclarative", "declarative", ["gui", "script"]),
- makeQtModuleInfo("QtDeclarative", "declarative-private", ["declarative"]),
- makeQtModuleInfo("QtDesigner", "designer", ["gui", "xml"]),
- makeQtModuleInfo("QtDesigner", "designer-private", ["designer"]),
- makeQtModuleInfo("QtUiTools", "uitools"),
- makeQtModuleInfo("QtUiTools", "uitools-private", ["uitools"]),
- makeQtModuleInfo("QtHelp", "help", ["network", "sql"]),
- makeQtModuleInfo("QtHelp", "help-private", ["help"]),
- makeQtModuleInfo("QtTest", "testlib"),
- makeQtModuleInfo("QtTest", "testlib-private", ["testlib"]));
- if (qtProps.mkspecName.startsWith("win")) {
- var axcontainer = makeQtModuleInfo("QAxContainer", "axcontainer");
- axcontainer.modulePrefix = "Q";
- axcontainer.isStaticLibrary = true;
- axcontainer.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt"));
- modules.push(axcontainer);
-
- var axserver = makeQtModuleInfo("QAxServer", "axserver");
- axserver.modulePrefix = "Q";
- axserver.isStaticLibrary = true;
- axserver.compilerDefines.push("QAXSERVER");
- axserver.includePaths.push(FileInfo.joinPaths(qtProps.includePath, "ActiveQt"));
- modules.push(axserver);
- } else {
- modules.push(makeQtModuleInfo("QtDBus", "dbus"));
- modules.push(makeQtModuleInfo("QtDBus", "dbus-private", ["dbus"]));
- }
-
- var designerComponentsPrivate = makeQtModuleInfo(
- "QtDesignerComponents", "designercomponents-private",
- ["gui-private", "designer-private"]);
- designerComponentsPrivate.hasLibrary = true;
- modules.push(designerComponentsPrivate);
-
- var phonon = makeQtModuleInfo("Phonon", "phonon");
- phonon.includePaths = qt4ModuleIncludePaths(phonon, qtProps);
- modules.push(phonon);
-
- // Set up include paths that haven't been set up before this point.
- for (i = 0; i < modules.length; ++i) {
- var module = modules[i];
- if (module.includePaths.length > 0)
- continue;
- module.includePaths = qt4ModuleIncludePaths(module, qtProps);
- }
-
- // Set up compiler defines haven't been set up before this point.
- for (i = 0; i < modules.length; ++i) {
- module = modules[i];
- if (module.compilerDefines.length > 0)
- continue;
- module.compilerDefines.push("QT_" + module.qbsName.toUpperCase() + "_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.
- var virtualModule = makeQtModuleInfo(undefined, "widgets", ["core", "gui"]);
- virtualModule.hasLibrary = false;
- modules.push(virtualModule);
- virtualModule = makeQtModuleInfo(undefined, "quick", ["declarative"]);
- virtualModule.hasLibrary = false;
- modules.push(virtualModule);
- virtualModule = makeQtModuleInfo(undefined, "concurrent");
- virtualModule.hasLibrary = false;
- modules.push(virtualModule);
- virtualModule = makeQtModuleInfo(undefined, "printsupport", ["core", "gui"]);
- virtualModule.hasLibrary = false;
- modules.push(virtualModule);
-
- addTestModule(modules);
- addDesignerComponentsModule(modules);
-
- var modulesThatCanBeDisabled = [
- "xmlpatterns", "multimedia", "phonon", "svg", "webkit", "script", "scripttools",
- "declarative", "gui", "dbus", "opengl", "openvg"];
- var nonExistingPrlFiles = [];
- for (i = 0; i < modules.length; ++i) {
- module = modules[i];
- var name = module.qbsName;
- var privateIndex = name.indexOf("-private");
- if (privateIndex !== -1)
- name = name.slice(0, privateIndex);
- if (modulesThatCanBeDisabled.contains(name))
- module.mustExist = false;
- if (qtProps.staticBuild)
- module.isStaticLibrary = true;
- setupLibraries(module, qtProps, nonExistingPrlFiles);
- }
- replaceQtLibNamesWithFilePath(modules, qtProps);
-
- return modules;
-}
-
-function getFileContentsRecursively(filePath) {
- var file = new TextFile(filePath, TextFile.ReadOnly);
- var lines = splitNonEmpty(file.readAll(), '\n');
- for (var i = 0; i < lines.length; ++i) {
- var includeString = "include(";
- var line = lines[i].trim();
- if (!line.startsWith(includeString))
- continue;
- var offset = includeString.length;
- var closingParenPos = line.indexOf(')', offset);
- if (closingParenPos === -1) {
- console.warn("Invalid include statement in '" + toNative(filePath) + "'");
- continue;
- }
- var includedFilePath = line.slice(offset, closingParenPos);
- if (!FileInfo.isAbsolutePath(includedFilePath))
- includedFilePath = FileInfo.joinPaths(FileInfo.path(filePath), includedFilePath);
- var includedContents = getFileContentsRecursively(includedFilePath);
- var j = i;
- for (var k = 0; k < includedContents.length; ++k)
- lines.splice(++j, 0, includedContents[k]);
- lines.splice(i--, 1);
- }
- file.close();
- return lines;
-}
-
-function extractPaths(rhs, filePath) {
- var paths = [];
- var startIndex = 0;
- for (;;) {
- while (startIndex < rhs.length && rhs.charAt(startIndex) === ' ')
- ++startIndex;
- if (startIndex >= rhs.length)
- break;
- var endIndex;
- if (rhs.charAt(startIndex) === '"') {
- ++startIndex;
- endIndex = rhs.indexOf('"', startIndex);
- if (endIndex === -1) {
- console.warn("Unmatched quote in file '" + toNative(filePath) + "'");
- break;
- }
- } else {
- endIndex = rhs.indexOf(' ', startIndex + 1);
- if (endIndex === -1)
- endIndex = rhs.length;
- }
- paths.push(FileInfo.cleanPath(rhs.slice(startIndex, endIndex)));
- startIndex = endIndex + 1;
- }
- return paths;
-}
-
-function removeDuplicatedDependencyLibs(modules) {
- var revDeps = {};
- var currentPath = [];
- var getLibraries;
- var getLibFilePath;
-
- function setupReverseDependencies(modules) {
- var moduleByName = {};
- for (var i = 0; i < modules.length; ++i)
- moduleByName[modules[i].qbsName] = modules[i];
- for (i = 0; i < modules.length; ++i) {
- var module = modules[i];
- for (var j = 0; j < module.dependencies.length; ++j) {
- var depmod = moduleByName[module.dependencies[j]];
- if (!depmod)
- continue;
- if (!revDeps[depmod.qbsName])
- revDeps[depmod.qbsName] = [];
- revDeps[depmod.qbsName].push(module);
- }
- }
- }
-
- function roots(modules) {
- var result = [];
- for (i = 0; i < modules.length; ++i) {
- var module = modules[i]
- if (module.dependencies.length === 0)
- result.push(module);
- }
- return result;
- }
-
- function traverse(module, libs) {
- if (currentPath.contains(module))
- return;
- currentPath.push(module);
- var moduleLibraryLists = getLibraries(module);
- for (var i = 0; i < moduleLibraryLists.length; ++i) {
- var modLibList = moduleLibraryLists[i];
- for (j = modLibList.length - 1; j >= 0; --j) {
- if (libs.contains(modLibList[j]))
- modLibList.splice(j, 1);
- }
- }
-
- var libFilePath = getLibFilePath(module);
- if (libFilePath)
- libs.push(libFilePath);
- for (i = 0; i < moduleLibraryLists.length; ++i)
- libs = libs.concat(moduleLibraryLists[i]);
- libs.sort();
-
- var deps = revDeps[module.qbsName];
- for (i = 0; i < (deps || []).length; ++i)
- traverse(deps[i], libs);
-
- currentPath.pop();
- }
-
- setupReverseDependencies(modules);
-
- // Traverse the debug variants of modules.
- getLibraries = function(module) {
- return [module.dynamicLibrariesDebug, module.staticLibrariesDebug];
- };
- getLibFilePath = function(module) { return module.libFilePathDebug; };
- var rootModules = roots(modules);
- for (var i = 0; i < rootModules.length; ++i)
- traverse(rootModules[i], []);
-
- // Traverse the release variants of modules.
- getLibraries = function(module) {
- return [module.dynamicLibrariesRelease, module.staticLibrariesRelease];
- };
- getLibFilePath = function(module) { return module.libFilePathRelease; };
- for (i = 0; i < rootModules.length; ++i)
- traverse(rootModules[i], []);
-}
-
-function allQt5Modules(qtProps) {
- var nonExistingPrlFiles = [];
- var modules = [];
- var modulesDir = FileInfo.joinPaths(qtProps.mkspecBasePath, "modules");
- var modulePriFiles = File.directoryEntries(modulesDir, File.Files);
- for (var i = 0; i < modulePriFiles.length; ++i) {
- var priFileName = modulePriFiles[i];
- var priFilePath = FileInfo.joinPaths(modulesDir, priFileName);
- var moduleFileNamePrefix = "qt_lib_";
- var pluginFileNamePrefix = "qt_plugin_";
- var moduleFileNameSuffix = ".pri";
- var fileHasPluginPrefix = priFileName.startsWith(pluginFileNamePrefix);
- if (!fileHasPluginPrefix && (!priFileName.startsWith(moduleFileNamePrefix))
- || !priFileName.endsWith(moduleFileNameSuffix)) {
- continue;
- }
- var moduleInfo = makeQtModuleInfo();
- moduleInfo.isPlugin = fileHasPluginPrefix;
- var fileNamePrefix = moduleInfo.isPlugin ? pluginFileNamePrefix : moduleFileNamePrefix;
- moduleInfo.qbsName = priFileName.slice(fileNamePrefix.length, -moduleFileNameSuffix.length);
- if (moduleInfo.isPlugin) {
- moduleInfo.name = moduleInfo.qbsName;
- moduleInfo.isStaticLibrary = true;
- }
- var moduleKeyPrefix = (moduleInfo.isPlugin ? "QT_PLUGIN" : "QT")
- + '.' + moduleInfo.qbsName + '.';
- moduleInfo.qbsName = moduleInfo.qbsName.replace("_private", "-private");
- var hasV2 = false;
- var hasModuleEntry = false;
- var lines = getFileContentsRecursively(priFilePath);
- for (var j = 0; j < lines.length; ++j) {
- var line = lines[j].trim();
- var firstEqualsOffset = line.indexOf('=');
- if (firstEqualsOffset === -1)
- continue;
- var key = line.slice(0, firstEqualsOffset).trim();
- var value = line.slice(firstEqualsOffset + 1).trim();
- if (!key.startsWith(moduleKeyPrefix) || !value)
- continue;
- if (key.endsWith(".name")) {
- moduleInfo.name = value;
- } else if (key.endsWith(".module")) {
- hasModuleEntry = true;
- } else if (key.endsWith(".depends")) {
- moduleInfo.dependencies = splitNonEmpty(value, ' ');
- for (var k = 0; k < moduleInfo.dependencies.length; ++k) {
- moduleInfo.dependencies[k]
- = moduleInfo.dependencies[k].replace("_private", "-private");
- }
- } else if (key.endsWith(".module_config")) {
- var elems = splitNonEmpty(value, ' ');
- for (k = 0; k < elems.length; ++k) {
- var elem = elems[k];
- 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 = extractPaths(value, priFilePath);
- for (k = 0; k < moduleInfo.includePaths.length; ++k) {
- moduleInfo.includePaths[k] = moduleInfo.includePaths[k]
- .replace("$$QT_MODULE_INCLUDE_BASE", qtProps.includePath)
- .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath)
- .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath);
- }
- } else if (key.endsWith(".libs")) {
- var libDirs = extractPaths(value, priFilePath);
- if (libDirs.length === 1) {
- moduleInfo.libDir = libDirs[0]
- .replace("$$QT_MODULE_HOST_LIB_BASE", qtProps.hostLibraryPath)
- .replace("$$QT_MODULE_LIB_BASE", qtProps.libraryPath);
- } else {
- moduleInfo.libDir = qtProps.libraryPath;
- }
- } else if (key.endsWith(".DEFINES")) {
- moduleInfo.compilerDefines = splitNonEmpty(value, ' ');
- } else if (key.endsWith(".VERSION")) {
- moduleInfo.version = value;
- } else if (key.endsWith(".plugin_types")) {
- moduleInfo.supportedPluginTypes = splitNonEmpty(value, ' ');
- } else if (key.endsWith(".TYPE")) {
- moduleInfo.pluginData.type = value;
- } else if (key.endsWith(".EXTENDS")) {
- moduleInfo.pluginData["extends"] = splitNonEmpty(value, ' ');
- for (k = 0; k < moduleInfo.pluginData["extends"].length; ++k) {
- if (moduleInfo.pluginData["extends"][k] === "-") {
- moduleInfo.pluginData["extends"].splice(k, 1);
- moduleInfo.pluginData.autoLoad = false;
- break;
- }
- }
- } else if (key.endsWith(".CLASS_NAME")) {
- moduleInfo.pluginData.className = 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 && isFramework(moduleInfo, qtProps)) {
- moduleInfo.includePaths = [];
- var baseIncDir = frameworkHeadersPath(moduleInfo, qtProps);
- if (moduleInfo.isPrivate) {
- baseIncDir = FileInfo.joinPaths(baseIncDir, moduleInfo.version);
- moduleInfo.includePaths.push(baseIncDir,
- FileInfo.joinPaths(baseIncDir, moduleInfo.name));
- } else {
- moduleInfo.includePaths.push(baseIncDir);
- }
- }
-
- setupLibraries(moduleInfo, qtProps, nonExistingPrlFiles);
-
- modules.push(moduleInfo);
- if (moduleInfo.qbsName === "testlib")
- addTestModule(modules);
- if (moduleInfo.qbsName === "designercomponents-private")
- addDesignerComponentsModule(modules);
- }
-
- replaceQtLibNamesWithFilePath(modules, qtProps);
- removeDuplicatedDependencyLibs(modules);
- return modules;
-}
-
function extractQbsArchs(module, qtProps) {
if (qtProps.mkspecName.startsWith("macx-")) {
var archs = [];
@@ -1144,7 +54,6 @@ function extractQbsArchs(module, qtProps) {
var qbsArch = Utilities.canonicalArchitecture(qtProps.architecture);
if (qbsArch === "arm" && qtProps.mkspecPath.contains("android"))
qbsArch = "armv7a";
-
// Qt4 has "QT_ARCH = windows" in qconfig.pri for both MSVC and mingw.
if (qbsArch === "windows")
return []
@@ -1196,7 +105,7 @@ function qbsTargetPlatformFromQtMkspec(qtProps) {
return "solaris";
if (mkspec.startsWith("vxworks-"))
return "vxworks";
- if (targetsDesktopWindows(qtProps) || mkspec.startsWith("winrt-"))
+ if (ProviderUtils.isDesktopWindowsQt(qtProps) || mkspec.startsWith("winrt-"))
return "windows";
}
@@ -1252,7 +161,7 @@ function defaultQpaPlugin(module, qtProps) {
function libraryFileTag(module, qtProps) {
if (module.isStaticLibrary)
return "staticlibrary";
- return isMsvcQt(qtProps) || qtProps.mkspecName.startsWith("win32-g++")
+ return ProviderUtils.isMsvcQt(qtProps) || qtProps.mkspecName.startsWith("win32-g++")
? "dynamiclibrary_import" : "dynamiclibrary";
}
@@ -1276,67 +185,86 @@ function findVariable(content, start) {
return result;
}
-function toJSLiteral(v) {
- if (v === undefined)
- return "undefined";
- return JSON.stringify(v);
+function minVersionJsString(minVersion) {
+ return !minVersion ? "" : ModUtils.toJSLiteral(minVersion);
}
-function minVersionJsString(minVersion) {
- return !minVersion ? "original" : toJSLiteral(minVersion);
+function abiToArchitecture(abi) {
+ switch (abi) {
+ case "armeabi-v7a":
+ return "armv7a";
+ case "arm64-v8a":
+ return "arm64";
+ case "x86":
+ case "x86_64":
+ default:
+ return abi;
+ }
}
-function replaceSpecialValues(content, module, qtProps) {
+function replaceSpecialValues(content, module, qtProps, abi) {
+ var architectures = [];
+ if (abi.length > 0)
+ architectures.push(abiToArchitecture(abi));
+ else
+ architectures = extractQbsArchs(module, qtProps);
var dict = {
- archs: toJSLiteral(extractQbsArchs(module, qtProps)),
- targetPlatform: toJSLiteral(qbsTargetPlatformFromQtMkspec(qtProps)),
- config: toJSLiteral(qtProps.configItems),
- qtConfig: toJSLiteral(qtProps.qtConfigItems),
- binPath: toJSLiteral(qtProps.binaryPath),
- libPath: toJSLiteral(qtProps.libraryPath),
- pluginPath: toJSLiteral(qtProps.pluginPath),
- incPath: toJSLiteral(qtProps.includePath),
- docPath: toJSLiteral(qtProps.documentationPath),
- mkspecName: toJSLiteral(qtProps.mkspecName),
- mkspecPath: toJSLiteral(qtProps.mkspecPath),
- version: toJSLiteral(qtProps.qtVersion),
- libInfix: toJSLiteral(qtProps.qtLibInfix),
- availableBuildVariants: toJSLiteral(qtProps.buildVariant),
- staticBuild: toJSLiteral(qtProps.staticBuild),
- frameworkBuild: toJSLiteral(qtProps.frameworkBuild),
- name: toJSLiteral(moduleNameWithoutPrefix(module)),
- has_library: toJSLiteral(module.hasLibrary),
- dependencies: toJSLiteral(module.dependencies),
- includes: toJSLiteral(module.includePaths),
- staticLibsDebug: toJSLiteral(module.staticLibrariesDebug),
- staticLibsRelease: toJSLiteral(module.staticLibrariesRelease),
- dynamicLibsDebug: toJSLiteral(module.dynamicLibrariesDebug),
- dynamicLibsRelease: toJSLiteral(module.dynamicLibrariesRelease),
- linkerFlagsDebug: toJSLiteral(module.linkerFlagsDebug),
- linkerFlagsRelease: toJSLiteral(module.linkerFlagsRelease),
- libraryPaths: toJSLiteral(module.libraryPaths),
- frameworkPathsDebug: toJSLiteral(module.frameworkPathsDebug),
- frameworkPathsRelease: toJSLiteral(module.frameworkPathsRelease),
- frameworksDebug: toJSLiteral(module.frameworksDebug),
- frameworksRelease: toJSLiteral(module.frameworksRelease),
- libFilePathDebug: toJSLiteral(module.libFilePathDebug),
- libFilePathRelease: toJSLiteral(module.libFilePathRelease),
- libNameForLinkerDebug: toJSLiteral(libNameForLinker(module, qtProps, true)),
- pluginTypes: toJSLiteral(module.supportedPluginTypes),
- moduleConfig: toJSLiteral(module.config),
- libNameForLinkerRelease: toJSLiteral(libNameForLinker(module, qtProps, false)),
- entryPointLibsDebug: toJSLiteral(qtProps.entryPointLibsDebug),
- entryPointLibsRelease: toJSLiteral(qtProps.entryPointLibsRelease),
- minWinVersion: minVersionJsString(qtProps.windowsVersion),
- minMacVersion: minVersionJsString(qtProps.macosVersion),
- minIosVersion: minVersionJsString(qtProps.iosVersion),
- minTvosVersion: minVersionJsString(qtProps.tvosVersion),
- minWatchosVersion: minVersionJsString(qtProps.watchosVersion),
- minAndroidVersion: minVersionJsString(qtProps.androidVersion),
+ archs: ModUtils.toJSLiteral(architectures),
+ targetPlatform: ModUtils.toJSLiteral(qbsTargetPlatformFromQtMkspec(qtProps)),
+ config: ModUtils.toJSLiteral(qtProps.configItems),
+ qtConfig: ModUtils.toJSLiteral(qtProps.qtConfigItems),
+ binPath: ModUtils.toJSLiteral(qtProps.binaryPath),
+ installPath: ModUtils.toJSLiteral(qtProps.installPath),
+ installPrefixPath: ModUtils.toJSLiteral(qtProps.installPrefixPath),
+ libPath: ModUtils.toJSLiteral(qtProps.libraryPath),
+ libExecPath: ModUtils.toJSLiteral(qtProps.libExecPath),
+ qmlLibExecPath: ModUtils.toJSLiteral(qtProps.qmlLibExecPath),
+ pluginPath: ModUtils.toJSLiteral(qtProps.pluginPath),
+ incPath: ModUtils.toJSLiteral(qtProps.includePath),
+ docPath: ModUtils.toJSLiteral(qtProps.documentationPath),
+ helpGeneratorLibExecPath: ModUtils.toJSLiteral(qtProps.helpGeneratorLibExecPath),
+ mkspecName: ModUtils.toJSLiteral(qtProps.mkspecName),
+ mkspecPath: ModUtils.toJSLiteral(qtProps.mkspecPath),
+ version: ModUtils.toJSLiteral(qtProps.qtVersion),
+ libInfix: ModUtils.toJSLiteral(qtProps.qtLibInfix),
+ availableBuildVariants: ModUtils.toJSLiteral(qtProps.buildVariant),
+ staticBuild: ModUtils.toJSLiteral(qtProps.staticBuild),
+ frameworkBuild: ModUtils.toJSLiteral(qtProps.frameworkBuild),
+ name: ModUtils.toJSLiteral(ProviderUtils.qtModuleNameWithoutPrefix(module)),
+ has_library: ModUtils.toJSLiteral(module.hasLibrary),
+ dependencies: ModUtils.toJSLiteral(module.dependencies),
+ includes: ModUtils.toJSLiteral(module.includePaths),
+ staticLibsDebug: ModUtils.toJSLiteral(module.staticLibrariesDebug),
+ staticLibsRelease: ModUtils.toJSLiteral(module.staticLibrariesRelease),
+ dynamicLibsDebug: ModUtils.toJSLiteral(module.dynamicLibrariesDebug),
+ dynamicLibsRelease: ModUtils.toJSLiteral(module.dynamicLibrariesRelease),
+ linkerFlagsDebug: ModUtils.toJSLiteral(module.linkerFlagsDebug),
+ linkerFlagsRelease: ModUtils.toJSLiteral(module.linkerFlagsRelease),
+ libraryPaths: ModUtils.toJSLiteral(module.libraryPaths),
+ frameworkPathsDebug: ModUtils.toJSLiteral(module.frameworkPathsDebug),
+ frameworkPathsRelease: ModUtils.toJSLiteral(module.frameworkPathsRelease),
+ frameworksDebug: ModUtils.toJSLiteral(module.frameworksDebug),
+ frameworksRelease: ModUtils.toJSLiteral(module.frameworksRelease),
+ libFilePathDebug: ModUtils.toJSLiteral(module.libFilePathDebug),
+ libFilePathRelease: ModUtils.toJSLiteral(module.libFilePathRelease),
+ libNameForLinkerDebug:
+ ModUtils.toJSLiteral(ProviderUtils.qtLibNameForLinker(module, qtProps, true)),
+ pluginTypes: ModUtils.toJSLiteral(module.supportedPluginTypes),
+ moduleConfig: ModUtils.toJSLiteral(module.config),
+ libNameForLinkerRelease:
+ ModUtils.toJSLiteral(ProviderUtils.qtLibNameForLinker(module, qtProps, false)),
+ entryPointLibsDebug: ModUtils.toJSLiteral(qtProps.entryPointLibsDebug),
+ entryPointLibsRelease: ModUtils.toJSLiteral(qtProps.entryPointLibsRelease),
+ minWinVersion_optional: minVersionJsString(qtProps.windowsVersion),
+ minMacVersion_optional: minVersionJsString(qtProps.macosVersion),
+ minIosVersion_optional: minVersionJsString(qtProps.iosVersion),
+ minTvosVersion_optional: minVersionJsString(qtProps.tvosVersion),
+ minWatchosVersion_optional: minVersionJsString(qtProps.watchosVersion),
+ minAndroidVersion_optional: minVersionJsString(qtProps.androidVersion),
};
var additionalContent = "";
- var compilerDefines = toJSLiteral(module.compilerDefines);
+ var compilerDefines = ModUtils.toJSLiteral(module.compilerDefines);
if (module.qbsName === "declarative" || module.qbsName === "quick") {
var debugMacro = module.qbsName === "declarative" || qtProps.qtMajorVersion < 5
? "QT_DECLARATIVE_DEBUG" : "QT_QML_DEBUG";
@@ -1360,7 +288,7 @@ function replaceSpecialValues(content, module, qtProps) {
}
dict.defines = compilerDefines;
if (module.qbsName === "gui")
- dict.defaultQpaPlugin = toJSLiteral(defaultQpaPlugin(module, qtProps));
+ dict.defaultQpaPlugin = ModUtils.toJSLiteral(defaultQpaPlugin(module, qtProps));
if (module.qbsName === "qml")
dict.qmlPath = pathToJSLiteral(qtProps.qmlPath);
if (module.isStaticLibrary && module.qbsName !== "core") {
@@ -1369,20 +297,30 @@ function replaceSpecialValues(content, module, qtProps) {
additionalContent += "isStaticLibrary: true";
}
if (module.isPlugin) {
- dict.className = toJSLiteral(module.pluginData.className);
- dict["extends"] = toJSLiteral(module.pluginData["extends"]);
+ dict.className = ModUtils.toJSLiteral(module.pluginData.className);
+ dict["extends"] = ModUtils.toJSLiteral(module.pluginData["extends"]);
+ }
+ indent = " ";
+ var metaTypesFile = qtProps.libraryPath + "/metatypes/qt"
+ + qtProps.qtMajorVersion + module.qbsName + "_metatypes.json";
+ if (File.exists(metaTypesFile)) {
+ if (additionalContent)
+ additionalContent += "\n" + indent;
+ additionalContent += "Group {\n";
+ additionalContent += indent + indent + "files: " + JSON.stringify(metaTypesFile) + "\n"
+ + indent + indent + "filesAreTargets: true\n"
+ + indent + indent + "fileTags: [\"qt.core.metatypes\"]\n"
+ + indent + "}";
}
- if (module.hasLibrary && !isFramework(module, qtProps)) {
+ if (module.hasLibrary && !ProviderUtils.qtIsFramework(module, qtProps)) {
if (additionalContent)
- additionalContent += "\n";
- indent = " ";
+ additionalContent += "\n" + indent;
additionalContent += "Group {\n";
if (module.isPlugin) {
additionalContent += indent + indent
- + "condition: Qt[\"" + module.qbsName + "\"].enableLinking\n";
+ + "condition: enableLinking\n";
}
- additionalContent += indent + indent + "files: [Qt[\"" + module.qbsName + "\"]"
- + ".libFilePath]\n"
+ additionalContent += indent + indent + "files: libFilePath\n"
+ indent + indent + "filesAreTargets: true\n"
+ indent + indent + "fileTags: [\"" + libraryFileTag(module, qtProps)
+ "\"]\n"
@@ -1392,29 +330,42 @@ function replaceSpecialValues(content, module, qtProps) {
for (var pos = findVariable(content, 0); pos[0] !== -1;
pos = findVariable(content, pos[0])) {
- var replacement = dict[content.slice(pos[0] + 1, pos[1])] || "";
- content = content.slice(0, pos[0]) + replacement + content.slice(pos[1] + 1);
- pos[0] += replacement.length;
+ var varName = content.slice(pos[0] + 1, pos[1]);
+ var replacement = dict[varName] || "";
+ if (!replacement && varName.endsWith("_optional")) {
+ var prevNewline = content.lastIndexOf('\n', pos[0]);
+ if (prevNewline === -1)
+ prevNewline = 0;
+ var nextNewline = content.indexOf('\n', pos[0]);
+ if (nextNewline === -1)
+ prevNewline = content.length;
+ content = content.slice(0, prevNewline) + content.slice(nextNewline);
+ pos[0] = prevNewline;
+ } else {
+ content = content.slice(0, pos[0]) + replacement + content.slice(pos[1] + 1);
+ pos[0] += replacement.length;
+ }
}
return content;
}
-function copyTemplateFile(fileName, targetDirectory, qtProps, location, allFiles, module, pluginMap,
- nonEssentialPlugins)
+function copyTemplateFile(fileName, targetDirectory, qtProps, abi, location, allFiles, module,
+ pluginMap, nonEssentialPlugins)
{
if (!File.makePath(targetDirectory)) {
- throw "Cannot create directory '" + toNative(targetDirectory) + "'.";
+ throw "Cannot create directory '" + FileInfo.toNativeSeparators(targetDirectory) + "'.";
}
var sourceFile = new TextFile(FileInfo.joinPaths(location, "templates", fileName),
TextFile.ReadOnly);
var newContent = sourceFile.readAll();
if (module) {
- newContent = replaceSpecialValues(newContent, module, qtProps);
+ newContent = replaceSpecialValues(newContent, module, qtProps, abi);
} else {
newContent = newContent.replace("@allPluginsByType@",
- '(' + toJSLiteral(pluginMap) + ')');
+ '(' + ModUtils.toJSLiteral(pluginMap) + ')');
newContent = newContent.replace("@nonEssentialPlugins@",
- toJSLiteral(nonEssentialPlugins));
+ ModUtils.toJSLiteral(nonEssentialPlugins));
+ newContent = newContent.replace("@version@", ModUtils.toJSLiteral(qtProps.qtVersion));
}
sourceFile.close();
var targetPath = FileInfo.joinPaths(targetDirectory, fileName);
@@ -1424,98 +375,117 @@ function copyTemplateFile(fileName, targetDirectory, qtProps, location, allFiles
targetFile.close();
}
-function setupOneQt(qmakeFilePath, outputBaseDir, uniquify, location, qbs) {
- if (!File.exists(qmakeFilePath))
- throw "The specified qmake file path '" + toNative(qmakeFilePath) + "' does not exist.";
- var qtProps = getQtProperties(qmakeFilePath, qbs);
- var modules = qtProps.qtMajorVersion < 5 ? allQt4Modules(qtProps) : allQt5Modules(qtProps);
- var pluginsByType = {};
- var nonEssentialPlugins = [];
- for (var i = 0; i < modules.length; ++i) {
- var m = modules[i];
- if (m.isPlugin) {
- if (!pluginsByType[m.pluginData.type])
- pluginsByType[m.pluginData.type] = [];
- pluginsByType[m.pluginData.type].push(m.name);
- if (!m.pluginData.autoLoad)
- nonEssentialPlugins.push(m.name);
- }
- }
-
- var relativeSearchPath = uniquify ? Utilities.getHash(qmakeFilePath) : "";
- var qbsQtModuleBaseDir = FileInfo.joinPaths(outputBaseDir, relativeSearchPath, "modules", "Qt");
- if (File.exists(qbsQtModuleBaseDir))
- File.remove(qbsQtModuleBaseDir);
-
- var allFiles = [];
- copyTemplateFile("QtModule.qbs", qbsQtModuleBaseDir, qtProps, location, allFiles);
- copyTemplateFile("QtPlugin.qbs", qbsQtModuleBaseDir, qtProps, location, allFiles);
- copyTemplateFile("plugin_support.qbs", FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"),
- qtProps, location, allFiles, undefined, pluginsByType, nonEssentialPlugins);
-
- for (i = 0; i < modules.length; ++i) {
- var module = modules[i];
- var qbsQtModuleDir = FileInfo.joinPaths(qbsQtModuleBaseDir, module.qbsName);
- var moduleTemplateFileName;
- if (module.qbsName === "core") {
- moduleTemplateFileName = "core.qbs";
- copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, location, allFiles);
- copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, location, allFiles);
- } else if (module.qbsName === "gui") {
- moduleTemplateFileName = "gui.qbs";
- } else if (module.qbsName === "scxml") {
- moduleTemplateFileName = "scxml.qbs";
- } else if (module.qbsName === "dbus") {
- moduleTemplateFileName = "dbus.qbs";
- copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, location, allFiles);
- } else if (module.qbsName === "qml") {
- moduleTemplateFileName = "qml.qbs";
- copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, location, allFiles);
+function setupOneQt(moduleName, qtInfo, outputBaseDir, uniquify, location) {
+ var qtProps = qtInfo.qtProps;
+
+ var relativeSearchPaths = [];
+ for (a = 0; a < qtInfo.abiInfos.length; ++a) {
+ var abiInfo = qtInfo.abiInfos[a];
+ var androidAbi = abiInfo.androidAbi;
+ if (qtInfo.abiInfos.length > 1)
+ console.info("Configuring abi '" + androidAbi + "'...");
+
+ var relativeSearchPath = uniquify ? Utilities.getHash(qtInfo.qmakeFilePath) : "";
+ relativeSearchPath = FileInfo.joinPaths(relativeSearchPath, androidAbi);
+ var qbsQtModuleBaseDir = FileInfo.joinPaths(outputBaseDir, relativeSearchPath,
+ "modules", "Qt");
+ // TODO:
+ // if (File.exists(qbsQtModuleBaseDir))
+ // File.remove(qbsQtModuleBaseDir);
+
+ var allFiles = [];
+ if (moduleName === "plugin_support") {
+ copyTemplateFile("plugin_support.qbs",
+ FileInfo.joinPaths(qbsQtModuleBaseDir, "plugin_support"), qtProps,
+ androidAbi, location, allFiles, undefined, abiInfo.pluginsByType,
+ abiInfo.nonEssentialPlugins);
+ relativeSearchPaths.push(relativeSearchPath);
+ return relativeSearchPaths;
+ } else if (moduleName === "android_support") {
+ // Note that it's not strictly necessary to copy this one, as it has no variable content.
+ // But we do it anyway for consistency.
+ copyTemplateFile("android_support.qbs",
+ FileInfo.joinPaths(qbsQtModuleBaseDir, "android_support"),
+ qtProps, androidAbi, location, allFiles);
+ relativeSearchPaths.push(relativeSearchPath);
+ return relativeSearchPaths;
+ } else if (moduleName === "qmlcache") {
var qmlcacheStr = "qmlcache";
- if (File.exists(FileInfo.joinPaths(qtProps.binaryPath,
- "qmlcachegen" + exeSuffix(qbs)))) {
+ if (File.exists(FileInfo.joinPaths(qtProps.qmlLibExecPath,
+ "qmlcachegen" + FileInfo.executableSuffix()))) {
copyTemplateFile(qmlcacheStr + ".qbs",
FileInfo.joinPaths(qbsQtModuleBaseDir, qmlcacheStr), qtProps,
- location, allFiles);
+ androidAbi, location, allFiles);
}
- } else if (module.qbsName === "quick") {
- moduleTemplateFileName = "quick.qbs";
- copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, location, allFiles);
- } else if (module.isPlugin) {
- moduleTemplateFileName = "plugin.qbs";
- } else {
- moduleTemplateFileName = "module.qbs";
+ relativeSearchPaths.push(relativeSearchPath);
+ return relativeSearchPaths;
}
- copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, location, allFiles,
- module);
- }
- // Note that it's not strictly necessary to copy this one, as it has no variable content.
- // But we do it anyway for consistency.
- copyTemplateFile("android_support.qbs",
- FileInfo.joinPaths(qbsQtModuleBaseDir, "android_support"),
- qtProps, location, allFiles);
- return relativeSearchPath;
+ if (abiInfo.modules[moduleName] !== undefined) {
+ var module = abiInfo.modules[moduleName];
+ var qbsQtModuleDir = FileInfo.joinPaths(qbsQtModuleBaseDir, module.qbsName);
+ var moduleTemplateFileName;
+
+ if (module.qbsName === "core") {
+ moduleTemplateFileName = "core.qbs";
+ copyTemplateFile("moc.js", qbsQtModuleDir, qtProps, androidAbi, location,
+ allFiles);
+ copyTemplateFile("qdoc.js", qbsQtModuleDir, qtProps, androidAbi, location,
+ allFiles);
+ copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbi, location,
+ allFiles);
+ } else if (module.qbsName === "gui") {
+ moduleTemplateFileName = "gui.qbs";
+ } else if (module.qbsName === "scxml") {
+ moduleTemplateFileName = "scxml.qbs";
+ } else if (module.qbsName === "dbus") {
+ moduleTemplateFileName = "dbus.qbs";
+ copyTemplateFile("dbus.js", qbsQtModuleDir, qtProps, androidAbi, location,
+ allFiles);
+ } else if (module.qbsName === "qml") {
+ moduleTemplateFileName = "qml.qbs";
+ copyTemplateFile("qml.js", qbsQtModuleDir, qtProps, androidAbi, location,
+ allFiles);
+ } else if (module.qbsName === "quick") {
+ moduleTemplateFileName = "quick.qbs";
+ copyTemplateFile("quick.js", qbsQtModuleDir, qtProps, androidAbi, location,
+ allFiles);
+ copyTemplateFile("rcc.js", qbsQtModuleDir, qtProps, androidAbi, location,
+ allFiles);
+ } else if (module.isPlugin) {
+ moduleTemplateFileName = "plugin.qbs";
+ } else {
+ moduleTemplateFileName = "module.qbs";
+ }
+ copyTemplateFile(moduleTemplateFileName, qbsQtModuleDir, qtProps, androidAbi,
+ location, allFiles, module);
+ relativeSearchPaths.push(relativeSearchPath);
+ }
+ }
+ return relativeSearchPaths;
}
-function doSetup(qmakeFilePaths, outputBaseDir, location, qbs) {
- qmakeFilePaths = getQmakeFilePaths(qmakeFilePaths, qbs);
- if (!qmakeFilePaths || qmakeFilePaths.length === 0)
+function doSetup(moduleName, qtInfos, outputBaseDir, location) {
+ if (!qtInfos || qtInfos.length === 0)
return [];
- var uniquifySearchPath = qmakeFilePaths.length > 1;
- var searchPaths = [];
- for (var i = 0; i < qmakeFilePaths.length; ++i) {
+ var uniquifySearchPath = qtInfos.length > 1;
+ var allSearchPaths = [];
+ moduleName = moduleName.substring(3);
+ for (var i = 0; i < qtInfos.length; ++i) {
try {
- console.info("Setting up Qt at '" + toNative(qmakeFilePaths[i]) + "'...");
- var searchPath = setupOneQt(qmakeFilePaths[i], outputBaseDir, uniquifySearchPath,
- location, qbs);
- if (searchPath !== undefined) {
- searchPaths.push(searchPath);
- console.info("Qt was set up successfully.");
+ console.info("Setting up Qt module '" + moduleName + "' for Qt located at '"
+ + FileInfo.toNativeSeparators(qtInfos[i].qmakeFilePath) + "'.");
+ var searchPaths = setupOneQt(moduleName, qtInfos[i], outputBaseDir, uniquifySearchPath,
+ location);
+ if (searchPaths.length > 0) {
+ for (var j = 0; j < searchPaths.length; ++j )
+ allSearchPaths.push(searchPaths[j]);
}
} catch (e) {
- console.warn("Error setting up Qt for '" + toNative(qmakeFilePaths[i]) + "': " + e);
+ console.warn("Error setting up Qt module '" + moduleName + "' for '"
+ + FileInfo.toNativeSeparators(qtInfos[i].qmakeFilePath) + "': " + e);
+ throw e;
}
}
- return searchPaths;
+ return allSearchPaths;
}
diff --git a/share/qbs/module-providers/Qt/templates/QtModule.qbs b/share/qbs/module-providers/Qt/templates/QtModule.qbs
deleted file mode 100644
index aa7c1d15a..000000000
--- a/share/qbs/module-providers/Qt/templates/QtModule.qbs
+++ /dev/null
@@ -1,86 +0,0 @@
-import qbs.FileInfo
-
-Module {
- condition: (qbs.targetPlatform === targetPlatform || isCombinedUIKitBuild)
- && (!qbs.architecture
- || architectures.length === 0
- || architectures.contains(qbs.architecture))
-
- readonly property bool isCombinedUIKitBuild: ["ios", "tvos", "watchos"].contains(targetPlatform)
- && ["x86", "x86_64"].contains(qbs.architecture)
- && qbs.targetPlatform === targetPlatform + "-simulator"
-
- Depends { name: "cpp" }
- Depends { name: "Qt.core" }
-
- Depends { name: "Qt.plugin_support" }
- property stringList pluginTypes
- Qt.plugin_support.pluginTypes: pluginTypes
- Depends {
- condition: Qt.core.staticBuild && !isPlugin
- name: "Qt";
- submodules: {
- // We have to pull in all plugins here, because dependency resolving happens
- // before module merging, and we don't know yet if someone set
- // Qt.pluginSupport.pluginsByType in the product.
- // The real filtering is done later by the plugin module files themselves.
- var list = [];
- var allPlugins = Qt.plugin_support.allPluginsByType;
- for (var i = 0; i < (pluginTypes || []).length; ++i)
- Array.prototype.push.apply(list, allPlugins[pluginTypes[i]])
- return list;
- }
- }
-
- 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 architectures
- property string targetPlatform
- 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
- property bool enableLinking: qtModuleName != undefined && hasLibrary
- property stringList moduleConfig
-
- Properties {
- condition: enableLinking
- cpp.staticLibraries: staticLibs
- cpp.dynamicLibraries: dynamicLibs
- cpp.frameworks: mFrameworks.concat(!isStaticLibrary && Qt.core.frameworkBuild
- ? [libNameForLinker] : [])
- cpp.frameworkPaths: mFrameworkPaths
- }
-}
diff --git a/share/qbs/module-providers/Qt/templates/QtPlugin.qbs b/share/qbs/module-providers/Qt/templates/QtPlugin.qbs
deleted file mode 100644
index 88bfa5a65..000000000
--- a/share/qbs/module-providers/Qt/templates/QtPlugin.qbs
+++ /dev/null
@@ -1,51 +0,0 @@
-import qbs.FileInfo
-import qbs.TextFile
-
-QtModule {
- isPlugin: true
-
- property string className
- property stringList extendsModules
-
- enableLinking: {
- if (!base)
- return false;
- if (!isStaticLibrary)
- return false;
- if (!Qt.plugin_support.linkPlugins)
- return false;
- if (!(Qt.plugin_support.enabledPlugins || []).contains(qtModuleName))
- return false;
- if (!extendsModules || extendsModules.length === 0)
- return true;
- for (var i = 0; i < extendsModules.length; ++i) {
- var moduleName = extendsModules[i];
- if (product.Qt[moduleName] && product.Qt[moduleName].present)
- return true;
- }
- return false;
- }
-
- Rule {
- condition: enableLinking
- multiplex: true
- Artifact {
- filePath: product.targetName + "_qt_plugin_import_"
- + product.moduleProperty(product.moduleName, "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 <QtPlugin>\n\nQ_IMPORT_PLUGIN(" + className + ")");
- f.close();
- };
- return cmd;
- }
- }
-}
diff --git a/share/qbs/module-providers/Qt/templates/android_support.qbs b/share/qbs/module-providers/Qt/templates/android_support.qbs
index c5f842a1f..6d548f194 100644
--- a/share/qbs/module-providers/Qt/templates/android_support.qbs
+++ b/share/qbs/module-providers/Qt/templates/android_support.qbs
@@ -3,29 +3,39 @@ import qbs.FileInfo
import qbs.ModUtils
import qbs.TextFile
import qbs.Utilities
+import qbs.Process
+import qbs.Xml
Module {
- property bool useMinistro: false
+ version: @version@
property string qmlRootDir: product.sourceDirectory
property stringList extraPrefixDirs
+ property stringList qmlImportPaths
property stringList deploymentDependencies // qmake: ANDROID_DEPLOYMENT_DEPENDENCIES
property stringList extraPlugins // qmake: ANDROID_EXTRA_PLUGINS
property stringList extraLibs // qmake: ANDROID_EXTRA_LIBS
property bool verboseAndroidDeployQt: false
-
- property string _androidDeployQtFilePath: FileInfo.joinPaths(_qtInstallDir, "bin",
+ property string _androidDeployQtFilePath: FileInfo.joinPaths(_qtBinaryDir, "bin",
"androiddeployqt")
+ property string rccFilePath
+ property string _qtBinaryDir
property string _qtInstallDir
- property bool _enableSdkSupport: product.type && product.type.contains("android.apk")
- && !consoleApplication
+ property bool _enableSdkSupport: product.type && product.type.contains("android.package")
+ && !product.consoleApplication
property bool _enableNdkSupport: !product.aggregate || product.multiplexConfigurationId
property string _templatesBaseDir: FileInfo.joinPaths(_qtInstallDir, "src", "android")
property string _deployQtOutDir: FileInfo.joinPaths(product.buildDirectory, "deployqt_out")
+ property bool _multiAbi: Utilities.versionCompare(version, "5.14") >= 0
+
+ // QTBUG-87288: correct QtNetwork jar dependencies for 5.15.0 < Qt < 5.15.3
+ property bool _correctQtNetworkDependencies: Utilities.versionCompare(version, "5.15.0") > 0 &&
+ Utilities.versionCompare(version, "5.15.3") < 0
+
Depends { name: "Android.sdk"; condition: _enableSdkSupport }
Depends { name: "Android.ndk"; condition: _enableNdkSupport }
Depends { name: "java"; condition: _enableSdkSupport }
- Depends { name: "cpp" }
+ Depends { name: "cpp"; condition: _enableNdkSupport }
Properties {
condition: _enableNdkSupport && qbs.toolchain.contains("clang")
@@ -40,15 +50,45 @@ Module {
Android.sdk.customManifestProcessing: true
java._tagJniHeaders: false // prevent rule cycle
}
+ readonly property string _qtAndroidJarFileName: Utilities.versionCompare(version, "6.0") >= 0 ?
+ "Qt6Android.jar" : "QtAndroid.jar"
+ Properties {
+ condition: _enableSdkSupport && Utilities.versionCompare(version, "5.15") >= 0
+ && Utilities.versionCompare(version, "6.0") < 0
+ java.additionalClassPaths: [FileInfo.joinPaths(_qtInstallDir, "jar", "QtAndroid.jar")]
+ }
+ Properties {
+ condition: _enableSdkSupport && Utilities.versionCompare(version, "6.0") >= 0
+ java.additionalClassPaths: [FileInfo.joinPaths(_qtInstallDir, "jar", "Qt6Android.jar")]
+ }
+ // "ANDROID_HAS_WSTRING" was removed from qtcore qstring.h in Qt 5.14.0
Properties {
- condition: _enableNdkSupport && (Android.ndk.abi === "armeabi-v7a" || Android.ndk.abi === "x86")
+ condition: _enableNdkSupport && Utilities.versionCompare(version, "5.14.0") < 0 &&
+ (Android.ndk.abi === "armeabi-v7a" || Android.ndk.abi === "x86")
cpp.defines: "ANDROID_HAS_WSTRING"
}
+ Properties {
+ condition: _enableSdkSupport
+ Android.sdk._bundledInAssets: _multiAbi
+ }
+ Properties {
+ condition: _enableSdkSupport && Utilities.versionCompare(version, "6.0") < 0
+ Android.sdk.minimumVersion: "21"
+ }
+ Properties {
+ condition: _enableSdkSupport && Utilities.versionCompare(version, "6.0") >= 0
+ Android.sdk.minimumVersion: "23"
+ }
+
+ Properties {
+ condition: _enableNdkSupport
+ cpp.archSuffix: _multiAbi ? "_" + Android.ndk.abi : ""
+ }
Rule {
condition: _enableSdkSupport
multiplex: true
- property stringList inputTags: "android.nativelibrary"
+ property stringList inputTags: ["android.nativelibrary", "qrc"]
inputsFromDependencies: inputTags
inputs: product.aggregate ? [] : inputTags
Artifact {
@@ -61,37 +101,78 @@ Module {
cmd.sourceCode = function() {
var theBinary;
var nativeLibs = inputs["android.nativelibrary"];
+ var architectures = [];
+ var triples = [];
+ var qtInstallDirectories = [];
+ var hostArch;
+ var targetArchitecture;
if (nativeLibs.length === 1) {
theBinary = nativeLibs[0];
+ hostArch = theBinary.Android.ndk.hostArch;
+ targetArchitecture = theBinary.Android.ndk.abi;
+ if (product.Qt.android_support._multiAbi) {
+ architectures.push(theBinary.Android.ndk.abi);
+ triples.push(theBinary.cpp.toolchainTriple);
+ qtInstallDirectories.push(theBinary.Qt.android_support._qtInstallDir);
+ }
} else {
for (i = 0; i < nativeLibs.length; ++i) {
var candidate = nativeLibs[i];
- if (!candidate.fileName.contains(candidate.product.targetName))
- continue;
- if (!theBinary) {
- theBinary = candidate;
- continue;
- }
- if (theBinary.product.name === product.name
- && candidate.product.name !== product.name) {
- continue; // We already have a better match.
- }
- if (candidate.product.name === product.name
- && theBinary.product.name !== product.name) {
- theBinary = candidate; // The new candidate is a better match.
- continue;
+ if (product.Qt.android_support._multiAbi) {
+ if (candidate.product.name === product.name) {
+ architectures.push(candidate.Android.ndk.abi);
+ triples.push(candidate.cpp.toolchainTriple);
+ qtInstallDirectories.push(
+ candidate.Qt.android_support._qtInstallDir);
+ hostArch = candidate.Android.ndk.hostArch;
+ targetArchitecture = candidate.Android.ndk.abi;
+ theBinary = candidate;
+ }
+ } else {
+ if (!candidate.fileName.contains(candidate.product.targetName))
+ continue;
+ if (!theBinary) {
+ theBinary = candidate;
+ hostArch = theBinary.Android.ndk.hostArch;
+ targetArchitecture = theBinary.Android.ndk.abi;
+ continue;
+ }
+ if (candidate.product.name !== product.name) {
+ continue; // This is not going to be a match
+ }
+ if (candidate.product.name === product.name
+ && theBinary.product.name !== product.name) {
+ theBinary = candidate; // The new candidate is a better match.
+ hostArch = theBinary.Android.ndk.hostArch;
+ targetArchitecture = theBinary.Android.ndk.abi;
+ continue;
+ }
+
+ throw "Qt applications for Android support only one native binary "
+ + "per package.\n"
+ + "In particular, you cannot build a Qt app for more than "
+ + "one architecture at the same time.";
}
- throw "Qt applications for Android support only one native binary "
- + "per package.\n"
- + "In particular, you cannot build a Qt app for more than "
- + "one architecture at the same time.";
}
}
var f = new TextFile(output.filePath, TextFile.WriteOnly);
f.writeLine("{");
f.writeLine('"description": "This file was generated by qbs to be read by '
+ 'androiddeployqt and should not be modified by hand.",');
- f.writeLine('"qt": "' + product.Qt.android_support._qtInstallDir + '",');
+ if (Utilities.versionCompare(product.Qt.core.version, "6.3.0") >= 0) {
+ var line = '"qt": {';
+ for (var i = 0; i < qtInstallDirectories.length && i < architectures.length;
+ ++i) {
+ line = line + '"' + architectures[i] + '":"' +
+ qtInstallDirectories[i] + '"';
+ if (i < qtInstallDirectories.length-1 || i < architectures.length-1)
+ line = line + ',';
+ }
+ line = line + "},";
+ f.writeLine(line);
+ } else {
+ f.writeLine('"qt": "' + product.Qt.android_support._qtInstallDir + '",');
+ }
f.writeLine('"sdk": "' + product.Android.sdk.sdkDir + '",');
f.writeLine('"sdkBuildToolsRevision": "' + product.Android.sdk.buildToolsVersion
+ '",');
@@ -99,8 +180,20 @@ Module {
f.writeLine('"toolchain-prefix": "llvm",');
f.writeLine('"tool-prefix": "llvm",');
f.writeLine('"useLLVM": true,');
- f.writeLine('"ndk-host": "' + theBinary.Android.ndk.hostArch + '",');
- f.writeLine('"target-architecture": "' + theBinary.Android.ndk.abi + '",');
+ f.writeLine('"ndk-host": "' + hostArch + '",');
+ if (!product.Qt.android_support._multiAbi) {
+ f.writeLine('"target-architecture": "' + targetArchitecture + '",');
+ }
+ else {
+ var line = '"architectures": {';
+ for (var i in architectures) {
+ line = line + '"' + architectures[i] + '":"' + triples[i] + '"';
+ if (i < architectures.length-1)
+ line = line + ',';
+ }
+ line = line + "},";
+ f.writeLine(line);
+ }
f.writeLine('"qml-root-path": "' + product.Qt.android_support.qmlRootDir + '",');
var deploymentDeps = product.Qt.android_support.deploymentDependencies;
if (deploymentDeps && deploymentDeps.length > 0)
@@ -120,15 +213,41 @@ Module {
var prefixDirs = product.Qt.android_support.extraPrefixDirs;
if (prefixDirs && prefixDirs.length > 0)
f.writeLine('"extraPrefixDirs": ' + JSON.stringify(prefixDirs) + ',');
- if (Array.isArray(product.qmlImportPaths) && product.qmlImportPaths.length > 0)
+ var qmlImportPaths = product.Qt.android_support.qmlImportPaths;
+ if (qmlImportPaths && qmlImportPaths.length > 0)
+ f.writeLine('"qml-import-paths": "' + qmlImportPaths.join(',') + '",');
+ else if ((product.qmlImportPaths instanceof Array) && product.qmlImportPaths.length > 0)
f.writeLine('"qml-import-paths": "' + product.qmlImportPaths.join(',') + '",');
+ if (Utilities.versionCompare(product.Qt.android_support.version, "6.0") >= 0) {
+ f.writeLine('"qml-importscanner-binary": "'
+ + product.Qt.core.qmlImportScannerFilePath + FileInfo.executableSuffix()
+ + '",');
+ f.writeLine('"rcc-binary": "' + product.Qt.android_support.rccFilePath
+ + FileInfo.executableSuffix() + '",');
+
+ if (inputs["qrc"] && inputs["qrc"].length > 0) {
+ var qrcFiles = [];
+ var qrcInputs = inputs["qrc"];
+ for (i = 0; i < qrcInputs.length; ++i) {
+ qrcFiles.push(qrcInputs[i].filePath);
+ }
+ f.writeLine('"qrcFiles": "' + qrcFiles.join(',') + '",');
+ }
+ }
+
// QBS-1429
- f.writeLine('"stdcpp-path": "' + (product.cpp.sharedStlFilePath
+ if (!product.Qt.android_support._multiAbi) {
+ f.writeLine('"stdcpp-path": "' + (product.cpp.sharedStlFilePath
? product.cpp.sharedStlFilePath : product.cpp.staticStlFilePath)
+ '",');
+ f.writeLine('"application-binary": "' + theBinary.filePath + '"');
+ } else {
+ f.writeLine('"stdcpp-path": "' + product.Android.sdk.ndkDir +
+ '/toolchains/llvm/prebuilt/' + hostArch + '/sysroot/usr/lib/",');
+ f.writeLine('"application-binary": "' + theBinary.product.targetName + '"');
+ }
- f.writeLine('"application-binary": "' + theBinary.filePath + '"');
f.writeLine("}");
f.close();
};
@@ -167,45 +286,48 @@ Module {
Rule {
condition: _enableSdkSupport
multiplex: true
- inputs: ["qt_androiddeployqt_input", "android.manifest_processed"]
+ property stringList defaultInputs: ["qt_androiddeployqt_input",
+ "android.manifest_processed"]
+ property stringList allInputs: ["qt_androiddeployqt_input", "android.manifest_processed",
+ "android.nativelibrary"]
+ inputsFromDependencies: "android.nativelibrary"
+ inputs: product.aggregate ? defaultInputs : allInputs
outputFileTags: [
"android.manifest_final", "android.resources", "android.assets", "bundled_jar",
"android.deployqt_list",
]
- outputArtifacts: {
- var artifacts = [
- {
- filePath: "AndroidManifest.xml",
- fileTags: "android.manifest_final"
- },
- {
- filePath: product.Qt.android_support._deployQtOutDir + "/res/values/libs.xml",
- fileTags: "android.resources"
- },
- {
- filePath: product.Qt.android_support._deployQtOutDir
- + "/res/values/strings.xml",
- fileTags: "android.resources"
- },
- {
- filePath: product.Qt.android_support._deployQtOutDir + "/assets/.dummy",
- fileTags: "android.assets"
- },
- {
- filePath: "deployqt.list",
- fileTags: "android.deployqt_list"
- },
-
- ];
- if (!product.Qt.android_support.useMinistro) {
- artifacts.push({
- filePath: FileInfo.joinPaths(product.java.classFilesDir, "QtAndroid.jar"),
- fileTags: ["bundled_jar"]
- });
+ outputArtifacts: [
+ {
+ filePath: "AndroidManifest.xml",
+ fileTags: "android.manifest_final"
+ },
+ {
+ filePath: product.Qt.android_support._deployQtOutDir + "/res/values/libs.xml",
+ fileTags: "android.resources"
+ },
+ {
+ filePath: product.Qt.android_support._deployQtOutDir
+ + "/res/values/strings.xml",
+ fileTags: "android.resources"
+ },
+ {
+ filePath: product.Qt.android_support._deployQtOutDir + "/assets/.dummy",
+ fileTags: "android.assets"
+ },
+ {
+ filePath: "deployqt.list",
+ fileTags: "android.deployqt_list"
+ },
+ // androiddeployqt potentially copies more jar files but this one will always be there
+ // since it comes with Qt.core
+ {
+ filePath: FileInfo.joinPaths(product.java.classFilesDir,
+ product.Qt.android_support._qtAndroidJarFileName),
+ fileTags: "bundled_jar"
}
- return artifacts;
- }
+ ]
prepare: {
+ var cmds = [];
var copyCmd = new JavaScriptCommand();
copyCmd.description = "copying Qt resource templates";
copyCmd.sourceCode = function() {
@@ -216,22 +338,36 @@ Module {
File.copy(product.Qt.android_support._templatesBaseDir
+ "/templates/res/values/libs.xml",
product.Qt.android_support._deployQtOutDir + "/res/values/libs.xml");
- try {
- File.remove(FileInfo.path(outputs["android.assets"][0].filePath));
- } catch (e) {
+ if (!product.Qt.android_support._multiAbi) {
+ try {
+ File.remove(FileInfo.path(outputs["android.assets"][0].filePath));
+ } catch (e) {
+ }
+ }
+ else {
+ for (var i in inputs["android.nativelibrary"]) {
+ var input = inputs["android.nativelibrary"][i];
+ File.copy(input.filePath,
+ FileInfo.joinPaths(product.Qt.android_support._deployQtOutDir,
+ "libs", input.Android.ndk.abi,
+ input.fileName));
+ }
}
};
+ cmds.push(copyCmd);
+
var androidDeployQtArgs = [
"--output", product.Qt.android_support._deployQtOutDir,
"--input", inputs["qt_androiddeployqt_input"][0].filePath, "--aux-mode",
- "--deployment", product.Qt.android_support.useMinistro ? "ministro" : "bundled",
+ "--deployment", "bundled",
"--android-platform", product.Android.sdk.platform,
];
if (product.Qt.android_support.verboseAndroidDeployQt)
- args.push("--verbose");
+ androidDeployQtArgs.push("--verbose");
var androidDeployQtCmd = new Command(
product.Qt.android_support._androidDeployQtFilePath, androidDeployQtArgs);
androidDeployQtCmd.description = "running androiddeployqt";
+ cmds.push(androidDeployQtCmd);
// We do not want androiddeployqt to write directly into our APK base dir, so
// we ran it on an isolated directory and now we move stuff over.
@@ -239,12 +375,11 @@ Module {
// of androiddeployqt creates fewer files, the other ones are removed from
// the APK base dir.
var moveCmd = new JavaScriptCommand();
- moveCmd.description = "processing androiddeployqt outout";
+ moveCmd.description = "processing androiddeployqt output";
moveCmd.sourceCode = function() {
- File.move(product.Qt.android_support._deployQtOutDir + "/AndroidManifest.xml",
- outputs["android.manifest_final"][0].filePath);
+ File.makePath(product.java.classFilesDir);
var libsDir = product.Qt.android_support._deployQtOutDir + "/libs";
- var libDir = product.Android.sdk.apkContentsDir + "/lib";
+ var libDir = product.Android.sdk.packageContentsDir + "/lib";
var listFilePath = outputs["android.deployqt_list"][0].filePath;
var oldLibs = [];
try {
@@ -286,7 +421,107 @@ Module {
File.remove(oldLibs[i]);
}
};
- return [copyCmd, androidDeployQtCmd, moveCmd];
+ cmds.push(moveCmd);
+
+ // androiddeployqt doesn't strip the deployed libraries anymore so it has to done here
+ // but only for release build
+ if (product.qbs.buildVariant == "release") {
+ var stripLibsCmd = new JavaScriptCommand();
+ stripLibsCmd.description = "stripping unneeded symbols from deployed qt libraries";
+ stripLibsCmd.sourceCode = function() {
+ var stripArgs = ["--strip-all"];
+ var architectures = [];
+ for (var i in inputs["android.nativelibrary"])
+ architectures.push(inputs["android.nativelibrary"][i].Android.ndk.abi);
+ for (var i in architectures) {
+ var abiDirPath = FileInfo.joinPaths(product.Android.sdk.packageContentsDir,
+ "lib", architectures[i]);
+ var files = File.directoryEntries(abiDirPath, File.Files);
+ for (var i = 0; i < files.length; ++i) {
+ var filePath = FileInfo.joinPaths(abiDirPath, files[i]);
+ if (FileInfo.suffix(filePath) == "so") {
+ stripArgs.push(filePath);
+ }
+ }
+ }
+ var process = new Process();
+ process.exec(product.cpp.stripPath, stripArgs, false);
+ }
+ cmds.push(stripLibsCmd);
+ }
+
+ var correctingCmd = new JavaScriptCommand();
+ if (product.Qt.android_support._correctQtNetworkDependencies) {
+ correctingCmd.description = "correcting network jar dependency";
+ correctingCmd.sourceCode = function() {
+ var findNetworkLib = function() {
+ var libsDir = product.Android.sdk.packageContentsDir + "/lib";
+ var dirList = File.directoryEntries(libsDir, File.Dirs |
+ File.NoDotAndDotDot);
+ for (var i = 0; i < dirList.length; ++i) {
+ var archDir = FileInfo.joinPaths(libsDir, dirList[i]);
+ var fileList = File.directoryEntries(archDir, File.Files);
+ if (fileList) {
+ for (var j = 0; j < fileList.length; ++j) {
+ if (fileList[j].contains("libQt5Network")) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ if (findNetworkLib()) {
+ var manifestData = new Xml.DomDocument();
+ var manifestFilePath = product.Qt.android_support._deployQtOutDir +
+ "/AndroidManifest.xml"
+ manifestData.load(manifestFilePath);
+
+ var rootElem = manifestData.documentElement();
+ if (!rootElem || !rootElem.isElement() || rootElem.tagName() != "manifest")
+ throw "No manifest tag found in '" + manifestFilePath + "'.";
+ var appElem = rootElem.firstChild("application");
+ if (!appElem || !appElem.isElement() || appElem.tagName() != "application")
+ throw "No application tag found in '" + manifestFilePath + "'.";
+ var activityElem = appElem.firstChild("activity");
+ if (!activityElem || !activityElem.isElement() ||
+ activityElem.tagName() != "activity")
+ throw "No activity tag found in '" + manifestFilePath + "'.";
+ var metaDataElem = activityElem.firstChild("meta-data");
+ while (metaDataElem && metaDataElem.isElement()) {
+ if (metaDataElem.attribute("android:name") ==
+ "android.app.load_local_jars" ) {
+ var value = metaDataElem.attribute("android:value");
+ var fileName = "QtAndroidNetwork.jar";
+ metaDataElem.setAttribute("android:value", value + ":jar/" +
+ fileName);
+ var jarFilePath = FileInfo.joinPaths(
+ product.Qt.android_support._qtInstallDir, "jar",
+ fileName);
+ var targetFilePath = FileInfo.joinPaths(product.java.classFilesDir,
+ fileName);
+ File.copy(jarFilePath, targetFilePath);
+ break;
+ }
+ metaDataElem = metaDataElem.nextSibling("meta-data");
+ }
+ manifestData.save(outputs["android.manifest_final"][0].filePath, 4);
+ } else {
+ File.move(product.Qt.android_support._deployQtOutDir + "/AndroidManifest.xml",
+ outputs["android.manifest_final"][0].filePath);
+ }
+ };
+ } else {
+ correctingCmd.description = "copying manifest";
+ correctingCmd.sourceCode = function() {
+ File.move(product.Qt.android_support._deployQtOutDir + "/AndroidManifest.xml",
+ outputs["android.manifest_final"][0].filePath);
+ }
+ }
+ cmds.push(correctingCmd);
+
+ return cmds;
}
}
diff --git a/share/qbs/module-providers/Qt/templates/core.qbs b/share/qbs/module-providers/Qt/templates/core.qbs
index 8e990db22..485402716 100644
--- a/share/qbs/module-providers/Qt/templates/core.qbs
+++ b/share/qbs/module-providers/Qt/templates/core.qbs
@@ -5,6 +5,7 @@ import qbs.Utilities
import qbs.Xml
import "moc.js" as Moc
import "qdoc.js" as Qdoc
+import "rcc.js" as Rcc
Module {
condition: (qbs.targetPlatform === targetPlatform || isCombinedUIKitBuild)
@@ -17,13 +18,21 @@ Module {
&& qbs.targetPlatform === targetPlatform + "-simulator"
Depends { name: "cpp" }
+ Depends { name: "Sanitizers.address"; condition: config.contains("sanitize_address") }
Depends { name: "Qt.android_support"; condition: qbs.targetOS.contains("android") }
Properties {
condition: qbs.targetOS.contains("android")
- Qt.android_support._qtInstallDir: FileInfo.path(binPath)
+ Qt.android_support._qtBinaryDir: FileInfo.path(binPath)
+ Qt.android_support._qtInstallDir: FileInfo.path(installPath)
Qt.android_support.version: version
+ Qt.android_support.rccFilePath: Rcc.fullPath(product)
}
+ // qmlImportScanner is required by androiddeployqt even if the project doesn't
+ // depend on qml. That's why the scannerName must be defined here and not in the
+ // qml module
+ property string qmlImportScannerName: "qmlimportscanner"
+ property string qmlImportScannerFilePath: qmlLibExecPath + '/' + qmlImportScannerName
version: @version@
property stringList architectures: @archs@
@@ -32,17 +41,23 @@ Module {
property stringList config: @config@
property stringList qtConfig: @qtConfig@
property path binPath: @binPath@
+ property path installPath: @installPath@
property path incPath: @incPath@
property path libPath: @libPath@
+ property path installPrefixPath: @installPrefixPath@
+ property path libExecPath: @libExecPath@
+ property path qmlLibExecPath: @qmlLibExecPath@
property path pluginPath: @pluginPath@
property string mkspecName: @mkspecName@
property path mkspecPath: @mkspecPath@
property string mocName: "moc"
property stringList mocFlags: []
property string lreleaseName: "lrelease"
+ property string rccName: "rcc"
property string qdocName: versionMajor >= 5 ? "qdoc" : "qdoc3"
property stringList qdocEnvironment
property path docPath: @docPath@
+ property string helpGeneratorLibExecPath: @helpGeneratorLibExecPath@
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]
@@ -52,11 +67,17 @@ Module {
property bool staticBuild: @staticBuild@
property stringList pluginMetaData: []
property bool enableKeywords: true
+ property bool generateMetaTypesFile
+ readonly property bool _generateMetaTypesFile: generateMetaTypesFile
+ && Utilities.versionCompare(version, "5.15") >= 0
+ property string metaTypesInstallDir
property stringList availableBuildVariants: @availableBuildVariants@
property string qtBuildVariant: {
if (availableBuildVariants.contains(qbs.buildVariant))
return qbs.buildVariant;
+ if (qbs.buildVariant === "profiling" && availableBuildVariants.contains("release"))
+ return "release";
return availableBuildVariants.length > 0 ? availableBuildVariants[0] : "";
}
@@ -88,6 +109,7 @@ Module {
property string libFilePathRelease: @libFilePathRelease@
property string libFilePath: qtBuildVariant === "debug"
? libFilePathDebug : libFilePathRelease
+ property bool useRPaths: qbs.targetOS.contains("linux") && !qbs.targetOS.contains("android")
property stringList coreLibPaths: @libraryPaths@
property bool hasLibrary: true
@@ -101,6 +123,7 @@ Module {
property bool lreleaseMultiplexMode: false
property stringList moduleConfig: @moduleConfig@
+
Properties {
condition: moduleConfig.contains("use_gold_linker")
cpp.linkerVariant: "gold"
@@ -110,14 +133,16 @@ Module {
cpp.linkerVariant: original
}
- cpp.cxxLanguageVersion: Utilities.versionCompare(version, "5.7.0") >= 0 ? "c++11" : original
+ cpp.cxxLanguageVersion: Utilities.versionCompare(version, "6.0.0") >= 0
+ ? "c++17"
+ : Utilities.versionCompare(version, "5.7.0") >= 0 ? "c++11" : original
cpp.enableCompilerDefinesByLanguage: ["cpp"].concat(
qbs.targetOS.contains("darwin") ? ["objcpp"] : [])
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)
+ // ### QT_NO_DEBUG must be added if the current build variant is not derived
+ // from the build variant "debug"
+ if (!qbs.enableDebugCode)
defines.push("QT_NO_DEBUG");
if (!enableKeywords)
defines.push("QT_NO_KEYWORDS");
@@ -127,13 +152,13 @@ Module {
if (Utilities.versionCompare(version, "5.6.0") < 0)
defines.push("main=qtmn");
}
+ if (qbs.toolchain.contains("msvc"))
+ defines.push("_ENABLE_EXTENDED_ALIGNED_STORAGE");
return defines;
}
cpp.driverFlags: {
var flags = [];
if (qbs.toolchain.contains("gcc")) {
- if (config.contains("sanitize_address"))
- flags.push("-fsanitize=address");
if (config.contains("sanitize_undefined"))
flags.push("-fsanitize=undefined");
if (config.contains("sanitize_thread"))
@@ -143,11 +168,8 @@ Module {
}
return flags;
}
- cpp.includePaths: {
- var paths = @includes@;
- paths.push(mkspecPath, generatedHeadersDir);
- return paths;
- }
+ cpp.includePaths: generatedHeadersDir
+ cpp.systemIncludePaths: @includes@.concat(mkspecPath)
cpp.libraryPaths: {
var libPaths = [libPath];
if (staticBuild && pluginPath)
@@ -166,7 +188,7 @@ Module {
}
cpp.dynamicLibraries: dynamicLibs
cpp.linkerFlags: coreLinkerFlags
- cpp.frameworkPaths: coreFrameworkPaths.concat(frameworkBuild ? [libPath] : [])
+ cpp.systemFrameworkPaths: coreFrameworkPaths.concat(frameworkBuild ? [libPath] : [])
cpp.frameworks: {
var frameworks = coreFrameworks
if (frameworkBuild)
@@ -177,18 +199,21 @@ Module {
return undefined;
return frameworks;
}
- cpp.rpaths: qbs.targetOS.contains('linux') ? [libPath] : undefined
+ cpp.rpaths: useRPaths ? libPath : undefined
cpp.runtimeLibrary: qbs.toolchain.contains("msvc")
? config.contains("static_runtime") ? "static" : "dynamic"
: original
- cpp.positionIndependentCode: versionMajor >= 5 ? true : undefined
+ cpp.positionIndependentCode: versionMajor >= 5 ? true : original
cpp.cxxFlags: {
var flags = [];
if (qbs.toolchain.contains('msvc')) {
if (versionMajor < 5)
flags.push('/Zc:wchar_t-');
+ if (Utilities.versionCompare(version, "6.3") >= 0
+ && Utilities.versionCompare(cpp.compilerVersion, "19.10") >= 0) {
+ flags.push("/permissive-");
+ }
}
-
return flags;
}
cpp.cxxStandardLibrary: {
@@ -197,19 +222,19 @@ Module {
return "libc++";
return original;
}
- cpp.minimumWindowsVersion: @minWinVersion@
- cpp.minimumMacosVersion: @minMacVersion@
- cpp.minimumIosVersion: @minIosVersion@
- cpp.minimumTvosVersion: @minTvosVersion@
- cpp.minimumWatchosVersion: @minWatchosVersion@
- cpp.minimumAndroidVersion: @minAndroidVersion@
+ cpp.minimumWindowsVersion: @minWinVersion_optional@
+ cpp.minimumMacosVersion: @minMacVersion_optional@
+ cpp.minimumIosVersion: @minIosVersion_optional@
+ cpp.minimumTvosVersion: @minTvosVersion_optional@
+ cpp.minimumWatchosVersion: @minWatchosVersion_optional@
+ cpp.minimumAndroidVersion: @minAndroidVersion_optional@
// Universal Windows Platform support
cpp.windowsApiFamily: mkspecName.startsWith("winrt-") ? "pc" : undefined
cpp.windowsApiAdditionalPartitions: mkspecPath.startsWith("winrt-") ? ["phone"] : undefined
cpp.requireAppContainer: mkspecName.startsWith("winrt-")
- additionalProductTypes: ["qm"]
+ additionalProductTypes: ["qm", "qt.core.metatypes"]
validate: {
var validator = new ModUtils.PropertyValidator("Qt.core");
@@ -279,24 +304,29 @@ Module {
property bool combineMocOutput: cpp.combineCxxSources
property bool enableBigResources: false
+ // Product should not moc in the aggregate when multiplexing.
+ property bool enableMoc: !(product.multiplexed || product.aggregate)
+ || product.multiplexConfigurationId
Rule {
name: "QtCoreMocRuleCpp"
+ condition: enableMoc
property string cppInput: cpp.combineCxxSources ? "cpp.combine" : "cpp"
property string objcppInput: cpp.combineObjcxxSources ? "objcpp.combine" : "objcpp"
inputs: [objcppInput, cppInput]
auxiliaryInputs: "qt_plugin_metadata"
excludedInputs: "unmocable"
- outputFileTags: ["hpp", "unmocable"]
+ outputFileTags: ["hpp", "unmocable", "qt.core.metatypes.in"]
outputArtifacts: Moc.outputArtifacts.apply(Moc, arguments)
prepare: Moc.commands.apply(Moc, arguments)
}
Rule {
name: "QtCoreMocRuleHpp"
+ condition: enableMoc
inputs: "hpp"
auxiliaryInputs: ["qt_plugin_metadata", "cpp", "objcpp"];
excludedInputs: "unmocable"
- outputFileTags: ["hpp", "cpp", "moc_cpp", "unmocable"]
+ outputFileTags: ["hpp", "cpp", "moc_cpp", "unmocable", "qt.core.metatypes.in"]
outputArtifacts: Moc.outputArtifacts.apply(Moc, arguments)
prepare: Moc.commands.apply(Moc, arguments)
}
@@ -319,6 +349,27 @@ Module {
}
}
+ Rule {
+ multiplex: true
+ inputs: "qt.core.metatypes.in"
+ Artifact {
+ filePath: product.targetName.toLowerCase() + "_metatypes.json"
+ fileTags: "qt.core.metatypes"
+ qbs.install: product.Qt.core.metaTypesInstallDir
+ qbs.installDir: product.Qt.core.metaTypesInstallDir
+ }
+ prepare: {
+ var inputFilePaths = inputs["qt.core.metatypes.in"].map(function(a) {
+ return a.filePath;
+ });
+ var cmd = new Command(Moc.fullPath(product),
+ ["--collect-json", "-o", output.filePath].concat(inputFilePaths));
+ cmd.description = "generating " + output.fileName;
+ cmd.highlight = "codegen";
+ return cmd;
+ }
+ }
+
property path resourceSourceBase
property string resourcePrefix: "/"
property string resourceFileBaseName: product.targetName
@@ -393,7 +444,7 @@ Module {
"-o", output.filePath];
if (input.Qt.core.enableBigResources)
args.push("-pass", "1");
- var cmd = new Command(product.Qt.core.binPath + '/rcc', args);
+ var cmd = new Command(Rcc.fullPath(product), args);
cmd.description = "rcc "
+ (input.Qt.core.enableBigResources ? "(pass 1) " : "")
+ input.fileName;
@@ -405,7 +456,7 @@ Module {
Rule {
inputs: ["intermediate_obj"]
Artifact {
- filePath: input.completeBaseName + ".2.o"
+ filePath: input.completeBaseName + ".2" + input.cpp.objectSuffix
fileTags: ["obj"]
}
prepare: {
@@ -424,7 +475,7 @@ Module {
}
var qrcArtifact = findChild(input, function(c) { return c.fileTags.contains("qrc"); });
var cppArtifact = findChild(input, function(c) { return c.fileTags.contains("cpp"); });
- var cmd = new Command(product.Qt.core.binPath + '/rcc',
+ var cmd = new Command(Rcc.fullPath(product),
[qrcArtifact.filePath,
"-temp", input.filePath,
"-name", FileInfo.completeBaseName(input.filePath),
@@ -457,7 +508,7 @@ Module {
var args = ['-silent', '-qm', output.filePath].concat(inputFilePaths);
var cmd = new Command(product.Qt.core.binPath + '/'
+ product.Qt.core.lreleaseName, args);
- cmd.description = 'Creating ' + output.fileName;
+ cmd.description = 'creating ' + output.fileName;
cmd.highlight = 'filegen';
return cmd;
}
@@ -497,7 +548,8 @@ Module {
args = args.concat(product.Qt.core.helpGeneratorArgs);
args.push("-o");
args.push(output.filePath);
- var cmd = new Command(product.Qt.core.binPath + "/qhelpgenerator", args);
+ var cmd = new Command(
+ product.Qt.core.helpGeneratorLibExecPath + "/qhelpgenerator", args);
cmd.description = 'qhelpgenerator ' + input.fileName;
cmd.highlight = 'filegen';
cmd.stdoutFilterFunction = function(output) {
diff --git a/share/qbs/module-providers/Qt/templates/dbus.js b/share/qbs/module-providers/Qt/templates/dbus.js
index 0674bf684..3cdd6fb90 100644
--- a/share/qbs/module-providers/Qt/templates/dbus.js
+++ b/share/qbs/module-providers/Qt/templates/dbus.js
@@ -29,6 +29,7 @@
****************************************************************************/
var FileInfo = require("qbs.FileInfo");
+var ModUtils = require("qbs.ModUtils");
function outputFileName(input, suffix)
{
diff --git a/share/qbs/module-providers/Qt/templates/dbus.qbs b/share/qbs/module-providers/Qt/templates/dbus.qbs
index 6556e2c9b..bbda5a4f2 100644
--- a/share/qbs/module-providers/Qt/templates/dbus.qbs
+++ b/share/qbs/module-providers/Qt/templates/dbus.qbs
@@ -1,6 +1,5 @@
import qbs.FileInfo
import qbs.ModUtils
-import "../QtModule.qbs" as QtModule
import "dbus.js" as DBus
QtModule {
@@ -23,9 +22,7 @@ QtModule {
fileTags: ["cpp"]
}
- prepare: {
- return DBus.createCommands(product, input, outputs, "-a");
- }
+ prepare: DBus.createCommands(product, input, outputs, "-a")
}
Rule {
@@ -41,9 +38,7 @@ QtModule {
fileTags: ["cpp"]
}
- prepare: {
- return DBus.createCommands(product, input, outputs, "-p");
- }
+ prepare: DBus.createCommands(product, input, outputs, "-p")
}
architectures: @archs@
@@ -66,7 +61,7 @@ QtModule {
moduleConfig: @moduleConfig@
cpp.defines: @defines@
- cpp.includePaths: @includes@
+ cpp.systemIncludePaths: @includes@
cpp.libraryPaths: @libraryPaths@
@additionalContent@
diff --git a/share/qbs/module-providers/Qt/templates/gui.qbs b/share/qbs/module-providers/Qt/templates/gui.qbs
index a3c427175..db491eafe 100644
--- a/share/qbs/module-providers/Qt/templates/gui.qbs
+++ b/share/qbs/module-providers/Qt/templates/gui.qbs
@@ -1,7 +1,6 @@
import qbs.FileInfo
import qbs.ModUtils
import qbs.Utilities
-import '../QtModule.qbs' as QtModule
QtModule {
qtModuleName: "Gui"
@@ -23,10 +22,12 @@ QtModule {
}
prepare: {
- var cmd = new Command(ModUtils.moduleProperty(product, "binPath") + '/'
- + ModUtils.moduleProperty(product, "uicName"),
- [input.filePath, '-o', output.filePath])
- cmd.description = 'uic ' + input.fileName;
+ var uicPath = Utilities.versionCompare(product.Qt.gui.version, "6.1") < 0
+ ? product.Qt.core.binPath + '/' + product.Qt.gui.uicName
+ : product.Qt.core.libExecPath + '/' + product.Qt.gui.uicName;
+
+ var cmd = new Command(uicPath, [input.filePath, '-o', output.filePath]);
+ cmd.description = 'generating ' + output.fileName;
cmd.highlight = 'codegen';
return cmd;
}
@@ -57,7 +58,7 @@ QtModule {
: undefined
cpp.defines: @defines@
- cpp.includePaths: @includes@
+ cpp.systemIncludePaths: @includes@
cpp.libraryPaths: @libraryPaths@
Properties {
diff --git a/share/qbs/module-providers/Qt/templates/moc.js b/share/qbs/module-providers/Qt/templates/moc.js
index aa67766b9..7d230267c 100644
--- a/share/qbs/module-providers/Qt/templates/moc.js
+++ b/share/qbs/module-providers/Qt/templates/moc.js
@@ -29,13 +29,14 @@
****************************************************************************/
var ModUtils = require("qbs.ModUtils");
+var Utilities = require("qbs.Utilities");
-function args(product, input, outputFileName)
+function args(product, input, outputs)
{
var defines = product.cpp.compilerDefinesByLanguage;
if (input.fileTags.contains("objcpp"))
defines = ModUtils.flattenDictionary(defines["objcpp"]) || [];
- else if (input.fileTags.contains("cpp"))
+ else if (input.fileTags.contains("cpp") || input.fileTags.contains("hpp"))
defines = ModUtils.flattenDictionary(defines["cpp"]) || [];
else
defines = [];
@@ -56,6 +57,15 @@ function args(product, input, outputFileName)
}
var pluginMetaData = product.Qt.core.pluginMetaData;
var args = [];
+ if (product.Qt.core._generateMetaTypesFile)
+ args.push("--output-json");
+ var outputFileName;
+ for (tag in outputs) {
+ if (tag !== "qt.core.metatypes.in") {
+ outputFileName = outputs[tag][0].filePath;
+ break;
+ }
+ }
args = args.concat(
defines.map(function(item) { return '-D' + item; }),
includePaths.map(function(item) { return '-I' + item; }),
@@ -69,7 +79,9 @@ function args(product, input, outputFileName)
function fullPath(product)
{
- return product.Qt.core.binPath + '/' + product.Qt.core.mocName;
+ if (Utilities.versionCompare(product.Qt.core.version, "6.1") < 0)
+ return product.Qt.core.binPath + '/' + product.Qt.core.mocName;
+ return product.Qt.core.libExecPath + '/' + product.Qt.core.mocName;
}
function outputArtifacts(project, product, inputs, input)
@@ -90,12 +102,15 @@ function outputArtifacts(project, product, inputs, input)
+ input.completeBaseName + ".moc";
artifact.fileTags.push("hpp");
}
- return [artifact];
+ var artifacts = [artifact];
+ if (product.Qt.core._generateMetaTypesFile)
+ artifacts.push({filePath: artifact.filePath + ".json", fileTags: "qt.core.metatypes.in"});
+ return artifacts;
}
function commands(project, product, inputs, outputs, input, output)
{
- var cmd = new Command(fullPath(product), args(product, input, output.filePath));
+ var cmd = new Command(fullPath(product), args(product, input, outputs));
cmd.description = 'moc ' + input.fileName;
cmd.highlight = 'codegen';
return cmd;
diff --git a/share/qbs/module-providers/Qt/templates/module.qbs b/share/qbs/module-providers/Qt/templates/module.qbs
index b09f79a87..9f0313ab5 100644
--- a/share/qbs/module-providers/Qt/templates/module.qbs
+++ b/share/qbs/module-providers/Qt/templates/module.qbs
@@ -1,5 +1,3 @@
-import '../QtModule.qbs' as QtModule
-
QtModule {
qtModuleName: @name@
Depends { name: "Qt"; submodules: @dependencies@}
@@ -24,7 +22,7 @@ QtModule {
pluginTypes: @pluginTypes@
moduleConfig: @moduleConfig@
cpp.defines: @defines@
- cpp.includePaths: @includes@
+ cpp.systemIncludePaths: @includes@
cpp.libraryPaths: @libraryPaths@
@additionalContent@
}
diff --git a/share/qbs/module-providers/Qt/templates/plugin.qbs b/share/qbs/module-providers/Qt/templates/plugin.qbs
index e73e2a4d9..34d4f4153 100644
--- a/share/qbs/module-providers/Qt/templates/plugin.qbs
+++ b/share/qbs/module-providers/Qt/templates/plugin.qbs
@@ -1,5 +1,3 @@
-import '../QtPlugin.qbs' as QtPlugin
-
QtPlugin {
qtModuleName: @name@
Depends { name: "Qt"; submodules: @dependencies@}
diff --git a/share/qbs/module-providers/Qt/templates/plugin_support.qbs b/share/qbs/module-providers/Qt/templates/plugin_support.qbs
index 1de923f17..19a739589 100644
--- a/share/qbs/module-providers/Qt/templates/plugin_support.qbs
+++ b/share/qbs/module-providers/Qt/templates/plugin_support.qbs
@@ -32,7 +32,7 @@ Module {
newValue = [];
else if (typeof newValue == "string")
newValue = [newValue];
- if (!Array.isArray(newValue))
+ if (!newValue instanceof Array)
throw "Invalid value '" + newValue + "' in Qt.plugin_support.pluginsByType";
eppt[pluginType] = (eppt[pluginType] || []).uniqueConcat(newValue);
}
diff --git a/share/qbs/module-providers/Qt/templates/qml.js b/share/qbs/module-providers/Qt/templates/qml.js
index df69034fe..38462dcf7 100644
--- a/share/qbs/module-providers/Qt/templates/qml.js
+++ b/share/qbs/module-providers/Qt/templates/qml.js
@@ -3,14 +3,31 @@ var FileInfo = require("qbs.FileInfo");
var Process = require("qbs.Process");
var TextFile = require("qbs.TextFile");
-function scannerData(scannerFilePath, qmlFiles, qmlPath)
+function scannerData(scannerFilePath, qmlFiles, qmlPath, hostOS)
{
var p;
try {
p = new Process();
- p.exec(scannerFilePath, ["-qmlFiles"].concat(qmlFiles).concat(["-importPath", qmlPath]),
- true);
- return JSON.parse(p.readStdOut());
+ if (!hostOS.contains("windows")) {
+ p.exec(scannerFilePath, ["-qmlFiles"].concat(qmlFiles).concat(["-importPath", qmlPath]),
+ true);
+ return JSON.parse(p.readStdOut());
+ }
+ var data = [];
+ var nextFileIndex = 0;
+ while (nextFileIndex < qmlFiles.length) {
+ var currentFileList = [];
+ var currentFileListStringLength = 0;
+ while (nextFileIndex < qmlFiles.length && currentFileListStringLength < 30000) {
+ var currentFile = qmlFiles[nextFileIndex++];
+ currentFileList.push(currentFile);
+ currentFileListStringLength += currentFile.length;
+ }
+ p.exec(scannerFilePath, ["-qmlFiles"].concat(currentFileList)
+ .concat(["-importPath", qmlPath]), true);
+ data = data.concat(JSON.parse(p.readStdOut()));
+ }
+ return data;
} finally {
if (p)
p.close();
@@ -22,16 +39,32 @@ function getPrlRhs(line)
return line.split('=')[1].trim();
}
-function getLibsForPlugin(pluginData, buildVariant, targetOS, toolchain, qtLibDir)
+function getLibsForPlugin(pluginData, product)
{
+ var targetOS = product.qbs.targetOS;
+ var toolchain = product.qbs.toolchain;
+ var buildVariant = product.Qt.core.qtBuildVariant;
+ var qtLibDir = product.Qt.core.libPath;
+ var qtPluginDir = product.Qt.core.pluginPath;
+ var qtDir = product.Qt.core.installPrefixPath;
+ var qtQmlPath = product.Qt.qml.qmlPath;
+
if (!pluginData.path)
return "";
var prlFileName = "";
if (!targetOS.contains("windows"))
prlFileName += "lib";
prlFileName += pluginData.plugin;
- if (buildVariant === "debug" && targetOS.contains("windows"))
- prlFileName += "d";
+ if (buildVariant === "debug") {
+ if (targetOS.contains("windows")) {
+ prlFileName += "d";
+ } else if (product.Qt.core.versionMajor >= 6 &&
+ (targetOS.contains("ios")
+ || targetOS.contains("tvos")
+ || targetOS.contains("watchos"))) {
+ prlFileName += "_debug";
+ }
+ }
prlFileName += ".prl";
var prlFilePath = FileInfo.joinPaths(pluginData.path, prlFileName);
if (!File.exists(prlFilePath)) {
@@ -42,7 +75,7 @@ function getLibsForPlugin(pluginData, buildVariant, targetOS, toolchain, qtLibDi
var prlFile = new TextFile(prlFilePath, TextFile.ReadOnly);
try {
var pluginLib;
- var otherLibs = "";
+ var otherLibs = [];
var line;
while (!prlFile.atEof()) {
line = prlFile.readLine().trim();
@@ -57,12 +90,15 @@ function getLibsForPlugin(pluginData, buildVariant, targetOS, toolchain, qtLibDi
otherLibsLine = otherLibsLine.replace(/-l([^ ]+)/g, "$1" + ".lib");
}
otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_LIBS\]/g, qtLibDir);
- otherLibs += otherLibsLine + '\n';
+ otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_PLUGINS\]/g, qtPluginDir);
+ otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_PREFIX\]/g, qtDir);
+ otherLibsLine = otherLibsLine.replace(/\$\$\[QT_INSTALL_QML\]/g, qtQmlPath);
+ otherLibs = otherLibs.concat(otherLibsLine.split(' ').map(FileInfo.cleanPath));
}
}
if (!pluginLib)
throw "Malformed prl file '" + prlFilePath + "'.";
- return pluginLib + ' ' + otherLibs;
+ return [pluginLib].concat(otherLibs);
} finally {
prlFile.close();
}
diff --git a/share/qbs/module-providers/Qt/templates/qml.qbs b/share/qbs/module-providers/Qt/templates/qml.qbs
index c95c0f367..0a938e58d 100644
--- a/share/qbs/module-providers/Qt/templates/qml.qbs
+++ b/share/qbs/module-providers/Qt/templates/qml.qbs
@@ -1,13 +1,14 @@
+import qbs.FileInfo
+import qbs.Host
import qbs.TextFile
-import '../QtModule.qbs' as QtModule
import "qml.js" as Qml
QtModule {
qtModuleName: "Qml"
Depends { name: "Qt"; submodules: @dependencies@}
- property string qmlImportScannerName: "qmlimportscanner"
- property string qmlImportScannerFilePath: Qt.core.binPath + '/' + qmlImportScannerName
+ property string qmlImportScannerName: Qt.core.qmlImportScannerName
+ property string qmlImportScannerFilePath: Qt.core.qmlImportScannerFilePath
property string qmlPath: @qmlPath@
property bool generateCacheFiles: false
@@ -46,7 +47,7 @@ QtModule {
pluginTypes: @pluginTypes@
moduleConfig: @moduleConfig@
cpp.defines: @defines@
- cpp.includePaths: @includes@
+ cpp.systemIncludePaths: @includes@
cpp.libraryPaths: @libraryPaths@
@additionalContent@
@@ -60,6 +61,60 @@ QtModule {
fileTags: ["qt.qml.js"]
}
+ // Type registration
+ property string importName
+ property string importVersion: product.version
+ readonly property stringList _importVersionParts: (importVersion || "").split(".")
+ property string typesFileName: {
+ if (product.type && product.type.contains("application"))
+ return product.targetName + ".qmltypes";
+ return "plugins.qmltypes";
+ }
+ property string typesInstallDir
+ property stringList extraMetaTypesFiles
+ Properties {
+ condition: importName
+ Qt.core.generateMetaTypesFile: true
+ }
+ Qt.core.generateMetaTypesFile: original
+ Rule {
+ condition: importName
+ inputs: "qt.core.metatypes"
+ multiplex: true
+ explicitlyDependsOnFromDependencies: "qt.core.metatypes"
+ Artifact {
+ filePath: product.targetName.toLowerCase() + "_qmltyperegistrations.cpp"
+ fileTags: ["cpp", "unmocable"]
+ }
+ Artifact {
+ filePath: product.Qt.qml.typesFileName
+ fileTags: "qt.qml.types"
+ qbs.install: product.Qt.qml.typesInstallDir
+ qbs.installDir: product.Qt.qml.typesInstallDir
+ }
+ prepare: {
+ var versionParts = product.Qt.qml._importVersionParts;
+ var args = [
+ "--generate-qmltypes=" + outputs["qt.qml.types"][0].filePath,
+ "--import-name=" + product.Qt.qml.importName,
+ "--major-version=" + versionParts[0],
+ "--minor-version=" + (versionParts.length > 1 ? versionParts[1] : "0")
+ ];
+ var foreignTypes = product.Qt.qml.extraMetaTypesFiles || [];
+ var metaTypeArtifactsFromDeps = explicitlyDependsOn["qt.core.metatypes"] || [];
+ var filePathFromArtifact = function(a) { return a.filePath; };
+ foreignTypes = foreignTypes.concat(metaTypeArtifactsFromDeps.map(filePathFromArtifact));
+ if (foreignTypes.length > 0)
+ args.push("--foreign-types=" + foreignTypes.join(","));
+ args.push("-o", outputs.cpp[0].filePath);
+ args = args.concat(inputs["qt.core.metatypes"].map(filePathFromArtifact));
+ var cmd = new Command(product.Qt.core.qmlLibExecPath + "/qmltyperegistrar", args);
+ cmd.description = "running qmltyperegistrar";
+ cmd.highlight = "codegen";
+ return cmd;
+ }
+ }
+
Rule {
condition: linkPlugins
multiplex: true
@@ -81,7 +136,7 @@ QtModule {
prepare: {
var cmd = new JavaScriptCommand();
if (inputs["qt.qml.qml"])
- cmd.description = "Creating " + outputs["cpp"][0].fileName;
+ cmd.description = "creating " + outputs["cpp"][0].fileName;
else
cmd.silent = true;
cmd.sourceCode = function() {
@@ -90,7 +145,7 @@ QtModule {
qmlInputs = [];
var scannerData = Qml.scannerData(product.Qt.qml.qmlImportScannerFilePath,
qmlInputs.map(function(inp) { return inp.filePath; }),
- product.Qt.qml.qmlPath);
+ product.Qt.qml.qmlPath, Host.os());
var cppFile;
var listFile;
try {
@@ -101,6 +156,7 @@ QtModule {
if (cppFile)
cppFile.writeLine("#include <QtPlugin>");
var plugins = { };
+ var libsWithUniqueObjects = [];
for (var p in scannerData) {
var plugin = scannerData[p].plugin;
if (!plugin || plugins[plugin])
@@ -113,13 +169,17 @@ QtModule {
}
if (cppFile)
cppFile.writeLine("Q_IMPORT_PLUGIN(" + className + ")");
- var libs = Qml.getLibsForPlugin(scannerData[p],
- product.Qt.core.qtBuildVariant,
- product.qbs.targetOS,
- product.qbs.toolchain,
- product.Qt.core.libPath);
- listFile.write(libs + ' ');
+ var libs = Qml.getLibsForPlugin(scannerData[p], product);
+ for (var i = 0; i < libs.length; ++i) {
+ var lib = libs[i];
+ if (!lib.endsWith(product.cpp.objectSuffix)
+ || (!libsWithUniqueObjects.contains(lib)
+ && !product.cpp.staticLibraries.contains(FileInfo.cleanPath(lib)))) {
+ libsWithUniqueObjects.push(lib);
+ }
+ }
}
+ listFile.write(libsWithUniqueObjects.join("\n"));
} finally {
if (cppFile)
cppFile.close();
@@ -130,4 +190,20 @@ QtModule {
return [cmd];
}
}
+
+ validate: {
+ if (importName) {
+ if (!importVersion)
+ throw "Qt.qml.importVersion must be set if Qt.qml.importName is set.";
+ var isInt = function(value) {
+ return !isNaN(value) && parseInt(Number(value)) == value
+ && !isNaN(parseInt(value, 10));
+ }
+ if (!isInt(_importVersionParts[0])
+ || (_importVersionParts.length > 1 && !isInt(_importVersionParts[1]))) {
+ throw "Qt.qml.importVersion must be of the form x.y, where x and y are integers.";
+ }
+
+ }
+ }
}
diff --git a/share/qbs/module-providers/Qt/templates/qmlcache.qbs b/share/qbs/module-providers/Qt/templates/qmlcache.qbs
index 9111eb500..7047884c0 100644
--- a/share/qbs/module-providers/Qt/templates/qmlcache.qbs
+++ b/share/qbs/module-providers/Qt/templates/qmlcache.qbs
@@ -9,8 +9,8 @@ Module {
if (!qmlcachegenProbe.found)
throw "qmlcachegen unsupported for this target";
}
- property string qmlCacheGenPath: FileInfo.joinPaths(Qt.core.binPath, "qmlcachegen")
- + (qbs.hostOS.contains("windows") ? ".exe" : "")
+ property string qmlCacheGenPath: FileInfo.joinPaths(Qt.core.qmlLibExecPath, "qmlcachegen")
+ + FileInfo.executableSuffix()
property bool supportsAllArchitectures: Utilities.versionCompare(Qt.core.version, "5.11") >= 0
property string installDir
diff --git a/share/qbs/module-providers/Qt/templates/quick.js b/share/qbs/module-providers/Qt/templates/quick.js
index 4f3da2fb0..d7e58984f 100644
--- a/share/qbs/module-providers/Qt/templates/quick.js
+++ b/share/qbs/module-providers/Qt/templates/quick.js
@@ -30,13 +30,14 @@
var FileInfo = require("qbs.FileInfo");
var Process = require("qbs.Process");
+var Rcc = require("rcc.js");
-function scanQrc(qrcFilePath) {
+function scanQrc(product, qrcFilePath) {
var absInputDir = FileInfo.path(qrcFilePath);
var result = [];
var process = new Process();
try {
- var rcc = FileInfo.joinPaths(product.Qt.core.binPath, 'rcc' + product.cpp.executableSuffix);
+ var rcc = FileInfo.joinPaths(Rcc.fullPath(product) + FileInfo.executableSuffix());
var exitCode = process.exec(rcc, ["--list", qrcFilePath], true);
for (;;) {
var line = process.readLine();
@@ -65,13 +66,14 @@ function qtQuickResourceFileOutputName(fileName) {
return fileName.replace(/\.qrc$/, "_qtquickcompiler.qrc");
}
-function contentFromQrc(qrcFilePath) {
- var filesInQrc = scanQrc(qrcFilePath);
+function contentFromQrc(product, qrcFilePath) {
+ var supportsFiltering = product.Qt.quick._supportsQmlJsFiltering;
+ var filesInQrc = scanQrc(product, qrcFilePath);
var qmlJsFiles = filesInQrc.filter(function (filePath) {
- return (/\.(js|qml)$/).test(filePath);
+ return (/\.(mjs|js|qml)$/).test(filePath);
} );
var content = {};
- if (filesInQrc.length - qmlJsFiles.length > 0) {
+ if (!supportsFiltering || filesInQrc.length - qmlJsFiles.length > 0) {
content.newQrcFileName = qtQuickResourceFileOutputName(input.fileName);
}
content.qmlJsFiles = qmlJsFiles.map(function (filePath) {
diff --git a/share/qbs/module-providers/Qt/templates/quick.qbs b/share/qbs/module-providers/Qt/templates/quick.qbs
index 5968949c8..5fc4aa349 100644
--- a/share/qbs/module-providers/Qt/templates/quick.qbs
+++ b/share/qbs/module-providers/Qt/templates/quick.qbs
@@ -33,12 +33,11 @@ import qbs.FileInfo
import qbs.Process
import qbs.TextFile
import qbs.Utilities
-import '../QtModule.qbs' as QtModule
import 'quick.js' as QC
QtModule {
qtModuleName: @name@
- Depends { name: "Qt"; submodules: @dependencies@.concat("qml-private") }
+ Depends { name: "Qt"; submodules: @dependencies@ }
hasLibrary: @has_library@
architectures: @archs@
@@ -60,18 +59,23 @@ QtModule {
pluginTypes: @pluginTypes@
moduleConfig: @moduleConfig@
cpp.defines: @defines@
- cpp.includePaths: @includes@
+ cpp.systemIncludePaths: @includes@
cpp.libraryPaths: @libraryPaths@
@additionalContent@
readonly property bool _compilerIsQmlCacheGen: Utilities.versionCompare(Qt.core.version,
"5.11") >= 0
+ readonly property bool _supportsQmlJsFiltering: Utilities.versionCompare(Qt.core.version,
+ "5.15") < 0
readonly property string _generatedLoaderFileName: _compilerIsQmlCacheGen
? "qmlcache_loader.cpp"
: "qtquickcompiler_loader.cpp"
+ property string _compilerBaseDir: _compilerIsQmlCacheGen ? Qt.core.qmlLibExecPath
+ : Qt.core.binPath
property string compilerBaseName: (_compilerIsQmlCacheGen ? "qmlcachegen" : "qtquickcompiler")
- property string compilerFilePath: FileInfo.joinPaths(Qt.core.binPath,
- compilerBaseName + product.cpp.executableSuffix)
+ property string compilerFilePath: FileInfo.joinPaths(_compilerBaseDir,
+ compilerBaseName + FileInfo.executableSuffix())
+
property bool compilerAvailable: File.exists(compilerFilePath);
property bool useCompiler: compilerAvailable && !_compilerIsQmlCacheGen
@@ -79,7 +83,7 @@ QtModule {
condition: useCompiler
inputs: 'qt.quick.qrc'
searchPaths: [FileInfo.path(input.filePath)]
- scan: QC.scanQrc(input.filePath)
+ scan: QC.scanQrc(product, input.filePath)
}
FileTagger {
@@ -100,7 +104,7 @@ QtModule {
var cmd = new JavaScriptCommand();
cmd.silent = true;
cmd.sourceCode = function() {
- var content = QC.contentFromQrc(input.filePath);
+ var content = QC.contentFromQrc(product, input.filePath);
content.qrcFilePath = input.filePath;
var tf = new TextFile(output.filePath, TextFile.WriteOnly);
@@ -174,7 +178,9 @@ QtModule {
if (info.newQrcFileName) {
loaderFlags.push("--resource-file-mapping="
+ FileInfo.fileName(info.qrcFilePath)
- + ":" + info.newQrcFileName);
+ + '=' + info.newQrcFileName);
+ // Qt 5.15 doesn't really filter anyting but merely copies the qrc
+ // content to the new location
var args = ["--filter-resource-file",
info.qrcFilePath];
if (useCacheGen)
diff --git a/share/qbs/module-providers/Qt/templates/rcc.js b/share/qbs/module-providers/Qt/templates/rcc.js
new file mode 100644
index 000000000..89c57d99b
--- /dev/null
+++ b/share/qbs/module-providers/Qt/templates/rcc.js
@@ -0,0 +1,8 @@
+var Utilities = require("qbs.Utilities");
+
+function fullPath(product)
+{
+ if (Utilities.versionCompare(product.Qt.core.version, "6.1") < 0)
+ return product.Qt.core.binPath + '/' + product.Qt.core.rccName;
+ return product.Qt.core.libExecPath + '/' + product.Qt.core.rccName;
+}
diff --git a/share/qbs/module-providers/Qt/templates/scxml.qbs b/share/qbs/module-providers/Qt/templates/scxml.qbs
index 7125ec53c..757041b52 100644
--- a/share/qbs/module-providers/Qt/templates/scxml.qbs
+++ b/share/qbs/module-providers/Qt/templates/scxml.qbs
@@ -1,10 +1,11 @@
import qbs.FileInfo
import qbs.Utilities
-import "../QtModule.qbs" as QtModule
QtModule {
qtModuleName: "Scxml"
+ property string _qscxmlcDir: Utilities.versionCompare(Qt.core.version, "6.3") >= 0
+ ? Qt.core.libExecPath : Qt.core.binPath
property string qscxmlcName: "qscxmlc"
property string className
property string namespace
@@ -26,8 +27,7 @@ QtModule {
prepare: {
var compilerName = product.moduleProperty("Qt.scxml", "qscxmlcName");
- var compilerPath = FileInfo.joinPaths(input.moduleProperty("Qt.core", "binPath"),
- compilerName);
+ var compilerPath = FileInfo.joinPaths(input.Qt.scxml._qscxmlcDir, compilerName);
var args = ["--header", outputs["hpp"][0].filePath,
"--impl", outputs["cpp"][0].filePath];
var cxx98 = input.moduleProperty("cpp", "cxxLanguageVersion") === "c++98";
@@ -73,7 +73,7 @@ QtModule {
moduleConfig: @moduleConfig@
cpp.defines: @defines@
- cpp.includePaths: @includes@
+ cpp.systemIncludePaths: @includes@
cpp.libraryPaths: @libraryPaths@
@additionalContent@
diff --git a/share/qbs/module-providers/__fallback/fallback.qbs b/share/qbs/module-providers/__fallback/fallback.qbs
index 632d1aa7a..349af3a52 100644
--- a/share/qbs/module-providers/__fallback/fallback.qbs
+++ b/share/qbs/module-providers/__fallback/fallback.qbs
@@ -37,7 +37,6 @@
**
****************************************************************************/
-import qbs
import qbs.FileInfo
import qbs.Probes
@@ -47,6 +46,8 @@ Module {
property string theName: FileInfo.completeBaseName(filePath)
+ readonly property bool __fallback: true // Hack, please look away
+
Probes.PkgConfigProbe {
id: pkgConfigProbe
condition: pkgconfig.present
diff --git a/share/qbs/module-providers/qbspkgconfig.qbs b/share/qbs/module-providers/qbspkgconfig.qbs
new file mode 100644
index 000000000..885a6da7a
--- /dev/null
+++ b/share/qbs/module-providers/qbspkgconfig.qbs
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 qbs.Environment
+import qbs.File
+import qbs.FileInfo
+import qbs.Host
+import qbs.ModUtils
+import qbs.PkgConfig
+import qbs.ProviderUtils
+import qbs.Probes
+import qbs.Process
+import qbs.TextFile
+
+import "Qt/setup-qt.js" as SetupQt
+
+ModuleProvider {
+ property stringList executableNames: ["pkgconf", "pkg-config"]
+ property string executableFilePath: pkgConfigProbe.filePath
+ property stringList extraPaths
+ property stringList libDirs
+ property bool staticMode: false
+ property bool definePrefix: Host.os().includes("windows")
+
+ // We take the sysroot default from qbs.sysroot, except for Xcode toolchains, where
+ // the sysroot points into the Xcode installation and does not contain .pc files.
+ property path sysroot: qbs.toolchain && qbs.toolchain.includes("xcode")
+ ? undefined : qbs.sysroot
+
+ Probes.BinaryProbe {
+ id: pkgConfigProbe
+ condition: !executableFilePath
+ names: executableNames
+ }
+
+ Probes.QbsPkgConfigProbe {
+ id: theProbe
+ _executableFilePath: executableFilePath
+ _extraPaths: extraPaths
+ _sysroot: sysroot
+ _libDirs: libDirs
+ _staticMode: staticMode
+ _definePrefix: definePrefix
+ }
+
+ Probes.QmakeProbe {
+ id: qmakeProbe
+ condition: moduleName.startsWith("Qt") && theProbe.qmakePaths
+ qmakePaths: theProbe.qmakePaths
+ }
+
+ isEager: false
+
+ relativeSearchPaths: {
+ function getModuleInfo(pkg, staticMode) {
+ var result = {};
+
+ var mapper = function(flag) { return flag.value; }
+ var typeFilter = function(type) {
+ return function(flag) { return flag.type === type; }
+ }
+
+ function getLibsInfo(libs) {
+ var result = {};
+ result.dynamicLibraries = libs.filter(typeFilter(PkgConfig.LibraryName)).map(mapper);
+ result.staticLibraries =
+ libs.filter(typeFilter(PkgConfig.StaticLibraryName)).map(mapper);
+ result.libraryPaths = libs.filter(typeFilter(PkgConfig.LibraryPath)).map(mapper);
+ result.frameworks = libs.filter(typeFilter(PkgConfig.Framework)).map(mapper);
+ result.frameworkPaths =
+ libs.filter(typeFilter(PkgConfig.FrameworkPath)).map(mapper);
+ result.driverLinkerFlags =
+ libs.filter(typeFilter(PkgConfig.LinkerFlag)).map(mapper);
+ return result;
+ }
+
+ result.version = pkg.version;
+ result.includePaths = pkg.cflags.filter(typeFilter(PkgConfig.IncludePath)).map(mapper);
+ result.systemIncludePaths =
+ pkg.cflags.filter(typeFilter(PkgConfig.SystemIncludePath)).map(mapper);
+ result.defines = pkg.cflags.filter(typeFilter(PkgConfig.Define)).map(mapper);
+ result.commonCompilerFlags =
+ pkg.cflags.filter(typeFilter(PkgConfig.CompilerFlag)).map(mapper);
+
+ var allLibs = pkg.libs;
+ if (staticMode)
+ allLibs = allLibs.concat(pkg.libsPrivate);
+ var libsInfo = getLibsInfo(allLibs);
+ for (var key in libsInfo) {
+ result[key] = libsInfo[key];
+ }
+
+ return result;
+ }
+
+ function getModuleDependencies(pkg, staticMode) {
+ var mapper = function(p) {
+ var result = {};
+ for (var key in p)
+ result[key] = p[key];
+ result.name = ProviderUtils.pkgConfigToModuleName(result.name);
+ return result;
+ }
+ var result = pkg.requires.map(mapper);
+ if (staticMode)
+ result = result.concat(pkg.requiresPrivate.map(mapper));
+ return result;
+ }
+
+ console.debug("Running pkgconfig provider for " + moduleName + ".");
+
+ var outputDir = FileInfo.joinPaths(outputBaseDir, "modules");
+ File.makePath(outputDir);
+
+ var moduleMapping = {
+ "protobuf": "protobuflib"
+ }
+ var reverseMapping = {}
+ for (var key in moduleMapping)
+ reverseMapping[moduleMapping[key]] = key
+
+ if (moduleName.startsWith("Qt")) {
+ function setupQt(packageName, qtInfos) {
+ if (qtInfos === undefined || qtInfos.length === 0)
+ return [];
+ var qtProviderDir = FileInfo.joinPaths(path, "Qt");
+ return SetupQt.doSetup(packageName, qtInfos, outputBaseDir, qtProviderDir);
+ }
+
+ if (!sysroot) {
+ return setupQt(moduleName, qmakeProbe.qtInfos);
+ }
+ return [];
+ }
+
+ var pkg;
+ pkg = theProbe.packages[reverseMapping[moduleName]];
+ if (pkg === undefined)
+ pkg = theProbe.packagesByModuleName[moduleName];
+ if (pkg === undefined)
+ return [];
+
+ if (pkg.isBroken) {
+ console.warn("Failed to load " + moduleName + " as it's pkg-config package is broken");
+ return [];
+ }
+ var moduleInfo = getModuleInfo(pkg, staticMode);
+ var deps = getModuleDependencies(pkg, staticMode);
+
+ var moduleDir = FileInfo.joinPaths(outputDir, moduleName);
+ File.makePath(moduleDir);
+ var module =
+ new TextFile(FileInfo.joinPaths(moduleDir, "module.qbs"), TextFile.WriteOnly);
+ module.writeLine("Module {");
+ module.writeLine(" version: " + ModUtils.toJSLiteral(moduleInfo.version));
+ module.writeLine(" Depends { name: 'cpp' }");
+ deps.forEach(function(dep) {
+ var depName = ProviderUtils.pkgConfigToModuleName(
+ moduleMapping[dep.name] ? moduleMapping[dep.name] : dep.name);
+ module.write(" Depends { name: '" + depName + "'");
+ for (var k in dep) {
+ if (k === "name")
+ continue;
+ module.write("; " + k + ": " + ModUtils.toJSLiteral(dep[k]));
+ }
+ module.writeLine(" }");
+ })
+ function writeProperty(propertyName) {
+ var value = moduleInfo[propertyName];
+ if (value.length !== 0) { // skip empty props for simplicity of the module file
+ module.writeLine(
+ " cpp." + propertyName + ":" + ModUtils.toJSLiteral(value));
+ }
+ }
+ writeProperty("includePaths");
+ writeProperty("systemIncludePaths");
+ writeProperty("defines");
+ writeProperty("commonCompilerFlags");
+ writeProperty("dynamicLibraries");
+ writeProperty("staticLibraries");
+ writeProperty("libraryPaths");
+ writeProperty("frameworks");
+ writeProperty("frameworkPaths");
+ writeProperty("driverLinkerFlags");
+ module.writeLine("}");
+ module.close();
+
+ return "";
+ }
+}
diff --git a/share/qbs/modules/Android/ndk/ndk.qbs b/share/qbs/modules/Android/ndk/ndk.qbs
index 3b104558c..3b223e4fb 100644
--- a/share/qbs/modules/Android/ndk/ndk.qbs
+++ b/share/qbs/modules/Android/ndk/ndk.qbs
@@ -48,8 +48,22 @@ Module {
PropertyOptions {
name: "abi"
description: "Supported Android ABIs"
- allowedValues: ["arm64-v8a", "armeabi", "armeabi-v7a", "x86", "x86_64"]
+ allowedValues: ["arm64-v8a", "armeabi-v7a", "x86", "x86_64"]
}
+ // From https://android.googlesource.com/platform/ndk/+/refs/heads/ndk-release-r21/docs/BuildSystemMaintainers.md
+ // Android Studio‘s LLDB debugger uses a binary’s build ID to locate debug information. To
+ // ensure that LLDB works with a binary, pass an option like -Wl,--build-id=sha1 to Clang when
+ // linking. Other --build-id= modes are OK, but avoid a plain --build-id argument when using
+ // LLD, because Android Studio‘s version of LLDB doesn’t recognize LLD's default 8-byte build
+ // ID. See Issue 885.
+
+ // Plain --build-id option is nevertheless implemented when buildId property is empty.
+
+ // Possible values (from man page of ld.lld — ELF linker from the LLVM project):
+ // one of fast, md5, sha1, tree, uuid, 0xhex-string, and none. tree is an alias for sha1.
+ // Build-IDs of type fast, md5, sha1, and tree are calculated from the object contents. fast is
+ // not intended to be cryptographically secure.
+ property string buildId: "sha1"
// See https://developer.android.com/ndk/guides/cpp-support.html
property string appStl: "c++_shared"
@@ -65,7 +79,6 @@ Module {
property string ndkSamplesDir: ndkProbe.samplesDir
property string platform: {
switch (abi) {
- case "armeabi":
case "armeabi-v7a":
// case "x86": // x86 has broken wstring support
return "android-19";
@@ -84,7 +97,7 @@ Module {
}
property stringList abis: {
- var list = ["armeabi", "armeabi-v7a"];
+ var list = ["armeabi-v7a"];
if (platformVersion >= 16)
list.push("x86");
if (platformVersion >= 21)
diff --git a/share/qbs/modules/Android/ndk/utils.js b/share/qbs/modules/Android/ndk/utils.js
index 739c205ae..ab312921e 100644
--- a/share/qbs/modules/Android/ndk/utils.js
+++ b/share/qbs/modules/Android/ndk/utils.js
@@ -77,12 +77,6 @@ function commonCompilerFlags(toolchain, buildVariant, ndk) {
}
}
- 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 (abi === "x86" || abi === "x86_64")
flags.push("-fPIC");
@@ -91,16 +85,22 @@ function commonCompilerFlags(toolchain, buildVariant, ndk) {
if (armMode)
flags.push("-m" + armMode);
- // TODO: Remove when https://github.com/android-ndk/ndk/issues/884 is fixed.
- flags.push("-fno-addrsig");
+ // https://github.com/android-ndk/ndk/issues/884 is fixed in r21
+ if (ndk.version < 21)
+ flags.push("-fno-addrsig");
+
+ // https://github.com/android/ndk/issues/635 is fixed in api 24
+ if (abi === "x86" && ndk.platformVersion < 24)
+ flags.push("-mstackrealign");
return flags;
}
-function commonLinkerFlags(abi) {
- return ["-z", "noexecstack", "-z", "relro", "-z", "now"];
+function commonLinkerFlags(ndk) {
+ var buildId = (ndk.buildId) ? "--build-id=" + ndk.buildId : "--build-id";
+ return ["-z", "noexecstack", "-z", "relro", "-z", "now", buildId, "--gc-sections"];
}
-function stlFilePath(path, ndk, suffix) {
- return path + ndk.appStl.slice(0, ndk.appStl.indexOf('_')) + suffix + "." + ndk.platformVersion;
+function stlFileName(prefix, ndk, suffix) {
+ return prefix + ndk.appStl.slice(0, ndk.appStl.indexOf('_')) + suffix;
}
diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs
index f8a046c3a..440448dcf 100644
--- a/share/qbs/modules/Android/sdk/sdk.qbs
+++ b/share/qbs/modules/Android/sdk/sdk.qbs
@@ -50,6 +50,16 @@ Module {
environmentPaths: (ndkDir ? [ndkDir] : []).concat(base)
}
+ Probes.PathProbe {
+ id: bundletoolProbe
+ platformSearchPaths: [sdkDir]
+ names: ["bundletool-all"]
+ nameSuffixes: ["-0.11.0.jar", "-0.12.0.jar", "-0.13.0.jar", "-0.13.3.jar", "-0.13.4.jar",
+ "-0.14.0.jar", "-0.15.0.jar", "-1.0.0.jar", "-1.1.0.jar", "-1.2.0.jar", "-1.3.0.jar",
+ "-1.4.0.jar", "-1.5.0.jar", "-1.6.0.jar", "-1.6.1.jar", "-1.7.0.jar", "-1.7.1.jar",
+ "-1.8.0.jar"]
+ }
+
property path sdkDir: sdkProbe.path
property path ndkDir: ndkProbe.path
property path ndkSamplesDir: ndkProbe.samplesDir
@@ -59,6 +69,10 @@ Module {
property int buildToolsVersionMinor: buildToolsVersionParts[1]
property int buildToolsVersionPatch: buildToolsVersionParts[2]
property string platform: sdkProbe.platform
+ property string minimumVersion: "21"
+ property string targetVersion: platformVersion.toString()
+
+ property path bundletoolFilePath: bundletoolProbe.filePath
// Product-specific properties and files
property string packageName: {
@@ -72,50 +86,52 @@ Module {
property string apkBaseName: packageName
property bool automaticSources: true
property bool legacyLayout: false
- property string sourceSetDir: legacyLayout
+ property path sourceSetDir: legacyLayout
? product.sourceDirectory
: FileInfo.joinPaths(product.sourceDirectory, "src/main")
- property string resourcesDir: FileInfo.joinPaths(sourceSetDir, "res")
- property string assetsDir: FileInfo.joinPaths(sourceSetDir, "assets")
- property string sourcesDir: FileInfo.joinPaths(sourceSetDir, legacyLayout ? "src" : "java")
+ property path resourcesDir: FileInfo.joinPaths(sourceSetDir, "res")
+ property path assetsDir: FileInfo.joinPaths(sourceSetDir, "assets")
+ property path sourcesDir: FileInfo.joinPaths(sourceSetDir, legacyLayout ? "src" : "java")
property string manifestFile: defaultManifestFile
readonly property string defaultManifestFile: FileInfo.joinPaths(sourceSetDir,
"AndroidManifest.xml")
property bool _enableRules: !product.multiplexConfigurationId && !!packageName
+ property bool _bundledInAssets: true
+
Group {
name: "java sources"
- condition: Android.sdk.automaticSources
- prefix: Android.sdk.sourcesDir + '/'
+ condition: product.Android.sdk.automaticSources
+ prefix: FileInfo.resolvePath(product.sourceDirectory, product.Android.sdk.sourcesDir + '/')
files: "**/*.java"
}
Group {
name: "android resources"
- condition: Android.sdk.automaticSources
+ condition: product.Android.sdk.automaticSources
fileTags: ["android.resources"]
- prefix: Android.sdk.resourcesDir + '/'
+ prefix: FileInfo.resolvePath(product.sourceDirectory, product.Android.sdk.resourcesDir + '/')
files: "**/*"
}
Group {
name: "android assets"
- condition: Android.sdk.automaticSources
+ condition: product.Android.sdk.automaticSources
fileTags: ["android.assets"]
- prefix: Android.sdk.assetsDir + '/'
+ prefix: FileInfo.resolvePath(product.sourceDirectory, product.Android.sdk.assetsDir + '/')
files: "**/*"
}
Group {
name: "manifest"
- condition: Android.sdk.automaticSources
+ condition: product.Android.sdk.automaticSources
fileTags: ["android.manifest"]
- files: Android.sdk.manifestFile
- && Android.sdk.manifestFile !== Android.sdk.defaultManifestFile
- ? [Android.sdk.manifestFile]
- : (File.exists(Android.sdk.defaultManifestFile)
- ? [Android.sdk.defaultManifestFile] : [])
+ files: product.Android.sdk.manifestFile
+ && product.Android.sdk.manifestFile !== product.Android.sdk.defaultManifestFile
+ ? [product.Android.sdk.manifestFile]
+ : (File.exists(product.Android.sdk.defaultManifestFile)
+ ? [product.Android.sdk.defaultManifestFile] : [])
}
@@ -136,10 +152,30 @@ Module {
}
property path buildToolsDir: FileInfo.joinPaths(sdkDir, "build-tools", buildToolsVersion)
- property path aaptFilePath: FileInfo.joinPaths(buildToolsDir, "aapt")
+ property string aaptName: "aapt2"
+ PropertyOptions {
+ name: "aaptName"
+ allowedValues: ["aapt", "aapt2"]
+ }
+ property path aaptFilePath: FileInfo.joinPaths(buildToolsDir, aaptName)
+ readonly property bool _enableAapt2: aaptName === "aapt2"
+ property string packageType: "apk"
+ PropertyOptions {
+ name: "packageType"
+ allowedValues: ["apk", "aab"]
+ }
+ readonly property bool _generateAab: packageType == "aab"
+
property path apksignerFilePath: FileInfo.joinPaths(buildToolsDir, "apksigner")
property path aidlFilePath: FileInfo.joinPaths(buildToolsDir, "aidl")
property path dxFilePath: FileInfo.joinPaths(buildToolsDir, "dx")
+ property path d8FilePath: FileInfo.joinPaths(buildToolsDir, "d8")
+ property string dexCompilerName: "d8"
+ PropertyOptions {
+ name: "dexCompilerName"
+ allowedValues: ["dx", "d8"]
+ }
+ readonly property bool _useD8: dexCompilerName === "d8"
property path zipalignFilePath: FileInfo.joinPaths(buildToolsDir, "zipalign")
property path androidJarFilePath: FileInfo.joinPaths(sdkDir, "platforms", platform,
"android.jar")
@@ -148,21 +184,21 @@ Module {
property path generatedJavaFilesBaseDir: FileInfo.joinPaths(product.buildDirectory, "gen")
property path generatedJavaFilesDir: FileInfo.joinPaths(generatedJavaFilesBaseDir,
(packageName || "").split('.').join('/'))
- property string apkContentsDir: FileInfo.joinPaths(product.buildDirectory, "bin")
- property string debugKeyStorePath: FileInfo.joinPaths(
- Environment.getEnv(qbs.hostOS.contains("windows")
- ? "USERPROFILE" : "HOME"),
- ".android", "debug.keystore")
- property bool useApksigner: buildToolsVersion && Utilities.versionCompare(
- buildToolsVersion, "24.0.3") >= 0
+ property path compiledResourcesDir: FileInfo.joinPaths(product.buildDirectory,
+ "compiled_resources")
+ property string packageContentsDir: FileInfo.joinPaths(product.buildDirectory, packageType)
property stringList aidlSearchPaths
Depends { name: "java"; condition: _enableRules }
+ Depends { name: "codesign"; condition: _enableRules }
Properties {
condition: _enableRules
java.languageVersion: platformJavaVersion
java.runtimeVersion: platformJavaVersion
java.bootClassPaths: androidJarFilePath
+ codesign.apksignerFilePath: apksignerFilePath
+ codesign._packageName: apkBaseName + (_generateAab ? ".aab" : ".apk")
+ codesign.useApksigner: !_generateAab
}
validate: {
@@ -174,6 +210,19 @@ Module {
+ "ANDROID_HOME environment variable to a valid "
+ "Android SDK location.");
}
+ if (!bundletoolFilePath && _generateAab) {
+ throw ModUtils.ModuleError("Could not find Android bundletool at the following "
+ + "location:\n\t" + Android.sdk.sdkDir
+ + "\nInstall the Android bundletool to the above location, "
+ + "or set the Android.sdk.bundletoolFilePath property.\n"
+ + "Android bundletool can be downloaded from "
+ + "https://github.com/google/bundletool");
+ }
+ if (Utilities.versionCompare(buildToolsVersion, "24.0.3") < 0) {
+ throw ModUtils.ModuleError("Version of Android SDK build tools too old. This version "
+ + "is " + buildToolsVersion + " and minimum version is "
+ + "24.0.3. Please update the Android SDK.")
+ }
}
FileTagger {
@@ -186,36 +235,6 @@ Module {
fileTags: ["android.aidl"]
}
- FileTagger {
- patterns: ["*.keystore"]
- fileTags: ["android.keystore"]
- }
-
- // Typically there is a debug keystore in ~/.android/debug.keystore which gets created
- // by the native build tools the first time a build is done. However, we don't want to create it
- // ourselves, because writing to a location outside the qbs build directory is both polluting
- // and has the potential for race conditions. So we'll instruct the user what to do.
- Group {
- name: "Android debug keystore"
- files: {
- if (!File.exists(Android.sdk.debugKeyStorePath)) {
- throw ModUtils.ModuleError("Could not find an Android debug keystore at " +
- Android.sdk.debugKeyStorePath + ". " +
- "If you are developing for Android on this machine for the first time and " +
- "have never built an application using the native Gradle / Android Studio " +
- "tooling, this is normal. You must create the debug keystore now using the " +
- "following command, in order to continue:\n\n" +
- SdkUtils.createDebugKeyStoreCommandString(java.keytoolFilePath,
- Android.sdk.debugKeyStorePath) +
- "\n\n" +
- "See the following URL for more information: " +
- "https://developer.android.com/studio/publish/app-signing.html#debug-mode");
- }
- return [Android.sdk.debugKeyStorePath];
- }
- fileTags: ["android.keystore"]
- }
-
Parameter {
property bool embedJar: true
}
@@ -236,15 +255,15 @@ Module {
for (var i = 0; i < (aidlSearchPaths ? aidlSearchPaths.length : 0); ++i)
args.push("-I" + aidlSearchPaths[i]);
args.push(input.filePath, output.filePath);
- cmd = new Command(aidl, args);
- cmd.description = "Processing " + input.fileName;
+ var cmd = new Command(aidl, args);
+ cmd.description = "processing " + input.fileName;
return [cmd];
}
}
property bool customManifestProcessing: false
Group {
- condition: !Android.sdk.customManifestProcessing
+ condition: !product.Android.sdk.customManifestProcessing
fileTagsFilter: "android.manifest_processed"
fileTags: "android.manifest_final"
}
@@ -257,7 +276,7 @@ Module {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Ensuring correct package name in Android manifest file";
+ cmd.description = "ensuring correct package name in Android manifest file";
cmd.sourceCode = function() {
var manifestData = new Xml.DomDocument();
manifestData.load(input.filePath);
@@ -277,6 +296,44 @@ Module {
rootElem.setAttribute("android:versionCode", product.Android.sdk.versionCode);
if (product.Android.sdk.versionName !== undefined)
rootElem.setAttribute("android:versionName", product.Android.sdk.versionName);
+
+ if (product.Android.sdk._bundledInAssets) {
+ // Remove <meta-data android:name="android.app.bundled_in_assets_resource_id"
+ // android:resource="@array/bundled_in_assets"/>
+ // custom AndroidManifest.xml because assets are in rcc files for qt >= 5.14
+ var appElem = rootElem.firstChild("application");
+ if (!appElem || !appElem.isElement() || appElem.tagName() != "application")
+ throw "No application tag found in '" + input.filePath + "'.";
+ var activityElem = appElem.firstChild("activity");
+ if (!activityElem || !activityElem.isElement() ||
+ activityElem.tagName() != "activity")
+ throw "No activity tag found in '" + input.filePath + "'.";
+ var metaDataElem = activityElem.firstChild("meta-data");
+ while (metaDataElem && metaDataElem.isElement()) {
+ if (SdkUtils.elementHasBundledAttributes(metaDataElem)) {
+ var elemToRemove = metaDataElem;
+ metaDataElem = metaDataElem.nextSibling("meta-data");
+ activityElem.removeChild(elemToRemove);
+ } else {
+ metaDataElem = metaDataElem.nextSibling("meta-data");
+ }
+ }
+ }
+
+ var usedSdkElem = rootElem.firstChild("uses-sdk");
+ if (!usedSdkElem || !usedSdkElem.isElement()) {
+ usedSdkElem = manifestData.createElement("uses-sdk");
+ rootElem.appendChild(usedSdkElem);
+ } else {
+ if (!usedSdkElem.isElement())
+ throw "Tag uses-sdk is not an element in '" + input.filePath + "'.";
+ }
+ usedSdkElem.setAttribute("android:minSdkVersion",
+ product.Android.sdk.minimumVersion);
+ usedSdkElem.setAttribute("android:targetSdkVersion",
+ product.Android.sdk.targetVersion);
+
+ rootElem.appendChild(usedSdkElem);
manifestData.save(output.filePath, 4);
}
return cmd;
@@ -284,7 +341,7 @@ Module {
}
Rule {
- condition: _enableRules
+ condition: _enableRules && !_enableAapt2
multiplex: true
inputs: ["android.resources", "android.assets", "android.manifest_final"]
@@ -307,6 +364,52 @@ Module {
}
Rule {
+ condition: _enableRules && _enableAapt2
+ inputs: ["android.resources"]
+ outputArtifacts: {
+ var outputs = [];
+ var resources = inputs["android.resources"];
+ for (var i = 0; i < resources.length; ++i) {
+ var filePath = resources[i].filePath;
+ var resourceFileName = SdkUtils.generateAapt2ResourceFileName(filePath);
+ var compiledName = FileInfo.joinPaths(product.Android.sdk.compiledResourcesDir,
+ resourceFileName);
+ outputs.push({filePath: compiledName, fileTags: "android.resources_compiled"});
+ }
+ return outputs;
+ }
+ outputFileTags: ["android.resources_compiled"]
+
+ prepare: SdkUtils.prepareAapt2CompileResource.apply(SdkUtils, arguments)
+ }
+
+ Rule {
+ condition: _enableRules && _enableAapt2
+ multiplex: true
+ inputs: ["android.resources_compiled", "android.assets", "android.manifest_final"]
+ outputFileTags: ["java.java", "android.apk_resources"]
+ outputArtifacts: {
+ var artifacts = [];
+ artifacts.push({
+ filePath: product.Android.sdk.apkBaseName + (product.Android.sdk._generateAab ?
+ ".apk_aab" : ".apk_apk"),
+ fileTags: ["android.apk_resources"]
+ });
+ var resources = inputs["android.resources_compiled"];
+ if (resources && resources.length) {
+ artifacts.push({
+ filePath: FileInfo.joinPaths(product.Android.sdk.generatedJavaFilesDir,
+ "R.java"),
+ fileTags: ["java.java"]
+ });
+ }
+
+ return artifacts;
+ }
+ prepare: SdkUtils.prepareAapt2Link.apply(SdkUtils, arguments)
+ }
+
+ Rule {
condition: _enableRules
multiplex: true
@@ -318,7 +421,7 @@ Module {
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Generating BuildConfig.java";
+ cmd.description = "generating BuildConfig.java";
cmd.sourceCode = function() {
var debugValue = product.qbs.buildVariant === "debug" ? "true" : "false";
var ofile = new TextFile(output.filePath, TextFile.WriteOnly);
@@ -336,9 +439,12 @@ Module {
condition: _enableRules
multiplex: true
inputs: ["java.class"]
- inputsFromDependencies: ["java.jar"]
+ inputsFromDependencies: ["java.jar", "bundled_jar"]
Artifact {
- filePath: FileInfo.joinPaths(product.Android.sdk.apkContentsDir, "classes.dex")
+ filePath: product.Android.sdk._generateAab ?
+ FileInfo.joinPaths(product.Android.sdk.packageContentsDir, "dex",
+ "classes.dex") :
+ FileInfo.joinPaths(product.Android.sdk.packageContentsDir, "classes.dex")
fileTags: ["android.dex"]
}
prepare: SdkUtils.prepareDex.apply(SdkUtils, arguments)
@@ -350,8 +456,10 @@ Module {
inputsFromDependencies: inputTags
inputs: product.aggregate ? [] : inputTags
Artifact {
- filePath: FileInfo.joinPaths(product.Android.sdk.apkContentsDir, "lib",
- input.Android.ndk.abi, input.fileName)
+ filePath: FileInfo.joinPaths(product.Android.sdk.packageContentsDir, "lib",
+ input.Android.ndk.abi, product.Android.sdk._archInName ?
+ input.baseName + "_" + input.Android.ndk.abi + ".so" :
+ input.fileName)
fileTags: "android.nativelibrary_deployed"
}
prepare: {
@@ -365,46 +473,14 @@ Module {
Rule {
condition: _enableRules
multiplex: true
- property stringList inputTags: "android.gdbserver"
- inputsFromDependencies: inputTags
- inputs: product.aggregate ? [] : inputTags
- outputFileTags: "android.gdbserver_deployed"
- outputArtifacts: {
- var deploymentData = SdkUtils.gdbserverOrStlDeploymentData(product, inputs,
- "gdbserver");
- var outputs = [];
- for (i = 0; i < deploymentData.outputFilePaths.length; ++i) {
- outputs.push({filePath: deploymentData.outputFilePaths[i],
- fileTags: "android.gdbserver_deployed"});
- }
- return outputs;
- }
- prepare: {
- var cmd = new JavaScriptCommand;
- cmd.description = "deploying gdbserver binaries";
- cmd.sourceCode = function() {
- var deploymentData = SdkUtils.gdbserverOrStlDeploymentData(product, inputs,
- "gdbserver");
- for (var i = 0; i < deploymentData.uniqueInputs.length; ++i) {
- File.copy(deploymentData.uniqueInputs[i].filePath,
- deploymentData.outputFilePaths[i]);
- }
- };
- return cmd;
- }
- }
-
- Rule {
- condition: _enableRules
- multiplex: true
property stringList inputTags: "android.stl"
inputsFromDependencies: inputTags
inputs: product.aggregate ? [] : inputTags
outputFileTags: "android.stl_deployed"
outputArtifacts: {
- var deploymentData = SdkUtils.gdbserverOrStlDeploymentData(product, inputs, "stl");
+ var deploymentData = SdkUtils.stlDeploymentData(product, inputs, "stl");
var outputs = [];
- for (i = 0; i < deploymentData.outputFilePaths.length; ++i) {
+ for (var i = 0; i < deploymentData.outputFilePaths.length; ++i) {
outputs.push({filePath: deploymentData.outputFilePaths[i],
fileTags: "android.stl_deployed"});
}
@@ -412,10 +488,10 @@ Module {
}
prepare: {
var cmds = [];
- var deploymentData = SdkUtils.gdbserverOrStlDeploymentData(product, inputs);
+ var deploymentData = SdkUtils.stlDeploymentData(product, inputs);
for (var i = 0; i < deploymentData.uniqueInputs.length; ++i) {
var input = deploymentData.uniqueInputs[i];
- var stripArgs = ["--strip-unneeded", "-o", deploymentData.outputFilePaths[i],
+ var stripArgs = ["--strip-all", "-o", deploymentData.outputFilePaths[i],
input.filePath];
var cmd = new Command(input.cpp.stripPath, stripArgs);
cmd.description = "deploying " + input.fileName;
@@ -426,17 +502,47 @@ Module {
}
Rule {
- condition: _enableRules
+ condition: _enableRules && !_enableAapt2 && !_generateAab
multiplex: true
inputs: [
"android.resources", "android.assets", "android.manifest_final",
- "android.dex", "android.gdbserver_deployed", "android.stl_deployed",
- "android.nativelibrary_deployed", "android.keystore"
+ "android.dex", "android.stl_deployed",
+ "android.nativelibrary_deployed"
]
Artifact {
- filePath: product.Android.sdk.apkBaseName + ".apk"
- fileTags: "android.apk"
+ filePath: product.Android.sdk.apkBaseName + ".apk_unsigned"
+ fileTags: "android.package_unsigned"
}
prepare: SdkUtils.prepareAaptPackage.apply(SdkUtils, arguments)
}
+
+ Rule {
+ condition: _enableRules && _enableAapt2 && !_generateAab
+ multiplex: true
+ inputs: [
+ "android.apk_resources", "android.manifest_final",
+ "android.dex", "android.stl_deployed",
+ "android.nativelibrary_deployed"
+ ]
+ Artifact {
+ filePath: product.Android.sdk.apkBaseName + ".apk_unsigned"
+ fileTags: "android.package_unsigned"
+ }
+ prepare: SdkUtils.prepareApkPackage.apply(SdkUtils, arguments)
+ }
+
+ Rule {
+ condition: _enableRules && _enableAapt2 && _generateAab
+ multiplex: true
+ inputs: [
+ "android.apk_resources", "android.manifest_final",
+ "android.dex", "android.stl_deployed",
+ "android.nativelibrary_deployed"
+ ]
+ Artifact {
+ filePath: product.Android.sdk.apkBaseName + ".aab_unsigned"
+ fileTags: "android.package_unsigned"
+ }
+ prepare: SdkUtils.prepareBundletoolPackage.apply(SdkUtils, arguments)
+ }
}
diff --git a/share/qbs/modules/Android/sdk/utils.js b/share/qbs/modules/Android/sdk/utils.js
index 3af179df2..232ee5a30 100644
--- a/share/qbs/modules/Android/sdk/utils.js
+++ b/share/qbs/modules/Android/sdk/utils.js
@@ -30,9 +30,11 @@
var File = require("qbs.File");
var FileInfo = require("qbs.FileInfo");
+var ModUtils = require("qbs.ModUtils");
var Process = require("qbs.Process");
var TextFile = require("qbs.TextFile");
var Utilities = require("qbs.Utilities");
+var Xml = require("qbs.Xml");
function availableBuildToolsVersions(sdkDir) {
var re = /^([0-9]+)\.([0-9]+)\.([0-9]+)$/;
@@ -55,8 +57,21 @@ function availableBuildToolsVersions(sdkDir) {
}
function prepareDex(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
- var dxFilePath = product.Android.sdk.dxFilePath;
- var args = ["--dex", "--output", output.filePath, product.java.classFilesDir];
+ var dexCompilerFilePath = product.Android.sdk._useD8 ? product.Android.sdk.d8FilePath
+ : product.Android.sdk.dxFilePath;
+ var args = ["--output", FileInfo.path(output.filePath)];
+
+ if (product.Android.sdk._useD8) {
+ args.push("--no-desugaring", "--release");
+ var classes = inputs["java.class"];
+ if (classes) {
+ args = args.concat(classes.map(function(javaClass) {
+ return FileInfo.relativePath(product.java.classFilesDir, javaClass.filePath) }));
+ }
+ } else {
+ args.unshift("--dex");
+ args.push(product.java.classFilesDir);
+ }
var jarFiles = [];
function traverseJarDeps(dep) {
@@ -69,17 +84,58 @@ function prepareDex(project, product, inputs, outputs, input, output, explicitly
return;
dep.artifacts["java.jar"].forEach(function(artifact) {
- if (!jarFiles.contains(artifact.filePath))
+ if (!jarFiles.includes(artifact.filePath))
jarFiles.push(artifact.filePath);
});
dep.dependencies.forEach(traverseJarDeps);
}
product.dependencies.forEach(traverseJarDeps);
+ if (typeof product.artifacts["java.jar"] !== "undefined") {
+ product.artifacts["java.jar"].forEach(function(artifact) {
+ if (!jarFiles.includes(artifact.filePath))
+ jarFiles.push(artifact.filePath);
+ });
+ }
+
args = args.concat(jarFiles);
- var cmd = new Command(dxFilePath, args);
- cmd.description = "Creating " + output.fileName;
+ var cmd;
+ if (product.Android.sdk._useD8) {
+ cmd = new JavaScriptCommand();
+ cmd.args = args;
+ cmd.dexCompilerFilePath = dexCompilerFilePath;
+ cmd.description = "creating " + output.fileName;
+ cmd.workingDirectory = product.java.classFilesDir;
+ cmd.extendedDescription = dexCompilerFilePath + " " + args.join(' ');
+ cmd.highlight = "compiler";
+ cmd.sourceCode = function() {
+ // androiddeployqt copied jar files in product.java.classFilesDir
+ // but the rule only tags one jar file ("QtAndroid.jar"/"Qt6Android.jar")
+ // So to pass all files to d8, Qbs needs to read the directory
+ var bundledJarFilesDir = product.java.classFilesDir;
+ var bundledJarFiles = File.directoryEntries(bundledJarFilesDir, File.Files
+ | File.NoDotAndDotDot);
+ args = args.concat(bundledJarFiles.map(function(jarFile) {
+ return FileInfo.joinPaths(bundledJarFilesDir, jarFile) }));
+ var process = new Process();
+ var exitCode;
+ process.setWorkingDirectory(workingDirectory);
+ process.exec(dexCompilerFilePath, args, true);
+ try {
+ process.exec(dexCompilerFilePath, args, true);
+ } catch (e) {
+ throw new Error("Error while running dex compiler command: '"
+ + Process.shellQuote(dexCompilerFilePath, args) + "': " + e.toString());
+ } finally {
+ process.close();
+ }
+ }
+ } else {
+ cmd = new Command(dexCompilerFilePath, args);
+ cmd.description = "creating " + output.fileName;
+ cmd.workingDirectory = product.java.classFilesDir;
+ }
return [cmd];
}
@@ -98,7 +154,7 @@ function findParentDir(filePath, parentDirName)
function commonAaptPackageArgs(project, product, inputs, outputs, input, output,
explicitlyDependsOn) {
var manifestFilePath = inputs["android.manifest_final"][0].filePath;
- var args = ["package", "-f",
+ var args = ["package", "--auto-add-overlay", "-f",
"-M", manifestFilePath,
"-I", product.Android.sdk.androidJarFilePath];
var resources = inputs["android.resources"];
@@ -110,7 +166,7 @@ function commonAaptPackageArgs(project, product, inputs, outputs, input, output,
throw "File '" + resources[i].filePath + "' is tagged as an Android resource, "
+ "but is not located under a directory called 'res'.";
}
- if (!resourceDirs.contains(resDir))
+ if (!resourceDirs.includes(resDir))
resourceDirs.push(resDir);
}
}
@@ -125,7 +181,7 @@ function commonAaptPackageArgs(project, product, inputs, outputs, input, output,
throw "File '" + assets[i].filePath + "' is tagged as an Android asset, "
+ "but is not located under a directory called 'assets'.";
}
- if (!assetDirs.contains(assetDir))
+ if (!assetDirs.includes(assetDir))
assetDirs.push(assetDir);
}
}
@@ -136,6 +192,97 @@ function commonAaptPackageArgs(project, product, inputs, outputs, input, output,
return args;
}
+// Rules: from https://developer.android.com/studio/command-line/aapt2
+// Input Output
+// XML resource files, such as Resource table with *.arsc.flat as its extension.
+// String and Style, which are
+// located in the res/values/
+// directory.
+
+// All other resource files. All files other than the files under res/values/ directory are
+// converted to binary XML files with *.flat extensions.
+// Additionally all PNG files are crunched by default and adopt
+// *.png.flat extensions.
+function generateAapt2ResourceFileName(filePath) {
+ var suffix = FileInfo.suffix(filePath);
+ if (suffix === "xml") {
+ var data = new Xml.DomDocument();
+ data.load(filePath);
+ var rootElem = data.documentElement();
+ if (rootElem && rootElem.isElement() && rootElem.tagName() === "resources")
+ // This is a valid XML resource file
+ suffix = "arsc";
+ // If the xml file is not a "resources" one then it's treated like any other resource file.
+ }
+ var dir = FileInfo.path(filePath);
+ var baseName = FileInfo.completeBaseName(filePath)
+ return FileInfo.fileName(dir) + "_" + baseName + "." + suffix + ".flat";
+}
+
+function prepareAapt2CompileResource(project, product, inputs, outputs, input, output,
+ explicitlyDependsOn) {
+ var cmds = [];
+ var resources = inputs["android.resources"];
+ var compilesResourcesDir = product.Android.sdk.compiledResourcesDir;
+ if (!File.makePath(compilesResourcesDir)) {
+ throw "Cannot create directory '" + FileInfo.toNativeSeparators(compilesResourcesDir) +
+ "'.";
+ }
+ var args = ["compile", FileInfo.toNativeSeparators(input.filePath),
+ "-o", FileInfo.toNativeSeparators(compilesResourcesDir)];
+ var cmd = new Command(product.Android.sdk.aaptFilePath, args);
+ var outputFileName = generateAapt2ResourceFileName(input.filePath);
+ cmd.description = "compiling resource " + input.fileName + " into " + outputFileName;
+ cmds.push(cmd);
+
+ return cmds;
+}
+
+function prepareAapt2Link(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var cmds = [];
+ var baseOutputFilePath = outputs["android.apk_resources"][0].filePath;
+ var manifestFilePath = inputs["android.manifest_final"][0].filePath;
+ var compilesResourcesDir = product.Android.sdk.compiledResourcesDir;
+ var compiledResources = inputs["android.resources_compiled"];
+
+ var args = ["link", "-o", baseOutputFilePath, "-I", product.Android.sdk.androidJarFilePath];
+ var i = 0;
+ if (compiledResources) {
+ for (i = 0; i < compiledResources.length; ++i)
+ args.push(compiledResources[i].filePath);
+ }
+ args.push("--no-auto-version");
+ args.push("--auto-add-overlay");
+ args.push("--manifest", manifestFilePath);
+ args.push("--java", product.Android.sdk.generatedJavaFilesBaseDir);
+
+ var assets = inputs["android.assets"];
+ var assetDirs = [];
+ if (assets) {
+ for (i = 0; i < assets.length; ++i) {
+ var assetDir = findParentDir(assets[i].filePath, "assets");
+ if (!assetDir) {
+ throw "File '" + assets[i].filePath + "' is tagged as an Android asset, "
+ + "but is not located under a directory called 'assets'.";
+ }
+ if (!assetDirs.includes(assetDir))
+ assetDirs.push(assetDir);
+ }
+ }
+ for (i = 0; i < assetDirs.length; ++i)
+ args.push("-A", assetDirs[i]);
+ if (product.qbs.buildVariant === "debug")
+ args.push("-v");
+ if (product.Android.sdk._generateAab)
+ args.push("--proto-format");
+ var cmd = new Command(product.Android.sdk.aaptFilePath, args);
+ cmd.description = "linking resources";
+ cmd.workingDirectory = product.buildDirectory;
+ cmds.push(cmd);
+
+ return cmds;
+}
+
function prepareAaptGenerate(project, product, inputs, outputs, input, output,
explicitlyDependsOn) {
var args = commonAaptPackageArgs.apply(this, arguments);
@@ -144,31 +291,20 @@ function prepareAaptGenerate(project, product, inputs, outputs, input, output,
if (resources && resources.length)
args.push("-J", ModUtils.moduleProperty(product, "generatedJavaFilesBaseDir"));
var cmd = new Command(product.Android.sdk.aaptFilePath, args);
- cmd.description = "Processing resources";
+ cmd.description = "processing resources";
return [cmd];
}
function prepareAaptPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
var cmds = [];
- var apkOutput = outputs["android.apk"][0];
+ var apkOutput = outputs["android.package_unsigned"][0];
var args = commonAaptPackageArgs.apply(this, arguments);
args.push("-F", apkOutput.filePath + ".unaligned");
- args.push(product.Android.sdk.apkContentsDir);
+ args.push(product.Android.sdk.packageContentsDir);
var cmd = new Command(product.Android.sdk.aaptFilePath, args);
- cmd.description = "Generating " + apkOutput.filePath;
+ cmd.description = "generating " + apkOutput.fileName;
cmds.push(cmd);
- if (!product.Android.sdk.useApksigner) {
- args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1",
- "-keystore", inputs["android.keystore"][0].filePath,
- "-storepass", "android",
- apkOutput.filePath + ".unaligned",
- "androiddebugkey"];
- cmd = new Command(product.java.jarsignerFilePath, args);
- cmd.description = "Signing " + apkOutput.fileName;
- cmds.push(cmd);
- }
-
cmd = new Command(product.Android.sdk.zipalignFilePath,
["-f", "4", apkOutput.filePath + ".unaligned", apkOutput.filePath]);
cmd.silent = true;
@@ -179,46 +315,135 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex
cmd.unalignedApk = apkOutput.filePath + ".unaligned";
cmd.sourceCode = function() { File.remove(unalignedApk); };
cmds.push(cmd);
+ return cmds;
+}
+
+function prepareApkPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var cmds = [];
+ var apkInputFilePath = inputs["android.apk_resources"][0].filePath;
+ var apkOutput = outputs["android.package_unsigned"][0];
+ var apkOutputFilePathUnaligned = apkOutput.filePath + ".unaligned";
+ var dexFilePath = inputs["android.dex"][0].filePath;
- if (product.Android.sdk.useApksigner) {
- // TODO: Implement full signing support, not just using the debug keystore
- args = ["sign",
- "--ks", inputs["android.keystore"][0].filePath,
- "--ks-pass", "pass:android",
- apkOutput.filePath];
- cmd = new Command(product.Android.sdk.apksignerFilePath, args);
- cmd.description = "Signing " + apkOutput.fileName;
- cmds.push(cmd);
+ var copyCmd = new JavaScriptCommand();
+ copyCmd.description = "copying apk";
+ copyCmd.source = apkInputFilePath;
+ copyCmd.target = apkOutputFilePathUnaligned;
+ copyCmd.sourceCode = function() {
+ File.copy(source, target);
}
+ cmds.push(copyCmd);
+
+ var jarArgs = ["-uvf", apkOutputFilePathUnaligned, "."];
+ var libPath = FileInfo.joinPaths(product.Android.sdk.packageContentsDir);
+ var jarCmd = new Command(product.java.jarFilePath, jarArgs);
+ jarCmd.description = "packaging files";
+ jarCmd.workingDirectory = libPath;
+ cmds.push(jarCmd);
+
+ cmd = new Command(product.Android.sdk.zipalignFilePath,
+ ["-f", "4", apkOutputFilePathUnaligned, apkOutput.filePath]);
+ cmd.silent = true;
+ cmds.push(cmd);
+ cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.unalignedApk = apkOutputFilePathUnaligned;
+ cmd.sourceCode = function() { File.remove(unalignedApk); };
+ cmds.push(cmd);
return cmds;
}
-function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath) {
- var args = ["-genkey", "-keystore", keystoreFilePath, "-alias", "androiddebugkey",
- "-storepass", "android", "-keypass", "android", "-keyalg", "RSA",
- "-keysize", "2048", "-validity", "10000", "-dname",
- "CN=Android Debug,O=Android,C=US"];
- return Process.shellQuote(keytoolFilePath, args);
+function prepareBundletoolPackage(project, product, inputs, outputs, input, output,
+ explicitlyDependsOn) {
+ var cmds = [];
+ var baseModuleDir = product.Android.sdk.packageContentsDir;
+ var manifestDirName = FileInfo.joinPaths(baseModuleDir, "manifest");
+ var pkgBaseFileName = inputs["android.apk_resources"][0].filePath;
+
+ var jarResourcesArgs = ["xf", pkgBaseFileName];
+ var jarResourcesCmd = new Command(product.java.jarFilePath, jarResourcesArgs);
+ jarResourcesCmd.description = "extracting resources apk";
+ jarResourcesCmd.workingDirectory = baseModuleDir;
+ cmds.push(jarResourcesCmd);
+
+ var moveManifestCmd = new JavaScriptCommand();
+ moveManifestCmd.description = "moving manifest in manifest directory";
+ moveManifestCmd.path = manifestDirName;
+ moveManifestCmd.manifestFilePath = baseModuleDir + "/AndroidManifest.xml";
+ moveManifestCmd.sourceCode = function() {
+ if (!File.exists(path))
+ File.makePath(path);
+ if (File.exists(manifestFilePath))
+ File.move(manifestFilePath, path + "/AndroidManifest.xml");
+ }
+ cmds.push(moveManifestCmd);
+
+ var baseFilePath = FileInfo.joinPaths(product.buildDirectory, "base.zip");
+ var jarBaseArgs = ["cfM", baseFilePath, "."];
+ var jarBaseCmd = new Command(product.java.jarFilePath, jarBaseArgs);
+ jarBaseCmd.description = "compressing base module";
+ jarBaseCmd.workingDirectory = baseModuleDir;
+ cmds.push(jarBaseCmd);
+
+ var aabFilePath = outputs["android.package_unsigned"][0].filePath;
+ var removeCmd = new JavaScriptCommand();
+ removeCmd.description = "removing previous aab";
+ removeCmd.filePath = aabFilePath;
+ removeCmd.sourceCode = function() {
+ if (File.exists(filePath))
+ File.remove(filePath);
+ }
+ cmds.push(removeCmd);
+
+ var bundleConfigFilePath = FileInfo.joinPaths(product.buildDirectory, "BundleConfig.json");
+ var createBundleConfigCmd = new JavaScriptCommand();
+ createBundleConfigCmd.description = "create BundleConfig.json";
+ createBundleConfigCmd.filePath = bundleConfigFilePath;
+ createBundleConfigCmd.sourceCode = function() {
+ var bc = new TextFile(filePath, TextFile.WriteOnly);
+ bc.writeLine('{"optimizations": {');
+ bc.writeLine('"uncompress_native_libraries": {');
+ bc.writeLine('"enabled": false');
+ bc.writeLine('}}}');
+ }
+ cmds.push(createBundleConfigCmd);
+
+ var args = ["-jar", product.Android.sdk.bundletoolFilePath, "build-bundle"];
+ args.push("--modules=" + baseFilePath);
+ args.push("--output=" + aabFilePath);
+ args.push("--config=" + bundleConfigFilePath);
+ var cmd = new Command(product.java.interpreterFilePath, args);
+ cmd.description = "generating " + aabFilePath.fileName;
+ cmds.push(cmd);
+
+ return cmds;
}
-function gdbserverOrStlDeploymentData(product, inputs, type)
+function stlDeploymentData(product, inputs, type)
{
var data = { uniqueInputs: [], outputFilePaths: []};
var uniqueFilePaths = [];
- var theInputs = inputs[type === "gdbserver" ? "android.gdbserver" : "android.stl"];
+ var theInputs = inputs["android.stl"];
if (!theInputs)
return data;
for (var i = 0; i < theInputs.length; ++i) {
var currentInput = theInputs[i];
- if (uniqueFilePaths.contains(currentInput.filePath))
+ if (uniqueFilePaths.includes(currentInput.filePath))
continue;
uniqueFilePaths.push(currentInput.filePath);
data.uniqueInputs.push(currentInput);
- var outputFileName = type === "gdbserver" ? "libgdbserver.so" : currentInput.fileName;
- data.outputFilePaths.push(FileInfo.joinPaths(product.Android.sdk.apkContentsDir, "lib",
+ var outputFileName = currentInput.fileName;
+ data.outputFilePaths.push(FileInfo.joinPaths(product.Android.sdk.packageContentsDir, "lib",
currentInput.Android.ndk.abi,
outputFileName));
}
return data;
}
+
+function elementHasBundledAttributes(element)
+{
+ return element.hasAttribute("android:name") &&
+ (element.attribute("android:name") === "android.app.bundled_in_assets_resource_id") ||
+ (element.attribute("android:name") === "android.app.bundled_in_lib_resource_id");
+}
diff --git a/share/qbs/modules/Exporter/cmake/cmakeexporter.js b/share/qbs/modules/Exporter/cmake/cmakeexporter.js
new file mode 100644
index 000000000..093032f2e
--- /dev/null
+++ b/share/qbs/modules/Exporter/cmake/cmakeexporter.js
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Raphaël Cotty <raphael.cotty@gmail.com>
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of the 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 = require("qbs.FileInfo");
+var ModUtils = require("qbs.ModUtils");
+var ExporterHelpers = require("../exporter.js");
+
+function tagListToString(tagList)
+{
+ return JSON.stringify(tagList);
+}
+
+function collectAutodetectedData(project, topLevelProduct, outputs)
+{
+ var packageName = topLevelProduct.Exporter.cmake.packageName;
+
+ var data = {};
+ data.packageName = packageName;
+ data.installPrefixDir = "_" + packageName.toUpperCase() + "_INSTALL_PREFIX";
+ data.packages = [];
+
+ function quote(value)
+ {
+ return "\"" + value + "\"";
+ }
+
+ function quoteAndPrefixify(propName)
+ {
+ function quoteAndPrefixifyHelper(value) {
+ var prefixToStrip =
+ ExporterHelpers.getPrefixToStrip(project, topLevelProduct, propName, value);
+ if (typeof value !== "string"
+ || !prefixToStrip
+ || (value.length > prefixToStrip.length
+ && value[prefixToStrip.length] !== '/')) {
+ return quote(value);
+ }
+ return quote("${" + data.installPrefixDir + "}" + value.slice(prefixToStrip.length));
+ }
+ return quoteAndPrefixifyHelper;
+ }
+
+ var installedOutputFilePath = ModUtils.artifactInstalledFilePath(
+ outputs["Exporter.cmake.configFile"][0]);
+ var installedOutputPathName = FileInfo.path(installedOutputFilePath);
+
+ var installRootPath = FileInfo.joinPaths(topLevelProduct.qbs.installRoot, topLevelProduct.qbs.installPrefix);
+ data.installPrefix = FileInfo.relativePath(installedOutputPathName, installRootPath);
+
+ var libArtifacts;
+ var libImportArtifacts;
+ var isProduct = !topLevelProduct.present;
+ var considerFramework = !isProduct || (topLevelProduct.type
+ && topLevelProduct.type.includes("bundle.content"))
+ && topLevelProduct.bundle
+ && topLevelProduct.bundle.isBundle
+ && topLevelProduct.qbs.targetOS.includes("darwin");
+ var considerDynamicLibs = !isProduct || (topLevelProduct.type
+ && topLevelProduct.type.includes("dynamiclibrary"));
+ var considerStaticLibs = !isProduct || (topLevelProduct.type
+ && topLevelProduct.type.includes("staticlibrary"));
+ if (considerFramework) {
+ libArtifacts = topLevelProduct.artifacts["bundle.symlink.executable"];
+ if (considerDynamicLibs)
+ data.type = "SHARED";
+ else if (considerStaticLibs)
+ data.type = "STATIC";
+ else
+ data.type = "INTERFACE";
+ } else if (considerDynamicLibs) {
+ libArtifacts = topLevelProduct.artifacts.dynamiclibrary;
+ libImportArtifacts = topLevelProduct.artifacts.dynamiclibrary_import;
+ data.type = "SHARED";
+ } else if (considerStaticLibs) {
+ libArtifacts = topLevelProduct.artifacts.staticlibrary;
+ data.type = "STATIC";
+ } else {
+ data.type = "INTERFACE";
+ }
+
+ for (var i = 0; i < (libArtifacts || []).length; ++i) {
+ var libArtifact = libArtifacts[i];
+ var libImportArtifact = (libImportArtifacts || [])[i];
+ if (libArtifact.qbs.install) {
+ var installPath = ModUtils.artifactInstalledFilePath(libArtifact);
+ data.importedLocation = quoteAndPrefixify("installRoot")(installPath);
+ data.soName = topLevelProduct.targetName;
+ if (libImportArtifact && libImportArtifact.qbs.install) {
+ installPath = ModUtils.artifactInstalledFilePath(libImportArtifact);
+ data.importedImplibLocation = quoteAndPrefixify("installRoot")(installPath);
+ }
+ break;
+ }
+ }
+ var cpp = topLevelProduct.exports.cpp;
+ if (cpp) {
+ data.libraryPaths = (cpp.libraryPaths || []).map(quoteAndPrefixify("cpp.libraryPaths"));
+
+ data.linkLibraries = [];
+ data.linkLibraries = data.linkLibraries.concat(cpp.dynamicLibraries || []);
+ data.linkLibraries = data.linkLibraries.concat(cpp.staticLibraries || []);
+ data.linkLibraries = data.linkLibraries.map(quoteAndPrefixify("cpp.dynamicLibraries"));
+
+ data.linkOptions = [];
+ data.linkOptions = data.linkOptions.concat(cpp.driverLinkerFlags || []);
+ if ((cpp.linkerFlags || []).length > 0) {
+ data.linkOptions =
+ data.linkOptions.concat("LINKER:" + (cpp.linkerFlags || []).join(","));
+ }
+ data.linkOptions = data.linkOptions.map(quote);
+
+ data.includeDirectories =
+ (cpp.includePaths || []).map(quoteAndPrefixify("cpp.includePaths"));
+ data.compileDefinitions = (cpp.defines || []).map(quote);
+
+ data.compileOptions = [];
+ data.compileOptions = data.compileOptions.concat(cpp.commonCompilerFlags || []);
+ data.compileOptions = data.compileOptions.concat(cpp.driverFlags || []);
+ data.compileOptions = data.compileOptions.concat(cpp.cxxFlags || []);
+ data.compileOptions = data.compileOptions.concat(cpp.cFlags || []);
+ data.compileOptions = data.compileOptions.map(quote);
+ }
+
+ function gatherDeps(dep) {
+ if (dep.name === "Exporter.cmake")
+ return;
+ var depHasExporter = dep.Exporter && dep.Exporter.cmake;
+ if (!depHasExporter)
+ return;
+ data.packages.push(dep.Exporter.cmake.packageName);
+ }
+
+ var exportedDeps = topLevelProduct.exports ? topLevelProduct.exports.dependencies : [];
+ exportedDeps.forEach(gatherDeps);
+
+ return data;
+}
+
+function writeConfigFile(project, product, outputs)
+{
+ var autoDetectedData = collectAutodetectedData(project, product, outputs);
+ var packageName = autoDetectedData.packageName;
+
+ function writeCommand(command, lines)
+ {
+ if ((lines || []).length === 0)
+ return;
+ cmakeConfigFile.writeLine(command + "(" + packageName + " INTERFACE");
+ for (i = 0; i < lines.length; i++) {
+ cmakeConfigFile.writeLine(" " + lines[i]);
+ }
+ cmakeConfigFile.writeLine(")");
+ }
+
+ var cmakeConfigFile = new TextFile(outputs["Exporter.cmake.configFile"][0].filePath,
+ TextFile.WriteOnly);
+ cmakeConfigFile.writeLine("# Generated by Qbs");
+
+ cmakeConfigFile.writeLine("cmake_minimum_required(VERSION 3.5)");
+
+ cmakeConfigFile.writeLine("if(TARGET " + packageName + ")");
+ cmakeConfigFile.writeLine(" return()");
+ cmakeConfigFile.writeLine("endif()");
+
+ cmakeConfigFile.writeLine("set(" + autoDetectedData.installPrefixDir +
+ " \"${CMAKE_CURRENT_LIST_DIR}/" +
+ autoDetectedData.installPrefix + "\")");
+
+ autoDetectedData.packages.forEach(function(packageName) {
+ cmakeConfigFile.writeLine("find_package(" + packageName + " REQUIRED SILENT)");
+ });
+ cmakeConfigFile.writeLine(
+ "add_library(" + packageName + " " + autoDetectedData.type + " IMPORTED)");
+ var configuration = (product.qbs.buildVariant) ?
+ product.qbs.buildVariant.toUpperCase() : "NONE";
+ cmakeConfigFile.writeLine("set_property(TARGET " + packageName +
+ " APPEND PROPERTY IMPORTED_CONFIGURATIONS " +
+ configuration + ")");
+
+ cmakeConfigFile.writeLine("set_target_properties(" + packageName + " PROPERTIES");
+ cmakeConfigFile.writeLine(" IMPORTED_LINK_INTERFACE_LANGUAGES_" + configuration +
+ " CXX");
+ if (autoDetectedData.type !== "INTERFACE") {
+ cmakeConfigFile.writeLine(" IMPORTED_LOCATION_" + configuration + " " +
+ autoDetectedData.importedLocation);
+ }
+ if (autoDetectedData.importedImplibLocation) {
+ cmakeConfigFile.writeLine(" IMPORTED_IMPLIB_" + configuration + " " +
+ autoDetectedData.importedImplibLocation);
+ }
+ cmakeConfigFile.writeLine(")");
+
+ writeCommand("target_link_directories", autoDetectedData.libraryPaths);
+ writeCommand("target_link_libraries",
+ autoDetectedData.linkLibraries.concat(autoDetectedData.packages));
+ writeCommand("target_link_options", autoDetectedData.linkOptions);
+ writeCommand("target_include_directories", autoDetectedData.includeDirectories);
+ writeCommand("target_compile_definitions", autoDetectedData.compileDefinitions);
+ writeCommand("target_compile_options", autoDetectedData.compileOptions);
+
+ cmakeConfigFile.close();
+}
+
+function writeVersionFile(product, outputs)
+{
+ var cmakeVersionFile = new TextFile(
+ outputs["Exporter.cmake.versionFile"][0].filePath, TextFile.WriteOnly);
+ cmakeVersionFile.writeLine("# Generated by Qbs");
+ cmakeVersionFile.writeLine("set(PACKAGE_VERSION \"" + product.version + "\")");
+ cmakeVersionFile.close();
+}
diff --git a/share/qbs/modules/Exporter/cmake/cmakeexporter.qbs b/share/qbs/modules/Exporter/cmake/cmakeexporter.qbs
new file mode 100644
index 000000000..a578e938b
--- /dev/null
+++ b/share/qbs/modules/Exporter/cmake/cmakeexporter.qbs
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Raphaël Cotty <raphael.cotty@gmail.com>
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 qbs.File
+import qbs.FileInfo
+import qbs.ModUtils
+import qbs.TextFile
+
+import "cmakeexporter.js" as HelperFunctions
+
+Module {
+ property string configFileName: packageName + "Config.cmake"
+ property string versionFileName: packageName + "ConfigVersion.cmake"
+ property string packageName: product.targetName
+
+ additionalProductTypes: ["Exporter.cmake.package"]
+
+ Rule {
+ multiplex: true
+ requiresInputs: false
+
+ auxiliaryInputs: {
+ if (product.type.includes("staticlibrary"))
+ return ["staticlibrary"];
+ if (product.type.includes("dynamiclibrary"))
+ return ["dynamiclibrary"];
+ }
+
+ Artifact {
+ filePath: product.Exporter.cmake.configFileName
+ fileTags: ["Exporter.cmake.package", "Exporter.cmake.configFile"]
+ }
+ Artifact {
+ filePath: product.Exporter.cmake.versionFileName
+ fileTags: ["Exporter.cmake.package", "Exporter.cmake.versionFile"]
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generate cmake package files";
+ cmd.sourceCode = function() {
+ HelperFunctions.writeConfigFile(project, product, outputs);
+ HelperFunctions.writeVersionFile(product, outputs);
+ }
+ return [cmd];
+ }
+ }
+}
diff --git a/share/qbs/modules/Exporter/exporter.js b/share/qbs/modules/Exporter/exporter.js
new file mode 100644
index 000000000..65a632ac8
--- /dev/null
+++ b/share/qbs/modules/Exporter/exporter.js
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of the 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 getPrefixToStrip(project, product, propName, value)
+{
+ function checkValuePrefix(forbiddenPrefix, prefixDescription)
+ {
+ if (value.startsWith(forbiddenPrefix)) {
+ throw "Value '" + value + "' for exported property '" + propName + "' in product '"
+ + product.name + "' points into " + prefixDescription + ".\n"
+ + "Did you forget to set the prefixMapping property in an Export item?";
+ }
+ }
+
+ // Catch user oversights: Paths that point into the project source or build directories
+ // make no sense in the module.
+ if (!value.startsWith(product.qbs.installRoot)) {
+ checkValuePrefix(project.buildDirectory, "project build directory");
+ checkValuePrefix(project.sourceDirectory, "project source directory");
+ }
+
+ // Adapt file paths pointing into the install dir, that is, make them relative to the
+ // module file for relocatability. We accept them with or without the install root.
+ // The latter form will typically be a result of applying the prefixMapping property,
+ // while the first one could be an untransformed path, for instance if the project
+ // file is written in such a way that include paths are picked up from the installed
+ // location rather than the source directory.
+ var result;
+ var fullInstallPrefix = FileInfo.joinPaths(product.qbs.installRoot, product.qbs.installPrefix);
+ if (fullInstallPrefix.length > 1 && value.startsWith(fullInstallPrefix)) {
+ result = fullInstallPrefix;
+ } else {
+ var installPrefix = FileInfo.joinPaths("/", product.qbs.installPrefix);
+ if (installPrefix.length > 1 && value.startsWith(installPrefix))
+ result = installPrefix;
+ }
+ return result;
+}
diff --git a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js
index a3109d61d..50bba536c 100644
--- a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js
+++ b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.js
@@ -33,7 +33,7 @@ var ModUtils = require("qbs.ModUtils");
function quote(value)
{
- if (value.contains(" ") || value.contains("'") || value.contains('"')) {
+ if (value.includes(" ") || value.includes("'") || value.includes('"')) {
return '"' + value.replace(/(["'\\])/g, "\\$1") + '"';
}
return value;
@@ -44,9 +44,9 @@ function writeEntry(product, file, key, propertyName, required, additionalValues
var value = product.Exporter.pkgconfig[propertyName];
if (additionalValues && additionalValues.length > 0)
value = (value || []).concat(additionalValues);
- var valueIsNotEmpty = value && (!Array.isArray(value) || value.length > 0);
+ var valueIsNotEmpty = value && (!(value instanceof Array) || value.length > 0);
if (valueIsNotEmpty) {
- if (Array.isArray(value))
+ if (value instanceof Array)
value = value.join(' ');
file.writeLine(key + ": " + value);
} else if (required) {
@@ -83,7 +83,7 @@ function collectAutodetectedData(topLevelProduct)
|| (value.length > installPrefix.length && value[installPrefix.length] !== '/')) {
return quotedValue;
}
- return quotedValue.replace(product.qbs.installPrefix, "${prefix}");
+ return quotedValue.replace(topLevelProduct.qbs.installPrefix, "${prefix}");
}
function transformedValue(product, moduleName, propertyName)
@@ -92,7 +92,7 @@ function collectAutodetectedData(topLevelProduct)
var value = transformFunc
? eval("(" + transformFunc + ")(product, moduleName, propertyName, originalValue)")
: originalValue;
- if (Array.isArray(value))
+ if (value instanceof Array)
value.forEach(function(v, i, a) { a[i] = quoteAndPrefixify(v); });
else if (value)
value = quoteAndPrefixify(value);
@@ -105,12 +105,12 @@ function collectAutodetectedData(topLevelProduct)
var libArtifacts;
var isProduct = !productOrModule.present;
var considerDynamicLibs = !isProduct || (productOrModule.type
- && productOrModule.type.contains("dynamiclibrary"));
+ && productOrModule.type.includes("dynamiclibrary"));
if (considerDynamicLibs) {
libArtifacts = productOrModule.artifacts.dynamiclibrary;
} else {
var considerStaticLibs = !isProduct || (productOrModule.type
- && productOrModule.type.contains("staticlibrary"));
+ && productOrModule.type.includes("staticlibrary"));
if (considerStaticLibs)
libArtifacts = productOrModule.artifacts.staticlibrary;
}
@@ -186,7 +186,7 @@ function collectAutodetectedData(topLevelProduct)
exportedDepNames.push(exportedDeps[i].name);
for (i = 0; i < (productOrModule.dependencies || []).length; ++i) {
var dep = productOrModule.dependencies[i];
- if (exportedDepNames.contains(dep.name))
+ if (exportedDepNames.includes(dep.name))
continue;
privateDeps.push(dep);
}
@@ -197,22 +197,22 @@ function collectAutodetectedData(topLevelProduct)
var depHasPkgConfig = dep.Exporter && dep.Exporter.pkgconfig;
if (depHasPkgConfig) {
var entry = FileInfo.completeBaseName(dep.Exporter.pkgconfig.fileName);
- if (excludedDeps.contains(entry))
+ if (excludedDeps.includes(entry))
return;
- if (isPrivateDep && !data.requiresPrivate.contains(entry)
- && !explicitRequiresPrivate.contains(entry)) {
+ if (isPrivateDep && !data.requiresPrivate.includes(entry)
+ && !explicitRequiresPrivate.includes(entry)) {
data.requiresPrivate.push(entry);
}
- if (!isPrivateDep && !data.requires.contains(entry)
- && !explicitRequires.contains(entry)) {
+ if (!isPrivateDep && !data.requires.includes(entry)
+ && !explicitRequires.includes(entry)) {
data.requires.push(entry);
}
} else {
- if (excludedDeps.contains(dep.name))
+ if (excludedDeps.includes(dep.name))
return;
- if (isPrivateDep && explicitRequiresPrivate.contains(dep.name))
+ if (isPrivateDep && explicitRequiresPrivate.includes(dep.name))
return;
- if (!isPrivateDep && explicitRequires.contains(dep.name))
+ if (!isPrivateDep && explicitRequires.includes(dep.name))
return;
collectAutodetectedDataRecursive(dep, isPrivateDep);
}
diff --git a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs
index 8cc55f885..feb0e017c 100644
--- a/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs
+++ b/share/qbs/modules/Exporter/pkgconfig/pkgconfig.qbs
@@ -34,9 +34,9 @@ Module {
auxiliaryInputs: {
if (!autoDetect)
return undefined;
- if (product.type.contains("staticlibrary"))
+ if (product.type.includes("staticlibrary"))
return ["staticlibrary"];
- if (product.type.contains("dynamiclibrary"))
+ if (product.type.includes("dynamiclibrary"))
return ["dynamiclibrary"];
}
@@ -46,7 +46,7 @@ Module {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.sourceCode = function() {
var f = new TextFile(output.filePath, TextFile.WriteOnly);
if (product.Exporter.pkgconfig._usePrefix)
diff --git a/share/qbs/modules/Exporter/qbs/qbsexporter.js b/share/qbs/modules/Exporter/qbs/qbsexporter.js
index 16408174b..145474a4b 100644
--- a/share/qbs/modules/Exporter/qbs/qbsexporter.js
+++ b/share/qbs/modules/Exporter/qbs/qbsexporter.js
@@ -30,6 +30,7 @@
var FileInfo = require("qbs.FileInfo");
var ModUtils = require("qbs.ModUtils");
+var ExporterHelpers = require("../exporter.js");
function tagListToString(tagList)
{
@@ -74,7 +75,7 @@ function writeTargetArtifactGroups(product, output, moduleFile)
var tag = product.Exporter.qbs._artifactTypes[i];
var artifactsForTag = product.artifacts[tag] || [];
for (var j = 0; j < artifactsForTag.length; ++j) {
- if (!relevantArtifacts.contains(artifactsForTag[j]))
+ if (!relevantArtifacts.includes(artifactsForTag[j]))
relevantArtifacts.push(artifactsForTag[j]);
}
}
@@ -82,7 +83,7 @@ function writeTargetArtifactGroups(product, output, moduleFile)
var artifactCount = relevantArtifacts ? relevantArtifacts.length : 0;
for (i = 0; i < artifactCount; ++i) {
var artifact = relevantArtifacts[i];
- if (!artifact.fileTags.contains("installable"))
+ if (!artifact.fileTags.includes("installable"))
continue;
// Put all artifacts with the same set of file tags into the same group, so we don't
@@ -102,18 +103,9 @@ function writeTargetArtifactGroups(product, output, moduleFile)
}
}
-function checkValuePrefix(name, value, forbiddenPrefix, prefixDescription)
-{
- if (value.startsWith(forbiddenPrefix)) {
- throw "Value '" + value + "' for exported property '" + name + "' in product '"
- + product.name + "' points into " + prefixDescription + ".\n"
- + "Did you forget to set the prefixMapping property in an Export item?";
- }
-}
-
function stringifyValue(project, product, moduleInstallDir, prop, value)
{
- if (Array.isArray(value)) {
+ if (value instanceof Array) {
var repr = "[";
for (var i = 0; i < value.length; ++i) {
repr += stringifyValue(project, product, moduleInstallDir, prop, value[i]) + ", ";
@@ -128,29 +120,9 @@ function stringifyValue(project, product, moduleInstallDir, prop, value)
return value;
}
- // Catch user oversights: Paths that point into the project source or build directories
- // make no sense in the module.
- if (!value.startsWith(product.qbs.installRoot)) {
- checkValuePrefix(prop.name, value, project.buildDirectory, "project build directory");
- checkValuePrefix(prop.name, value, project.sourceDirectory, "project source directory");
- }
-
- // Adapt file paths pointing into the install dir, that is, make them relative to the
- // module file for relocatability. We accept them with or without the install root.
- // The latter form will typically be a result of applying the prefixMapping property,
- // while the first one could be an untransformed path, for instance if the project
- // file is written in such a way that include paths are picked up from the installed
- // location rather than the source directory.
- var valuePrefixToStrip;
- var fullInstallPrefix = FileInfo.joinPaths(product.qbs.installRoot, product.qbs.installPrefix);
- if (fullInstallPrefix.length > 1 && value.startsWith(fullInstallPrefix)) {
- valuePrefixToStrip = fullInstallPrefix;
- } else {
- var installPrefix = FileInfo.joinPaths("/", product.qbs.installPrefix);
- if (installPrefix.length > 1 && value.startsWith(installPrefix))
- valuePrefixToStrip = installPrefix;
- }
+ var valuePrefixToStrip = ExporterHelpers.getPrefixToStrip(project, product, prop.name, value);
if (valuePrefixToStrip) {
+ var fullInstallPrefix = FileInfo.joinPaths(product.qbs.installRoot, product.qbs.installPrefix);
var deployedModuleInstallDir = moduleInstallDir.slice(fullInstallPrefix.length);
return "FileInfo.cleanPath(FileInfo.joinPaths(path, FileInfo.relativePath("
+ JSON.stringify(deployedModuleInstallDir) + ", "
@@ -172,14 +144,14 @@ function writeProperty(project, product, moduleInstallDir, prop, indentation, co
var moduleName;
if (isModuleProperty) {
moduleName = prop.name.slice(0, separatorIndex);
- if ((product.Exporter.qbs.excludedDependencies || []).contains(moduleName))
+ if ((product.Exporter.qbs.excludedDependencies || []).includes(moduleName))
return;
}
line += prop.name + ": ";
// We emit the literal value, unless the source code clearly refers to values from inside the
// original project, in which case the evaluated value is used.
- if (considerValue && /(project|product)\./.test(prop.sourceCode)) {
+ if (considerValue && /(project|product|exportingProduct)\./.test(prop.sourceCode)) {
var value;
if (isModuleProperty) {
var propertyName = prop.name.slice(separatorIndex + 1);
@@ -244,7 +216,7 @@ function isExcludedDependency(product, childItem)
for (var i = 0; i < childItem.properties.length; ++i) {
var prop = childItem.properties[i];
var unquotedRhs = prop.sourceCode.slice(1, -1);
- if (prop.name === "name" && product.Exporter.qbs.excludedDependencies.contains(unquotedRhs))
+ if (prop.name === "name" && product.Exporter.qbs.excludedDependencies.includes(unquotedRhs))
return true;
}
return false;
@@ -264,9 +236,9 @@ function writeImportStatements(product, moduleFile)
var imports = product.exports.imports;
// We potentially use FileInfo ourselves when transforming paths in stringifyValue().
- if (!imports.contains("import qbs.FileInfo"))
+ if (!imports.includes("import qbs.FileInfo"))
imports.push("import qbs.FileInfo");
- for (var i = 0; i < product.exports.imports.length; ++i)
- moduleFile.writeLine(product.exports.imports[i]);
+ for (var i = 0; i < imports.length; ++i)
+ moduleFile.writeLine(imports[i]);
}
diff --git a/share/qbs/modules/Exporter/qbs/qbsexporter.qbs b/share/qbs/modules/Exporter/qbs/qbsexporter.qbs
index 861483ef0..1e7cb02d5 100644
--- a/share/qbs/modules/Exporter/qbs/qbsexporter.qbs
+++ b/share/qbs/modules/Exporter/qbs/qbsexporter.qbs
@@ -59,10 +59,9 @@ Module {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.sourceCode = function() {
var f = new TextFile(output.filePath, TextFile.WriteOnly);
- f.writeLine("import qbs");
HelperFunctions.writeImportStatements(product, f);
f.writeLine("\nModule {");
HelperFunctions.writeModuleProperties(project, product, output, f);
diff --git a/share/qbs/modules/Sanitizers/address/asan.qbs b/share/qbs/modules/Sanitizers/address/asan.qbs
new file mode 100644
index 000000000..9d8f5b97e
--- /dev/null
+++ b/share/qbs/modules/Sanitizers/address/asan.qbs
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 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.Utilities
+
+Module {
+ Depends { name: "cpp" }
+
+ property bool enabled: true
+ readonly property bool _supported: qbs.toolchain.includes("gcc")
+ || qbs.toolchain.includes("clang-cl")
+ || (qbs.toolchain.includes("msvc")
+ && Utilities.versionCompare(cpp.compilerVersion, "19.28.29500.0") >= 0)
+ readonly property bool _enabled: enabled && _supported
+
+ property string detectUseAfterReturn: "always"
+ PropertyOptions {
+ name: "detectUseAfterReturn"
+ description: "Whether to detect problems with stack use after return from a function"
+ allowedValues: ["never", "runtime", "always"]
+ }
+
+ property bool detectUseAfterScope: true
+
+ cpp.driverFlags: {
+ var flags = [];
+ if (!_enabled)
+ return flags;
+ if (qbs.toolchain.includes("msvc") && !qbs.toolchain.includes("clang-cl")) {
+ flags.push("/fsanitize=address");
+ if (detectUseAfterReturn !== "never")
+ flags.push("/fsanitize-address-use-after-return");
+ return flags;
+ }
+ flags.push("-fsanitize=address", "-fno-omit-frame-pointer");
+ if (detectUseAfterScope)
+ flags.push("-fsanitize-address-use-after-scope");
+ if (detectUseAfterReturn) {
+ if (qbs.toolchain.includes("llvm")) {
+ var minVersion = qbs.toolchain.contains("xcode") ? "14" : "13";
+ if (Utilities.versionCompare(cpp.compilerVersion, minVersion) >= 0)
+ flags.push("-fsanitize-address-use-after-return=" + detectUseAfterReturn);
+ } else if (detectUseAfterReturn === "never") {
+ flags.push("--param", "asan-use-after-return=0");
+ }
+ }
+ return flags;
+ }
+}
diff --git a/share/qbs/modules/archiver/archiver.qbs b/share/qbs/modules/archiver/archiver.qbs
index 6ae53dd37..069e76717 100644
--- a/share/qbs/modules/archiver/archiver.qbs
+++ b/share/qbs/modules/archiver/archiver.qbs
@@ -30,6 +30,7 @@
import qbs.Environment
import qbs.File
+import qbs.Host
import qbs.FileInfo
import qbs.Probes
@@ -53,7 +54,7 @@ Module {
names: ["7z"]
platformSearchPaths: {
var paths = base;
- if (qbs.hostOS.contains("windows")) {
+ if (Host.os().includes("windows")) {
var env32 = Environment.getEnv("PROGRAMFILES(X86)");
var env64 = Environment.getEnv("PROGRAMFILES");
if (env64 === env32 && env64.endsWith(" (x86)"))
@@ -211,7 +212,7 @@ Module {
args.push("-0");
} else {
compression = compression === "bz2" ? "bzip2" : compression;
- if (["store", "deflate", "bzip2"].contains(compression))
+ if (["store", "deflate", "bzip2"].includes(compression))
args.push("-Z", compression);
if (compressionLevel)
@@ -220,7 +221,7 @@ Module {
args.push("-r", output.filePath, ".", "-i@" + input.filePath);
args = args.concat(product.moduleProperty("archiver", "flags"));
- } else if (["tar", "zip", "jar"].contains(binaryName)) {
+ } else if (["tar", "zip", "jar"].includes(binaryName)) {
throw binaryName + ": unrecognized archive type: '" + type + "'";
} else if (binaryName) {
throw "unrecognized archive tool: '" + binaryName + "'";
@@ -229,7 +230,7 @@ Module {
}
var archiverCommand = new Command(binary, args);
- archiverCommand.description = "Creating archive file " + output.fileName;
+ archiverCommand.description = "creating archive file " + output.fileName;
archiverCommand.highlight = "linker";
archiverCommand.workingDirectory
= product.moduleProperty("archiver", "workingDirectory");
diff --git a/share/qbs/modules/bundle/BundleModule.qbs b/share/qbs/modules/bundle/BundleModule.qbs
index 1e83dc458..f640fd55f 100644
--- a/share/qbs/modules/bundle/BundleModule.qbs
+++ b/share/qbs/modules/bundle/BundleModule.qbs
@@ -33,25 +33,29 @@ import qbs.DarwinTools
import qbs.Environment
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.PropertyList
import qbs.TextFile
import qbs.Utilities
import "bundle.js" as Bundle
+import "../codesign/codesign.js" as Codesign
Module {
Depends { name: "xcode"; required: false; }
+ Depends { name: "codesign"; required: false; }
Probe {
id: bundleSettingsProbe
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
property string xcodeDeveloperPath: xcode.developerPath
property var xcodeArchSettings: xcode._architectureSettings
property string productTypeIdentifier: _productTypeIdentifier
- property bool useXcodeBuildSpecs: _useXcodeBuildSpecs
- property bool isMacOs: qbs.targetOS.contains("macos")
+ property bool useXcodeBuildSpecs: !useBuiltinXcodeBuildSpecs
+ property bool isMacOs: qbs.targetOS.includes("macos")
property bool xcodePresent: xcode.present
+ property string xcodeVersion: xcode.version
// 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
@@ -68,8 +72,11 @@ Module {
"PRODUCT_NAME": product.targetName,
"LOCAL_APPS_DIR": Environment.getEnv("HOME") + "/Applications",
"LOCAL_LIBRARY_DIR": Environment.getEnv("HOME") + "/Library",
+ // actually, this is cpp.targetAbi, but XCode does not set it for non-simulator builds
+ // while Qbs set it to "macho".
+ "LLVM_TARGET_TRIPLE_SUFFIX": qbs.targetOS.includes("simulator") ? "-simulator" : "",
"SWIFT_PLATFORM_TARGET_PREFIX": isMacOs ? "macos"
- : qbs.targetOS.contains("ios") ? "ios" : "",
+ : qbs.targetOS.includes("ios") ? "ios" : "",
"TARGET_BUILD_DIR": product.buildDirectory,
"WRAPPER_NAME": bundleName,
"WRAPPER_EXTENSION": extension
@@ -80,15 +87,14 @@ Module {
property var productTypeIdentifierChain: []
configure: {
- var specsPath = path;
+ var specsPaths = [path];
var specsSeparator = "-";
if (xcodeDeveloperPath && useXcodeBuildSpecs) {
- specsPath = xcodeDeveloperPath
- + "/Platforms/MacOSX.platform/Developer/Library/Xcode/Specifications";
+ specsPaths = Bundle.macOSSpecsPaths(xcodeVersion, xcodeDeveloperPath);
specsSeparator = " ";
}
- var reader = new Bundle.XcodeBuildSpecsReader(specsPath,
+ var reader = new Bundle.XcodeBuildSpecsReader(specsPaths,
specsSeparator,
additionalSettings,
!isMacOs);
@@ -112,7 +118,7 @@ Module {
additionalProductTypes: !(product.multiplexed || product.aggregate)
|| !product.multiplexConfigurationId ? ["bundle.content"] : []
- property bool isBundle: !product.consoleApplication && qbs.targetOS.contains("darwin")
+ property bool isBundle: !product.consoleApplication && qbs.targetOS.includes("darwin")
readonly property bool isShallow: bundleSettingsProbe.xcodeSettings["SHALLOW_BUNDLE"] === "YES"
@@ -146,7 +152,7 @@ Module {
property var infoPlist
property bool processInfoPlist: true
property bool embedInfoPlist: product.consoleApplication && !isBundle
- property string infoPlistFormat: qbs.targetOS.contains("macos") ? "same-as-input" : "binary1"
+ property string infoPlistFormat: qbs.targetOS.includes("macos") ? "same-as-input" : "binary1"
property string localizedResourcesFolderSuffix: ".lproj"
@@ -181,13 +187,20 @@ Module {
readonly property string unlocalizedResourcesFolderPath: bundleSettingsProbe.xcodeSettings["UNLOCALIZED_RESOURCES_FOLDER_PATH"]
readonly property string versionsFolderPath: bundleSettingsProbe.xcodeSettings["VERSIONS_FOLDER_PATH"]
+ property bool useBuiltinXcodeBuildSpecs: !_useXcodeBuildSpecs // true to use ONLY the qbs build specs
+
// private properties
property string _productTypeIdentifier: Bundle.productTypeIdentifier(product.type)
property stringList _productTypeIdentifierChain: bundleSettingsProbe.productTypeIdentifierChain
- property bool _useXcodeBuildSpecs: true // false to use ONLY the qbs build specs
+ readonly property path _developerPath: xcode.developerPath
+ readonly property path _platformInfoPlist: xcode.platformInfoPlist
+ readonly property path _sdkSettingsPlist: xcode.sdkSettingsPlist
+ readonly property path _toolchainInfoPlist: xcode.toolchainInfoPlist
+
+ property bool _useXcodeBuildSpecs: true // TODO: remove in 1.25
- readonly property var extraEnv: ({
+ property var extraEnv: ({
"PRODUCT_BUNDLE_IDENTIFIER": identifier
})
@@ -219,7 +232,7 @@ Module {
}
validate: {
- if (!qbs.targetOS.contains("darwin"))
+ if (!qbs.targetOS.includes("darwin"))
return;
if (!bundleSettingsProbe.found) {
var error = "Bundle product type " + _productTypeIdentifier + " is not supported.";
@@ -265,7 +278,7 @@ Module {
}
Rule {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
multiplex: true
requiresInputs: false // TODO: The resources property should probably be a tag instead.
inputs: ["infoplist", "partial_infoplist"]
@@ -273,8 +286,8 @@ Module {
outputFileTags: ["bundle.input", "aggregate_infoplist"]
outputArtifacts: {
var artifacts = [];
- var embed = ModUtils.moduleProperty(product, "embedInfoPlist");
- if (ModUtils.moduleProperty(product, "isBundle") || embed) {
+ var embed = product.bundle.embedInfoPlist;
+ if (product.bundle.isBundle || embed) {
artifacts.push({
filePath: FileInfo.joinPaths(
product.destinationDirectory, product.name + "-Info.plist"),
@@ -282,7 +295,7 @@ Module {
bundle: {
_bundleFilePath: FileInfo.joinPaths(
product.destinationDirectory,
- ModUtils.moduleProperty(product, "infoPlistPath")),
+ product.bundle.infoPlistPath),
}
});
}
@@ -293,20 +306,21 @@ Module {
var cmd = new JavaScriptCommand();
cmd.description = "generating Info.plist for " + product.name;
cmd.highlight = "codegen";
- 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.infoPlist = product.bundle.infoPlist || {};
+ cmd.processInfoPlist = product.bundle.processInfoPlist;
+ cmd.infoPlistFormat = product.bundle.infoPlistFormat;
+ cmd.extraEnv = product.bundle.extraEnv;
+ cmd.qmakeEnv = product.bundle.qmakeEnv;
+ // TODO: bundle module should know nothing about cpp module
cmd.buildEnv = product.moduleProperty("cpp", "buildEnv");
- 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.developerPath = product.bundle._developerPath;
+ cmd.platformInfoPlist = product.bundle._platformInfoPlist;
+ cmd.sdkSettingsPlist = product.bundle._sdkSettingsPlist;
+ cmd.toolchainInfoPlist = product.bundle._toolchainInfoPlist;
- cmd.osBuildVersion = product.moduleProperty("qbs", "hostOSBuildVersion");
+ cmd.osBuildVersion = product.qbs.hostOSBuildVersion;
cmd.sourceCode = function() {
var plist, process, key, i;
@@ -335,7 +349,7 @@ Module {
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");
+ var defaultValues = product.bundle.defaultInfoPlist;
for (key in defaultValues) {
if (defaultValues.hasOwnProperty(key) && !(key in aggregatePlist))
aggregatePlist[key] = defaultValues[key];
@@ -447,7 +461,7 @@ Module {
infoPlistFormat = "xml1";
var validFormats = [ "xml1", "binary1", "json" ];
- if (!validFormats.contains(infoPlistFormat))
+ if (!validFormats.includes(infoPlistFormat))
throw("Invalid Info.plist format " + infoPlistFormat + ". " +
"Must be in [xml1, binary1, json].");
@@ -465,18 +479,18 @@ Module {
}
Rule {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
multiplex: true
inputs: ["aggregate_infoplist"]
outputFileTags: ["bundle.input", "pkginfo"]
outputArtifacts: {
var artifacts = [];
- if (ModUtils.moduleProperty(product, "isBundle") && ModUtils.moduleProperty(product, "generatePackageInfo")) {
+ if (product.bundle.isBundle && product.bundle.generatePackageInfo) {
artifacts.push({
filePath: FileInfo.joinPaths(product.destinationDirectory, "PkgInfo"),
fileTags: ["bundle.input", "pkginfo"],
- bundle: { _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "pkgInfoPath")) }
+ bundle: { _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.pkgInfoPath) }
});
}
return artifacts;
@@ -506,13 +520,13 @@ Module {
}
Rule {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
multiplex: true
inputs: ["bundle.input",
"aggregate_infoplist", "pkginfo", "hpp",
- "icns", "xcent",
+ "icns", "codesign.xcent",
"compiled_ibdoc", "compiled_assetcatalog",
- "xcode.provisioningprofile.main"]
+ "codesign.embedded_provisioningprofile"]
// Make sure the inputs of this rule are only those rules which produce outputs compatible
// with the type of the bundle being produced.
@@ -523,15 +537,16 @@ Module {
"bundle.symlink.headers", "bundle.symlink.private-headers",
"bundle.symlink.resources", "bundle.symlink.executable",
"bundle.symlink.version", "bundle.hpp", "bundle.resource",
- "bundle.provisioningprofile", "bundle.content.copied", "bundle.application-executable"]
+ "bundle.provisioningprofile", "bundle.content.copied", "bundle.application-executable",
+ "bundle.code-signature"]
outputArtifacts: {
var i, artifacts = [];
- if (ModUtils.moduleProperty(product, "isBundle")) {
+ if (product.bundle.isBundle) {
for (i in inputs["bundle.input"]) {
- var fp = inputs["bundle.input"][i].moduleProperty("bundle", "_bundleFilePath");
+ var fp = inputs["bundle.input"][i].bundle._bundleFilePath;
if (!fp)
throw("Artifact " + inputs["bundle.input"][i].filePath + " has no associated bundle file path");
- var extraTags = inputs["bundle.input"][i].fileTags.contains("application")
+ var extraTags = inputs["bundle.input"][i].fileTags.includes("application")
? ["bundle.application-executable"] : [];
artifacts.push({
filePath: fp,
@@ -539,24 +554,23 @@ Module {
});
}
- for (i in inputs["xcode.provisioningprofile.main"]) {
- var ext = inputs["xcode.provisioningprofile.main"][i].fileName.split('.')[1];
+ var provprofiles = inputs["codesign.embedded_provisioningprofile"];
+ for (i in provprofiles) {
artifacts.push({
filePath: FileInfo.joinPaths(product.destinationDirectory,
- ModUtils.moduleProperty(product,
- "contentsFolderPath"),
- "embedded." + ext),
+ product.bundle.contentsFolderPath,
+ provprofiles[i].fileName),
fileTags: ["bundle.provisioningprofile", "bundle.content"]
});
}
- var packageType = ModUtils.moduleProperty(product, "packageType");
- var isShallow = ModUtils.moduleProperty(product, "isShallow");
+ var packageType = product.bundle.packageType;
+ var isShallow = product.bundle.isShallow;
if (packageType === "FMWK" && !isShallow) {
- var publicHeaders = ModUtils.moduleProperty(product, "publicHeaders");
+ var publicHeaders = product.bundle.publicHeaders;
if (publicHeaders && publicHeaders.length) {
artifacts.push({
- filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "Headers"),
+ filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, "Headers"),
fileTags: ["bundle.symlink.headers", "bundle.content"]
});
}
@@ -564,23 +578,23 @@ Module {
var privateHeaders = ModUtils.moduleProperty(product, "privateHeaders");
if (privateHeaders && privateHeaders.length) {
artifacts.push({
- filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "PrivateHeaders"),
+ filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, "PrivateHeaders"),
fileTags: ["bundle.symlink.private-headers", "bundle.content"]
});
}
artifacts.push({
- filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), "Resources"),
+ filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, "Resources"),
fileTags: ["bundle.symlink.resources", "bundle.content"]
});
artifacts.push({
- filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "bundleName"), product.targetName),
+ filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName, product.targetName),
fileTags: ["bundle.symlink.executable", "bundle.content"]
});
artifacts.push({
- filePath: FileInfo.joinPaths(product.destinationDirectory, ModUtils.moduleProperty(product, "versionsFolderPath"), "Current"),
+ filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.versionsFolderPath, "Current"),
fileTags: ["bundle.symlink.version", "bundle.content"]
});
}
@@ -597,7 +611,7 @@ Module {
}
}
- sources = ModUtils.moduleProperty(product, "resources");
+ sources = product.bundle.resources;
for (i in sources) {
destination = BundleTools.destinationDirectoryForResource(product, {baseDir: FileInfo.path(sources[i]), fileName: FileInfo.fileName(sources[i])});
artifacts.push({
@@ -608,16 +622,24 @@ Module {
var wrapperPath = FileInfo.joinPaths(
product.destinationDirectory,
- ModUtils.moduleProperty(product, "bundleName"));
+ product.bundle.bundleName);
for (var i = 0; i < artifacts.length; ++i)
artifacts[i].bundle = { wrapperPath: wrapperPath };
+
+ if (Host.os().includes("darwin") && product.codesign
+ && product.codesign.enableCodeSigning) {
+ artifacts.push({
+ filePath: FileInfo.joinPaths(product.bundle.contentsFolderPath, "_CodeSignature/CodeResources"),
+ fileTags: ["bundle.code-signature", "bundle.content"]
+ });
+ }
}
return artifacts;
}
prepare: {
var i, cmd, commands = [];
- var packageType = ModUtils.moduleProperty(product, "packageType");
+ var packageType = product.bundle.packageType;
var bundleType = "bundle";
if (packageType === "APPL")
@@ -625,19 +647,8 @@ Module {
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) {
+ if (!product.bundle.isBundle) {
cmd = new JavaScriptCommand();
cmd.silent = true;
cmd.sourceCode = function () { };
@@ -646,7 +657,7 @@ Module {
var symlinks = outputs["bundle.symlink.version"];
for (i in symlinks) {
- cmd = new Command("ln", ["-sfn", ModUtils.moduleProperty(product, "frameworkVersion"),
+ cmd = new Command("ln", ["-sfn", product.bundle.frameworkVersion,
symlinks[i].filePath]);
cmd.silent = true;
commands.push(cmd);
@@ -693,8 +704,8 @@ Module {
}
var bundleInputs = sortedArtifactList(inputs["bundle.input"], function (a, b) {
- return a.moduleProperty("bundle", "_bundleFilePath").localeCompare(
- b.moduleProperty("bundle", "_bundleFilePath"));
+ return a.bundle._bundleFilePath.localeCompare(
+ b.bundle._bundleFilePath);
});
var bundleContents = sortedArtifactList(outputs["bundle.content.copied"]);
for (i in bundleContents) {
@@ -708,24 +719,27 @@ Module {
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);
- };
+ cmd = new JavaScriptCommand();
+ cmd.description = "copying provisioning profile";
+ cmd.highlight = "filegen";
+ cmd.sources = (inputs["codesign.embedded_provisioningprofile"] || [])
+ .map(function(artifact) { return artifact.filePath; });
+ cmd.destination = (outputs["bundle.provisioningprofile"] || [])
+ .map(function(artifact) { return artifact.filePath; });
+ cmd.sourceCode = function() {
+ var i;
+ for (var i in sources) {
+ File.copy(sources[i], destination[i]);
+ }
+ };
+ if (cmd.sources && cmd.sources.length)
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.sources = product.bundle.publicHeaders;
+ cmd.destination = FileInfo.joinPaths(product.destinationDirectory, product.bundle.publicHeadersFolderPath);
cmd.sourceCode = function() {
var i;
for (var i in sources) {
@@ -738,8 +752,8 @@ Module {
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.sources = product.bundle.privateHeaders;
+ cmd.destination = FileInfo.joinPaths(product.destinationDirectory, product.bundle.privateHeadersFolderPath);
cmd.sourceCode = function() {
var i;
for (var i in sources) {
@@ -752,7 +766,7 @@ Module {
cmd = new JavaScriptCommand();
cmd.description = "copying resources";
cmd.highlight = "filegen";
- cmd.sources = ModUtils.moduleProperty(product, "resources");
+ cmd.sources = product.bundle.resources;
cmd.sourceCode = function() {
var i;
for (var i in sources) {
@@ -763,45 +777,18 @@ Module {
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 (bundleType === "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);
- }
+ if (product.qbs.hostOS.includes("darwin")) {
+ Array.prototype.push.apply(commands, Codesign.prepareSign(
+ project, product, inputs, outputs, input, output));
+
+ if (bundleType === "application"
+ && product.qbs.targetOS.includes("macos")) {
+ var bundlePath = FileInfo.joinPaths(
+ product.destinationDirectory, product.bundle.bundleName);
+ cmd = new Command(product.bundle.lsregisterPath,
+ ["-f", bundlePath]);
+ cmd.description = "registering " + product.bundle.bundleName;
+ commands.push(cmd);
}
}
diff --git a/share/qbs/modules/bundle/MacOSX-Package-Types.xcspec b/share/qbs/modules/bundle/MacOSX-Package-Types.xcspec
index b36353fc7..23f094641 100644
--- a/share/qbs/modules/bundle/MacOSX-Package-Types.xcspec
+++ b/share/qbs/modules/bundle/MacOSX-Package-Types.xcspec
@@ -1,462 +1,533 @@
-[
- {
- "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)"
+/**
+ MacOSX Package Types.xcspec
+
+ Copyright (c) 1999-2015 Apple Inc. All rights reserved.
+
+ Package type specifications in the Mac OS X platform.
+*/
+(
+ // Mach-O executable
+ { Type = PackageType;
+ Identifier = com.apple.package-type.mach-o-executable;
+ Name = "Mach-O Executable";
+ Description = "Mach-O executable";
+ DefaultBuildSettings = {
+ EXECUTABLE_PREFIX = "";
+ EXECUTABLE_SUFFIX = "";
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)";
+ EXECUTABLE_PATH = "$(EXECUTABLE_NAME)";
+ };
+ ProductReference = {
+ FileType = compiled.mach-o.executable;
+ Name = "$(EXECUTABLE_NAME)";
+ IsLaunchable = YES;
+ };
},
- "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"
+
+ // Mach-O object file
+ { Type = PackageType;
+ Identifier = com.apple.package-type.mach-o-objfile;
+ Name = "Mach-O Object File";
+ Description = "Mach-O Object File";
+ DefaultBuildSettings = {
+ EXECUTABLE_PREFIX = "";
+ EXECUTABLE_SUFFIX = "";
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)";
+ EXECUTABLE_PATH = "$(EXECUTABLE_NAME)";
+ };
+ ProductReference = {
+ FileType = compiled.mach-o.objfile;
+ Name = "$(EXECUTABLE_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"
+
+ // Mach-O dynamic library
+ { Type = PackageType;
+ Identifier = com.apple.package-type.mach-o-dylib;
+ Name = "Mach-O Dynamic Library";
+ Description = "Mach-O dynamic library";
+ DefaultBuildSettings = {
+ EXECUTABLE_PREFIX = "";
+ EXECUTABLE_SUFFIX = "";
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)";
+ EXECUTABLE_PATH = "$(EXECUTABLE_NAME)";
+ };
+ ProductReference = {
+ FileType = compiled.mach-o.dylib;
+ Name = "$(EXECUTABLE_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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"
+
+ // Static library ('ar' archive containing .o files)
+ { Type = PackageType;
+ Identifier = com.apple.package-type.static-library;
+ Name = "Mach-O Static Library";
+ Description = "Mach-O static library";
+ DefaultBuildSettings = {
+ EXECUTABLE_PREFIX = "lib";
+ EXECUTABLE_SUFFIX = ".a";
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)";
+ EXECUTABLE_PATH = "$(EXECUTABLE_NAME)";
+ };
+ ProductReference = {
+ FileType = archive.ar;
+ Name = "$(EXECUTABLE_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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"
+
+ // Mach-O bundle (not related to a CFBundle)
+ { Type = PackageType;
+ Identifier = com.apple.package-type.mach-o-bundle;
+ Name = "Mach-O Loadable";
+ Description = "Mach-O loadable";
+ DefaultBuildSettings = {
+ EXECUTABLE_PREFIX = "";
+ EXECUTABLE_SUFFIX = ".dylib";
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)";
+ EXECUTABLE_PATH = "$(EXECUTABLE_NAME)";
+ };
+ ProductReference = {
+ FileType = compiled.mach-o.bundle;
+ Name = "$(EXECUTABLE_NAME)";
+ IsLaunchable = NO;
+ };
},
- "DefaultBuildSettings" : {
- "EXECUTABLE_SUFFIX" : "",
- "EXECUTABLE_NAME" : "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)",
- "EXECUTABLE_PREFIX" : ""
+
+ // CFBundle wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.wrapper;
+ Name = "Wrapper";
+ Description = "Wrapper";
+ DefaultBuildSettings = {
+ PRODUCT_BUNDLE_PACKAGE_TYPE = "BNDL";
+ WRAPPER_PREFIX = "";
+ WRAPPER_SUFFIX = ".bundle";
+ WRAPPER_NAME = "$(WRAPPER_PREFIX)$(PRODUCT_NAME)$(WRAPPER_SUFFIX)";
+ CONTENTS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH_SHALLOW_BUNDLE_$(SHALLOW_BUNDLE))";
+ EXECUTABLE_PREFIX = "";
+ EXECUTABLE_SUFFIX = "";
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_VARIANT_SUFFIX)$(EXECUTABLE_SUFFIX)";
+ EXECUTABLE_FOLDER_PATH = "$(EXECUTABLE_FOLDER_PATH_SHALLOW_BUNDLE_$(SHALLOW_BUNDLE))";
+ EXECUTABLE_PATH = "$(EXECUTABLE_FOLDER_PATH)/$(EXECUTABLE_NAME)";
+ INFOPLIST_PATH = "$(CONTENTS_FOLDER_PATH)/Info.plist";
+ INFOSTRINGS_PATH = "$(LOCALIZED_RESOURCES_FOLDER_PATH)/InfoPlist.strings";
+ PKGINFO_PATH = "$(CONTENTS_FOLDER_PATH)/PkgInfo";
+ PBDEVELOPMENTPLIST_PATH = "$(CONTENTS_FOLDER_PATH)/pbdevelopment.plist";
+ VERSIONPLIST_PATH = "$(CONTENTS_FOLDER_PATH)/version.plist";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Headers";
+ PRIVATE_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/PrivateHeaders";
+ EXECUTABLES_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Executables"; // Not the same as EXECUTABLE_FOLDER_PATH
+ FRAMEWORKS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Frameworks";
+ SHARED_FRAMEWORKS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/SharedFrameworks";
+ SHARED_SUPPORT_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/SharedSupport";
+ UNLOCALIZED_RESOURCES_FOLDER_PATH = "$(UNLOCALIZED_RESOURCES_FOLDER_PATH_SHALLOW_BUNDLE_$(SHALLOW_BUNDLE))";
+ LOCALIZED_RESOURCES_FOLDER_PATH = "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/$(DEVELOPMENT_LANGUAGE).lproj";
+ DOCUMENTATION_FOLDER_PATH = "$(LOCALIZED_RESOURCES_FOLDER_PATH)/Documentation";
+ MODULES_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Modules";
+ PLUGINS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/PlugIns";
+ SCRIPTS_FOLDER_PATH = "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/Scripts";
+ JAVA_FOLDER_PATH = "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/Java";
+ SYSTEM_EXTENSIONS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Library/SystemExtensions";
+
+ // Settings specific to deep bundles (macOS)
+ CONTENTS_FOLDER_PATH_SHALLOW_BUNDLE_NO = "$(WRAPPER_NAME)/Contents";
+ EXECUTABLE_FOLDER_PATH_SHALLOW_BUNDLE_NO = "$(CONTENTS_FOLDER_PATH)/MacOS";
+ UNLOCALIZED_RESOURCES_FOLDER_PATH_SHALLOW_BUNDLE_NO = "$(CONTENTS_FOLDER_PATH)/Resources";
+
+ // Settings specific to shallow bundles (iOS, DriverKit on any OS)
+ CONTENTS_FOLDER_PATH_SHALLOW_BUNDLE_YES = "$(WRAPPER_NAME)";
+ EXECUTABLE_FOLDER_PATH_SHALLOW_BUNDLE_YES = "$(CONTENTS_FOLDER_PATH)";
+ UNLOCALIZED_RESOURCES_FOLDER_PATH_SHALLOW_BUNDLE_YES = "$(CONTENTS_FOLDER_PATH)";
+
+ // DriverKit should always use shallow bundles
+ SHALLOW_BUNDLE = "$(SHALLOW_BUNDLE_$(SWIFT_PLATFORM_TARGET_PREFIX))";
+ SHALLOW_BUNDLE_ = YES;
+ SHALLOW_BUNDLE_macos = NO;
+ SHALLOW_BUNDLE_ios = NO; // for macCatalyst
+ SHALLOW_BUNDLE_driverkit = YES;
+ };
+ ProductReference = {
+ FileType = wrapper.cfbundle;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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"
+
+ // Shallow CFBundle wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.wrapper.shallow;
+ BasedOn = com.apple.package-type.wrapper;
+ Name = "Wrapper (Shallow)";
+ Description = "Shallow Wrapper";
+ DefaultBuildSettings = {
+ SHALLOW_BUNDLE = YES;
+ };
+ ProductReference = {
+ FileType = wrapper.cfbundle;
+ 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)"
+
+ // Application wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.wrapper.application;
+ BasedOn = com.apple.package-type.wrapper;
+ Name = "Application Wrapper";
+ Description = "Application Wrapper";
+ DefaultBuildSettings = {
+ PRODUCT_BUNDLE_PACKAGE_TYPE = "APPL";
+ GENERATE_PKGINFO_FILE = YES;
+ };
+ ProductReference = {
+ FileType = wrapper.application;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = YES;
+ };
},
- "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"
+
+ // Shallow Application wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.wrapper.application.shallow;
+ BasedOn = com.apple.package-type.wrapper.shallow;
+ Name = "Application Wrapper (Shallow)";
+ Description = "Shallow Application Wrapper";
+ DefaultBuildSettings = {
+ PRODUCT_BUNDLE_PACKAGE_TYPE = "APPL";
+ GENERATE_PKGINFO_FILE = YES;
+ SHALLOW_BUNDLE = YES;
+ };
+ ProductReference = {
+ FileType = wrapper.application;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = YES;
+ };
},
- "DefaultBuildSettings" : {
- "WRAPPER_SUFFIX" : "xctest"
+
+ // System extension wrapper
+ {
+ Type = PackageType;
+ Identifier = com.apple.package-type.wrapper.system-extension;
+ BasedOn = com.apple.package-type.wrapper;
+ Name = "System Extension Wrapper";
+ Description = "System Extension Wrapper";
+ DefaultBuildSettings = {
+ PRODUCT_BUNDLE_PACKAGE_TYPE = "SYSX";
+ };
+ ProductReference = {
+ FileType = wrapper.system-extension;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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"
+
+ // Driver extension wrapper
+ {
+ Type = PackageType;
+ Identifier = com.apple.package-type.wrapper.driver-extension;
+ BasedOn = com.apple.package-type.wrapper.system-extension;
+ Name = "Driver Extension Wrapper";
+ Description = "Driver Extension Wrapper";
+ DefaultBuildSettings = {
+ PRODUCT_BUNDLE_PACKAGE_TYPE = "DEXT";
+ };
+ ProductReference = {
+ FileType = wrapper.driver-extension;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "DefaultBuildSettings" : {
- "WRAPPER_SUFFIX" : "octest"
+
+ // Kernel extension wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.wrapper.kernel-extension;
+ BasedOn = com.apple.package-type.wrapper;
+ Name = "Kernel Extension Wrapper";
+ Description = "Kernel Extension Wrapper";
+ DefaultBuildSettings = {
+ PRODUCT_BUNDLE_PACKAGE_TYPE = "KEXT";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(KEXT_FRAMEWORK)/Contents/Headers/$(KEXT_FAMILY_NAME)";
+ PRIVATE_HEADERS_FOLDER_PATH = "$(KEXT_FRAMEWORK)/Contents/PrivateHeaders/$(KEXT_FAMILY_NAME)";
+ };
+ ProductReference = {
+ FileType = wrapper.cfbundle;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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"
+
+ // Shallow Kernel extension wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.wrapper.kernel-extension.shallow;
+ BasedOn = com.apple.package-type.wrapper.shallow;
+ Name = "Kernel Extension Wrapper (Shallow)";
+ Description = "Shallow Kernel Extension Wrapper";
+ DefaultBuildSettings = {
+ PRODUCT_BUNDLE_PACKAGE_TYPE = "KEXT";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(KEXT_FRAMEWORK)/Contents/Headers/$(KEXT_FAMILY_NAME)";
+ PRIVATE_HEADERS_FOLDER_PATH = "$(KEXT_FRAMEWORK)/Contents/PrivateHeaders/$(KEXT_FAMILY_NAME)";
+ SHALLOW_BUNDLE = YES;
+ };
+ ProductReference = {
+ FileType = wrapper.cfbundle;
+ 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)"
+
+ // Framework wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.wrapper.framework;
+ BasedOn = com.apple.package-type.wrapper;
+ Name = "Framework Wrapper";
+ Description = "Framework wrapper";
+ DefaultBuildSettings = {
+ PRODUCT_BUNDLE_PACKAGE_TYPE = "FMWK";
+ WRAPPER_SUFFIX = ".framework";
+ VERSIONS_FOLDER_PATH = "$(VERSIONS_FOLDER_PATH_SHALLOW_BUNDLE_$(SHALLOW_BUNDLE))";
+ CURRENT_VERSION = "Current";
+ INFOPLIST_PATH = "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/Info.plist";
+ PKGINFO_PATH = "$(WRAPPER_NAME)/PkgInfo";
+ VERSIONPLIST_PATH = "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/version.plist";
+ EXECUTABLES_FOLDER_PATH = "$(LOCALIZED_RESOURCES_FOLDER_PATH)"; // Not the same as EXECUTABLE_FOLDER_PATH
+ SHARED_SUPPORT_FOLDER_PATH = "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)";
+ CODESIGNING_FOLDER_PATH = "$(TARGET_BUILD_DIR)/$(CONTENTS_FOLDER_PATH)";
+
+ // Settings specific to deep bundles (macOS)
+ VERSIONS_FOLDER_PATH_SHALLOW_BUNDLE_NO = "$(WRAPPER_NAME)/Versions";
+ CONTENTS_FOLDER_PATH_SHALLOW_BUNDLE_NO = "$(VERSIONS_FOLDER_PATH)/$(FRAMEWORK_VERSION)";
+ EXECUTABLE_FOLDER_PATH_SHALLOW_BUNDLE_NO = "$(CONTENTS_FOLDER_PATH)";
+
+ // Settings specific to shallow bundles (iOS, DriverKit on any OS)
+ VERSIONS_FOLDER_PATH_SHALLOW_BUNDLE_YES = "$(WRAPPER_NAME)";
+ };
+ ProductReference = {
+ FileType = wrapper.framework;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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"
+
+ // Static framework wrapper (like a framework, except that it contains a libX.a instead of a dylib)
+ { Type = PackageType;
+ Identifier = com.apple.package-type.wrapper.framework.static;
+ Name = "Mach-O Static Framework";
+ Description = "Mach-O static framework";
+ BasedOn = com.apple.package-type.wrapper.framework;
+ DefaultBuildSettings = {
+ };
+ ProductReference = {
+ FileType = wrapper.framework.static;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "DefaultBuildSettings" : {
- "WRAPPER_SUFFIX" : ".xpc"
+
+ // Shallow Framework Package
+ { Type = PackageType;
+ Identifier = com.apple.package-type.wrapper.framework.shallow;
+ Name = "Shallow Framework Wrapper";
+ Description = "Shallow framework wrapper";
+ BasedOn = com.apple.package-type.wrapper.framework;
+ DefaultBuildSettings = {
+ SHALLOW_BUNDLE = YES;
+ };
+ ProductReference = {
+ FileType = wrapper.framework;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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"
+
+ // Unit Test Bundle wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.bundle.unit-test;
+ BasedOn = com.apple.package-type.wrapper;
+ Name = "Unit Test Bundle";
+ Description = "Unit Test Bundle";
+ DefaultBuildSettings = {
+ WRAPPER_SUFFIX = "xctest";
+ };
+ ProductReference = {
+ FileType = wrapper.cfbundle;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "DefaultBuildSettings" : {
- "WRAPPER_SUFFIX" : ".pluginkit"
+
+ // Legacy OCUnit Test Bundle wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.bundle.ocunit-test;
+ BasedOn = com.apple.package-type.wrapper;
+ Name = "OCUnit Test Bundle";
+ Description = "OCUnit Test Bundle";
+ DefaultBuildSettings = {
+ WRAPPER_SUFFIX = "octest";
+ };
+ ProductReference = {
+ FileType = wrapper.cfbundle;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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"
+
+ // In-app Purchase Content wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.in-app-purchase-content;
+ BasedOn = com.apple.package-type.wrapper;
+ Name = "In-App Purchase Content";
+ Description = "In-App Purchase Content";
+ DefaultBuildSettings = {
+ PRODUCT_BUNDLE_PACKAGE_TYPE = "";
+ WRAPPER_SUFFIX = "";
+ EXECUTABLE_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)";
+ INFOPLIST_PATH = "$(WRAPPER_NAME)/ContentInfo.plist";
+ INFOSTRINGS_PATH = "$(LOCALIZED_RESOURCES_FOLDER_PATH)/ContentInfo.strings";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)";
+ PRIVATE_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)";
+ EXECUTABLES_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)";
+ FRAMEWORKS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)";
+ SHARED_FRAMEWORKS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)";
+ SHARED_SUPPORT_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)";
+ UNLOCALIZED_RESOURCES_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)";
+ LOCALIZED_RESOURCES_FOLDER_PATH = "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)/$(DEVELOPMENT_LANGUAGE).lproj";
+ DOCUMENTATION_FOLDER_PATH = "$(LOCALIZED_RESOURCES_FOLDER_PATH)";
+ MODULES_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)";
+ PLUGINS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)";
+ SCRIPTS_FOLDER_PATH = "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)";
+ JAVA_FOLDER_PATH = "$(UNLOCALIZED_RESOURCES_FOLDER_PATH)";
+ };
+ ProductReference = {
+ FileType = folder;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "DefaultBuildSettings" : {
- "WRAPPER_SUFFIX" : ".appex"
+
+ // XPC Service wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.xpc-service;
+ BasedOn = com.apple.package-type.wrapper;
+ Name = "XPC Service";
+ Description = "XPC Service";
+ DefaultBuildSettings = {
+ PRODUCT_BUNDLE_PACKAGE_TYPE = "XPC!";
+ WRAPPER_SUFFIX = ".xpc";
+ };
+ ProductReference = {
+ FileType = wrapper.xpc-service;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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"
+
+ // PlugInKit PlugIn wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.pluginkit-plugin;
+ BasedOn = com.apple.package-type.xpc-service;
+ Name = "PlugInKit PlugIn";
+ Description = "PlugInKit PlugIn";
+ DefaultBuildSettings = {
+ WRAPPER_SUFFIX = ".pluginkit";
+ };
+ ProductReference = {
+ FileType = wrapper.app-extension;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "DefaultBuildSettings" : {
+
+ // App Extension wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.app-extension;
+ BasedOn = com.apple.package-type.pluginkit-plugin;
+ Name = "App Extension";
+ Description = "App Extension";
+ DefaultBuildSettings = {
+ WRAPPER_SUFFIX = ".appex";
+ };
+ ProductReference = {
+ FileType = wrapper.app-extension;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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)"
+
+ // Spotlight Importer wrapper
+ { Type = PackageType;
+ Identifier = com.apple.package-type.spotlight-importer;
+ BasedOn = com.apple.package-type.wrapper;
+ Name = "Spotlight Importer";
+ Description = "Spotlight Importer";
+ DefaultBuildSettings = {
+ };
+ ProductReference = {
+ FileType = wrapper.spotlight-importer;
+ Name = "$(WRAPPER_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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)"
+
+ // Jar file
+ { Type = PackageType;
+ Identifier = com.apple.package-type.jarfile;
+ Name = "Jar File";
+ Description = "Jar file";
+ DefaultBuildSettings = {
+ EXECUTABLE_PREFIX = "";
+ EXECUTABLE_SUFFIX = ".jar";
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_SUFFIX)";
+ EXECUTABLE_PATH = "$(EXECUTABLE_NAME)";
+ JAVA_ARCHIVE_CLASSES = YES;
+ JAVA_MAKE_ZIPFILE = NO;
+ };
+ ProductReference = {
+ FileType = archive.jar;
+ Name = "$(EXECUTABLE_NAME)";
+ IsLaunchable = NO;
+ };
},
- "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)"
+
+ // Zip file
+ { Type = PackageType;
+ Identifier = com.apple.package-type.zipfile;
+ Name = "Zip File";
+ Description = "Zip file";
+ DefaultBuildSettings = {
+ EXECUTABLE_PREFIX = "";
+ EXECUTABLE_SUFFIX = ".zip";
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_SUFFIX)";
+ EXECUTABLE_PATH = "$(EXECUTABLE_NAME)";
+ JAVA_ARCHIVE_CLASSES = YES;
+ JAVA_MAKE_ZIPFILE = YES;
+ };
+ ProductReference = {
+ FileType = archive.zip;
+ Name = "$(EXECUTABLE_NAME)";
+ IsLaunchable = NO;
+ };
},
- "Identifier" : "com.apple.package-type.javaclassfolder",
- "Type" : "PackageType",
- "Name" : "Class Folder",
- "Description" : "Class folder",
- "ProductReference" : {
- "FileType" : "wrapper.java-classfolder",
- "Name" : "$(EXECUTABLE_NAME)",
- "IsLaunchable" : "NO"
- }
- }
-]
+
+ // Java class folder
+ { Type = PackageType;
+ Identifier = com.apple.package-type.javaclassfolder;
+ Name = "Class Folder";
+ Description = "Class folder";
+ DefaultBuildSettings = {
+ EXECUTABLE_PREFIX = "";
+ EXECUTABLE_SUFFIX = "";
+ EXECUTABLE_NAME = "$(EXECUTABLE_PREFIX)$(PRODUCT_NAME)$(EXECUTABLE_SUFFIX)";
+ EXECUTABLE_PATH = "$(EXECUTABLE_NAME)";
+ JAVA_ARCHIVE_CLASSES = NO;
+ };
+ 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
index 8d8450869..bc1d087d6 100644
--- a/share/qbs/modules/bundle/MacOSX-Product-Types.xcspec
+++ b/share/qbs/modules/bundle/MacOSX-Product-Types.xcspec
@@ -1,590 +1,803 @@
-[
- {
- "IconNamePrefix" : "TargetExecutable",
- "DefaultBuildProperties" : {
- "REZ_EXECUTABLE" : "YES",
- "FULL_PRODUCT_NAME" : "$(EXECUTABLE_NAME)",
- "LIBRARY_FLAG_NOSPACE" : "YES",
- "FRAMEWORK_FLAG_PREFIX" : "-framework",
- "INSTALL_PATH" : "\/usr\/local\/bin",
- "GCC_INLINES_ARE_PRIVATE_EXTERN" : "YES",
- "GCC_DYNAMIC_NO_PIC" : "NO",
- "GCC_SYMBOLS_PRIVATE_EXTERN" : "YES",
- "CODE_SIGNING_ALLOWED" : "YES",
- "STRIP_STYLE" : "all",
- "EXECUTABLE_PREFIX" : "",
- "MACH_O_TYPE" : "mh_execute",
- "EXECUTABLE_SUFFIX" : "",
- "LIBRARY_FLAG_PREFIX" : "-l"
+//
+// MacOSX Product Types.xcspec
+//
+// Copyright © 1999-2020 Apple Inc. All rights reserved.
+//
+// Product type specifications in the macOS platform.
+//
+(
+ //
+ // Single-file product types
+ //
+
+ // Tool (normal Unix command-line executable)
+ { Type = ProductType;
+ Identifier = com.apple.product-type.tool;
+ Class = PBXToolProductType;
+ Name = "Command-line Tool";
+ Description = "Standalone command-line tool";
+ IconNamePrefix = "TargetExecutable";
+ DefaultTargetName = "Command-line Tool";
+ DefaultBuildProperties = {
+ FULL_PRODUCT_NAME = "$(EXECUTABLE_NAME)";
+ MACH_O_TYPE = "mh_execute";
+ EXECUTABLE_PREFIX = "";
+ EXECUTABLE_SUFFIX = "";
+ REZ_EXECUTABLE = YES;
+ INSTALL_PATH = "/usr/local/bin";
+ FRAMEWORK_FLAG_PREFIX = "-framework";
+ LIBRARY_FLAG_PREFIX = "-l";
+ LIBRARY_FLAG_NOSPACE = YES;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ STRIP_STYLE = "all";
+ CODE_SIGNING_ALLOWED = YES;
+ };
+ PackageTypes = (
+ com.apple.package-type.mach-o-executable // default
+ );
+ WantsSigningEditing = YES;
+ WantsBundleIdentifierEditing = YES;
},
- "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"
+
+ // Java tool
+ { Type = ProductType;
+ Identifier = com.apple.product-type.tool.java;
+ Name = "Java Command-line Tool";
+ Description = "Java Command-line tool";
+ IconNamePrefix = "TargetExecutable";
+ DefaultTargetName = "Java Command-line Tool";
+ DefaultBuildProperties = {
+ FULL_PRODUCT_NAME = "$(EXECUTABLE_NAME)";
+ REZ_EXECUTABLE = YES;
+ INSTALL_PATH = "/usr/local/bin";
+ };
+ IsJava = YES;
+ PackageTypes = (
+ com.apple.package-type.jarfile, // default
+ com.apple.package-type.zipfile,
+ com.apple.package-type.javaclassfolder
+ );
},
- "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"
+
+ // Object file
+ { Type = ProductType;
+ Identifier = com.apple.product-type.objfile;
+ Class = XCStandaloneExecutableProductType;
+ Name = "Object File";
+ Description = "Object File";
+ IconNamePrefix = "TargetPlugin";
+ DefaultTargetName = "Object File";
+ DefaultBuildProperties = {
+ FULL_PRODUCT_NAME = "$(EXECUTABLE_NAME)";
+ MACH_O_TYPE = "mh_object";
+ LINK_WITH_STANDARD_LIBRARIES = NO;
+ REZ_EXECUTABLE = YES;
+ EXECUTABLE_SUFFIX = ".$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_EXTENSION = "o";
+ PUBLIC_HEADERS_FOLDER_PATH = "/usr/local/include";
+ PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include";
+ INSTALL_PATH = "$(HOME)/Objects";
+ FRAMEWORK_FLAG_PREFIX = "-framework";
+ LIBRARY_FLAG_PREFIX = "-l";
+ LIBRARY_FLAG_NOSPACE = YES;
+ SKIP_INSTALL = YES;
+ STRIP_STYLE = "debugging";
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ KEEP_PRIVATE_EXTERNS = YES;
+ DEAD_CODE_STRIPPING = NO;
+ };
+ PackageTypes = (
+ com.apple.package-type.mach-o-objfile // default
+ );
},
- "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",
- "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"
+
+ // Dynamic library
+ { Type = ProductType;
+ Identifier = com.apple.product-type.library.dynamic;
+ Class = PBXDynamicLibraryProductType;
+ Name = "Dynamic Library";
+ Description = "Dynamic library";
+ IconNamePrefix = "TargetLibrary";
+ DefaultTargetName = "Dynamic Library";
+ DefaultBuildProperties = {
+ FULL_PRODUCT_NAME = "$(EXECUTABLE_NAME)";
+ MACH_O_TYPE = "mh_dylib";
+ REZ_EXECUTABLE = YES;
+ EXECUTABLE_SUFFIX = ".$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_EXTENSION = "dylib";
+ PUBLIC_HEADERS_FOLDER_PATH = "/usr/local/include";
+ PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include";
+ INSTALL_PATH = "/usr/local/lib";
+ DYLIB_INSTALL_NAME_BASE = "$(INSTALL_PATH)";
+ LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)";
+ DYLIB_COMPATIBILITY_VERSION = "1";
+ DYLIB_CURRENT_VERSION = "1";
+ FRAMEWORK_FLAG_PREFIX = "-framework";
+ LIBRARY_FLAG_PREFIX = "-l";
+ LIBRARY_FLAG_NOSPACE = YES;
+ STRIP_STYLE = "debugging";
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ CODE_SIGNING_ALLOWED = YES;
+ CODE_SIGNING_REQUIRED = NO;
+ };
+ PackageTypes = (
+ com.apple.package-type.mach-o-dylib // default
+ );
},
- "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"
+
+ // Static library
+ { Type = ProductType;
+ Identifier = com.apple.product-type.library.static;
+ Class = PBXStaticLibraryProductType;
+ Name = "Static Library";
+ Description = "Static library";
+ IconNamePrefix = "TargetLibrary";
+ DefaultTargetName = "Static Library";
+ DefaultBuildProperties = {
+ FULL_PRODUCT_NAME = "$(EXECUTABLE_NAME)";
+ MACH_O_TYPE = "staticlib";
+ REZ_EXECUTABLE = YES;
+ EXECUTABLE_PREFIX = "lib";
+ EXECUTABLE_SUFFIX = ".$(EXECUTABLE_EXTENSION)";
+ EXECUTABLE_EXTENSION = "a";
+ PUBLIC_HEADERS_FOLDER_PATH = "/usr/local/include";
+ PRIVATE_HEADERS_FOLDER_PATH = "/usr/local/include";
+ INSTALL_PATH = "/usr/local/lib";
+ FRAMEWORK_FLAG_PREFIX = "-framework";
+ LIBRARY_FLAG_PREFIX = "-l";
+ LIBRARY_FLAG_NOSPACE = YES;
+ STRIP_STYLE = "debugging";
+ CLANG_ENABLE_MODULE_DEBUGGING = NO;
+ CODE_SIGNING_ALLOWED = NO;
+ };
+ AlwaysPerformSeparateStrip = YES;
+ PackageTypes = (
+ com.apple.package-type.static-library // default
+ );
},
- "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"
+
+ // Java library (jar or zip file)
+ { Type = ProductType;
+ Identifier = com.apple.product-type.library.java.archive;
+ Name = "Java Library";
+ Description = "Java library packaged as a Jar file, Zip file, or class folder";
+ IconNamePrefix = "TargetPlugin";
+ DefaultTargetName = "Java Library";
+ DefaultBuildProperties = {
+ FULL_PRODUCT_NAME = "$(PRODUCT_NAME)";
+ INSTALL_PATH = "/usr/local/lib";
+ };
+ IsJava = YES;
+ PackageTypes = (
+ com.apple.package-type.jarfile, // default
+ com.apple.package-type.zipfile,
+ com.apple.package-type.javaclassfolder
+ );
},
- "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" : ""
+
+
+ //
+ // Wrapper product types
+ //
+
+ // Bundle
+ { Type = ProductType;
+ Identifier = com.apple.product-type.bundle;
+ Class = PBXBundleProductType;
+ Name = "Bundle";
+ Description = "Generic bundle";
+ IconNamePrefix = "TargetPlugin";
+ DefaultTargetName = "Bundle";
+ DefaultBuildProperties = {
+ FULL_PRODUCT_NAME = "$(WRAPPER_NAME)";
+ MACH_O_TYPE = "mh_bundle";
+ WRAPPER_PREFIX = "";
+ WRAPPER_SUFFIX = ".$(WRAPPER_EXTENSION)";
+ WRAPPER_EXTENSION = "bundle";
+ WRAPPER_NAME = "$(WRAPPER_PREFIX)$(PRODUCT_NAME)$(WRAPPER_SUFFIX)";
+ FRAMEWORK_FLAG_PREFIX = "-framework";
+ LIBRARY_FLAG_PREFIX = "-l";
+ LIBRARY_FLAG_NOSPACE = YES;
+ STRIP_STYLE = "non-global";
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ CODE_SIGNING_ALLOWED = YES;
+ };
+ PackageTypes = (
+ com.apple.package-type.wrapper, // default
+ com.apple.package-type.wrapper.shallow // Not clear if this is needed in the presence of the Shallow Bundle product type below.
+ );
+ IsWrapper = YES;
+ HasInfoPlist = YES;
+ HasInfoPlistStrings = YES;
+ WantsBundleIdentifierEditing = YES;
+ WantsSigningEditing = YES;
},
- "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"
+
+ // Shallow Bundle Product
+ { Type = ProductType;
+ Identifier = com.apple.product-type.bundle.shallow;
+ BasedOn = com.apple.product-type.bundle;
+ Class = PBXBundleProductType;
+ Name = "Bundle (Shallow)";
+ Description = "Bundle (Shallow)";
+ PackageTypes = (
+ com.apple.package-type.wrapper.shallow
+ );
},
- "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" : {
- "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"
+
+ // Application
+ { Type = ProductType;
+ Identifier = com.apple.product-type.application;
+ BasedOn = com.apple.product-type.bundle;
+ Class = PBXApplicationProductType;
+ Name = "Application";
+ Description = "Application";
+ IconNamePrefix = "TargetApp";
+ DefaultTargetName = "Application";
+ DefaultBuildProperties = {
+ MACH_O_TYPE = "mh_execute";
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ WRAPPER_SUFFIX = ".$(WRAPPER_EXTENSION)";
+ WRAPPER_EXTENSION = "app";
+ INSTALL_PATH = "$(LOCAL_APPS_DIR)";
+ STRIP_STYLE = "all";
+ CODE_SIGNING_ALLOWED = YES;
+ LD_RUNPATH_SEARCH_PATHS = "$(LD_RUNPATH_SEARCH_PATHS_$(IS_MACCATALYST))";
+ LD_RUNPATH_SEARCH_PATHS_YES = ( "@loader_path/../Frameworks", );
+ };
+ PackageTypes = (
+ com.apple.package-type.wrapper.application // default
+ );
+
+ /** Product type validation hooks */
+ Validation = {
+
+ // Checks - a dictionary of checks that are perform just prior to building the product
+ // Each dictionary key is the "XCPropertyMacroExpression" to evaluate
+ // The dictionary value contains the error message to display if the condition evaluates to false.
+ Checks = {
+ // This is intentially empty as currently all of the checks are done in-code for this particular application type. However, we enable the plubmbing for the validation tool spec infrastructure to be called to avoid any further hacks in pbxbuild or xcbuild. This is to enable rdar://problem/45590882.
+ };
+
+ // Determines if the legacy build system has support for this validation hook. This is a workaround for enabling additional validation in xcbuild (specifically for rdar://problem/45590882), which does not have the same plugin restriction that is imposed on pbxbuild's implementation of the validation hooks.
+ EnabledForLegacyBuildSystem = NO;
+
+ // ValidationToolSpec - the identifier of the tool (script) to run after a target is constructed.
+ ValidationToolSpec = "com.apple.build-tools.platform.validate";
+ };
+ CanEmbedCompilerSanitizerLibraries = YES;
+ RunpathSearchPathForEmbeddedFrameworks = "@executable_path/../Frameworks";
+ ValidateEmbeddedBinaries = YES;
+ ProvisioningProfileSupported = YES;
+ ProvisioningProfileRequired = NO;
+ WantsBundleIdentifierEditing = YES;
+ WantsSigningEditing = YES;
},
- "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",
- "PRODUCT_TYPE_CPLUSPLUSFLAGS" : "$(inherited) $(KEXT_CPLUSPLUSFLAGS)",
- "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",
- "PRODUCT_SPECIFIC_LDFLAGS" : "$(inherited) $(KEXT_LDFLAGS)",
- "WRAPPER_EXTENSION" : "kext",
- "KERNEL_EXTENSION_HEADER_SEARCH_PATHS" : "$(KERNEL_FRAMEWORK)\/PrivateHeaders $(KERNEL_FRAMEWORK_HEADERS)",
- "GCC_INLINES_ARE_PRIVATE_EXTERN" : "NO",
- "PRODUCT_TYPE_CFLAGS" : "$(inherited) $(KEXT_CFLAGS)",
- "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"
+
+ // Shallow Application Product
+ { Type = ProductType;
+ Identifier = com.apple.product-type.application.shallow;
+ BasedOn = com.apple.product-type.application;
+ Class = PBXApplicationProductType; // see radar 5604879, this shouldn't be necesary
+ Name = "Application (Shallow Bundle)";
+ Description = "Application (Shallow Bundle)";
+ PackageTypes = (
+ com.apple.package-type.wrapper.application.shallow
+ );
},
- "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" : {
+ // Java Application
+ { Type = ProductType;
+ Identifier = com.apple.product-type.application.java;
+ BasedOn = com.apple.product-type.application;
+ Name = "Java Application";
+ Description = "Java Application";
+ DefaultTargetName = "Java Application";
+ DefaultBuildProperties = {
+ INFOPLIST_PATH = "";
+ PKGINFO_PATH = "";
+ };
+ IsJava = YES;
},
- "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"
+
+ // Framework
+ { Type = ProductType;
+ Identifier = com.apple.product-type.framework;
+ BasedOn = com.apple.product-type.bundle;
+ Class = PBXFrameworkProductType;
+ Name = "Framework";
+ Description = "Framework";
+ IconNamePrefix = "TargetFramework";
+ DefaultTargetName = "Framework";
+ DefaultBuildProperties = {
+ MACH_O_TYPE = "mh_dylib";
+ FRAMEWORK_VERSION = "A";
+ WRAPPER_SUFFIX = ".$(WRAPPER_EXTENSION)";
+ WRAPPER_EXTENSION = "framework";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ DYLIB_INSTALL_NAME_BASE = "$(INSTALL_PATH)";
+ LD_DYLIB_INSTALL_NAME = "$(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(EXECUTABLE_PATH)";
+ STRIP_STYLE = "debugging";
+ CODE_SIGNING_ALLOWED = YES;
+ CODE_SIGNING_REQUIRED = NO;
+ ENTITLEMENTS_REQUIRED = NO;
+ CODE_SIGNING_REQUIRES_TEAM = YES;
+ };
+ PackageTypes = (
+ com.apple.package-type.wrapper.framework // default
+ );
},
- "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" : {
+ // Shallow Framework Product
+ { Type = ProductType;
+ Identifier = com.apple.product-type.framework.shallow;
+ BasedOn = com.apple.product-type.framework;
+ Class = PBXFrameworkProductType;
+ Name = "Framework (Shallow Bundle)";
+ Description = "Framework (Shallow Bundle)";
+ PackageTypes = (
+ com.apple.package-type.wrapper.framework.shallow
+ );
},
- "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)"
+
+ // Static framework
+ { Type = ProductType;
+ Identifier = com.apple.product-type.framework.static;
+ BasedOn = com.apple.product-type.framework;
+ Class = XCStaticFrameworkProductType;
+ Name = "Static Framework";
+ Description = "Static Framework";
+ IconNamePrefix = "TargetFramework";
+ DefaultTargetName = "Static Framework";
+ DefaultBuildProperties = {
+ MACH_O_TYPE = "staticlib";
+ FRAMEWORK_VERSION = "A";
+ WRAPPER_SUFFIX = ".$(WRAPPER_EXTENSION)";
+ WRAPPER_EXTENSION = "framework";
+ INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+ DYLIB_INSTALL_NAME_BASE = "";
+ LD_DYLIB_INSTALL_NAME = "";
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO;
+ CODE_SIGNING_ALLOWED = NO;
+ };
+ AlwaysPerformSeparateStrip = YES;
+ PackageTypes = (
+ com.apple.package-type.wrapper.framework.static // default
+ );
},
- "PackageTypes" : [
- "com.apple.package-type.bundle.unit-test"
- ],
- "CanEmbedCompilerSanitizerLibraries" : "YES",
- "IsUnitTest" : "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)"
+
+ // System extension
+ { Type = ProductType;
+ Identifier = com.apple.product-type.system-extension;
+ BasedOn = com.apple.product-type.bundle;
+ Name = "DriverKit Driver";
+ Description = "DriverKit driver";
+ DefaultTargetName = "DriverKit Driver";
+ DefaultBuildProperties = {
+ MACH_O_TYPE = "mh_execute";
+ WRAPPER_SUFFIX = ".$(WRAPPER_EXTENSION)";
+ WRAPPER_EXTENSION = "systemextension";
+
+ PRODUCT_NAME = "$(PRODUCT_BUNDLE_IDENTIFIER)";
+ };
+ PackageTypes = (
+ com.apple.package-type.wrapper.system-extension,
+ );
+ ProvisioningProfileSupported = YES;
+ ProvisioningProfileRequired = NO;
},
- "PackageTypes" : [
- "com.apple.package-type.bundle.unit-test"
- ],
- "ProvisioningProfileSupported" : "YES",
- "IsUITest" : "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"
+
+ // DriverKit driver
+ { Type = ProductType;
+ Identifier = com.apple.product-type.driver-extension;
+ BasedOn = com.apple.product-type.system-extension;
+ Name = "DriverKit Driver";
+ Description = "DriverKit driver";
+ DefaultTargetName = "DriverKit Driver";
+ DefaultBuildProperties = {
+ SDKROOT = "driverkit";
+ MACH_O_TYPE = "mh_execute";
+ WRAPPER_SUFFIX = ".$(WRAPPER_EXTENSION)";
+ WRAPPER_EXTENSION = "dext";
+ INSTALL_PATH = "$(DEFAULT_DEXT_INSTALL_PATH)";
+ STRIP_STYLE = "debugging";
+
+ DEXT_FRAMEWORK_NAME = "DriverKit";
+ DEXT_FRAMEWORK = "$(SYSTEM_LIBRARY_DIR)/Frameworks/$(DEXT_FRAMEWORK_NAME).framework";
+
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+
+ PRODUCT_NAME = "$(PRODUCT_BUNDLE_IDENTIFIER)";
+ PRODUCT_TYPE_HEADER_SEARCH_PATHS = "$(inherited) $(IIG_HEADERS_DIR)";
+ };
+ DefaultEntitlements = {
+ "com.apple.developer.driverkit" = YES;
+ };
+ PackageTypes = (
+ com.apple.package-type.wrapper.driver-extension,
+ );
},
- "PackageTypes" : [
- "com.apple.package-type.bundle.ocunit-test"
- ],
- "IsUnitTest" : "YES",
- "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)"
+
+ // Kernel Extension
+ { Type = ProductType;
+ Identifier = com.apple.product-type.kernel-extension;
+ BasedOn = com.apple.product-type.bundle;
+ Class = XCKernelExtensionProductType;
+ Name = "Kernel Extension";
+ Description = "Kernel extension";
+ DefaultTargetName = "Kernel Extension";
+ DefaultBuildProperties = {
+ MACH_O_TYPE = "mh_execute";
+ WRAPPER_SUFFIX = ".$(WRAPPER_EXTENSION)";
+ WRAPPER_EXTENSION = "kext";
+ INSTALL_PATH = "$(DEFAULT_KEXT_INSTALL_PATH)";
+ STRIP_STYLE = "debugging";
+
+ KEXT_FRAMEWORK_NAME = "Kernel";
+ KEXT_FRAMEWORK = "$(SYSTEM_LIBRARY_DIR)/Frameworks/$(KEXT_FRAMEWORK_NAME).framework";
+ KEXT_FAMILY_NAME = "family";
+ PUBLIC_HEADERS_FOLDER_PATH = "$(KEXT_FRAMEWORK)/Contents/Headers/$(KEXT_FAMILY_NAME)";
+ PRIVATE_HEADERS_FOLDER_PATH = "$(KEXT_FRAMEWORK)/Contents/PrivateHeaders/$(KEXT_FAMILY_NAME)";
+ MODULE_NAME = "com.company.driver.modulename";
+ MODULE_VERSION = "1.0";
+ MODULE_START = "0";
+ MODULE_STOP = "0";
+
+ ENABLE_APPLE_KEXT_CODE_GENERATION = YES; // -fapple-kext
+ GCC_ENABLE_KERNEL_DEVELOPMENT = YES; // -mkernel
+
+ GCC_USE_STANDARD_INCLUDE_SEARCHING = NO; // -nostdinc
+ GCC_ENABLE_PASCAL_STRINGS = NO; // Disables -fpascal-strings
+ GCC_FORCE_CPU_SUBTYPE_ALL = YES; // -force_cpusubtype_ALL
+ GCC_CHECK_RETURN_VALUE_OF_OPERATOR_NEW = YES; // -fcheck-new
+ GCC_INLINES_ARE_PRIVATE_EXTERN = NO; // -fvisibility-inlines-hidden
+ GCC_ENABLE_BUILTIN_FUNCTIONS = NO; // -fno-builtin
+ GCC_NO_COMMON_BLOCKS = YES; // -fno-common
+ GCC_ENABLE_CPP_EXCEPTIONS = NO; // -fno-exceptions
+ GCC_ENABLE_CPP_RTTI = NO; // -fno-rtti
+
+ // Supposedly these two flags are the default in GCC, but they're provided here for compatibility with older Xcode versions.
+ GCC_ENABLE_FUNCTION_INLINING = YES; // -finline
+ GCC_DISABLE_STATIC_FUNCTION_INLINING = YES; // -fno-keep-inline-functions
+
+ // Per-product-type settings.
+ // The KERNEL_*** settings are defined differently for some SDKs, so we continue to use them for compatibility reasons.
+ KERNEL_FRAMEWORK = "$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/Kernel.framework";
+ KERNEL_FRAMEWORK_HEADERS = "$(KERNEL_FRAMEWORK)/Headers";
+ KERNEL_EXTENSION_HEADER_SEARCH_PATHS = "$(KERNEL_FRAMEWORK)/PrivateHeaders $(KERNEL_FRAMEWORK_HEADERS)";
+ // These settings are reserved for use by product types.
+ GCC_PRODUCT_TYPE_PREPROCESSOR_DEFINITIONS = "$(inherited) KERNEL KERNEL_PRIVATE DRIVER_PRIVATE APPLE NeXT";
+ PRODUCT_TYPE_CFLAGS = "$(inherited) $(KEXT_CFLAGS)";
+ PRODUCT_TYPE_CPLUSPLUSFLAGS = "$(inherited) $(KEXT_CPLUSPLUSFLAGS)";
+ PRODUCT_SPECIFIC_LDFLAGS = "$(inherited) $(KEXT_LDFLAGS)";
+ PRODUCT_TYPE_HEADER_SEARCH_PATHS = "$(inherited) $(KERNEL_EXTENSION_HEADER_SEARCH_PATHS)";
+
+ CODE_SIGNING_ALLOWED = YES;
+
+ ARCHS_STANDARD = "arm64e x86_64";
+ ARCHS_STANDARD_64_BIT = "$(ARCHS_STANDARD)";
+ ARCHS_STANDARD_INCLUDING_64_BIT = "$(ARCHS_STANDARD)";
+ };
+ PackageTypes = (
+ com.apple.package-type.wrapper.kernel-extension, // Default
+ com.apple.package-type.wrapper.kernel-extension.shallow,
+ );
+ },
+
+ // Shallow Kernel Extension
+ {
+ Type = ProductType;
+ Identifier = com.apple.product-type.kernel-extension.shallow;
+ BasedOn = com.apple.product-type.kernel-extension;
+ Class = XCKernelExtensionProductType;
+ Name = "Kernel Extension (Shallow)";
+ Description = "Kernel extension (shallow)";
+ DefaultBuildProperties = {
+ // None yet
+ };
+ PackageTypes = (
+ com.apple.package-type.wrapper.kernel-extension.shallow,
+ );
+ },
+
+ // IOKit Kernel Extension
+ { Type = ProductType;
+ Identifier = com.apple.product-type.kernel-extension.iokit;
+ BasedOn = com.apple.product-type.kernel-extension;
+ Class = XCKernelExtensionProductType;
+ Name = "IOKit Kernel Extension";
+ Description = "IOKit Kernel extension";
+ DefaultTargetName = "IOKit Kernel Extension";
+ DefaultBuildProperties = {
+ CODE_SIGNING_ALLOWED = YES;
+ };
+ PackageTypes = (
+ com.apple.package-type.wrapper.kernel-extension, // Default
+ com.apple.package-type.wrapper.kernel-extension.shallow,
+ );
+ },
+
+ // Shallow IOKit Kernel Extension
+ { Type = ProductType;
+ Identifier = com.apple.product-type.kernel-extension.iokit.shallow;
+ BasedOn = com.apple.product-type.kernel-extension;
+ Class = XCKernelExtensionProductType;
+ Name = "IOKit Kernel Extension (Shallow)";
+ Description = "IOKit Kernel extension (Shallow)";
+ DefaultTargetName = "IOKit Kernel Extension (Shallow)";
+ DefaultBuildProperties = {
+ // None yet
+ };
+ PackageTypes = (
+ com.apple.package-type.wrapper.kernel-extension.shallow,
+ );
+ },
+
+ // Unit Test Bundle
+ { Type = ProductType;
+ Identifier = com.apple.product-type.bundle.unit-test;
+ BasedOn = com.apple.product-type.bundle;
+ Class = PBXXCTestBundleProductType;
+ Name = "Unit Test Bundle";
+ Description = "Unit Test Bundle";
+ DefaultBuildProperties = {
+ WRAPPER_EXTENSION = "xctest";
+ ENABLE_TESTING_SEARCH_PATHS = YES;
+ PRODUCT_SPECIFIC_LDFLAGS = "-framework XCTest";
+ XCTRUNNER_PATH = "$(XCTRUNNER_PATH$(TEST_BUILD_STYLE))";
+ LD_RUNPATH_SEARCH_PATHS = "$(LD_RUNPATH_SEARCH_PATHS_$(IS_MACCATALYST))";
+ LD_RUNPATH_SEARCH_PATHS_YES = (
+ "@loader_path/../Frameworks",
+ "@executable_path/../Frameworks",
+ );
+ };
+ PackageTypes = (
+ com.apple.package-type.bundle.unit-test
+ );
+ CanEmbedCompilerSanitizerLibraries = YES;
+ IsUnitTest = YES;
+ WantsBundleIdentifierEditing = NO;
+ },
+
+ // UI Testing Bundle
+ { Type = ProductType;
+ Identifier = com.apple.product-type.bundle.ui-testing;
+ BasedOn = com.apple.product-type.bundle.unit-test;
+ Class = PBXXCTestBundleProductType;
+ Name = "UI Testing Bundle";
+ Description = "UI Testing Bundle";
+ DefaultBuildProperties = {
+ USES_XCTRUNNER = "YES";
+ };
+ PackageTypes = (
+ com.apple.package-type.bundle.unit-test
+ );
+ ProvisioningProfileSupported = YES;
+ IsUITest = YES;
+ InfoPlistAdditions = {
+ XCTContainsUITests = YES;
+ };
+ WantsBundleIdentifierEditing = NO;
},
- "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"
+
+ // Legacy OCUnit Test Bundle
+ { Type = ProductType;
+ Identifier = com.apple.product-type.bundle.ocunit-test;
+ BasedOn = com.apple.product-type.bundle;
+ Class = PBXBundleProductType;
+ Name = "OCUnit Test Bundle";
+ Description = "OCUnit Test Bundle";
+ DefaultBuildProperties = {
+ WRAPPER_EXTENSION = "octest";
+ };
+ PackageTypes = (
+ com.apple.package-type.bundle.ocunit-test
+ );
+ IsUnitTest = YES;
+ },
+
+ // In-App Purchase Content
+ { Type = ProductType;
+ Identifier = com.apple.product-type.in-app-purchase-content;
+ Class = PBXBundleProductType;
+ Name = "In-App Purchase Content";
+ Description = "In-App Purchase Content";
+ DefaultBuildProperties = {
+ CODE_SIGNING_ALLOWED = NO;
+ FULL_PRODUCT_NAME = "$(WRAPPER_NAME)";
+ };
+ PackageTypes = (
+ com.apple.package-type.in-app-purchase-content, // default
+ );
+ IsWrapper = YES;
+ HasInfoPlist = YES;
+ HasInfoPlistStrings = NO;
+ },
+
+ // XPC Service
+ {
+ Type = ProductType;
+ Identifier = "com.apple.product-type.xpc-service";
+ BasedOn = "com.apple.product-type.bundle";
+ Class = PBXBundleProductType;
+ Name = "XPC Service";
+ Description = "XPC Service";
+ IconNamePrefix = XPCService;
+ DefaultTargetName = "XPC Service";
+ CanEmbedCompilerSanitizerLibraries = YES;
+ "DefaultBuildProperties" = {
+ "MACH_O_TYPE" = "mh_execute";
+ WRAPPER_EXTENSION = "xpc";
+ LD_RUNPATH_SEARCH_PATHS = "$(LD_RUNPATH_SEARCH_PATHS_$(IS_MACCATALYST)_$(_BOOL_$(SKIP_INSTALL)))";
+ LD_RUNPATH_SEARCH_PATHS_YES_YES = (
+ "@loader_path/../Frameworks",
+ "@loader_path/../../../../Frameworks",
+ );
+ LD_RUNPATH_SEARCH_PATHS_YES_NO = ( "@loader_path/../Frameworks", );
+ };
+ PackageTypes = (
+ "com.apple.package-type.xpc-service",
+ );
+ ProvisioningProfileSupported = YES;
+ ProvisioningProfileRequired = NO;
+ WantsBundleIdentifierEditing = YES;
+ WantsSigningEditing = YES;
+ },
+
+ // PlugIn-Kit PlugIn
+ {
+ Type = ProductType;
+ Identifier = "com.apple.product-type.pluginkit-plugin";
+ BasedOn = "com.apple.product-type.xpc-service";
+ Class = PBXBundleProductType;
+ Name = "PlugInKit PlugIn";
+ Description = "PlugInKit PlugIn";
+ IconNamePrefix = XPCService;
+ DefaultTargetName = "PlugInKit PlugIn";
+ "DefaultBuildProperties" = {
+ "PRODUCT_SPECIFIC_LDFLAGS" = "$(SDKROOT)/System/Library/PrivateFrameworks/PlugInKit.framework/PlugInKit";
+ WRAPPER_EXTENSION = "pluginkit";
+ };
+ PackageTypes = (
+ "com.apple.package-type.pluginkit-plugin",
+ );
},
- "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"
+
+ // App Extension
+ {
+ Type = ProductType;
+ Identifier = "com.apple.product-type.app-extension";
+ BasedOn = "com.apple.product-type.pluginkit-plugin";
+ Class = PBXBundleProductType;
+ Name = "App Extension";
+ Description = "App Extension";
+ IconNamePrefix = AppExtension;
+ DefaultTargetName = "App Extension";
+ "DefaultBuildProperties" = {
+ "CODE_SIGNING_ALLOWED" = YES;
+ "APPLICATION_EXTENSION_API_ONLY" = YES;
+ "PRODUCT_SPECIFIC_LDFLAGS" = "-e _NSExtensionMain";
+ WRAPPER_EXTENSION = "appex";
+ };
+ PackageTypes = (
+ "com.apple.package-type.app-extension",
+ );
+ ProvisioningProfileSupported = YES;
+ ProvisioningProfileRequired = NO;
+ WantsBundleIdentifierEditing = YES;
+ WantsSigningEditing = YES;
},
- "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"
+
+ // Xcode Extension
+ {
+ Type = ProductType;
+ Identifier = "com.apple.product-type.xcode-extension";
+ BasedOn = "com.apple.product-type.app-extension";
+ Name = "Xcode Extension";
+ Description = "Xcode Extension";
+ IconNamePrefix = XcodeExtension;
+ DefaultTargetName = "Xcode Extension";
+ "DefaultBuildProperties" = {
+ "CODE_SIGNING_ALLOWED" = YES;
+ "APPLICATION_EXTENSION_API_ONLY" = YES;
+ "PRODUCT_SPECIFIC_LDFLAGS" = "-e _XCExtensionMain -lXcodeExtension";
+ "PRODUCT_TYPE_FRAMEWORK_SEARCH_PATHS" = (
+ "$(inherited)",
+ "$(DEVELOPER_FRAMEWORKS_DIR)",
+ );
+ "PRODUCT_TYPE_LIBRARY_SEARCH_PATHS" = (
+ "$(inherited)",
+ "$(DEVELOPER_USR_DIR)/lib",
+ );
+ LD_RUNPATH_SEARCH_PATHS = (
+ "@loader_path/../Frameworks",
+ );
+ WRAPPER_EXTENSION = "appex";
+ };
+ PackageTypes = (
+ "com.apple.package-type.app-extension",
+ );
+ ProvisioningProfileSupported = YES;
+ ProvisioningProfileRequired = NO;
+ WantsBundleIdentifierEditing = YES;
+ WantsSigningEditing = 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)"
- ]
+
+ // Spotlight Importer
+ {
+ Type = ProductType;
+ Identifier = "com.apple.product-type.spotlight-importer";
+ BasedOn = "com.apple.product-type.bundle";
+ Class = PBXBundleProductType;
+ Name = "Spotlight Importer";
+ Description = "Spotlight Importer";
+ DefaultTargetName = "Spotlight";
+ "DefaultBuildProperties" = {
+ "CODE_SIGNING_ALLOWED" = YES;
+ };
+ PackageTypes = (
+ "com.apple.package-type.spotlight-importer",
+ );
},
- "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"
+
+ // Copied from iOS specs to aid in build system diagnostic support.
+ // Later, we should put these in some central place.
+ {
+ Type = ProductType;
+ Identifier = "com.apple.product-type.app-extension.messages";
+ BasedOn = "com.apple.product-type.app-extension";
+ },
+
+ {
+ Type = ProductType;
+ Identifier = "com.apple.product-type.app-extension.messages-sticker-pack";
+ BasedOn = "com.apple.product-type.app-extension";
+ IsCapabilitiesUnsupported = YES;
+ WantsSimpleTargetEditing = YES;
+ DefaultBuildProperties = {
+ MESSAGES_APPLICATION_EXTENSION_PRODUCT_BINARY_SOURCE_PATH = "$(PLATFORM_DIR)/Library/Application Support/MessagesApplicationExtensionStub/MessagesApplicationExtensionStub";
+ ASSETCATALOG_COMPILER_INCLUDE_STICKER_CONTENT = YES;
+ ASSETCATALOG_COMPILER_TARGET_STICKERS_ICON_ROLE = "extension";
+ ASSETCATALOG_COMPILER_STICKER_PACK_IDENTIFIER_PREFIX = "$(PRODUCT_BUNDLE_IDENTIFIER).sticker-pack.";
+ };
+ AllowedBuildPhases = (
+ "com.apple.buildphase.resources",
+ "com.apple.buildphase.shell-script",
+ );
+ InfoPlistAdditions = {
+ LSApplicationIsStickerProvider = YES;
+ };
+ },
+
+ {
+ Type = ProductType;
+ Identifier = "com.apple.product-type.application.on-demand-install-capable";
+ BasedOn = "com.apple.product-type.application";
+ Name = "App Clip";
+ Description = "App Clip";
+ DefaultBuildProperties = {
+ ENABLE_ON_DEMAND_RESOURCES = NO;
+ ASSETCATALOG_COMPILER_STANDALONE_ICON_BEHAVIOR = none; // Don't include standalone icons to keep size down
+ PRODUCT_SPECIFIC_LDFLAGS = "$(inherited) -framework AppClip";
+ SKIP_INSTALL = YES;
+ };
+ DefaultEntitlements = {
+ "com.apple.developer.on-demand-install-capable" = YES;
+ };
+ SupportsOnDemandResources = NO;
+ IsEmbeddable = YES;
+ BuildPhaseInjectionsWhenEmbedding = (
+ {
+ BuildPhase = "CopyFiles";
+ Name = "Embed App Clips";
+ RunOnlyForDeploymentPostprocessing = NO;
+ DstSubFolderSpec = 16;
+ DstPath = "$(CONTENTS_FOLDER_PATH)/AppClips";
+ }
+ );
},
- "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
index 2354d88b5..cf765f7be 100644
--- a/share/qbs/modules/bundle/bundle.js
+++ b/share/qbs/modules/bundle/bundle.js
@@ -28,9 +28,12 @@
**
****************************************************************************/
+var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
var DarwinTools = require("qbs.DarwinTools");
var ModUtils = require("qbs.ModUtils");
var Process = require("qbs.Process");
+var Utilities = require("qbs.Utilities");
// HACK: Workaround until the PropertyList extension is supported cross-platform
var TextFile = require("qbs.TextFile");
@@ -80,14 +83,14 @@ var _productTypeIdentifiers = {
function productTypeIdentifier(productType) {
for (var k in _productTypeIdentifiers) {
- if (productType.contains(k))
+ if (productType.includes(k))
return _productTypeIdentifiers[k];
}
return "com.apple.package-type.wrapper";
}
function excludedAuxiliaryInputs(project, product) {
- var chain = product.moduleProperty("bundle", "_productTypeIdentifierChain");
+ var chain = product.bundle._productTypeIdentifierChain;
var bestPossibleType;
for (var i = chain.length - 1; i >= 0; --i) {
switch (chain[i]) {
@@ -147,27 +150,77 @@ function _assign(target, source) {
}
}
+function macOSSpecsPaths(version, developerPath) {
+ var result = [];
+ if (Utilities.versionCompare(version, "15.3") >= 0) {
+ result.push(FileInfo.joinPaths(
+ developerPath, "..", "SharedFrameworks", "XCBuild.framework", "PlugIns",
+ "XCBBuildService.bundle", "Contents", "PlugIns", "XCBSpecifications.ideplugin",
+ "Contents", "Resources"));
+ }
+ if (Utilities.versionCompare(version, "14.3") >= 0) {
+ result.push(FileInfo.joinPaths(
+ developerPath, "Library", "Xcode", "Plug-ins", "XCBSpecifications.ideplugin",
+ "Contents", "Resources"));
+ } else if (Utilities.versionCompare(version, "12.5") >= 0) {
+ result.push(FileInfo.joinPaths(
+ developerPath, "..", "PlugIns", "XCBSpecifications.ideplugin",
+ "Contents", "Resources"));
+ } else if (Utilities.versionCompare(version, "12") >= 0) {
+ result.push(FileInfo.joinPaths(
+ developerPath, "Platforms", "MacOSX.platform", "Developer", "Library", "Xcode",
+ "PrivatePlugIns", "IDEOSXSupportCore.ideplugin", "Contents", "Resources"));
+ } else {
+ result.push(FileInfo.joinPaths(
+ developerPath, "Platforms", "MacOSX.platform", "Developer", "Library", "Xcode",
+ "Specifications"));
+ }
+ return result;
+}
+
var XcodeBuildSpecsReader = (function () {
- function XcodeBuildSpecsReader(specsPath, separator, additionalSettings, useShallowBundles) {
+ function XcodeBuildSpecsReader(specsPaths, 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();
+
+ this._packageTypes = [];
+ this._productTypes = [];
+
+ var i, j;
+ for (i = 0; i < specsPaths.length; ++i) {
+ var specsPath = specsPaths[i];
+ var names = ["", "Darwin", "MacOSX"];
+ for (j = 0; j < names.length; ++j) {
+ var name = names[j];
+ var plist = new PropertyList2();
+ var plist2 = new PropertyList2();
+ try
+ {
+ var plistName = [name, "Package", "Types.xcspec"].join(name ? separator : "");
+ var plistName2 = [name, "Product", "Types.xcspec"].join(name ? separator : "");
+ var plistPath = FileInfo.joinPaths(specsPath, plistName);
+ var plistPath2 = FileInfo.joinPaths(specsPath, plistName2);
+ if (File.exists(plistPath)) {
+ plist.readFromFile(plistPath);
+ this._packageTypes = this._packageTypes.concat(plist.toObject());
+ }
+ if (File.exists(plistPath2)) {
+ plist2.readFromFile(plistPath2);
+ this._productTypes = this._productTypes.concat(plist2.toObject());
+ }
+ } finally {
+ plist.clear();
+ plist2.clear();
+ }
+ }
}
+
+ 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];
+
}
XcodeBuildSpecsReader.prototype.productTypeIdentifierChain = function (typeIdentifier) {
var ids = [typeIdentifier];
@@ -266,7 +319,8 @@ var XcodeBuildSpecsReader = (function () {
};
XcodeBuildSpecsReader.prototype.expandedSetting = function (typeIdentifier, baseSettings,
settingName) {
- var obj = baseSettings || {};
+ var obj = {};
+ _assign(obj, baseSettings); // todo: copy recursively
obj = _assign(obj, this.settings(typeIdentifier, true));
if (obj) {
for (var x in this._additionalSettings) {
@@ -278,7 +332,7 @@ var XcodeBuildSpecsReader = (function () {
var original;
while (original !== setting) {
original = setting;
- setting = DarwinTools.expandPlistEnvironmentVariables({ key: setting }, obj, true)["key"];
+ setting = DarwinTools.expandPlistEnvironmentVariables({ key: setting }, obj, false)["key"];
}
return setting;
}
diff --git a/share/qbs/modules/capnproto/capnproto.js b/share/qbs/modules/capnproto/capnproto.js
new file mode 100644
index 000000000..64464f42c
--- /dev/null
+++ b/share/qbs/modules/capnproto/capnproto.js
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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 = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var Utilities = require("qbs.Utilities");
+
+function validateCompiler(name, path) {
+ if (!File.exists(path))
+ throw "Cannot find executable '" + name + "'. Please set the compilerPath "
+ + "property or make sure the compiler is found in PATH";
+}
+
+function validatePlugin(name, path) {
+ if (!name)
+ throw "pluginName is not set";
+ if (!File.exists(path))
+ throw "Cannot find plugin '" + name + "'. Please set the pluginPath "
+ + "property or make sure the plugin is found in PATH";
+}
+
+function getOutputDir(module, input) {
+ var outputDir = module.outputDir;
+ var importPaths = module.importPaths;
+ if (importPaths.length !== 0) {
+ var canonicalInput = File.canonicalFilePath(FileInfo.path(input.filePath));
+ for (var i = 0; i < importPaths.length; ++i) {
+ var path = File.canonicalFilePath(importPaths[i]);
+
+ if (canonicalInput.startsWith(path)) {
+ return outputDir + "/" + FileInfo.relativePath(path, canonicalInput);
+ }
+ }
+ }
+ return outputDir;
+}
+
+function artifact(outputDir, input, tag, suffix) {
+ return {
+ fileTags: [tag],
+ filePath: FileInfo.joinPaths(outputDir, FileInfo.baseName(input.fileName) + suffix),
+ cpp: {
+ includePaths: [].concat(input.cpp.includePaths, outputDir),
+ warningLevel: "none",
+ }
+ };
+}
+
+function doPrepare(module, product, input, outputs, lang)
+{
+ var outputDir = FileInfo.path(outputs["cpp"][0].filePath);
+
+ var args = [];
+ args.push("--output=" + module.pluginPath + ":" + outputDir);
+ args.push("--src-prefix=" + FileInfo.path(input.filePath));
+
+ var importPaths = module.importPaths;
+ importPaths.forEach(function(path) {
+ if (!FileInfo.isAbsolutePath(path))
+ path = FileInfo.joinPaths(product.sourceDirectory, path);
+ args.push("--import-path", path);
+ });
+
+ args.push(input.filePath);
+
+ var cmd = new Command(module.compilerPath, args);
+ cmd.highlight = "codegen";
+ cmd.description = "generating " + lang + " files for " + input.fileName;
+ return [cmd];
+}
diff --git a/share/qbs/modules/capnproto/capnprotobase.qbs b/share/qbs/modules/capnproto/capnprotobase.qbs
new file mode 100644
index 000000000..56d542770
--- /dev/null
+++ b/share/qbs/modules/capnproto/capnprotobase.qbs
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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.Probes
+import "capnproto.js" as HelperFunctions
+
+Module {
+ property string compilerName: "capnpc"
+ property string compilerPath: compilerProbe.filePath
+
+ property string pluginName
+ property string pluginPath: pluginProbe.filePath
+
+ property pathList importPaths: []
+
+ property string outputDir: product.buildDirectory + "/capnp"
+
+ Probes.BinaryProbe {
+ id: compilerProbe
+ names: compilerName ? [compilerName] : []
+ }
+
+ Probes.BinaryProbe {
+ id: pluginProbe
+ names: pluginName ? [pluginName] : []
+ }
+
+ FileTagger {
+ patterns: ["*.capnp"]
+ fileTags: ["capnproto.input"];
+ }
+
+ validate: {
+ HelperFunctions.validateCompiler(compilerName, compilerPath);
+ HelperFunctions.validatePlugin(pluginName, pluginPath);
+ }
+}
diff --git a/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs b/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs
new file mode 100644
index 000000000..bccfca192
--- /dev/null
+++ b/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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 "../capnprotobase.qbs" as CapnProtoBase
+import "../capnproto.js" as HelperFunctions
+
+CapnProtoBase {
+ property bool useRpc: false
+
+ Depends { name: "cpp" }
+ Depends { name: "capnp" }
+ Depends { name: "capnp-rpc"; condition: useRpc }
+
+ pluginName: "capnpc-c++"
+ version: capnp.version
+
+ cpp.systemIncludePaths: outputDir
+ cpp.cxxLanguageVersion: "c++14"
+
+ Rule {
+ inputs: ["capnproto.input"]
+ outputFileTags: ["hpp", "cpp"]
+ outputArtifacts: {
+ var outputDir = HelperFunctions.getOutputDir(input.capnproto.cpp, input);
+ var result = [
+ HelperFunctions.artifact(outputDir, input, "hpp", ".capnp.h"),
+ HelperFunctions.artifact(outputDir, input, "cpp", ".capnp.c++")
+ ];
+ return result;
+ }
+
+ prepare: {
+ var result = HelperFunctions.doPrepare(
+ input.capnproto.cpp, product, input, outputs, "cpp");
+ return result;
+ }
+ }
+}
diff --git a/share/qbs/modules/cli/CLIModule.qbs b/share/qbs/modules/cli/CLIModule.qbs
index d0ca120ef..3b12284ed 100644
--- a/share/qbs/modules/cli/CLIModule.qbs
+++ b/share/qbs/modules/cli/CLIModule.qbs
@@ -1,12 +1,13 @@
// base for Common Language Infrastructure modules
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import "cli.js" as CLI
Module {
- Depends { name: "bundle"; condition: qbs.targetOS.contains("darwin") }
+ Depends { name: "bundle"; condition: qbs.targetOS.includes("darwin") }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
@@ -87,8 +88,8 @@ Module {
}
setupBuildEnvironment: {
- var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator,
- product.qbs.hostOS.contains("windows"));
+ var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(),
+ Host.os().includes("windows"));
v.prepend(product.cli.toolchainInstallPath);
v.set();
}
@@ -115,9 +116,7 @@ Module {
"debugInfoSuffix"))
}
- prepare: {
- return CLI.prepareCompiler(product, inputs, outputs.application[0]);
- }
+ prepare: CLI.prepareCompiler(product, inputs, outputs.application[0])
}
Rule {
@@ -142,9 +141,7 @@ Module {
"debugInfoSuffix"))
}
- prepare: {
- return CLI.prepareCompiler(product, inputs, outputs.dynamiclibrary[0]);
- }
+ prepare: CLI.prepareCompiler(product, inputs, outputs.dynamiclibrary[0])
}
Rule {
@@ -169,9 +166,7 @@ Module {
"debugInfoSuffix"))
}
- prepare: {
- return CLI.prepareCompiler(product, inputs, outputs["cli.netmodule"][0]);
- }
+ prepare: CLI.prepareCompiler(product, inputs, outputs["cli.netmodule"][0])
}
Rule {
diff --git a/share/qbs/modules/cli/cli.js b/share/qbs/modules/cli/cli.js
index 38833ac51..faa812201 100644
--- a/share/qbs/modules/cli/cli.js
+++ b/share/qbs/modules/cli/cli.js
@@ -59,16 +59,16 @@ function prepareCompiler(product, inputs, output) {
"cli.fsharp": fsharpCompilerPath
};
- var pathFunction = product.moduleProperty("qbs", "hostOS").contains("windows")
+ var pathFunction = product.moduleProperty("qbs", "hostOS").includes("windows")
? FileInfo.toWindowsSeparators
: function (path) { return path; };
var outputDescription = "assembly";
- if (output.fileTags.contains("application")) {
+ if (output.fileTags.includes("application")) {
args.push("/target:" + (product.consoleApplication === false ? "winexe" : "exe"));
- } else if (output.fileTags.contains("dynamiclibrary")) {
+ } else if (output.fileTags.includes("dynamiclibrary")) {
args.push("/target:library");
- } else if (output.fileTags.contains("cli.netmodule")) {
+ } else if (output.fileTags.includes("cli.netmodule")) {
args.push("/target:module");
outputDescription = "netmodule";
}
@@ -77,7 +77,7 @@ function prepareCompiler(product, inputs, output) {
var keys = Object.keys(inputs);
var language;
for (i in keys) {
- if (Object.keys(compilers).contains(keys[i])) {
+ if (Object.keys(compilers).includes(keys[i])) {
if (language)
throw("You cannot compile source files in more than one CLI language into a single " + outputDescription + ".");
language = keys[i];
diff --git a/share/qbs/modules/cli/mono.qbs b/share/qbs/modules/cli/mono.qbs
index f04956a5e..f16387dd7 100644
--- a/share/qbs/modules/cli/mono.qbs
+++ b/share/qbs/modules/cli/mono.qbs
@@ -1,8 +1,9 @@
import qbs.File
+import qbs.Host
import qbs.Probes
CLIModule {
- condition: qbs.toolchain && qbs.toolchain.contains("mono")
+ condition: qbs.toolchain && qbs.toolchain.includes("mono")
debugInfoSuffix: ".mdb"
csharpCompilerName: "mcs"
@@ -14,9 +15,9 @@ CLIModule {
names: ["mono"]
platformSearchPaths: {
var paths = [];
- if (qbs.hostOS.contains("macos"))
+ if (Host.os().includes("macos"))
paths.push("/Library/Frameworks/Mono.framework/Commands");
- if (qbs.hostOS.contains("unix"))
+ if (Host.os().includes("unix"))
paths.push("/usr/bin");
return paths;
}
diff --git a/share/qbs/modules/cli/windows-dotnet.qbs b/share/qbs/modules/cli/windows-dotnet.qbs
index 6fde50b85..a4d27c207 100644
--- a/share/qbs/modules/cli/windows-dotnet.qbs
+++ b/share/qbs/modules/cli/windows-dotnet.qbs
@@ -1,7 +1,7 @@
import qbs.Utilities
CLIModule {
- condition: qbs.toolchain && qbs.toolchain.contains("dotnet")
+ condition: qbs.toolchain && qbs.toolchain.includes("dotnet")
debugInfoSuffix: ".pdb"
csharpCompilerName: "csc"
diff --git a/share/qbs/modules/codesign/CodeSignModule.qbs b/share/qbs/modules/codesign/CodeSignModule.qbs
new file mode 100644
index 000000000..6db35c85c
--- /dev/null
+++ b/share/qbs/modules/codesign/CodeSignModule.qbs
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** 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.File
+import qbs.FileInfo
+import "codesign.js" as CodeSign
+
+Module {
+ condition: false
+
+ property bool enableCodeSigning: false
+
+ property string codesignName
+ property string codesignPath: codesignName
+ property stringList codesignFlags
+
+ property string signingTimestamp
+ PropertyOptions {
+ name: "signingTimestamp"
+ description: "URL of the RFC 3161 time stamp server."
+ }
+
+ property bool _canSignArtifacts: false // whether can sign individual actifacts
+}
diff --git a/share/qbs/modules/codesign/android.qbs b/share/qbs/modules/codesign/android.qbs
new file mode 100644
index 000000000..b1811dcfd
--- /dev/null
+++ b/share/qbs/modules/codesign/android.qbs
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Raphaël Cotty <raphael.cotty@gmail.com>
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of the 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.Environment
+import qbs.File
+import qbs.FileInfo
+import qbs.Host
+import qbs.ModUtils
+import qbs.Probes
+import "codesign.js" as CodeSign
+
+CodeSignModule {
+ condition: qbs.targetOS.includes("android")
+ priority: 1
+ enableCodeSigning: true
+
+ property bool useApksigner: true
+ property path apksignerFilePath
+
+ Probes.JdkProbe {
+ id: jdk
+ environmentPaths: (jdkPath ? [jdkPath] : []).concat(base)
+ }
+ property string jdkPath: jdk.path
+ property string jarsignerFilePath: FileInfo.joinPaths(jdkPath, "bin", jarsignerName)
+ property string jarsignerName: "jarsigner"
+ property string keytoolFilePath: FileInfo.joinPaths(jdkPath, "bin", keytoolName)
+ property string keytoolName: "keytool"
+
+ property string debugKeystorePath: FileInfo.joinPaths(
+ Environment.getEnv(Host.os().includes("windows")
+ ? "USERPROFILE" : "HOME"),
+ ".android", "debug.keystore")
+ readonly property string debugKeystorePassword: "android"
+ readonly property string debugPassword: "android"
+ readonly property string debugKeyAlias: "androiddebugkey"
+
+ property string keystorePath: debugKeystorePath
+ property string keystorePassword: debugKeystorePassword
+ property string keyPassword: debugPassword
+ property string keyAlias: debugKeyAlias
+
+ // Private property set by the Android.sdk module
+ property string _packageName
+
+ Rule {
+ condition: useApksigner
+ inputs: ["android.package_unsigned"]
+ Artifact {
+ filePath: product.codesign._packageName
+ fileTags: "android.package"
+ }
+ prepare: CodeSign.signApkPackage.apply(this, arguments)
+ }
+
+ Rule {
+ condition: !useApksigner
+ inputs: ["android.package_unsigned"]
+ Artifact {
+ filePath: product.codesign._packageName
+ fileTags: "android.package"
+ }
+ prepare: CodeSign.signAabPackage.apply(this, arguments)
+ }
+
+ validate: {
+ // Typically there is a debug keystore in ~/.android/debug.keystore which gets created
+ // by the native build tools the first time a build is done. However, we don't want to
+ // create it ourselves, because writing to a location outside the qbs build directory is
+ // both polluting and has the potential for race conditions. So we'll instruct the user what
+ // to do.
+ if (keystorePath === debugKeystorePath && !File.exists(debugKeystorePath)) {
+ throw ModUtils.ModuleError("Could not find an Android debug keystore at " +
+ codesign.debugKeystorePath + ". " +
+ "If you are developing for Android on this machine for the first time and " +
+ "have never built an application using the native Gradle / Android Studio " +
+ "tooling, this is normal. You must create the debug keystore now using the " +
+ "following command, in order to continue:\n\n" +
+ CodeSign.createDebugKeyStoreCommandString(codesign.keytoolFilePath,
+ codesign.debugKeystorePath,
+ codesign.debugKeystorePassword,
+ codesign.debugPassword,
+ codesign.debugKeyAlias) +
+ "\n\n" +
+ "See the following URL for more information: " +
+ "https://developer.android.com/studio/publish/app-signing.html#debug-mode");
+ }
+ }
+}
diff --git a/share/qbs/modules/codesign/apple.qbs b/share/qbs/modules/codesign/apple.qbs
new file mode 100644
index 000000000..0d1335d92
--- /dev/null
+++ b/share/qbs/modules/codesign/apple.qbs
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** 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.BundleTools
+import qbs.DarwinTools
+import qbs.Environment
+import qbs.File
+import qbs.FileInfo
+import qbs.Host
+import qbs.ModUtils
+import qbs.PropertyList
+import qbs.Probes
+import qbs.Utilities
+import "codesign.js" as CodeSign
+import "../xcode/xcode.js" as XcodeUtils
+
+CodeSignModule {
+ Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.includes("xcode") }
+
+ Probes.BinaryProbe {
+ id: codesignProbe
+ names: [codesignName]
+ }
+
+ condition: Host.os().includes("macos") && qbs.targetOS.includes("darwin")
+ priority: 0
+
+ enableCodeSigning: _codeSigningRequired
+
+ codesignName: "codesign"
+ codesignPath: codesignProbe.filePath
+
+ _canSignArtifacts: true
+
+ property string signingType: {
+ if (_adHocCodeSigningAllowed)
+ return "ad-hoc";
+ if (_codeSigningAllowed)
+ return "app-store";
+ }
+
+ PropertyOptions {
+ name: "signingType"
+ allowedValues: ["app-store", "apple-id", "ad-hoc"]
+ }
+
+ property string signingIdentity: {
+ if (signingType === "ad-hoc") // only useful on macOS
+ return "-";
+
+ var isDebug = qbs.buildVariant !== "release";
+
+ if (qbs.targetOS.includes("ios") || qbs.targetOS.includes("tvos")
+ || qbs.targetOS.includes("watchos")) {
+ switch (signingType) {
+ case "app-store":
+ return isDebug ? "iPhone Developer" : "iPhone Distribution";
+ }
+ }
+
+ if (qbs.targetOS.includes("macos")) {
+ switch (signingType) {
+ case "app-store":
+ return isDebug ? "Mac Developer" : "3rd Party Mac Developer Application";
+ case "apple-id":
+ return "Developer ID Application";
+ }
+ }
+ }
+
+ signingTimestamp: "none"
+
+ property string provisioningProfile
+ PropertyOptions {
+ name: "provisioningProfile"
+ description: "Name or UUID of the provisioning profile to embed in the application; " +
+ "typically left blank to allow automatic provisioning"
+ }
+
+ property string teamIdentifier
+ PropertyOptions {
+ name: "teamIdentifier"
+ description: "Name or identifier of the development team whose identities will be used; " +
+ "typically left blank unless signed into multiple development teams"
+ }
+
+ property path provisioningProfilesPath: "~/Library/MobileDevice/Provisioning Profiles"
+
+ readonly property var _actualSigningIdentity: {
+ if (signingIdentity === "-") {
+ return {
+ SHA1: signingIdentity,
+ subjectInfo: { CN: "ad hoc" }
+ }
+ }
+
+ var identities = CodeSign.findSigningIdentities(signingIdentity, teamIdentifier);
+ if (identities && Object.keys(identities).length > 1) {
+ throw "Multiple codesigning identities (i.e. certificate and private key pairs) " +
+ "matching '" + signingIdentity + "' were found." +
+ CodeSign.humanReadableIdentitySummary(identities);
+ }
+
+ for (var i in identities)
+ return identities[i];
+ }
+
+ // Allowed for macOS
+ readonly property bool _adHocCodeSigningAllowed:
+ XcodeUtils.boolFromSdkOrPlatform("AD_HOC_CODE_SIGNING_ALLOWED",
+ xcode._sdkProps, xcode._platformProps, true)
+
+ // Allowed for all device platforms (not simulators)
+ readonly property bool _codeSigningAllowed:
+ XcodeUtils.boolFromSdkOrPlatform("CODE_SIGNING_ALLOWED",
+ xcode._sdkProps, xcode._platformProps, true)
+
+ // Required for tvOS, iOS, and watchOS (not simulators)
+ property bool _codeSigningRequired: {
+ // allow to override value from Xcode so tests do not require signing
+ var envRequired = Environment.getEnv("QBS_AUTOTEST_CODE_SIGNING_REQUIRED");
+ if (envRequired)
+ return envRequired === "1";
+ return XcodeUtils.boolFromSdkOrPlatform("CODE_SIGNING_REQUIRED",
+ xcode._sdkProps, xcode._platformProps, false)
+ }
+
+ // Required for tvOS, iOS, and watchOS (not simulators)
+ readonly property bool _entitlementsRequired:
+ XcodeUtils.boolFromSdkOrPlatform("ENTITLEMENTS_REQUIRED",
+ xcode._sdkProps, xcode._platformProps, false)
+
+ readonly property bool _provisioningProfileAllowed:
+ product.bundle
+ && product.bundle.isBundle
+ && product.type.includes("application")
+ && xcode.platformType !== "simulator"
+
+ // Required for tvOS, iOS, and watchOS (not simulators)
+ // PROVISIONING_PROFILE_REQUIRED is specified only in Embedded-Device.xcspec in the
+ // IDEiOSSupportCore IDE plugin, so we'll just write out the logic here manually
+ readonly property bool _provisioningProfileRequired:
+ _provisioningProfileAllowed && !qbs.targetOS.includes("macos")
+
+ // Not used on simulator platforms either but provisioning profiles aren't used there anyways
+ readonly property string _provisioningProfilePlatform: {
+ if (qbs.targetOS.includes("macos"))
+ return "OSX";
+ if (qbs.targetOS.includes("ios") || qbs.targetOS.includes("watchos"))
+ return "iOS";
+ if (qbs.targetOS.includes("tvos"))
+ return "tvOS";
+ }
+
+ readonly property string _embeddedProfileName:
+ (xcode._platformProps || {})["EMBEDDED_PROFILE_NAME"] || "embedded.mobileprovision"
+
+ setupBuildEnvironment: {
+ var prefixes = product.xcode ? [
+ product.xcode.platformPath + "/Developer",
+ product.xcode.toolchainPath,
+ product.xcode.developerPath
+ ] : [];
+ for (var i = 0; i < prefixes.length; ++i) {
+ var codesign_allocate = prefixes[i] + "/usr/bin/codesign_allocate";
+ if (File.exists(codesign_allocate)) {
+ var v = new ModUtils.EnvironmentVariable("CODESIGN_ALLOCATE");
+ v.value = codesign_allocate;
+ v.set();
+ break;
+ }
+ }
+ }
+
+ Group {
+ name: "Provisioning Profiles"
+ prefix: codesign.provisioningProfilesPath + "/"
+ files: ["*.mobileprovision", "*.provisionprofile"]
+ }
+
+ FileTagger {
+ fileTags: ["codesign.entitlements"]
+ patterns: ["*.entitlements"]
+ }
+
+ FileTagger {
+ fileTags: ["codesign.provisioningprofile"]
+ patterns: ["*.mobileprovision", "*.provisionprofile"]
+ }
+
+ Rule {
+ multiplex: true
+ condition: product.codesign.enableCodeSigning &&
+ product.codesign._provisioningProfileAllowed
+ inputs: ["codesign.provisioningprofile"]
+
+ outputFileTags: ["codesign.embedded_provisioningprofile"]
+ outputArtifacts: {
+ var artifacts = [];
+ var provisioningProfiles = (inputs["codesign.provisioningprofile"] || [])
+ .map(function (a) { return a.filePath; });
+ var bestProfile = CodeSign.findBestProvisioningProfile(product, provisioningProfiles);
+ var uuid = product.provisioningProfile;
+ if (bestProfile) {
+ artifacts.push({
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ product.codesign._embeddedProfileName),
+ fileTags: ["codesign.embedded_provisioningprofile"],
+ codesign: {
+ _provisioningProfileFilePath: bestProfile.filePath,
+ _provisioningProfileData: JSON.stringify(bestProfile.data),
+ }
+ });
+ } else if (uuid) {
+ throw "Your build settings specify a provisioning profile with the UUID '"
+ + uuid + "', however, no such provisioning profile was found.";
+ } else if (product._provisioningProfileRequired) {
+ var hasProfiles = !!((inputs["codesign.provisioningprofile"] || []).length);
+ var teamIdentifier = product.teamIdentifier;
+ var codeSignIdentity = product.signingIdentity;
+ if (hasProfiles) {
+ if (codeSignIdentity) {
+ console.warn("No provisioning profiles matching the bundle identifier '"
+ + product.bundle.identifier
+ + "' were found.");
+ } else {
+ console.warn("No provisioning profiles matching an applicable signing "
+ + "identity were found.");
+ }
+ } else {
+ if (codeSignIdentity) {
+ if (teamIdentifier) {
+ console.warn("No provisioning profiles with a valid signing identity "
+ + "(i.e. certificate and private key pair) matching the "
+ + "team ID '" + teamIdentifier + "' were found.")
+ } else {
+ console.warn("No provisioning profiles with a valid signing identity "
+ + "(i.e. certificate and private key pair) were found.");
+ }
+ } else {
+ console.warn("No non-expired provisioning profiles were found.");
+ }
+ }
+ }
+ return artifacts;
+ }
+
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ var data = JSON.parse(output.codesign._provisioningProfileData);
+ cmd.source = output.codesign._provisioningProfileFilePath;
+ cmd.destination = output.filePath;
+ cmd.description = "using provisioning profile " + data.Name + " (" + data.UUID + ")";
+ cmd.highlight = "filegen";
+ cmd.sourceCode = function() {
+ File.copy(source, destination);
+ };
+ return [cmd];
+ }
+ }
+
+ Rule {
+ multiplex: true
+ condition: product.codesign.enableCodeSigning
+ inputs: ["codesign.entitlements", "codesign.embedded_provisioningprofile"]
+
+ Artifact {
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ product.targetName + ".xcent")
+ fileTags: ["codesign.xcent"]
+ }
+
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating entitlements";
+ cmd.highlight = "codegen";
+ cmd.bundleIdentifier = product.bundle.identifier;
+ cmd.signingEntitlements = (inputs["codesign.entitlements"] || [])
+ .map(function (a) { return a.filePath; });
+ cmd.provisioningProfiles = (inputs["codesign.embedded_provisioningprofile"] || [])
+ .map(function (a) { return a.filePath; });
+ cmd.platformPath = product.xcode ? product.xcode.platformPath : undefined;
+ cmd.sdkPath = product.xcode ? product.xcode.sdkPath : undefined;
+ cmd.sourceCode = function() {
+ var i;
+ var provData = {};
+ var provisionProfiles = inputs["codesign.embedded_provisioningprofile"];
+ for (i in provisionProfiles) {
+ var plist = new PropertyList();
+ try {
+ plist.readFromData(Utilities.smimeMessageContent(
+ provisionProfiles[i].filePath));
+ provData = plist.toObject();
+ } finally {
+ plist.clear();
+ }
+ }
+
+ 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 = [];
+ if (platformPath) {
+ entitlementsSources.push(
+ entitlementsFileContents(
+ FileInfo.joinPaths(platformPath, "Entitlements.plist")));
+ }
+ if (sdkPath) {
+ entitlementsSources.push(
+ 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["codesign.xcent"][0].filePath, "xml1");
+ } finally {
+ plist.clear();
+ }
+ };
+ return [cmd];
+ }
+ }
+}
diff --git a/share/qbs/modules/codesign/codesign.js b/share/qbs/modules/codesign/codesign.js
new file mode 100644
index 000000000..482225ea2
--- /dev/null
+++ b/share/qbs/modules/codesign/codesign.js
@@ -0,0 +1,493 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** 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 = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var PathTools = require("qbs.PathTools");
+var Process = require("qbs.Process");
+var PropertyList = require("qbs.PropertyList");
+var Utilities = require("qbs.Utilities");
+
+function findSigningIdentities(searchString, team) {
+ if (!searchString)
+ return {};
+ var identities = Utilities.signingIdentities();
+ var matchedIdentities = {};
+ for (var key in identities) {
+ var identity = identities[key];
+ if (team && ![identity.subjectInfo.O, identity.subjectInfo.OU].includes(team))
+ continue;
+ if (searchString === key
+ || (identity.subjectInfo.CN && identity.subjectInfo.CN.startsWith(searchString))) {
+ matchedIdentities[key] = identity;
+ }
+ }
+ return matchedIdentities;
+}
+
+function humanReadableIdentitySummary(identities) {
+ return "\n\t" + Object.keys(identities).map(function (key) {
+ return identities[key].subjectInfo.CN
+ + " in team "
+ + identities[key].subjectInfo.O
+ + " (" + identities[key].subjectInfo.OU + ")";
+ }).join("\n\t");
+}
+
+/**
+ * Returns the best provisioning profile for code signing a binary with the given parameters.
+ * Ideally, this should behave identically as Xcode but the algorithm is not documented
+ * \l{https://developer.apple.com/library/ios/qa/qa1814/_index.html}{Automatic Provisioning}
+ */
+function findBestProvisioningProfile(product, files) {
+ var actualSigningIdentity = product.codesign._actualSigningIdentity || {};
+ var teamIdentifier = product.codesign.teamIdentifier;
+ var bundleIdentifier = product.bundle.identifier;
+ var targetOS = product.qbs.targetOS;
+ var buildVariant = product.qbs.buildVariant;
+ var query = product.codesign.provisioningProfile;
+ var profilePlatform = product.codesign._provisioningProfilePlatform;
+
+ // Read all provisioning profiles on disk into plist objects in memory
+ var profiles = files.map(function(filePath) {
+ var plist = new PropertyList();
+ try {
+ plist.readFromData(Utilities.smimeMessageContent(filePath));
+ return {
+ data: plist.toObject(),
+ filePath: filePath
+ };
+ } finally {
+ plist.clear();
+ }
+ });
+
+ // Do a simple search by matching UUID or Name
+ if (query) {
+ for (var i = 0; i < profiles.length; ++i) {
+ var obj = profiles[i];
+ if (obj.data && (obj.data.UUID === query || obj.data.Name === query))
+ return obj;
+ }
+
+ // If we asked for a specific provisioning profile, don't select one automatically
+ return undefined;
+ }
+
+ // Provisioning profiles are not normally used with ad-hoc code signing or non-apps
+ // We do these checks down here only for the automatic selection but not above because
+ // if the user explicitly selects a provisioning profile it should be used no matter what
+ if (actualSigningIdentity.SHA1 === "-" || !product.type.includes("application"))
+ return undefined;
+
+ // Filter out any provisioning profiles we know to be unsuitable from the start
+ profiles = profiles.filter(function (profile) {
+ var data = profile.data;
+
+ if (actualSigningIdentity.subjectInfo) {
+ var certCommonNames = (data["DeveloperCertificates"] || []).map(function (cert) {
+ return Utilities.certificateInfo(cert).subjectInfo.CN;
+ });
+ if (!certCommonNames.includes(actualSigningIdentity.subjectInfo.CN)) {
+ console.log("Skipping provisioning profile with no matching certificate names for '"
+ + actualSigningIdentity.subjectInfo.CN
+ + "' (found " + certCommonNames.join(", ") + "): "
+ + profile.filePath);
+ return false;
+ }
+ }
+
+ var platforms = data["Platform"] || [];
+ if (platforms.length > 0 && profilePlatform && !platforms.includes(profilePlatform)) {
+ console.log("Skipping provisioning profile for platform " + platforms.join(", ")
+ + " (current platform " + profilePlatform + ")"
+ + ": " + profile.filePath);
+ return false;
+ }
+
+ if (teamIdentifier
+ && !data["TeamIdentifier"].includes(teamIdentifier)
+ && data["TeamName"] !== teamIdentifier) {
+ console.log("Skipping provisioning profile for team " + data["TeamIdentifier"]
+ + " (" + data["TeamName"] + ") (current team " + teamIdentifier + ")"
+ + ": " + profile.filePath);
+ return false;
+ }
+
+ if (Date.parse(data["ExpirationDate"]) <= Date.now()) {
+ console.log("Skipping expired provisioning profile: " + profile.filePath);
+ return false;
+ }
+
+ // Filter development vs distribution profiles;
+ // though the certificate common names check should have been sufficient
+ var isDebug = buildVariant === "debug";
+ if (data["Entitlements"]["get-task-allow"] !== isDebug) {
+ console.log("Skipping provisioning profile for wrong debug mode: " + profile.filePath);
+ return false;
+ }
+
+ var prefix = data["ApplicationIdentifierPrefix"];
+ var fullAppId = data["Entitlements"]["application-identifier"];
+ if ([prefix, bundleIdentifier].join(".") !== fullAppId
+ && [prefix, "*"].join(".") !== fullAppId) {
+ console.log("Skipping provisioning profile not matching full ("
+ + [prefix, bundleIdentifier].join(".") + ") or wildcard ("
+ + [prefix, "*"].join(".") + ") app ID (found " + fullAppId + "): "
+ + profile.filePath);
+ return false;
+ }
+
+ return true;
+ });
+
+ // Sort by expiration date - sooner expiration dates come last
+ profiles.sort(function(profileA, profileB) {
+ var expA = Date.parse(profileA.data["ExpirationDate"]);
+ var expB = Date.parse(profileB.data["ExpirationDate"]);
+ if (expA < expB)
+ return -1;
+ if (expA > expB)
+ return 1;
+ return 0;
+ });
+
+ // Sort by application identifier - wildcard profiles come last
+ profiles.sort(function(profileA, profileB) {
+ var idA = profileA.data["Entitlements"]["application-identifier"];
+ var idB = profileB.data["Entitlements"]["application-identifier"];
+ if (!idA.endsWith(".*") && idB.endsWith(".*"))
+ return -1;
+ if (idA.endsWith(".*") && !idB.endsWith(".*"))
+ return 1;
+ return 0;
+ });
+
+ if (profiles.length) {
+ console.log("Automatic provisioning using profile "
+ + profiles[0].data.UUID
+ + " ("
+ + profiles[0].data.TeamName
+ + " - "
+ + profiles[0].data.Name
+ + ") in product "
+ + product.name);
+ return profiles[0];
+ }
+}
+
+/**
+ * Finds out the search paths for the `signtool.exe` utility supplied with
+ * the Windows SDK's.
+ */
+function findBestSignToolSearchPaths(arch) {
+ var searchPaths = [];
+ var keys = [
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows",
+ "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Microsoft SDKs\\Windows"
+ ];
+ for (var keyIndex = 0; keyIndex < keys.length; ++keyIndex) {
+ var re = /^v([0-9]+)\.([0-9]+)$/;
+ var groups = Utilities.nativeSettingGroups(keys[keyIndex]).filter(function(version) {
+ return version.match(re);
+ });
+
+ groups.sort(function(a, b) {
+ return Utilities.versionCompare(b.substring(1), a.substring(1));
+ });
+
+ function addSearchPath(searchPath) {
+ if (File.exists(searchPath) && !searchPaths.includes(searchPath)) {
+ searchPaths.push(searchPath);
+ return true;
+ }
+ return false;
+ }
+
+ for (var groupIndex = 0; groupIndex < groups.length; ++groupIndex) {
+ var fullKey = keys[keyIndex] + "\\" + groups[groupIndex];
+ var fullVersion = Utilities.getNativeSetting(fullKey, "ProductVersion");
+ if (fullVersion) {
+ var installRoot = FileInfo.cleanPath(
+ Utilities.getNativeSetting(fullKey, "InstallationFolder"));
+ if (installRoot) {
+ // Try to add the architecture-independent path at first.
+ var searchPath = FileInfo.joinPaths(installRoot, "App Certification Kit");
+ if (!addSearchPath(searchPath)) {
+ // Try to add the architecture-dependent paths at second.
+ var binSearchPath = FileInfo.joinPaths(installRoot, "bin/" + fullVersion);
+ if (!File.exists(binSearchPath)) {
+ binSearchPath += ".0";
+ if (!File.exists(binSearchPath))
+ continue;
+ }
+
+ function kitsArchitectureSubDirectory(arch) {
+ if (arch === "x86")
+ return "x86";
+ else if (arch === "x86_64")
+ return "x64";
+ else if (arch.startsWith("arm64"))
+ return "arm64";
+ else if (arch.startsWith("arm"))
+ return "arm";
+ }
+
+ var archDir = kitsArchitectureSubDirectory(arch);
+ searchPath = FileInfo.joinPaths(binSearchPath, archDir);
+ addSearchPath(searchPath);
+ }
+ }
+ }
+ }
+ }
+
+ return searchPaths;
+}
+
+function prepareSign(project, product, inputs, outputs, input, output) {
+ var cmd, cmds = [];
+
+ if (!product.codesign.enableCodeSigning)
+ return cmds;
+
+ var isBundle = "bundle.content" in outputs;
+
+ var artifacts = [];
+ if (isBundle) {
+ artifacts = [{
+ filePath: FileInfo.joinPaths(product.destinationDirectory, product.bundle.bundleName),
+ fileName: product.bundle.bundleName
+ }];
+ } else {
+ artifacts = outputs["codesign.signed_artifact"];
+ }
+ var isProductBundle = product.bundle && product.bundle.isBundle;
+ var shouldSignArtifact = !isProductBundle || isBundle;
+
+ var enableCodeSigning = product.codesign.enableCodeSigning;
+ if (enableCodeSigning) {
+ var actualSigningIdentity = product.codesign._actualSigningIdentity;
+ if (!actualSigningIdentity) {
+ throw "No codesigning identities (i.e. certificate and private key pairs) matching “"
+ + product.codesign.signingIdentity + "” were found.";
+ }
+
+ // If this is a framework, we need to sign its versioned directory
+ var subpath = "";
+ if (isBundle) {
+ var isFramework = product.bundle.packageType === "FMWK";
+ if (isFramework) {
+ subpath = product.bundle.contentsFolderPath;
+ subpath = subpath.substring(product.bundle.bundleName.length);
+ }
+ }
+
+ // The codesign tool behaves weirdly. It can sign a bundle with a single artifact, but if
+ // say debug build variant is present, it starts complaining that it is not signed.
+ // We could always sign everything, but again, in case of a framework (but not in case of
+ // app or loadable bundle), codesign produces a warning that artifact is already signed.
+ // So, we skip signing the release artifact and only sign if other build variants present.
+ if (!shouldSignArtifact && artifacts.length == 1) {
+ artifacts = [];
+ }
+ for (var i = 0; i < artifacts.length; ++i) {
+ if (!shouldSignArtifact
+ && artifacts[i].qbs && artifacts[i].qbs.buildVariant === "release") {
+ continue;
+ }
+ var outputFilePath = artifacts[i].filePath;
+ var outputFileName = artifacts[i].fileName;
+
+ var args = ["--force", "--sign", actualSigningIdentity.SHA1];
+
+ // If signingTimestamp is undefined or empty, do not specify the flag at all -
+ // this uses the system-specific default behavior
+ var signingTimestamp = product.codesign.signingTimestamp;
+ if (signingTimestamp) {
+ // 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;
+ args.push(flag);
+ }
+
+ for (var j in inputs["codesign.xcent"]) {
+ args.push("--entitlements", inputs["codesign.xcent"][j].filePath);
+ break; // there should only be one
+ }
+
+ args = args.concat(product.codesign.codesignFlags || []);
+
+ args.push(outputFilePath + subpath);
+ cmd = new Command(product.codesign.codesignPath, args);
+ cmd.description = "codesign " + outputFileName
+ + " (" + actualSigningIdentity.subjectInfo.CN + ")";
+ cmd.outputFilePath = outputFilePath;
+ cmd.stderrFilterFunction = function(stderr) {
+ return stderr.replace(outputFilePath + ": replacing existing signature\n", "");
+ };
+ cmds.push(cmd);
+ }
+ }
+
+ if (isBundle) {
+ cmd = new Command("touch", ["-c", outputFilePath]);
+ cmd.silent = true;
+ cmds.push(cmd);
+ }
+
+ return cmds;
+}
+
+function signApkPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var apkInput = inputs["android.package_unsigned"][0];
+ var apkOutput = outputs["android.package"][0];
+ var cmd;
+ if (product.codesign.enableCodeSigning) {
+ var args = ["sign",
+ "--ks", product.codesign.keystorePath,
+ "--ks-pass", "pass:" + product.codesign.keystorePassword,
+ "--ks-key-alias", product.codesign.keyAlias,
+ "--key-pass", "pass:" + product.codesign.keyPassword,
+ "--out", apkOutput.filePath,
+ apkInput.filePath];
+ cmd = new Command(product.codesign.apksignerFilePath, args);
+ cmd.description = "signing " + apkOutput.fileName;
+ } else {
+ cmd = new JavaScriptCommand();
+ cmd.description = "copying without signing " + apkOutput.fileName;
+ cmd.source = apkInput.filePath;
+ cmd.target = apkOutput.filePath;
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ // If enableCodeSigning is changed to false without any change to unsigned package then
+ // the copy won't happen because of timestamps. So the target file needs file needs to
+ // be removed to avoid it.
+ File.remove(target);
+ File.copy(source, target);
+ }
+ }
+ return cmd;
+}
+
+function signAabPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var aabInput = inputs["android.package_unsigned"][0];
+ var aabOutput = outputs["android.package"][0];
+ var cmd;
+ if (product.codesign.enableCodeSigning) {
+ args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1",
+ "-keystore", product.codesign.keystorePath,
+ "-storepass", product.codesign.keystorePassword,
+ "-keypass", product.codesign.keyPassword,
+ "-signedjar", aabOutput.filePath,
+ aabInput.filePath,
+ product.codesign.keyAlias];
+ cmd = new Command(product.codesign.jarsignerFilePath, args);
+ cmd.description = "signing " + aabOutput.fileName;
+ } else {
+ cmd = new JavaScriptCommand();
+ cmd.description = "copying without signing " + aabOutput.fileName;
+ cmd.source = aabInput.filePath;
+ cmd.target = aabOutput.filePath;
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ // If enableCodeSigning is changed to false without any change to unsigned package then
+ // the copy won't happen because of timestamps. So the target file needs file needs to
+ // be removed to avoid it.
+ File.remove(target);
+ File.copy(source, target);
+ }
+ }
+ return cmd;
+}
+
+function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath, keystorePassword,
+ keyPassword, keyAlias) {
+ var args = ["-genkey", "-keystore", keystoreFilePath, "-alias", keyAlias,
+ "-storepass", keystorePassword, "-keypass", keyPassword, "-keyalg", "RSA",
+ "-keysize", "2048", "-validity", "10000", "-dname",
+ "CN=Android Debug,O=Android,C=US"];
+ return Process.shellQuote(keytoolFilePath, args);
+}
+
+function prepareSigntool(project, product, inputs, outputs, input, output) {
+ var cmd, cmds = [];
+
+ if (!product.codesign.enableCodeSigning)
+ return cmds;
+
+ var args = ["sign"];
+
+ var subjectName = product.codesign.subjectName;
+ if (subjectName)
+ args.push("/n", subjectName);
+
+ var rootSubjectName = product.codesign.rootSubjectName;
+ if (rootSubjectName)
+ args.push("/r", rootSubjectName);
+
+ var hashAlgorithm = product.codesign.hashAlgorithm;
+ if (hashAlgorithm)
+ args.push("/fd", hashAlgorithm);
+
+ var signingTimestamp = product.codesign.signingTimestamp;
+ if (signingTimestamp)
+ args.push("/tr", signingTimestamp);
+
+ var timestampAlgorithm = product.codesign.timestampAlgorithm;
+ if (timestampAlgorithm)
+ args.push("/td", timestampAlgorithm);
+
+ var certificatePath = product.codesign.certificatePath;
+ if (certificatePath)
+ args.push("/f", certificatePath);
+
+ var certificatePassword = product.codesign.certificatePassword;
+ if (certificatePassword)
+ args.push("/p", certificatePassword);
+
+ var crossCertificatePath = product.codesign.crossCertificatePath;
+ if (crossCertificatePath)
+ args.push("/ac", crossCertificatePath);
+
+ args = args.concat(product.codesign.codesignFlags || []);
+
+ var outputArtifact = outputs["codesign.signed_artifact"][0];
+ args.push(outputArtifact.filePath);
+
+ cmd = new Command(product.codesign.codesignPath, args);
+ cmd.description = "signing " + outputArtifact.fileName;
+ cmd.highlight = "linker";
+ cmds.push(cmd);
+ return cmds;
+}
diff --git a/share/qbs/modules/codesign/noop.qbs b/share/qbs/modules/codesign/noop.qbs
new file mode 100644
index 000000000..d8c75eabc
--- /dev/null
+++ b/share/qbs/modules/codesign/noop.qbs
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** 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.
+**
+****************************************************************************/
+
+CodeSignModule {
+ condition: true
+ priority: -100
+}
diff --git a/share/qbs/modules/codesign/signtool.qbs b/share/qbs/modules/codesign/signtool.qbs
new file mode 100644
index 000000000..111f0a307
--- /dev/null
+++ b/share/qbs/modules/codesign/signtool.qbs
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** 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.File
+import qbs.Host
+import qbs.ModUtils
+import qbs.Probes
+import "codesign.js" as CODESIGN
+
+CodeSignModule {
+ condition: qbs.targetOS.includes("windows")
+ && Host.os().includes("windows")
+ && qbs.toolchain.includes("msvc")
+
+ _canSignArtifacts: true
+
+ Probes.BinaryProbe {
+ id: signtoolProbe
+ names: [codesignName]
+ searchPaths: CODESIGN.findBestSignToolSearchPaths(Host.architecture())
+ }
+
+ codesignName: "signtool"
+ codesignPath: signtoolProbe.filePath
+
+ property string subjectName
+ PropertyOptions {
+ name: "subjectName"
+ description: "Name of the subject of the signing certificate."
+ }
+
+ property string rootSubjectName
+ PropertyOptions {
+ name: "rootSubjectName"
+ description: "Name of the subject of the root certificate that the signing " +
+ "certificate must chain to."
+ }
+
+ property string hashAlgorithm
+ PropertyOptions {
+ name: "hashAlgorithm"
+ description: "Name of the hash algorithm used on the signing certificate."
+ allowedValues: ["sha1", "sha256", "sha384", "sha512"]
+ }
+
+ property string timestampAlgorithm
+ PropertyOptions {
+ name: "timestampAlgorithm"
+ description: "Name of the timestamp algorithm."
+ allowedValues: ["sha1", "sha256"]
+ }
+
+ property path certificatePath
+ PropertyOptions {
+ name: "certificatePath"
+ description: "Path to the signing certificate PFX file."
+ }
+
+ property string certificatePassword
+ PropertyOptions {
+ name: "certificatePassword"
+ description: "Password to use when opening a certificate PFX file."
+ }
+
+ property path crossCertificatePath
+ PropertyOptions {
+ name: "crossCertificatePath"
+ description: "Path to the additional certificate CER file."
+ }
+
+ validate: {
+ if (enableCodeSigning && !File.exists(codesignPath)) {
+ throw ModUtils.ModuleError("Could not find 'signtool' utility");
+ }
+ }
+}
diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs
index bdd6d2750..bd1eaa242 100644
--- a/share/qbs/modules/cpp/CppModule.qbs
+++ b/share/qbs/modules/cpp/CppModule.qbs
@@ -29,10 +29,11 @@
****************************************************************************/
// base for Cpp modules
+import qbs.FileInfo
import qbs.ModUtils
import qbs.Utilities
import qbs.WindowsUtils
-
+import "cpp.js" as Cpp
import "setuprunenv.js" as SetupRunEnv
Module {
@@ -128,7 +129,7 @@ Module {
defaults will be used."
}
- property string minimumAndroidVersion
+ property string minimumAndroidVersion // not used, undocumented
PropertyOptions {
name: "minimumAndroidVersion"
description: "a version number in the format [major].[minor] indicating the earliest \
@@ -136,7 +137,7 @@ Module {
version which is then written to AndroidManifest.xml."
}
- property string maximumAndroidVersion
+ property string maximumAndroidVersion // not used, undocumented
PropertyOptions {
name: "maximumAndroidVersion"
description: "a version number in the format [major].[minor] indicating the latest \
@@ -145,6 +146,12 @@ Module {
be set."
}
+ property string toolchainInstallPath
+ PropertyOptions {
+ name: "toolchainInstallPath"
+ description: "a path to the directory where the toolchain executable files are located."
+ }
+
property pathList includePaths
property pathList systemIncludePaths
property pathList distributionIncludePaths
@@ -173,12 +180,19 @@ Module {
property string executablePrefix: ""
property string staticLibrarySuffix: ""
property string dynamicLibrarySuffix: ""
+ property string archSuffix: ""
property string loadableModuleSuffix: ""
property string executableSuffix: ""
property string debugInfoSuffix: ""
property string debugInfoBundleSuffix: ""
property string variantSuffix: ""
property string dynamicLibraryImportSuffix: ".lib"
+ property string objectSuffix: ".o"
+ property string linkerMapSuffix: ".map"
+ property string compilerListingSuffix: ".lst"
+ property string assemblerListingSuffix: ".lst"
+ property string resourceSuffix: ".res"
+ property string precompiledHeaderSuffix
property bool createSymlinks: true
property stringList dynamicLibraries // list of names, will be linked with -lname
property stringList staticLibraries // list of static library files
@@ -193,6 +207,33 @@ Module {
property bool discardUnusedData
property bool removeDuplicateLibraries: true
+ property string defineFlag
+ property string includeFlag
+ property string systemIncludeFlag
+ property string preincludeFlag
+ property string libraryDependencyFlag
+ property string libraryPathFlag
+ property string linkerScriptFlag
+
+ property stringList knownArchitectures: []
+ property var toolchainDetails
+ property string compilerExtension: FileInfo.executableSuffix()
+
+ 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 stringList assemblerFlags
PropertyOptions {
name: "assemblerFlags"
@@ -299,14 +340,12 @@ Module {
property stringList cLanguageVersion
PropertyOptions {
name: "cLanguageVersion"
- allowedValues: ["c89", "c99", "c11"]
description: "The version of the C standard with which the code must comply."
}
property stringList cxxLanguageVersion
PropertyOptions {
name: "cxxLanguageVersion"
- allowedValues: ["c++98", "c++11", "c++14", "c++17"]
description: "The version of the C++ standard with which the code must comply."
}
@@ -369,13 +408,14 @@ Module {
property bool combineObjcSources: false
property bool combineObjcxxSources: false
+ // Those are set internally by different cpp module implementations
property stringList targetAssemblerFlags
property stringList targetDriverFlags
property stringList targetLinkerFlags
property bool _skipAllChecks: false // Internal
- property bool validateTargetTriple: true
+ property bool validateTargetTriple: true // undocumented
// TODO: The following four rules could use a convenience base item if rule properties
// were available in Artifact items and prepare scripts.
@@ -508,6 +548,13 @@ Module {
return '"' + a + '"'; }).join(", ")
+ ". See https://docs.microsoft.com/en-us/windows/desktop/SysInfo/operating-system-version");
}
+
+ if (knownArchitectures && knownArchitectures.length > 0) {
+ var isSupported = Cpp.supportsArchitecture(qbs.architecture, knownArchitectures);
+ if (!isSupported) {
+ throw ModUtils.ModuleError("Unsupported architecture: '" + qbs.architecture + "'");
+ }
+ }
}
}
diff --git a/share/qbs/modules/cpp/DarwinGCC.qbs b/share/qbs/modules/cpp/DarwinGCC.qbs
index 9332603ec..e20973acc 100644
--- a/share/qbs/modules/cpp/DarwinGCC.qbs
+++ b/share/qbs/modules/cpp/DarwinGCC.qbs
@@ -43,7 +43,7 @@ import "gcc.js" as Gcc
UnixGCC {
condition: false
- Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.contains("xcode") }
+ Depends { name: "xcode"; required: qbs.toolchain && qbs.toolchain.includes("xcode") }
Probes.BinaryProbe {
id: lipoProbe
@@ -76,7 +76,7 @@ UnixGCC {
dynamicLibrarySuffix: ".dylib"
Properties {
- condition: product.multiplexByQbsProperties.contains("buildVariants")
+ condition: product.multiplexByQbsProperties.includes("buildVariants")
&& qbs.buildVariants && qbs.buildVariants.length > 1
&& (!product.aggregate || !!product.multiplexConfigurationId)
&& qbs.buildVariant !== "release"
@@ -98,7 +98,7 @@ UnixGCC {
setupBuildEnvironment: {
for (var key in product.cpp.buildEnv) {
- v = new ModUtils.EnvironmentVariable(key);
+ var v = new ModUtils.EnvironmentVariable(key);
v.value = product.cpp.buildEnv[key];
v.set();
}
@@ -107,32 +107,42 @@ UnixGCC {
property var defaultInfoPlist: {
var dict = {};
- if (qbs.targetOS.contains("macos")) {
+ if (qbs.targetOS.includes("macos")) {
dict["NSPrincipalClass"] = "NSApplication"; // needed for Retina display support
+ // QBS-1670: set this flag by default to avoid extensive GPU usage
+ dict["NSSupportsAutomaticGraphicsSwitching"] = true;
+
if (minimumMacosVersion)
dict["LSMinimumSystemVersion"] = minimumMacosVersion;
}
+ if (qbs.targetOS.includes("ios") && minimumIosVersion)
+ dict["MinimumOSVersion"] = minimumIosVersion;
+ else if (qbs.targetOS.includes("tvos") && minimumTvosVersion)
+ dict["MinimumOSVersion"] = minimumTvosVersion;
+ else if (qbs.targetOS.includes("watchos") && minimumWatchosVersion)
+ dict["MinimumOSVersion"] = minimumWatchosVersion;
+
if (qbs.targetOS.containsAny(["ios", "tvos"])) {
dict["LSRequiresIPhoneOS"] = true;
if (xcode.platformType === "device") {
- if (qbs.targetOS.contains("ios")) {
+ if (qbs.targetOS.includes("ios")) {
if (qbs.architecture === "arm64")
dict["UIRequiredDeviceCapabilities"] = ["arm64"];
else
dict["UIRequiredDeviceCapabilities"] = ["armv7"];
}
- if (qbs.targetOS.contains("tvos"))
+ if (qbs.targetOS.includes("tvos"))
dict["UIRequiredDeviceCapabilities"] = ["arm64"];
}
}
if (xcode.present) {
var targetDevices = DarwinTools.targetedDeviceFamily(xcode.targetDevices);
- if (qbs.targetOS.contains("ios"))
+ if (qbs.targetOS.includes("ios"))
dict["UIDeviceFamily"] = targetDevices;
if (qbs.targetOS.containsAny(["ios", "watchos"])) {
@@ -143,13 +153,13 @@ UnixGCC {
"UIInterfaceOrientationLandscapeRight"
];
- if (targetDevices.contains("ipad"))
+ if (targetDevices.includes("ipad"))
dict["UISupportedInterfaceOrientations~ipad"] = orientations;
- if (targetDevices.contains("watch"))
+ if (targetDevices.includes("watch"))
dict["UISupportedInterfaceOrientations"] = orientations.slice(0, 2);
- if (targetDevices.contains("iphone")) {
+ if (targetDevices.includes("iphone")) {
orientations.splice(1, 1);
dict["UISupportedInterfaceOrientations"] = orientations;
}
@@ -189,13 +199,13 @@ UnixGCC {
// 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") && minimumIosVersion)
+ if (qbs.targetOS.includes("ios") && minimumIosVersion)
env["IPHONEOS_DEPLOYMENT_TARGET"] = minimumIosVersion;
- if (qbs.targetOS.contains("macos") && minimumMacosVersion)
+ if (qbs.targetOS.includes("macos") && minimumMacosVersion)
env["MACOSX_DEPLOYMENT_TARGET"] = minimumMacosVersion;
- if (qbs.targetOS.contains("watchos") && minimumWatchosVersion)
+ if (qbs.targetOS.includes("watchos") && minimumWatchosVersion)
env["WATCHOS_DEPLOYMENT_TARGET"] = minimumWatchosVersion;
- if (qbs.targetOS.contains("tvos") && minimumTvosVersion)
+ if (qbs.targetOS.includes("tvos") && minimumTvosVersion)
env["TVOS_DEPLOYMENT_TARGET"] = minimumTvosVersion;
if (xcode.present)
@@ -208,7 +218,7 @@ UnixGCC {
property string minimumDarwinVersionCompilerFlag
property string minimumDarwinVersionLinkerFlag
- property bool libcxxAvailable: qbs.toolchain.contains("clang") && cxxLanguageVersion !== "c++98"
+ property bool libcxxAvailable: qbs.toolchain.includes("clang") && cxxLanguageVersion !== "c++98"
Rule {
condition: enableAggregationRules
@@ -216,7 +226,8 @@ UnixGCC {
multiplex: true
outputFileTags: ["bundle.input", "application", "primary", "debuginfo_app",
- "debuginfo_bundle", "bundle.variant_symlink", "debuginfo_plist"]
+ "debuginfo_bundle", "bundle.variant_symlink", "debuginfo_plist",
+ "codesign.signed_artifact"]
outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "application", "app")
prepare: Darwin.prepareLipo.apply(Darwin, arguments)
@@ -227,7 +238,8 @@ UnixGCC {
inputsFromDependencies: ["loadablemodule"]
multiplex: true
- outputFileTags: ["bundle.input", "loadablemodule", "primary", "debuginfo_loadablemodule"]
+ outputFileTags: ["bundle.input", "loadablemodule", "primary", "debuginfo_loadablemodule",
+ "debuginfo_bundle", "debuginfo_plist", "codesign.signed_artifact"]
outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "loadablemodule",
"loadablemodule")
@@ -241,7 +253,7 @@ UnixGCC {
outputFileTags: ["bundle.input", "dynamiclibrary", "dynamiclibrary_symbols", "primary",
"debuginfo_dll","debuginfo_bundle","bundle.variant_symlink",
- "debuginfo_plist"]
+ "debuginfo_plist", "codesign.signed_artifact"]
outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "dynamiclibrary", "dll")
prepare: Darwin.prepareLipo.apply(Darwin, arguments)
@@ -252,14 +264,14 @@ UnixGCC {
inputsFromDependencies: ["staticlibrary"]
multiplex: true
- outputFileTags: ["bundle.input", "staticlibrary", "primary"]
+ outputFileTags: ["bundle.input", "staticlibrary", "primary", "codesign.signed_artifact"]
outputArtifacts: Darwin.lipoOutputArtifacts(product, inputs, "staticlibrary")
prepare: Darwin.prepareLipo.apply(Darwin, arguments)
}
Rule {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
multiplex: true
Artifact {
diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs
index 63d5db7b8..f84028940 100644
--- a/share/qbs/modules/cpp/GenericGCC.qbs
+++ b/share/qbs/modules/cpp/GenericGCC.qbs
@@ -30,6 +30,7 @@
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.PathTools
import qbs.Probes
@@ -38,12 +39,15 @@ import qbs.TextFile
import qbs.Utilities
import qbs.UnixUtils
import qbs.WindowsUtils
+import 'cpp.js' as Cpp
import 'gcc.js' as Gcc
CppModule {
- condition: qbs.toolchain && qbs.toolchain.contains("gcc")
+ condition: qbs.toolchain && qbs.toolchain.includes("gcc")
priority: -100
+ Depends { name: "codesign" }
+
Probes.GccBinaryProbe {
id: compilerPathProbe
condition: !toolchainInstallPath && !_skipAllChecks
@@ -114,9 +118,14 @@ CppModule {
compilerFrameworkPaths: gccProbe.frameworkPaths
compilerLibraryPaths: gccProbe.libraryPaths
- property bool compilerHasTargetOption: qbs.toolchain.contains("clang")
+ staticLibraryPrefix: "lib"
+ staticLibrarySuffix: ".a"
+
+ precompiledHeaderSuffix: ".gch"
+
+ property bool compilerHasTargetOption: qbs.toolchain.includes("clang")
&& Utilities.versionCompare(compilerVersion, "3.1") >= 0
- property bool assemblerHasTargetOption: qbs.toolchain.contains("xcode")
+ property bool assemblerHasTargetOption: qbs.toolchain.includes("xcode")
&& Utilities.versionCompare(compilerVersion, "7") >= 0
property string target: targetArch
? [targetArch, targetVendor, targetSystem, targetAbi].join("-")
@@ -131,8 +140,7 @@ CppModule {
property string toolchainPrefix: compilerPathProbe.found
? compilerPathProbe.tcPrefix
: undefined
- property string toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path
- : undefined
+ toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined
property string binutilsPath: binutilsProbe.found ? binutilsProbe.path : toolchainInstallPath
assemblerName: 'as' + compilerExtension
@@ -148,21 +156,6 @@ CppModule {
property string syslibroot: sysroot
property stringList sysrootFlags: sysroot ? ["--sysroot=" + 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"
@@ -177,7 +170,7 @@ CppModule {
property string linkerVariant
PropertyOptions {
name: "linkerVariant"
- allowedValues: ["bfd", "gold", "lld"]
+ allowedValues: ["bfd", "gold", "lld", "mold"]
description: "Allows to specify the linker variant. Maps to gcc's and clang's -fuse-ld "
+ "option."
}
@@ -189,10 +182,9 @@ CppModule {
property string toolchainPathPrefix: Gcc.pathPrefix(toolchainInstallPath, toolchainPrefix)
property string binutilsPathPrefix: Gcc.pathPrefix(binutilsPath, toolchainPrefix)
- property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : ""
- property string cCompilerName: (qbs.toolchain.contains("clang") ? "clang" : "gcc")
+ property string cCompilerName: (qbs.toolchain.includes("clang") ? "clang" : "gcc")
+ compilerExtension
- property string cxxCompilerName: (qbs.toolchain.contains("clang") ? "clang++" : "g++")
+ property string cxxCompilerName: (qbs.toolchain.includes("clang") ? "clang++" : "g++")
+ compilerExtension
compilerPathByLanguage: ({
@@ -216,13 +208,22 @@ CppModule {
property stringList dsymutilFlags
property bool alwaysUseLipo: false
- property string includeFlag: "-I"
- property string systemIncludeFlag: "-isystem"
+ defineFlag: "-D"
+ includeFlag: "-I"
+ systemIncludeFlag: "-isystem"
+ preincludeFlag: "-include"
+ libraryPathFlag: "-L"
+ linkerScriptFlag: "-T"
readonly property bool shouldCreateSymlinks: {
- return createSymlinks && internalVersion && ["macho", "elf"].contains(cpp.imageFormat);
+ return createSymlinks && internalVersion && ["macho", "elf"].includes(imageFormat);
}
+ readonly property bool shouldSignArtifacts: codesign._canSignArtifacts
+ && codesign.enableCodeSigning
+ // codesigning is done during the lipo step
+ && !product.multiplexed
+
property string internalVersion: {
if (product.version === undefined)
return undefined;
@@ -250,18 +251,18 @@ CppModule {
property var buildEnv: {
var env = {};
- if (qbs.toolchain.contains("mingw"))
- env.PATH = [toolchainInstallPath]; // For libwinpthread etc
+ if (qbs.toolchain.includes("mingw"))
+ env.PATH = toolchainInstallPath; // For libwinpthread etc
return env;
}
exceptionHandlingModel: {
- if (qbs.toolchain.contains("mingw")) {
+ if (qbs.toolchain.includes("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"))
+ if (!Host.os().includes("windows"))
prefix = FileInfo.joinPaths(toolchainInstallPath, "..", "lib", "gcc",
toolchainPrefix,
[compilerVersionMajor, compilerVersionMinor].join("."));
@@ -313,7 +314,7 @@ CppModule {
if (gccProbe.targetPlatform) {
// Can't differentiate Darwin OSes at the compiler level alone
if (gccProbe.targetPlatform === "darwin"
- ? !qbs.targetOS.contains("darwin")
+ ? !qbs.targetOS.includes("darwin")
: qbs.targetPlatform !== gccProbe.targetPlatform)
isWrongTriple = true;
} else if (qbs.targetPlatform) {
@@ -350,7 +351,7 @@ CppModule {
var validateFlagsFunction = function (value) {
if (value) {
for (var i = 0; i < value.length; ++i) {
- if (["-target", "-triple", "-arch"].contains(value[i]))
+ if (["-target", "-triple", "-arch"].includes(value[i]))
return false;
}
}
@@ -400,28 +401,33 @@ CppModule {
condition: product.cpp.shouldLink
multiplex: true
inputs: {
- var tags = ["obj", "linkerscript", "versionscript"];
+ var tags = ["obj", "res", "linkerscript", "versionscript"];
if (product.bundle && product.bundle.embedInfoPlist
- && product.qbs.targetOS.contains("darwin")) {
+ && product.qbs.targetOS.includes("darwin")) {
tags.push("aggregate_infoplist");
}
return tags;
}
inputsFromDependencies: ["dynamiclibrary_symbols", "staticlibrary", "dynamiclibrary_import"]
- outputFileTags: [
- "bundle.input",
- "dynamiclibrary", "dynamiclibrary_symlink", "dynamiclibrary_symbols", "debuginfo_dll",
- "debuginfo_bundle","dynamiclibrary_import", "debuginfo_plist",
- ]
+ outputFileTags: {
+ var tags = ["bundle.input", "dynamiclibrary", "dynamiclibrary_symlink",
+ "dynamiclibrary_symbols", "debuginfo_dll", "debuginfo_bundle",
+ "dynamiclibrary_import", "debuginfo_plist"];
+ if (shouldSignArtifacts)
+ tags.push("codesign.signed_artifact");
+ return tags;
+ }
outputArtifacts: {
var artifacts = [{
- filePath: product.destinationDirectory + "/"
- + PathTools.dynamicLibraryFilePath(product),
- fileTags: ["bundle.input", "dynamiclibrary"],
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ PathTools.dynamicLibraryFilePath(product)),
+ fileTags: ["bundle.input", "dynamiclibrary"]
+ .concat(product.cpp.shouldSignArtifacts
+ ? ["codesign.signed_artifact"] : []),
bundle: {
- _bundleFilePath: product.destinationDirectory + "/"
- + PathTools.bundleExecutableFilePath(product)
+ _bundleFilePath: FileInfo.joinPaths(product.destinationDirectory,
+ PathTools.bundleExecutableFilePath(product))
}
}];
if (product.cpp.imageFormat === "pe") {
@@ -445,9 +451,9 @@ CppModule {
var maxVersionParts = product.cpp.internalVersion ? 3 : 1;
for (var i = 0; i < maxVersionParts; ++i) {
var symlink = {
- filePath: product.destinationDirectory + "/"
- + PathTools.dynamicLibraryFilePath(product, undefined, undefined,
- i),
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ PathTools.dynamicLibraryFilePath(
+ product, undefined, undefined, i)),
fileTags: ["dynamiclibrary_symlink"]
};
if (i > 0 && artifacts[i-1].filePath == symlink.filePath)
@@ -460,16 +466,14 @@ CppModule {
return artifacts;
}
- prepare: {
- return Gcc.prepareLinker.apply(Gcc, arguments);
- }
+ prepare: Gcc.prepareLinker.apply(Gcc, arguments)
}
Rule {
name: "staticLibraryLinker"
condition: product.cpp.shouldLink
multiplex: true
- inputs: ["obj", "linkerscript"]
+ inputs: ["obj", "res", "linkerscript"]
inputsFromDependencies: ["dynamiclibrary_symbols", "dynamiclibrary_import", "staticlibrary"]
outputFileTags: ["bundle.input", "staticlibrary", "c_staticlibrary", "cpp_staticlibrary"]
@@ -479,9 +483,9 @@ CppModule {
var objCount = objs ? objs.length : 0;
for (var i = 0; i < objCount; ++i) {
var ft = objs[i].fileTags;
- if (ft.contains("c_obj"))
+ if (ft.includes("c_obj"))
tags.push("c_staticlibrary");
- if (ft.contains("cpp_obj"))
+ if (ft.includes("cpp_obj"))
tags.push("cpp_staticlibrary");
}
return [{
@@ -499,6 +503,8 @@ CppModule {
var args = ['rcs', output.filePath];
for (var i in inputs.obj)
args.push(inputs.obj[i].filePath);
+ for (var i in inputs.res)
+ args.push(inputs.res[i].filePath);
var cmd = new Command(product.cpp.archiverPath, args);
cmd.description = 'creating ' + output.fileName;
cmd.highlight = 'linker'
@@ -513,22 +519,29 @@ CppModule {
condition: product.cpp.shouldLink
multiplex: true
inputs: {
- var tags = ["obj", "linkerscript"];
+ var tags = ["obj", "res", "linkerscript"];
if (product.bundle && product.bundle.embedInfoPlist
- && product.qbs.targetOS.contains("darwin")) {
+ && product.qbs.targetOS.includes("darwin")) {
tags.push("aggregate_infoplist");
}
return tags;
}
inputsFromDependencies: ["dynamiclibrary_symbols", "dynamiclibrary_import", "staticlibrary"]
- outputFileTags: ["bundle.input", "loadablemodule", "debuginfo_loadablemodule",
- "debuginfo_bundle","debuginfo_plist"]
+ outputFileTags: {
+ var tags = ["bundle.input", "loadablemodule", "debuginfo_loadablemodule",
+ "debuginfo_bundle", "debuginfo_plist"];
+ if (shouldSignArtifacts)
+ tags.push("codesign.signed_artifact");
+ return tags;
+ }
outputArtifacts: {
var app = {
filePath: FileInfo.joinPaths(product.destinationDirectory,
PathTools.loadableModuleFilePath(product)),
- fileTags: ["bundle.input", "loadablemodule"],
+ fileTags: ["bundle.input", "loadablemodule"]
+ .concat(product.cpp.shouldSignArtifacts
+ ? ["codesign.signed_artifact"] : []),
bundle: {
_bundleFilePath: FileInfo.joinPaths(product.destinationDirectory,
PathTools.bundleExecutableFilePath(product))
@@ -541,9 +554,7 @@ CppModule {
return artifacts;
}
- prepare: {
- return Gcc.prepareLinker.apply(Gcc, arguments);
- }
+ prepare: Gcc.prepareLinker.apply(Gcc, arguments)
}
Rule {
@@ -551,22 +562,30 @@ CppModule {
condition: product.cpp.shouldLink
multiplex: true
inputs: {
- var tags = ["obj", "linkerscript"];
+ var tags = ["obj", "res", "linkerscript"];
if (product.bundle && product.bundle.embedInfoPlist
- && product.qbs.targetOS.contains("darwin")) {
+ && product.qbs.targetOS.includes("darwin")) {
tags.push("aggregate_infoplist");
}
return tags;
}
inputsFromDependencies: ["dynamiclibrary_symbols", "dynamiclibrary_import", "staticlibrary"]
- outputFileTags: ["bundle.input", "application", "debuginfo_app","debuginfo_bundle",
- "debuginfo_plist", "mem_map"]
+ outputFileTags: {
+ var tags = ["bundle.input", "application", "debuginfo_app", "debuginfo_bundle",
+ "debuginfo_plist"];
+ if (shouldSignArtifacts)
+ tags.push("codesign.signed_artifact");
+ if (generateLinkerMapFile)
+ tags.push("mem_map");
+ return tags;
+ }
outputArtifacts: {
var app = {
filePath: FileInfo.joinPaths(product.destinationDirectory,
PathTools.applicationFilePath(product)),
- fileTags: ["bundle.input", "application"],
+ fileTags: ["bundle.input", "application"].concat(
+ product.cpp.shouldSignArtifacts ? ["codesign.signed_artifact"] : []),
bundle: {
_bundleFilePath: FileInfo.joinPaths(product.destinationDirectory,
PathTools.bundleExecutableFilePath(product))
@@ -578,16 +597,14 @@ CppModule {
if (product.cpp.generateLinkerMapFile) {
artifacts.push({
filePath: FileInfo.joinPaths(product.destinationDirectory,
- product.targetName + ".map"),
+ product.targetName + product.cpp.linkerMapSuffix),
fileTags: ["mem_map"]
});
}
return artifacts;
}
- prepare: {
- return Gcc.prepareLinker.apply(Gcc, arguments);
- }
+ prepare: Gcc.prepareLinker.apply(Gcc, arguments)
}
Rule {
@@ -595,94 +612,53 @@ CppModule {
inputs: ["cpp", "c", "objcpp", "objc", "asm_cpp"]
auxiliaryInputs: ["hpp"]
explicitlyDependsOn: ["c_pch", "cpp_pch", "objc_pch", "objcpp_pch"]
-
- outputFileTags: ["obj", "c_obj", "cpp_obj", "intermediate_obj"]
- outputArtifacts: {
- var tags;
- if (input.fileTags.contains("cpp_intermediate_object"))
- tags = ["intermediate_obj"];
- else
- 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(Utilities.getHash(input.baseDir),
- input.fileName + ".o")
- }];
- }
-
- prepare: {
- return Gcc.prepareCompiler.apply(Gcc, arguments);
- }
+ outputFileTags: Cpp.compilerOutputTags(false).concat(["c_obj", "cpp_obj"])
+ outputArtifacts: Cpp.compilerOutputArtifacts(input, inputs)
+ prepare: Gcc.prepareCompiler.apply(Gcc, arguments)
}
Rule {
name: "assembler"
inputs: ["asm"]
-
- Artifact {
- fileTags: ["obj"]
- filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir), input.fileName + ".o")
- }
-
- prepare: {
- return Gcc.prepareAssembler.apply(Gcc, arguments);
- }
+ outputFileTags: Cpp.assemblerOutputTags(false)
+ outputArtifacts: Cpp.assemblerOutputArtifacts(input)
+ prepare: Gcc.prepareAssembler.apply(Gcc, arguments)
}
Rule {
condition: useCPrecompiledHeader
inputs: ["c_pch_src"]
auxiliaryInputs: ["hpp"]
- Artifact {
- filePath: product.name + "_c.gch"
- fileTags: ["c_pch"]
- }
- prepare: {
- return Gcc.prepareCompiler.apply(Gcc, arguments);
- }
+ outputFileTags: Cpp.precompiledHeaderOutputTags("c", false)
+ outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "c", false)
+ prepare: Gcc.prepareCompiler.apply(Gcc, arguments)
}
Rule {
condition: useCxxPrecompiledHeader
inputs: ["cpp_pch_src"]
auxiliaryInputs: ["hpp"]
- Artifact {
- filePath: product.name + "_cpp.gch"
- fileTags: ["cpp_pch"]
- }
- prepare: {
- return Gcc.prepareCompiler.apply(Gcc, arguments);
- }
+ outputFileTags: Cpp.precompiledHeaderOutputTags("cpp", false)
+ outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "cpp", false)
+ prepare: Gcc.prepareCompiler.apply(Gcc, arguments)
}
Rule {
condition: useObjcPrecompiledHeader
inputs: ["objc_pch_src"]
auxiliaryInputs: ["hpp"]
- Artifact {
- filePath: product.name + "_objc.gch"
- fileTags: ["objc_pch"]
- }
- prepare: {
- return Gcc.prepareCompiler.apply(Gcc, arguments);
- }
+ outputFileTags: Cpp.precompiledHeaderOutputTags("objc", false)
+ outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "objc", false)
+ prepare: Gcc.prepareCompiler.apply(Gcc, arguments)
}
Rule {
condition: useObjcxxPrecompiledHeader
inputs: ["objcpp_pch_src"]
auxiliaryInputs: ["hpp"]
- Artifact {
- filePath: product.name + "_objcpp.gch"
- fileTags: ["objcpp_pch"]
- }
- prepare: {
- return Gcc.prepareCompiler.apply(Gcc, arguments);
- }
+ outputFileTags: Cpp.precompiledHeaderOutputTags("objcpp", false)
+ outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "objcpp", false)
+ prepare: Gcc.prepareCompiler.apply(Gcc, arguments)
}
FileTagger {
diff --git a/share/qbs/modules/cpp/LinuxGCC.qbs b/share/qbs/modules/cpp/LinuxGCC.qbs
index 14fb0a7e9..4b594a0aa 100644
--- a/share/qbs/modules/cpp/LinuxGCC.qbs
+++ b/share/qbs/modules/cpp/LinuxGCC.qbs
@@ -28,11 +28,12 @@
**
****************************************************************************/
+import qbs.Host
import qbs.Process
UnixGCC {
- condition: qbs.targetOS.contains('linux') &&
- qbs.toolchain && qbs.toolchain.contains('gcc')
+ condition: qbs.targetOS.includes('linux') &&
+ qbs.toolchain && qbs.toolchain.includes('gcc')
priority: 1
targetVendor: "pc"
@@ -41,7 +42,7 @@ UnixGCC {
Probe {
id: runPathsProbe
- condition: !_skipAllChecks && qbs.targetPlatform === qbs.hostPlatform
+ condition: !_skipAllChecks && qbs.targetPlatform === Host.platform()
property stringList systemRunPaths: []
configure: {
var paths = [];
diff --git a/share/qbs/modules/cpp/MingwBaseModule.qbs b/share/qbs/modules/cpp/MingwBaseModule.qbs
index 60ad28b08..c9cfe9bd8 100644
--- a/share/qbs/modules/cpp/MingwBaseModule.qbs
+++ b/share/qbs/modules/cpp/MingwBaseModule.qbs
@@ -37,8 +37,6 @@ import "setuprunenv.js" as SetupRunEnv
GenericGCC {
condition: false
- staticLibraryPrefix: "lib"
- staticLibrarySuffix: ".a"
dynamicLibrarySuffix: ".dll"
executableSuffix: ".exe"
debugInfoSuffix: ".debug"
@@ -46,9 +44,10 @@ GenericGCC {
windowsApiCharacterSet: "unicode"
platformDefines: base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet))
.concat("WIN32")
+ runtimeLibrary: "dynamic"
Properties {
- condition: product.multiplexByQbsProperties.contains("buildVariants")
+ condition: product.multiplexByQbsProperties.includes("buildVariants")
&& qbs.buildVariants && qbs.buildVariants.length > 1
&& qbs.buildVariant !== "release"
&& product.type.containsAny(["staticlibrary", "dynamiclibrary"])
@@ -97,9 +96,9 @@ GenericGCC {
var tf;
try {
tf = new TextFile(outputFilePath, TextFile.WriteOnly);
- if (productType.contains("application"))
+ if (productType.includes("application"))
tf.write("1 "); // CREATEPROCESS_MANIFEST_RESOURCE_ID
- else if (productType.contains("dynamiclibrary"))
+ else if (productType.includes("dynamiclibrary"))
tf.write("2 "); // ISOLATIONAWARE_MANIFEST_RESOURCE_ID
tf.write("24 "); // RT_MANIFEST
tf.writeLine(Utilities.cStringQuote(inputFilePath));
diff --git a/share/qbs/modules/cpp/UnixGCC.qbs b/share/qbs/modules/cpp/UnixGCC.qbs
index e5b99cd98..6f377c5c8 100644
--- a/share/qbs/modules/cpp/UnixGCC.qbs
+++ b/share/qbs/modules/cpp/UnixGCC.qbs
@@ -31,14 +31,12 @@
import qbs.File
GenericGCC {
- condition: qbs.toolchain && qbs.toolchain.contains("gcc")
- && qbs.targetOS && qbs.targetOS.contains("unix")
+ condition: qbs.toolchain && qbs.toolchain.includes("gcc")
+ && qbs.targetOS.includes("unix")
priority: -50
- staticLibraryPrefix: "lib"
dynamicLibraryPrefix: "lib"
loadableModulePrefix: "lib"
- staticLibrarySuffix: ".a"
dynamicLibrarySuffix: ".so"
debugInfoSuffix: ".debug"
imageFormat: "elf"
diff --git a/share/qbs/modules/cpp/android-gcc.qbs b/share/qbs/modules/cpp/android-gcc.qbs
index 3e44f4ff3..7c45d3c6b 100644
--- a/share/qbs/modules/cpp/android-gcc.qbs
+++ b/share/qbs/modules/cpp/android-gcc.qbs
@@ -39,25 +39,36 @@ import 'gcc.js' as Gcc
LinuxGCC {
Depends { name: "Android.ndk" }
- condition: qbs.targetOS.contains("android") && qbs.toolchain && qbs.toolchain.contains("llvm")
+ condition: qbs.targetOS.includes("android") && qbs.toolchain && qbs.toolchain.includes("llvm")
priority: 2
- rpaths: [rpathOrigin]
+ rpaths: []
+ // toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android
cxxLanguageVersion: "c++14"
- property string cxxStlBaseDir: FileInfo.joinPaths(Android.ndk.ndkDir, "sources", "cxx-stl")
- property string stlBaseDir: FileInfo.joinPaths(cxxStlBaseDir, "llvm-libc++")
- property string stlLibsDir: {
- if (stlBaseDir)
- return FileInfo.joinPaths(stlBaseDir, "libs", Android.ndk.abi);
- return undefined;
+ property string archLibsDir: {
+ switch (qbs.architecture) {
+ case "arm64":
+ return "aarch64";
+ case "armv7a":
+ return "arm";
+ case "x86_64":
+ return qbs.architecture;
+ case "x86":
+ return "i686";
+ }
}
+ property string targetDir: "android" + (["armeabi", "armeabi-v7a"].includes(Android.ndk.abi) ? "eabi" : "")
+ property string triple: [archLibsDir, targetSystem, targetDir].join("-")
+ property string libsDir: FileInfo.joinPaths(sysroot, "usr", "lib", triple);
- property string sharedStlFilePath: (stlLibsDir && Android.ndk.appStl.endsWith("_shared"))
- ? FileInfo.joinPaths(stlLibsDir, dynamicLibraryPrefix + Android.ndk.appStl + dynamicLibrarySuffix)
+ property string sharedStlFilePath: (libsDir && Android.ndk.appStl.endsWith("_shared"))
+ ? FileInfo.joinPaths(libsDir, dynamicLibraryPrefix + Android.ndk.appStl + dynamicLibrarySuffix)
: undefined
- property string staticStlFilePath: (stlLibsDir && Android.ndk.appStl.endsWith("_static"))
- ? FileInfo.joinPaths(stlLibsDir, NdkUtils.stlFilePath(staticLibraryPrefix, Android.ndk, staticLibrarySuffix))
+ property string staticStlFilePath: (libsDir && Android.ndk.appStl.endsWith("_static"))
+ ? FileInfo.joinPaths(libsDir, Android.ndk.platformVersion,
+ NdkUtils.stlFileName(staticLibraryPrefix, Android.ndk,
+ staticLibrarySuffix))
: undefined
Group {
@@ -67,15 +78,6 @@ LinuxGCC {
fileTags: "android.stl"
}
- Group {
- name: "gdbserver"
- condition: qbs.buildVariant !== "release" && product.cpp.shouldLink
- files: FileInfo.joinPaths(Android.ndk.ndkDir, "prebuilt",
- "android-" + NdkUtils.abiNameToDirName(Android.ndk.abi),
- "gdbserver", "gdbserver")
- fileTags: "android.gdbserver"
- }
-
toolchainInstallPath: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains",
"llvm", "prebuilt",
Android.ndk.hostArch, "bin")
@@ -83,11 +85,10 @@ LinuxGCC {
property string toolchainTriple: [targetAbi === "androideabi" ? "arm" : targetArch,
targetSystem, targetAbi].join("-")
+ internalVersion: undefined
toolchainPrefix: undefined
machineType: {
- if (Android.ndk.abi === "armeabi")
- return "armv5te";
if (Android.ndk.abi === "armeabi-v7a")
return "armv7-a";
}
@@ -99,59 +100,43 @@ LinuxGCC {
commonCompilerFlags: NdkUtils.commonCompilerFlags(qbs.toolchain, qbs.buildVariant, Android.ndk)
- linkerFlags: NdkUtils.commonLinkerFlags(Android.ndk.abi);
+ linkerFlags: NdkUtils.commonLinkerFlags(Android.ndk);
driverLinkerFlags: {
- var flags = ["-fuse-ld=lld", "-Wl,--exclude-libs,libgcc.a", "-Wl,--exclude-libs,libatomic.a", "-nostdlib++"];
- if (Android.ndk.appStl.startsWith("c++") && Android.ndk.abi === "armeabi-v7a")
- flags = flags.concat(["-Wl,--exclude-libs,libunwind.a"]);
+ var flags = ["-fuse-ld=lld", "-Wl,--exclude-libs,libgcc.a", "-nostdlib++"];
+ // See https://android.googlesource.com/platform/ndk/+/ndk-release-r21/docs/BuildSystemMaintainers.md#Unwinding
+ if (Android.ndk.abi === "armeabi-v7a") {
+ flags = flags.concat(["-Wl,--exclude-libs,libgcc_real.a"]);
+ if (Android.ndk.appStl.startsWith("c++"))
+ flags = flags.concat(["-Wl,--exclude-libs,libunwind.a"]);
+ }
return flags;
}
platformDriverFlags: ["-fdata-sections", "-ffunction-sections", "-funwind-tables",
"-fstack-protector-strong", "-no-canonical-prefixes"]
- libraryPaths: {
- var prefix = FileInfo.joinPaths(sysroot, "usr");
- var paths = [];
- if (Android.ndk.abi === "x86_64") // no lib64 for arm64-v8a
- paths.push(FileInfo.joinPaths(prefix, "lib64"));
- paths.push(FileInfo.joinPaths(prefix, "lib"));
- paths.push(stlLibsDir);
- return paths;
- }
-
dynamicLibraries: {
var libs = ["c", "m"];
if (sharedStlFilePath)
- libs.push(FileInfo.joinPaths(stlLibsDir, NdkUtils.stlFilePath(dynamicLibraryPrefix, Android.ndk, dynamicLibrarySuffix)));
+ libs.push(FileInfo.joinPaths(libsDir, Android.ndk.platformVersion,
+ NdkUtils.stlFileName(dynamicLibraryPrefix, Android.ndk,
+ dynamicLibrarySuffix)));
return libs;
}
staticLibraries: staticStlFilePath
- systemIncludePaths: {
- var includes = [FileInfo.joinPaths(sysroot, "usr", "include", toolchainTriple)];
- if (Android.ndk.abi === "armeabi-v7a") {
- includes.push(FileInfo.joinPaths(Android.ndk.ndkDir, "sources", "android",
- "support", "include"));
- }
- includes.push(FileInfo.joinPaths(stlBaseDir, "include"));
- includes.push(FileInfo.joinPaths(stlBaseDir + "abi", "include"));
- return includes;
- }
- defines: {
- var list = ["ANDROID"];
- // Might be superseded by an -mandroid-version or similar Clang compiler flag in future
- list.push("__ANDROID_API__=" + Android.ndk.platformVersion);
- return list;
- }
+ // When using ndk r19c, llvm doesn't add sysroot/usr/include/c++/v1 to the path
+ // But it works starting with ndk r20b
+ systemIncludePaths: (Utilities.versionCompare(Android.ndk.version, "20") < 0) ?
+ FileInfo.joinPaths(sysroot, "usr", "include", "c++", "v1") : []
+
+ defines: ["ANDROID", "__ANDROID__"]
binutilsPath: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains", "llvm", "prebuilt",
Android.ndk.hostArch, "bin");
binutilsPathPrefix: FileInfo.joinPaths(binutilsPath, "llvm-")
- syslibroot: FileInfo.joinPaths(Android.ndk.ndkDir, "platforms",
- Android.ndk.platform, "arch-"
- + NdkUtils.abiNameToDirName(Android.ndk.abi))
- sysroot: FileInfo.joinPaths(Android.ndk.ndkDir, "sysroot")
+ sysroot: FileInfo.joinPaths(Android.ndk.ndkDir, "toolchains", "llvm", "prebuilt",
+ Android.ndk.hostArch, "sysroot")
targetArch: {
switch (qbs.architecture) {
@@ -168,9 +153,10 @@ LinuxGCC {
}
}
- targetVendor: "none"
+ target: [targetArch, targetSystem, targetAbi].join("-")
targetSystem: "linux"
- targetAbi: "android" + (["armeabi", "armeabi-v7a"].contains(Android.ndk.abi) ? "eabi" : "")
+ targetAbi: "android" + (["armeabi", "armeabi-v7a"].includes(Android.ndk.abi) ? "eabi" : "") +
+ Android.ndk.platformVersion
endianness: "little"
@@ -182,9 +168,9 @@ LinuxGCC {
fileTags: "android.nativelibrary"
}
prepare: {
- var stripArgs = ["--strip-unneeded", "-o", output.filePath, input.filePath];
+ var stripArgs = ["--strip-all", "-o", output.filePath, input.filePath];
var stripCmd = new Command(product.cpp.stripPath, stripArgs);
- stripCmd.description = "Stripping unneeded symbols from " + input.fileName;
+ stripCmd.description = "stripping unneeded symbols from " + input.fileName;
return stripCmd;
}
}
diff --git a/share/qbs/modules/cpp/cosmic.js b/share/qbs/modules/cpp/cosmic.js
new file mode 100644
index 000000000..4979f33f7
--- /dev/null
+++ b/share/qbs/modules/cpp/cosmic.js
@@ -0,0 +1,443 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 Cpp = require("cpp.js");
+var Environment = require("qbs.Environment");
+var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var ModUtils = require("qbs.ModUtils");
+var Process = require("qbs.Process");
+var TemporaryDir = require("qbs.TemporaryDir");
+var TextFile = require("qbs.TextFile");
+
+function toolchainDetails(qbs) {
+ var architecture = qbs.architecture;
+ if (architecture.startsWith("arm")) {
+ return {
+ "executableSuffix": ".cxm",
+ "staticLibrarySuffix": ".cxm",
+ "assemblerName": "cacorm",
+ "compilerName": "cxcorm"
+ };
+ } else if (architecture === "stm8") {
+ return {
+ "executableSuffix": ".sm8",
+ "staticLibrarySuffix": ".sm8",
+ "assemblerName": "castm8",
+ "compilerName": "cxstm8"
+ };
+ } else if (architecture === "hcs8") {
+ return {
+ "executableSuffix": ".h08",
+ "staticLibrarySuffix": ".h08",
+ "assemblerName": "ca6808",
+ "compilerName": "cx6808"
+ };
+ } else if (architecture === "hcs12") {
+ return {
+ "executableSuffix": ".h12",
+ "staticLibrarySuffix": ".h12",
+ "assemblerName": "ca6812",
+ "compilerName": "cx6812"
+ };
+ } else if (architecture === "m68k") {
+ return {
+ "executableSuffix": ".332",
+ "staticLibrarySuffix": ".332",
+ "assemblerName": "ca332",
+ "compilerName": "cx332"
+ };
+ }
+}
+
+function guessArchitecture(compilerFilePath) {
+ var baseName = FileInfo.baseName(compilerFilePath);
+ if (baseName === "cxcorm")
+ return "arm";
+ else if (baseName === "cxstm8")
+ return "stm8";
+ else if (baseName === "cx6808")
+ return "hcs8";
+ else if (baseName === "cx6812")
+ return "hcs12";
+ else if (baseName === "cx332")
+ return "m68k";
+}
+
+function dumpMacros(compilerFilePath) {
+ // Note: The COSMIC compiler does not support the predefined
+ // macros dumping. So, we do it with the following trick, where we try
+ // to create and compile a special temporary file and to parse the console
+ // output with the own magic pattern: (""|"key"|"value"|"").
+
+ var outputDirectory = new TemporaryDir();
+ var outputFilePath = FileInfo.fromNativeSeparators(FileInfo.joinPaths(outputDirectory.path(),
+ "dump-macros.c"));
+ var outputFile = new TextFile(outputFilePath, TextFile.WriteOnly);
+ outputFile.writeLine("#define VALUE_TO_STRING(x) #x");
+ outputFile.writeLine("#define VALUE(x) VALUE_TO_STRING(x)");
+ outputFile.writeLine("#define VAR_NAME_VALUE(var) #var VALUE(var)");
+ // The COSMIC compiler defines only one pre-defined macro
+ // (at least nothing is said about other macros in the documentation).
+ var keys = ["__CSMC__"];
+ for (var i in keys) {
+ var key = keys[i];
+ outputFile.writeLine("#if defined (" + key + ")");
+ outputFile.writeLine("#pragma message (VAR_NAME_VALUE(" + key + "))");
+ outputFile.writeLine("#endif");
+ }
+ outputFile.close();
+
+ var process = new Process();
+ process.exec(compilerFilePath, [outputFilePath], false);
+ File.remove(outputFilePath);
+
+ var map = {};
+ // COSMIC compiler use the errors output!
+ process.readStdErr().trim().split(/\r?\n/g).map(function(line) {
+ var match = line.match(/^#message \("(.+)" "(.+)"\)$/);
+ if (match)
+ map[match[1]] = match[2];
+ });
+ return map;
+}
+
+function dumpVersion(compilerFilePath) {
+ var p = new Process();
+ p.exec(compilerFilePath, ["-vers"]);
+ // COSMIC compiler use the errors output!
+ var output = p.readStdErr();
+ var match = output.match(/^COSMIC.+V(\d+)\.?(\d+)\.?(\*|\d+)?/);
+ if (match) {
+ var major = match[1] ? parseInt(match[1], 10) : 0;
+ var minor = match[2] ? parseInt(match[2], 10) : 0;
+ var patch = match[3] ? parseInt(match[3], 10) : 0;
+ return { major: major, minor: minor, patch: patch };
+ }
+}
+
+function guessEndianness(architecture) {
+ // There is no mention of supported endianness in the cosmic compiler.
+ return "big";
+}
+
+function dumpDefaultPaths(compilerFilePath, architecture) {
+ var rootPath = FileInfo.path(compilerFilePath);
+ var includePath;
+ var includePaths = [];
+ if (architecture.startsWith("arm")) {
+ includePath = FileInfo.joinPaths(rootPath, "hcorm");
+ if (File.exists(includePath))
+ includePaths.push(includePath);
+ } else if (architecture === "stm8") {
+ includePath = FileInfo.joinPaths(rootPath, "hstm8");
+ if (File.exists(includePath))
+ includePaths.push(includePath);
+ } else if (architecture === "hcs8") {
+ includePath = FileInfo.joinPaths(rootPath, "h6808");
+ if (File.exists(includePath))
+ includePaths.push(includePath);
+ } else if (architecture === "hcs12") {
+ includePath = FileInfo.joinPaths(rootPath, "h6812");
+ if (File.exists(includePath))
+ includePaths.push(includePath);
+ } else if (architecture === "m68k") {
+ includePath = FileInfo.joinPaths(rootPath, "h332");
+ if (File.exists(includePath))
+ includePaths.push(includePath);
+ }
+
+ var libraryPaths = [];
+ var libraryPath = FileInfo.joinPaths(rootPath, "lib");
+ if (File.exists(libraryPath))
+ libraryPaths.push(libraryPath);
+
+ return {
+ "includePaths": includePaths,
+ "libraryPaths": libraryPaths,
+ }
+}
+
+function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
+ var args = [];
+
+ // Up to 128 include files.
+ args = args.concat(Cpp.collectPreincludePathsArguments(input));
+
+ // Defines.
+ args = args.concat(Cpp.collectDefinesArguments(input));
+
+ // Up to 128 include paths.
+ args = args.concat(Cpp.collectIncludePathsArguments(input));
+ args = args.concat(Cpp.collectSystemIncludePathsArguments(input));
+
+ // Debug information flags.
+ if (input.cpp.debugInformation)
+ args.push("+debug");
+
+ var architecture = input.qbs.architecture;
+ var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
+
+ // Warning level flags.
+ switch (input.cpp.warningLevel) {
+ case "none":
+ // Disabled by default.
+ break;
+ case "all":
+ // Highest warning level.
+ args.push("-pw7");
+ break;
+ }
+
+ // C language version flags.
+ if (tag === "c") {
+ var knownValues = ["c99"];
+ var cLanguageVersion = Cpp.languageVersion(
+ input.cpp.cLanguageVersion, knownValues, "C");
+ switch (cLanguageVersion) {
+ case "c99":
+ args.push("-p", "c99");
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Objects output directory.
+ args.push("-co", FileInfo.path(outputs.obj[0].filePath));
+
+ // Listing files generation flag.
+ if (input.cpp.generateCompilerListingFiles) {
+ // Enable listings.
+ args.push("-l");
+ // Listings output directory.
+ args.push("-cl", FileInfo.path(outputs.lst[0].filePath));
+ }
+
+ // Misc flags.
+ args = args.concat(Cpp.collectMiscCompilerArguments(input, tag),
+ Cpp.collectMiscDriverArguments(product));
+
+ // Input.
+ args.push(input.filePath);
+ return args;
+}
+
+function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
+ var args = [];
+
+ // Up to 128 include paths.
+ args = args.concat(Cpp.collectIncludePathsArguments(input));
+ args = args.concat(Cpp.collectSystemIncludePathsArguments(input));
+
+ // Debug information flags.
+ if (input.cpp.debugInformation)
+ args.push("-xx");
+
+ // Misc flags.
+ args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm"));
+
+ // Listing files generation flag.
+ if (input.cpp.generateAssemblerListingFiles) {
+ args.push("-l");
+ args.push("+l", outputs.lst[0].filePath);
+ }
+
+ // Objects output file path.
+ args.push("-o", outputs.obj[0].filePath);
+
+ // Input.
+ args.push(input.filePath);
+ return args;
+}
+
+function linkerFlags(project, product, inputs, outputs) {
+ var args = [];
+
+ // Library paths.
+ args = args.concat(Cpp.collectLibraryPaths(product).map(function(path) {
+ // It is workaround to use the relative paths avoids a strange linking
+ // errors. Maybe it is related to the limitations on the length of the
+ // command arguments, or on the length of the paths.
+ return product.cpp.libraryPathFlag + Cpp.relativePath(product.buildDirectory, path);
+ }));
+
+ // Output.
+ args.push("-o", outputs.application[0].filePath);
+
+ // Map file generation flag.
+ if (product.cpp.generateLinkerMapFile)
+ args.push("-m", outputs.mem_map[0].filePath);
+
+ // Misc flags.
+ args = args.concat(Cpp.collectMiscEscapableLinkerArguments(product),
+ Cpp.collectMiscLinkerArguments(product),
+ Cpp.collectMiscDriverArguments(product));
+
+ // Linker scripts.
+ args = args.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs));
+
+ // Input objects.
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs));
+
+ // Library dependencies (order has matters).
+ args = args.concat(Cpp.collectLibraryDependencies(product).map(function(dep) {
+ // It is workaround to use the relative paths avoids a strange linking
+ // errors. Maybe it is related to the limitations on the length of the
+ // command arguments, or on the length of the paths.
+ return Cpp.relativePath(product.buildDirectory, dep.filePath);
+ }));
+
+ return args;
+}
+
+function archiverFlags(project, product, inputs, outputs) {
+ var args = ["-cl"];
+
+ // Output.
+ args.push(outputs.staticlibrary[0].filePath);
+
+ // Input objects.
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs));
+ return args;
+}
+
+function createPath(fullPath) {
+ var cmd = new JavaScriptCommand();
+ cmd.fullPath = fullPath;
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ File.makePath(fullPath);
+ };
+ return cmd;
+}
+
+// It is a workaround to rename the generated object file to the desired name.
+// Reason is that the Cosmic compiler always generates the object files in the
+// format of 'module.o', but we expect it in flexible format, e.g. 'module.c.obj'
+// or 'module.c.o' depending on the cpp.objectSuffix property.
+function renameObjectFile(project, product, inputs, outputs, input, output) {
+ var object = outputs.obj[0];
+ var cmd = new JavaScriptCommand();
+ cmd.newObject = object.filePath;
+ cmd.oldObject = FileInfo.joinPaths(FileInfo.path(object.filePath),
+ object.baseName + ".o");
+ cmd.silent = true;
+ cmd.sourceCode = function() { File.move(oldObject, newObject); };
+ return cmd;
+}
+
+// It is a workaround to rename the generated listing file to the desired name.
+// Reason is that the Cosmic compiler always generates the listing files in the
+// format of 'module.ls', but we expect it in flexible format, e.g. 'module.c.lst'
+// or 'module.c.ls' depending on the cpp.compilerListingSuffix property.
+function renameListingFile(project, product, inputs, outputs, input, output) {
+ var listing = outputs.lst[0];
+ var cmd = new JavaScriptCommand();
+ cmd.newListing = listing.filePath;
+ cmd.oldListing = FileInfo.joinPaths(FileInfo.path(listing.filePath),
+ listing.baseName + ".ls");
+ cmd.silent = true;
+ cmd.sourceCode = function() { File.move(oldListing, newListing); };
+ return cmd;
+}
+
+function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var cmds = [];
+
+ // Create output objects path, because the Cosmic doesn't do it.
+ var cmd = createPath(FileInfo.path(outputs.obj[0].filePath));
+ cmds.push(cmd);
+
+ // Create output listing path, because the Cosmic doesn't do it.
+ if (input.cpp.generateCompilerListingFiles) {
+ cmd = createPath(FileInfo.path(outputs.lst[0].filePath));
+ cmds.push(cmd);
+ }
+
+ var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn);
+ cmd = new Command(input.cpp.compilerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "compiling " + input.fileName;
+ cmd.highlight = "compiler";
+ cmd.jobPool = "compiler";
+ cmds.push(cmd);
+
+ cmds.push(renameObjectFile(project, product, inputs, outputs, input, output));
+
+ if (input.cpp.generateCompilerListingFiles)
+ cmds.push(renameListingFile(project, product, inputs, outputs, input, output));
+
+ return cmds;
+}
+
+function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var cmds = [];
+
+ // Create output objects path, because the Cosmic doesn't do it.
+ var cmd = createPath(FileInfo.path(outputs.obj[0].filePath));
+ cmds.push(cmd);
+
+ // Create output listing path, because the Cosmic doesn't do it.
+ if (input.cpp.generateCompilerListingFiles) {
+ cmd = createPath(FileInfo.path(outputs.lst[0].filePath));
+ cmds.push(cmd);
+ }
+
+ var args = assemblerFlags(project, product, input, outputs, explicitlyDependsOn);
+ cmd = new Command(input.cpp.assemblerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "assembling " + input.fileName;
+ cmd.highlight = "compiler";
+ cmd.jobPool = "assembler";
+ cmds.push(cmd);
+ return cmds;
+}
+
+function prepareLinker(project, product, inputs, outputs, input, output) {
+ var primaryOutput = outputs.application[0];
+ var args = linkerFlags(project, product, inputs, outputs);
+ var cmd = new Command(product.cpp.linkerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "linking " + primaryOutput.fileName;
+ cmd.highlight = "linker";
+ cmd.jobPool = "linker";
+ return [cmd];
+}
+
+function prepareArchiver(project, product, inputs, outputs, input, output) {
+ var args = archiverFlags(project, product, inputs, outputs);
+ var cmd = new Command(product.cpp.archiverPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "creating " + output.fileName;
+ cmd.highlight = "linker";
+ cmd.jobPool = "linker";
+ return [cmd];
+}
diff --git a/share/qbs/modules/cpp/cosmic.qbs b/share/qbs/modules/cpp/cosmic.qbs
new file mode 100644
index 000000000..8bf0f22a3
--- /dev/null
+++ b/share/qbs/modules/cpp/cosmic.qbs
@@ -0,0 +1,147 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** 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.File
+import qbs.FileInfo
+import qbs.PathTools
+import qbs.Probes
+import qbs.Utilities
+import "cosmic.js" as COSMIC
+import "cpp.js" as Cpp
+
+CppModule {
+ condition: qbs.toolchain && qbs.toolchain.includes("cosmic")
+
+ Probes.BinaryProbe {
+ id: compilerPathProbe
+ condition: !toolchainInstallPath && !_skipAllChecks
+ names: ["cxcorm"]
+ }
+
+ Probes.CosmicProbe {
+ id: cosmicProbe
+ condition: !_skipAllChecks
+ compilerFilePath: compilerPath
+ enableDefinesByLanguage: enableCompilerDefinesByLanguage
+ }
+
+ qbs.architecture: cosmicProbe.found ? cosmicProbe.architecture : original
+ qbs.targetPlatform: "none"
+
+ compilerVersionMajor: cosmicProbe.versionMajor
+ compilerVersionMinor: cosmicProbe.versionMinor
+ compilerVersionPatch: cosmicProbe.versionPatch
+ endianness: cosmicProbe.endianness
+
+ compilerDefinesByLanguage: cosmicProbe.compilerDefinesByLanguage
+ compilerIncludePaths: cosmicProbe.includePaths
+
+ toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined
+
+ /* Work-around for QtCreator which expects these properties to exist. */
+ property string cCompilerName: compilerName
+ property string cxxCompilerName: compilerName
+
+ compilerName: toolchainDetails.compilerName + compilerExtension
+ compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName)
+
+ assemblerName: toolchainDetails.assemblerName + compilerExtension
+ assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName)
+
+ linkerName: "clnk" + compilerExtension
+ linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName)
+
+ property string archiverName: "clib" + compilerExtension
+ property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName)
+
+ runtimeLibrary: "static"
+
+ staticLibrarySuffix: toolchainDetails.staticLibrarySuffix
+ executableSuffix: toolchainDetails.executableSuffix
+
+ imageFormat: "cosmic"
+
+ enableExceptions: false
+ enableRtti: false
+
+ defineFlag: "-d"
+ includeFlag: "-i"
+ systemIncludeFlag: "-i"
+ preincludeFlag: "-ph"
+ libraryDependencyFlag: ""
+ libraryPathFlag: "-l"
+ linkerScriptFlag: ""
+
+ toolchainDetails: COSMIC.toolchainDetails(qbs)
+
+ knownArchitectures: ["arm", "hcs12", "hcs8", "m68k", "stm8"]
+
+ Rule {
+ id: assembler
+ inputs: ["asm"]
+ outputFileTags: Cpp.assemblerOutputTags(generateAssemblerListingFiles)
+ outputArtifacts: Cpp.assemblerOutputArtifacts(input)
+ prepare: COSMIC.prepareAssembler.apply(COSMIC, arguments)
+ }
+
+ FileTagger {
+ patterns: ["*.s"]
+ fileTags: ["asm"]
+ }
+
+ Rule {
+ id: compiler
+ inputs: ["cpp", "c"]
+ auxiliaryInputs: ["hpp"]
+ outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles)
+ outputArtifacts: Cpp.compilerOutputArtifacts(input)
+ prepare: COSMIC.prepareCompiler.apply(COSMIC, arguments)
+ }
+
+ Rule {
+ id: applicationLinker
+ multiplex: true
+ inputs: ["obj", "linkerscript"]
+ inputsFromDependencies: ["staticlibrary"]
+ outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile)
+ outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product)
+ prepare: COSMIC.prepareLinker.apply(COSMIC, arguments)
+ }
+
+ Rule {
+ id: staticLibraryLinker
+ multiplex: true
+ inputs: ["obj"]
+ inputsFromDependencies: ["staticlibrary"]
+ outputFileTags: Cpp.staticLibraryLinkerOutputTags()
+ outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product)
+ prepare: COSMIC.prepareArchiver.apply(COSMIC, arguments)
+ }
+}
diff --git a/share/qbs/modules/cpp/cpp.js b/share/qbs/modules/cpp/cpp.js
index 0e440bdb0..9f907580a 100644
--- a/share/qbs/modules/cpp/cpp.js
+++ b/share/qbs/modules/cpp/cpp.js
@@ -28,6 +28,12 @@
**
****************************************************************************/
+var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var ModUtils = require("qbs.ModUtils");
+var PathTools = require("qbs.PathTools");
+var Utilities = require("qbs.Utilities");
+
function languageVersion(versionArray, knownValues, lang) {
if (!versionArray)
return undefined;
@@ -36,7 +42,7 @@ function languageVersion(versionArray, knownValues, lang) {
return versions[0];
for (var i = 0; i < knownValues.length; ++i) {
var candidate = knownValues[i];
- if (versions.contains(candidate))
+ if (versions.includes(candidate))
return candidate;
}
var version = versions[0];
@@ -44,3 +50,426 @@ function languageVersion(versionArray, knownValues, lang) {
+ "' from list of unknown " + lang + " version strings (" + versions + ")");
return version;
}
+
+function extractMacros(output, regexp) {
+ var m = {};
+ output.trim().split(/\r?\n/g).map(function(line) {
+ if (regexp) {
+ var match = regexp.exec(line);
+ if (!match)
+ return;
+ line = match[1];
+ }
+ var prefix = "#define ";
+ if (!line.startsWith(prefix))
+ return;
+ var index = line.indexOf(" ", prefix.length);
+ if (index !== -1)
+ m[line.substr(prefix.length, index - prefix.length)] = line.substr(index + 1);
+ });
+ return m;
+}
+
+function relativePath(baseDirectory, filePath) {
+ if (FileInfo.isAbsolutePath(filePath))
+ return FileInfo.relativePath(baseDirectory, filePath);
+ return filePath;
+}
+
+function assemblerOutputTags(needsListingFiles) {
+ var tags = ["obj"];
+ if (needsListingFiles)
+ tags.push("lst");
+ return tags;
+}
+
+function compilerOutputTags(needsListingFiles) {
+ var tags = ["obj", "intermediate_obj"];
+ if (needsListingFiles)
+ tags.push("lst");
+ return tags;
+}
+
+function applicationLinkerOutputTags(needsLinkerMapFile) {
+ var tags = ["application"];
+ if (needsLinkerMapFile)
+ tags.push("mem_map");
+ return tags;
+}
+
+function dynamicLibraryLinkerOutputTags() {
+ return ["dynamiclibrary", "dynamiclibrary_import"];
+}
+
+function staticLibraryLinkerOutputTags() {
+ return ["staticlibrary"];
+}
+
+function resourceCompilerOutputTags() {
+ return ["res"];
+}
+
+function precompiledHeaderOutputTags(lang, generateObjects) {
+ var tags = [lang + "_pch"];
+ if (generateObjects)
+ tags.push("obj");
+ return tags;
+};
+
+function assemblerOutputArtifacts(input) {
+ var artifacts = [];
+ artifacts.push({
+ fileTags: ["obj"],
+ filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir),
+ input.fileName + input.cpp.objectSuffix)
+ });
+ if (input.cpp.generateAssemblerListingFiles) {
+ artifacts.push({
+ fileTags: ["lst"],
+ filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir),
+ input.fileName + input.cpp.assemblerListingSuffix)
+ });
+ }
+ return artifacts;
+}
+
+function compilerOutputArtifacts(input, inputs) {
+ var objTags = input.fileTags.includes("cpp_intermediate_object")
+ ? ["intermediate_obj"]
+ : ["obj"];
+ if (inputs) {
+ if (inputs.c || inputs.objc)
+ objTags.push("c_obj");
+ if (inputs.cpp || inputs.objcpp)
+ objTags.push("cpp_obj");
+ }
+ var artifacts = [{
+ fileTags: objTags,
+ filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir),
+ input.fileName + input.cpp.objectSuffix)
+ }];
+ if (input.cpp.generateCompilerListingFiles) {
+ artifacts.push({
+ fileTags: ["lst"],
+ filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir),
+ input.fileName + input.cpp.compilerListingSuffix)
+ });
+ }
+ return artifacts;
+}
+
+function applicationLinkerOutputArtifacts(product) {
+ var artifacts = [{
+ fileTags: ["application"],
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ PathTools.applicationFilePath(product))
+ }];
+ if (product.cpp.generateLinkerMapFile) {
+ artifacts.push({
+ fileTags: ["mem_map"],
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ product.targetName + product.cpp.linkerMapSuffix)
+ });
+ }
+ return artifacts;
+}
+
+function dynamicLibraryLinkerOutputArtifacts(product) {
+ return [{
+ fileTags: ["dynamiclibrary"],
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ PathTools.dynamicLibraryFilePath(product))
+ }, {
+ fileTags: ["dynamiclibrary_import"],
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ PathTools.importLibraryFilePath(product)),
+ alwaysUpdated: false
+ }];
+}
+
+function staticLibraryLinkerOutputArtifacts(product) {
+ return [{
+ fileTags: ["staticlibrary"],
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ PathTools.staticLibraryFilePath(product))
+ }];
+}
+
+function resourceCompilerOutputArtifacts(input) {
+ return [{
+ fileTags: ["res"],
+ filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir),
+ input.completeBaseName + input.cpp.resourceSuffix)
+ }];
+}
+
+function precompiledHeaderOutputArtifacts(input, product, lang, generateObjects) {
+ var artifacts = [{
+ fileTags: [lang + "_pch"],
+ filePath: product.name + "_" + lang + product.cpp.precompiledHeaderSuffix
+ }];
+ if (generateObjects) {
+ artifacts.push({
+ fileTags: ["obj"],
+ filePath: Utilities.getHash(input.completeBaseName)
+ + "_" + lang + input.cpp.objectSuffix
+ });
+ }
+ return artifacts;
+}
+
+function collectLibraryDependencies(product) {
+ var seen = {};
+ var seenObjectFiles = [];
+ var result = [];
+
+ function addFilePath(filePath, wholeArchive, productName) {
+ result.push({ filePath: filePath, wholeArchive: wholeArchive, productName: productName });
+ }
+
+ function addArtifactFilePaths(dep, artifacts) {
+ if (!artifacts)
+ return;
+ var artifactFilePaths = artifacts.map(function(a) { return a.filePath; });
+ var wholeArchive = dep.parameters.cpp && dep.parameters.cpp.linkWholeArchive;
+ var artifactsAreImportLibs = artifacts.length > 0
+ && artifacts[0].fileTags.includes("dynamiclibrary_import");
+ for (var i = 0; i < artifactFilePaths.length; ++i) {
+ addFilePath(artifactFilePaths[i], wholeArchive,
+ artifactsAreImportLibs ? dep.name : undefined);
+ }
+ }
+
+ function addExternalLibs(obj) {
+ if (!obj.cpp)
+ return;
+ function ensureArray(a) {
+ return (a instanceof Array) ? a : [];
+ }
+ function sanitizedModuleListProperty(obj, moduleName, propertyName) {
+ return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName));
+ }
+ function handleExternalLibraries(tag, libSuffix, objSuffix) {
+ var externalLibs = sanitizedModuleListProperty(obj, "cpp", tag) || [];
+ externalLibs.forEach(function(libName) {
+ var isObjectFile = objSuffix && libName.endsWith(objSuffix);
+ if (isObjectFile) {
+ if (seenObjectFiles.includes(libName))
+ return;
+ seenObjectFiles.push(libName);
+ }
+ if (!libName.endsWith(libSuffix) && !isObjectFile && !libName.startsWith('@'))
+ libName += libSuffix;
+ addFilePath(libName, false);
+ });
+ }
+ handleExternalLibraries("staticLibraries",
+ obj.moduleProperty("cpp", "staticLibrarySuffix"),
+ obj.moduleProperty("cpp", "objectSuffix"));
+ handleExternalLibraries("dynamicLibraries",
+ obj.moduleProperty("cpp", "dynamicLibraryImportSuffix"));
+ }
+
+ function traverse(dep) {
+ if (seen.hasOwnProperty(dep.name))
+ return;
+ if (dep.parameters.cpp && dep.parameters.cpp.link === false)
+ return;
+
+ var staticLibraryArtifacts = dep.artifacts["staticlibrary"];
+ var dynamicLibraryArtifacts = staticLibraryArtifacts
+ ? null : dep.artifacts["dynamiclibrary_import"];
+ if (staticLibraryArtifacts) {
+ seen[dep.name] = true;
+ dep.dependencies.forEach(traverse);
+ addArtifactFilePaths(dep, staticLibraryArtifacts);
+ addExternalLibs(dep);
+ } else if (dynamicLibraryArtifacts) {
+ seen[dep.name] = true;
+ addArtifactFilePaths(dep, dynamicLibraryArtifacts);
+ }
+ }
+
+ product.dependencies.forEach(traverse);
+ addExternalLibs(product);
+ return result;
+}
+
+function collectAbsoluteLibraryDependencyPaths(product) {
+ var paths = collectLibraryPaths(product);
+ var deps = collectLibraryDependencies(product);
+ return deps.map(function(dep) {
+ var filePath = dep.filePath;
+ if (FileInfo.isAbsolutePath(filePath))
+ return filePath;
+ for (var i = 0; i < paths.length; ++i) {
+ var fullPath = FileInfo.joinPaths(paths[i], filePath);
+ if (File.exists(fullPath))
+ return fullPath;
+ }
+ return filePath;
+ });
+}
+
+function collectDefines(input) {
+ var allDefines = [];
+ var platformDefines = input.cpp.platformDefines;
+ if (platformDefines)
+ allDefines = allDefines.uniqueConcat(platformDefines);
+ var defines = input.cpp.defines;
+ if (defines)
+ allDefines = allDefines.uniqueConcat(defines);
+ return allDefines;
+}
+
+function collectIncludePaths(input) {
+ var allIncludePaths = [];
+ var includePaths = input.cpp.includePaths;
+ if (includePaths)
+ allIncludePaths = allIncludePaths.uniqueConcat(includePaths);
+ var builtIns = input.cpp.compilerIncludePaths;
+ return allIncludePaths.filter(function(p) {
+ return !builtIns.includes(p);
+ });
+}
+
+function collectSystemIncludePaths(input) {
+ var allIncludePaths = [];
+ var systemIncludePaths = input.cpp.systemIncludePaths;
+ if (systemIncludePaths)
+ allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths);
+ var distributionIncludePaths = input.cpp.distributionIncludePaths;
+ if (distributionIncludePaths)
+ allIncludePaths = allIncludePaths.uniqueConcat(distributionIncludePaths);
+ var builtIns = input.cpp.compilerIncludePaths;
+ return allIncludePaths.filter(function(p) {
+ return !builtIns.includes(p);
+ });
+}
+
+function collectPreincludePaths(input) {
+ return input.cpp.prefixHeaders;
+}
+
+function collectLibraryPaths(product) {
+ var allLibraryPaths = [];
+ var libraryPaths = product.cpp.libraryPaths;
+ if (libraryPaths)
+ allLibraryPaths = allLibraryPaths.uniqueConcat(libraryPaths);
+ var distributionLibraryPaths = product.cpp.distributionLibraryPaths;
+ if (distributionLibraryPaths)
+ allLibraryPaths = allLibraryPaths.uniqueConcat(distributionLibraryPaths);
+ return allLibraryPaths;
+}
+
+function collectLinkerScriptPaths(inputs) {
+ return inputs.linkerscript
+ ? inputs.linkerscript.map(function(script) { return script.filePath; })
+ : [];
+}
+
+function collectLinkerObjectPaths(inputs) {
+ return inputs.obj ? inputs.obj.map(function(obj) { return obj.filePath; }) : [];
+}
+
+function collectResourceObjectPaths(inputs) {
+ return inputs.res ? inputs.res.map(function(res) { return res.filePath; }) : [];
+}
+
+function collectLibraryDependenciesArguments(product) {
+ var deps = collectLibraryDependencies(product);
+ return deps.map(function(dep) { return product.cpp.libraryDependencyFlag + dep.filePath })
+}
+
+function collectDefinesArguments(input) {
+ var defines = collectDefines(input);
+ return defines.map(function(define) { return input.cpp.defineFlag + define });
+}
+
+function collectIncludePathsArguments(input) {
+ var paths = collectIncludePaths(input);
+ return paths.map(function(path) { return input.cpp.includeFlag + path });
+}
+
+function collectSystemIncludePathsArguments(input, flag) {
+ flag = (flag === undefined) ? input.cpp.systemIncludeFlag : flag;
+ var paths = collectSystemIncludePaths(input);
+ return paths.map(function(path) { return flag + path });
+}
+
+function collectPreincludePathsArguments(input, split) {
+ var paths = collectPreincludePaths(input);
+ if (split) {
+ var args = [];
+ for (var i = 0; i < paths.length; ++i)
+ args.push(input.cpp.preincludeFlag, paths[i]);
+ return args;
+ } else {
+ return paths.map(function(path) { return input.cpp.preincludeFlag + path });
+ }
+}
+
+function collectLibraryPathsArguments(product, flag) {
+ flag = (flag === undefined) ? product.cpp.libraryPathFlag : flag;
+ var paths = collectLibraryPaths(product);
+ return paths.map(function(path) { return flag + path });
+}
+
+function collectLinkerScriptPathsArguments(product, inputs, split, flag) {
+ flag = (flag === undefined) ? product.cpp.linkerScriptFlag : flag;
+ var paths = collectLinkerScriptPaths(inputs);
+ if (split) {
+ var args = [];
+ for (var i = 0; i < paths.length; ++i)
+ args.push(flag, paths[i]);
+ return args;
+ } else {
+ return paths.map(function(path) { return flag + path });
+ }
+}
+
+function collectLinkerObjectPathsArguments(product, inputs, flag) {
+ flag = (flag === undefined) ? "" : flag;
+ var paths = collectLinkerObjectPaths(product);
+ return paths.map(function(path) { return flag + path });
+}
+
+function collectMiscCompilerArguments(input, tag) {
+ return [].concat(ModUtils.moduleProperty(input, "platformFlags"),
+ ModUtils.moduleProperty(input, "flags"),
+ ModUtils.moduleProperty(input, "platformFlags", tag),
+ ModUtils.moduleProperty(input, "flags", tag));
+}
+
+function collectMiscAssemblerArguments(input, tag) {
+ return [].concat(ModUtils.moduleProperty(input, "platformFlags", tag),
+ ModUtils.moduleProperty(input, "flags", tag));
+}
+
+function collectMiscDriverArguments(config) {
+ return [].concat(ModUtils.moduleProperty(config, "platformDriverFlags"),
+ ModUtils.moduleProperty(config, "driverFlags"),
+ ModUtils.moduleProperty(config, "targetDriverFlags"));
+}
+
+function collectMiscLinkerArguments(product) {
+ return [].concat(ModUtils.moduleProperty(product, "driverLinkerFlags"));
+}
+
+function collectMiscEscapableLinkerArguments(product) {
+ return [].concat(ModUtils.moduleProperty(product, "platformLinkerFlags"),
+ ModUtils.moduleProperty(product, "linkerFlags"));
+}
+
+function supportsArchitecture(architecture, knownArchitectures) {
+ for (var i = 0; i < knownArchitectures.length; ++i) {
+ if (architecture.startsWith("arm")) {
+ if (architecture.startsWith(knownArchitectures[i]))
+ return true;
+ } else {
+ if (architecture === knownArchitectures[i])
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/share/qbs/modules/cpp/darwin.js b/share/qbs/modules/cpp/darwin.js
index 6373b57c4..0223c5ed3 100644
--- a/share/qbs/modules/cpp/darwin.js
+++ b/share/qbs/modules/cpp/darwin.js
@@ -28,6 +28,7 @@
**
****************************************************************************/
+var Codesign = require("../codesign/codesign.js");
var File = require("qbs.File");
var FileInfo = require("qbs.FileInfo");
var Gcc = require("./gcc.js");
@@ -72,7 +73,7 @@ function lipoOutputArtifacts(product, inputs, fileTag, debugSuffix) {
// approach for all bundle types.
var defaultVariant;
if (!buildVariants.some(function (x) { return x.name === "release"; })
- && product.multiplexByQbsProperties.contains("buildVariants")
+ && product.multiplexByQbsProperties.includes("buildVariants")
&& product.qbs.buildVariants && product.qbs.buildVariants.length > 1) {
var defaultBuildVariant = product.qbs.defaultBuildVariant;
buildVariants.map(function (variant) {
@@ -99,6 +100,9 @@ function lipoOutputArtifacts(product, inputs, fileTag, debugSuffix) {
else
tags.push(fileTag, "primary");
+ if (product.codesign.enableCodeSigning)
+ tags.push("codesign.signed_artifact");
+
return {
filePath: FileInfo.joinPaths(product.destinationDirectory,
PathTools.linkerOutputFilePath(fileTag, product,
@@ -133,7 +137,7 @@ function prepareLipo(project, product, inputs, outputs, input, output) {
for (var p in inputs)
inputs[p] = inputs[p].filter(function(inp) { return inp.product.name === product.name; });
var allInputs = [].concat.apply([], Object.keys(inputs).map(function (tag) {
- return ["application", "dynamiclibrary", "staticlibrary", "loadablemodule"].contains(tag)
+ return ["application", "dynamiclibrary", "staticlibrary", "loadablemodule"].includes(tag)
? inputs[tag] : [];
}));
@@ -168,26 +172,17 @@ function prepareLipo(project, product, inputs, outputs, input, output) {
commands.push(cmd);
}
- var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll
- || outputs.debuginfo_loadablemodule;
- if (debugInfo) {
- var dsymPath = 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
- ]).concat(outputs.primary.map(function (f) { return f.filePath; })));
- cmd.description = "generating dSYM for " + product.name;
- commands.push(cmd);
- }
+ commands = commands.concat(
+ Gcc.separateDebugInfoCommandsDarwin(product, outputs, outputs.primary));
- cmd = new Command(ModUtils.moduleProperty(product, "stripPath"),
- ["-S", outputs.primary[0].filePath]);
- cmd.silent = true;
- commands.push(cmd);
if (outputs.dynamiclibrary_symbols)
Array.prototype.push.apply(commands, Gcc.createSymbolCheckingCommands(product, outputs));
+
+ if (product.codesign.enableCodeSigning) {
+ Array.prototype.push.apply(
+ commands, Codesign.prepareSign(project, product, inputs, outputs, input, output));
+ }
+
return commands;
}
diff --git a/share/qbs/modules/cpp/dmc.js b/share/qbs/modules/cpp/dmc.js
new file mode 100644
index 000000000..ea7cd7bb5
--- /dev/null
+++ b/share/qbs/modules/cpp/dmc.js
@@ -0,0 +1,506 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 Cpp = require("cpp.js");
+var Environment = require("qbs.Environment");
+var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var ModUtils = require("qbs.ModUtils");
+var Process = require("qbs.Process");
+var TemporaryDir = require("qbs.TemporaryDir");
+var TextFile = require("qbs.TextFile");
+var Utilities = require("qbs.Utilities");
+
+function targetFlags(platform, architecture, extender, consoleApp, type) {
+ if (platform === "dos") {
+ if (architecture === "x86_16") {
+ if (extender === "dosz")
+ return ["-mz"];
+ else if (extender === "dosr")
+ return ["-mr"];
+ return ["-mc"];
+ } else if (architecture === "x86") {
+ if (extender === "dosx")
+ return ["-mx"];
+ else if (extender === "dosp")
+ return ["-mp"];
+ }
+ } else if (platform === "windows") {
+ var flags = [];
+ if (architecture === "x86_16") {
+ flags.push("-ml");
+ if (type.includes("application") && !consoleApp)
+ flags.push("-WA");
+ else if (type.includes("dynamiclibrary"))
+ flags.push("-WD");
+ } else if (architecture === "x86") {
+ flags.push("-mn");
+ if (type.includes("application"))
+ flags.push("-WA");
+ else if (type.includes("dynamiclibrary"))
+ flags.push("-WD");
+ }
+ return flags;
+ }
+ return [];
+}
+
+function languageFlags(tag) {
+ if (tag === "cpp")
+ return ["-cpp"];
+ return [];
+}
+
+function dumpMacros(compilerPath, platform, architecture, extender, tag) {
+ // Note: The DMC compiler does not support the predefined/ macros dumping. So, we do it
+ // with the following trick, where we try to create and compile a special temporary file
+ // and to parse the console output with the own magic pattern: #define <key> <value>.
+
+ var outputDirectory = new TemporaryDir();
+ var outputFilePath = FileInfo.joinPaths(outputDirectory.path(), "dump-macros.c");
+ var outputFile = new TextFile(outputFilePath, TextFile.WriteOnly);
+ outputFile.writeLine("#define VALUE_TO_STRING(x) #x");
+ outputFile.writeLine("#define VALUE(x) VALUE_TO_STRING(x)");
+ outputFile.writeLine("#define VAR_NAME_VALUE(var) \"#define \" #var\" \"VALUE(var)");
+ // Declare all available pre-defined macros of DMC compiler.
+ var keys = [
+ // Prepare the DOS target macros.
+ "_MSDOS", "MSDOS",
+ // Prepare the OS/2 target macros.
+ "__OS2__",
+ // Prepare the Windows target macros.
+ "WIN32", "_WIN32", "__NT__",
+ // Prepare extended the 32 and 16 bit DOS target macros.
+ "DOS386", "DOS16RM",
+ // Prepare the memory model macros.
+ "M_I86", "_M_I86",
+ "_M_I86TM", "M_I86TM",
+ "_M_I86SM", "M_I86SM",
+ "_M_I86MM", "M_I86MM",
+ "_M_I86CM", "M_I86CM",
+ "_M_I86LM", "M_I86LM",
+ "_M_I86VM", "M_I86VM",
+ // Prepare 8086 macros.
+ "_M_I8086", "M_I8086",
+ // Prepare 286 macros.
+ "_M_I286", "M_I286",
+ // Prepare 32 bit macros.
+ "_M_IX86",
+ // Prepare compiler identification macros.
+ "__DMC__", "__DMC_VERSION_STRING__",
+ // Prepare common compiler macros.
+ "_CHAR_UNSIGNED", "_CHAR_EQ_UCHAR", "_DEBUG_TRACE", "_DLL",
+ "_ENABLE_ARRAYNEW", "_BOOL_DEFINED", "_WCHAR_T_DEFINED",
+ "_CPPRTTI", "_CPPUNWIND", "_MD", "_PUSHPOP_SUPPORTED",
+ "_STDCALL_SUPPORTED", "__INTSIZE", "__DEFALIGN", "_INTEGRAL_MAX_BITS",
+ "_WINDOWS", "_WINDLL", "__INLINE_8087", "__I86__", "__SMALL__",
+ "__MEDIUM__", "__COMPACT__", "__LARGE__", "__VCM__", "__FPCE__",
+ "__FPCE__IEEE__", "DEBUG",
+ // Prepare C99 and C++98 macros.
+ "__STDC__", "__STDC_HOSTED__", "__STDC_VERSION__", "__STDC_IEC_559__",
+ "__STDC_IEC_559_COMPLEX__", "__STDC_ISO_10646__", "__cplusplus"
+ ];
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ outputFile.writeLine("#if defined (" + key + ")");
+ outputFile.writeLine("#pragma message (VAR_NAME_VALUE(" + key + "))");
+ outputFile.writeLine("#endif");
+ }
+ outputFile.close();
+
+ var process = new Process();
+ process.setWorkingDirectory(outputDirectory.path());
+ var lang = languageFlags(tag);
+ var target = targetFlags(platform, architecture, extender, false, ["application"]);
+ var args = ["-c"].concat(lang, target, FileInfo.toWindowsSeparators(outputFilePath));
+ process.exec(compilerPath, args, false);
+ File.remove(outputFilePath);
+ var out = process.readStdOut();
+ return Cpp.extractMacros(out);
+}
+
+function dumpDefaultPaths(compilerFilePath, tag) {
+ var binPath = FileInfo.path(compilerFilePath);
+ var rootPath = FileInfo.path(binPath);
+ var includePaths = [];
+ var cppIncludePath = FileInfo.joinPaths(rootPath, "stlport/stlport");
+ if (File.exists(cppIncludePath))
+ includePaths.push(cppIncludePath);
+ var cIncludePath = FileInfo.joinPaths(rootPath, "include");
+ if (File.exists(cIncludePath))
+ includePaths.push(cIncludePath);
+
+ var libraryPaths = [];
+ var libraryPath = FileInfo.joinPaths(rootPath, "lib");
+ if (File.exists(libraryPath))
+ libraryPaths.push(libraryPath);
+
+ return {
+ "includePaths": includePaths,
+ "libraryPaths": libraryPaths,
+ }
+}
+
+function guessVersion(macros) {
+ var version = macros["__DMC__"];
+ return { major: parseInt(version / 100),
+ minor: parseInt(version % 100),
+ patch: 0 };
+}
+
+function effectiveLinkerPath(product) {
+ if (product.cpp.linkerMode === "automatic") {
+ var compilerPath = product.cpp.compilerPath;
+ if (compilerPath)
+ return compilerPath;
+ console.log("Found no C-language objects, choosing system linker for " + product.name);
+ }
+ return product.cpp.linkerPath;
+}
+
+function useCompilerDriverLinker(product) {
+ var linker = effectiveLinkerPath(product);
+ var compilers = product.cpp.compilerPathByLanguage;
+ if (compilers)
+ return linker === compilers["cpp"] || linker === compilers["c"];
+ return linker === product.cpp.compilerPath;
+}
+
+function depsOutputTags() {
+ return ["dep"];
+}
+
+function depsOutputArtifacts(input, product) {
+ return [{
+ fileTags: depsOutputTags(),
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ input.baseName + ".dep")
+ }];
+}
+
+function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
+ var args = ["-c"];
+
+ var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
+ args = args.concat(languageFlags(tag));
+ args = args.concat(targetFlags(product.qbs.targetPlatform, product.qbs.architecture,
+ product.cpp.extenderName, product.consoleApplication,
+ product.type));
+
+ // Input.
+ args.push(FileInfo.toWindowsSeparators(input.filePath));
+ // Output.
+ args.push("-o" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath));
+ // Preinclude headers.
+ args = args.concat(Cpp.collectPreincludePaths(input).map(function(path) {
+ return input.cpp.preincludeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+
+ // Defines.
+ args = args.concat(Cpp.collectDefinesArguments(input));
+
+ if (tag === "cpp") {
+ // We need to add the paths to the STL includes, because the DMC compiler does
+ // not handle it by default (because the STL libraries is a separate port).
+ var compilerIncludePaths = input.cpp.compilerIncludePaths || [];
+ args = args.concat(compilerIncludePaths.map(function(path) {
+ return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+ }
+
+ // Other includes.
+ args = args.concat(Cpp.collectIncludePaths(input).map(function(path) {
+ return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+ args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) {
+ return input.cpp.systemIncludeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+
+ // Debug information flags.
+ if (input.cpp.debugInformation)
+ args.push("-d");
+
+ // Optimization flags.
+ switch (input.cpp.optimization) {
+ case "small":
+ args.push("-o+space");
+ break;
+ case "fast":
+ args.push("-o+speed");
+ break;
+ case "none":
+ args.push("-o+none");
+ break;
+ }
+
+ // Warning level flags.
+ switch (input.cpp.warningLevel) {
+ case "none":
+ args.push("-w");
+ break;
+ case "all":
+ args.push("-w-");
+ break;
+ }
+ if (input.cpp.treatWarningsAsErrors)
+ args.push("-wx");
+
+ if (tag === "cpp") {
+ // Exceptions flag.
+ if (input.cpp.enableExceptions)
+ args.push("-Ae");
+
+ // RTTI flag.
+ var enableRtti = input.cpp.enableRtti;
+ if (input.cpp.enableRtti)
+ args.push("-Ar");
+ }
+
+ // Listing files generation flag.
+ if (input.cpp.generateCompilerListingFiles) {
+ // We need to use the relative path here, because the DMC compiler does not handle
+ // a long file path for this option.
+ var listingPath = Cpp.relativePath(product.buildDirectory, outputs.lst[0].filePath);
+ args.push("-l" + FileInfo.toWindowsSeparators(listingPath));
+ }
+
+ // Misc flags.
+ args = args.concat(Cpp.collectMiscCompilerArguments(input, tag),
+ Cpp.collectMiscDriverArguments(product));
+ return args;
+}
+
+function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
+ var args = ["-c"];
+
+ // Input.
+ args.push(FileInfo.toWindowsSeparators(input.filePath));
+ // Output.
+ args.push("-o" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath));
+ // Preinclude headers.
+ args = args.concat(Cpp.collectPreincludePaths(input).map(function(path) {
+ return input.cpp.preincludeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+
+ // Defines.
+ args = args.concat(Cpp.collectDefinesArguments(input));
+
+ // Other includes.
+ args = args.concat(Cpp.collectIncludePaths(input).map(function(path) {
+ return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+ args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) {
+ return input.cpp.systemIncludeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+
+ // Misc flags.
+ args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm"));
+ return args;
+}
+
+function linkerFlags(project, product, inputs, outputs) {
+ var args = [];
+
+ var useCompilerDriver = useCompilerDriverLinker(product);
+ if (useCompilerDriver) {
+ args = args.concat(targetFlags(product.qbs.targetPlatform, product.qbs.architecture,
+ product.cpp.extenderName, product.consoleApplication,
+ product.type));
+
+ // Input objects.
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) {
+ return FileInfo.toWindowsSeparators(path);
+ }));
+
+ // Input resources.
+ args = args.concat(Cpp.collectResourceObjectPaths(inputs).map(function(path) {
+ return FileInfo.toWindowsSeparators(path);
+ }));
+
+ // Input libraries.
+ args = args.concat(Cpp.collectAbsoluteLibraryDependencyPaths(product).map(function(path) {
+ return FileInfo.toWindowsSeparators(path);
+ }));
+
+ // Output.
+ if (product.type.includes("application")) {
+ args.push("-o" + FileInfo.toWindowsSeparators(outputs.application[0].filePath));
+ args.push("-L/" + (product.cpp.generateLinkerMapFile ? "MAP" : "NOMAP"));
+ if (product.qbs.targetPlatform === "windows" && product.qbs.architecture === "x86")
+ args.push("-L/SUBSYSTEM:" + (product.consoleApplication ? "CONSOLE" : "WINDOWS"));
+ } else if (product.type.includes("dynamiclibrary")) {
+ args.push("-o" + FileInfo.toWindowsSeparators(outputs.dynamiclibrary[0].filePath));
+ if (product.qbs.targetPlatform === "windows" && product.qbs.architecture === "x86") {
+ args.push("kernel32.lib");
+ args.push("-L/IMPLIB:" + FileInfo.toWindowsSeparators(
+ outputs.dynamiclibrary_import[0].filePath));
+ }
+ }
+
+ if (product.cpp.debugInformation)
+ args.push("-L/DEBUG");
+
+ args.push("-L/NOLOGO", "-L/SILENT");
+ }
+
+ // Misc flags.
+ args = args.concat(Cpp.collectMiscEscapableLinkerArguments(product),
+ Cpp.collectMiscLinkerArguments(product),
+ Cpp.collectMiscDriverArguments(product));
+ return args;
+}
+
+function archiverFlags(project, product, inputs, outputs) {
+ var args = ["-c"];
+ // Output.
+ args.push(FileInfo.toWindowsSeparators(outputs.staticlibrary[0].filePath));
+ // Input objects.
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) {
+ return FileInfo.toWindowsSeparators(path);
+ }));
+ return args;
+}
+
+function rccCompilerFlags(project, product, input, outputs) {
+ // Input.
+ var args = [FileInfo.toWindowsSeparators(input.filePath)];
+ // Output.
+ args.push("-o" + FileInfo.toWindowsSeparators(outputs.res[0].filePath));
+ // Bitness.
+ args.push("-32");
+
+ // Defines
+ args = args.concat(Cpp.collectDefinesArguments(input));
+
+ // Other includes.
+ args = args.concat(Cpp.collectIncludePaths(input).map(function(path) {
+ return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+ args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) {
+ return input.cpp.systemIncludeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+ return args;
+}
+
+function buildLinkerMapFilePath(target, suffix) {
+ return FileInfo.joinPaths(FileInfo.path(target.filePath),
+ FileInfo.completeBaseName(target.fileName) + suffix);
+}
+
+// It is a workaround which removes the generated linker map file if it is disabled
+// by cpp.generateLinkerMapFile property. Reason is that the DMC compiler always
+// generates this file, and does not have an option to disable generation for a linker
+// map file. So, we can to remove a listing files only after the linking completes.
+function removeLinkerMapFile(project, product, inputs, outputs, input, output) {
+ var target = outputs.dynamiclibrary ? outputs.dynamiclibrary[0]
+ : outputs.application[0];
+ var cmd = new JavaScriptCommand();
+ cmd.mapFilePath = buildLinkerMapFilePath(target, ".map")
+ cmd.silent = true;
+ cmd.sourceCode = function() { File.remove(mapFilePath); };
+ return cmd;
+}
+
+// It is a workaround to rename the extension of the output linker map file to the
+// specified one, since the linker generates only files with the '.map' extension.
+function renameLinkerMapFile(project, product, inputs, outputs, input, output) {
+ var target = outputs.dynamiclibrary ? outputs.dynamiclibrary[0]
+ : outputs.application[0];
+ var cmd = new JavaScriptCommand();
+ cmd.newMapFilePath = buildLinkerMapFilePath(target, product.cpp.linkerMapSuffix);
+ cmd.oldMapFilePath = buildLinkerMapFilePath(target, ".map");
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ if (oldMapFilePath !== newMapFilePath)
+ File.move(oldMapFilePath, newMapFilePath);
+ };
+ return cmd;
+}
+
+function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn);
+ var cmd = new Command(input.cpp.compilerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "compiling " + input.fileName;
+ cmd.highlight = "compiler";
+ cmd.jobPool = "compiler";
+ return [cmd];
+}
+
+function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var args = assemblerFlags(project, product, input, outputs, explicitlyDependsOn);
+ var cmd = new Command(input.cpp.assemblerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "compiling " + input.fileName;
+ cmd.highlight = "compiler";
+ cmd.jobPool = "assembler";
+ return [cmd];
+}
+
+function prepareLinker(project, product, inputs, outputs, input, output) {
+ var cmds = [];
+ var primaryOutput = outputs.dynamiclibrary ? outputs.dynamiclibrary[0]
+ : outputs.application[0];
+ var args = linkerFlags(project, product, inputs, outputs);
+ var linkerPath = effectiveLinkerPath(product);
+ var cmd = new Command(linkerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "linking " + primaryOutput.fileName;
+ cmd.highlight = "linker";
+ cmd.jobPool = "linker";
+ cmds.push(cmd);
+
+ if (product.cpp.generateLinkerMapFile)
+ cmds.push(renameLinkerMapFile(project, product, inputs, outputs, input, output));
+ else
+ cmds.push(removeLinkerMapFile(project, product, inputs, outputs, input, output));
+
+ return cmds;
+}
+
+function prepareArchiver(project, product, inputs, outputs, input, output) {
+ var args = archiverFlags(project, product, inputs, outputs);
+ var cmd = new Command(product.cpp.archiverPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "creating " + output.fileName;
+ cmd.highlight = "linker";
+ cmd.jobPool = "linker";
+ return [cmd];
+}
+
+function prepareRccCompiler(project, product, inputs, outputs, input, output) {
+ var args = rccCompilerFlags(project, product, input, outputs);
+ var cmd = new Command(input.cpp.rccCompilerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "compiling " + input.fileName;
+ cmd.highlight = "compiler";
+ cmd.jobPool = "compiler";
+ return [cmd];
+}
diff --git a/share/qbs/modules/cpp/dmc.qbs b/share/qbs/modules/cpp/dmc.qbs
new file mode 100644
index 000000000..ac89550e8
--- /dev/null
+++ b/share/qbs/modules/cpp/dmc.qbs
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** 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.File
+import qbs.FileInfo
+import qbs.Host
+import qbs.PathTools
+import qbs.Probes
+import qbs.Utilities
+import "dmc.js" as DMC
+import "cpp.js" as Cpp
+
+CppModule {
+ condition: Host.os().includes("windows") && qbs.toolchain && qbs.toolchain.includes("dmc")
+
+ Probes.BinaryProbe {
+ id: compilerPathProbe
+ condition: !toolchainInstallPath && !_skipAllChecks
+ names: ["dmc"]
+ }
+
+ Probes.DmcProbe {
+ id: dmcProbe
+ condition: !_skipAllChecks
+ compilerFilePath: compilerPath
+ enableDefinesByLanguage: enableCompilerDefinesByLanguage
+ _targetPlatform: qbs.targetPlatform
+ _targetArchitecture: qbs.architecture
+ _targetExtender: extenderName
+ }
+
+ qbs.architecture: dmcProbe.found ? dmcProbe.architecture : original
+ qbs.targetPlatform: dmcProbe.found ? dmcProbe.targetPlatform : original
+
+ compilerVersionMajor: dmcProbe.versionMajor
+ compilerVersionMinor: dmcProbe.versionMinor
+ compilerVersionPatch: dmcProbe.versionPatch
+ endianness: "little"
+
+ compilerDefinesByLanguage: dmcProbe.compilerDefinesByLanguage
+ compilerIncludePaths: dmcProbe.includePaths
+
+ toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined
+
+ /* Work-around for QtCreator which expects these properties to exist. */
+ property string cCompilerName: compilerName
+ property string cxxCompilerName: compilerName
+
+ compilerName: "dmc.exe"
+ compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName)
+
+ assemblerName: "dmc.exe"
+ assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName)
+
+ linkerName: "link.exe"
+ linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName)
+
+ property string archiverName: "lib.exe"
+ property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName)
+ property string rccCompilerName: "rcc.exe"
+ property string rccCompilerPath: FileInfo.joinPaths(toolchainInstallPath, rccCompilerName)
+
+ property string extenderName
+ PropertyOptions {
+ name: "extenderName"
+ allowedValues: [undefined, "dosz", "dosr", "dosx", "dosp"]
+ description: "Specifies the DOS memory extender to compile with:\n"
+ + " - \"dosz\" is the ZPM 16 bit DOS Extender\n"
+ + " - \"dosr\" is the Rational 16 bit DOS Extender\n"
+ + " - \"dosx\" is the DOSX 32 bit DOS Extender\n"
+ + " - \"dosp\" is the Pharlap 32 bit DOS Extender\n"
+ ;
+ }
+
+ runtimeLibrary: "dynamic"
+
+ staticLibrarySuffix: ".lib"
+ dynamicLibrarySuffix: ".dll"
+ executableSuffix: ".exe"
+ objectSuffix: ".obj"
+
+ imageFormat: {
+ if (qbs.targetPlatform === "dos")
+ return "mz";
+ else if (qbs.targetPlatform === "windows")
+ return "pe";
+ }
+
+ defineFlag: "-D"
+ includeFlag: "-I"
+ systemIncludeFlag: "-I"
+ preincludeFlag: "-HI"
+ libraryPathFlag: "-L/packcode"
+
+ knownArchitectures: ["x86", "x86_16"]
+
+ Rule {
+ id: assembler
+ inputs: ["asm"]
+ outputFileTags: DMC.depsOutputTags().concat(
+ Cpp.assemblerOutputTags(generateAssemblerListingFiles))
+ outputArtifacts: DMC.depsOutputArtifacts(input, product).concat(
+ Cpp.assemblerOutputArtifacts(input))
+ prepare: DMC.prepareAssembler.apply(DMC, arguments)
+ }
+
+ FileTagger {
+ patterns: ["*.s", "*.asm"]
+ fileTags: ["asm"]
+ }
+
+ Rule {
+ id: compiler
+ inputs: ["cpp", "c"]
+ auxiliaryInputs: ["hpp"]
+ outputFileTags: DMC.depsOutputTags().concat(
+ Cpp.compilerOutputTags(generateCompilerListingFiles))
+ outputArtifacts: DMC.depsOutputArtifacts(input, product).concat(
+ Cpp.compilerOutputArtifacts(input))
+ prepare: DMC.prepareCompiler.apply(DMC, arguments)
+ }
+
+
+ Rule {
+ id: rccCompiler
+ inputs: ["rc"]
+ auxiliaryInputs: ["hpp"]
+ outputFileTags: Cpp.resourceCompilerOutputTags()
+ outputArtifacts: Cpp.resourceCompilerOutputArtifacts(input)
+ prepare: DMC.prepareRccCompiler.apply(DMC, arguments)
+ }
+
+ FileTagger {
+ patterns: ["*.rc"]
+ fileTags: ["rc"]
+ }
+
+ Rule {
+ id: applicationLinker
+ multiplex: true
+ inputs: ["obj", "res", "linkerscript"]
+ inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"]
+ outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile)
+ outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product)
+ prepare: DMC.prepareLinker.apply(DMC, arguments)
+ }
+
+ Rule {
+ id: dynamicLibraryLinker
+ multiplex: true
+ inputs: ["obj", "res"]
+ inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"]
+ outputFileTags: Cpp.dynamicLibraryLinkerOutputTags()
+ outputArtifacts: Cpp.dynamicLibraryLinkerOutputArtifacts(product)
+ prepare: DMC.prepareLinker.apply(DMC, arguments)
+ }
+
+ Rule {
+ id: staticLibraryLinker
+ multiplex: true
+ inputs: ["obj"]
+ inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"]
+ outputFileTags: Cpp.staticLibraryLinkerOutputTags()
+ outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product)
+ prepare: DMC.prepareArchiver.apply(DMC, arguments)
+ }
+}
diff --git a/share/qbs/modules/cpp/freebsd-gcc.qbs b/share/qbs/modules/cpp/freebsd-gcc.qbs
index b5a4b89f3..e04ed7f2b 100644
--- a/share/qbs/modules/cpp/freebsd-gcc.qbs
+++ b/share/qbs/modules/cpp/freebsd-gcc.qbs
@@ -29,13 +29,14 @@
****************************************************************************/
import "freebsd.js" as FreeBSD
+import qbs.Host
UnixGCC {
- condition: qbs.targetOS && qbs.targetOS.contains("freebsd") &&
- qbs.toolchain && qbs.toolchain.contains("gcc")
+ condition: qbs.targetOS.includes("freebsd") &&
+ qbs.toolchain && qbs.toolchain.includes("gcc")
priority: 1
- targetSystem: "freebsd" + (qbs.hostOS.contains("freebsd") ? FreeBSD.hostKernelRelease() : "")
+ targetSystem: "freebsd" + (Host.os().includes("freebsd") ? FreeBSD.hostKernelRelease() : "")
distributionIncludePaths: ["/usr/local/include"]
distributionLibraryPaths: ["/usr/local/lib"]
diff --git a/share/qbs/modules/cpp/gcc.js b/share/qbs/modules/cpp/gcc.js
index 574fe769b..90f8fc933 100644
--- a/share/qbs/modules/cpp/gcc.js
+++ b/share/qbs/modules/cpp/gcc.js
@@ -28,9 +28,11 @@
**
****************************************************************************/
+var Codesign = require("../codesign/codesign.js");
var Cpp = require("cpp.js");
var File = require("qbs.File");
var FileInfo = require("qbs.FileInfo");
+var Host = require("qbs.Host");
var DarwinTools = require("qbs.DarwinTools");
var ModUtils = require("qbs.ModUtils");
var PathTools = require("qbs.PathTools");
@@ -76,6 +78,7 @@ function useCompilerDriverLinker(product, inputs) {
function collectLibraryDependencies(product, isDarwin) {
var publicDeps = {};
+ var privateDeps = {};
var objects = [];
var objectByFilePath = {};
var tagForLinkingAgainstSharedLib = product.cpp.imageFormat === "pe"
@@ -132,7 +135,7 @@ function collectLibraryDependencies(product, isDarwin) {
if (!obj.cpp)
return;
function ensureArray(a) {
- return Array.isArray(a) ? a : [];
+ return (a instanceof Array) ? a : [];
}
function sanitizedModuleListProperty(obj, moduleName, propertyName) {
return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName));
@@ -168,6 +171,8 @@ function collectLibraryDependencies(product, isDarwin) {
&& typeof dep.artifacts[tagForLinkingAgainstSharedLib] !== "undefined";
if (!isStaticLibrary && !isDynamicLibrary)
return;
+ if (isBelowIndirectDynamicLib && privateDeps[dep.name])
+ return;
var nextIsBelowIndirectDynamicLib = isBelowIndirectDynamicLib || isDynamicLibrary;
dep.dependencies.forEach(function(depdep) {
@@ -191,6 +196,7 @@ function collectLibraryDependencies(product, isDarwin) {
publicDeps[dep.name] = true;
} else {
addArtifactFilePaths(dep, tagForLinkingAgainstSharedLib, addPrivateFilePath);
+ privateDeps[dep.name] = true;
}
}
}
@@ -202,7 +208,6 @@ function collectLibraryDependencies(product, isDarwin) {
product.dependencies.forEach(traverseDirectDependency);
addExternalLibs(product);
- var seenRPathLinkDirs = {};
var result = { libraries: [], rpath_link: [] };
objects.forEach(
function (obj) {
@@ -213,10 +218,7 @@ function collectLibraryDependencies(product, isDarwin) {
framework: obj.framework });
} else {
var dirPath = FileInfo.path(obj.filePath);
- if (!seenRPathLinkDirs.hasOwnProperty(dirPath)) {
- seenRPathLinkDirs[dirPath] = true;
- result.rpath_link.push(dirPath);
- }
+ result.rpath_link.push(dirPath);
}
});
return result;
@@ -228,7 +230,7 @@ function escapeLinkerFlags(product, inputs, linkerFlags) {
if (useCompilerDriverLinker(product, inputs)) {
var sep = ",";
- var useXlinker = linkerFlags.some(function (f) { return f.contains(sep); });
+ var useXlinker = linkerFlags.some(function (f) { return f.includes(sep); });
if (useXlinker) {
// One or more linker arguments contain the separator character itself
// Use -Xlinker to handle these
@@ -245,7 +247,7 @@ function escapeLinkerFlags(product, inputs, linkerFlags) {
return xlinkerFlags;
}
- if (product.cpp.enableSuspiciousLinkerFlagWarnings && linkerFlags.contains("-Xlinker")) {
+ if (product.cpp.enableSuspiciousLinkerFlagWarnings && linkerFlags.includes("-Xlinker")) {
console.warn("Encountered -Xlinker linker flag escape sequence. This may cause the " +
"target to fail to link. Please do not escape these flags manually; " +
"qbs does that for you.");
@@ -260,9 +262,7 @@ function escapeLinkerFlags(product, inputs, linkerFlags) {
}
function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPath) {
- var libraryPaths = product.cpp.libraryPaths;
- var distributionLibraryPaths = product.cpp.distributionLibraryPaths;
- var isDarwin = product.qbs.targetOS.contains("darwin");
+ var isDarwin = product.qbs.targetOS.includes("darwin");
var libraryDependencies = collectLibraryDependencies(product, isDarwin);
var frameworks = product.cpp.frameworks;
var weakFrameworks = product.cpp.weakFrameworks;
@@ -275,7 +275,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
var escapableLinkerFlags = [];
- if (primaryOutput.fileTags.contains("dynamiclibrary")) {
+ if (primaryOutput.fileTags.includes("dynamiclibrary")) {
if (isDarwin) {
args.push((function () {
var tags = ["c", "cpp", "objc", "objcpp", "asm_cpp"];
@@ -300,7 +300,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
}
}
- if (primaryOutput.fileTags.contains("loadablemodule"))
+ if (primaryOutput.fileTags.includes("loadablemodule"))
args.push(isDarwin ? "-bundle" : "-shared");
if (primaryOutput.fileTags.containsAny(["dynamiclibrary", "loadablemodule"])) {
@@ -323,7 +323,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
var sysroot = product.cpp.syslibroot;
if (sysroot) {
- if (product.qbs.toolchain.contains("qcc"))
+ if (product.qbs.toolchain.includes("qcc"))
escapableLinkerFlags.push("--sysroot=" + sysroot);
else if (isDarwin)
escapableLinkerFlags.push("-syslibroot", sysroot);
@@ -340,7 +340,7 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
function fixupRPath(rpath) {
// iOS, tvOS, watchOS, and others, are fine
- if (!product.qbs.targetOS.contains("macos"))
+ if (!product.qbs.targetOS.includes("macos"))
return rpath;
// ...as are newer versions of macOS
@@ -355,11 +355,12 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
return rpath;
}
- if (!product.qbs.targetOS.contains("windows")) {
- function isNotSystemRunPath(p) {
- return !FileInfo.isAbsolutePath(p) || (!systemRunPaths.contains(p)
- && !canonicalSystemRunPaths.contains(File.canonicalFilePath(p)));
- };
+ function isNotSystemRunPath(p) {
+ return !FileInfo.isAbsolutePath(p) || (!systemRunPaths.includes(p)
+ && !canonicalSystemRunPaths.includes(File.canonicalFilePath(p)));
+ };
+
+ if (!product.qbs.targetOS.includes("windows")) {
for (i in rpaths) {
if (isNotSystemRunPath(rpaths[i]))
escapableLinkerFlags.push("-rpath", fixupRPath(rpaths[i]));
@@ -369,13 +370,16 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
if (product.cpp.entryPoint)
escapableLinkerFlags.push("-e", product.cpp.entryPoint);
- if (product.qbs.toolchain.contains("mingw")) {
+ if (product.qbs.toolchain.includes("mingw")) {
if (product.consoleApplication !== undefined)
escapableLinkerFlags.push("-subsystem",
product.consoleApplication ? "console" : "windows");
var minimumWindowsVersion = product.cpp.minimumWindowsVersion;
if (minimumWindowsVersion) {
+ // workaround for QBS-1724, mingw seems to be broken
+ if (Utilities.versionCompare(minimumWindowsVersion, "6.2") > 0)
+ minimumWindowsVersion = "6.2";
var subsystemVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'subsystem');
if (subsystemVersion) {
var major = subsystemVersion.split('.')[0];
@@ -397,23 +401,16 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
var stdlib = isLinkingCppObjects
? product.cpp.cxxStandardLibrary
: undefined;
- if (stdlib && product.qbs.toolchain.contains("clang"))
+ if (stdlib && product.qbs.toolchain.includes("clang"))
args.push("-stdlib=" + stdlib);
// Flags for library search paths
- var allLibraryPaths = [];
- if (libraryPaths)
- allLibraryPaths = allLibraryPaths.uniqueConcat(libraryPaths);
- if (distributionLibraryPaths)
- allLibraryPaths = allLibraryPaths.uniqueConcat(distributionLibraryPaths);
- if (systemRunPaths.length > 0)
- allLibraryPaths = allLibraryPaths.filter(isNotSystemRunPath);
- args = args.concat(allLibraryPaths.map(function(path) { return '-L' + path }));
-
- var linkerScripts = inputs.linkerscript
- ? inputs.linkerscript.map(function(a) { return a.filePath; }) : [];
- Array.prototype.push.apply(escapableLinkerFlags, [].uniqueConcat(linkerScripts)
- .map(function(path) { return '-T' + path }));
+ var allLibraryPaths = Cpp.collectLibraryPaths(product);
+ var builtIns = product.cpp.compilerLibraryPaths
+ allLibraryPaths = allLibraryPaths.filter(function(p) { return !builtIns.includes(p); });
+ args = args.concat(allLibraryPaths.map(function(path) { return product.cpp.libraryPathFlag + path }));
+
+ escapableLinkerFlags = escapableLinkerFlags.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs));
var versionScripts = inputs.versionscript
? inputs.versionscript.map(function(a) { return a.filePath; }) : [];
@@ -425,16 +422,15 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
var useCompilerDriver = useCompilerDriverLinker(product, inputs);
args = args.concat(configFlags(product, useCompilerDriver));
- Array.prototype.push.apply(escapableLinkerFlags, product.cpp.platformLinkerFlags);
- Array.prototype.push.apply(escapableLinkerFlags, product.cpp.linkerFlags);
+ escapableLinkerFlags = escapableLinkerFlags.concat(Cpp.collectMiscEscapableLinkerArguments(product));
// Note: due to the QCC response files hack in prepareLinker(), at least one object file or
// library file must follow the output file path so that QCC has something to process before
// sending the rest of the arguments through the response file.
args.push("-o", primaryOutput.filePath);
- if (inputs.obj)
- args = args.concat(inputs.obj.map(function (obj) { return obj.filePath }));
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs));
+ args = args.concat(Cpp.collectResourceObjectPaths(inputs));
for (i in frameworks) {
frameworkExecutablePath = PathTools.frameworkExecutablePath(frameworks[i]);
@@ -479,9 +475,12 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
var symbolLinkMode = dep.symbolLinkMode;
if (isDarwin && symbolLinkMode) {
- if (!["lazy", "reexport", "upward", "weak"].contains(symbolLinkMode))
+ if (!["lazy", "reexport", "upward", "weak"].includes(symbolLinkMode))
throw new Error("unknown value '" + symbolLinkMode + "' for cpp.symbolLinkMode");
+ }
+ var supportsLazyMode = Utilities.versionCompare(product.cpp.compilerVersion, "15.0.0") < 0;
+ if (isDarwin && symbolLinkMode && (symbolLinkMode !== "lazy" || supportsLazyMode)) {
if (FileInfo.isAbsolutePath(lib) || lib.startsWith('@'))
escapableLinkerFlags.push("-" + symbolLinkMode + "_library", lib);
else if (dep.framework)
@@ -535,9 +534,10 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
var escapedLinkerFlags = escapeLinkerFlags(product, inputs, escapableLinkerFlags);
Array.prototype.push.apply(escapedLinkerFlags, args);
- var driverLinkerFlags = useCompilerDriver ? product.cpp.driverLinkerFlags : undefined;
- if (driverLinkerFlags)
- Array.prototype.push.apply(escapedLinkerFlags, driverLinkerFlags);
+ if (useCompilerDriver)
+ escapedLinkerFlags = escapedLinkerFlags.concat(Cpp.collectMiscLinkerArguments(product));
+ if (product.qbs.toolchain.includes("mingw") && product.cpp.runtimeLibrary === "static")
+ escapedLinkerFlags = ['-static-libgcc', '-static-libstdc++'].concat(escapedLinkerFlags);
return escapedLinkerFlags;
}
@@ -545,15 +545,12 @@ function linkerFlags(project, product, inputs, outputs, primaryOutput, linkerPat
function configFlags(config, isDriver) {
var args = [];
- if (isDriver !== false) {
- args = args.concat(config.cpp.platformDriverFlags);
- args = args.concat(config.cpp.driverFlags);
- args = args.concat(config.cpp.targetDriverFlags);
- }
+ if (isDriver !== false)
+ args = args.concat(Cpp.collectMiscDriverArguments(config));
var frameworkPaths = config.cpp.frameworkPaths;
if (frameworkPaths)
- args = args.concat(frameworkPaths.map(function(path) { return '-F' + path }));
+ args = args.uniqueConcat(frameworkPaths.map(function(path) { return '-F' + path }));
var allSystemFrameworkPaths = [];
@@ -586,7 +583,7 @@ function languageTagFromFileExtension(toolchain, fileName) {
"s" : "asm",
"S" : "asm_cpp"
};
- if (!toolchain.contains("clang"))
+ if (!toolchain.includes("clang"))
m["sx"] = "asm_cpp"; // clang does NOT recognize .sx
return m[fileName.substring(i + 1)];
}
@@ -594,7 +591,7 @@ function languageTagFromFileExtension(toolchain, fileName) {
// Older versions of the QNX SDK have C and C++ compilers whose filenames differ only by case,
// which won't work in case insensitive environments like Win32+NTFS, HFS+ and APFS
function isLegacyQnxSdk(config) {
- return config.qbs.toolchain.contains("qcc") && config.qnx && !config.qnx.qnx7;
+ return config.qbs.toolchain.includes("qcc") && config.qnx && !config.qnx.qnx7;
}
function effectiveCompilerInfo(toolchain, input, output) {
@@ -602,7 +599,7 @@ function effectiveCompilerInfo(toolchain, input, output) {
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 pchOutput = output.fileTags.includes(tag + "_pch");
var compilerPathByLanguage = input.cpp.compilerPathByLanguage;
if (compilerPathByLanguage)
@@ -610,7 +607,7 @@ function effectiveCompilerInfo(toolchain, input, output) {
if (!compilerPath
|| tag !== languageTagFromFileExtension(toolchain, input.fileName)
|| isLegacyQnxSdk(input)) {
- if (input.qbs.toolchain.contains("qcc"))
+ if (input.qbs.toolchain.includes("qcc"))
language = qnxLangArgs(input, tag);
else
language = ["-x", languageName(tag) + (pchOutput ? '-header' : '')];
@@ -704,6 +701,22 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers
{"name": "gcc", "version": "4.7"}
]
},
+ "c17": {
+ "fallback": "c11",
+ "toolchains": [
+ {"name": "xcode", "version": "10.2"},
+ {"name": "clang", "version": "7.0"},
+ {"name": "gcc", "version": "8.1"}
+ ]
+ },
+ "c2x": {
+ "fallback": "c17",
+ "toolchains": [
+ {"name": "xcode", "version": "11.4"},
+ {"name": "clang", "version": "9.0"},
+ {"name": "gcc", "version": "9.0"}
+ ]
+ },
"c++14": {
"fallback": "c++1y",
"toolchains": [
@@ -723,9 +736,17 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers
"c++20": {
"fallback": "c++2a",
"toolchains": [
- {"name": "xcode"}, // not yet implemented
- {"name": "clang"}, // not yet implemented
- {"name": "gcc"} // not yet implemented
+ {"name": "xcode", "version": "12.5"},
+ {"name": "clang", "version": "11.0"},
+ {"name": "gcc", "version": "10.1"}
+ ]
+ },
+ "c++23": {
+ "fallback": "c++2b",
+ "toolchains": [
+ {"name": "xcode"},
+ {"name": "clang"},
+ {"name": "gcc"}
]
}
};
@@ -734,7 +755,7 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers
if (m) {
for (var idx = 0; idx < m.toolchains.length; ++idx) {
var tc = m.toolchains[idx];
- if (toolchain.contains(tc.name)) {
+ if (toolchain.includes(tc.name)) {
// If we found our toolchain and it doesn't yet support the language standard
// we're requesting, or we're using an older version that only supports the
// preliminary flag, use that.
@@ -754,16 +775,9 @@ function standardFallbackValueOrDefault(toolchain, compilerVersion, languageVers
function compilerFlags(project, product, input, output, explicitlyDependsOn) {
var i;
- var includePaths = input.cpp.includePaths;
- var systemIncludePaths = input.cpp.systemIncludePaths;
- var distributionIncludePaths = input.cpp.distributionIncludePaths;
-
- var platformDefines = input.cpp.platformDefines;
- var defines = input.cpp.defines;
-
// 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))
+ if (!["c", "cpp", "objc", "objcpp", "asm_cpp"].includes(tag))
throw ("unsupported source language: " + tag);
var compilerInfo = effectiveCompilerInfo(product.qbs.toolchain,
@@ -796,12 +810,12 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) {
args = args.concat(configFlags(input));
- if (!input.qbs.toolchain.contains("qcc"))
+ if (!input.qbs.toolchain.includes("qcc"))
args.push('-pipe');
if (input.cpp.enableReproducibleBuilds) {
var toolchain = product.qbs.toolchain;
- if (!toolchain.contains("clang")) {
+ if (!toolchain.includes("clang")) {
var hashString = FileInfo.relativePath(project.sourceDirectory, input.filePath);
var hash = Utilities.getHash(hashString);
args.push("-frandom-seed=0x" + hash.substring(0, 8));
@@ -809,8 +823,8 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) {
var major = product.cpp.compilerVersionMajor;
var minor = product.cpp.compilerVersionMinor;
- if ((toolchain.contains("clang") && (major > 3 || (major === 3 && minor >= 5))) ||
- (toolchain.contains("gcc") && (major > 4 || (major === 4 && minor >= 9)))) {
+ if ((toolchain.includes("clang") && (major > 3 || (major === 3 && minor >= 5))) ||
+ (toolchain.includes("gcc") && (major > 4 || (major === 4 && minor >= 9)))) {
args.push("-Wdate-time");
}
}
@@ -838,7 +852,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) {
}
var visibility = input.cpp.visibility;
- if (!product.qbs.toolchain.contains("mingw")) {
+ if (!product.qbs.toolchain.includes("mingw")) {
if (visibility === 'hidden' || visibility === 'minimal')
args.push('-fvisibility=hidden');
if ((visibility === 'hiddenInlines' || visibility === 'minimal') && tag === 'cpp')
@@ -851,62 +865,40 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) {
// Only push language arguments if we have to.
Array.prototype.push.apply(args, compilerInfo.language);
- args = args.concat(ModUtils.moduleProperty(input, 'platformFlags'),
- ModUtils.moduleProperty(input, 'flags'),
- ModUtils.moduleProperty(input, 'platformFlags', tag),
- ModUtils.moduleProperty(input, 'flags', tag));
+ args = args.concat(Cpp.collectMiscCompilerArguments(input, tag));
var pchTag = compilerInfo.tag + "_pch";
- var pchOutput = output.fileTags.contains(pchTag);
+ var pchOutput = output.fileTags.includes(pchTag);
var pchInputs = explicitlyDependsOn[pchTag];
if (!pchOutput && pchInputs && pchInputs.length === 1
&& ModUtils.moduleProperty(input, 'usePrecompiledHeader', tag)) {
var pchInput = pchInputs[0];
var pchFilePath = FileInfo.joinPaths(FileInfo.path(pchInput.filePath),
pchInput.completeBaseName);
- args.push('-include', pchFilePath);
+ args.push(input.cpp.preincludeFlag, pchFilePath);
}
- var prefixHeaders = input.cpp.prefixHeaders;
- for (i in prefixHeaders) {
- args.push('-include');
- args.push(prefixHeaders[i]);
- }
+ args = args.concat(Cpp.collectPreincludePathsArguments(input));
var positionIndependentCode = input.cpp.positionIndependentCode;
- if (positionIndependentCode && !product.qbs.targetOS.contains("windows"))
+ if (positionIndependentCode && !product.qbs.targetOS.includes("windows"))
args.push('-fPIC');
var cppFlags = input.cpp.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 input.cpp.includeFlag + path;
- }));
- }
-
- var allSystemIncludePaths = [];
- if (systemIncludePaths)
- allSystemIncludePaths = allSystemIncludePaths.uniqueConcat(systemIncludePaths);
- if (distributionIncludePaths)
- allSystemIncludePaths = allSystemIncludePaths.uniqueConcat(distributionIncludePaths);
- allSystemIncludePaths.forEach(function(v) { args.push(input.cpp.systemIncludeFlag, v); });
+ args = args.concat(Cpp.collectDefinesArguments(input));
+ args = args.concat(Cpp.collectIncludePathsArguments(input));
+ args = args.concat(Cpp.collectSystemIncludePathsArguments(input));
var minimumWindowsVersion = input.cpp.minimumWindowsVersion;
- if (minimumWindowsVersion && product.qbs.targetOS.contains("windows")) {
+ if (minimumWindowsVersion && product.qbs.targetOS.includes("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);
+ args.push(input.cpp.defineFlag + versionDefs[i] + '=' + hexVersion);
}
}
@@ -914,11 +906,11 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) {
switch (tag) {
case "c":
case "objc":
- var knownValues = ["c11", "c99", "c90", "c89"];
+ var knownValues = ["c2x", "c17", "c11", "c99", "c90", "c89"];
return Cpp.languageVersion(input.cpp.cLanguageVersion, knownValues, "C");
case "cpp":
case "objcpp":
- knownValues = ["c++20", "c++2a", "c++17", "c++1z",
+ knownValues = ["c++23", "c++2b", "c++20", "c++2a", "c++17", "c++1z",
"c++14", "c++1y", "c++11", "c++0x",
"c++03", "c++98"];
return Cpp.languageVersion(input.cpp.cxxLanguageVersion, knownValues, "C++");
@@ -935,7 +927,7 @@ function compilerFlags(project, product, input, output, explicitlyDependsOn) {
if (tag === "cpp" || tag === "objcpp") {
var cxxStandardLibrary = product.cpp.cxxStandardLibrary;
- if (cxxStandardLibrary && product.qbs.toolchain.contains("clang")) {
+ if (cxxStandardLibrary && product.qbs.toolchain.includes("clang")) {
args.push("-stdlib=" + cxxStandardLibrary);
}
}
@@ -950,7 +942,7 @@ function additionalCompilerAndLinkerFlags(product) {
var args = []
var requireAppExtensionSafeApi = product.cpp.requireAppExtensionSafeApi;
- if (requireAppExtensionSafeApi !== undefined && product.qbs.targetOS.contains("darwin")) {
+ if (requireAppExtensionSafeApi !== undefined && product.qbs.targetOS.includes("darwin")) {
args.push(requireAppExtensionSafeApi ? "-fapplication-extension" : "-fno-application-extension");
}
@@ -976,10 +968,6 @@ function languageName(fileTag) {
function prepareAssembler(project, product, inputs, outputs, input, output) {
var assemblerPath = product.cpp.assemblerPath;
- var includePaths = input.cpp.includePaths;
- var systemIncludePaths = input.cpp.systemIncludePaths;
- var distributionIncludePaths = input.cpp.distributionIncludePaths;
-
var args = product.cpp.targetAssemblerFlags;
if (input.cpp.debugInformation)
@@ -989,18 +977,9 @@ function prepareAssembler(project, product, inputs, outputs, input, output) {
if (warnings === 'none')
args.push('-W');
- var tag = "asm";
- 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);
- if (distributionIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(distributionIncludePaths);
- args = args.concat(allIncludePaths.map(function(path) { return input.cpp.includeFlag + path }));
+ args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm"));
+ args = args.concat(Cpp.collectIncludePathsArguments(input));
+ args = args.concat(Cpp.collectSystemIncludePathsArguments(input, input.cpp.includeFlag));
args.push("-o", output.filePath);
args.push(input.filePath);
@@ -1014,7 +993,7 @@ function prepareAssembler(project, product, inputs, outputs, input, output) {
function compilerEnvVars(config, compilerInfo)
{
- if (config.qbs.toolchain.contains("qcc"))
+ if (config.qbs.toolchain.includes("qcc"))
return ["QCC_CONF_PATH"];
var list = ["CPATH", "TMPDIR"];
@@ -1026,15 +1005,15 @@ function compilerEnvVars(config, compilerInfo)
list.push("OBJC_INCLUDE_PATH");
else if (compilerInfo.tag === "objccpp")
list.push("OBJCPLUS_INCLUDE_PATH");
- if (config.qbs.targetOS.contains("macos"))
+ if (config.qbs.targetOS.includes("macos"))
list.push("MACOSX_DEPLOYMENT_TARGET");
- else if (config.qbs.targetOS.contains("ios"))
+ else if (config.qbs.targetOS.includes("ios"))
list.push("IPHONEOS_DEPLOYMENT_TARGET");
- else if (config.qbs.targetOS.contains("tvos"))
+ else if (config.qbs.targetOS.includes("tvos"))
list.push("TVOS_DEPLOYMENT_TARGET");
- else if (config.qbs.targetOS.contains("watchos"))
+ else if (config.qbs.targetOS.includes("watchos"))
list.push("WATCHOS_DEPLOYMENT_TARGET");
- if (config.qbs.toolchain.contains("clang")) {
+ if (config.qbs.toolchain.includes("clang")) {
list.push("TEMP", "TMP");
} else {
list.push("LANG", "LC_CTYPE", "LC_MESSAGES", "LC_ALL", "GCC_COMPARE_DEBUG",
@@ -1045,7 +1024,7 @@ function compilerEnvVars(config, compilerInfo)
function linkerEnvVars(config, inputs)
{
- if (config.qbs.toolchain.contains("clang") || config.qbs.toolchain.contains("qcc"))
+ if (config.qbs.toolchain.includes("clang") || config.qbs.toolchain.includes("qcc"))
return [];
var list = ["GNUTARGET", "LDEMULATION", "COLLECT_NO_DEMANGLE"];
if (useCompilerDriverLinker(config, inputs))
@@ -1055,7 +1034,7 @@ function linkerEnvVars(config, inputs)
function setResponseFileThreshold(command, product)
{
- if (product.qbs.targetOS.contains("windows") && product.qbs.hostOS.contains("windows"))
+ if (product.qbs.targetOS.includes("windows") && Host.os().includes("windows"))
command.responseFileThreshold = 10000;
}
@@ -1063,7 +1042,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
var compilerInfo = effectiveCompilerInfo(product.qbs.toolchain,
input, output);
var compilerPath = compilerInfo.path;
- var pchOutput = output.fileTags.contains(compilerInfo.tag + "_pch");
+ var pchOutput = output.fileTags.includes(compilerInfo.tag + "_pch");
var args = compilerFlags(project, product, input, output, explicitlyDependsOn);
var wrapperArgsLength = 0;
@@ -1153,7 +1132,7 @@ function getSymbolInfo(product, inputFile)
// construct the list of defined symbols by subtracting.
var undefinedGlobalSymbols = collectStdoutLines(command, args.concat(["-u", inputFile]));
result.definedGlobalSymbols = result.allGlobalSymbols.filter(function(line) {
- return !undefinedGlobalSymbols.contains(line); });
+ return !undefinedGlobalSymbols.includes(line); });
result.success = true;
} catch (e) {
console.debug("Failed to collect symbols for shared library: nm command '"
@@ -1253,8 +1232,13 @@ function createSymbolCheckingCommands(product, outputs) {
oldSymbols = oldNmResult.allGlobalSymbols;
newSymbols = newNmResult.allGlobalSymbols;
} else {
- oldSymbols = oldNmResult.definedGlobalSymbols;
- newSymbols = newNmResult.definedGlobalSymbols;
+ var weakFilter = function(line) {
+ var symbolType = line.split(/\s+/)[1];
+ return symbolType != "v" && symbolType != "V"
+ && symbolType != "w" && symbolType != "W";
+ };
+ oldSymbols = oldNmResult.definedGlobalSymbols.filter(weakFilter);
+ newSymbols = newNmResult.definedGlobalSymbols.filter(weakFilter);
}
if (oldSymbols.length !== newSymbols.length) {
console.debug("List of relevant symbols differs for '" + libFilePath + "'.");
@@ -1281,6 +1265,59 @@ function createSymbolCheckingCommands(product, outputs) {
return commands;
}
+function separateDebugInfoCommands(product, outputs, primaryOutput) {
+ var commands = [];
+
+ var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll
+ || outputs.debuginfo_loadablemodule;
+
+ if (debugInfo) {
+ var objcopy = product.cpp.objcopyPath;
+
+ var cmd = new Command(objcopy, ["--only-keep-debug", primaryOutput.filePath,
+ 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=" + debugInfo[0].filePath,
+ primaryOutput.filePath]);
+ cmd.silent = true;
+ commands.push(cmd);
+ }
+
+ return commands;
+}
+
+function separateDebugInfoCommandsDarwin(product, outputs, primaryOutputs) {
+ var commands = [];
+
+ var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll
+ || outputs.debuginfo_loadablemodule;
+ if (debugInfo) {
+ var dsymPath = debugInfo[0].filePath;
+ if (outputs.debuginfo_bundle && outputs.debuginfo_bundle[0])
+ dsymPath = outputs.debuginfo_bundle[0].filePath;
+
+ var flags = product.cpp.dsymutilFlags || [];
+ var files = primaryOutputs.map(function (f) { return f.filePath; });
+ var cmd = new Command(product.cpp.dsymutilPath,
+ flags.concat(["-o", dsymPath].concat(files)));
+ cmd.description = "generating dSYM for " + product.name;
+ commands.push(cmd);
+
+ // strip debug info
+ cmd = new Command(product.cpp.stripPath, ["-S"].concat(files));
+ cmd.silent = true;
+ commands.push(cmd);
+ }
+
+ return commands;
+}
+
function prepareLinker(project, product, inputs, outputs, input, output) {
var i, primaryOutput, cmd, commands = [];
@@ -1307,7 +1344,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
var responseFileArgumentIndex = wrapperArgsLength;
// qcc doesn't properly handle response files, so we have to do it manually
- var useQnxResponseFileHack = product.qbs.toolchain.contains("qcc")
+ var useQnxResponseFileHack = product.qbs.toolchain.includes("qcc")
&& useCompilerDriverLinker(product, inputs);
if (useQnxResponseFileHack) {
// qcc needs to see at least one object/library file to think it has something to do,
@@ -1327,43 +1364,13 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
setResponseFileThreshold(cmd, product);
commands.push(cmd);
- var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll
- || outputs.debuginfo_loadablemodule;
- if (debugInfo) {
- if (product.qbs.targetOS.contains("darwin")) {
- if (!product.aggregate) {
- var dsymPath = debugInfo[0].filePath;
- if (outputs.debuginfo_bundle && outputs.debuginfo_bundle[0])
- dsymPath = outputs.debuginfo_bundle[0].filePath;
- var flags = product.cpp.dsymutilFlags || [];
- cmd = new Command(product.cpp.dsymutilPath, flags.concat([
- "-o", dsymPath, primaryOutput.filePath
- ]));
- cmd.description = "generating dSYM for " + product.name;
- commands.push(cmd);
-
- cmd = new Command(product.cpp.stripPath,
- ["-S", primaryOutput.filePath]);
- cmd.silent = true;
- commands.push(cmd);
- }
- } else {
- var objcopy = product.cpp.objcopyPath;
-
- cmd = new Command(objcopy, ["--only-keep-debug", primaryOutput.filePath,
- 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=" + debugInfo[0].filePath,
- primaryOutput.filePath]);
- cmd.silent = true;
- commands.push(cmd);
+ if (product.qbs.targetOS.includes("darwin")) {
+ if (!product.aggregate) {
+ commands = commands.concat(separateDebugInfoCommandsDarwin(
+ product, outputs, [primaryOutput]));
}
+ } else {
+ commands = commands.concat(separateDebugInfoCommands(product, outputs, primaryOutput));
}
if (outputs.dynamiclibrary) {
@@ -1383,27 +1390,9 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
}
}
- if (product.xcode && product.bundle) {
- var actualSigningIdentity = product.xcode.actualSigningIdentity;
- var codesignDisplayName = product.xcode.actualSigningIdentityDisplayName;
- if (actualSigningIdentity && !product.bundle.isBundle) {
- args = product.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.xcode.codesignPath, args);
- cmd.description = "codesign "
- + primaryOutput.fileName
- + " using " + codesignDisplayName
- + " (" + actualSigningIdentity + ")";
- commands.push(cmd);
- }
+ if (product.cpp.shouldSignArtifacts) {
+ Array.prototype.push.apply(
+ commands, Codesign.prepareSign(project, product, inputs, outputs, input, output));
}
return commands;
@@ -1462,12 +1451,7 @@ function dumpMacros(env, compilerFilePath, args, nullDevice, tag) {
p.exec(compilerFilePath,
(args || []).concat(["-Wp,-dM", "-E", "-x", languageName(tag || "c") , nullDevice]),
true);
- var map = {};
- p.readStdOut().trim().split(/\r?\n/g).map(function (line) {
- var parts = line.split(" ", 3);
- map[parts[1]] = parts[2];
- });
- return map;
+ return Cpp.extractMacros(p.readStdOut());
} finally {
p.close();
}
@@ -1514,7 +1498,7 @@ function dumpDefaultPaths(env, compilerFilePath, args, nullDevice, pathListSepar
if (libraryPaths.length === 0)
libraryPaths.push(sysroot + "/lib", sysroot + "/usr/lib");
- if (frameworkPaths.length === 0 && targetOS.contains("darwin"))
+ if (frameworkPaths.length === 0 && targetOS.includes("darwin"))
frameworkPaths.push(sysroot + "/System/Library/Frameworks");
return {
@@ -1527,6 +1511,28 @@ function dumpDefaultPaths(env, compilerFilePath, args, nullDevice, pathListSepar
}
}
+function targetLinkerFlags(targetArch, targetOS) {
+ var linkerFlags = {
+ "windows": {
+ "i386": "i386pe",
+ "x86_64": "i386pep",
+ },
+ "freebsd": {
+ "i386": "elf_i386_fbsd",
+ "x86_64": "elf_x86_64_fbsd",
+ },
+ "other": {
+ "i386": "elf_i386",
+ "x86_64": "elf_x86_64",
+ }
+ };
+ if (targetOS.includes("windows"))
+ return linkerFlags["windows"][targetArch];
+ else if (targetOS.includes("freebsd"))
+ return linkerFlags["freebsd"][targetArch];
+ return linkerFlags["other"][targetArch];
+}
+
function targetFlags(tool, hasTargetOption, target, targetArch, machineType, targetOS) {
var args = [];
if (hasTargetOption) {
@@ -1539,8 +1545,8 @@ function targetFlags(tool, hasTargetOption, target, targetArch, machineType, tar
"x86_64": ["-m64"],
},
"linker": {
- "i386": ["-m", targetOS.contains("windows") ? "i386pe" : "elf_i386"],
- "x86_64": ["-m", targetOS.contains("windows") ? "i386pep" : "elf_x86_64"],
+ "i386": ["-m", targetLinkerFlags("i386", targetOS)],
+ "x86_64": ["-m", targetLinkerFlags("x86_64", targetOS)],
},
"assembler": {
"i386": ["--32"],
diff --git a/share/qbs/modules/cpp/iar.js b/share/qbs/modules/cpp/iar.js
index d51668095..ed58ac262 100644
--- a/share/qbs/modules/cpp/iar.js
+++ b/share/qbs/modules/cpp/iar.js
@@ -37,155 +37,342 @@ var Process = require("qbs.Process");
var TemporaryDir = require("qbs.TemporaryDir");
var TextFile = require("qbs.TextFile");
-function compilerName(qbs) {
- switch (qbs.architecture) {
- case "arm":
- return "iccarm";
- case "mcs51":
- return "icc8051";
- case "avr":
- return "iccavr";
- case "stm8":
- return "iccstm8";
- case "msp430":
- return "icc430";
- case "rl78":
- return "iccrl78";
- }
- throw "Unable to deduce compiler name for unsupported architecture: '"
- + qbs.architecture + "'";
+function supportXLinker(architecture) {
+ return architecture === "78k"
+ || architecture === "avr"
+ || architecture === "avr32"
+ || architecture === "cr16"
+ || architecture === "hcs12"
+ || architecture === "hcs8"
+ || architecture === "m16c"
+ || architecture === "m32c"
+ || architecture === "m68k"
+ || architecture === "mcs51"
+ || architecture === "msp430"
+ || architecture === "r32c"
+ || architecture === "v850";
}
-function assemblerName(qbs) {
- switch (qbs.architecture) {
- case "arm":
- return "iasmarm";
- case "rl78":
- return "iasmrl78";
- case "mcs51":
- return "a8051";
- case "avr":
- return "aavr";
- case "stm8":
- return "iasmstm8";
- case "msp430":
- return "a430";
- }
- throw "Unable to deduce assembler name for unsupported architecture: '"
- + qbs.architecture + "'";
+function supportILinker(architecture) {
+ return architecture.startsWith("arm")
+ || architecture === "rh850"
+ || architecture === "riscv"
+ || architecture === "rl78"
+ || architecture === "rx"
+ || architecture === "sh"
+ || architecture === "stm8";
}
-function linkerName(qbs) {
- switch (qbs.architecture) {
- case "arm":
- return "ilinkarm";
- case "stm8":
- return "ilinkstm8";
- case "rl78":
- return "ilinkrl78";
- case "mcs51":
- case "avr":
- case "msp430":
- return "xlink";
- }
- throw "Unable to deduce linker name for unsupported architecture: '"
- + qbs.architecture + "'";
+function supportXArchiver(architecture) {
+ return architecture === "78k"
+ || architecture === "avr"
+ || architecture === "avr32"
+ || architecture === "cr16"
+ || architecture === "hcs12"
+ || architecture === "hcs8"
+ || architecture === "m16c"
+ || architecture === "m32c"
+ || architecture === "m68k"
+ || architecture === "mcs51"
+ || architecture === "msp430"
+ || architecture === "r32c"
+ || architecture === "v850";
}
-function archiverName(qbs) {
- switch (qbs.architecture) {
- case "arm":
- case "stm8":
- case "rl78":
- return "iarchive";
- case "mcs51":
- case "avr":
- case "msp430":
- return "xlib";
- }
- throw "Unable to deduce archiver name for unsupported architecture: '"
- + qbs.architecture + "'";
+function supportIArchiver(architecture) {
+ return architecture.startsWith("arm")
+ || architecture === "rh850"
+ || architecture === "riscv"
+ || architecture === "rl78"
+ || architecture === "rx"
+ || architecture === "sh"
+ || architecture === "stm8";
}
-function staticLibrarySuffix(qbs) {
- switch (qbs.architecture) {
- case "arm":
- case "stm8":
- case "rl78":
- return ".a";
- case "mcs51":
- return ".r51";
- case "avr":
- return ".r90";
- case "msp430":
- return ".r43";
- }
- throw "Unable to deduce static library suffix for unsupported architecture: '"
- + qbs.architecture + "'";
+function supportXAssembler(architecture) {
+ return architecture.startsWith("arm")
+ || architecture === "78k"
+ || architecture === "avr"
+ || architecture === "hcs12"
+ || architecture === "m16c"
+ || architecture === "mcs51"
+ || architecture === "msp430"
+ || architecture === "m32c"
+ || architecture === "v850";
}
-function executableSuffix(qbs) {
- switch (qbs.architecture) {
- case "arm":
- case "stm8":
- case "rl78":
- return ".out";
- case "mcs51":
- return qbs.debugInformation ? ".d51" : ".a51";
- case "avr":
- return qbs.debugInformation ? ".d90" : ".a90";
- case "msp430":
- return qbs.debugInformation ? ".d43" : ".a43";
- }
- throw "Unable to deduce executable suffix for unsupported architecture: '"
- + qbs.architecture + "'";
+function supportIAssembler(architecture) {
+ return architecture === "avr32"
+ || architecture === "cr16"
+ || architecture === "hcs8"
+ || architecture === "r32c"
+ || architecture === "rh850"
+ || architecture === "riscv"
+ || architecture === "rl78"
+ || architecture === "rx"
+ || architecture === "sh"
+ || architecture === "stm8"
+ || architecture === "m68k";
}
-function objectSuffix(qbs) {
- switch (qbs.architecture) {
- case "arm":
- case "stm8":
- case "rl78":
- return ".o";
- case "mcs51":
- return ".r51";
+function supportEndianness(architecture) {
+ return architecture.startsWith("arm")
+ || architecture === "rx";
+}
+
+function supportCppExceptions(architecture) {
+ return architecture.startsWith("arm")
+ || architecture === "rh850"
+ || architecture === "riscv"
+ || architecture === "rl78"
+ || architecture === "rx";
+}
+
+function supportCppRtti(architecture) {
+ return architecture.startsWith("arm")
+ || architecture === "rh850"
+ || architecture === "riscv"
+ || architecture === "rl78"
+ || architecture === "rx";
+}
+
+function supportCppWarningAboutCStyleCast(architecture) {
+ return architecture.startsWith("arm")
+ || architecture === "avr"
+ || architecture === "avr32"
+ || architecture === "cr16"
+ || architecture === "mcs51"
+ || architecture === "msp430"
+ || architecture === "rh850"
+ || architecture === "rl78"
+ || architecture === "rx"
+ || architecture === "stm8"
+ || architecture === "v850";
+}
+
+function supportDeprecatedFeatureWarnings(architecture) {
+ return architecture.startsWith("arm")
+ || architecture === "avr"
+ || architecture === "cr16"
+ || architecture === "mcs51"
+ || architecture === "msp430"
+ || architecture === "rh850"
+ || architecture === "rl78"
+ || architecture === "rx"
+ || architecture === "stm8"
+ || architecture === "v850";
+}
+
+function supportCLanguageVersion(architecture) {
+ return architecture !== "78k";
+}
+
+function supportCppLanguage(compilerFilePath) {
+ var baseName = FileInfo.baseName(compilerFilePath);
+ return baseName !== "iccs08";
+}
+
+// It is a 'magic' IAR-specific target architecture code.
+function architectureCode(architecture) {
+ switch (architecture) {
+ case "78k":
+ return "26";
case "avr":
- return ".r90";
+ return "90";
+ case "avr32":
+ return "82";
+ case "mcs51":
+ return "51";
case "msp430":
- return ".r43";
+ return "43";
+ case "v850":
+ return "85";
+ case "m68k":
+ return "68";
+ case "m32c":
+ return "48";
+ case "r32c":
+ return "53";
+ case "m16c":
+ return "34";
+ case "cr16":
+ return "45";
+ case "hcs12":
+ return "12";
+ case "hcs8":
+ return "78";
+ case "rh850": case "riscv": case "rl78": case "rx": case "sh": case "stm8":
+ return "";
+ default:
+ if (architecture.startsWith("arm"))
+ return "";
+ break;
}
- throw "Unable to deduce object file suffix for unsupported architecture: '"
- + qbs.architecture + "'";
}
-function imageFormat(qbs) {
- switch (qbs.architecture) {
- case "arm":
- case "stm8":
- case "rl78":
- return "elf";
- case "mcs51":
- case "avr":
- case "msp430":
- return "ubrof";
+function toolchainDetails(qbs) {
+ var architecture = qbs.architecture;
+ var code = architectureCode(architecture);
+ var details = {};
+
+ if (supportXLinker(architecture)) {
+ details.libraryPathFlag = "-I";
+ details.linkerScriptFlag = "-f";
+ details.linkerName = "xlink";
+ details.linkerSilentFlag = "-S";
+ details.linkerMapFileFlag = "-l";
+ details.linkerEntryPointFlag = "-s";
+ } else if (supportILinker(architecture)) {
+ details.libraryPathFlag = "-L";
+ details.linkerScriptFlag = "--config";
+ details.linkerSilentFlag = "--silent";
+ details.linkerMapFileFlag = "--map";
+ details.linkerEntryPointFlag = "--entry";
+ details.linkerName = architecture.startsWith("arm")
+ ? "ilinkarm" : ("ilink" + architecture);
+ }
+
+ if (supportXArchiver(architecture))
+ details.archiverName = "xar";
+ else if (supportIArchiver(architecture))
+ details.archiverName = "iarchive";
+
+ var hasCode = (code !== "");
+ details.staticLibrarySuffix = hasCode ? (".r" + code) : ".a";
+ details.executableSuffix = hasCode
+ ? ((qbs.debugInformation) ? (".d" + code) : (".a" + code)) : ".out";
+ details.objectSuffix = hasCode ? (".r" + code) : ".o";
+ details.imageFormat = hasCode ? "ubrof" : "elf";
+
+ if (architecture.startsWith("arm")) {
+ details.compilerName = "iccarm";
+ details.assemblerName = "iasmarm";
+ } else if (architecture === "78k") {
+ details.compilerName = "icc78k";
+ details.assemblerName = "a78k";
+ } else if (architecture === "avr") {
+ details.compilerName = "iccavr";
+ details.assemblerName = "aavr";
+ } else if (architecture === "avr32") {
+ details.compilerName = "iccavr32";
+ details.assemblerName = "aavr32";
+ } else if (architecture === "cr16") {
+ details.compilerName = "icccr16c";
+ details.assemblerName = "acr16c";
+ } else if (architecture === "hcs12") {
+ details.compilerName = "icchcs12";
+ details.assemblerName = "ahcs12";
+ } else if (architecture === "hcs8") {
+ details.compilerName = "iccs08";
+ details.assemblerName = "as08";
+ } else if (architecture === "m16c") {
+ details.compilerName = "iccm16c";
+ details.assemblerName = "am16c";
+ } else if (architecture === "m32c") {
+ details.compilerName = "iccm32c";
+ details.assemblerName = "am32c";
+ } else if (architecture === "m68k") {
+ details.compilerName = "icccf";
+ details.assemblerName = "acf";
+ } else if (architecture === "mcs51") {
+ details.compilerName = "icc8051";
+ details.assemblerName = "a8051";
+ } else if (architecture === "msp430") {
+ details.compilerName = "icc430";
+ details.assemblerName = "a430";
+ } else if (architecture === "r32c") {
+ details.compilerName = "iccr32c";
+ details.assemblerName = "ar32c";
+ } else if (architecture === "rh850") {
+ details.compilerName = "iccrh850";
+ details.assemblerName = "iasmrh850";
+ } else if (architecture === "riscv") {
+ details.compilerName = "iccriscv";
+ details.assemblerName = "iasmriscv";
+ } else if (architecture === "rl78") {
+ details.compilerName = "iccrl78";
+ details.assemblerName = "iasmrl78";
+ } else if (architecture === "rx") {
+ details.compilerName = "iccrx";
+ details.assemblerName = "iasmrx";
+ } else if (architecture === "sh") {
+ details.compilerName = "iccsh";
+ details.assemblerName = "iasmsh";
+ } else if (architecture === "stm8") {
+ details.compilerName = "iccstm8";
+ details.assemblerName = "iasmstm8";
+ } else if (architecture === "v850") {
+ details.compilerName = "iccv850";
+ details.assemblerName = "av850";
}
- throw "Unable to deduce image format for unsupported architecture: '"
- + qbs.architecture + "'";
+
+ return details;
+}
+
+function guessArmArchitecture(core) {
+ var arch = "arm";
+ if (core === "__ARM4M__")
+ arch += "v4m";
+ else if (core === "__ARM4TM__")
+ arch += "v4tm";
+ else if (core === "__ARM5E__")
+ arch += "v5e";
+ else if (core === "__ARM5__")
+ arch += "v5";
+ else if (core === "__ARM6M__")
+ arch += "v6m";
+ else if (core === "__ARM6SM__")
+ arch += "v6sm";
+ else if (core === "__ARM6__")
+ arch += "v6";
+ else if (core === "__ARM7M__")
+ arch += "v7m";
+ else if (core === "__ARM7R__")
+ arch += "v7r";
+ return arch;
}
function guessArchitecture(macros) {
- if (macros["__ICCARM__"] === "1")
- return "arm";
+ if (macros["__ICC430__"] === "1")
+ return "msp430";
+ else if (macros["__ICC78K__"] === "1")
+ return "78k";
else if (macros["__ICC8051__"] === "1")
return "mcs51";
+ else if (macros["__ICCARM__"] === "1")
+ return guessArmArchitecture(macros["__CORE__"]);
+ else if (macros["__ICCAVR32__"] === "1")
+ return "avr32";
else if (macros["__ICCAVR__"] === "1")
return "avr";
- else if (macros["__ICCSTM8__"] === "1")
- return "stm8";
- else if (macros["__ICC430__"] === "1")
- return "msp430";
+ else if (macros["__ICCCF__"] === "1")
+ return "m68k";
+ else if (macros["__ICCCR16C__"] === "1")
+ return "cr16";
+ else if (macros["__ICCHCS12__"] === "1")
+ return "hcs12";
+ else if (macros["__ICCM16C__"] === "1")
+ return "m16c";
+ else if (macros["__ICCM32C__"] === "1")
+ return "m32c";
+ else if (macros["__ICCR32C__"] === "1")
+ return "r32c";
+ else if (macros["__ICCRH850__"] === "1")
+ return "rh850";
+ else if (macros["__ICCRISCV__"] === "1")
+ return "riscv";
else if (macros["__ICCRL78__"] === "1")
return "rl78";
+ else if (macros["__ICCRX__"] === "1")
+ return "rx";
+ else if (macros["__ICCS08__"] === "1")
+ return "hcs8";
+ else if (macros["__ICCSH__"] === "1")
+ return "sh";
+ else if (macros["__ICCSTM8__"] === "1")
+ return "stm8";
+ else if (macros["__ICCV850__"] === "1")
+ return "v850";
}
function guessEndianness(macros) {
@@ -194,24 +381,34 @@ function guessEndianness(macros) {
return "big"
}
-function guessVersion(macros, architecture)
-{
+function guessVersion(macros, architecture) {
var version = parseInt(macros["__VER__"], 10);
- switch (architecture) {
- case "arm":
+ if (architecture.startsWith("arm")) {
return { major: parseInt(version / 1000000),
minor: parseInt(version / 1000) % 1000,
- patch: parseInt(version) % 1000,
- found: true }
- case "mcs51":
- case "avr":
- case "stm8":
- case "msp430":
- case "rl78":
+ patch: parseInt(version) % 1000 }
+ } else if (architecture === "78k"
+ || architecture === "avr"
+ || architecture === "avr32"
+ || architecture === "cr16"
+ || architecture === "hcs12"
+ || architecture === "hcs8"
+ || architecture === "m16c"
+ || architecture === "m32c"
+ || architecture === "m68k"
+ || architecture === "mcs51"
+ || architecture === "msp430"
+ || architecture === "r32c"
+ || architecture === "rh850"
+ || architecture === "riscv"
+ || architecture === "rl78"
+ || architecture === "rx"
+ || architecture === "sh"
+ || architecture === "stm8"
+ || architecture === "v850") {
return { major: parseInt(version / 100),
minor: parseInt(version % 100),
- patch: 0,
- found: true }
+ patch: 0 }
}
}
@@ -219,16 +416,27 @@ function cppLanguageOption(compilerFilePath) {
var baseName = FileInfo.baseName(compilerFilePath);
switch (baseName) {
case "iccarm":
- case "rl78":
+ case "iccrh850":
+ case "iccriscv":
+ case "iccrl78":
+ case "iccrx":
return "--c++";
+ case "icc430":
+ case "icc78k":
case "icc8051":
case "iccavr":
+ case "iccavr32":
+ case "icccf":
+ case "icccr16c":
+ case "icchcs12":
+ case "iccm16c":
+ case "iccm32c":
+ case "iccr32c":
+ case "iccsh":
case "iccstm8":
- case "icc430":
+ case "iccv850":
return "--ec++";
}
- throw "Unable to deduce C++ language option for unsupported compiler: '"
- + FileInfo.toNativeSeparators(compilerFilePath) + "'";
}
function dumpMacros(compilerFilePath, tag) {
@@ -238,169 +446,65 @@ function dumpMacros(compilerFilePath, tag) {
var outFilePath = FileInfo.fromNativeSeparators(tempDir.path() + "/iar-macros.predef");
var args = [ inFilePath, "--predef_macros", outFilePath ];
- if (tag && tag === "cpp")
+ if (tag === "cpp" && supportCppLanguage(compilerFilePath))
args.push(cppLanguageOption(compilerFilePath));
var p = new Process();
+ p.setWorkingDirectory(tempDir.path());
p.exec(compilerFilePath, args, true);
var outFile = new TextFile(outFilePath, TextFile.ReadOnly);
- var map = {};
- outFile.readAll().trim().split(/\r?\n/g).map(function (line) {
- var parts = line.split(" ", 3);
- map[parts[1]] = parts[2];
- });
- return map;
+ return Cpp.extractMacros(outFile.readAll());
}
-function dumpDefaultPaths(compilerFilePath, tag) {
+function dumpCompilerIncludePaths(compilerFilePath, tag) {
+ // We can dump the compiler include paths using the undocumented `--IDE3` flag,
+ // e.g. which also is used in the IAR extension for the VSCode. In this case the
+ // compiler procuces the console output in the following format:
+ // `$$TOOL_BEGIN $$VERSION "3" $$INC_BEGIN $$FILEPATH "<path\\to\\directory>" $$TOOL_END`
+
var tempDir = new TemporaryDir();
var inFilePath = FileInfo.fromNativeSeparators(tempDir.path() + "/empty-source.c");
var inFile = new TextFile(inFilePath, TextFile.WriteOnly);
- var args = [ inFilePath, "--preinclude", "." ];
- if (tag === "cpp")
+ var args = ["--IDE3", inFilePath];
+ if (tag === "cpp" && supportCppLanguage(compilerFilePath))
args.push(cppLanguageOption(compilerFilePath));
+ var includePaths = [];
var p = new Process();
- // This process should return an error, don't throw
- // an error in this case.
+ p.setWorkingDirectory(tempDir.path());
+ // It is possible that the process can return an error code in case the
+ // compiler does not support the `--IDE3` flag. So, don't throw an error in this case.
p.exec(compilerFilePath, args, false);
- var output = p.readStdErr();
-
- var includePaths = [];
- var pass = 0;
- for (var pos = 0; pos < output.length; ++pos) {
- var searchIndex = output.indexOf("searched:", pos);
- if (searchIndex === -1)
- break;
- var startQuoteIndex = output.indexOf('"', searchIndex + 1);
- if (startQuoteIndex === -1)
- break;
- var endQuoteIndex = output.indexOf('"', startQuoteIndex + 1);
- if (endQuoteIndex === -1)
- break;
- pos = endQuoteIndex + 1;
-
- // Ignore the first path as it is not a compiler include path.
- ++pass;
- if (pass === 1)
- continue;
-
- var parts = output.substring(startQuoteIndex + 1, endQuoteIndex).split("\n");
- var includePath = "";
- for (var i in parts)
- includePath += parts[i].trim();
-
- includePaths.push(includePath);
- }
-
- return {
- "includePaths": includePaths
- };
-}
-
-function collectLibraryDependencies(product) {
- var seen = {};
- var result = [];
-
- function addFilePath(filePath) {
- result.push({ filePath: filePath });
- }
-
- function addArtifactFilePaths(dep, artifacts) {
- if (!artifacts)
- return;
- var artifactFilePaths = artifacts.map(function(a) { return a.filePath; });
- artifactFilePaths.forEach(addFilePath);
- }
-
- function addExternalStaticLibs(obj) {
- if (!obj.cpp)
- return;
- function ensureArray(a) {
- return Array.isArray(a) ? a : [];
- }
- function sanitizedModuleListProperty(obj, moduleName, propertyName) {
- return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName));
- }
- var externalLibs = [].concat(
- sanitizedModuleListProperty(obj, "cpp", "staticLibraries"));
- var staticLibrarySuffix = obj.moduleProperty("cpp", "staticLibrarySuffix");
- externalLibs.forEach(function(staticLibraryName) {
- if (!staticLibraryName.endsWith(staticLibrarySuffix))
- staticLibraryName += staticLibrarySuffix;
- addFilePath(staticLibraryName);
- });
- }
-
- function traverse(dep) {
- if (seen.hasOwnProperty(dep.name))
- return;
- seen[dep.name] = true;
-
- if (dep.parameters.cpp && dep.parameters.cpp.link === false)
- return;
-
- var staticLibraryArtifacts = dep.artifacts["staticlibrary"];
- if (staticLibraryArtifacts) {
- dep.dependencies.forEach(traverse);
- addArtifactFilePaths(dep, staticLibraryArtifacts);
- addExternalStaticLibs(dep);
+ p.readStdOut().trim().split(/\r?\n/g).map(function(line) {
+ var m = line.match(/\$\$INC_BEGIN\s\$\$FILEPATH\s\"([^"]*)/);
+ if (m) {
+ var includePath = m[1].replace(/\\\\/g, '/');
+ if (includePath && File.exists(includePath))
+ includePaths.push(includePath);
}
- }
-
- product.dependencies.forEach(traverse);
- addExternalStaticLibs(product);
- return result;
-}
-
-function compilerOutputArtifacts(input, useListing) {
- var artifacts = [];
- artifacts.push({
- fileTags: ["obj"],
- filePath: Utilities.getHash(input.baseDir) + "/"
- + input.fileName + input.cpp.objectSuffix
});
- if (useListing) {
- artifacts.push({
- fileTags: ["lst"],
- filePath: Utilities.getHash(input.baseDir) + "/"
- + input.fileName + ".lst"
- });
+
+ if (includePaths.length === 0) {
+ // This can happen if the compiler does not support the `--IDE3` flag,
+ // e.g. IAR for S08 architecture. In this case we use fallback to the
+ // detection of the `inc` directory.
+ var includePath = FileInfo.joinPaths(FileInfo.path(compilerFilePath), "../inc/");
+ if (File.exists(includePath))
+ includePaths.push(includePath);
}
- return artifacts;
-}
-function applicationLinkerOutputArtifacts(product) {
- var app = {
- fileTags: ["application"],
- filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- PathTools.applicationFilePath(product))
- };
- var mem_map = {
- fileTags: ["mem_map"],
- filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- product.targetName + ".map")
- };
- return [app, mem_map]
+ return includePaths;
}
-function staticLibraryLinkerOutputArtifacts(product) {
- var staticLib = {
- fileTags: ["staticlibrary"],
- filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- PathTools.staticLibraryFilePath(product))
+function dumpDefaultPaths(compilerFilePath, tag) {
+ var includePaths = dumpCompilerIncludePaths(compilerFilePath, tag);
+ return {
+ "includePaths": includePaths
};
- return [staticLib]
}
function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
- // Determine which C-language we're compiling.
- var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
-
var args = [];
// Input.
@@ -409,28 +513,15 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
// Output.
args.push("-o", outputs.obj[0].filePath);
+ // Preinclude headers.
+ args = args.concat(Cpp.collectPreincludePathsArguments(input, true));
+
// Defines.
- var allDefines = [];
- var platformDefines = input.cpp.platformDefines;
- if (platformDefines)
- allDefines = allDefines.uniqueConcat(platformDefines);
- var defines = input.cpp.defines;
- if (defines)
- allDefines = allDefines.uniqueConcat(defines);
- args = args.concat(allDefines.map(function(define) { return "-D" + define }));
+ args = args.concat(Cpp.collectDefinesArguments(input));
// Includes.
- var allIncludePaths = [];
- var includePaths = input.cpp.includePaths;
- if (includePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(includePaths);
- var systemIncludePaths = input.cpp.systemIncludePaths;
- if (systemIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths);
- var compilerIncludePaths = input.cpp.compilerIncludePaths;
- if (compilerIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths);
- args = args.concat(allIncludePaths.map(function(include) { return "-I" + include }));
+ args = args.concat(Cpp.collectIncludePathsArguments(input));
+ args = args.concat(Cpp.collectSystemIncludePathsArguments(input));
// Silent output generation flag.
args.push("--silent");
@@ -442,35 +533,40 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
// Optimization flags.
switch (input.cpp.optimization) {
case "small":
- args.push("-Ohs");
+ args.push("-Ohz");
break;
case "fast":
- args.push("-Ohz");
+ args.push("-Ohs");
break;
case "none":
args.push("-On");
break;
}
+ var architecture = input.qbs.architecture;
+ var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
+
// Warning level flags.
switch (input.cpp.warningLevel) {
case "none":
args.push("--no_warnings");
break;
case "all":
- args.push("--deprecated_feature_warnings="
- +"+attribute_syntax,"
- +"+preprocessor_extensions,"
- +"+segment_pragmas");
- if (tag === "cpp")
- args.push("--warn_about_c_style_casts");
+ if (supportDeprecatedFeatureWarnings(architecture)) {
+ args.push("--deprecated_feature_warnings="
+ +"+attribute_syntax,"
+ +"+preprocessor_extensions,"
+ +"+segment_pragmas");
+ }
+ if (tag === "cpp" && supportCppWarningAboutCStyleCast(architecture))
+ args.push("--warn_about_c_style_casts");
break;
}
if (input.cpp.treatWarningsAsErrors)
args.push("--warnings_are_errors");
// C language version flags.
- if (tag === "c") {
+ if (tag === "c" && supportCLanguageVersion(architecture)) {
var knownValues = ["c89"];
var cLanguageVersion = Cpp.languageVersion(
input.cpp.cLanguageVersion, knownValues, "C");
@@ -479,58 +575,46 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
args.push("--c89");
break;
default:
- // Default C language version is C11/C99 that
+ // Default C language version is C18/C11/C99 that
// depends on the IAR version.
break;
}
}
- // Architecture specific flags.
- switch (input.qbs.architecture) {
- case "arm":
- case "rl78":
- // Byte order flags.
- var endianness = input.cpp.endianness;
- if (endianness && input.qbs.architecture === "arm")
- args.push("--endian=" + endianness);
- if (tag === "cpp") {
- // Enable C++ language flags.
- args.push("--c++");
- // Exceptions flags.
- if (!input.cpp.enableExceptions)
- args.push("--no_exceptions");
- // RTTI flags.
- if (!input.cpp.enableRtti)
- args.push("--no_rtti");
- }
- break;
- case "stm8":
- case "mcs51":
- case "avr":
- case "msp430":
- // Enable C++ language flags.
- if (tag === "cpp")
- args.push("--ec++");
- break;
+ // C++ language version flags.
+ var compilerFilePath = input.cpp.compilerPath;
+ if (tag === "cpp" && supportCppLanguage(compilerFilePath)) {
+ // C++ language flag.
+ var cppOption = cppLanguageOption(compilerFilePath);
+ args.push(cppOption);
+
+ // Exceptions flag.
+ var enableExceptions = input.cpp.enableExceptions;
+ if (!enableExceptions && supportCppExceptions(architecture))
+ args.push("--no_exceptions");
+
+ // RTTI flag.
+ var enableRtti = input.cpp.enableRtti;
+ if (!enableRtti && supportCppRtti(architecture))
+ args.push("--no_rtti");
}
+ // Byte order flags.
+ var endianness = input.cpp.endianness;
+ if (endianness && supportEndianness(architecture))
+ args.push("--endian=" + endianness);
+
// Listing files generation flag.
- if (product.cpp.generateCompilerListingFiles)
+ if (input.cpp.generateCompilerListingFiles)
args.push("-l", outputs.lst[0].filePath);
// Misc flags.
- args = args.concat(ModUtils.moduleProperty(input, "platformFlags"),
- ModUtils.moduleProperty(input, "flags"),
- ModUtils.moduleProperty(input, "platformFlags", tag),
- ModUtils.moduleProperty(input, "flags", tag),
- ModUtils.moduleProperty(input, "driverFlags", tag));
+ args = args.concat(Cpp.collectMiscCompilerArguments(input, tag),
+ Cpp.collectMiscDriverArguments(product));
return args;
}
function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
- // Determine which C-language we"re compiling
- var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
-
var args = [];
// Input.
@@ -539,24 +623,23 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
// Output.
args.push("-o", outputs.obj[0].filePath);
+ var architecture = input.qbs.architecture;
+
+ // The `--preinclude` flag is only supported for a certain
+ // set of assemblers, not for all.
+ if (supportIAssembler(architecture))
+ args = args.concat(Cpp.collectPreincludePathsArguments(input));
+
// Includes.
- var allIncludePaths = [];
- var systemIncludePaths = input.cpp.systemIncludePaths;
- if (systemIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths);
- var compilerIncludePaths = input.cpp.compilerIncludePaths;
- if (compilerIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths);
- args = args.concat(allIncludePaths.map(function(include) { return "-I" + include }));
+ args = args.concat(Cpp.collectIncludePathsArguments(input));
+ args = args.concat(Cpp.collectSystemIncludePathsArguments(input));
// Debug information flags.
if (input.cpp.debugInformation)
args.push("-r");
// Architecture specific flags.
- switch (input.qbs.architecture) {
- case "stm8":
- case "rl78":
+ if (supportIAssembler(architecture)) {
// Silent output generation flag.
args.push("--silent");
// Warning level flags.
@@ -564,104 +647,79 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
args.push("--no_warnings");
if (input.cpp.treatWarningsAsErrors)
args.push("--warnings_are_errors");
- break;
- default:
+ } else if (supportXAssembler(architecture)){
// Silent output generation flag.
args.push("-S");
// Warning level flags.
args.push("-w" + (input.cpp.warningLevel === "none" ? "-" : "+"));
- break;
}
+ // Byte order flags.
+ var endianness = input.cpp.endianness;
+ if (endianness && supportEndianness(architecture))
+ args.push("--endian", endianness);
+
// Listing files generation flag.
- if (product.cpp.generateAssemblerListingFiles)
+ if (input.cpp.generateAssemblerListingFiles)
args.push("-l", outputs.lst[0].filePath);
// Misc flags.
- args = args.concat(ModUtils.moduleProperty(input, "platformFlags", tag),
- ModUtils.moduleProperty(input, "flags", tag),
- ModUtils.moduleProperty(input, "driverFlags", tag));
+ args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm"));
return args;
}
-function linkerFlags(project, product, input, outputs) {
+function linkerFlags(project, product, inputs, outputs) {
var args = [];
// Inputs.
- if (inputs.obj)
- args = args.concat(inputs.obj.map(function(obj) { return obj.filePath }));
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs));
// Output.
args.push("-o", outputs.application[0].filePath);
// Library paths.
- var allLibraryPaths = [];
- var libraryPaths = product.cpp.libraryPaths;
- if (libraryPaths)
- allLibraryPaths = allLibraryPaths.uniqueConcat(libraryPaths);
- var distributionLibraryPaths = product.cpp.distributionLibraryPaths;
- if (distributionLibraryPaths)
- allLibraryPaths = allLibraryPaths.uniqueConcat(distributionLibraryPaths);
- args = args.concat(allLibraryPaths.map(function(path) { return '-L' + path }));
+ args = args.concat(Cpp.collectLibraryPathsArguments(product));
// Library dependencies.
- var libraryDependencies = collectLibraryDependencies(product);
- if (libraryDependencies)
- args = args.concat(libraryDependencies.map(function(dep) { return dep.filePath }));
+ args = args.concat(Cpp.collectLibraryDependenciesArguments(product));
// Linker scripts.
- var linkerScripts = inputs.linkerscript
- ? inputs.linkerscript.map(function(a) { return a.filePath; }) : [];
+ args = args.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs, true));
- // Architecture specific flags.
- switch (product.qbs.architecture) {
- case "arm":
- case "stm8":
- case "rl78":
- // Silent output generation flag.
- args.push("--silent");
- // Map file generation flag.
- if (product.cpp.generateLinkerMapFile)
- args.push("--map", outputs.mem_map[0].filePath);
- // Entry point flag.
- if (product.cpp.entryPoint)
- args.push("--entry", product.cpp.entryPoint);
- // Linker scripts flags.
- linkerScripts.forEach(function(script) { args.push("--config", script); });
- break;
- case "mcs51":
- case "avr":
- case "msp430":
- // Silent output generation flag.
- args.push("-S");
- // Debug information flag.
+ // Silent output generation flag.
+ args.push(product.cpp.linkerSilentFlag);
+
+ // Map file generation flag.
+ if (product.cpp.generateLinkerMapFile)
+ args.push(product.cpp.linkerMapFileFlag, outputs.mem_map[0].filePath);
+
+ // Entry point flag.
+ if (product.cpp.entryPoint)
+ args.push(product.cpp.linkerEntryPointFlag, product.cpp.entryPoint);
+
+ // Debug information flag.
+ if (supportXLinker(product.qbs.architecture)) {
if (product.cpp.debugInformation)
args.push("-rt");
- // Map file generation flag.
- if (product.cpp.generateLinkerMapFile)
- args.push("-l", outputs.mem_map[0].filePath);
- // Entry point flag.
- if (product.cpp.entryPoint)
- args.push("-s", product.cpp.entryPoint);
- // Linker scripts flags.
- linkerScripts.forEach(function(script) { args.push("-f", script); });
- break;
}
// Misc flags.
- args = args.concat(ModUtils.moduleProperty(product, "driverLinkerFlags"));
+ args = args.concat(Cpp.collectMiscEscapableLinkerArguments(product),
+ Cpp.collectMiscLinkerArguments(product),
+ Cpp.collectMiscDriverArguments(product));
return args;
}
-function archiverFlags(project, product, input, outputs) {
+function archiverFlags(project, product, inputs, outputs) {
var args = [];
// Inputs.
- if (inputs.obj)
- args = args.concat(inputs.obj.map(function(obj) { return obj.filePath }));
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs));
// Output.
- args.push("--create");
+ var architecture = product.qbs.architecture;
+ if (supportIArchiver(architecture))
+ args.push("--create");
args.push("-o", outputs.staticlibrary[0].filePath);
return args;
@@ -673,6 +731,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
var cmd = new Command(compilerPath, args);
cmd.description = "compiling " + input.fileName;
cmd.highlight = "compiler";
+ cmd.jobPool = "compiler";
return [cmd];
}
@@ -682,25 +741,28 @@ function prepareAssembler(project, product, inputs, outputs, input, output, expl
var cmd = new Command(assemblerPath, args);
cmd.description = "assembling " + input.fileName;
cmd.highlight = "compiler";
+ cmd.jobPool = "assembler";
return [cmd];
}
function prepareLinker(project, product, inputs, outputs, input, output) {
var primaryOutput = outputs.application[0];
- var args = linkerFlags(project, product, input, outputs);
+ var args = linkerFlags(project, product, inputs, outputs);
var linkerPath = product.cpp.linkerPath;
var cmd = new Command(linkerPath, args);
cmd.description = "linking " + primaryOutput.fileName;
cmd.highlight = "linker";
+ cmd.jobPool = "linker";
return [cmd];
}
function prepareArchiver(project, product, inputs, outputs, input, output) {
- var args = archiverFlags(project, product, input, outputs);
+ var args = archiverFlags(project, product, inputs, outputs);
var archiverPath = product.cpp.archiverPath;
var cmd = new Command(archiverPath, args);
- cmd.description = "linking " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.highlight = "linker";
+ cmd.jobPool = "linker";
cmd.stdoutFilterFunction = function(output) {
return "";
};
diff --git a/share/qbs/modules/cpp/iar.qbs b/share/qbs/modules/cpp/iar.qbs
index bc226389b..558635ef5 100644
--- a/share/qbs/modules/cpp/iar.qbs
+++ b/share/qbs/modules/cpp/iar.qbs
@@ -28,17 +28,16 @@
**
****************************************************************************/
-import qbs 1.0
import qbs.File
import qbs.FileInfo
-import qbs.ModUtils
import qbs.PathTools
import qbs.Probes
import qbs.Utilities
+import "cpp.js" as Cpp
import "iar.js" as IAR
CppModule {
- condition: qbs.toolchain && qbs.toolchain.contains("iar")
+ condition: qbs.toolchain && qbs.toolchain.includes("iar")
Probes.BinaryProbe {
id: compilerPathProbe
@@ -54,6 +53,7 @@ CppModule {
}
qbs.architecture: iarProbe.found ? iarProbe.architecture : original
+ qbs.targetPlatform: "none"
compilerVersionMajor: iarProbe.versionMajor
compilerVersionMinor: iarProbe.versionMinor
@@ -63,45 +63,58 @@ CppModule {
compilerDefinesByLanguage: iarProbe.compilerDefinesByLanguage
compilerIncludePaths: iarProbe.includePaths
- property string toolchainInstallPath: compilerPathProbe.found
- ? compilerPathProbe.path : undefined
-
- property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : ""
+ toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined
/* Work-around for QtCreator which expects these properties to exist. */
property string cCompilerName: compilerName
property string cxxCompilerName: compilerName
- compilerName: IAR.compilerName(qbs) + compilerExtension
+ compilerName: toolchainDetails.compilerName + compilerExtension
compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName)
- assemblerName: IAR.assemblerName(qbs) + compilerExtension
+ assemblerName: toolchainDetails.assemblerName + compilerExtension
assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName)
- linkerName: IAR.linkerName(qbs) + compilerExtension
+ linkerName: toolchainDetails.linkerName + compilerExtension
linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName)
- property string archiverName: IAR.archiverName(qbs) + compilerExtension
+ property string archiverName: toolchainDetails.archiverName + compilerExtension
property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName)
runtimeLibrary: "static"
- staticLibrarySuffix: IAR.staticLibrarySuffix(qbs)
- executableSuffix: IAR.executableSuffix(qbs)
-
- property string objectSuffix: IAR.objectSuffix(qbs)
+ staticLibrarySuffix: toolchainDetails.staticLibrarySuffix
+ executableSuffix: toolchainDetails.executableSuffix
+ objectSuffix: toolchainDetails.objectSuffix
- imageFormat: IAR.imageFormat(qbs)
+ imageFormat: toolchainDetails.imageFormat
enableExceptions: false
enableRtti: false
+ defineFlag: "-D"
+ includeFlag: "-I"
+ systemIncludeFlag: "-I"
+ preincludeFlag: "--preinclude"
+ libraryDependencyFlag: ""
+ libraryPathFlag: toolchainDetails.libraryPathFlag
+ linkerScriptFlag: toolchainDetails.linkerScriptFlag
+
+ property string linkerSilentFlag: toolchainDetails.linkerSilentFlag
+ property string linkerMapFileFlag: toolchainDetails.linkerMapFileFlag
+ property string linkerEntryPointFlag: toolchainDetails.linkerEntryPointFlag
+
+ toolchainDetails: IAR.toolchainDetails(qbs)
+
+ knownArchitectures: ["78k", "arm", "avr", "avr32", "cr16",
+ "hcs12", "hcs8", "m16c", "m32c", "m68k", "mcs51", "msp430",
+ "r32c", "rh850", "riscv", "rl78", "rx", "sh", "stm8", "v850"]
+
Rule {
id: assembler
inputs: ["asm"]
- outputFileTags: ["obj", "lst"]
- outputArtifacts: IAR.compilerOutputArtifacts(
- input, input.cpp.generateAssemblerListingFiles)
+ outputFileTags: Cpp.assemblerOutputTags(generateAssemblerListingFiles)
+ outputArtifacts: Cpp.assemblerOutputArtifacts(input)
prepare: IAR.prepareAssembler.apply(IAR, arguments)
}
@@ -114,9 +127,8 @@ CppModule {
id: compiler
inputs: ["cpp", "c"]
auxiliaryInputs: ["hpp"]
- outputFileTags: ["obj", "lst"]
- outputArtifacts: IAR.compilerOutputArtifacts(
- input, input.cpp.generateCompilerListingFiles)
+ outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles)
+ outputArtifacts: Cpp.compilerOutputArtifacts(input)
prepare: IAR.prepareCompiler.apply(IAR, arguments)
}
@@ -125,8 +137,8 @@ CppModule {
multiplex: true
inputs: ["obj", "linkerscript"]
inputsFromDependencies: ["staticlibrary"]
- outputFileTags: ["application", "mem_map"]
- outputArtifacts: IAR.applicationLinkerOutputArtifacts(product)
+ outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile)
+ outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product)
prepare: IAR.prepareLinker.apply(IAR, arguments)
}
@@ -135,8 +147,8 @@ CppModule {
multiplex: true
inputs: ["obj"]
inputsFromDependencies: ["staticlibrary"]
- outputFileTags: ["staticlibrary"]
- outputArtifacts: IAR.staticLibraryLinkerOutputArtifacts(product)
+ outputFileTags: Cpp.staticLibraryLinkerOutputTags()
+ outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product)
prepare: IAR.prepareArchiver.apply(IAR, arguments)
}
}
diff --git a/share/qbs/modules/cpp/ios-gcc.qbs b/share/qbs/modules/cpp/ios-gcc.qbs
index 0ff99679f..968b873da 100644
--- a/share/qbs/modules/cpp/ios-gcc.qbs
+++ b/share/qbs/modules/cpp/ios-gcc.qbs
@@ -31,21 +31,30 @@
import qbs.DarwinTools
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.Utilities
DarwinGCC {
priority: 1
- condition: qbs.targetOS.contains('ios') &&
- qbs.toolchain && qbs.toolchain.contains('gcc')
+ condition: qbs.targetOS.includes('ios') &&
+ qbs.toolchain && qbs.toolchain.includes('gcc')
+ minimumIosVersion: {
+ if (Host.architecture() == "armv7a")
+ return "6.0";
+ // XCode 12 requres version (at least 8.0) to be present in the -target triplet
+ // when compiling for ios-simulator
+ if (xcode.present && Utilities.versionCompare(xcode.version, "12.0") >= 0)
+ return "8.0";
+ }
targetSystem: "ios" + (minimumIosVersion || "")
minimumDarwinVersion: minimumIosVersion
- minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("ios-simulator")
+ minimumDarwinVersionCompilerFlag: qbs.targetOS.includes("ios-simulator")
? "-mios-simulator-version-min"
: "-miphoneos-version-min"
- minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("ios-simulator")
+ minimumDarwinVersionLinkerFlag: qbs.targetOS.includes("ios-simulator")
? "-ios_simulator_version_min"
: "-iphoneos_version_min"
@@ -60,13 +69,13 @@ DarwinGCC {
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")
+ return qbs.targetOS.includes("ios-simulator")
? ["-fobjc-abi-version=2", "-fobjc-legacy-dispatch"]
: [];
}
Rule {
- condition: !product.qbs.targetOS.contains("ios-simulator")
+ condition: !product.qbs.targetOS.includes("ios-simulator")
inputsFromDependencies: ["bundle.content"]
Artifact {
diff --git a/share/qbs/modules/cpp/keil.js b/share/qbs/modules/cpp/keil.js
index befcc5a33..8f3297aa2 100644
--- a/share/qbs/modules/cpp/keil.js
+++ b/share/qbs/modules/cpp/keil.js
@@ -39,382 +39,456 @@ var TemporaryDir = require("qbs.TemporaryDir");
var TextFile = require("qbs.TextFile");
var Utilities = require("qbs.Utilities");
-function compilerName(qbs) {
- switch (qbs.architecture) {
- case "mcs51":
- return "c51";
- case "arm":
- return "armcc";
- }
- throw "Unable to deduce compiler name for unsupported architecture: '"
- + qbs.architecture + "'";
+function isMcs51Architecture(architecture) {
+ return architecture === "mcs51";
}
-function assemblerName(qbs) {
- switch (qbs.architecture) {
- case "mcs51":
- return "a51";
- case "arm":
- return "armasm";
- }
- throw "Unable to deduce assembler name for unsupported architecture: '"
- + qbs.architecture + "'";
+function isMcs251Architecture(architecture) {
+ return architecture === "mcs251";
}
-function linkerName(qbs) {
- switch (qbs.architecture) {
- case "mcs51":
- return "bl51";
- case "arm":
- return "armlink";
- }
- throw "Unable to deduce linker name for unsupported architecture: '"
- + qbs.architecture + "'";
+function isMcsArchitecture(architecture) {
+ return isMcs51Architecture(architecture)
+ || isMcs251Architecture(architecture);
}
-function archiverName(qbs) {
- switch (qbs.architecture) {
- case "mcs51":
- return "lib51";
- case "arm":
- return "armar";
- }
- throw "Unable to deduce archiver name for unsupported architecture: '"
- + qbs.architecture + "'";
+function isC166Architecture(architecture) {
+ return architecture === "c166";
}
-function staticLibrarySuffix(qbs) {
- switch (qbs.architecture) {
- case "mcs51":
- case "arm":
- return ".lib";
- }
- throw "Unable to deduce static library suffix for unsupported architecture: '"
- + qbs.architecture + "'";
+function isArmArchitecture(architecture) {
+ return architecture.startsWith("arm");
}
-function executableSuffix(qbs) {
- switch (qbs.architecture) {
- case "mcs51":
- return ".abs";
- case "arm":
- return ".axf";
- }
- throw "Unable to deduce executable suffix for unsupported architecture: '"
- + qbs.architecture + "'";
+function isMcsCompiler(compilerPath) {
+ var base = FileInfo.baseName(compilerPath).toLowerCase();
+ return base === "c51" || base === "cx51" || base === "c251";
}
-function objectSuffix(qbs) {
- switch (qbs.architecture) {
- case "mcs51":
- return ".obj";
- case "arm":
- return ".o";
- }
- throw "Unable to deduce object file suffix for unsupported architecture: '"
- + qbs.architecture + "'";
-}
-
-function imageFormat(qbs) {
- switch (qbs.architecture) {
- case "mcs51":
- // Keil OMF51 or OMF2 Object Module Format (which is an
- // extension of the original Intel OMF51).
- return "omf";
- case "arm":
- return "elf";
+function isC166Compiler(compilerPath) {
+ return FileInfo.baseName(compilerPath).toLowerCase() === "c166";
+}
+
+function isArmCCCompiler(compilerPath) {
+ return FileInfo.baseName(compilerPath).toLowerCase() === "armcc";
+}
+
+function isArmClangCompiler(compilerPath) {
+ return FileInfo.baseName(compilerPath).toLowerCase() === "armclang";
+}
+
+function preincludeFlag(compilerPath) {
+ if (isArmCCCompiler(compilerPath))
+ return "--preinclude";
+ else if (isArmClangCompiler(compilerPath))
+ return "-include";
+}
+
+function toolchainDetails(qbs) {
+ var architecture = qbs.architecture;
+ if (isMcs51Architecture(architecture)) {
+ return {
+ "imageFormat": "omf",
+ "linkerMapSuffix":".m51",
+ "objectSuffix": ".obj",
+ "executableSuffix": ".abs",
+ "compilerName": "c51",
+ "assemblerName": "a51",
+ "linkerName": "bl51",
+ "archiverName": "lib51"
+ };
+ } else if (isMcs251Architecture(architecture)) {
+ return {
+ "imageFormat": "omf",
+ "linkerMapSuffix":".m51",
+ "objectSuffix": ".obj",
+ "executableSuffix": ".abs",
+ "compilerName": "c251",
+ "assemblerName": "a251",
+ "linkerName": "l251",
+ "archiverName": "lib251"
+ };
+ } else if (isC166Architecture(architecture)) {
+ return {
+ "imageFormat": "omf",
+ "linkerMapSuffix":".m66",
+ "objectSuffix": ".obj",
+ "executableSuffix": ".abs",
+ "compilerName": "c166",
+ "assemblerName": "a166",
+ "linkerName": "l166",
+ "archiverName": "lib166"
+ };
+ } else if (isArmArchitecture(architecture)) {
+ return {
+ "imageFormat": "elf",
+ "linkerMapSuffix":".map",
+ "objectSuffix": ".o",
+ "executableSuffix": ".axf",
+ "disassemblerName": "fromelf",
+ "compilerName": "armcc",
+ "assemblerName": "armasm",
+ "linkerName": "armlink",
+ "archiverName": "armar"
+ };
}
- throw "Unable to deduce image format for unsupported architecture: '"
- + qbs.architecture + "'";
+}
+
+function guessArmCCArchitecture(targetArchArm, targetArchThumb) {
+ var arch = "arm";
+ if (targetArchArm === "4" && targetArchThumb === "0")
+ arch += "v4";
+ else if (targetArchArm === "4" && targetArchThumb === "1")
+ arch += "v4t";
+ else if (targetArchArm === "5" && targetArchThumb === "2")
+ arch += "v5t";
+ else if (targetArchArm === "6" && targetArchThumb === "3")
+ arch += "v6";
+ else if (targetArchArm === "6" && targetArchThumb === "4")
+ arch += "v6t2";
+ else if (targetArchArm === "0" && targetArchThumb === "3")
+ arch += "v6m";
+ else if (targetArchArm === "7" && targetArchThumb === "4")
+ arch += "v7r";
+ else if (targetArchArm === "0" && targetArchThumb === "4")
+ arch += "v7m";
+ return arch;
+}
+
+function guessArmClangArchitecture(targetArchArm, targetArchProfile) {
+ targetArchProfile = targetArchProfile.replace(/'/g, "");
+ var arch = "arm";
+ if (targetArchArm !== "" && targetArchProfile !== "")
+ arch += "v" + targetArchArm + targetArchProfile.toLowerCase();
+ return arch;
}
function guessArchitecture(macros) {
if (macros["__C51__"])
return "mcs51";
- else if (macros["__CC_ARM"] === 1)
- return "arm";
+ else if (macros["__C251__"])
+ return "mcs251";
+ else if (macros["__C166__"])
+ return "c166";
+ else if (macros["__CC_ARM"] === "1")
+ return guessArmCCArchitecture(macros["__TARGET_ARCH_ARM"], macros["__TARGET_ARCH_THUMB"]);
+ else if (macros["__clang__"] === "1" && macros["__arm__"] === "1")
+ return guessArmClangArchitecture(macros["__ARM_ARCH"], macros["__ARM_ARCH_PROFILE"]);
}
function guessEndianness(macros) {
- if (macros["__C51__"]) {
+ if (macros["__C51__"] || macros["__C251__"]) {
// The 8051 processors are 8-bit. So, the data with an integer type
// represented by more than one byte is stored as big endian in the
// Keil toolchain. See for more info:
// * http://www.keil.com/support/man/docs/c51/c51_ap_2bytescalar.htm
// * http://www.keil.com/support/man/docs/c51/c51_ap_4bytescalar.htm
+ // * http://www.keil.com/support/man/docs/c251/c251_ap_2bytescalar.htm
+ // * http://www.keil.com/support/man/docs/c251/c251_ap_4bytescalar.htm
return "big";
- } else if (macros["__ARMCC_VERSION"]) {
+ } else if (macros["__C166__"]) {
+ // The C166 processors are 16-bit. So, the data with an integer type
+ // represented by more than one byte is stored as little endian in the
+ // Keil toolchain. See for more info:
+ // * http://www.keil.com/support/man/docs/c166/c166_ap_ints.htm
+ // * http://www.keil.com/support/man/docs/c166/c166_ap_longs.htm
+ return "little";
+ } else if (macros["__CC_ARM"]) {
return macros["__BIG_ENDIAN"] ? "big" : "little";
+ } else if (macros["__clang__"] && macros["__arm__"]) {
+ switch (macros["__BYTE_ORDER__"]) {
+ case "__ORDER_BIG_ENDIAN__":
+ return "big";
+ case "__ORDER_LITTLE_ENDIAN__":
+ return "little";
+ }
}
}
function guessVersion(macros) {
- if (macros["__C51__"]) {
- var mcsVersion = macros["__C51__"];
+ if (macros["__C51__"] || macros["__C251__"]) {
+ var mcsVersion = macros["__C51__"] || macros["__C251__"];
return { major: parseInt(mcsVersion / 100),
minor: parseInt(mcsVersion % 100),
- patch: 0,
- found: true }
- } else if (macros["__CC_ARM"]) {
+ patch: 0 }
+ } else if (macros["__C166__"]) {
+ var xcVersion = macros["__C166__"];
+ return { major: parseInt(xcVersion / 100),
+ minor: parseInt(xcVersion % 100),
+ patch: 0 }
+ } else if (macros["__CC_ARM"] || macros["__clang__"]) {
var armVersion = macros["__ARMCC_VERSION"];
return { major: parseInt(armVersion / 1000000),
minor: parseInt(armVersion / 10000) % 100,
- patch: parseInt(armVersion) % 10000,
- found: true }
+ patch: parseInt(armVersion) % 10000 }
}
}
-// Note: The KEIL 8051 compiler does not support the predefined
-// macros dumping. So, we do it with following trick where we try
-// to compile a temporary file and to parse the console output.
-function dumpC51CompilerMacros(compilerFilePath, tag) {
- // C51 compiler support only C language.
+function dumpMcsCompilerMacros(compilerFilePath, tag) {
+ // C51 or C251 compiler support only C language.
if (tag === "cpp")
return {};
- function createDumpMacrosFile() {
- var td = new TemporaryDir();
- var fn = FileInfo.fromNativeSeparators(td.path() + "/dump-macros.c");
- var tf = new TextFile(fn, TextFile.WriteOnly);
- tf.writeLine("#define VALUE_TO_STRING(x) #x");
- tf.writeLine("#define VALUE(x) VALUE_TO_STRING(x)");
- tf.writeLine("#define VAR_NAME_VALUE(var) \"\"\"|\"#var\"|\"VALUE(var)");
- tf.writeLine("#ifdef __C51__");
- tf.writeLine("#pragma message(VAR_NAME_VALUE(__C51__))");
- tf.writeLine("#endif");
- tf.close();
- return fn;
+ // Note: The C51 or C251 compiler does not support the predefined
+ // macros dumping. So, we do it with the following trick, where we try
+ // to create and compile a special temporary file and to parse the console
+ // output with the own magic pattern: (""|"key"|"value"|"").
+
+ var outputDirectory = new TemporaryDir();
+ var outputFilePath = FileInfo.fromNativeSeparators(FileInfo.joinPaths(outputDirectory.path(),
+ "dump-macros.c"));
+ var outputFile = new TextFile(outputFilePath, TextFile.WriteOnly);
+
+ outputFile.writeLine("#define VALUE_TO_STRING(x) #x");
+ outputFile.writeLine("#define VALUE(x) VALUE_TO_STRING(x)");
+
+ // Predefined keys for C51 and C251 compilers, see details:
+ // * https://www.keil.com/support/man/docs/c51/c51_pp_predefmacroconst.htm
+ // * https://www.keil.com/support/man/docs/c251/c251_pp_predefmacroconst.htm
+ var keys = ["__C51__", "__CX51__", "__C251__", "__MODEL__",
+ "__STDC__", "__FLOAT64__", "__MODSRC__"];
+
+ // For C51 compiler.
+ outputFile.writeLine("#if defined(__C51__) || defined(__CX51__)");
+ outputFile.writeLine("# define VAR_NAME_VALUE(var) \"(\"\"\"\"|\"#var\"|\"VALUE(var)\"|\"\"\"\")\"");
+ for (var i in keys) {
+ var key = keys[i];
+ outputFile.writeLine("# if defined (" + key + ")");
+ outputFile.writeLine("# pragma message (VAR_NAME_VALUE(" + key + "))");
+ outputFile.writeLine("# endif");
+ }
+ outputFile.writeLine("#endif");
+
+ // For C251 compiler.
+ outputFile.writeLine("#if defined(__C251__)");
+ outputFile.writeLine("# define VAR_NAME_VALUE(var) \"\"|#var|VALUE(var)|\"\"");
+ for (var i in keys) {
+ var key = keys[i];
+ outputFile.writeLine("# if defined (" + key + ")");
+ outputFile.writeLine("# warning (VAR_NAME_VALUE(" + key + "))");
+ outputFile.writeLine("# endif");
}
+ outputFile.writeLine("#endif");
- var fn = createDumpMacrosFile();
- var p = new Process();
- p.exec(compilerFilePath, [ fn ], false);
+ outputFile.close();
+
+ var process = new Process();
+ process.exec(compilerFilePath, [outputFilePath], false);
+ File.remove(outputFilePath);
var map = {};
- p.readStdOut().trim().split(/\r?\n/g).map(function(line) {
- var parts = line.split("\"|\"", 3);
- map[parts[1]] = parts[2];
+ process.readStdOut().trim().split(/\r?\n/g).map(function(line) {
+ var parts = line.split("\"|\"", 4);
+ if (parts.length === 4)
+ map[parts[1]] = parts[2];
});
return map;
}
-function dumpArmCompilerMacros(compilerFilePath, tag, nullDevice) {
- var args = [ "-E", "--list-macros", nullDevice ];
+function dumpC166CompilerMacros(compilerFilePath, tag) {
+ // C166 compiler support only C language.
if (tag === "cpp")
- args.push("--cpp");
+ return {};
- var p = new Process();
- p.exec(compilerFilePath, args, false);
- var map = {};
- p.readStdOut().trim().split(/\r?\n/g).map(function (line) {
- if (!line.startsWith("#define"))
- return;
- var parts = line.split(" ", 3);
- map[parts[1]] = parts[2];
- });
- return map;
-}
+ // Note: The C166 compiler does not support the predefined
+ // macros dumping. Also, it does not support the '#pragma' and
+ // '#message|warning|error' directives properly (it is impossible
+ // to print to console the value of macro).
+ // So, we do it with the following trick, where we try
+ // to create and compile a special temporary file and to parse the console
+ // output with the own magic pattern, e.g:
+ //
+ // *** WARNING C320 IN LINE 41 OF c51.c: __C166__
+ // *** WARNING C2 IN LINE 42 OF c51.c: '757': unknown #pragma/control, line ignored
+ //
+ // where the '__C166__' is a key, and the '757' is a value.
+
+ var outputDirectory = new TemporaryDir();
+ var outputFilePath = FileInfo.fromNativeSeparators(outputDirectory.path() + "/dump-macros.c");
+ var outputFile = new TextFile(outputFilePath, TextFile.WriteOnly);
+
+ // Predefined keys for C166 compiler, see details:
+ // * https://www.keil.com/support/man/docs/c166/c166_pp_predefmacroconst.htm
+ var keys = ["__C166__", "__DUS__", "__MAC__", "__MOD167__",
+ "__MODEL__", "__MODV2__", "__SAVEMAC__", "__STDC__"];
+
+ // For C166 compiler.
+ outputFile.writeLine("#if defined(__C166__)");
+ for (var i in keys) {
+ var key = keys[i];
+ outputFile.writeLine("# if defined (" + key + ")");
+ outputFile.writeLine("# warning " + key);
+ outputFile.writeLine("# pragma " + key);
+ outputFile.writeLine("# endif");
+ }
+ outputFile.writeLine("#endif");
-function dumpMacros(compilerFilePath, tag, nullDevice) {
- var map1 = dumpC51CompilerMacros(compilerFilePath, tag, nullDevice);
- var map2 = dumpArmCompilerMacros(compilerFilePath, tag, nullDevice);
- var map = {};
- for (var key1 in map1)
- map[key1] = map1[key1];
- for (var key2 in map2)
- map[key2] = map2[key2];
- return map;
-}
+ outputFile.close();
-function dumpDefaultPaths(compilerFilePath, architecture) {
- var incDir = (architecture === "arm") ? "include" : "inc";
- var includePath = compilerFilePath.replace(/bin[\\\/](.*)$/i, incDir);
- return {
- "includePaths": [includePath]
+ function extractKey(line, knownKeys) {
+ for (var i in keys) {
+ var key = knownKeys[i];
+ var regexp = new RegExp("^\\*\\*\\* WARNING C320 IN LINE .+: (" + key + ")$");
+ var match = regexp.exec(line);
+ if (match)
+ return key;
+ }
};
-}
-
-function adjustPathsToWindowsSeparators(sourcePaths) {
- var resulingPaths = [];
- sourcePaths.forEach(function(path) {
- resulingPaths.push(FileInfo.toWindowsSeparators(path));
- });
- return resulingPaths;
-}
-function getMaxExitCode(architecture) {
- if (architecture === "mcs51")
- return 1;
- else if (architecture === "arm")
- return 0;
-}
+ function extractValue(line) {
+ var regexp = new RegExp("^\\*\\*\\* WARNING C2 IN LINE .+'(.+)':.+$");
+ var match = regexp.exec(line);
+ if (match)
+ return match[1];
+ };
-function collectLibraryDependencies(product) {
- var seen = {};
- var result = [];
+ var process = new Process();
+ process.exec(compilerFilePath, [outputFilePath], false);
+ File.remove(outputFilePath);
+ var lines = process.readStdOut().trim().split(/\r?\n/g);
+ var map = {};
+ for (var i = 0; i < lines.length; ++i) {
+ // First line should contains the macro key.
+ var keyLine = lines[i];
+ var key = extractKey(keyLine, keys);
+ if (!key)
+ continue;
+
+ i += 1;
+ if (i >= lines.length)
+ break;
- function addFilePath(filePath) {
- result.push({ filePath: filePath });
+ // Second line should contains the macro value.
+ var valueLine = lines[i];
+ var value = extractValue(valueLine);
+ if (!value)
+ continue;
+ map[key] = value;
}
+ return map;
+}
- function addArtifactFilePaths(dep, artifacts) {
- if (!artifacts)
- return;
- var artifactFilePaths = artifacts.map(function(a) { return a.filePath; });
- artifactFilePaths.forEach(addFilePath);
- }
+function dumpArmCCCompilerMacros(compilerFilePath, tag, nullDevice) {
+ var args = [ "-E", "--list-macros", "--cpu=cortex-m0", nullDevice ];
+ if (tag === "cpp")
+ args.push("--cpp");
- function addExternalStaticLibs(obj) {
- if (!obj.cpp)
- return;
- function ensureArray(a) {
- return Array.isArray(a) ? a : [];
- }
- function sanitizedModuleListProperty(obj, moduleName, propertyName) {
- return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName));
- }
- var externalLibs = [].concat(
- sanitizedModuleListProperty(obj, "cpp", "staticLibraries"));
- var staticLibrarySuffix = obj.moduleProperty("cpp", "staticLibrarySuffix");
- externalLibs.forEach(function(staticLibraryName) {
- if (!staticLibraryName.endsWith(staticLibrarySuffix))
- staticLibraryName += staticLibrarySuffix;
- addFilePath(staticLibraryName);
- });
- }
+ var p = new Process();
+ p.exec(compilerFilePath, args, false);
+ return Cpp.extractMacros(p.readStdOut());
+}
- function traverse(dep) {
- if (seen.hasOwnProperty(dep.name))
- return;
- seen[dep.name] = true;
+function dumpArmClangCompilerMacros(compilerFilePath, tag, nullDevice) {
+ var args = [ "--target=arm-arm-none-eabi", "-mcpu=cortex-m0", "-dM", "-E",
+ "-x", ((tag === "cpp") ? "c++" : "c"), nullDevice ];
+ var p = new Process();
+ p.exec(compilerFilePath, args, false);
+ return Cpp.extractMacros(p.readStdOut());
+}
- if (dep.parameters.cpp && dep.parameters.cpp.link === false)
- return;
+function dumpMacros(compilerFilePath, tag, nullDevice) {
+ if (isMcsCompiler(compilerFilePath))
+ return dumpMcsCompilerMacros(compilerFilePath, tag, nullDevice);
+ else if (isC166Compiler(compilerFilePath))
+ return dumpC166CompilerMacros(compilerFilePath, tag, nullDevice);
+ else if (isArmCCCompiler(compilerFilePath))
+ return dumpArmCCCompilerMacros(compilerFilePath, tag, nullDevice);
+ else if (isArmClangCompiler(compilerFilePath))
+ return dumpArmClangCompilerMacros(compilerFilePath, tag, nullDevice);
+ return {};
+}
- var staticLibraryArtifacts = dep.artifacts["staticlibrary"];
- if (staticLibraryArtifacts) {
- dep.dependencies.forEach(traverse);
- addArtifactFilePaths(dep, staticLibraryArtifacts);
- addExternalStaticLibs(dep);
- }
- }
+function dumpMcsCompilerIncludePaths(compilerFilePath) {
+ var includePath = compilerFilePath.replace(/bin[\\\/](.*)$/i, "inc");
+ return [includePath];
+}
- product.dependencies.forEach(traverse);
- addExternalStaticLibs(product);
- return result;
-}
-
-function filterStdOutput(cmd) {
- cmd.stdoutFilterFunction = function(output) {
- var sourceLines = output.split("\n");
- var filteredLines = [];
- for (var i in sourceLines) {
- if (sourceLines[i].startsWith("***")
- || sourceLines[i].startsWith(">>")
- || sourceLines[i].startsWith(" ")
- || sourceLines[i].startsWith("Program Size:")
- || sourceLines[i].startsWith("A51 FATAL")
- || sourceLines[i].startsWith("C51 FATAL")
- || sourceLines[i].startsWith("ASSEMBLER INVOKED BY")
- || sourceLines[i].startsWith("LOC OBJ LINE SOURCE")
- ) {
- filteredLines.push(sourceLines[i]);
- } else {
- var regexp = /^([0-9A-F]{4})/;
- if (regexp.exec(sourceLines[i]))
- filteredLines.push(sourceLines[i]);
- }
- }
- return filteredLines.join("\n");
- };
+function dumpC166CompilerIncludePaths(compilerFilePath) {
+ // Same as for MCS compiler.
+ return dumpMcsCompilerIncludePaths(compilerFilePath);
}
-function compilerOutputArtifacts(input, useListing) {
- var artifacts = [];
- artifacts.push({
- fileTags: ["obj"],
- filePath: Utilities.getHash(input.baseDir) + "/"
- + input.fileName + input.cpp.objectSuffix
- });
- if (useListing) {
- artifacts.push({
- fileTags: ["lst"],
- filePath: Utilities.getHash(input.baseDir) + "/"
- + (input.cpp.architecture === "mcs51"
- ? input.fileName : input.baseName)
- + ".lst"
- });
- }
- return artifacts;
+function dumpArmCCCompilerIncludePaths(compilerFilePath) {
+ var includePath = compilerFilePath.replace(/bin[\\\/](.*)$/i, "include");
+ return [includePath];
}
-function applicationLinkerOutputArtifacts(product) {
- var app = {
- fileTags: ["application"],
- filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- PathTools.applicationFilePath(product))
- };
- var mem_map = {
- fileTags: ["mem_map"],
- filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- product.targetName
- + (product.cpp.architecture === "mcs51" ? ".m51" : ".map"))
- };
- return [app, mem_map];
+function dumpArmClangCompilerIncludePaths(compilerFilePath, nullDevice) {
+ var args = [ "--target=arm-arm-none-eabi", "-mcpu=cortex-m0", "-v", "-E",
+ "-x", "c++", nullDevice ];
+ var p = new Process();
+ p.exec(compilerFilePath, args, false);
+ var lines = p.readStdErr().trim().split(/\r?\n/g).map(function (line) { return line.trim(); });
+ var addIncludes = false;
+ var includePaths = [];
+ for (var i = 0; i < lines.length; ++i) {
+ var line = lines[i];
+ if (line === "#include <...> search starts here:")
+ addIncludes = true;
+ else if (line === "End of search list.")
+ addIncludes = false;
+ else if (addIncludes)
+ includePaths.push(line);
+ }
+ return includePaths;
}
-function staticLibraryLinkerOutputArtifacts(product) {
- var staticLib = {
- fileTags: ["staticlibrary"],
- filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- PathTools.staticLibraryFilePath(product))
- };
- return [staticLib]
+function dumpDefaultPaths(compilerFilePath, nullDevice) {
+ var includePaths = [];
+ if (isMcsCompiler(compilerFilePath))
+ includePaths = dumpMcsCompilerIncludePaths(compilerFilePath);
+ else if (isC166Compiler(compilerFilePath))
+ includePaths = dumpC166CompilerIncludePaths(compilerFilePath);
+ else if (isArmCCCompiler(compilerFilePath))
+ includePaths = dumpArmCCCompilerIncludePaths(compilerFilePath);
+ else if (isArmClangCompiler(compilerFilePath))
+ includePaths = dumpArmClangCompilerIncludePaths(compilerFilePath, nullDevice);
+ return { "includePaths": includePaths };
}
+function filterMcsOutput(output) {
+ var filteredLines = [];
+ output.split(/\r\n|\r|\n/).forEach(function(line) {
+ var regexp = /^\s*\*{3}\s|^\s{29}|^\s{4}\S|^\s{2}[0-9A-F]{4}|^\s{21,25}\d+|^[0-9A-F]{4}\s\d+/;
+ if (regexp.exec(line))
+ filteredLines.push(line);
+ });
+ return filteredLines.join('\n');
+};
+
+function filterC166Output(output) {
+ var filteredLines = [];
+ output.split(/\r\n|\r|\n/).forEach(function(line) {
+ var regexp = /^\s*\*{3}\s|^\s{29}|^\s{27,28}\d+|^\s{21}\d+|^\s{4}\S|^\s{2}[0-9A-F]{4}|^[0-9A-F]{4}\s\d+/;
+ if (regexp.exec(line))
+ filteredLines.push(line);
+ });
+ return filteredLines.join('\n');
+};
+
function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
// Determine which C-language we're compiling.
var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
var args = [];
- var allDefines = [];
- var platformDefines = input.cpp.platformDefines;
- if (platformDefines)
- allDefines = allDefines.uniqueConcat(platformDefines);
- var defines = input.cpp.defines;
- if (defines)
- allDefines = allDefines.uniqueConcat(defines);
-
- var allIncludePaths = [];
- var includePaths = input.cpp.includePaths;
- if (includePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(includePaths);
- var systemIncludePaths = input.cpp.systemIncludePaths;
- if (systemIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths);
- var compilerIncludePaths = input.cpp.compilerIncludePaths;
- if (compilerIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths);
-
var architecture = input.qbs.architecture;
- if (architecture === "mcs51") {
+ if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) {
// Input.
- args.push(FileInfo.toWindowsSeparators(input.filePath));
+ args.push(input.filePath);
// Output.
- args.push("OBJECT (" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath) + ")");
+ args.push("OBJECT(" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath) + ")");
// Defines.
- if (allDefines.length > 0)
- args = args.concat("DEFINE (" + allDefines.join(",") + ")");
+ var defines = Cpp.collectDefines(input);
+ if (defines.length > 0)
+ args = args.concat("DEFINE (" + defines.join(",") + ")");
// Includes.
- if (allIncludePaths.length > 0) {
- var adjusted = adjustPathsToWindowsSeparators(allIncludePaths);
- args = args.concat("INCDIR (" + adjusted.join(";") + ")");
- }
+ var allIncludePaths = [].concat(Cpp.collectIncludePaths(input),
+ Cpp.collectSystemIncludePaths(input));
+ if (allIncludePaths.length > 0)
+ args = args.concat("INCDIR(" + allIncludePaths.map(function(path) {
+ return FileInfo.toWindowsSeparators(path); }).join(";") + ")");
// Debug information flags.
if (input.cpp.debugInformation)
@@ -440,153 +514,191 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
break;
case "all":
args.push("WARNINGLEVEL (2)");
- args.push("FARWARNING");
+ if (isMcs51Architecture(architecture))
+ args.push("FARWARNING");
break;
}
// Listing files generation flag.
- if (!product.cpp.generateCompilerListingFiles)
+ if (!input.cpp.generateCompilerListingFiles)
args.push("NOPRINT");
else
args.push("PRINT(" + FileInfo.toWindowsSeparators(outputs.lst[0].filePath) + ")");
- } else if (architecture === "arm") {
+ } else if (isArmArchitecture(architecture)) {
// Input.
args.push("-c", input.filePath);
// Output.
args.push("-o", outputs.obj[0].filePath);
+ // Preinclude headers.
+ args = args.concat(Cpp.collectPreincludePathsArguments(input, true));
+
// Defines.
- args = args.concat(allDefines.map(function(define) { return '-D' + define }));
+ args = args.concat(Cpp.collectDefinesArguments(input));
// Includes.
- args = args.concat(allIncludePaths.map(function(include) { return '-I' + include }));
+ args = args.concat(Cpp.collectIncludePathsArguments(input));
+ args = args.concat(Cpp.collectSystemIncludePathsArguments(input));
+
+ var compilerPath = input.cpp.compilerPath;
+ if (isArmCCCompiler(compilerPath)) {
+ // Debug information flags.
+ if (input.cpp.debugInformation) {
+ args.push("--debug");
+ args.push("-g");
+ }
- // Debug information flags.
- if (input.cpp.debugInformation) {
- args.push("--debug");
- args.push("-g");
- }
+ // Optimization level flags.
+ switch (input.cpp.optimization) {
+ case "small":
+ args.push("-Ospace")
+ break;
+ case "fast":
+ args.push("-Otime")
+ break;
+ case "none":
+ args.push("-O0")
+ break;
+ }
- // Optimization level flags.
- switch (input.cpp.optimization) {
- case "small":
- args.push("-Ospace")
- break;
- case "fast":
- args.push("-Otime")
- break;
- case "none":
- args.push("-O0")
- break;
- }
+ // Warning level flags.
+ switch (input.cpp.warningLevel) {
+ case "none":
+ args.push("-W");
+ break;
+ default:
+ // By default all warnings are enabled.
+ break;
+ }
- // Warning level flags.
- switch (input.cpp.warningLevel) {
- case "none":
- args.push("-W");
- break;
- default:
- // By default all warnings are enabled.
- break;
- }
+ if (tag === "c") {
+ // C language version flags.
+ var knownCLanguageValues = ["c99", "c90"];
+ var cLanguageVersion = Cpp.languageVersion(
+ input.cpp.cLanguageVersion, knownCLanguageValues, "C");
+ switch (cLanguageVersion) {
+ case "c99":
+ args.push("--c99");
+ break;
+ case "c90":
+ args.push("--c90");
+ break;
+ }
+ } else if (tag === "cpp") {
+ // C++ language version flags.
+ var knownCppLanguageValues = ["c++11", "c++03"];
+ var cppLanguageVersion = Cpp.languageVersion(
+ input.cpp.cxxLanguageVersion, knownCppLanguageValues, "C++");
+ switch (cppLanguageVersion) {
+ case "c++11":
+ args.push("--cpp11");
+ break;
+ default:
+ // Default C++ language is C++03.
+ args.push("--cpp");
+ break;
+ }
+
+ // Exceptions flags.
+ var enableExceptions = input.cpp.enableExceptions;
+ if (enableExceptions !== undefined)
+ args.push(enableExceptions ? "--exceptions" : "--no_exceptions");
+
+ // RTTI flags.
+ var enableRtti = input.cpp.enableRtti;
+ if (enableRtti !== undefined)
+ args.push(enableRtti ? "--rtti" : "--no_rtti");
+ }
- if (tag === "c") {
- // C language version flags.
- var knownCLanguageValues = ["c99", "c90"];
- var cLanguageVersion = Cpp.languageVersion(
- input.cpp.cLanguageVersion, knownCLanguageValues, "C");
- switch (cLanguageVersion) {
- case "c99":
- args.push("--c99");
+ // Listing files generation flag.
+ if (input.cpp.generateCompilerListingFiles) {
+ args.push("--list");
+ args.push("--list_dir", FileInfo.path(outputs.lst[0].filePath));
+ }
+ } else if (isArmClangCompiler(compilerPath)) {
+ // Debug information flags.
+ if (input.cpp.debugInformation)
+ args.push("-g");
+
+ // Optimization level flags.
+ switch (input.cpp.optimization) {
+ case "small":
+ args.push("-Oz")
+ break;
+ case "fast":
+ args.push("-Ofast")
break;
- case "c90":
- args.push("--c90");
+ case "none":
+ args.push("-O0")
break;
}
- } else if (tag === "cpp") {
- // C++ language version flags.
- var knownCppLanguageValues = ["c++11", "c++03"];
- var cppLanguageVersion = Cpp.languageVersion(
- input.cpp.cxxLanguageVersion, knownCppLanguageValues, "C++");
- switch (cppLanguageVersion) {
- case "c++11":
- args.push("--cpp11");
+
+ // Warning level flags.
+ switch (input.cpp.warningLevel) {
+ case "all":
+ args.push("-Wall");
break;
default:
- // Default C++ language is C++03.
- args.push("--cpp");
break;
}
- // Exceptions flags.
- var enableExceptions = input.cpp.enableExceptions;
- if (enableExceptions !== undefined)
- args.push(enableExceptions ? "--exceptions" : "--no_exceptions");
-
- // RTTI flags.
- var enableRtti = input.cpp.enableRtti;
- if (enableRtti !== undefined)
- args.push(enableRtti ? "--rtti" : "--no_rtti");
- }
+ if (input.cpp.treatWarningsAsErrors)
+ args.push("-Werror");
+
+ if (tag === "c") {
+ // C language version flags.
+ var knownCLanguageValues = ["c11", "c99", "c90", "c89"];
+ var cLanguageVersion = Cpp.languageVersion(
+ input.cpp.cLanguageVersion, knownCLanguageValues, "C");
+ if (cLanguageVersion)
+ args.push("-std=" + cLanguageVersion);
+ } else if (tag === "cpp") {
+ // C++ language version flags.
+ var knownCppLanguageValues = ["c++17", "c++14", "c++11", "c++03", "c++98"];
+ var cppLanguageVersion = Cpp.languageVersion(
+ input.cpp.cxxLanguageVersion, knownCppLanguageValues, "C++");
+ if (cppLanguageVersion)
+ args.push("-std=" + cppLanguageVersion);
+
+ // Exceptions flags.
+ var enableExceptions = input.cpp.enableExceptions;
+ if (enableExceptions !== undefined)
+ args.push(enableExceptions ? "-fexceptions" : "-fno-exceptions");
+
+ // RTTI flags.
+ var enableRtti = input.cpp.enableRtti;
+ if (enableRtti !== undefined)
+ args.push(enableRtti ? "-frtti" : "-fno-rtti");
+ }
- // Listing files generation flag.
- if (product.cpp.generateCompilerListingFiles) {
- args.push("--list");
- args.push("--list_dir", FileInfo.path(outputs.lst[0].filePath));
+ // Listing files generation does not supported!
+ // There are only a workaround: http://www.keil.com/support/docs/4152.htm
}
}
// Misc flags.
- args = args.concat(ModUtils.moduleProperty(input, "platformFlags"),
- ModUtils.moduleProperty(input, "flags"),
- ModUtils.moduleProperty(input, "platformFlags", tag),
- ModUtils.moduleProperty(input, "flags", tag),
- ModUtils.moduleProperty(input, "driverFlags", tag));
+ args = args.concat(Cpp.collectMiscCompilerArguments(input, tag),
+ Cpp.collectMiscDriverArguments(input));
return args;
}
function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
- // Determine which C-language we're compiling
- var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
var args = [];
-
- var allDefines = [];
- var platformDefines = input.cpp.platformDefines;
- if (platformDefines)
- allDefines = allDefines.uniqueConcat(platformDefines);
- var defines = input.cpp.defines;
- if (defines)
- allDefines = allDefines.uniqueConcat(defines);
-
- var allIncludePaths = [];
- var includePaths = input.cpp.includePaths;
- if (includePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(includePaths);
- var systemIncludePaths = input.cpp.systemIncludePaths;
- if (systemIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths);
- var compilerIncludePaths = input.cpp.compilerIncludePaths;
- if (compilerIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths);
-
var architecture = input.qbs.architecture;
- if (architecture === "mcs51") {
+ if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) {
// Input.
- args.push(FileInfo.toWindowsSeparators(input.filePath));
+ args.push(input.filePath);
// Output.
- args.push("OBJECT (" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath) + ")");
-
- // Defines.
- if (allDefines.length > 0)
- args = args.concat("DEFINE (" + allDefines.join(",") + ")");
+ args.push("OBJECT(" + FileInfo.toWindowsSeparators(outputs.obj[0].filePath) + ")");
// Includes.
- if (allIncludePaths.length > 0) {
- var adjusted = adjustPathsToWindowsSeparators(allIncludePaths);
- args = args.concat("INCDIR (" + adjusted.join(";") + ")");
- }
+ var allIncludePaths = [].concat(Cpp.collectIncludePaths(input),
+ Cpp.collectSystemIncludePaths(input));
+ if (allIncludePaths.length > 0)
+ args = args.concat("INCDIR(" + allIncludePaths.map(function(path) {
+ return FileInfo.toWindowsSeparators(path); }).join(";") + ")");
// Debug information flags.
if (input.cpp.debugInformation)
@@ -596,31 +708,20 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
args.push("EP");
// Listing files generation flag.
- if (!product.cpp.generateAssemblerListingFiles)
+ if (!input.cpp.generateAssemblerListingFiles)
args.push("NOPRINT");
else
args.push("PRINT(" + FileInfo.toWindowsSeparators(outputs.lst[0].filePath) + ")");
- } else if (architecture === "arm") {
+ } else if (isArmArchitecture(architecture)) {
// Input.
args.push(input.filePath);
// Output.
args.push("-o", outputs.obj[0].filePath);
- // Defines.
- allDefines.forEach(function(define) {
- var parts = define.split("=");
- args.push("--pd");
- if (parts[1] === undefined)
- args.push(parts[0] + " SETA " + 1);
- else if (parts[1].contains("\""))
- args.push(parts[0] + " SETS " + parts[1]);
- else
- args.push(parts[0] + " SETA " + parts[1]);
- });
-
// Includes.
- args = args.concat(allIncludePaths.map(function(include) { return '-I' + include }));
+ args = args.concat(Cpp.collectIncludePathsArguments(input));
+ args = args.concat(Cpp.collectSystemIncludePathsArguments(input));
// Debug information flags.
if (input.cpp.debugInformation) {
@@ -638,72 +739,86 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
args.push((endianness === "little") ? "--littleend" : "--bigend");
// Listing files generation flag.
- if (product.cpp.generateAssemblerListingFiles)
+ if (input.cpp.generateAssemblerListingFiles)
args.push("--list", outputs.lst[0].filePath);
}
// Misc flags.
- args = args.concat(ModUtils.moduleProperty(input, "platformFlags", tag),
- ModUtils.moduleProperty(input, "flags", tag),
- ModUtils.moduleProperty(input, "driverFlags", tag));
+ args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm"));
+ return args;
+}
+
+function disassemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
+ var args = ["--disassemble", "--interleave=source"];
+ args.push(outputs.obj[0].filePath);
+ args.push("--output=" + outputs.lst[0].filePath);
return args;
}
-function linkerFlags(project, product, input, outputs) {
+function linkerFlags(project, product, inputs, outputs) {
var args = [];
+ var libraryPaths = Cpp.collectLibraryPaths(product);
+
var architecture = product.qbs.architecture;
- if (architecture === "mcs51") {
- // Note: The C51 linker does not distinguish an object files and
+ if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) {
+ // Semi-intelligent handling the library paths.
+ // We need to add the full path prefix to the library file if this
+ // file is not absolute or not relative. Reason is that the C51, C251,
+ // and C166 linkers does not support the library paths.
+ function collectLibraryObjectPaths(product) {
+ var libraryObjects = Cpp.collectLibraryDependencies(product);
+ return libraryObjects.map(function(dep) {
+ var filePath = dep.filePath;
+ if (FileInfo.isAbsolutePath(filePath))
+ return filePath;
+ for (var i = 0; i < libraryPaths.length; ++i) {
+ var fullPath = FileInfo.joinPaths(libraryPaths[i], filePath);
+ if (File.exists(fullPath))
+ return fullPath;
+ }
+ return filePath;
+ });
+ }
+
+ // Note: The C51, C251, or C166 linker does not distinguish an object files and
// a libraries, it interpret all this stuff as an input objects,
// so, we need to pass it together in one string.
-
- var allObjectPaths = [];
- function addObjectPath(obj) {
- allObjectPaths.push(obj.filePath);
+ function collectAllObjectPathsArguments(product, inputs) {
+ return [].concat(Cpp.collectLinkerObjectPaths(inputs),
+ collectLibraryObjectPaths(product));
}
- // Inputs.
- if (inputs.obj)
- inputs.obj.map(function(obj) { addObjectPath(obj) });
-
- // Library dependencies.
- var libraryObjects = collectLibraryDependencies(product);
- libraryObjects.forEach(function(dep) { addObjectPath(dep); })
-
// Add all input objects as arguments (application and library object files).
- var adjusted = adjustPathsToWindowsSeparators(allObjectPaths);
- args = args.concat(adjusted.join(","));
+ var allObjectPaths = collectAllObjectPathsArguments(product, inputs);
+ if (allObjectPaths.length > 0)
+ args = args.concat(allObjectPaths.map(function(path) {
+ return FileInfo.toWindowsSeparators(path); }).join(","));
// Output.
- // Note: We need to wrap an output file name with quotes. Otherwise
- // the linker will ignore a specified file name.
- args.push("TO", '"' + FileInfo.toWindowsSeparators(outputs.application[0].filePath) + '"');
+ args.push("TO", FileInfo.toWindowsSeparators(outputs.application[0].filePath));
// Map file generation flag.
if (!product.cpp.generateLinkerMapFile)
- args.push("NOMAP");
- } else if (architecture === "arm") {
+ args.push("NOPRINT");
+ else
+ args.push("PRINT(" + FileInfo.toWindowsSeparators(outputs.mem_map[0].filePath) + ")");
+ } else if (isArmArchitecture(architecture)) {
// Inputs.
- if (inputs.obj)
- args = args.concat(inputs.obj.map(function(obj) { return obj.filePath }));
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs));
// Output.
args.push("--output", outputs.application[0].filePath);
// Library paths.
- var libraryPaths = product.cpp.libraryPaths;
- if (libraryPaths)
- args.push("--userlibpath=" + libraryPaths.join(","));
+ if (libraryPaths.length > 0)
+ args.push(product.cpp.libraryPathFlag + libraryPaths.join(","));
// Library dependencies.
- var libraryDependencies = collectLibraryDependencies(product);
- args = args.concat(libraryDependencies.map(function(dep) { return dep.filePath; }));
+ args = args.concat(Cpp.collectLibraryDependenciesArguments(product));
- // Debug information flag.
- var debugInformation = product.cpp.debugInformation;
- if (debugInformation !== undefined)
- args.push(debugInformation ? "--debug" : "--no_debug");
+ // Linker scripts.
+ args = args.concat(Cpp.collectLinkerScriptPathsArguments(product, inputs, true));
// Map file generation flag.
if (product.cpp.generateLinkerMapFile)
@@ -713,43 +828,38 @@ function linkerFlags(project, product, input, outputs) {
if (product.cpp.entryPoint)
args.push("--entry", product.cpp.entryPoint);
- // Linker scripts flags.
- var linkerScripts = inputs.linkerscript
- ? inputs.linkerscript.map(function(a) { return a.filePath; }) : [];
- linkerScripts.forEach(function(script) { args.push("--scatter", script); });
+ // Debug information flag.
+ var debugInformation = product.cpp.debugInformation;
+ if (debugInformation !== undefined)
+ args.push(debugInformation ? "--debug" : "--no_debug");
}
// Misc flags.
- args = args.concat(ModUtils.moduleProperty(product, "driverLinkerFlags"));
+ args = args.concat(Cpp.collectMiscEscapableLinkerArguments(product),
+ Cpp.collectMiscLinkerArguments(product),
+ Cpp.collectMiscDriverArguments(product));
return args;
}
-function archiverFlags(project, product, input, outputs) {
+function archiverFlags(project, product, inputs, outputs) {
var args = [];
+ // Inputs.
+ var objectPaths = Cpp.collectLinkerObjectPaths(inputs);
+
var architecture = product.qbs.architecture;
- if (architecture === "mcs51") {
+ if (isMcsArchitecture(architecture) || isC166Architecture(architecture)) {
// Library creation command.
args.push("TRANSFER");
- var allObjectPaths = [];
- function addObjectPath(obj) {
- allObjectPaths.push(obj.filePath);
- }
-
// Inputs.
- if (inputs.obj)
- inputs.obj.map(function(obj) { addObjectPath(obj) });
-
- // Add all input objects as arguments.
- var adjusted = adjustPathsToWindowsSeparators(allObjectPaths);
- args = args.concat(adjusted.join(","));
+ if (objectPaths.length > 0)
+ args = args.concat(objectPaths.map(function(path) {
+ return FileInfo.toWindowsSeparators(path); }).join(","));
// Output.
- // Note: We need to wrap a output file name with quotes. Otherwise
- // the linker will ignore a specified file name.
- args.push("TO", '"' + FileInfo.toWindowsSeparators(outputs.staticlibrary[0].filePath) + '"');
- } else if (architecture === "arm") {
+ args.push("TO", FileInfo.toWindowsSeparators(outputs.staticlibrary[0].filePath));
+ } else if (isArmArchitecture(architecture)) {
// Note: The ARM archiver command line expect the output file
// first, and then a set of input objects.
@@ -757,8 +867,7 @@ function archiverFlags(project, product, input, outputs) {
args.push("--create", outputs.staticlibrary[0].filePath);
// Inputs.
- if (inputs.obj)
- args = args.concat(inputs.obj.map(function(obj) { return obj.filePath }));
+ args = args.concat(objectPaths);
// Debug information flag.
if (product.cpp.debugInformation)
@@ -768,48 +877,116 @@ function archiverFlags(project, product, input, outputs) {
return args;
}
+// The ARMCLANG compiler does not support generation
+// for the listing files:
+// * https://www.keil.com/support/docs/4152.htm
+// So, we generate the listing files from the object files
+// using the disassembler.
+function generateClangCompilerListing(project, product, inputs, outputs, input, output) {
+ if (isArmClangCompiler(input.cpp.compilerPath) && input.cpp.generateCompilerListingFiles) {
+ var args = disassemblerFlags(project, product, input, outputs, explicitlyDependsOn);
+ var disassemblerPath = input.cpp.disassemblerPath;
+ var cmd = new Command(disassemblerPath, args);
+ cmd.silent = true;
+ return cmd;
+ }
+}
+
+// The ARMCC compiler generates the listing files only in a short form,
+// e.g. to 'module.lst' instead of 'module.{c|cpp}.lst', that complicates
+// the auto-tests. Therefore we need to rename generated listing files
+// with correct unified names.
+function generateArmccCompilerListing(project, product, inputs, outputs, input, output) {
+ if (isArmCCCompiler(input.cpp.compilerPath) && input.cpp.generateCompilerListingFiles) {
+ var listingPath = FileInfo.path(outputs.lst[0].filePath);
+ var cmd = new JavaScriptCommand();
+ cmd.oldListing = FileInfo.joinPaths(listingPath, input.baseName + ".lst");
+ cmd.newListing = FileInfo.joinPaths(
+ listingPath, input.fileName + input.cpp.compilerListingSuffix);
+ cmd.silent = true;
+ cmd.sourceCode = function() { File.move(oldListing, newListing); };
+ return cmd;
+ }
+}
+
function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var cmds = [];
var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn);
var compilerPath = input.cpp.compilerPath;
var architecture = input.cpp.architecture;
var cmd = new Command(compilerPath, args);
cmd.description = "compiling " + input.fileName;
cmd.highlight = "compiler";
- cmd.maxExitCode = getMaxExitCode(architecture);
- filterStdOutput(cmd);
- return [cmd];
-}
+ cmd.jobPool = "compiler";
+ if (isMcsArchitecture(architecture)) {
+ cmd.maxExitCode = 1;
+ cmd.stdoutFilterFunction = filterMcsOutput;
+ } else if (isC166Architecture(architecture)) {
+ cmd.maxExitCode = 1;
+ cmd.stdoutFilterFunction = filterC166Output;
+ }
+ cmds.push(cmd);
+
+ cmd = generateClangCompilerListing(project, product, inputs, outputs, input, output);
+ if (cmd)
+ cmds.push(cmd);
+ cmd = generateArmccCompilerListing(project, product, inputs, outputs, input, output);
+ if (cmd)
+ cmds.push(cmd);
+
+ return cmds;
+}
function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
var args = assemblerFlags(project, product, input, outputs, explicitlyDependsOn);
var assemblerPath = input.cpp.assemblerPath;
+ var architecture = input.cpp.architecture;
var cmd = new Command(assemblerPath, args);
cmd.description = "assembling " + input.fileName;
cmd.highlight = "compiler";
- filterStdOutput(cmd);
+ cmd.jobPool = "assembler";
+ if (isMcsArchitecture(architecture)) {
+ cmd.maxExitCode = 1;
+ cmd.stdoutFilterFunction = filterMcsOutput;
+ } else if (isC166Architecture(architecture)) {
+ cmd.maxExitCode = 1;
+ cmd.stdoutFilterFunction = filterC166Output;
+ }
return [cmd];
}
function prepareLinker(project, product, inputs, outputs, input, output) {
var primaryOutput = outputs.application[0];
- var args = linkerFlags(project, product, input, outputs);
+ var args = linkerFlags(project, product, inputs, outputs);
var linkerPath = product.cpp.linkerPath;
var architecture = product.cpp.architecture;
var cmd = new Command(linkerPath, args);
cmd.description = "linking " + primaryOutput.fileName;
cmd.highlight = "linker";
- cmd.maxExitCode = getMaxExitCode(architecture);
- filterStdOutput(cmd);
+ cmd.jobPool = "linker";
+ if (isMcsArchitecture(architecture)) {
+ cmd.maxExitCode = 1;
+ cmd.stdoutFilterFunction = filterMcsOutput;
+ } else if (isC166Architecture(architecture)) {
+ cmd.maxExitCode = 1;
+ cmd.stdoutFilterFunction = filterC166Output;
+ }
return [cmd];
}
function prepareArchiver(project, product, inputs, outputs, input, output) {
- var args = archiverFlags(project, product, input, outputs);
+ var args = archiverFlags(project, product, inputs, outputs);
var archiverPath = product.cpp.archiverPath;
+ var architecture = product.cpp.architecture;
var cmd = new Command(archiverPath, args);
- cmd.description = "linking " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.highlight = "linker";
- filterStdOutput(cmd);
+ cmd.jobPool = "linker";
+ if (isMcsArchitecture(architecture)) {
+ cmd.stdoutFilterFunction = filterMcsOutput;
+ } else if (isC166Architecture(architecture)) {
+ cmd.stdoutFilterFunction = filterC166Output;
+ }
return [cmd];
}
diff --git a/share/qbs/modules/cpp/keil.qbs b/share/qbs/modules/cpp/keil.qbs
index 67ea5e675..5922ad4e2 100644
--- a/share/qbs/modules/cpp/keil.qbs
+++ b/share/qbs/modules/cpp/keil.qbs
@@ -28,15 +28,15 @@
**
****************************************************************************/
-import qbs 1.0
import qbs.File
import qbs.FileInfo
-import qbs.ModUtils
+import qbs.Host
import qbs.Probes
+import "cpp.js" as Cpp
import "keil.js" as KEIL
CppModule {
- condition: qbs.hostOS.contains("windows") && qbs.toolchain && qbs.toolchain.contains("keil")
+ condition: Host.os().includes("windows") && qbs.toolchain && qbs.toolchain.includes("keil")
Probes.BinaryProbe {
id: compilerPathProbe
@@ -52,6 +52,7 @@ CppModule {
}
qbs.architecture: keilProbe.found ? keilProbe.architecture : original
+ qbs.targetPlatform: "none"
compilerVersionMajor: keilProbe.versionMajor
compilerVersionMinor: keilProbe.versionMinor
@@ -61,45 +62,56 @@ CppModule {
compilerDefinesByLanguage: keilProbe.compilerDefinesByLanguage
compilerIncludePaths: keilProbe.includePaths
- property string toolchainInstallPath: compilerPathProbe.found
- ? compilerPathProbe.path : undefined
-
- property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : ""
+ toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined
/* Work-around for QtCreator which expects these properties to exist. */
property string cCompilerName: compilerName
property string cxxCompilerName: compilerName
- compilerName: KEIL.compilerName(qbs) + compilerExtension
+ compilerName: toolchainDetails.compilerName + compilerExtension
compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName)
- assemblerName: KEIL.assemblerName(qbs) + compilerExtension
+ assemblerName: toolchainDetails.assemblerName + compilerExtension
assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName)
- linkerName: KEIL.linkerName(qbs) + compilerExtension
+ linkerName: toolchainDetails.linkerName + compilerExtension
linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName)
- property string archiverName: KEIL.archiverName(qbs) + compilerExtension
+ property string archiverName: toolchainDetails.archiverName + compilerExtension
property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName)
- runtimeLibrary: "static"
+ property string disassemblerName: toolchainDetails.disassemblerName + compilerExtension
+ property string disassemblerPath: FileInfo.joinPaths(toolchainInstallPath, disassemblerName)
- staticLibrarySuffix: KEIL.staticLibrarySuffix(qbs)
- executableSuffix: KEIL.executableSuffix(qbs)
+ runtimeLibrary: "static"
- property string objectSuffix: KEIL.objectSuffix(qbs)
+ staticLibrarySuffix: ".lib"
+ executableSuffix: toolchainDetails.executableSuffix
+ objectSuffix: toolchainDetails.objectSuffix
+ linkerMapSuffix: toolchainDetails.linkerMapSuffix
- imageFormat: KEIL.imageFormat(qbs)
+ imageFormat: toolchainDetails.imageFormat
enableExceptions: false
enableRtti: false
+ defineFlag: "-D"
+ includeFlag: "-I"
+ systemIncludeFlag: "-I"
+ preincludeFlag: KEIL.preincludeFlag(compilerPath)
+ libraryDependencyFlag: ""
+ libraryPathFlag: "--userlibpath="
+ linkerScriptFlag: "--scatter"
+
+ toolchainDetails: KEIL.toolchainDetails(qbs)
+
+ knownArchitectures: ["arm", "c166", "mcs251", "mcs51"]
+
Rule {
id: assembler
inputs: ["asm"]
- outputFileTags: ["obj", "lst"]
- outputArtifacts: KEIL.compilerOutputArtifacts(
- input, input.cpp.generateAssemblerListingFiles)
+ outputFileTags: Cpp.assemblerOutputTags(generateAssemblerListingFiles)
+ outputArtifacts: Cpp.assemblerOutputArtifacts(input)
prepare: KEIL.prepareAssembler.apply(KEIL, arguments)
}
@@ -112,9 +124,8 @@ CppModule {
id: compiler
inputs: ["cpp", "c"]
auxiliaryInputs: ["hpp"]
- outputFileTags: ["obj", "lst"]
- outputArtifacts: KEIL.compilerOutputArtifacts(
- input, input.cpp.generateCompilerListingFiles)
+ outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles)
+ outputArtifacts: Cpp.compilerOutputArtifacts(input)
prepare: KEIL.prepareCompiler.apply(KEIL, arguments)
}
@@ -122,8 +133,9 @@ CppModule {
id: applicationLinker
multiplex: true
inputs: ["obj", "linkerscript"]
- outputFileTags: ["application", "mem_map"]
- outputArtifacts: KEIL.applicationLinkerOutputArtifacts(product)
+ inputsFromDependencies: ["staticlibrary"]
+ outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile)
+ outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product)
prepare: KEIL.prepareLinker.apply(KEIL, arguments)
}
@@ -132,8 +144,8 @@ CppModule {
multiplex: true
inputs: ["obj"]
inputsFromDependencies: ["staticlibrary"]
- outputFileTags: ["staticlibrary"]
- outputArtifacts: KEIL.staticLibraryLinkerOutputArtifacts(product)
+ outputFileTags: Cpp.staticLibraryLinkerOutputTags()
+ outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product)
prepare: KEIL.prepareArchiver.apply(KEIL, arguments)
}
}
diff --git a/share/qbs/modules/cpp/macos-gcc.qbs b/share/qbs/modules/cpp/macos-gcc.qbs
index 612b46ae8..c667bbc29 100644
--- a/share/qbs/modules/cpp/macos-gcc.qbs
+++ b/share/qbs/modules/cpp/macos-gcc.qbs
@@ -33,8 +33,8 @@ import qbs.Utilities
DarwinGCC {
priority: 1
- condition: qbs.targetOS.contains('macos') &&
- qbs.toolchain && qbs.toolchain.contains('gcc')
+ condition: qbs.targetOS.includes('macos') &&
+ qbs.toolchain && qbs.toolchain.includes('gcc')
targetSystem: "macosx" + (minimumMacosVersion || "")
diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js
index b67ab811f..1700c21b3 100644
--- a/share/qbs/modules/cpp/msvc.js
+++ b/share/qbs/modules/cpp/msvc.js
@@ -28,6 +28,7 @@
**
****************************************************************************/
+var Codesign = require("../codesign/codesign.js");
var Cpp = require("cpp.js");
var File = require("qbs.File");
var FileInfo = require("qbs.FileInfo");
@@ -35,6 +36,22 @@ var ModUtils = require("qbs.ModUtils");
var Utilities = require("qbs.Utilities");
var WindowsUtils = require("qbs.WindowsUtils");
+function effectiveLinkerPath(product, inputs) {
+ if (product.cpp.linkerMode === "automatic") {
+ var compiler = product.cpp.compilerPath;
+ if (compiler) {
+ if (inputs.obj || inputs.staticlibrary) {
+ console.log("Found C/C++ objects, using compiler as a linker for " + product.name);
+ return compiler;
+ }
+ }
+
+ console.log("Found no C-language objects, choosing system linker for " + product.name);
+ }
+
+ return product.cpp.linkerPath;
+}
+
function handleCpuFeatures(input, flags) {
if (!input.qbs.architecture)
return;
@@ -62,20 +79,54 @@ function hasCxx17Option(input)
{
// Probably this is not the earliest version to support the flag, but we have tested this one
// and it's a pain to find out the exact minimum.
- return Utilities.versionCompare(input.cpp.compilerVersion, "19.12.25831") >= 0
- || (input.qbs.toolchain.contains("clang-cl") && input.cpp.compilerVersionMajor >= 7);
+ return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 7)
+ || Utilities.versionCompare(input.cpp.compilerVersion, "19.12.25831") >= 0;
+}
+
+function hasZCplusPlusOption(input)
+{
+ // /Zc:__cplusplus is supported starting from Visual Studio 15.7
+ // Looks like closest MSVC version is 14.14.26428 (cl ver 19.14.26433)
+ // At least, this version is tested
+ // https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus
+ // clang-cl supports this option starting around-ish versions 8/9, but it
+ // ignores this option, so this doesn't really matter
+ // https://reviews.llvm.org/D45877
+ return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 9)
+ || Utilities.versionCompare(input.cpp.compilerVersion, "19.14.26433") >= 0;
}
-function addLanguageVersionFlag(input, args) {
+function hasCxx20Option(input)
+{
+ return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 13)
+ || Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30133.0") >= 0;
+}
+
+function hasCVerOption(input)
+{
+ return (input.qbs.toolchain.includes("clang-cl") && input.cpp.compilerVersionMajor >= 13)
+ || Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30138.0") >= 0;
+}
+
+function supportsExternalIncludesOption(input) {
+ if (input.qbs.toolchain.includes("clang-cl"))
+ return false; // Exclude clang-cl.
+ // This option was introcuded since MSVC 2017 v15.6 (aka _MSC_VER 19.13).
+ // But due to some MSVC bugs:
+ // * https://developercommunity.visualstudio.com/content/problem/181006/externali-include-paths-not-working.html
+ // this option has been fixed since MSVC 2017 update 9, v15.9 (aka _MSC_VER 19.16).
+ return Utilities.versionCompare(input.cpp.compilerVersion, "19.16") >= 0;
+}
+
+function addCxxLanguageVersionFlag(input, args) {
var cxxVersion = Cpp.languageVersion(input.cpp.cxxLanguageVersion,
- ["c++17", "c++14", "c++11", "c++98"], "C++");
+ ["c++23", "c++20", "c++17", "c++14", "c++11", "c++98"], "C++");
if (!cxxVersion)
return;
- // Visual C++ 2013, Update 3
- var hasStdOption = Utilities.versionCompare(input.cpp.compilerVersion, "18.00.30723") >= 0
- // or clang-cl
- || input.qbs.toolchain.contains("clang-cl");
+ // Visual C++ 2013, Update 3 or clang-cl
+ var hasStdOption = input.qbs.toolchain.includes("clang-cl")
+ || Utilities.versionCompare(input.cpp.compilerVersion, "18.00.30723") >= 0;
if (!hasStdOption)
return;
@@ -84,12 +135,42 @@ function addLanguageVersionFlag(input, args) {
flag = "/std:c++14";
else if (cxxVersion === "c++17" && hasCxx17Option(input))
flag = "/std:c++17";
+ else if (cxxVersion === "c++20" && hasCxx20Option(input))
+ flag = "/std:c++20";
else if (cxxVersion !== "c++11" && cxxVersion !== "c++98")
flag = "/std:c++latest";
if (flag)
args.push(flag);
}
+function addCLanguageVersionFlag(input, args) {
+ var cVersion = Cpp.languageVersion(input.cpp.cLanguageVersion,
+ ["c17", "c11"], "C");
+ if (!cVersion)
+ return;
+
+ var hasStdOption = hasCVerOption(input);
+ if (!hasStdOption)
+ return;
+
+ var flag;
+ if (cVersion === "c17")
+ flag = "/std:c17";
+ else if (cVersion === "c11")
+ flag = "/std:c11";
+ if (flag)
+ args.push(flag);
+}
+
+function handleClangClArchitectureFlags(product, architecture, flags) {
+ if (product.qbs.toolchain.includes("clang-cl")) {
+ if (architecture === "x86")
+ flags.push("-m32");
+ else if (architecture === "x86_64")
+ flags.push("-m64");
+ }
+}
+
function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
var i;
var debugInformation = input.cpp.debugInformation;
@@ -99,7 +180,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
// Determine which C-language we're compiling
var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(Object.keys(outputs)));
- if (!["c", "cpp"].contains(tag))
+ if (!["c", "cpp"].includes(tag))
throw ("unsupported source language");
var enableExceptions = input.cpp.enableExceptions;
@@ -135,6 +216,8 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
break;
}
+ handleClangClArchitectureFlags(product, input.cpp.architecture, args);
+
if (debugInformation) {
if (product.cpp.separateDebugInformation)
args.push('/Zi');
@@ -150,6 +233,8 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
args.push(rtl);
}
+ args = args.concat(Cpp.collectMiscDriverArguments(product));
+
// warnings:
var warningLevel = input.cpp.warningLevel;
if (warningLevel === 'none')
@@ -158,24 +243,33 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
args.push('/Wall')
if (input.cpp.treatWarningsAsErrors)
args.push('/WX')
- var allIncludePaths = [];
- var includePaths = input.cpp.includePaths;
- if (includePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(includePaths);
- var systemIncludePaths = input.cpp.systemIncludePaths;
- if (systemIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths);
- for (i in allIncludePaths)
- args.push('/I' + FileInfo.toWindowsSeparators(allIncludePaths[i]))
- var allDefines = [];
- var platformDefines = input.cpp.platformDefines;
- if (platformDefines)
- allDefines = allDefines.uniqueConcat(platformDefines);
- var defines = input.cpp.defines;
- if (defines)
- allDefines = allDefines.uniqueConcat(defines);
- for (i in allDefines)
- args.push('/D' + allDefines[i].replace(/%/g, "%%"));
+
+ var includePaths = Cpp.collectIncludePaths(input);
+ args = args.concat([].uniqueConcat(includePaths).map(function(path) {
+ return input.cpp.includeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+
+ var includeFlag = input.qbs.toolchain.includes("clang-cl")
+ ? input.cpp.systemIncludeFlag : input.cpp.includeFlag;
+ if (!input.qbs.toolchain.includes("clang-cl")) {
+ if (supportsExternalIncludesOption(input)) {
+ args.push("/experimental:external");
+ var enforcesSlashW =
+ Utilities.versionCompare(input.cpp.compilerVersion, "19.29.30037") >= 0
+ if (enforcesSlashW)
+ args.push("/external:W0")
+ includeFlag = input.cpp.systemIncludeFlag;
+ }
+ }
+ var systemIncludePaths = Cpp.collectSystemIncludePaths(input);
+ args = args.concat([].uniqueConcat(systemIncludePaths).map(function(path) {
+ return includeFlag + FileInfo.toWindowsSeparators(path);
+ }));
+
+ var defines = Cpp.collectDefines(input);
+ args = args.concat([].uniqueConcat(defines).map(function(define) {
+ return input.cpp.defineFlag + define.replace(/%/g, "%%");
+ }));
var minimumWindowsVersion = product.cpp.minimumWindowsVersion;
if (minimumWindowsVersion) {
@@ -183,7 +277,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
if (hexVersion) {
var versionDefs = [ 'WINVER', '_WIN32_WINNT', '_WIN32_WINDOWS' ];
for (i in versionDefs) {
- args.push('/D' + versionDefs[i] + '=' + hexVersion);
+ args.push(input.cpp.defineFlag + versionDefs[i] + '=' + hexVersion);
}
}
}
@@ -191,21 +285,29 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
if (product.cpp.debugInformation && product.cpp.separateDebugInformation)
args.push("/Fd" + product.targetName + ".cl" + product.cpp.debugInfoSuffix);
+ if (input.cpp.generateCompilerListingFiles)
+ args.push("/Fa" + FileInfo.toWindowsSeparators(outputs.lst[0].filePath));
+
+ if (input.cpp.enableCxxLanguageMacro && hasZCplusPlusOption(input))
+ args.push("/Zc:__cplusplus");
+
var objectMap = outputs.obj || outputs.intermediate_obj
var objOutput = objectMap ? objectMap[0] : undefined
args.push('/Fo' + FileInfo.toWindowsSeparators(objOutput.filePath))
args.push(FileInfo.toWindowsSeparators(input.filePath))
- var prefixHeaders = product.cpp.prefixHeaders;
- for (i in prefixHeaders)
- args.push("/FI" + FileInfo.toWindowsSeparators(prefixHeaders[i]));
+ var preincludePaths = Cpp.collectPreincludePaths(input);
+ args = args.concat([].uniqueConcat(preincludePaths).map(function(path) {
+ return input.cpp.preincludeFlag + FileInfo.toWindowsSeparators(path);
+ }));
// Language
if (tag === "cpp") {
args.push("/TP");
- addLanguageVersionFlag(input, args);
+ addCxxLanguageVersionFlag(input, args);
} else if (tag === "c") {
args.push("/TC");
+ addCLanguageVersionFlag(input, args);
}
// Whether we're compiling a precompiled header or normal source file
@@ -213,7 +315,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
var pchInputs = explicitlyDependsOn[tag + "_pch"];
if (pchOutput) {
// create PCH
- if (input.qbs.toolchain.contains("clang-cl")) {
+ if (input.qbs.toolchain.includes("clang-cl")) {
// clang-cl does not support /Yc flag without filename
args.push("/Yc" + FileInfo.toWindowsSeparators(input.filePath));
// clang-cl complains when pch file is not included
@@ -243,10 +345,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
}
}
- args = args.concat(ModUtils.moduleProperty(input, 'platformFlags'),
- ModUtils.moduleProperty(input, 'flags'),
- ModUtils.moduleProperty(input, 'platformFlags', tag),
- ModUtils.moduleProperty(input, 'flags', tag));
+ args = args.concat(Cpp.collectMiscCompilerArguments(input, tag));
var compilerPath = product.cpp.compilerPath;
var wrapperArgs = product.cpp.compilerWrapper;
@@ -272,74 +371,10 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli
return [cmd];
}
-function collectLibraryDependencies(product) {
- var seen = {};
- var result = [];
-
- function addFilePath(filePath, wholeArchive, productName) {
- result.push({ filePath: filePath, wholeArchive: wholeArchive, productName: productName });
- }
-
- function addArtifactFilePaths(dep, artifacts) {
- if (!artifacts)
- return;
- var artifactFilePaths = artifacts.map(function(a) { return a.filePath; });
- var wholeArchive = dep.parameters.cpp && dep.parameters.cpp.linkWholeArchive;
- var artifactsAreImportLibs = artifacts.length > 0
- && artifacts[0].fileTags.contains("dynamiclibrary_import");
- for (var i = 0; i < artifactFilePaths.length; ++i) {
- addFilePath(artifactFilePaths[i], wholeArchive,
- artifactsAreImportLibs ? dep.name : undefined);
- }
- }
-
- function addExternalLibs(obj) {
- if (!obj.cpp)
- return;
- function ensureArray(a) {
- return Array.isArray(a) ? a : [];
- }
- function sanitizedModuleListProperty(obj, moduleName, propertyName) {
- return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName));
- }
- var externalLibs = [].concat(
- sanitizedModuleListProperty(obj, "cpp", "staticLibraries"),
- sanitizedModuleListProperty(obj, "cpp", "dynamicLibraries"));
- externalLibs.forEach(function (libName) {
- if (!libName.match(/\.lib$/i) && !libName.startsWith('@'))
- libName += ".lib";
- addFilePath(libName, false);
- });
- }
-
- function traverse(dep) {
- if (seen.hasOwnProperty(dep.name))
- return;
- seen[dep.name] = true;
-
- if (dep.parameters.cpp && dep.parameters.cpp.link === false)
- return;
-
- var staticLibraryArtifacts = dep.artifacts["staticlibrary"];
- var dynamicLibraryArtifacts = staticLibraryArtifacts
- ? null : dep.artifacts["dynamiclibrary_import"];
- if (staticLibraryArtifacts) {
- dep.dependencies.forEach(traverse);
- addArtifactFilePaths(dep, staticLibraryArtifacts);
- addExternalLibs(dep);
- } else if (dynamicLibraryArtifacts) {
- addArtifactFilePaths(dep, dynamicLibraryArtifacts);
- }
- }
-
- product.dependencies.forEach(traverse);
- addExternalLibs(product);
- return result;
-}
-
function linkerSupportsWholeArchive(product)
{
- return Utilities.versionCompare(product.cpp.compilerVersion, "19.0.24215.1") >= 0
+ return product.qbs.toolchainType.includes("clang-cl") ||
+ Utilities.versionCompare(product.cpp.compilerVersion, "19.0.24215.1") >= 0
}
function handleDiscardProperty(product, flags) {
@@ -355,49 +390,76 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
var linkDLL = (outputs.dynamiclibrary ? true : false)
var primaryOutput = (linkDLL ? outputs.dynamiclibrary[0] : outputs.application[0])
var debugInformation = product.cpp.debugInformation;
- var additionalManifestInputs = Array.prototype.map.call(inputs["native.pe.manifest"],
+ var additionalManifestInputs = Array.prototype.map.call(inputs["native.pe.manifest"] || [],
+ function (a) {
+ return a.filePath;
+ });
+ var moduleDefinitionInputs = Array.prototype.map.call(inputs["def"] || [],
function (a) {
return a.filePath;
});
var generateManifestFiles = !linkDLL && product.cpp.generateManifestFile;
- var canEmbedManifest = (product.cpp.compilerVersionMajor >= 17); // VS 2012
+ var useClangCl = product.qbs.toolchain.includes("clang-cl");
+ var canEmbedManifest = useClangCl || product.cpp.compilerVersionMajor >= 17 // VS 2012
+
+ var linkerPath = effectiveLinkerPath(product, inputs);
+ var useCompilerDriver = linkerPath === product.cpp.compilerPath;
+ // args variable is built as follows:
+ // [linkerWrapper] linkerPath /nologo [driverFlags driverLinkerFlags]
+ // allInputs libDeps [/link] linkerArgs
+ var args = []
+
+ if (useCompilerDriver) {
+ args.push('/nologo');
+ args = args.concat(Cpp.collectMiscDriverArguments(product),
+ Cpp.collectMiscLinkerArguments(product));
+ }
+
+ var allInputs = [].concat(Cpp.collectLinkerObjectPaths(inputs),
+ Cpp.collectResourceObjectPaths(inputs));
+ args = args.concat([].uniqueConcat(allInputs).map(function(path) {
+ return FileInfo.toWindowsSeparators(path);
+ }));
- var args = ['/nologo']
+ var linkerArgs = ['/nologo']
if (linkDLL) {
- args.push('/DLL');
- args.push('/IMPLIB:' + FileInfo.toWindowsSeparators(outputs.dynamiclibrary_import[0].filePath));
+ linkerArgs.push('/DLL');
+ linkerArgs.push('/IMPLIB:' + FileInfo.toWindowsSeparators(outputs.dynamiclibrary_import[0].filePath));
}
if (debugInformation) {
- args.push("/DEBUG");
+ linkerArgs.push("/DEBUG");
var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll;
if (debugInfo)
- args.push("/PDB:" + debugInfo[0].fileName);
+ linkerArgs.push("/PDB:" + debugInfo[0].fileName);
} else {
- args.push('/INCREMENTAL:NO')
+ linkerArgs.push('/INCREMENTAL:NO')
}
switch (product.qbs.architecture) {
case "x86":
- args.push("/MACHINE:X86");
+ linkerArgs.push("/MACHINE:X86");
break;
case "x86_64":
- args.push("/MACHINE:X64");
+ linkerArgs.push("/MACHINE:X64");
break;
case "ia64":
- args.push("/MACHINE:IA64");
+ linkerArgs.push("/MACHINE:IA64");
break;
case "armv7":
- args.push("/MACHINE:ARM");
+ linkerArgs.push("/MACHINE:ARM");
break;
case "arm64":
- args.push("/MACHINE:ARM64");
+ linkerArgs.push("/MACHINE:ARM64");
break;
}
+ if (useCompilerDriver)
+ handleClangClArchitectureFlags(product, product.qbs.architecture, args);
+
var requireAppContainer = product.cpp.requireAppContainer;
if (requireAppContainer !== undefined)
- args.push("/APPCONTAINER" + (requireAppContainer ? "" : ":NO"));
+ linkerArgs.push("/APPCONTAINER" + (requireAppContainer ? "" : ":NO"));
var minimumWindowsVersion = product.cpp.minimumWindowsVersion;
var subsystemSwitch = undefined;
@@ -407,26 +469,29 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
subsystemSwitch = product.consoleApplication === false ? '/SUBSYSTEM:WINDOWS' : '/SUBSYSTEM:CONSOLE';
}
+ var useLldLink = useCompilerDriver && product.cpp.linkerVariant === "lld"
+ || !useCompilerDriver && product.cpp.linkerName === "lld-link.exe";
if (minimumWindowsVersion) {
var subsystemVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion,
'subsystem');
if (subsystemVersion) {
subsystemSwitch += ',' + subsystemVersion;
- if (product.cpp.linkerName !== "lld-link.exe") // llvm linker does not support /OSVERSION
- args.push('/OSVERSION:' + subsystemVersion);
+ // llvm linker does not support /OSVERSION
+ if (!useLldLink)
+ linkerArgs.push('/OSVERSION:' + subsystemVersion);
}
}
if (subsystemSwitch)
- args.push(subsystemSwitch);
+ linkerArgs.push(subsystemSwitch);
var linkerOutputNativeFilePath = FileInfo.toWindowsSeparators(primaryOutput.filePath);
var manifestFileNames = [];
if (generateManifestFiles) {
if (canEmbedManifest) {
- args.push("/MANIFEST:embed");
+ linkerArgs.push("/MANIFEST:embed");
additionalManifestInputs.forEach(function (manifestFileName) {
- args.push("/MANIFESTINPUT:" + manifestFileName);
+ linkerArgs.push("/MANIFESTINPUT:" + manifestFileName);
});
} else {
linkerOutputNativeFilePath
@@ -435,20 +500,19 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
+ primaryOutput.fileName);
var manifestFileName = linkerOutputNativeFilePath + ".manifest";
- args.push('/MANIFEST', '/MANIFESTFILE:' + manifestFileName);
+ linkerArgs.push('/MANIFEST', '/MANIFESTFILE:' + manifestFileName);
manifestFileNames = [manifestFileName].concat(additionalManifestInputs);
}
}
- var allInputs = inputs.obj || [];
- for (i in allInputs) {
- var fileName = FileInfo.toWindowsSeparators(allInputs[i].filePath)
- args.push(fileName)
- }
+ if (moduleDefinitionInputs.length === 1)
+ linkerArgs.push("/DEF:" + moduleDefinitionInputs[0]);
+ else if (moduleDefinitionInputs.length > 1)
+ throw new Error("Only one '.def' file can be specified for linking");
var wholeArchiveSupported = linkerSupportsWholeArchive(product);
var wholeArchiveRequested = false;
- var libDeps = collectLibraryDependencies(product);
+ var libDeps = Cpp.collectLibraryDependencies(product);
var prevLib;
for (i = 0; i < libDeps.length; ++i) {
var dep = libDeps[i];
@@ -456,8 +520,16 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
if (lib === prevLib)
continue;
prevLib = lib;
- args.push((wholeArchiveSupported && dep.wholeArchive ? "/WHOLEARCHIVE:" : "")
- + FileInfo.toWindowsSeparators(lib));
+
+ if (wholeArchiveSupported && dep.wholeArchive) {
+ // need to pass libraries to the driver to avoid "no input files" error if no object
+ // files are specified; thus libraries are duplicated when using "WHOLEARCHIVE"
+ if (useCompilerDriver && allInputs.length === 0)
+ args.push(FileInfo.toWindowsSeparators(lib));
+ linkerArgs.push("/WHOLEARCHIVE:" + FileInfo.toWindowsSeparators(lib));
+ } else {
+ args.push(FileInfo.toWindowsSeparators(lib));
+ }
if (dep.wholeArchive)
wholeArchiveRequested = true;
}
@@ -468,25 +540,31 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
}
if (product.cpp.entryPoint)
- args.push("/ENTRY:" + product.cpp.entryPoint);
+ linkerArgs.push("/ENTRY:" + product.cpp.entryPoint);
- if (outputs.application && product.cpp.generateLinkerMapFile)
- args.push("/MAP:" + outputs.mem_map[0].filePath);
-
- args.push('/OUT:' + linkerOutputNativeFilePath)
- var libraryPaths = product.cpp.libraryPaths;
- if (libraryPaths)
- libraryPaths = [].uniqueConcat(libraryPaths);
- for (i in libraryPaths) {
- args.push('/LIBPATH:' + FileInfo.toWindowsSeparators(libraryPaths[i]))
+ if (outputs.application && product.cpp.generateLinkerMapFile) {
+ if (useLldLink)
+ linkerArgs.push("/lldmap:" + outputs.mem_map[0].filePath);
+ else
+ linkerArgs.push("/MAP:" + outputs.mem_map[0].filePath);
}
- handleDiscardProperty(product, args);
+
+ if (useCompilerDriver)
+ args.push('/Fe' + linkerOutputNativeFilePath);
+ else
+ linkerArgs.push('/OUT:' + linkerOutputNativeFilePath);
+
+ var libraryPaths = Cpp.collectLibraryPaths(product);
+ linkerArgs = linkerArgs.concat([].uniqueConcat(libraryPaths).map(function(path) {
+ return product.cpp.libraryPathFlag + FileInfo.toWindowsSeparators(path);
+ }));
+
+ handleDiscardProperty(product, linkerArgs);
var linkerFlags = product.cpp.platformLinkerFlags.concat(product.cpp.linkerFlags);
- args = args.concat(linkerFlags);
+ linkerArgs = linkerArgs.concat(linkerFlags);
if (product.cpp.allowUnresolvedSymbols)
- args.push("/FORCE:UNRESOLVED");
+ linkerArgs.push("/FORCE:UNRESOLVED");
- var linkerPath = product.cpp.linkerPath;
var wrapperArgs = product.cpp.linkerWrapper;
if (wrapperArgs && wrapperArgs.length > 0) {
args.unshift(linkerPath);
@@ -509,6 +587,10 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
}
};
commands.push(warningCmd);
+
+ if (linkerArgs.length !== 0)
+ args = args.concat(useCompilerDriver ? ['/link'] : []).concat(linkerArgs);
+
var cmd = new Command(linkerPath, args)
cmd.description = 'linking ' + primaryOutput.fileName;
cmd.highlight = 'linker';
@@ -516,9 +598,13 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
cmd.relevantEnvironmentVariables = ["LINK", "_LINK_", "LIB", "TMP"];
cmd.workingDirectory = FileInfo.path(primaryOutput.filePath)
cmd.responseFileUsagePrefix = '@';
+ cmd.responseFileSeparator = useCompilerDriver ? ' ' : '\n';
cmd.stdoutFilterFunction = function(output) {
res = output.replace(/^.*performing full link.*\s*/, "");
- return res.replace(/^ *Creating library.*\r\n$/, "");
+ res = res.replace(/^ *Creating library.*\s*/, "");
+ res = res.replace(/^\s*Generating code\s*/, "");
+ res = res.replace(/^\s*Finished generating code\s*/, "");
+ return res;
};
commands.push(cmd);
@@ -542,6 +628,12 @@ function prepareLinker(project, product, inputs, outputs, input, output) {
commands.push(cmd);
}
+ if (product.cpp.shouldSignArtifacts) {
+ Array.prototype.push.apply(
+ commands, Codesign.prepareSigntool(
+ project, product, inputs, outputs, input, output));
+ }
+
return commands;
}
diff --git a/share/qbs/modules/cpp/qnx-qcc.qbs b/share/qbs/modules/cpp/qnx-qcc.qbs
index a39a6a998..69288ddd8 100644
--- a/share/qbs/modules/cpp/qnx-qcc.qbs
+++ b/share/qbs/modules/cpp/qnx-qcc.qbs
@@ -33,8 +33,8 @@ import qbs.FileInfo
UnixGCC {
Depends { name: "qnx" }
- condition: qbs.targetOS.contains("qnx") &&
- qbs.toolchain && qbs.toolchain.contains("qcc")
+ condition: qbs.targetOS.includes("qnx") &&
+ qbs.toolchain && qbs.toolchain.includes("qcc")
priority: 1
distributionIncludePaths: FileInfo.joinPaths(qnx.targetDir, "usr", "include")
@@ -79,7 +79,7 @@ UnixGCC {
toolchainPrefix: target + "-"
- targetVendor: ["x86", "x86_64"].contains(qbs.architecture) ? "pc" : base
+ targetVendor: ["x86", "x86_64"].includes(qbs.architecture) ? "pc" : base
targetSystem: "nto"
targetAbi: "qnx" + qnx.version + (qnxTargetArchName === "armv7le" ? "eabi" : "")
diff --git a/share/qbs/modules/cpp/sdcc.js b/share/qbs/modules/cpp/sdcc.js
index 70d0506b9..49da8a715 100644
--- a/share/qbs/modules/cpp/sdcc.js
+++ b/share/qbs/modules/cpp/sdcc.js
@@ -28,10 +28,12 @@
**
****************************************************************************/
+var BinaryFile = require("qbs.BinaryFile");
var Cpp = require("cpp.js");
var Environment = require("qbs.Environment");
var File = require("qbs.File");
var FileInfo = require("qbs.FileInfo");
+var Host = require("qbs.Host");
var ModUtils = require("qbs.ModUtils");
var PathTools = require("qbs.PathTools");
var Process = require("qbs.Process");
@@ -40,34 +42,24 @@ var TextFile = require("qbs.TextFile");
var Utilities = require("qbs.Utilities");
var WindowsUtils = require("qbs.WindowsUtils");
-function compilerName(qbs) {
- return "sdcc";
-}
-
-function assemblerName(qbs) {
- switch (qbs.architecture) {
- case "mcs51":
- return "sdas8051";
- case "stm8":
- return "sdasstm8";
- }
- throw "Unable to deduce assembler name for unsupported architecture: '"
- + qbs.architecture + "'";
-}
-
-function linkerName(qbs) {
- switch (qbs.architecture) {
- case "mcs51":
- return "sdld";
- case "stm8":
- return "sdldstm8";
+function toolchainDetails(qbs) {
+ var architecture = qbs.architecture;
+ if (architecture === "mcs51") {
+ return {
+ "assemblerName": "sdas8051",
+ "linkerName": "sdld"
+ }
+ } else if (architecture === "stm8") {
+ return {
+ "assemblerName": "sdasstm8",
+ "linkerName": "sdldstm8"
+ }
+ } else if (architecture === "hcs8") {
+ return {
+ "assemblerName": "sdas6808",
+ "linkerName": "sdld6808"
+ }
}
- throw "Unable to deduce linker name for unsupported architecture: '"
- + qbs.architecture + "'";
-}
-
-function archiverName(qbs) {
- return "sdar";
}
function targetArchitectureFlag(architecture) {
@@ -75,6 +67,8 @@ function targetArchitectureFlag(architecture) {
return "-mmcs51";
if (architecture === "stm8")
return "-mstm8";
+ if (architecture === "hcs8")
+ return "-mhc08";
}
function guessArchitecture(macros) {
@@ -82,6 +76,8 @@ function guessArchitecture(macros) {
return "mcs51";
if (macros["__SDCC_stm8"] === "1")
return "stm8";
+ if (macros["__SDCC_hc08"] === "1")
+ return "hcs8";
}
function guessEndianness(macros) {
@@ -90,10 +86,20 @@ function guessEndianness(macros) {
}
function guessVersion(macros) {
- return { major: parseInt(macros["__SDCC_VERSION_MAJOR"], 10),
- minor: parseInt(macros["__SDCC_VERSION_MINOR"], 10),
- patch: parseInt(macros["__SDCC_VERSION_PATCH"], 10),
- found: macros["SDCC"] }
+ if ("__SDCC_VERSION_MAJOR" in macros
+ && "__SDCC_VERSION_MINOR" in macros
+ && "__SDCC_VERSION_PATCH" in macros) {
+ return { major: parseInt(macros["__SDCC_VERSION_MAJOR"], 10),
+ minor: parseInt(macros["__SDCC_VERSION_MINOR"], 10),
+ patch: parseInt(macros["__SDCC_VERSION_PATCH"], 10) }
+ } else if ("__SDCC" in macros) {
+ var versions = macros["__SDCC"].split("_");
+ if (versions.length === 3) {
+ return { major: parseInt(versions[0], 10),
+ minor: parseInt(versions[1], 10),
+ patch: parseInt(versions[2], 10) };
+ }
+ }
}
function dumpMacros(compilerFilePath, architecture) {
@@ -107,12 +113,7 @@ function dumpMacros(compilerFilePath, architecture) {
var p = new Process();
p.exec(compilerFilePath, args, true);
- var map = {};
- p.readStdOut().trim().split(/\r?\n/g).map(function (line) {
- var parts = line.split(" ", 3);
- map[parts[1]] = parts[2];
- });
- return map;
+ return Cpp.extractMacros(p.readStdOut());
}
function dumpDefaultPaths(compilerFilePath, architecture) {
@@ -137,7 +138,8 @@ function dumpDefaultPaths(compilerFilePath, architecture) {
|| line.startsWith("libpath:")) {
addIncludePaths = false;
} else if (addIncludePaths) {
- includePaths.push(line);
+ if (File.exists(line))
+ includePaths.push(line);
}
}
@@ -163,142 +165,69 @@ function escapeLinkerFlags(product, linkerFlags) {
return ["-Wl " + linkerFlags.join(",")];
}
-function collectLibraryDependencies(product) {
- var seen = {};
- var result = [];
-
- function addFilePath(filePath) {
- result.push({ filePath: filePath });
- }
-
- function addArtifactFilePaths(dep, artifacts) {
- if (!artifacts)
- return;
- var artifactFilePaths = artifacts.map(function(a) { return a.filePath; });
- artifactFilePaths.forEach(addFilePath);
- }
-
- function addExternalStaticLibs(obj) {
- if (!obj.cpp)
- return;
- function ensureArray(a) {
- return Array.isArray(a) ? a : [];
- }
- function sanitizedModuleListProperty(obj, moduleName, propertyName) {
- return ensureArray(ModUtils.sanitizedModuleProperty(obj, moduleName, propertyName));
- }
- var externalLibs = [].concat(
- sanitizedModuleListProperty(obj, "cpp", "staticLibraries"));
- var staticLibrarySuffix = obj.moduleProperty("cpp", "staticLibrarySuffix");
- externalLibs.forEach(function(staticLibraryName) {
- if (!staticLibraryName.endsWith(staticLibrarySuffix))
- staticLibraryName += staticLibrarySuffix;
- addFilePath(staticLibraryName);
- });
- }
-
- function traverse(dep) {
- if (seen.hasOwnProperty(dep.name))
- return;
- seen[dep.name] = true;
-
- if (dep.parameters.cpp && dep.parameters.cpp.link === false)
- return;
-
- var staticLibraryArtifacts = dep.artifacts["staticlibrary"];
- if (staticLibraryArtifacts) {
- dep.dependencies.forEach(traverse);
- addArtifactFilePaths(dep, staticLibraryArtifacts);
- addExternalStaticLibs(dep);
- }
- }
+function escapePreprocessorFlags(preprocessorFlags) {
+ if (!preprocessorFlags || preprocessorFlags.length === 0)
+ return preprocessorFlags;
+ return ["-Wp " + preprocessorFlags.join(",")];
+}
- product.dependencies.forEach(traverse);
- addExternalStaticLibs(product);
- return result;
+// We need to use the asm_adb, asm_src, asm_sym and rst_data
+// artifacts without of any conditions. Because SDCC always generates
+// it (and seems, this behavior can not be disabled for SDCC).
+function extraCompilerOutputTags() {
+ return ["asm_adb", "asm_src", "asm_sym", "rst_data"];
}
-function compilerOutputArtifacts(input) {
- var obj = {
- fileTags: ["obj"],
- filePath: Utilities.getHash(input.baseDir) + "/"
- + input.fileName + input.cpp.objectSuffix
- };
+// We need to use the lk_cmd, and mem_summary artifacts without
+// of any conditions. Because SDCC always generates
+// it (and seems, this behavior can not be disabled for SDCC).
+function extraApplicationLinkerOutputTags() {
+ return ["lk_cmd", "mem_summary"];
+}
- // We need to use the asm_adb, lst, asm_src, asm_sym and rst_data
- // artifacts without of any conditions. Because SDCC always generates
- // it (and seems, this behavior can not be disabled for SDCC).
- var asm_adb = {
+// We need to use the asm_adb, asm_src, asm_sym and rst_data
+// artifacts without of any conditions. Because SDCC always generates
+// it (and seems, this behavior can not be disabled for SDCC).
+function extraCompilerOutputArtifacts(input) {
+ return [{
fileTags: ["asm_adb"],
filePath: Utilities.getHash(input.baseDir) + "/"
- + input.fileName + ".adb"
- };
- var lst = {
- fileTags: ["lst"],
- filePath: Utilities.getHash(input.baseDir) + "/"
- + input.fileName + ".lst"
- };
- var asm_src = {
+ + input.fileName + ".adb"
+ }, {
fileTags: ["asm_src"],
filePath: Utilities.getHash(input.baseDir) + "/"
- + input.fileName + ".asm"
- };
- var asm_sym = {
+ + input.fileName + ".asm"
+ }, {
fileTags: ["asm_sym"],
filePath: Utilities.getHash(input.baseDir) + "/"
- + input.fileName + ".sym"
- };
- var rst_data = {
+ + input.fileName + ".sym"
+ }, {
fileTags: ["rst_data"],
filePath: Utilities.getHash(input.baseDir) + "/"
- + input.fileName + ".rst"
- };
- return [obj, asm_adb, lst, asm_src, asm_sym, rst_data];
+ + input.fileName + ".rst"
+ }];
}
-function applicationLinkerOutputArtifacts(product) {
- var app = {
- fileTags: ["application"],
- filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- PathTools.applicationFilePath(product))
- };
- var lk_cmd = {
+// We need to use the lk_cmd, and mem_summary artifacts without
+// of any conditions. Because SDCC always generates
+// it (and seems, this behavior can not be disabled for SDCC).
+function extraApplicationLinkerOutputArtifacts(product) {
+ return [{
fileTags: ["lk_cmd"],
filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- product.targetName + ".lk")
- };
- var mem_summary = {
+ product.destinationDirectory,
+ product.targetName + ".lk")
+ }, {
fileTags: ["mem_summary"],
filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- product.targetName + ".mem")
- };
- var mem_map = {
- fileTags: ["mem_map"],
- filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- product.targetName + ".map")
- };
- return [app, lk_cmd, mem_summary, mem_map]
-}
-
-function staticLibraryLinkerOutputArtifacts(product) {
- var staticLib = {
- fileTags: ["staticlibrary"],
- filePath: FileInfo.joinPaths(
- product.destinationDirectory,
- PathTools.staticLibraryFilePath(product))
- };
- return [staticLib]
+ product.destinationDirectory,
+ product.targetName + ".mem")
+ }];
}
function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
- // Determine which C-language we"re compiling.
- var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
-
var args = [];
+ var escapablePreprocessorFlags = [];
// Input.
args.push(input.filePath);
@@ -307,29 +236,19 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
args.push("-c");
args.push("-o", outputs.obj[0].filePath);
+ // Prefix headers.
+ escapablePreprocessorFlags = escapablePreprocessorFlags.concat(
+ Cpp.collectPreincludePathsArguments(input));
+
// Defines.
- var allDefines = [];
- var platformDefines = input.cpp.platformDefines;
- if (platformDefines)
- allDefines = allDefines.uniqueConcat(platformDefines);
- var defines = input.cpp.defines;
- if (defines)
- allDefines = allDefines.uniqueConcat(defines);
- args = args.concat(allDefines.map(function(define) { return "-D" + define }));
+ args = args.concat(Cpp.collectDefinesArguments(input));
// Includes.
- var allIncludePaths = [];
- var includePaths = input.cpp.includePaths;
- if (includePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(includePaths);
- var systemIncludePaths = input.cpp.systemIncludePaths;
- if (systemIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths);
- var compilerIncludePaths = input.cpp.compilerIncludePaths;
- if (compilerIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths);
- args = args.concat(allIncludePaths.map(function(include) { return "-I" + include }));
+ args = args.concat(Cpp.collectIncludePathsArguments(input));
+ escapablePreprocessorFlags = escapablePreprocessorFlags.concat(
+ Cpp.collectSystemIncludePathsArguments(input));
+ // Target MCU flag.
var targetFlag = targetArchitectureFlag(input.cpp.architecture);
if (targetFlag)
args.push(targetFlag);
@@ -352,56 +271,56 @@ function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
}
// Warning level flags.
- if (input.cpp.warningLevel === "none")
+ var warnings = input.cpp.warningLevel
+ if (warnings === "none") {
args.push("--less-pedantic");
+ escapablePreprocessorFlags.push("-w");
+ } else if (warnings === "all") {
+ escapablePreprocessorFlags.push("-Wall");
+ }
if (input.cpp.treatWarningsAsErrors)
args.push("--Werror");
+ // Determine which C-language we"re compiling.
+ var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
+
// C language version flags.
if (tag === "c") {
- var knownValues = ["c11", "c89"];
+ var knownValues = ["c2x", "c17", "c11", "c99", "c89"];
var cLanguageVersion = Cpp.languageVersion(
input.cpp.cLanguageVersion, knownValues, "C");
switch (cLanguageVersion) {
+ case "c17":
+ cLanguageVersion = "c11";
+ // fall through
case "c89":
- args.push("--std-c89");
- break;
+ case "c99":
case "c11":
- args.push("--std-c11");
+ case "c2x":
+ args.push("--std-" + cLanguageVersion);
break;
}
}
- // Misc flags.
- args = args.concat(ModUtils.moduleProperty(input, "platformFlags"),
- ModUtils.moduleProperty(input, "flags"),
- ModUtils.moduleProperty(input, "platformFlags", tag),
- ModUtils.moduleProperty(input, "flags", tag),
- ModUtils.moduleProperty(input, "driverFlags", tag));
+ var escapedPreprocessorFlags = escapePreprocessorFlags(escapablePreprocessorFlags);
+ if (escapedPreprocessorFlags)
+ Array.prototype.push.apply(args, escapedPreprocessorFlags);
+ // Misc flags.
+ args = args.concat(Cpp.collectMiscCompilerArguments(input, tag),
+ Cpp.collectMiscDriverArguments(input));
return args;
}
function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
- // Determine which C-language we"re compiling
- var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
-
var args = [];
// Includes.
- var allIncludePaths = [];
- var systemIncludePaths = input.cpp.systemIncludePaths;
- if (systemIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(systemIncludePaths);
- var compilerIncludePaths = input.cpp.compilerIncludePaths;
- if (compilerIncludePaths)
- allIncludePaths = allIncludePaths.uniqueConcat(compilerIncludePaths);
- args = args.concat(allIncludePaths.map(function(include) { return "-I" + include }));
+ args = args.concat(Cpp.collectIncludePathsArguments(input));
+ args = args.concat(Cpp.collectSystemIncludePathsArguments(input, input.cpp.includeFlag));
// Misc flags.
- args = args.concat(ModUtils.moduleProperty(input, "platformFlags", tag),
- ModUtils.moduleProperty(input, "flags", tag),
- ModUtils.moduleProperty(input, "driverFlags", tag));
+ args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm"));
args.push("-ol");
args.push(outputs.obj[0].filePath);
@@ -409,7 +328,7 @@ function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
return args;
}
-function linkerFlags(project, product, input, outputs) {
+function linkerFlags(project, product, inputs, outputs) {
var args = [];
// Target MCU flag.
@@ -417,118 +336,237 @@ function linkerFlags(project, product, input, outputs) {
if (targetFlag)
args.push(targetFlag);
- var allLibraryPaths = [];
- var libraryPaths = product.cpp.libraryPaths;
- if (libraryPaths)
- allLibraryPaths = allLibraryPaths.uniqueConcat(libraryPaths);
- var distributionLibraryPaths = product.cpp.distributionLibraryPaths;
- if (distributionLibraryPaths)
- allLibraryPaths = allLibraryPaths.uniqueConcat(distributionLibraryPaths);
-
- var libraryDependencies = collectLibraryDependencies(product);
-
var escapableLinkerFlags = [];
// Map file generation flag.
if (product.cpp.generateLinkerMapFile)
escapableLinkerFlags.push("-m");
- if (product.cpp.platformLinkerFlags)
- Array.prototype.push.apply(escapableLinkerFlags, product.cpp.platformLinkerFlags);
- if (product.cpp.linkerFlags)
- Array.prototype.push.apply(escapableLinkerFlags, product.cpp.linkerFlags);
+ escapableLinkerFlags = escapableLinkerFlags.concat(Cpp.collectMiscEscapableLinkerArguments(product));
var useCompilerDriver = useCompilerDriverLinker(product);
- if (useCompilerDriver) {
- // Output.
- args.push("-o", outputs.application[0].filePath);
-
- // Inputs.
- if (inputs.obj)
- args = args.concat(inputs.obj.map(function(obj) { return obj.filePath }));
-
- // Library paths.
- args = args.concat(allLibraryPaths.map(function(path) { return "-L" + path }));
-
- // Linker scripts.
- var scripts = inputs.linkerscript
- ? inputs.linkerscript.map(function(scr) { return "-f" + scr.filePath; }) : [];
- if (scripts)
- Array.prototype.push.apply(escapableLinkerFlags, scripts);
- } else {
- // Output.
- args.push(outputs.application[0].filePath);
-
- // Inputs.
- if (inputs.obj)
- args = args.concat(inputs.obj.map(function(obj) { return obj.filePath }));
-
- // Library paths.
- args = args.concat(allLibraryPaths.map(function(path) { return "-k" + path }));
-
- // Linker scripts.
- // Note: We need to split the '-f' and the file path to separate
- // lines; otherwise the linking fails.
- inputs.linkerscript.forEach(function(scr) {
- escapableLinkerFlags.push("-f", scr.filePath);
- });
- }
+
+ // Output.
+ if (useCompilerDriver)
+ args.push("-o");
+ args.push(outputs.application[0].filePath);
+
+ // Inputs.
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs));
+
+ // Library paths.
+ var libraryPathFlag = useCompilerDriver ? "-L" : "-k";
+ args = args.concat(Cpp.collectLibraryPathsArguments(product, libraryPathFlag));
+
+ // Linker scripts.
+ // Note: We need to split the '-f' flag and the file path to separate
+ // lines when we don't use the compiler driver mode.
+ escapableLinkerFlags = escapableLinkerFlags.concat(
+ Cpp.collectLinkerScriptPathsArguments(product, inputs, !useCompilerDriver));
// Library dependencies.
- if (libraryDependencies)
- args = args.concat(libraryDependencies.map(function(dep) { return "-l" + dep.filePath }));
+ args = args.concat(Cpp.collectLibraryDependenciesArguments(product));
- // Misc flags.
var escapedLinkerFlags = escapeLinkerFlags(product, escapableLinkerFlags);
if (escapedLinkerFlags)
Array.prototype.push.apply(args, escapedLinkerFlags);
- var driverLinkerFlags = useCompilerDriver ? product.cpp.driverLinkerFlags : undefined;
- if (driverLinkerFlags)
- Array.prototype.push.apply(args, driverLinkerFlags);
+
+ // Misc flags.
+ if (useCompilerDriver) {
+ args = args.concat(Cpp.collectMiscLinkerArguments(product),
+ Cpp.collectMiscDriverArguments(product));
+ }
return args;
}
-function archiverFlags(project, product, input, outputs) {
+function archiverFlags(project, product, inputs, outputs) {
var args = ["-rc"];
args.push(outputs.staticlibrary[0].filePath);
- if (inputs.obj)
- args = args.concat(inputs.obj.map(function(obj) { return obj.filePath }));
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs));
return args;
}
+function buildLinkerMapFilePath(target, suffix) {
+ return FileInfo.joinPaths(FileInfo.path(target.filePath),
+ FileInfo.completeBaseName(target.fileName) + suffix);
+}
+
+// This is the workaround for the SDCC bug on a Windows host:
+// * https://sourceforge.net/p/sdcc/bugs/2970/
+// We need to replace the '\r\n\' line endings with the'\n' line
+// endings for each generated object file.
+function patchObjectFile(project, product, inputs, outputs, input, output) {
+ var isWindows = Host.os().includes("windows");
+ if (isWindows && input.cpp.debugInformation) {
+ var cmd = new JavaScriptCommand();
+ cmd.objectPath = outputs.obj[0].filePath;
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ var file = new BinaryFile(objectPath, BinaryFile.ReadWrite);
+ var data = file.read(file.size());
+ file.resize(0);
+ for (var pos = 0; pos < data.length; ++pos) {
+ // Find the next index of CR (\r) symbol.
+ var index = data.indexOf(0x0d, pos);
+ if (index < 0)
+ index = data.length;
+ // Write next data chunk between the previous position and the CR
+ // symbol, exclude the CR symbol.
+ file.write(data.slice(pos, index));
+ pos = index;
+ }
+ };
+ return cmd;
+ }
+}
+
+// It is a workaround which removes the generated linker map file
+// if it is disabled by cpp.generateLinkerMapFile property.
+// Reason is that the SDCC compiler always generates this file,
+// and does not have an option to disable generation for a linker
+// map file. So, we can to remove a listing files only after the
+// linking completes.
+function removeLinkerMapFile(project, product, inputs, outputs, input, output) {
+ if (!product.cpp.generateLinkerMapFile) {
+ var target = outputs.application[0];
+ var cmd = new JavaScriptCommand();
+ cmd.mapFilePath = buildLinkerMapFilePath(target, product.cpp.linkerMapSuffix)
+ cmd.silent = true;
+ cmd.sourceCode = function() { File.remove(mapFilePath); };
+ return cmd;
+ }
+}
+
+// It is a workaround to rename the extension of the output linker
+// map file to the specified one, since the linker generates only
+// files with the '.map' extension.
+function renameLinkerMapFile(project, product, inputs, outputs, input, output) {
+ if (product.cpp.generateLinkerMapFile && (product.cpp.linkerMapSuffix !== ".map")) {
+ var target = outputs.application[0];
+ var cmd = new JavaScriptCommand();
+ cmd.newMapFilePath = buildLinkerMapFilePath(target, product.cpp.linkerMapSuffix);
+ cmd.oldMapFilePath = buildLinkerMapFilePath(target, ".map");
+ cmd.silent = true;
+ cmd.sourceCode = function() { File.move(oldMapFilePath, newMapFilePath); };
+ return cmd;
+ }
+}
+
+// It is a workaround which removes the generated listing files
+// if it is disabled by cpp.generateCompilerListingFiles property
+// or when the cpp.compilerListingSuffix differs with '.lst'.
+// Reason is that the SDCC compiler does not have an option to
+// disable generation for a listing files. Besides, the SDCC
+// compiler use this files and for the linking. So, we can to
+// remove a listing files only after the linking completes.
+function removeCompilerListingFiles(project, product, inputs, outputs, input, output) {
+ var cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ inputs.obj.forEach(function(object) {
+ if (!object.filePath.endsWith(".c" + object.cpp.objectSuffix))
+ return; // Skip the assembler generated objects.
+ if (!object.cpp.generateCompilerListingFiles
+ || (object.cpp.compilerListingSuffix !== ".lst")) {
+ var listingPath = FileInfo.joinPaths(FileInfo.path(object.filePath),
+ object.completeBaseName + ".lst");
+ File.remove(listingPath);
+ }
+ })
+ };
+ return cmd;
+}
+
+// It is a workaround that duplicates the generated listing files
+// but with desired names. The problem is that the SDCC compiler does
+// not support an options to specify names for the generated listing
+// files. At the same time, the compiler always generates the listing
+// files in the form of 'module.c.lst', which makes it impossible to
+// change the file suffix to a user-specified one. In addition, these
+// files are also somehow used for linking. Thus, we can not rename them
+// on the compiling stage.
+function duplicateCompilerListingFile(project, product, inputs, outputs, input, output) {
+ if (input.cpp.generateCompilerListingFiles
+ && (input.cpp.compilerListingSuffix !== ".lst")) {
+ var cmd = new JavaScriptCommand();
+ cmd.newListing = outputs.lst[0].filePath;
+ cmd.oldListing = FileInfo.joinPaths(FileInfo.path(outputs.lst[0].filePath),
+ outputs.lst[0].completeBaseName + ".lst");
+ cmd.silent = true;
+ cmd.sourceCode = function() { File.copy(oldListing, newListing); };
+ return cmd;
+ }
+}
+
function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var cmds = [];
var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn);
var compilerPath = input.cpp.compilerPath;
var cmd = new Command(compilerPath, args);
cmd.description = "compiling " + input.fileName;
cmd.highlight = "compiler";
- return [cmd];
+ cmd.jobPool = "compiler";
+ cmds.push(cmd);
+
+ cmd = patchObjectFile(project, product, inputs, outputs, input, output);
+ if (cmd)
+ cmds.push(cmd);
+
+ cmd = duplicateCompilerListingFile(project, product, inputs, outputs, input, output);
+ if (cmd)
+ cmds.push(cmd);
+
+ return cmds;
}
function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var cmds = [];
var args = assemblerFlags(project, product, input, outputs, explicitlyDependsOn);
var assemblerPath = input.cpp.assemblerPath;
var cmd = new Command(assemblerPath, args);
cmd.description = "assembling " + input.fileName;
cmd.highlight = "compiler";
- return [cmd];
+ cmd.jobPool = "assembler";
+ cmds.push(cmd);
+
+ cmd = patchObjectFile(project, product, inputs, outputs, input, output);
+ if (cmd)
+ cmds.push(cmd);
+
+ return cmds;
}
function prepareLinker(project, product, inputs, outputs, input, output) {
- var primaryOutput = outputs.application[0];
- var args = linkerFlags(project, product, input, outputs);
+ var cmds = [];
+ var args = linkerFlags(project, product, inputs, outputs);
var linkerPath = effectiveLinkerPath(product);
var cmd = new Command(linkerPath, args);
- cmd.description = "linking " + primaryOutput.fileName;
+ cmd.description = "linking " + outputs.application[0].fileName;
cmd.highlight = "linker";
- return [cmd];
+ cmd.jobPool = "linker";
+ cmds.push(cmd);
+
+ cmd = removeCompilerListingFiles(project, product, inputs, outputs, input, output);
+ if (cmd)
+ cmds.push(cmd);
+
+ cmd = renameLinkerMapFile(project, product, inputs, outputs, input, output);
+ if (cmd)
+ cmds.push(cmd);
+
+ cmd = removeLinkerMapFile(project, product, inputs, outputs, input, output);
+ if (cmd)
+ cmds.push(cmd);
+
+ return cmds;
}
function prepareArchiver(project, product, inputs, outputs, input, output) {
- var args = archiverFlags(project, product, input, outputs);
+ var args = archiverFlags(project, product, inputs, outputs);
var archiverPath = product.cpp.archiverPath;
var cmd = new Command(archiverPath, args);
- cmd.description = "linking " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.highlight = "linker";
+ cmd.jobPool = "linker";
return [cmd];
}
diff --git a/share/qbs/modules/cpp/sdcc.qbs b/share/qbs/modules/cpp/sdcc.qbs
index ec763ba47..24cb6d738 100644
--- a/share/qbs/modules/cpp/sdcc.qbs
+++ b/share/qbs/modules/cpp/sdcc.qbs
@@ -28,15 +28,14 @@
**
****************************************************************************/
-import qbs 1.0
import qbs.File
import qbs.FileInfo
-import qbs.ModUtils
import qbs.Probes
+import "cpp.js" as Cpp
import "sdcc.js" as SDCC
CppModule {
- condition: qbs.toolchain && qbs.toolchain.contains("sdcc")
+ condition: qbs.toolchain && qbs.toolchain.includes("sdcc")
Probes.BinaryProbe {
id: compilerPathProbe
@@ -48,10 +47,12 @@ CppModule {
id: sdccProbe
condition: !_skipAllChecks
compilerFilePath: compilerPath
+ enableDefinesByLanguage: enableCompilerDefinesByLanguage
preferredArchitecture: qbs.architecture
}
qbs.architecture: sdccProbe.found ? sdccProbe.architecture : original
+ qbs.targetPlatform: "none"
compilerVersionMajor: sdccProbe.versionMajor
compilerVersionMinor: sdccProbe.versionMinor
@@ -61,10 +62,7 @@ CppModule {
compilerDefinesByLanguage: sdccProbe.compilerDefinesByLanguage
compilerIncludePaths: sdccProbe.includePaths
- property string toolchainInstallPath: compilerPathProbe.found
- ? compilerPathProbe.path : undefined
-
- property string compilerExtension: qbs.hostOS.contains("windows") ? ".exe" : ""
+ toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined
/* Work-around for QtCreator which expects these properties to exist. */
property string cCompilerName: compilerName
@@ -72,47 +70,53 @@ CppModule {
property string linkerMode: "automatic"
- compilerName: SDCC.compilerName(qbs) + compilerExtension
+ compilerName: "sdcc" + compilerExtension
compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName)
- assemblerName: SDCC.assemblerName(qbs) + compilerExtension
+ assemblerName: toolchainDetails.assemblerName + compilerExtension
assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName)
- linkerName: SDCC.linkerName(qbs) + compilerExtension
+ linkerName: toolchainDetails.linkerName + compilerExtension
linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName)
- property string archiverName: SDCC.archiverName(qbs) + compilerExtension
+ property string archiverName: "sdar" + compilerExtension
property string archiverPath: FileInfo.joinPaths(toolchainInstallPath, archiverName)
runtimeLibrary: "static"
staticLibrarySuffix: ".lib"
executableSuffix: ".ihx"
-
- property string objectSuffix: ".rel"
+ objectSuffix: ".rel"
imageFormat: "ihx"
enableExceptions: false
enableRtti: false
+ defineFlag: "-D"
+ includeFlag: "-I"
+ systemIncludeFlag: "-isystem"
+ preincludeFlag: "-include"
+ libraryDependencyFlag: "-l"
+ libraryPathFlag: "-L"
+ linkerScriptFlag: "-f"
+
+ toolchainDetails: SDCC.toolchainDetails(qbs)
+
+ knownArchitectures: ["hcs8", "mcs51", "stm8"]
+
Rule {
id: assembler
inputs: ["asm"]
- outputFileTags: ["obj", "asm_adb", "lst", "asm_src", "asm_sym", "rst_data"]
- outputArtifacts: SDCC.compilerOutputArtifacts(input)
+ outputFileTags: SDCC.extraCompilerOutputTags().concat(
+ Cpp.assemblerOutputTags(generateAssemblerListingFiles))
+ outputArtifacts: SDCC.extraCompilerOutputArtifacts(input).concat(
+ Cpp.assemblerOutputArtifacts(input))
prepare: SDCC.prepareAssembler.apply(SDCC, arguments)
}
FileTagger {
- condition: qbs.architecture === "arm";
- patterns: "*.s"
- fileTags: ["asm"]
- }
-
- FileTagger {
- condition: qbs.architecture === "mcs51";
- patterns: ["*.s51", "*.asm"]
+ patterns: ["*.s", "*.a51", "*.asm"]
fileTags: ["asm"]
}
@@ -120,8 +124,10 @@ CppModule {
id: compiler
inputs: ["cpp", "c"]
auxiliaryInputs: ["hpp"]
- outputFileTags: ["obj", "asm_adb", "lst", "asm_src", "asm_sym", "rst_data"]
- outputArtifacts: SDCC.compilerOutputArtifacts(input)
+ outputFileTags: SDCC.extraCompilerOutputTags().concat(
+ Cpp.compilerOutputTags(generateCompilerListingFiles))
+ outputArtifacts: SDCC.extraCompilerOutputArtifacts(input).concat(
+ Cpp.compilerOutputArtifacts(input))
prepare: SDCC.prepareCompiler.apply(SDCC, arguments)
}
@@ -130,8 +136,10 @@ CppModule {
multiplex: true
inputs: ["obj", "linkerscript"]
inputsFromDependencies: ["staticlibrary"]
- outputFileTags: ["application", "lk_cmd", "mem_summary", "mem_map"]
- outputArtifacts: SDCC.applicationLinkerOutputArtifacts(product)
+ outputFileTags: SDCC.extraApplicationLinkerOutputTags().concat(
+ Cpp.applicationLinkerOutputTags(generateLinkerMapFile))
+ outputArtifacts: SDCC.extraApplicationLinkerOutputArtifacts(product).concat(
+ Cpp.applicationLinkerOutputArtifacts(product))
prepare: SDCC.prepareLinker.apply(SDCC, arguments)
}
@@ -140,8 +148,8 @@ CppModule {
multiplex: true
inputs: ["obj"]
inputsFromDependencies: ["staticlibrary"]
- outputFileTags: ["staticlibrary"]
- outputArtifacts: SDCC.staticLibraryLinkerOutputArtifacts(product)
+ outputFileTags: Cpp.staticLibraryLinkerOutputTags()
+ outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product)
prepare: SDCC.prepareArchiver.apply(SDCC, arguments)
}
}
diff --git a/share/qbs/modules/cpp/setuprunenv.js b/share/qbs/modules/cpp/setuprunenv.js
index 550d08d63..2ea331a30 100644
--- a/share/qbs/modules/cpp/setuprunenv.js
+++ b/share/qbs/modules/cpp/setuprunenv.js
@@ -30,11 +30,12 @@
var FileInfo = require("qbs.FileInfo");
var File = require("qbs.File");
+var Host = require("qbs.Host");
var ModUtils = require("qbs.ModUtils"); // TODO: append/prepend functionality should go to qbs.Environment
function addNewElement(list, elem)
{
- if (!list.contains(elem))
+ if (!list.includes(elem))
list.push(elem);
}
@@ -48,7 +49,7 @@ function artifactDir(artifact)
function addExternalLibPath(product, list, path)
{
addNewElement(list, path);
- if (product.qbs.hostOS.contains("windows") && FileInfo.fileName(path) === "lib") {
+ if (Host.os().includes("windows") && FileInfo.fileName(path) === "lib") {
var binPath = FileInfo.joinPaths(FileInfo.path(path), "bin");
if (File.exists(binPath))
addNewElement(list, binPath);
@@ -57,7 +58,7 @@ function addExternalLibPath(product, list, path)
function gatherPaths(product, libPaths, frameworkPaths, seenProducts)
{
- if (seenProducts.contains(product.name))
+ if (seenProducts.includes(product.name))
return;
seenProducts.push(product.name);
@@ -66,6 +67,8 @@ function gatherPaths(product, libPaths, frameworkPaths, seenProducts)
product.cpp.libraryPaths.forEach(function(p) { addExternalLibPath(product, libPaths, p); });
if (product.cpp && product.cpp.frameworkPaths)
product.cpp.frameworkPaths.forEach(function(p) { addNewElement(frameworkPaths, p); });
+ if (product.cpp && product.cpp.systemFrameworkPaths)
+ product.cpp.systemFrameworkPaths.forEach(function(p) { addNewElement(frameworkPaths, p); });
// Extract paths from dynamic libraries, if they are given as file paths.
if (product.cpp && product.cpp.dynamicLibraries) {
@@ -103,10 +106,10 @@ function gatherPaths(product, libPaths, frameworkPaths, seenProducts)
function setupRunEnvironment(product, config)
{
- if (config.contains("ignore-lib-dependencies"))
+ if (config.includes("ignore-lib-dependencies"))
return;
- if (product.qbs.hostPlatform !== product.qbs.targetPlatform)
+ if (Host.platform() !== product.qbs.targetPlatform)
return;
var libPaths = [];
@@ -117,8 +120,8 @@ function setupRunEnvironment(product, config)
if (runPaths && runPaths.length > 0) {
var canonicalRunPaths = runPaths.map(function(p) { return File.canonicalFilePath(p); });
var filterFunc = function(libPath) {
- return !runPaths.contains(libPath)
- && !canonicalRunPaths.contains(File.canonicalFilePath(libPath));
+ return !runPaths.includes(libPath)
+ && !canonicalRunPaths.includes(File.canonicalFilePath(libPath));
};
libPaths = libPaths.filter(filterFunc);
frameworkPaths = frameworkPaths.filter(filterFunc);
@@ -126,19 +129,19 @@ function setupRunEnvironment(product, config)
if (libPaths.length > 0) {
var envVarName;
- if (product.qbs.targetOS.contains("windows"))
+ if (product.qbs.targetOS.includes("windows"))
envVarName = "PATH";
- else if (product.qbs.targetOS.contains("macos"))
+ else if (product.qbs.targetOS.includes("macos"))
envVarName = "DYLD_LIBRARY_PATH";
else
envVarName = "LD_LIBRARY_PATH";
- var envVar = new ModUtils.EnvironmentVariable(envVarName, product.qbs.pathListSeparator,
- product.qbs.hostOS.contains("windows"));
+ var envVar = new ModUtils.EnvironmentVariable(envVarName, FileInfo.pathListSeparator(),
+ Host.os().includes("windows"));
libPaths.forEach(function(p) { envVar.prepend(p); });
envVar.set();
}
- if (product.qbs.targetOS.contains("macos") && frameworkPaths.length > 0) {
+ if (product.qbs.targetOS.includes("macos") && frameworkPaths.length > 0) {
envVar = new ModUtils.EnvironmentVariable("DYLD_FRAMEWORK_PATH", ':', false);
frameworkPaths.forEach(function(p) { envVar.prepend(p); });
envVar.set();
diff --git a/share/qbs/modules/cpp/tvos-gcc.qbs b/share/qbs/modules/cpp/tvos-gcc.qbs
index 19bc9b787..eba3b091e 100644
--- a/share/qbs/modules/cpp/tvos-gcc.qbs
+++ b/share/qbs/modules/cpp/tvos-gcc.qbs
@@ -30,16 +30,16 @@
DarwinGCC {
priority: 1
- condition: qbs.targetOS.contains('tvos') &&
- qbs.toolchain && qbs.toolchain.contains('gcc')
+ condition: qbs.targetOS.includes('tvos') &&
+ qbs.toolchain && qbs.toolchain.includes('gcc')
targetSystem: "tvos" + (minimumTvosVersion || "")
minimumDarwinVersion: minimumTvosVersion
- minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("tvos-simulator")
+ minimumDarwinVersionCompilerFlag: qbs.targetOS.includes("tvos-simulator")
? "-mtvos-simulator-version-min"
: "-mtvos-version-min"
- minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("tvos-simulator")
+ minimumDarwinVersionLinkerFlag: qbs.targetOS.includes("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
index 46e4bf874..3a160f6d0 100644
--- a/share/qbs/modules/cpp/watchos-gcc.qbs
+++ b/share/qbs/modules/cpp/watchos-gcc.qbs
@@ -31,16 +31,16 @@
DarwinGCC {
priority: 1
- condition: qbs.targetOS.contains('watchos') &&
- qbs.toolchain && qbs.toolchain.contains('gcc')
+ condition: qbs.targetOS.includes('watchos') &&
+ qbs.toolchain && qbs.toolchain.includes('gcc')
targetSystem: "watchos" + (minimumWatchosVersion || "")
minimumDarwinVersion: minimumWatchosVersion
- minimumDarwinVersionCompilerFlag: qbs.targetOS.contains("watchos-simulator")
+ minimumDarwinVersionCompilerFlag: qbs.targetOS.includes("watchos-simulator")
? "-mwatchos-simulator-version-min"
: "-mwatchos-version-min"
- minimumDarwinVersionLinkerFlag: qbs.targetOS.contains("watchos-simulator")
+ minimumDarwinVersionLinkerFlag: qbs.targetOS.includes("watchos-simulator")
? "-watchos_simulator_version_min"
: "-watchos_version_min"
}
diff --git a/share/qbs/modules/cpp/watcom.js b/share/qbs/modules/cpp/watcom.js
new file mode 100644
index 000000000..9ae893db9
--- /dev/null
+++ b/share/qbs/modules/cpp/watcom.js
@@ -0,0 +1,568 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 Cpp = require("cpp.js");
+var Environment = require("qbs.Environment");
+var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var ModUtils = require("qbs.ModUtils");
+var PathTools = require("qbs.PathTools");
+var Process = require("qbs.Process");
+var TemporaryDir = require("qbs.TemporaryDir");
+var TextFile = require("qbs.TextFile");
+var Utilities = require("qbs.Utilities");
+
+function toolchainDetails(qbs) {
+ var platform = qbs.targetPlatform;
+ var details = {};
+ if (platform === "dos") {
+ details.imageFormat = "mz";
+ details.executableSuffix = ".exe";
+ } else if (platform === "os2") {
+ details.imageFormat = "pe";
+ details.executableSuffix = ".exe";
+ details.dynamicLibrarySuffix = ".dll";
+ } else if (platform === "windows") {
+ details.imageFormat = "pe";
+ details.executableSuffix = ".exe";
+ details.dynamicLibrarySuffix = ".dll";
+ } else if (platform === "linux") {
+ details.imageFormat = "elf";
+ details.executableSuffix = "";
+ details.dynamicLibrarySuffix = ".so";
+ }
+ return details;
+}
+
+function languageFlag(tag) {
+ if (tag === "c")
+ return "-xc";
+ else if (tag === "cpp")
+ return "-xc++";
+}
+
+function targetFlag(platform, architecture, type) {
+ if (platform === "dos") {
+ if (architecture === "x86_16")
+ return "-bdos";
+ else if (architecture === "x86")
+ return "-bdos4g";
+ } else if (platform === "os2") {
+ if (architecture === "x86_16")
+ return "-bos2";
+ else if (architecture === "x86")
+ return "-bos2v2";
+ } else if (platform === "windows") {
+ if (architecture === "x86_16") {
+ if (type.includes("dynamiclibrary"))
+ return "-bwindows_dll";
+ return "-bwindows";
+ } else if (architecture === "x86") {
+ if (type.includes("dynamiclibrary"))
+ return "-bnt_dll";
+ return "-bnt";
+ }
+ } else if (platform === "linux") {
+ return "-blinux";
+ }
+}
+
+function guessVersion(macros) {
+ var version = parseInt(macros["__WATCOMC__"], 10)
+ || parseInt(macros["__WATCOM_CPLUSPLUS__"], 10);
+ if (version) {
+ return { major: parseInt((version - 1100) / 100),
+ minor: parseInt(version / 10) % 10,
+ patch: ((version % 10) > 0) ? parseInt(version % 10) : 0 }
+ }
+}
+
+function guessEnvironment(hostOs, platform, architecture,
+ toolchainInstallPath, pathListSeparator) {
+ var toolchainRootPath = FileInfo.path(toolchainInstallPath);
+ if (!File.exists(toolchainRootPath)) {
+ throw "Unable to deduce environment due to compiler root directory: '"
+ + toolchainRootPath + "' does not exist";
+ }
+
+ var env = {};
+
+ function setVariable(key, properties, path, separator) {
+ var values = [];
+ for (var i = 0; i < properties.length; ++i) {
+ if (path) {
+ var fullpath = FileInfo.joinPaths(path, properties[i]);
+ values.push(FileInfo.toNativeSeparators(fullpath));
+ } else {
+ values.push(properties[i]);
+ }
+ }
+ env[key] = values.join(separator);
+ }
+
+ setVariable("WATCOM", [toolchainRootPath], undefined, pathListSeparator);
+ setVariable("EDPATH", ["eddat"], toolchainRootPath, pathListSeparator);
+
+ if (hostOs.includes("linux"))
+ setVariable("PATH", ["binl64", "binl"], toolchainRootPath, pathListSeparator);
+ else if (hostOs.includes("windows"))
+ setVariable("PATH", ["binnt64", "binnt"], toolchainRootPath, pathListSeparator);
+
+ if (platform === "linux") {
+ setVariable("INCLUDE", ["lh"], toolchainRootPath, pathListSeparator);
+ } else {
+ // Common for DOS, Windows, OS/2.
+ setVariable("WIPFC", ["wipfc"], toolchainRootPath, pathListSeparator);
+ setVariable("WHTMLHELP", ["binnt/help"], toolchainRootPath, pathListSeparator);
+
+ var includes = ["h"];
+ if (platform === "dos") {
+ // Same includes as before.
+ } else if (platform === "os2") {
+ if (architecture === "x86")
+ includes = includes.concat(["h/os2"]);
+ else if (architecture === "x86_16")
+ includes = includes.concat(["h/os21x"]);
+ } else if (platform === "windows") {
+ if (architecture === "x86")
+ includes = includes.concat(["h/nt", "h/nt/directx", "h/nt/ddk"]);
+ else if (architecture === "x86_16")
+ includes = includes.concat(["h/win"]);
+ } else {
+ throw "Unable to deduce environment for unsupported target platform: '"
+ + platform + "'";
+ }
+
+ setVariable("INCLUDE", includes, toolchainRootPath, pathListSeparator);
+ }
+
+ return env;
+}
+
+function dumpMacros(environment, compilerPath, platform, architecture, tag) {
+ // Note: The Open Watcom compiler does not support the predefined
+ // macros dumping. So, we do it with the following trick, where we try
+ // to create and compile a special temporary file and to parse the console
+ // output with the own magic pattern: #define <key> <value>.
+
+ var outputDirectory = new TemporaryDir();
+ var outputFilePath = FileInfo.joinPaths(outputDirectory.path(), "dump-macros.c");
+ var outputFile = new TextFile(outputFilePath, TextFile.WriteOnly);
+ outputFile.writeLine("#define VALUE_TO_STRING(x) #x");
+ outputFile.writeLine("#define VALUE(x) VALUE_TO_STRING(x)");
+ outputFile.writeLine("#define VAR_NAME_VALUE(var) \"#define \"#var\" \"VALUE(var)");
+ // Declare all available pre-defined macros of Watcon compiler.
+ var keys = [
+ // Prepare the DOS target macros.
+ "__DOS__", "_DOS", "MSDOS",
+ // Prepare the OS/2 target macros.
+ "__OS2__",
+ // Prepare the QNX target macros.
+ "__QNX__",
+ // Prepare the Netware target macros.
+ "__NETWARE__", "__NETWARE_386__",
+ // Prepare the Windows target macros.
+ "__NT__", "__WINDOWS__", "_WINDOWS", "__WINDOWS_386__",
+ // Prepare the Linux and Unix target macros.
+ "__LINUX__", "__UNIX__",
+ // Prepare the 16-bit target specific macros.
+ "__I86__", "M_I86", "_M_I86", "_M_IX86",
+ // Prepare the 32-bit target specific macros.
+ "__386__", "M_I386", "_M_I386", "_M_IX86",
+ // Prepare the indicated options macros.
+ "_MT", "_DLL", "__FPI__", "__CHAR_SIGNED__", "__INLINE_FUNCTIONS__",
+ "_CPPRTTI", "_CPPUNWIND", "NO_EXT_KEYS",
+ // Prepare the common memory model macros.
+ "__FLAT__", "__SMALL__", "__MEDIUM__",
+ "__COMPACT__", "__LARGE__", "__HUGE__",
+ // Prepare the 16-bit memory model macros.
+ "M_I86SM", "_M_I86SM", "M_I86MM", "_M_I86MM", "M_I86CM",
+ "_M_I86CM", "M_I86LM", "_M_I86LM", "M_I86HM", "_M_I86HM",
+ // Prepare the 32-bit memory model macros.
+ "M_386FM", "_M_386FM", "M_386SM", "M_386MM", "_M_386MM",
+ "M_386CM", "_M_386CM", "M_386LM", "_M_386LM",
+ // Prepare the compiler macros.
+ "__X86__", "__cplusplus", "__WATCOMC__", "__WATCOM_CPLUSPLUS__",
+ "_INTEGRAL_MAX_BITS", "_PUSHPOP_SUPPORTED", "_STDCALL_SUPPORTED",
+ // Prepare the other macros.
+ "__3R__", "_based", "_cdecl", "cdecl", "_export", "_far16", "_far", "far",
+ "_fastcall", "_fortran", "fortran", "_huge", "huge", "_inline", "_interrupt",
+ "interrupt", "_loadds", "_near", "near", "_pascal", "pascal", "_saveregs",
+ "_segment", "_segname", "_self", "SOMDLINK", "_STDCALL_SUPPORTED", "__SW_0",
+ "__SW_3R", "__SW_5", "__SW_FP287", "__SW_FP2", "__SW_FP387", "__SW_FP3",
+ "__SW_FPI", "__SW_MF", "__SW_MS", "__SW_ZDP", "__SW_ZFP", "__SW_ZGF",
+ "__SW_ZGP", "_stdcall", "_syscall", "__BIG_ENDIAN"
+ ];
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ outputFile.writeLine("#if defined(" + key + ")");
+ outputFile.writeLine("#pragma message (VAR_NAME_VALUE(" + key + "))");
+ outputFile.writeLine("#endif");
+ }
+ outputFile.close();
+
+ var process = new Process();
+ process.setWorkingDirectory(outputDirectory.path());
+ for (var envkey in environment)
+ process.setEnv(envkey, environment[envkey]);
+
+ var target = targetFlag(platform, architecture, ["application"]);
+ var lang = languageFlag(tag);
+ var args = [ target, lang, outputFilePath ];
+
+ process.exec(compilerPath, args, false);
+ var m = Cpp.extractMacros(process.readStdOut(), /"?(#define(\s\w+){1,2})"?$/);
+ if (tag === "cpp" && m["__cplusplus"] === "1")
+ return m;
+ else if (tag === "c")
+ return m;
+}
+
+function effectiveLinkerPath(product) {
+ if (product.cpp.linkerMode === "automatic") {
+ var compilerPath = product.cpp.compilerPath;
+ if (compilerPath)
+ return compilerPath;
+ console.log("Found no C-language objects, choosing system linker for " + product.name);
+ }
+ return product.cpp.linkerPath;
+}
+
+function useCompilerDriverLinker(product) {
+ var linker = effectiveLinkerPath(product);
+ var compiler = product.cpp.compilerPath;
+ return linker === compiler;
+}
+
+function escapeLinkerFlags(useCompilerDriver, linkerFlags) {
+ if (!linkerFlags || linkerFlags.length === 0)
+ return [];
+
+ if (useCompilerDriver) {
+ var sep = ",";
+ return [["-Wl"].concat(linkerFlags).join(sep)];
+ }
+ return linkerFlags;
+}
+
+function assemblerFlags(project, product, input, outputs, explicitlyDependsOn) {
+ var args = [FileInfo.toNativeSeparators(input.filePath)];
+ args.push("-fo=" + FileInfo.toNativeSeparators(outputs.obj[0].filePath));
+
+ args = args.concat(Cpp.collectPreincludePaths(input).map(function(path) {
+ return "-fi" + FileInfo.toNativeSeparators(path);
+ }));
+
+ args = args.concat(Cpp.collectDefinesArguments(input, "-d"));
+
+ var includePaths = [].concat(Cpp.collectIncludePaths(input)).concat(
+ Cpp.collectSystemIncludePaths(input));
+ args = args.concat(includePaths.map(function(path) {
+ return "-i" + FileInfo.toNativeSeparators(path);
+ }));
+
+ if (input.cpp.debugInformation)
+ args.push("-d1");
+
+ var warnings = input.cpp.warningLevel
+ if (warnings === "none")
+ args.push("-w0");
+ else if (warnings === "all")
+ args.push("-wx");
+ if (input.cpp.treatWarningsAsErrors)
+ args.push("-we");
+
+ args.push("-zq"); // Silent.
+ args = args.concat(Cpp.collectMiscAssemblerArguments(input));
+ return args;
+}
+
+function compilerFlags(project, product, input, outputs, explicitlyDependsOn) {
+ var args = ["-g" + (input.cpp.debugInformation ? "3" : "0")];
+
+ var target = targetFlag(product.qbs.targetPlatform, product.qbs.architecture,
+ product.type);
+ args.push(target);
+
+ if (product.type.includes("application")) {
+ if (product.qbs.targetPlatform === "windows") {
+ var consoleApplication = product.consoleApplication;
+ args.push(consoleApplication ? "-mconsole" : "-mwindows");
+ }
+ } else if (product.type.includes("dynamiclibrary")) {
+ args.push("-shared");
+ }
+
+ var optimization = input.cpp.optimization
+ if (optimization === "fast")
+ args.push("-Ot");
+ else if (optimization === "small")
+ args.push("-Os");
+ else if (optimization === "none")
+ args.push("-O0");
+
+ var warnings = input.cpp.warningLevel
+ if (warnings === "none") {
+ args.push("-w");
+ } else if (warnings === "all") {
+ args.push("-Wall");
+ args.push("-Wextra");
+ }
+ if (input.cpp.treatWarningsAsErrors)
+ args.push("-Werror");
+
+ var tag = ModUtils.fileTagForTargetLanguage(input.fileTags.concat(outputs.obj[0].fileTags));
+
+ var langFlag = languageFlag(tag);
+ if (langFlag)
+ args.push(langFlag);
+
+ if (tag === "cpp") {
+ var enableExceptions = input.cpp.enableExceptions;
+ if (enableExceptions) {
+ var ehModel = input.cpp.exceptionHandlingModel;
+ switch (ehModel) {
+ case "direct":
+ args.push("-feh-direct");
+ break;
+ case "table":
+ args.push("-feh-table");
+ break;
+ default:
+ args.push("-feh");
+ break;
+ }
+ } else {
+ args.push("-fno-eh");
+ }
+
+ var enableRtti = input.cpp.enableRtti;
+ args.push(enableRtti ? "-frtti" : "-fno-rtti");
+ } else if (tag === "c") {
+ var knownValues = ["c99", "c89"];
+ var cLanguageVersion = Cpp.languageVersion(input.cpp.cLanguageVersion, knownValues, "C");
+ switch (cLanguageVersion) {
+ case "c89":
+ args.push("-std=c89");
+ break;
+ case "c99":
+ args.push("-std=c99");
+ break;
+ }
+ }
+
+ var preincludePaths = Cpp.collectPreincludePaths(input);
+ for (var i = 0; i < preincludePaths.length; ++i)
+ args.push(input.cpp.preincludeFlag, preincludePaths[i]);
+
+ args = args.concat(Cpp.collectDefinesArguments(input));
+
+ args = args.concat(Cpp.collectIncludePaths(input).map(function(path) {
+ return input.cpp.includeFlag + FileInfo.toNativeSeparators(path);
+ }));
+ args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) {
+ return input.cpp.systemIncludeFlag + FileInfo.toNativeSeparators(path);
+ }));
+
+ args = args.concat(Cpp.collectMiscCompilerArguments(input, tag),
+ Cpp.collectMiscDriverArguments(input));
+
+ args.push("-o", FileInfo.toNativeSeparators(outputs.obj[0].filePath));
+ args.push("-c", FileInfo.toNativeSeparators(input.filePath));
+
+ return args;
+}
+
+function resourceCompilerFlags(project, product, input, outputs) {
+ var args = [input.filePath];
+ args.push("-fo=" + FileInfo.toNativeSeparators(outputs.res[0].filePath));
+ args = args.concat(Cpp.collectDefinesArguments(input, "-d"));
+
+ args = args.concat(Cpp.collectIncludePaths(input).map(function(path) {
+ return input.cpp.includeFlag + FileInfo.toNativeSeparators(path);
+ }));
+ args = args.concat(Cpp.collectSystemIncludePaths(input).map(function(path) {
+ return input.cpp.includeFlag + FileInfo.toNativeSeparators(path);
+ }));
+ args.push("-q", "-ad", "-r");
+ return args;
+}
+
+function linkerFlags(project, product, inputs, outputs) {
+ var args = [];
+ var useCompilerDriver = useCompilerDriverLinker(product);
+ if (useCompilerDriver) {
+ var target = targetFlag(product.qbs.targetPlatform, product.qbs.architecture,
+ product.type);
+ args.push(target);
+
+ if (product.type.includes("application")) {
+ args.push("-o", FileInfo.toNativeSeparators(outputs.application[0].filePath));
+ if (product.cpp.generateLinkerMapFile)
+ args.push("-fm=" + FileInfo.toNativeSeparators(outputs.mem_map[0].filePath));
+ } else if (product.type.includes("dynamiclibrary")) {
+ if (product.qbs.targetPlatform === "windows") {
+ args.push("-Wl, option implib=" + FileInfo.toNativeSeparators(
+ outputs.dynamiclibrary_import[0].filePath));
+ }
+ args.push("-o", FileInfo.toNativeSeparators(outputs.dynamiclibrary[0].filePath))
+ }
+
+ var escapableLinkerFlags = [];
+ var targetLinkerFlags = product.cpp.targetLinkerFlags;
+ if (targetLinkerFlags)
+ escapableLinkerFlags = escapableLinkerFlags.concat(targetLinkerFlags);
+
+ escapableLinkerFlags = escapableLinkerFlags.concat(
+ Cpp.collectMiscEscapableLinkerArguments(product));
+
+ var escapedLinkerFlags = escapeLinkerFlags(useCompilerDriver, escapableLinkerFlags);
+ if (escapedLinkerFlags)
+ args = args.concat(escapedLinkerFlags);
+
+ args = args.concat(Cpp.collectLibraryPaths(product).map(function(path) {
+ return product.cpp.libraryPathFlag + FileInfo.toNativeSeparators(path);
+ }));
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) {
+ return FileInfo.toNativeSeparators(path);
+ }));
+
+ var libraryDependencies = Cpp.collectLibraryDependencies(product);
+ for (var i = 0; i < libraryDependencies.length; ++i) {
+ var lib = libraryDependencies[i].filePath;
+ if (FileInfo.isAbsolutePath(lib) || lib.startsWith('@'))
+ args.push(FileInfo.toNativeSeparators(lib));
+ else
+ args.push("-Wl, libfile " + lib);
+ }
+
+ var resourcePaths = Cpp.collectResourceObjectPaths(inputs).map(function(path) {
+ return FileInfo.toNativeSeparators(path);
+ });
+ if (resourcePaths.length > 0)
+ args = args.concat("-Wl, resource " + resourcePaths.join(","));
+ }
+
+ args = args.concat(Cpp.collectMiscLinkerArguments(product),
+ Cpp.collectMiscDriverArguments(product));
+ return args;
+}
+
+function libraryManagerFlags(project, product, inputs, outputs) {
+ var args = ["-b", "-n", "-q"];
+ args = args.concat(Cpp.collectLinkerObjectPaths(inputs).map(function(path) {
+ return "+" + FileInfo.toNativeSeparators(path);
+ }));
+ args.push("-o", FileInfo.toNativeSeparators(outputs.staticlibrary[0].filePath));
+ return args;
+}
+
+function disassemblerFlags(project, product, inputs, outputs) {
+ var objectPath = Cpp.relativePath(product.buildDirectory, outputs.obj[0].filePath);
+ var listingPath = Cpp.relativePath(product.buildDirectory, outputs.lst[0].filePath);
+ var args = [];
+ args.push(FileInfo.toNativeSeparators(objectPath));
+ args.push("-l=" + FileInfo.toNativeSeparators(listingPath));
+ args.push("-s", "-a");
+ return args;
+}
+
+function generateCompilerListing(project, product, inputs, outputs, input, output) {
+ var args = disassemblerFlags(project, product, input, outputs);
+ var cmd = new Command(input.cpp.disassemblerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.silent = true;
+ cmd.jobPool = "watcom_job_pool";
+ return cmd;
+}
+
+function prepareAssembler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var cmds = [];
+ var args = assemblerFlags(project, product, input, outputs);
+ var cmd = new Command(input.cpp.assemblerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "assembling " + input.fileName;
+ cmd.highlight = "compiler";
+ cmd.jobPool = "watcom_job_pool";
+ cmds.push(cmd);
+ if (input.cpp.generateAssemblerListingFiles)
+ cmds.push(generateCompilerListing(project, product, inputs, outputs, input, output));
+ return cmds;
+}
+
+function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var cmds = [];
+ var args = compilerFlags(project, product, input, outputs, explicitlyDependsOn);
+ var cmd = new Command(input.cpp.compilerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "compiling " + input.fileName;
+ cmd.highlight = "compiler";
+ cmd.jobPool = "watcom_job_pool";
+ cmds.push(cmd);
+ if (input.cpp.generateCompilerListingFiles)
+ cmds.push(generateCompilerListing(project, product, inputs, outputs, input, output));
+ return cmds;
+}
+
+function prepareResourceCompiler(project, product, inputs, outputs, input, output,
+ explicitlyDependsOn) {
+ var args = resourceCompilerFlags(project, product, input, outputs);
+ var cmd = new Command(input.cpp.resourceCompilerPath, args);
+ // Set working directory to source directory as a workaround
+ // to make the resources compilable by resource compiler (it is magic).
+ cmd.workingDirectory = product.sourceDirectory;
+ cmd.description = "compiling " + input.fileName;
+ cmd.highlight = "compiler";
+ cmd.jobPool = "watcom_job_pool";
+ return [cmd];
+}
+
+function prepareLinker(project, product, inputs, outputs, input, output) {
+ var primaryOutput = outputs.dynamiclibrary ? outputs.dynamiclibrary[0]
+ : outputs.application[0];
+ var args = linkerFlags(project, product, inputs, outputs);
+ var linkerPath = effectiveLinkerPath(product);
+ var cmd = new Command(linkerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "linking " + primaryOutput.fileName;
+ cmd.highlight = "linker";
+ cmd.jobPool = "watcom_job_pool";
+ return [cmd];
+}
+
+function prepareLibraryManager(project, product, inputs, outputs, input, output) {
+ var args = libraryManagerFlags(project, product, inputs, outputs);
+ var cmd = new Command(product.cpp.libraryManagerPath, args);
+ cmd.workingDirectory = product.buildDirectory;
+ cmd.description = "linking " + outputs.staticlibrary[0].fileName;
+ cmd.highlight = "linker";
+ cmd.jobPool = "watcom_job_pool";
+ return [cmd];
+}
diff --git a/share/qbs/modules/cpp/watcom.qbs b/share/qbs/modules/cpp/watcom.qbs
new file mode 100644
index 000000000..84157b67b
--- /dev/null
+++ b/share/qbs/modules/cpp/watcom.qbs
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com>
+** 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.Probes
+import "cpp.js" as Cpp
+import "watcom.js" as WATCOM
+
+CppModule {
+ condition: qbs.toolchain && qbs.toolchain.includes("watcom")
+
+ Probes.BinaryProbe {
+ id: compilerPathProbe
+ condition: !toolchainInstallPath && !_skipAllChecks
+ names: ["owcc"]
+ }
+
+ Probes.WatcomProbe {
+ id: watcomProbe
+ condition: !_skipAllChecks
+ compilerFilePath: compilerPath
+ enableDefinesByLanguage: enableCompilerDefinesByLanguage
+ _pathListSeparator: qbs.pathListSeparator
+ _toolchainInstallPath: toolchainInstallPath
+ _targetPlatform: qbs.targetPlatform
+ _targetArchitecture: qbs.architecture
+ }
+
+ qbs.architecture: watcomProbe.found ? watcomProbe.architecture : original
+ qbs.targetPlatform: watcomProbe.found ? watcomProbe.targetPlatform : original
+
+ compilerVersionMajor: watcomProbe.versionMajor
+ compilerVersionMinor: watcomProbe.versionMinor
+ compilerVersionPatch: watcomProbe.versionPatch
+ endianness: watcomProbe.endianness
+
+ compilerDefinesByLanguage: watcomProbe.compilerDefinesByLanguage
+ compilerIncludePaths: watcomProbe.includePaths
+
+ toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path : undefined
+
+ /* Work-around for QtCreator which expects these properties to exist. */
+ property string cCompilerName: compilerName
+ property string cxxCompilerName: compilerName
+
+ compilerName: "owcc" + compilerExtension
+ compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName)
+
+ assemblerName: "wasm" + compilerExtension
+ assemblerPath: FileInfo.joinPaths(toolchainInstallPath, assemblerName)
+
+ linkerName: "wlink" + compilerExtension
+ linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName)
+
+ property string disassemblerName: "wdis" + compilerExtension
+ property string disassemblerPath: FileInfo.joinPaths(toolchainInstallPath,
+ disassemblerName)
+ property string resourceCompilerName: "wrc" + compilerExtension
+ property string resourceCompilerPath: FileInfo.joinPaths(toolchainInstallPath,
+ resourceCompilerName)
+ property string libraryManagerName: "wlib" + compilerExtension
+ property string libraryManagerPath: FileInfo.joinPaths(toolchainInstallPath,
+ libraryManagerName)
+
+ runtimeLibrary: "dynamic"
+
+ staticLibrarySuffix: ".lib"
+ dynamicLibrarySuffix: toolchainDetails.dynamicLibrarySuffix
+ executableSuffix: toolchainDetails.executableSuffix
+ objectSuffix: ".obj"
+
+ imageFormat: toolchainDetails.imageFormat
+
+ defineFlag: "-D"
+ includeFlag: "-I"
+ systemIncludeFlag: "-I"
+ preincludeFlag: "-include"
+ libraryDependencyFlag: "-l"
+ libraryPathFlag: "-L"
+ linkerScriptFlag: ""
+
+ toolchainDetails: WATCOM.toolchainDetails(qbs)
+
+ knownArchitectures: ["x86", "x86_16"]
+
+ property var buildEnv: watcomProbe.environment
+ setupBuildEnvironment: {
+ for (var key in product.cpp.buildEnv) {
+ var v = new ModUtils.EnvironmentVariable(key, product.qbs.pathListSeparator);
+ v.prepend(product.cpp.buildEnv[key]);
+ v.set();
+ }
+ }
+
+ Rule {
+ id: assembler
+ inputs: ["asm"]
+ outputFileTags: Cpp.assemblerOutputTags(generateAssemblerListingFiles)
+ outputArtifacts: Cpp.assemblerOutputArtifacts(input)
+ prepare: WATCOM.prepareAssembler.apply(WATCOM, arguments)
+ }
+
+ FileTagger {
+ patterns: ["*.asm"]
+ fileTags: ["asm"]
+ }
+
+ Rule {
+ id: compiler
+ inputs: ["cpp", "c"]
+ auxiliaryInputs: ["hpp"]
+ outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles)
+ outputArtifacts: Cpp.compilerOutputArtifacts(input)
+ prepare: WATCOM.prepareCompiler.apply(WATCOM, arguments)
+ }
+
+ Rule {
+ id: rccCompiler
+ inputs: ["rc"]
+ auxiliaryInputs: ["hpp"]
+ outputFileTags: Cpp.resourceCompilerOutputTags()
+ outputArtifacts: Cpp.resourceCompilerOutputArtifacts(input)
+ prepare: WATCOM.prepareResourceCompiler.apply(WATCOM, arguments)
+ }
+
+ FileTagger {
+ patterns: ["*.rc"]
+ fileTags: ["rc"]
+ }
+
+ Rule {
+ id: applicationLinker
+ multiplex: true
+ inputs: ["obj", "res", "linkerscript"]
+ inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"]
+ outputFileTags: Cpp.applicationLinkerOutputTags(generateLinkerMapFile)
+ outputArtifacts: Cpp.applicationLinkerOutputArtifacts(product)
+ prepare: WATCOM.prepareLinker.apply(WATCOM, arguments)
+ }
+
+ Rule {
+ id: dynamicLibraryLinker
+ condition: qbs.targetOS.includes("windows")
+ multiplex: true
+ inputs: ["obj", "res"]
+ inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"]
+ outputFileTags: Cpp.dynamicLibraryLinkerOutputTags();
+ outputArtifacts: Cpp.dynamicLibraryLinkerOutputArtifacts(product)
+ prepare: WATCOM.prepareLinker.apply(WATCOM, arguments)
+ }
+
+ Rule {
+ id: libraryManager
+ multiplex: true
+ inputs: ["obj"]
+ inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"]
+ outputFileTags: Cpp.staticLibraryLinkerOutputTags()
+ outputArtifacts: Cpp.staticLibraryLinkerOutputArtifacts(product)
+ prepare: WATCOM.prepareLibraryManager.apply(WATCOM, arguments)
+ }
+
+ JobLimit {
+ jobPool: "watcom_job_pool"
+ jobCount: 1
+ }
+}
diff --git a/share/qbs/modules/cpp/windows-clang-cl.qbs b/share/qbs/modules/cpp/windows-clang-cl.qbs
index 1b2833060..9aa9d7395 100644
--- a/share/qbs/modules/cpp/windows-clang-cl.qbs
+++ b/share/qbs/modules/cpp/windows-clang-cl.qbs
@@ -28,19 +28,19 @@
**
****************************************************************************/
-import qbs
+import qbs.Host
import qbs.ModUtils
import qbs.Probes
import qbs.FileInfo
import 'windows-msvc-base.qbs' as MsvcBaseModule
MsvcBaseModule {
- condition: qbs.hostOS.contains('windows') &&
- qbs.targetOS.contains('windows') &&
- qbs.toolchain && qbs.toolchain.contains('clang-cl')
+ condition: Host.os().includes('windows') &&
+ qbs.targetOS.includes('windows') &&
+ qbs.toolchain && qbs.toolchain.includes('clang-cl')
priority: 100
- Probes.BinaryProbe {
+ Probes.ClangClBinaryProbe {
id: clangPathProbe
condition: !toolchainInstallPath && !_skipAllChecks
names: ["clang-cl"]
@@ -52,9 +52,12 @@ MsvcBaseModule {
compilerFilePath: compilerPath
vcvarsallFilePath: vcvarsallPath
enableDefinesByLanguage: enableCompilerDefinesByLanguage
- architecture: qbs.architecture
+ preferredArchitecture: qbs.architecture
+ winSdkVersion: windowsSdkVersion
}
+ qbs.architecture: clangClProbe.found ? clangClProbe.architecture : original
+
compilerVersionMajor: clangClProbe.versionMajor
compilerVersionMinor: clangClProbe.versionMinor
compilerVersionPatch: clangClProbe.versionPatch
@@ -65,20 +68,34 @@ MsvcBaseModule {
: undefined
buildEnv: clangClProbe.buildEnv
- property string vcvarsallPath
+ property string linkerVariant
+ PropertyOptions {
+ name: "linkerVariant"
+ allowedValues: ["lld", "link"]
+ description: "Allows to specify the linker variant. Maps to clang-cl's -fuse-ld option."
+ }
+ Properties {
+ condition: linkerVariant
+ driverLinkerFlags: "-fuse-ld=" + linkerVariant
+ }
+
+ property string vcvarsallPath : clangPathProbe.found ? clangPathProbe.vcvarsallPath : undefined
compilerName: "clang-cl.exe"
linkerName: "lld-link.exe"
linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName)
+ systemIncludeFlag: "/imsvc"
+
validateFunc: {
+ var baseFunc = base;
return function() {
if (_skipAllChecks)
return;
var validator = new ModUtils.PropertyValidator("cpp");
validator.setRequiredProperty("vcvarsallPath", vcvarsallPath);
validator.validate();
- base();
+ baseFunc();
}
}
}
diff --git a/share/qbs/modules/cpp/windows-clang-mingw.qbs b/share/qbs/modules/cpp/windows-clang-mingw.qbs
index 8389dbf2e..cfe6f2745 100644
--- a/share/qbs/modules/cpp/windows-clang-mingw.qbs
+++ b/share/qbs/modules/cpp/windows-clang-mingw.qbs
@@ -30,6 +30,7 @@
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.Utilities
import "msvc.js" as MSVC
@@ -37,8 +38,8 @@ import "msvc.js" as MSVC
import "setuprunenv.js" as SetupRunEnv
MingwBaseModule {
- condition: qbs.targetOS.contains("windows") &&
- qbs.toolchain && qbs.toolchain.contains("clang")
+ condition: qbs.targetOS.includes("windows") &&
+ qbs.toolchain && qbs.toolchain.includes("clang")
priority: 0
// llvm-as and llvm-objopy are not shipped with the official binaries on Windows at the
@@ -68,16 +69,16 @@ MingwBaseModule {
"llvm-rc" + compilerExtension)
setupBuildEnvironment: {
- if (product.qbs.hostOS.contains("windows") && product.qbs.sysroot) {
- var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true);
+ if (Host.os().includes("windows") && product.qbs.sysroot) {
+ var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true);
v.prepend(FileInfo.joinPaths(product.qbs.sysroot, "bin"));
v.set();
}
}
setupRunEnvironment: {
- if (product.qbs.hostOS.contains("windows") && product.qbs.sysroot) {
- var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true);
+ if (Host.os().includes("windows") && product.qbs.sysroot) {
+ var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true);
v.prepend(FileInfo.joinPaths(product.qbs.sysroot, "bin"));
v.set();
SetupRunEnv.setupRunEnvironment(product, config);
@@ -89,10 +90,11 @@ MingwBaseModule {
auxiliaryInputs: ["hpp"]
Artifact {
- filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".res"
+ filePath: FileInfo.joinPaths(Utilities.getHash(input.baseDir),
+ input.completeBaseName + ".res")
fileTags: ["obj"]
}
- prepare: MSVC.createRcCommand(product.cpp.rcFilePath, input, output);
+ prepare: MSVC.createRcCommand(product.cpp.rcFilePath, input, output)
}
}
diff --git a/share/qbs/modules/cpp/windows-mingw.qbs b/share/qbs/modules/cpp/windows-mingw.qbs
index ffed76cdd..9ba4258c5 100644
--- a/share/qbs/modules/cpp/windows-mingw.qbs
+++ b/share/qbs/modules/cpp/windows-mingw.qbs
@@ -33,11 +33,12 @@ import qbs.FileInfo
import qbs.ModUtils
import qbs.Utilities
+import 'cpp.js' as Cpp
import "setuprunenv.js" as SetupRunEnv
MingwBaseModule {
- condition: qbs.targetOS.contains("windows") &&
- qbs.toolchain && qbs.toolchain.contains("mingw")
+ condition: qbs.targetOS.includes("windows") &&
+ qbs.toolchain && qbs.toolchain.includes("mingw")
priority: 0
probeEnv: buildEnv
@@ -51,13 +52,13 @@ MingwBaseModule {
}
setupBuildEnvironment: {
- var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true);
+ var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true);
v.prepend(product.cpp.toolchainInstallPath);
v.set();
}
setupRunEnvironment: {
- var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true);
+ var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true);
v.prepend(product.cpp.toolchainInstallPath);
v.set();
SetupRunEnv.setupRunEnvironment(product, config);
@@ -66,12 +67,8 @@ MingwBaseModule {
Rule {
inputs: ["rc"]
auxiliaryInputs: ["hpp"]
-
- Artifact {
- filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + "_res.o"
- fileTags: ["obj"]
- }
-
+ outputFileTags: Cpp.resourceCompilerOutputTags()
+ outputArtifacts: Cpp.resourceCompilerOutputArtifacts(input)
prepare: {
var platformDefines = input.cpp.platformDefines;
var defines = input.cpp.defines;
@@ -96,6 +93,7 @@ MingwBaseModule {
args.push(systemIncludePaths[i]);
}
+ args.push("-O", "coff"); // Set COFF format explicitly.
args = args.concat(['-i', input.filePath, '-o', output.filePath]);
var cmd = new Command(product.cpp.windresPath, args);
cmd.description = 'compiling ' + input.fileName;
diff --git a/share/qbs/modules/cpp/windows-msvc-base.qbs b/share/qbs/modules/cpp/windows-msvc-base.qbs
index aa1c8256c..9613800e5 100644
--- a/share/qbs/modules/cpp/windows-msvc-base.qbs
+++ b/share/qbs/modules/cpp/windows-msvc-base.qbs
@@ -35,11 +35,14 @@ import qbs.PathTools
import qbs.Probes
import qbs.Utilities
import qbs.WindowsUtils
+import 'cpp.js' as Cpp
import 'msvc.js' as MSVC
CppModule {
condition: false
+ Depends { name: "codesign" }
+
windowsApiCharacterSet: "unicode"
platformDefines: {
var defines = base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet))
@@ -81,7 +84,6 @@ CppModule {
separateDebugInformation: true
property bool generateManifestFile: true
- property string toolchainInstallPath
architecture: qbs.architecture
endianness: "little"
@@ -89,9 +91,11 @@ CppModule {
dynamicLibrarySuffix: ".dll"
executableSuffix: ".exe"
debugInfoSuffix: ".pdb"
+ objectSuffix: ".obj"
+ precompiledHeaderSuffix: ".pch"
imageFormat: "pe"
Properties {
- condition: product.multiplexByQbsProperties.contains("buildVariants")
+ condition: product.multiplexByQbsProperties.includes("buildVariants")
&& qbs.buildVariants && qbs.buildVariants.length > 1
&& qbs.buildVariant !== "release"
&& product.type.containsAny(["staticlibrary", "dynamiclibrary"])
@@ -100,6 +104,9 @@ CppModule {
property var buildEnv
+ readonly property bool shouldSignArtifacts: codesign.enableCodeSigning
+ property bool enableCxxLanguageMacro: false
+
setupBuildEnvironment: {
for (var key in product.cpp.buildEnv) {
var v = new ModUtils.EnvironmentVariable(key, ';');
@@ -108,21 +115,21 @@ CppModule {
}
}
+ property string windowsSdkVersion
+
+ defineFlag: "/D"
+ includeFlag: "/I"
+ systemIncludeFlag: "/external:I"
+ preincludeFlag: "/FI"
+ libraryPathFlag: "/LIBPATH:"
+
Rule {
condition: useCPrecompiledHeader
inputs: ["c_pch_src"]
auxiliaryInputs: ["hpp"]
- Artifact {
- fileTags: ['obj']
- filePath: Utilities.getHash(input.completeBaseName) + '_c.obj'
- }
- Artifact {
- fileTags: ['c_pch']
- filePath: product.name + '_c.pch'
- }
- prepare: {
- return MSVC.prepareCompiler.apply(MSVC, arguments);
- }
+ outputFileTags: Cpp.precompiledHeaderOutputTags("c", true)
+ outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "c", true)
+ prepare: MSVC.prepareCompiler.apply(MSVC, arguments)
}
Rule {
@@ -130,17 +137,9 @@ CppModule {
inputs: ["cpp_pch_src"]
explicitlyDependsOn: ["c_pch"] // to prevent vc--0.pdb conflict
auxiliaryInputs: ["hpp"]
- Artifact {
- fileTags: ['obj']
- filePath: Utilities.getHash(input.completeBaseName) + '_cpp.obj'
- }
- Artifact {
- fileTags: ['cpp_pch']
- filePath: product.name + '_cpp.pch'
- }
- prepare: {
- return MSVC.prepareCompiler.apply(MSVC, arguments);
- }
+ outputFileTags: Cpp.precompiledHeaderOutputTags("cpp", true)
+ outputArtifacts: Cpp.precompiledHeaderOutputArtifacts(input, product, "cpp", true)
+ prepare: MSVC.prepareCompiler.apply(MSVC, arguments)
}
Rule {
@@ -148,21 +147,9 @@ CppModule {
inputs: ["cpp", "c"]
auxiliaryInputs: ["hpp"]
explicitlyDependsOn: ["c_pch", "cpp_pch"]
-
- outputFileTags: ["obj", "intermediate_obj"]
- outputArtifacts: {
- var tags = input.fileTags.contains("cpp_intermediate_object")
- ? ["intermediate_obj"]
- : ["obj"];
- return [{
- fileTags: tags,
- filePath: Utilities.getHash(input.baseDir) + "/" + input.fileName + ".obj"
- }];
- }
-
- prepare: {
- return MSVC.prepareCompiler.apply(MSVC, arguments);
- }
+ outputFileTags: Cpp.compilerOutputTags(generateCompilerListingFiles)
+ outputArtifacts: Cpp.compilerOutputArtifacts(input)
+ prepare: MSVC.prepareCompiler.apply(MSVC, arguments)
}
FileTagger {
@@ -170,16 +157,29 @@ CppModule {
fileTags: ["native.pe.manifest"]
}
+ FileTagger {
+ patterns: ["*.def"]
+ fileTags: ["def"]
+ }
+
Rule {
name: "applicationLinker"
multiplex: true
- inputs: ['obj', 'native.pe.manifest']
+ inputs: ['obj', 'res', 'native.pe.manifest', 'def']
inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo_app"]
- outputFileTags: ["application", "debuginfo_app", "mem_map"]
+ outputFileTags: {
+ var tags = ["application", "debuginfo_app"];
+ if (generateLinkerMapFile)
+ tags.push("mem_map");
+ if (shouldSignArtifacts)
+ tags.push("codesign.signed_artifact");
+ return tags;
+ }
outputArtifacts: {
var app = {
- fileTags: ["application"],
+ fileTags: ["application"].concat(
+ product.cpp.shouldSignArtifacts ? ["codesign.signed_artifact"] : []),
filePath: FileInfo.joinPaths(
product.destinationDirectory,
PathTools.applicationFilePath(product))
@@ -197,33 +197,39 @@ CppModule {
fileTags: ["mem_map"],
filePath: FileInfo.joinPaths(
product.destinationDirectory,
- product.targetName + ".map")
+ product.targetName + product.cpp.linkerMapSuffix)
});
}
return artifacts;
}
- prepare: {
- return MSVC.prepareLinker.apply(MSVC, arguments);
- }
+ prepare: MSVC.prepareLinker.apply(MSVC, arguments)
}
Rule {
name: "dynamicLibraryLinker"
multiplex: true
- inputs: ['obj', 'native.pe.manifest']
+ inputs: ['obj', 'res', 'native.pe.manifest', 'def']
inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo_dll"]
- outputFileTags: ["dynamiclibrary", "dynamiclibrary_import", "debuginfo_dll"]
+ outputFileTags: {
+ var tags = ["dynamiclibrary", "dynamiclibrary_import", "debuginfo_dll"];
+ if (shouldSignArtifacts)
+ tags.push("codesign.signed_artifact");
+ return tags;
+ }
outputArtifacts: {
var artifacts = [
{
- fileTags: ["dynamiclibrary"],
- filePath: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath(product)
+ fileTags: ["dynamiclibrary"].concat(
+ product.cpp.shouldSignArtifacts ? ["codesign.signed_artifact"] : []),
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ PathTools.dynamicLibraryFilePath(product))
},
{
fileTags: ["dynamiclibrary_import"],
- filePath: product.destinationDirectory + "/" + PathTools.importLibraryFilePath(product),
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ PathTools.importLibraryFilePath(product)),
alwaysUpdated: false
}
];
@@ -238,15 +244,13 @@ CppModule {
return artifacts;
}
- prepare: {
- return MSVC.prepareLinker.apply(MSVC, arguments);
- }
+ prepare: MSVC.prepareLinker.apply(MSVC, arguments)
}
Rule {
name: "libtool"
multiplex: true
- inputs: ["obj"]
+ inputs: ["obj", "res"]
inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"]
outputFileTags: ["staticlibrary", "debuginfo_cl"]
outputArtifacts: {
@@ -274,6 +278,10 @@ CppModule {
var fileName = FileInfo.toWindowsSeparators(inputs.obj[i].filePath)
args.push(fileName)
}
+ for (var i in inputs.res) {
+ var fileName = FileInfo.toWindowsSeparators(inputs.res[i].filePath)
+ args.push(fileName)
+ }
var cmd = new Command("lib.exe", args);
cmd.description = 'creating ' + lib.fileName;
cmd.highlight = 'linker';
@@ -292,12 +300,8 @@ CppModule {
Rule {
inputs: ["rc"]
auxiliaryInputs: ["hpp"]
-
- Artifact {
- filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".res"
- fileTags: ["obj"]
- }
-
+ outputFileTags: Cpp.resourceCompilerOutputTags()
+ outputArtifacts: Cpp.resourceCompilerOutputArtifacts(input)
prepare: {
// From MSVC 2010 on, the logo can be suppressed.
var logo = product.cpp.compilerVersionMajor >= 16
@@ -313,18 +317,14 @@ CppModule {
Rule {
inputs: ["asm"]
- Artifact {
- filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".obj"
- fileTags: ["obj"]
- }
+ outputFileTags: Cpp.assemblerOutputTags(false)
+ outputArtifacts: Cpp.assemblerOutputArtifacts(input)
prepare: {
- var args = ["/nologo", "/c",
- "/Fo" + FileInfo.toWindowsSeparators(output.filePath),
- FileInfo.toWindowsSeparators(input.filePath)];
+ var args = ["/nologo", "/c", "/Fo" + FileInfo.toWindowsSeparators(output.filePath)];
if (product.cpp.debugInformation)
args.push("/Zi");
- args = args.concat(ModUtils.moduleProperty(input, 'platformFlags', 'asm'),
- ModUtils.moduleProperty(input, 'flags', 'asm'));
+ args = args.concat(Cpp.collectMiscAssemblerArguments(input, "asm"));
+ args.push(FileInfo.toWindowsSeparators(input.filePath));
var cmd = new Command(product.cpp.assemblerPath, args);
cmd.description = "assembling " + input.fileName;
cmd.jobPool = "assembler";
diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs
index 59032d28a..c30cec239 100644
--- a/share/qbs/modules/cpp/windows-msvc.qbs
+++ b/share/qbs/modules/cpp/windows-msvc.qbs
@@ -29,17 +29,19 @@
**
****************************************************************************/
+import qbs.Host
import qbs.Probes
import "windows-msvc-base.qbs" as MsvcBaseModule
MsvcBaseModule {
- condition: qbs.hostOS.contains('windows') &&
- qbs.targetOS.contains('windows') &&
- qbs.toolchain && qbs.toolchain.contains('msvc')
+ condition: Host.os().includes('windows') &&
+ qbs.targetOS.includes('windows') &&
+ qbs.toolchain && qbs.toolchain.includes('msvc')
priority: 50
- Probes.BinaryProbe {
+ Probes.ClBinaryProbe {
id: compilerPathProbe
+ preferredArchitecture: qbs.architecture
condition: !toolchainInstallPath && !_skipAllChecks
names: ["cl"]
}
@@ -50,6 +52,7 @@ MsvcBaseModule {
compilerFilePath: compilerPath
enableDefinesByLanguage: enableCompilerDefinesByLanguage
preferredArchitecture: qbs.architecture
+ winSdkVersion: windowsSdkVersion
}
qbs.architecture: msvcProbe.found ? msvcProbe.architecture : original
@@ -63,4 +66,6 @@ MsvcBaseModule {
toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path
: undefined
buildEnv: msvcProbe.buildEnv
+
+ enableCxxLanguageMacro: true
}
diff --git a/share/qbs/modules/dmg/DMGModule.qbs b/share/qbs/modules/dmg/DMGModule.qbs
index c5d097a0b..27a945033 100644
--- a/share/qbs/modules/dmg/DMGModule.qbs
+++ b/share/qbs/modules/dmg/DMGModule.qbs
@@ -31,15 +31,17 @@
import qbs.DarwinTools
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.Process
+import qbs.Probes
import qbs.TextFile
import "dmg.js" as Dmg
Module {
Depends { name: "xcode"; required: false }
- condition: qbs.hostOS.contains("darwin") && qbs.targetOS.contains("darwin")
+ condition: Host.os().includes("darwin") && qbs.targetOS.includes("darwin")
property string volumeName: product.targetName
PropertyOptions {
@@ -72,6 +74,12 @@ Module {
property string sourceBase
+ Probes.BinaryProbe {
+ id: pythonProbe
+ names: ["python3"]
+ }
+ property string pythonExePath: pythonProbe.found ? pythonProbe.filePath : "python3"
+
readonly property string pythonPath: File.canonicalFilePath(FileInfo.joinPaths(path,
"..", "..",
"python"))
diff --git a/share/qbs/modules/dmg/dmg.js b/share/qbs/modules/dmg/dmg.js
index 4d972db9b..06fe7b906 100644
--- a/share/qbs/modules/dmg/dmg.js
+++ b/share/qbs/modules/dmg/dmg.js
@@ -57,7 +57,7 @@ function dmgbuildSettings(product, inputs) {
volumeIcon = volumeIcons[0].filePath;
}
- var licenseFileObjects = Array.prototype.map.call(inputs["dmg.license"], function (a) {
+ var licenseFileObjects = Array.prototype.map.call(inputs["dmg.license"] || [], function (a) {
return {
"dmg": {
"licenseLocale": localizationFromArtifact(a),
@@ -97,14 +97,14 @@ function dmgbuildSettings(product, inputs) {
}, {});
}
- var contentsArray = Array.prototype.map.call(inputs["dmg.input"], function (a) {
+ var contentsArray = Array.prototype.map.call(inputs["dmg.input"] || [], function (a) {
if (a.dmg.sourceBase && !a.filePath.startsWith(a.dmg.sourceBase)) {
throw new Error("Cannot install '" + a.filePath + "', " +
"because it doesn't start with the value of " +
"dmg.sourceBase '" + a.dmg.sourceBase + "'.");
}
- var isSymlink = a.fileTags.contains("dmg.input.symlink");
+ var isSymlink = a.fileTags.includes("dmg.input.symlink");
return {
"x": a.dmg.iconX,
"y": a.dmg.iconY,
@@ -114,7 +114,7 @@ function dmgbuildSettings(product, inputs) {
};
});
- Array.prototype.forEach.call(product.dmg.iconPositions, function (obj) {
+ Array.prototype.forEach.call(product.dmg.iconPositions || [], function (obj) {
var existingIndex = -1;
Array.prototype.forEach.call(contentsArray, function (contentsItem, i) {
if (contentsItem["name"] === obj["path"])
@@ -135,7 +135,7 @@ function dmgbuildSettings(product, inputs) {
}
});
- return {
+ var result = {
"title": product.dmg.volumeName,
"icon": !product.dmg.badgeVolumeIcon ? volumeIcon : undefined,
"badge-icon": product.dmg.badgeVolumeIcon ? volumeIcon : undefined,
@@ -154,13 +154,16 @@ function dmgbuildSettings(product, inputs) {
},
"format": product.dmg.format,
"compression-level": product.dmg.compressionLevel,
- "license": {
+ "contents": contentsArray
+ };
+ if (licenseFileObjects.length >= 0) {
+ result["license"] = {
"default-language": product.dmg.defaultLicenseLocale,
"licenses": reduceLicensesForKey(licenseFileObjects, "licenses"),
"buttons": reduceLicensesForKey(licenseFileObjects, "buttons")
- },
- "contents": contentsArray
- };
+ };
+ }
+ return result;
}
function prepareLicense(project, product, inputs, outputs, input, output) {
@@ -199,8 +202,10 @@ function prepareDmg(project, product, inputs, outputs, input, output) {
cmds.push(cmd);
// Create the actual DMG via dmgbuild
- cmd = new Command(FileInfo.joinPaths(product.qbs.libexecPath, "dmgbuild"),
- [product.dmg.volumeName,
+ cmd = new Command(product.dmg.pythonExePath,
+ ["-m",
+ "dmgbuild",
+ product.dmg.volumeName,
output.filePath,
"--no-hidpi", // qbs handles this by itself
"--settings", settingsJsonFilePath]);
diff --git a/share/qbs/modules/freedesktop/FreeDesktop.qbs b/share/qbs/modules/freedesktop/FreeDesktop.qbs
new file mode 100644
index 000000000..60722c674
--- /dev/null
+++ b/share/qbs/modules/freedesktop/FreeDesktop.qbs
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Alberto Mardegan <mardy@users.sourceforge.net>
+** 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.ModUtils
+import qbs.TextFile
+import "freedesktop.js" as Fdo
+
+Module {
+ property string name: product.name
+ property string appName: name
+
+ property var desktopKeys
+
+ readonly property var defaultDesktopKeys: {
+ return {
+ 'Type': 'Application',
+ 'Name': product.freedesktop.appName,
+ 'Exec': product.targetName,
+ 'Terminal': 'false',
+ 'Version': '1.1',
+ }
+ }
+ property bool _fdoSupported: qbs.targetOS.includes("unix") && !qbs.targetOS.includes("darwin")
+
+ additionalProductTypes: "freedesktop.desktopfile"
+
+ FileTagger {
+ patterns: [ "*.desktop" ]
+ fileTags: [ "freedesktop.desktopfile_source" ]
+ }
+
+ Rule {
+ condition: _fdoSupported
+
+ inputs: [ "freedesktop.desktopfile_source" ]
+ outputFileTags: [ "freedesktop.desktopfile" ]
+
+ Artifact {
+ fileTags: [ "freedesktop.desktopfile" ]
+ filePath: input.fileName
+ }
+
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating " + output.fileName + " from " + input.fileName;
+ cmd.highlight = "codegen";
+ cmd.sourceCode = function() {
+ var aggregateDesktopKeys = Fdo.parseDesktopFile(input.filePath);
+ var desktopKeys = ModUtils.moduleProperty(product, "desktopKeys") || {}
+ var mainSection = aggregateDesktopKeys['Desktop Entry'];
+ for (key in desktopKeys) {
+ if (desktopKeys.hasOwnProperty(key)) {
+ mainSection[key] = desktopKeys[key];
+ }
+ }
+
+ var defaultValues = product.freedesktop.defaultDesktopKeys
+ for (key in defaultValues) {
+ if (!(key in mainSection)) {
+ mainSection[key] = defaultValues[key];
+ }
+ }
+
+ Fdo.dumpDesktopFile(output.filePath, aggregateDesktopKeys);
+ }
+ return [cmd];
+ }
+ }
+
+ Group {
+ condition: product.freedesktop._fdoSupported
+ fileTagsFilter: [ "freedesktop.desktopfile" ]
+ qbs.install: true
+ qbs.installDir: "share/applications"
+ }
+
+ Group {
+ condition: product.freedesktop._fdoSupported
+ fileTagsFilter: [ "freedesktop.appIcon" ]
+ qbs.install: true
+ qbs.installDir: "share/icons/hicolor/scalable/apps"
+ }
+
+ FileTagger {
+ patterns: [ "*.metainfo.xml", "*.appdata.xml" ]
+ fileTags: [ "freedesktop.appstream" ]
+ }
+
+ Group {
+ condition: product.freedesktop._fdoSupported
+ fileTagsFilter: [ "freedesktop.appstream" ]
+ qbs.install: true
+ qbs.installDir: "share/metainfo"
+ }
+}
diff --git a/share/qbs/modules/freedesktop/freedesktop.js b/share/qbs/modules/freedesktop/freedesktop.js
new file mode 100644
index 000000000..d3c60b191
--- /dev/null
+++ b/share/qbs/modules/freedesktop/freedesktop.js
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 Alberto Mardegan <mardy@users.sourceforge.net>
+** 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 TextFile = require("qbs.TextFile");
+
+function parseDesktopFile(filePath) {
+ var file = new TextFile(filePath);
+ var fileValues = {};
+ var currentSection = {};
+ var sectionRex = /\[(.+)\]/g;
+ while (!file.atEof()) {
+ var line = file.readLine().trim();
+ if (line.length == 0) continue;
+ if (line[0] == '#') continue;
+ var match = sectionRex.exec(line);
+ if (match) {
+ var currentSectionName = match[1];
+ fileValues[currentSectionName] = {};
+ currentSection = fileValues[currentSectionName];
+ continue;
+ }
+ var parts = line.split('=', 2);
+ currentSection[parts[0]] = parts[1]
+ }
+ file.close();
+ return fileValues
+}
+
+function dumpDesktopFile(filePath, keys) {
+ var file = new TextFile(filePath, TextFile.WriteOnly);
+ for (var sectionName in keys) {
+ file.writeLine('[' + sectionName + ']');
+ var section = keys[sectionName];
+ for (var key in section) {
+ var line = key + '=' + section[key];
+ file.writeLine(line);
+ }
+ // Write an empty line between sections (and before EOF)
+ file.writeLine('');
+ }
+ file.close();
+}
diff --git a/share/qbs/modules/ib/IBModule.qbs b/share/qbs/modules/ib/IBModule.qbs
index 6af5e94e4..e6183e20c 100644
--- a/share/qbs/modules/ib/IBModule.qbs
+++ b/share/qbs/modules/ib/IBModule.qbs
@@ -31,6 +31,7 @@
import qbs.BundleTools
import qbs.DarwinTools
import qbs.File
+import qbs.Host
import qbs.FileInfo
import qbs.ModUtils
import qbs.Process
@@ -49,7 +50,7 @@ Module {
}
}
- condition: qbs.hostOS.contains("darwin") && qbs.targetOS.contains("darwin")
+ condition: Host.os().includes("darwin") && qbs.targetOS.includes("darwin")
property bool warnings: true
property bool errors: true
@@ -195,7 +196,7 @@ Module {
cmd.highlight = 'compiler';
// May not be strictly needed, but is set by some versions of Xcode
- if (input.fileTags.contains("storyboard"))
+ if (input.fileTags.includes("storyboard"))
cmd.environment.push("IBSC_MINIMUM_COMPATIBILITY_VERSION=" +
(product.moduleProperty("cpp", "minimumDarwinVersion") || ""));
diff --git a/share/qbs/modules/ib/ib.js b/share/qbs/modules/ib/ib.js
index 40bd1dc23..d6873b261 100644
--- a/share/qbs/modules/ib/ib.js
+++ b/share/qbs/modules/ib/ib.js
@@ -116,7 +116,7 @@ function ibtooldArguments(product, inputs, input, outputs, overrideOutput) {
var outputFormat = ModUtils.moduleProperty(product, "outputFormat");
if (outputFormat) {
- if (!["binary1", "xml1", "human-readable-text"].contains(outputFormat))
+ if (!["binary1", "xml1", "human-readable-text"].includes(outputFormat))
throw("Invalid ibtoold output format: " + outputFormat + ". " +
"Must be in [binary1, xml1, human-readable-text].");
@@ -173,7 +173,7 @@ function ibtooldArguments(product, inputs, input, outputs, overrideOutput) {
// --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
+ ? outputs.partial_infoplist[outputs.partial_infoplist.length - 1].filePath
: "/dev/null");
// For iOS, we'd normally only output the devices specified in TARGETED_DEVICE_FAMILY
@@ -205,9 +205,9 @@ function ibtooldArguments(product, inputs, input, outputs, overrideOutput) {
function ibtoolFileTaggers(fileTags) {
var ext;
- if (fileTags.contains("nib") && !fileTags.contains("storyboard"))
+ if (fileTags.includes("nib") && !fileTags.includes("storyboard"))
ext = "nib";
- if (fileTags.contains("storyboard") && !fileTags.contains("nib"))
+ if (fileTags.includes("storyboard") && !fileTags.includes("nib"))
ext = "storyboard";
if (!ext)
@@ -223,9 +223,9 @@ function ibtoolFileTaggers(fileTags) {
function ibtoolCompiledDirSuffix(product, input) {
var suffix = input.completeBaseName;
- if (input.fileTags.contains("nib"))
+ if (input.fileTags.includes("nib"))
suffix += ModUtils.moduleProperty(product, "compiledNibSuffix");
- else if (input.fileTags.contains("storyboard"))
+ else if (input.fileTags.includes("storyboard"))
suffix += ModUtils.moduleProperty(product, "compiledStoryboardSuffix");
return suffix;
}
@@ -252,7 +252,7 @@ function ibtoolOutputArtifacts(product, inputs, input) {
var artifacts = tracker.artifacts(ibtoolBuildDirectory);
if (product.moduleProperty("ib", "ibtoolVersionMajor") >= 6) {
- var prefix = input.fileTags.contains("storyboard") ? "SB" : "";
+ var prefix = input.fileTags.includes("storyboard") ? "SB" : "";
var path = FileInfo.joinPaths(product.destinationDirectory, input.completeBaseName +
"-" + prefix + "PartialInfo.plist");
artifacts.push({ filePath: path, fileTags: ["partial_infoplist"] });
@@ -261,7 +261,7 @@ function ibtoolOutputArtifacts(product, inputs, input) {
// Let the output artifacts known the "main" output
// This can be either a file or directory so the artifact might already exist in the output list
for (var i = 0; i < artifacts.length; ++i) {
- if (artifacts[i].fileTags.contains("compiled_ibdoc"))
+ if (artifacts[i].fileTags.includes("compiled_ibdoc"))
artifacts[i].bundle = {
_bundleFilePath: artifacts[i].filePath.replace(ibtoolBuildDirectory, main)
};
@@ -296,7 +296,7 @@ function actoolOutputArtifacts(product, inputs) {
}
for (var i = 0; i < artifacts.length; ++i) {
- if (artifacts[i].fileTags.contains("compiled_assetcatalog")) {
+ if (artifacts[i].fileTags.includes("compiled_assetcatalog")) {
artifacts[i].bundle = {
_bundleFilePath: artifacts[i].filePath.replace(
product.buildDirectory + "/actool.dir",
diff --git a/share/qbs/modules/ico/ico.js b/share/qbs/modules/ico/ico.js
index 997a6dc23..dc96eedf3 100644
--- a/share/qbs/modules/ico/ico.js
+++ b/share/qbs/modules/ico/ico.js
@@ -29,6 +29,7 @@
****************************************************************************/
var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
var Process = require("qbs.Process");
function prepareIconset(project, product, inputs, outputs, input, output) {
@@ -39,20 +40,20 @@ function prepareIconset(project, product, inputs, outputs, input, output) {
ico: {}
};
});
- inputs = {"png": inputs.filter(function (a) { return a.fileTags.contains("png"); })};
+ inputs = {"png": inputs.filter(function (a) { return a.fileTags.includes("png"); })};
input = undefined;
return prepare(project, product, inputs, outputs, input, output);
}
function prepare(project, product, inputs, outputs, input, output) {
var args = ["--create", "--output=" + output.filePath];
- if (output.fileTags.contains("ico")) {
+ if (output.fileTags.includes("ico")) {
args.push("--icon");
if (product.ico.alphaThreshold !== undefined)
args.push("--alpha-threshold=" + product.ico.alphaThreshold);
}
- var isCursor = output.fileTags.contains("cur");
+ var isCursor = output.fileTags.includes("cur");
if (isCursor)
args.push("--cursor");
diff --git a/share/qbs/modules/innosetup/InnoSetupModule.qbs b/share/qbs/modules/innosetup/InnoSetupModule.qbs
index 1caf39dc8..9f1018ec4 100644
--- a/share/qbs/modules/innosetup/InnoSetupModule.qbs
+++ b/share/qbs/modules/innosetup/InnoSetupModule.qbs
@@ -33,7 +33,7 @@ import qbs.ModUtils
import qbs.Probes
Module {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
Probes.InnoSetupProbe {
id: innoSetupProbe
@@ -76,6 +76,10 @@ Module {
readonly property string executableSuffix: ".exe"
validate: {
+ if (!innoSetupProbe.found) {
+ throw ModUtils.ModuleError("Could not find InnoSetup in Windows registry. Make " +
+ "sure InnoSetup is installed correctly.");
+ }
var validator = new ModUtils.PropertyValidator("innosetup");
validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath);
validator.setRequiredProperty("version", version);
diff --git a/share/qbs/modules/java/JavaModule.qbs b/share/qbs/modules/java/JavaModule.qbs
index 71f7d8432..343b2f4ba 100644
--- a/share/qbs/modules/java/JavaModule.qbs
+++ b/share/qbs/modules/java/JavaModule.qbs
@@ -29,6 +29,7 @@
****************************************************************************/
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.Probes
import qbs.Process
@@ -122,10 +123,10 @@ Module {
} else {
paths.push(FileInfo.joinPaths(jdkPath, "include"));
- var hostOS = qbs.hostOS.contains("windows") ? qbs.hostOS.concat(["win32"]) : qbs.hostOS;
+ var hostOS = Host.os().includes("windows") ? Host.os().concat(["win32"]) : Host.os();
var platforms = ["win32", "darwin", "linux", "bsd", "solaris"];
for (var i = 0; i < platforms.length; ++i) {
- if (hostOS.contains(platforms[i])) {
+ if (hostOS.includes(platforms[i])) {
// Corresponds to JDK_INCLUDE_SUBDIR in the JDK Makefiles
paths.push(FileInfo.joinPaths(jdkPath, "include", platforms[i]));
break;
@@ -140,7 +141,7 @@ Module {
property path classFilesDir: FileInfo.joinPaths(product.buildDirectory, "classes")
property path internalClassFilesDir: FileInfo.joinPaths(product.buildDirectory, ".classes")
- property bool isAppleJava: qbs.hostOS.contains("darwin")
+ property bool isAppleJava: Host.os().includes("darwin")
&& (compilerVersionMajor < 1
|| (compilerVersionMajor === 1 && compilerVersionMinor < 7))
@@ -234,14 +235,18 @@ Module {
inputsFromDependencies: ["java.jar"]
explicitlyDependsOn: ["java.class-internal"]
- outputFileTags: ["java.class"].concat(_tagJniHeaders ? ["hpp"] : []) // Annotations can produce additional java source files. Ignored for now.
+ outputFileTags: ["java.class"].concat(_tagJniHeaders ? ["hpp"] : ["java.jni-hpp"]) // Annotations can produce additional java source files. Ignored for now.
outputArtifacts: {
var artifacts = JavaUtils.outputArtifacts(product, inputs);
if (!product.java._tagJniHeaders) {
for (var i = 0; i < artifacts.length; ++i) {
var a = artifacts[i];
- if (Array.isArray(a.fileTags))
- a.fileTags = a.fileTags.filter(function(tag) { return tag != "hpp"; });
+ if (a.fileTags instanceof Array)
+ a.fileTags = a.fileTags.map(function(tag) {
+ if (tag === "hpp")
+ return "java.jni-hpp";
+ return tag;
+ });
}
}
return artifacts;
@@ -249,7 +254,7 @@ Module {
prepare: {
var cmd = new Command(ModUtils.moduleProperty(product, "compilerFilePath"),
JavaUtils.javacArguments(product, inputs));
- cmd.description = "Compiling Java sources";
+ cmd.description = "compiling Java sources";
cmd.highlight = "compiler";
return [cmd];
}
diff --git a/share/qbs/modules/java/utils.js b/share/qbs/modules/java/utils.js
index ae0c1356b..a59b8d41d 100644
--- a/share/qbs/modules/java/utils.js
+++ b/share/qbs/modules/java/utils.js
@@ -74,13 +74,12 @@ function jdkRootRegistryKey(wow64) {
function findJdkPath(hostOS, arch, environmentPaths, searchPaths) {
var i;
- for (var key in environmentPaths) {
- if (environmentPaths[key]) {
- return environmentPaths[key];
- }
+ for (i = 0; i < environmentPaths.length; ++i) {
+ if (environmentPaths[i])
+ return environmentPaths[i];
}
- if (hostOS.contains("windows")) {
+ if (hostOS.includes("windows")) {
var rootKey = jdkRootRegistryKey(useWow64Key(arch));
if (rootKey) {
var current = Utilities.getNativeSetting(rootKey, "CurrentVersion"); // 1.8 etc.
@@ -95,27 +94,55 @@ function findJdkPath(hostOS, arch, environmentPaths, searchPaths) {
return undefined;
}
- if (hostOS.contains("macos")) {
+ if (hostOS.includes("macos")) {
var p = new Process();
try {
// We filter by architecture here so that we'll get a compatible JVM for JNI use.
var args = [];
+ var canonicalArch;
if (arch) {
// Hardcoding apple/macosx/macho here is fine because we know we're on macOS
- args.push("--arch",
- Utilities.canonicalTargetArchitecture(arch, undefined,
- "apple", "macosx", "macho"));
+ canonicalArch = Utilities.canonicalTargetArchitecture(arch, undefined, "apple",
+ "macosx", "macho");
+ args.push("--arch", canonicalArch);
}
// --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;
+ if (status === 0)
+ return p.readStdOut().trim();
+
+ // It has been obvserved that java_home fails for any architecture that is passed,
+ // so try without the filter and look up the JDK architecture manually.
+ if (!canonicalArch)
+ return undefined;
+
+ if (p.exec("/usr/libexec/java_home", ["--failfast"]) !== 0)
+ return undefined;
+ var jdkPath = p.readStdOut().trim();
+ var releaseFile = new TextFile(jdkPath + "/release", TextFile.ReadOnly);
+ var line;
+ while ((line = releaseFile.readLine())) {
+ if (!line.startsWith("OS_ARCH="))
+ continue;
+ var firstQuote = line.indexOf('"');
+ if (firstQuote === -1)
+ break;
+ var secondQuote = line.indexOf('"', firstQuote + 1);
+ if (secondQuote === -1)
+ break;
+ var archFromFile = line.substring(firstQuote + 1, secondQuote);
+ if (archFromFile !== canonicalArch)
+ break;
+ return jdkPath;
+ }
+ return undefined;
} finally {
p.close();
}
}
- if (hostOS.contains("unix")) {
+ if (hostOS.includes("unix")) {
var requiredTools = ["javac", "java", "jar"];
for (i = 0; i < searchPaths.length; ++i) {
function fullToolPath(tool) {
@@ -136,7 +163,7 @@ function findJdkVersion(compilerFilePath) {
var p = new Process();
try {
p.exec(compilerFilePath, ["-version"]);
- var re = /^javac (([0-9]+(?:\.[0-9]+){0,2})(_([0-9]+))?)$/m;
+ var re = /^javac (([0-9]+(?:\.[0-9]+){0,2})(_([0-9]+))?)(.*)?$/m;
var match = p.readStdErr().trim().match(re);
if (!match)
match = p.readStdOut().trim().match(re);
@@ -194,7 +221,7 @@ function javacArguments(product, inputs, overrides) {
classPaths.push(inputs["java.jar"][i].filePath);
var debugArg = product.moduleProperty("qbs", "buildVariant") === "debug"
? "-g" : "-g:none";
- var pathListSeparator = product.moduleProperty("qbs", "pathListSeparator");
+ var pathListSeparator = FileInfo.pathListSeparator();
var args = [
"-classpath", classPaths.join(pathListSeparator),
"-s", product.buildDirectory,
@@ -247,7 +274,7 @@ function helperFullyQualifiedNames(type) {
];
if (type === "java") {
return names.filter(function (name) {
- return !name.contains("$");
+ return !name.includes("$");
});
} else if (type === "class") {
return names;
@@ -313,7 +340,7 @@ function outputArtifacts(product, inputs) {
process.setWorkingDirectory(
FileInfo.joinPaths(ModUtils.moduleProperty(product, "internalClassFilesDir")));
- var sep = product.moduleProperty("qbs", "pathListSeparator");
+ var sep = FileInfo.pathListSeparator();
var toolsJarPath = ModUtils.moduleProperty(product, "toolsJarPath");
var javaArgs = [
"-classpath", process.workingDirectory() + (toolsJarPath ? (sep + toolsJarPath) : ""),
@@ -347,7 +374,7 @@ function manifestContents(filePath) {
if (contents) {
var dict = {};
var lines = contents.split(/\r?\n/g).filter(function (line) { return line.length > 0; });
- for (var i in lines) {
+ for (var i = 0; i < lines.length; ++i) {
var kv = lines[i].split(":");
if (kv.length !== 2)
throw new Error("Syntax error in manifest file '"
diff --git a/share/qbs/modules/nodejs/NodeJS.qbs b/share/qbs/modules/nodejs/NodeJS.qbs
index 7fd992fdb..412f9b8bb 100644
--- a/share/qbs/modules/nodejs/NodeJS.qbs
+++ b/share/qbs/modules/nodejs/NodeJS.qbs
@@ -31,6 +31,7 @@
import qbs.Environment
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.Probes
@@ -84,13 +85,13 @@ Module {
"tmp", "nodejs.intermediate")
setupBuildEnvironment: {
- var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, product.qbs.hostOS.contains("windows"));
+ var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), Host.os().includes("windows"));
v.prepend(product.nodejs.toolchainInstallPath);
v.set();
}
setupRunEnvironment: {
- var v = new ModUtils.EnvironmentVariable("NODE_PATH", product.qbs.pathListSeparator, product.qbs.hostOS.contains("windows"));
+ var v = new ModUtils.EnvironmentVariable("NODE_PATH", FileInfo.pathListSeparator(), Host.os().includes("windows"));
v.prepend(FileInfo.path(Environment.getEnv("QBS_RUN_FILE_PATH")));
v.set();
}
@@ -118,7 +119,7 @@ Module {
outputArtifacts: {
var tags = ["nodejs_processed_js"];
- if (input.fileTags.contains("application_js") ||
+ if (input.fileTags.includes("application_js") ||
product.moduleProperty("nodejs", "applicationFile") === input.filePath)
tags.push("application");
diff --git a/share/qbs/modules/nsis/NSISModule.qbs b/share/qbs/modules/nsis/NSISModule.qbs
index e426aee56..11a5670b5 100644
--- a/share/qbs/modules/nsis/NSISModule.qbs
+++ b/share/qbs/modules/nsis/NSISModule.qbs
@@ -30,11 +30,12 @@
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.Utilities
Module {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
property path toolchainInstallPath: Utilities.getNativeSetting(registryKey)
@@ -89,7 +90,7 @@ Module {
// Private properties
property string registryKey: {
- if (!qbs.hostOS.contains("windows"))
+ if (!Host.os().includes("windows"))
return undefined;
var keys = [ "HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS", "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\NSIS" ];
@@ -104,7 +105,7 @@ Module {
// 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"))
+ if (qbs.targetOS.includes("windows"))
validator.setRequiredProperty("toolchainInstallPath", toolchainInstallPath);
validator.setRequiredProperty("versionMajor", versionMajor);
@@ -120,7 +121,7 @@ Module {
}
setupBuildEnvironment: {
- if (toolchainInstallPath) {
+ if (product.nsis.toolchainInstallPath) {
var v = new ModUtils.EnvironmentVariable("PATH", ";", true);
v.prepend(product.nsis.toolchainInstallPath);
v.prepend(FileInfo.joinPaths(product.nsis.toolchainInstallPath, "bin"));
@@ -148,7 +149,9 @@ Module {
Artifact {
fileTags: ["nsissetup", "application"]
- filePath: product.destinationDirectory + "/" + product.targetName + ModUtils.moduleProperty(product, "executableSuffix")
+ filePath: FileInfo.joinPaths(product.destinationDirectory,
+ product.targetName + ModUtils.moduleProperty(
+ product, "executableSuffix"))
}
prepare: {
@@ -156,7 +159,7 @@ Module {
var args = [];
// Prefix character for makensis options
- var opt = product.moduleProperty("qbs", "hostOS").contains("windows") ? "/" : "-";
+ var opt = product.moduleProperty("qbs", "hostOS").includes("windows") ? "/" : "-";
if (ModUtils.moduleProperty(product, "disableConfig")) {
args.push(opt + "NOCONFIG");
diff --git a/share/qbs/modules/pkgconfig/pkgconfig.qbs b/share/qbs/modules/pkgconfig/pkgconfig.qbs
index ec4b59188..901e7e02e 100644
--- a/share/qbs/modules/pkgconfig/pkgconfig.qbs
+++ b/share/qbs/modules/pkgconfig/pkgconfig.qbs
@@ -37,7 +37,6 @@
**
****************************************************************************/
-import qbs
import qbs.File
import qbs.Probes
@@ -48,7 +47,7 @@ Module {
}
property path sysroot: {
- if (qbs.targetOS.contains("macos"))
+ if (qbs.targetOS.includes("macos"))
return "";
return qbs.sysroot;
}
diff --git a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs
index 0c511f2aa..b5dab2372 100644
--- a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs
+++ b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.File
import qbs.FileInfo
import qbs.Probes
@@ -7,15 +6,27 @@ import "../protobufbase.qbs" as ProtobufBase
import "../protobuf.js" as HelperFunctions
ProtobufBase {
- property string includePath: includeProbe.path
- property string libraryPath: libraryProbe.path
-
property bool useGrpc: false
- property string grpcIncludePath: grpcIncludeProbe.path
- property string grpcLibraryPath: grpcLibraryProbe.path
+ property bool _linkLibraries: true
+ readonly property bool _hasModules: protobuflib.present && (!useGrpc || grpcpp.present)
+
+ property string _cxxLanguageVersion: "c++17"
+
+ cpp.includePaths: outputDir
Depends { name: "cpp" }
+ Depends {
+ name: "protobuflib";
+ condition: _linkLibraries;
+ required: false
+ }
+ Depends {
+ name: "grpc++";
+ id: grpcpp
+ condition: _linkLibraries && useGrpc;
+ required: false
+ }
property path grpcPluginPath: grpcPluginProbe.filePath
@@ -25,39 +36,23 @@ ProtobufBase {
names: "grpc_cpp_plugin"
}
- cpp.libraryPaths: {
- var result = [libraryPath];
- if (useGrpc)
- result.push(grpcLibraryPath);
- return result;
- }
- cpp.dynamicLibraries: {
- var result = ["protobuf"];
- if (qbs.targetOS.contains("unix"))
- result.push("pthread");
- if (useGrpc)
- result.push("grpc++");
- return result;
- }
- cpp.includePaths: {
- var result = [outputDir, includePath];
- if (useGrpc)
- result.push("grpcIncludePath");
- return result;
- }
+ cpp.cxxLanguageVersion: _cxxLanguageVersion
Rule {
inputs: ["protobuf.input", "protobuf.grpc"]
- outputFileTags: ["hpp", "cpp"]
+ outputFileTags: ["hpp", "protobuf.hpp", "cpp"]
outputArtifacts: {
+ var outputDir = HelperFunctions.getOutputDir(input.protobuf.cpp, input);
var result = [
- HelperFunctions.cppArtifact(input.protobuf.cpp, product, input, "hpp", ".pb.h"),
- HelperFunctions.cppArtifact(input.protobuf.cpp, product, input, "cpp", ".pb.cc")
+ HelperFunctions.cppArtifact(outputDir, input, ["hpp", "protobuf.hpp"],
+ ".pb.h"),
+ HelperFunctions.cppArtifact(outputDir, input, "cpp", ".pb.cc")
];
- if (input.fileTags.contains("protobuf.grpc")) {
+ if (input.fileTags.includes("protobuf.grpc")) {
result.push(
- HelperFunctions.cppArtifact(input.protobuf.cpp, product, input, "hpp", ".grpc.pb.h"),
- HelperFunctions.cppArtifact(input.protobuf.cpp, product, input, "cpp", ".grpc.pb.cc"));
+ HelperFunctions.cppArtifact(outputDir, input, ["hpp", "protobuf.hpp"],
+ ".grpc.pb.h"),
+ HelperFunctions.cppArtifact(outputDir, input, ["cpp"], ".grpc.pb.cc"));
}
return result;
@@ -66,51 +61,25 @@ ProtobufBase {
prepare: {
var result = HelperFunctions.doPrepare(
input.protobuf.cpp, product, input, outputs, "cpp");
- if (input.fileTags.contains("protobuf.grpc")) {
- result = ModUtils.concatAll(result, HelperFunctions.doPrepareGrpc(
- input.protobuf.cpp, product, input, outputs, "cpp"));
+ if (input.fileTags.includes("protobuf.grpc")) {
+ result = ModUtils.concatAll(result, HelperFunctions.doPrepare(
+ input.protobuf.cpp, product, input, outputs, "grpc",
+ "protoc-gen-grpc=" + input.protobuf.cpp.grpcPluginPath));
}
return result;
}
}
- validateFunc: {
- return function() {
- base();
- if (!HelperFunctions.checkPath(includePath))
- throw "Can't find cpp protobuf include files. Please set the includePath property.";
- if (!HelperFunctions.checkPath(libraryPath))
- throw "Can't find cpp protobuf library. Please set the libraryPath property.";
+ validate: {
+ HelperFunctions.validateCompiler(compilerName, compilerPath);
- if (useGrpc) {
- if (!File.exists(grpcPluginPath))
- throw "Can't find grpc_cpp_plugin plugin. Please set the grpcPluginPath property.";
- if (!HelperFunctions.checkPath(grpcIncludePath))
- throw "Can't find grpc++ include files. Please set the grpcIncludePath property.";
- if (!HelperFunctions.checkPath(grpcLibraryPath))
- throw "Can't find grpc++ library. Please set the grpcLibraryPath property.";
- }
+ if (_linkLibraries && ! _hasModules) {
+ throw "Can't find cpp protobuf runtime. Make sure .pc files are present";
}
- }
-
- Probes.IncludeProbe {
- id: includeProbe
- names: "google/protobuf/message.h"
- }
-
- Probes.LibraryProbe {
- id: libraryProbe
- names: "protobuf"
- }
-
- Probes.IncludeProbe {
- id: grpcIncludeProbe
- pathSuffixes: "grpc++"
- names: "grpc++.h"
- }
- Probes.LibraryProbe {
- id: grpcLibraryProbe
- names: "grpc++"
+ if (useGrpc) {
+ if (!File.exists(grpcPluginPath))
+ throw "Can't find grpc_cpp_plugin plugin. Please set the grpcPluginPath property.";
+ }
}
}
diff --git a/share/qbs/modules/protobuf/nanopb/nanopb.qbs b/share/qbs/modules/protobuf/nanopb/nanopb.qbs
new file mode 100644
index 000000000..ada7c20bb
--- /dev/null
+++ b/share/qbs/modules/protobuf/nanopb/nanopb.qbs
@@ -0,0 +1,104 @@
+import qbs.File
+import qbs.FileInfo
+import qbs.Probes
+import qbs.ModUtils
+import "../protobufbase.qbs" as ProtobufBase
+import "../protobuf.js" as HelperFunctions
+
+ProtobufBase {
+ property string includePath: includeProbe.found ? includeProbe.path : undefined
+ property string libraryPath: libraryProbe.found ? libraryProbe.path : undefined
+ property string pluginPath: pluginProbe.filePath
+ property string pluginName: "protoc-gen-nanopb"
+ readonly property string _plugin: "protoc-gen-nanopb=" + pluginPath
+ readonly property string _libraryName: {
+ var libraryName;
+ if (libraryProbe.found) {
+ libraryName = FileInfo.baseName(libraryProbe.fileName);
+ if (libraryName.startsWith("lib"))
+ libraryName = libraryName.substring(3);
+ }
+ return libraryName;
+ }
+
+ Depends { name: "cpp" }
+
+ cpp.libraryPaths: {
+ var result = [];
+ if (libraryProbe.found)
+ result.push(libraryProbe.path);
+ return result;
+ }
+ cpp.dynamicLibraries: {
+ var result = [];
+ if (_libraryName)
+ result.push(_libraryName);
+ return result;
+ }
+ cpp.includePaths: {
+ var result = [outputDir];
+ if (includeProbe.found)
+ result.push(includePath);
+ return result;
+ }
+ cpp.cxxLanguageVersion: qbs.targetOS.contains("darwin") ? ["c++17"] : ["c++11"]
+
+ Rule {
+ inputs: ["protobuf.input"]
+ outputFileTags: ["hpp", "protobuf.hpp", "cpp"]
+ outputArtifacts: {
+ var outputDir = HelperFunctions.getOutputDir(input.protobuf.nanopb, input);
+ var result = [
+ HelperFunctions.cppArtifact(outputDir, input, ["hpp", "protobuf.hpp"],
+ ".pb.h"),
+ HelperFunctions.cppArtifact(outputDir, input, ["cpp"], ".pb.c")
+ ];
+
+ return result;
+ }
+
+ prepare: {
+ var options = input.protobuf.nanopb.importPaths.map(function (path) {
+ return "-I" + path;
+ })
+
+ var result = HelperFunctions.doPrepare(
+ input.protobuf.nanopb, product, input, outputs, "nanopb",
+ input.protobuf.nanopb._plugin, options);
+ return result;
+ }
+ }
+
+ Probes.IncludeProbe {
+ id: includeProbe
+ names: ["pb.h", "pb_encode.h", "pb_decode.h", "pb_common.h"]
+ platformSearchPaths: includePath ? [] : base
+ searchPaths: includePath ? [includePath] : []
+ }
+
+ Probes.LibraryProbe {
+ id: libraryProbe
+ names: [
+ "protobuf-nanopb",
+ "protobuf-nanopbd",
+ ]
+ platformSearchPaths: libraryPath ? [] : base
+ searchPaths: libraryPath ? [libraryPath] : []
+ }
+
+ Probes.BinaryProbe {
+ id: pluginProbe
+ names: pluginName
+ }
+
+ validate: {
+ HelperFunctions.validateCompiler(compilerName, compilerPath);
+
+ if (!includeProbe.found)
+ throw "Can't find nanopb protobuf include files. Please set the includePath property.";
+ if (!libraryProbe.found)
+ throw "Can't find nanopb protobuf library. Please set the libraryPath property.";
+ if (!HelperFunctions.checkPath(pluginPath))
+ throw "Can't find nanopb protobuf plugin. Please set the pluginPath property.";
+ }
+}
diff --git a/share/qbs/modules/protobuf/objc/protobufobjc.qbs b/share/qbs/modules/protobuf/objc/protobufobjc.qbs
index e2c4b5260..a3e708898 100644
--- a/share/qbs/modules/protobuf/objc/protobufobjc.qbs
+++ b/share/qbs/modules/protobuf/objc/protobufobjc.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.File
import qbs.FileInfo
import qbs.Probes
@@ -12,49 +11,28 @@ ProtobufBase {
Depends { name: "cpp" }
- // library build
- Properties {
- condition: !frameworkPath
- cpp.includePaths: [outputDir, includePath]
- cpp.libraryPaths: [libraryPath]
- cpp.frameworks: ["Foundation"]
- cpp.dynamicLibraries: ["ProtocolBuffers"]
- }
-
- // framework build
- Properties {
- condition: frameworkPath
- cpp.includePaths: [outputDir]
- cpp.frameworkPaths: [frameworkPath]
- cpp.frameworks: ["Foundation", "Protobuf"]
- cpp.defines: ["GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS"]
- }
+ cpp.includePaths: [outputDir].concat(!frameworkPath && includePath ? [includePath] : [])
+ cpp.libraryPaths: !frameworkPath && libraryPath ? [libraryPath] : []
+ cpp.dynamicLibraries: !frameworkPath && libraryPath ? ["ProtocolBuffers"] : []
+ cpp.frameworkPaths: frameworkPath ? [frameworkPath] : []
+ cpp.frameworks: ["Foundation"].concat(frameworkPath ? ["Protobuf"] : [])
+ cpp.defines: frameworkPath ? ["GPB_USE_PROTOBUF_FRAMEWORK_IMPORTS"] : []
Rule {
inputs: ["protobuf.input"]
- outputFileTags: ["hpp", "objc"]
+ outputFileTags: ["hpp", "protobuf.hpp", "objc"]
outputArtifacts: {
+ var outputDir = HelperFunctions.getOutputDir(input.protobuf.objc, input);
return [
- HelperFunctions.objcArtifact(input.protobuf.objc, product, input, "hpp", ".pbobjc.h"),
- HelperFunctions.objcArtifact(input.protobuf.objc, product, input, "objc", ".pbobjc.m")
+ HelperFunctions.objcArtifact(outputDir, input, ["hpp", "protobuf.hpp"],
+ ".pbobjc.h"),
+ HelperFunctions.objcArtifact(outputDir, input, ["objc"], ".pbobjc.m")
];
}
prepare: HelperFunctions.doPrepare(input.protobuf.objc, product, input, outputs, "objc")
}
- validateFunc: {
- return function() {
- base();
- if (!HelperFunctions.checkPath(frameworkPath)) {
- if (!HelperFunctions.checkPath(includePath))
- throw "Can't find objective-c protobuf include files. Please set the includePath or frameworkPath property.";
- if (!HelperFunctions.checkPath(libraryPath))
- throw "Can't find objective-c protobuf library. Please set the libraryPath or frameworkPath property.";
- }
- }
- }
-
Probes.IncludeProbe {
id: includeProbe
names: "GPBMessage.h"
@@ -69,4 +47,18 @@ ProtobufBase {
id: frameworkProbe
names: ["Protobuf"]
}
+
+ validate: {
+ HelperFunctions.validateCompiler(compilerName, compilerPath);
+ if (!HelperFunctions.checkPath(frameworkPath)) {
+ if (!HelperFunctions.checkPath(includePath)) {
+ throw "Can't find objective-c protobuf include files. Please set the includePath "
+ + "or frameworkPath property.";
+ }
+ if (!HelperFunctions.checkPath(libraryPath)) {
+ throw "Can't find objective-c protobuf library. Please set the libraryPath or "
+ + "frameworkPath property.";
+ }
+ }
+ }
}
diff --git a/share/qbs/modules/protobuf/protobuf.js b/share/qbs/modules/protobuf/protobuf.js
index 511f5a6c6..60d6f48e4 100644
--- a/share/qbs/modules/protobuf/protobuf.js
+++ b/share/qbs/modules/protobuf/protobuf.js
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2018 Ivan Komissarov
-** Contact: abbapoh@gmail.com
+** Copyright (C) 2018 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
@@ -31,8 +31,15 @@
var File = require("qbs.File");
var FileInfo = require("qbs.FileInfo");
-var checkPath = function(path) {
- return path && File.exists(path)
+function validateCompiler(compilerName, compilerPath) {
+ if (!File.exists(compilerPath)) {
+ throw "Can't find '" + compilerName + "' binary. Please set the compilerPath property or "
+ + "make sure the compiler is found in PATH";
+ }
+}
+
+function checkPath(path) {
+ return path && File.exists(path);
};
function toCamelCase(str){
@@ -46,13 +53,13 @@ function toCamelCase(str){
}).join('');
}
-function getOutputDir(module, product, input) {
+function getOutputDir(module, input) {
var outputDir = module.outputDir;
var importPaths = module.importPaths;
if (importPaths.length !== 0) {
var canonicalInput = File.canonicalFilePath(FileInfo.path(input.filePath));
for (var i = 0; i < importPaths.length; ++i) {
- path = File.canonicalFilePath(importPaths[i]);
+ var path = File.canonicalFilePath(importPaths[i]);
if (canonicalInput.startsWith(path)) {
return outputDir + "/" + FileInfo.relativePath(path, canonicalInput);
@@ -62,61 +69,44 @@ function getOutputDir(module, product, input) {
return outputDir;
}
-function cppArtifact(module, product, input, tag, suffix) {
- var outputDir = getOutputDir(module, product, input);
+function cppArtifact(outputDir, input, tags, suffix) {
return {
- fileTags: [tag],
- filePath: outputDir + "/" + FileInfo.baseName(input.fileName) + suffix,
+ fileTags: tags,
+ filePath: FileInfo.joinPaths(outputDir, FileInfo.baseName(input.fileName) + suffix),
cpp: {
includePaths: [].concat(input.cpp.includePaths, outputDir),
+ defines: ["NDEBUG"],
warningLevel: "none",
}
};
}
-function objcArtifact(module, product, input, tag, suffix) {
- var outputDir = getOutputDir(module, product, input);
+function objcArtifact(outputDir, input, tags, suffix) {
return {
- fileTags: [tag],
- filePath: outputDir + "/" + toCamelCase(FileInfo.baseName(input.fileName)) + suffix,
+ fileTags: tags,
+ filePath: FileInfo.joinPaths(
+ outputDir, toCamelCase(FileInfo.baseName(input.fileName)) + suffix),
cpp: {
+ automaticReferenceCounting: false,
includePaths: [].concat(input.cpp.includePaths, outputDir),
warningLevel: "none",
}
}
}
-function doPrepare(module, product, input, outputs, lang)
+function doPrepare(module, product, input, outputs, generator, plugin, generatorOptions)
{
var outputDir = module.outputDir;
var args = [];
- args.push("--" + lang + "_out", outputDir);
-
- var importPaths = module.importPaths;
- if (importPaths.length === 0)
- importPaths = [FileInfo.path(input.filePath)];
- importPaths.forEach(function(path) {
- if (!FileInfo.isAbsolutePath(path))
- path = FileInfo.joinPaths(product.sourceDirectory, path);
- args.push("--proto_path", path);
- });
+ if (!!plugin)
+ args.push("--plugin=" + plugin)
- args.push(input.filePath);
-
- var cmd = new Command(module.protocBinary, args);
- cmd.highlight = "codegen";
- cmd.description = "generating " + lang + " files for " + input.fileName;
- return [cmd];
-}
-
-function doPrepareGrpc(module, product, input, outputs, lang)
-{
- var outputDir = module.outputDir;
- var args = [];
-
- args.push("--grpc_out", outputDir);
- args.push("--plugin=protoc-gen-grpc=" + module.grpcPluginPath);
+ args.push("--" + generator + "_out", outputDir);
+ if (!!generatorOptions) {
+ for (var i = 0; i < generatorOptions.length; ++i)
+ args.push("--" + generator + "_opt=" + generatorOptions[i])
+ }
var importPaths = module.importPaths;
if (importPaths.length === 0)
@@ -129,8 +119,8 @@ function doPrepareGrpc(module, product, input, outputs, lang)
args.push(input.filePath);
- var cmd = new Command(module.protocBinary, args);
+ var cmd = new Command(module.compilerPath, args);
cmd.highlight = "codegen";
- cmd.description = "generating " + lang + " files for " + input.fileName;
+ cmd.description = "generating " + generator + " files for " + input.fileName;
return [cmd];
}
diff --git a/share/qbs/modules/protobuf/protobufbase.qbs b/share/qbs/modules/protobuf/protobufbase.qbs
index 0ac6c1949..e302d3758 100644
--- a/share/qbs/modules/protobuf/protobufbase.qbs
+++ b/share/qbs/modules/protobuf/protobufbase.qbs
@@ -1,33 +1,23 @@
-import qbs
import qbs.File
import qbs.FileInfo
import qbs.Probes
import "protobuf.js" as HelperFunctions
Module {
- property string protocBinary: protocProbe.filePath
- property pathList importPaths: []
+ property string compilerName: "protoc"
+ property string compilerPath: compilerProbe.filePath
- property string outputDir: product.buildDirectory + "/protobuf"
+ property pathList importPaths: []
- property var validateFunc: {
- return function() {
- if (!File.exists(protocBinary))
- throw "Can't find protoc binary. Please set the protocBinary property or make sure it is found in PATH";
- }
- }
+ readonly property string outputDir: product.buildDirectory + "/protobuf"
FileTagger {
patterns: ["*.proto"]
- fileTags: ["protobuf.input"];
+ fileTags: ["protobuf.input"]
}
Probes.BinaryProbe {
- id: protocProbe
- names: ["protoc"]
- }
-
- validate: {
- validateFunc();
+ id: compilerProbe
+ names: [compilerName]
}
}
diff --git a/share/qbs/modules/qbs/common.qbs b/share/qbs/modules/qbs/common.qbs
index 63bc29f8e..8b31092a9 100644
--- a/share/qbs/modules/qbs/common.qbs
+++ b/share/qbs/modules/qbs/common.qbs
@@ -48,25 +48,26 @@ Module {
property string buildVariant: defaultBuildVariant
property bool enableDebugCode: buildVariant == "debug"
- property bool debugInformation: (buildVariant == "debug")
- property string optimization: (buildVariant == "debug" ? "none" : "fast")
+ property bool debugInformation: (buildVariant !== "release")
+ property string optimization: (buildVariant === "debug" ? "none" : "fast")
readonly property string hostPlatform: undefined // set internally
readonly property stringList hostOS: Utilities.canonicalPlatform(hostPlatform)
property string hostOSVersion: {
- if (hostOS && hostOS.contains("macos")) {
+ if (hostOS && hostOS.includes("macos")) {
return Utilities.getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductVersion") ||
Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductVersion");
- } else if (hostOS && hostOS.contains("windows")) {
+ } else if (hostOS && hostOS.includes("windows")) {
var version = Utilities.getNativeSetting(windowsRegistryKey, "CurrentVersion");
return version + "." + hostOSBuildVersion;
}
}
+ readonly property string hostArchitecture: undefined // set internally
property string hostOSBuildVersion: {
- if (hostOS.contains("macos")) {
+ if (hostOS.includes("macos")) {
return Utilities.getNativeSetting("/System/Library/CoreServices/ServerVersion.plist", "ProductBuildVersion") ||
Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductBuildVersion");
- } else if (hostOS.contains("windows")) {
+ } else if (hostOS.includes("windows")) {
return Utilities.getNativeSetting(windowsRegistryKey, "CurrentBuildNumber");
}
}
@@ -78,19 +79,19 @@ Module {
property string targetPlatform: hostPlatform
readonly property stringList targetOS: Utilities.canonicalPlatform(targetPlatform)
- 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 pathListSeparator: hostOS.includes("windows") ? ";" : ":"
+ property string pathSeparator: hostOS.includes("windows") ? "\\" : "/"
+ property string nullDevice: hostOS.includes("windows") ? "NUL" : "/dev/null"
+ property path shellPath: hostOS.includes("windows") ? windowsShellPath : "/bin/sh"
property string profile: project.profile
property string toolchainType: {
- if (targetOS.contains("windows"))
- return hostOS.contains("windows") ? "msvc" : "mingw";
- if (targetOS.contains("darwin"))
- return hostOS.contains("macos") ? "xcode" : "clang";
- if (targetOS.contains("freebsd"))
+ if (targetOS.includes("windows"))
+ return hostOS.includes("windows") ? "msvc" : "mingw";
+ if (targetOS.includes("darwin"))
+ return hostOS.includes("macos") ? "xcode" : "clang";
+ if (targetOS.includes("freebsd"))
return "clang";
- if (targetOS.contains("qnx"))
+ if (targetOS.includes("qnx"))
return "qcc";
if (targetOS.containsAny(["haiku", "vxworks", "unix"]))
return "gcc";
@@ -101,12 +102,12 @@ Module {
property path installSourceBase
property string installRoot: project.buildDirectory + "/install-root"
property string installDir
- property string installPrefix: qbs.targetOS.contains("unix") ? "/usr/local" : ""
+ property string installPrefix: targetOS.includes("unix") ? "/usr/local" : ""
property path sysroot
PropertyOptions {
name: "buildVariant"
- allowedValues: ['debug', 'release']
+ allowedValues: ['debug', 'release', 'profiling']
description: "name of the build variant"
}
@@ -121,12 +122,12 @@ Module {
validator.setRequiredProperty("hostOS", hostOS);
validator.setRequiredProperty("targetOS", targetOS);
validator.addCustomValidator("targetOS", targetOS, function (value) {
- if (!value || (value.contains("osx") && !value.contains("macos")))
+ if (!value || (value.includes("osx") && !value.includes("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"))) {
+ if (hostOS && (hostOS.includes("windows") || hostOS.includes("macos"))) {
validator.setRequiredProperty("hostOSVersion", hostOSVersion,
"could not detect host operating system version; " +
"verify that system files and registry keys have not " +
@@ -142,8 +143,9 @@ Module {
validator.addCustomValidator("architecture", architecture, function (value) {
return !architecture || architecture === Utilities.canonicalArchitecture(architecture);
- }, "'" + architecture + "' is invalid. You must use the canonical name '" +
- Utilities.canonicalArchitecture(architecture) + "'");
+ }, "'" + architecture + "' is invalid." + (architecture
+ ? " You must use the canonical name '" + Utilities.canonicalArchitecture(architecture)
+ : "") + "'");
validator.addCustomValidator("toolchain", toolchain, function (value) {
if (toolchain === undefined)
@@ -168,7 +170,7 @@ Module {
];
var canonical = Utilities.canonicalToolchain.apply(Utilities, value);
for (var i = 0; i < pairs.length; ++i) {
- if (canonical.contains(pairs[i][0]) && canonical.contains(pairs[i][1]))
+ if (canonical.includes(pairs[i][0]) && canonical.includes(pairs[i][1]))
return false;
}
return true;
@@ -181,14 +183,14 @@ Module {
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 string windowsPathVariable: hostOS.includes("windows") ? "PATH" : "WINEPATH"
property var commonRunEnvironment: ({})
setupRunEnvironment: {
var env = product.qbs.commonRunEnvironment;
for (var i in env) {
var v = new ModUtils.EnvironmentVariable(i, product.qbs.pathListSeparator,
- product.qbs.hostOS.contains("windows"));
+ product.qbs.hostOS.includes("windows"));
v.value = env[i];
v.set();
}
diff --git a/share/qbs/modules/qnx/qnx.qbs b/share/qbs/modules/qnx/qnx.qbs
index 9cab5abb6..899b72441 100644
--- a/share/qbs/modules/qnx/qnx.qbs
+++ b/share/qbs/modules/qnx/qnx.qbs
@@ -31,6 +31,7 @@
import qbs.Environment
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.Probes
import qbs.Utilities
@@ -39,7 +40,7 @@ Module {
Probes.PathProbe {
id: qnxSdkProbe
names: ["qnx700", "qnx660", "qnx650"]
- searchPaths: qbs.hostOS.contains("windows")
+ searchPaths: Host.os().includes("windows")
? [Environment.getEnv("USERPROFILE"), Environment.getEnv("SystemDrive")]
: [Environment.getEnv("HOME"), "/opt"]
}
@@ -77,11 +78,11 @@ Module {
property string hostArch: qnx7 ? "x86_64" : "x86"
property string hostOs: {
- if (qbs.hostOS.contains("linux"))
+ if (Host.os().includes("linux"))
return "linux";
- if (qbs.hostOS.contains("macos"))
+ if (Host.os().includes("macos"))
return "darwin";
- if (qbs.hostOS.contains("windows"))
+ if (Host.os().includes("windows"))
return qnx7 ? "win64" : "win32";
}
@@ -111,7 +112,7 @@ Module {
}
if (!hostOs) {
- throw ModUtils.ModuleError("Host operating system '" + qbs.hostOS
+ throw ModUtils.ModuleError("Host operating system '" + Host.os()
+ "' is not supported by the QNX SDK.");
} else if (!File.exists(hostDir)) {
throw ModUtils.ModuleError("Detected host tools operating system '" + hostOs
diff --git a/share/qbs/modules/typescript/TypeScriptModule.qbs b/share/qbs/modules/typescript/TypeScriptModule.qbs
index d8e61b17f..76df529c2 100644
--- a/share/qbs/modules/typescript/TypeScriptModule.qbs
+++ b/share/qbs/modules/typescript/TypeScriptModule.qbs
@@ -129,7 +129,7 @@ Module {
var preValidator = new ModUtils.PropertyValidator("nodejs");
preValidator.addCustomValidator("interpreterFileName", nodejs.interpreterFileName, function (value) {
- return value === "node" + (qbs.hostOS.contains("windows") ? ".exe" : "");
+ return value === "node" + FileInfo.executableSuffix();
}, interpreterMessage);
preValidator.addCustomValidator("interpreterFilePath", nodejs.interpreterFilePath, function (value) {
return value.endsWith(nodejs.interpreterFileName);
diff --git a/share/qbs/modules/typescript/typescript.js b/share/qbs/modules/typescript/typescript.js
index 858041b3d..199f486cf 100644
--- a/share/qbs/modules/typescript/typescript.js
+++ b/share/qbs/modules/typescript/typescript.js
@@ -132,7 +132,7 @@ function outputArtifacts(product, inputs) {
process.setEnv("NODE_PATH", [
ModUtils.moduleProperty(product, "toolchainInstallPath"),
product.moduleProperty("nodejs", "packageManagerRootPath")
- ].join(product.moduleProperty("qbs", "pathListSeparator")));
+ ].join(FileInfo.pathListSeparator()));
process.exec(product.moduleProperty("nodejs", "interpreterFilePath"),
[FileInfo.joinPaths(product.buildDirectory,
".io.qt.qbs.internal.typescript",
@@ -146,7 +146,7 @@ function outputArtifacts(product, inputs) {
var i, appIndex = -1;
if (product.moduleProperty("typescript", "singleFile")) {
for (i = 0; i < artifacts.length; ++i) {
- if (artifacts[i].fileTags.contains("compiled_typescript")) {
+ if (artifacts[i].fileTags.includes("compiled_typescript")) {
appIndex = i;
break;
}
@@ -169,7 +169,7 @@ function outputArtifacts(product, inputs) {
}
}
- if (appIndex === -1 || !artifacts[appIndex].fileTags.contains("compiled_typescript"))
+ if (appIndex === -1 || !artifacts[appIndex].fileTags.includes("compiled_typescript"))
throw "nodejs.applicationFile was set, but Qbs couldn't find the compiled " +
"JavaScript file corresponding to '" + applicationFile + "'";
diff --git a/share/qbs/modules/wix/WiXModule.qbs b/share/qbs/modules/wix/WiXModule.qbs
index ad8586da1..077f259f4 100644
--- a/share/qbs/modules/wix/WiXModule.qbs
+++ b/share/qbs/modules/wix/WiXModule.qbs
@@ -30,12 +30,13 @@
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.ModUtils
import qbs.Probes
import qbs.Utilities
Module {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
Probes.WiXProbe {
id: wixProbe
@@ -112,19 +113,19 @@ Module {
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
+ property stringList extensions: product.type.includes("wixsetup") ? ["WixBalExtension"] : [] // default to WiX Standard Bootstrapper extension
// private properties
property string targetSuffix: {
- if (product.type.contains("msi")) {
+ if (product.type.includes("msi")) {
return windowsInstallerSuffix;
- } else if (product.type.contains("wixsetup")) {
+ } else if (product.type.includes("wixsetup")) {
return executableSuffix;
}
}
// MSI/MSM package validation only works natively on Windows
- property bool enablePackageValidation: qbs.hostOS.contains("windows")
+ property bool enablePackageValidation: Host.os().includes("windows")
property string executableSuffix: ".exe"
property string windowsInstallerSuffix: ".msi"
@@ -151,7 +152,7 @@ Module {
}
setupBuildEnvironment: {
- var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, true);
+ var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), true);
v.prepend(product.wix.toolchainInstallPath);
v.prepend(product.wix.toolchainInstallRoot);
v.set();
@@ -226,7 +227,7 @@ Module {
if (ModUtils.moduleProperty(input, "visualStudioCompatibility")) {
var toolchain = product.moduleProperty("qbs", "toolchain");
var toolchainInstallPath = product.moduleProperty("cpp", "toolchainInstallPath");
- if (toolchain && toolchain.contains("msvc") && toolchainInstallPath) {
+ if (toolchain && toolchain.includes("msvc") && toolchainInstallPath) {
var vcDir = toolchainInstallPath.replace(/[\\/]bin$/i, "");
var vcRootDir = vcDir.replace(/[\\/]VC$/i, "");
args.push("-dDevEnvDir=" + FileInfo.toWindowsSeparators(FileInfo.joinPaths(vcRootDir, 'Common7', 'IDE')));
@@ -334,12 +335,12 @@ Module {
multiplex: true
inputs: ["wixobj", "wxl"]
auxiliaryInputs: ["installable"]
- inputsFromDependencies: product.type.contains("wixsetup") ? ["msi"] : []
+ inputsFromDependencies: product.type.includes("wixsetup") ? ["msi"] : []
outputArtifacts: {
var artifacts = [];
- if (product.type.contains("wixsetup")) {
+ if (product.type.includes("wixsetup")) {
artifacts.push({
fileTags: ["wixsetup", "application"],
filePath: FileInfo.joinPaths(product.destinationDirectory,
@@ -349,7 +350,7 @@ Module {
});
}
- if (product.type.contains("msi")) {
+ if (product.type.includes("msi")) {
artifacts.push({
fileTags: ["msi"],
filePath: FileInfo.joinPaths(product.destinationDirectory,
@@ -375,9 +376,9 @@ Module {
prepare: {
var i;
var primaryOutput;
- if (product.type.contains("wixsetup")) {
+ if (product.type.includes("wixsetup")) {
primaryOutput = outputs.wixsetup[0];
- } else if (product.type.contains("msi")) {
+ } else if (product.type.includes("msi")) {
primaryOutput = outputs.msi[0];
} else {
throw("WiX: Unsupported product type '" + product.type + "'");
@@ -426,7 +427,7 @@ Module {
args.push(FileInfo.toWindowsSeparators(inputs.wxl[i].filePath));
}
- if (product.type.contains("msi")) {
+ if (product.type.includes("msi")) {
var cultures = ModUtils.moduleProperty(product, "cultures");
args.push("-cultures:"
+ (cultures && cultures.length > 0 ? cultures.join(";") : "null"));
diff --git a/share/qbs/modules/xcode/xcode.js b/share/qbs/modules/xcode/xcode.js
index bda41ade9..726974579 100644
--- a/share/qbs/modules/xcode/xcode.js
+++ b/share/qbs/modules/xcode/xcode.js
@@ -93,6 +93,16 @@ var XcodeArchSpecsReader = (function () {
return XcodeArchSpecsReader;
}());
+function platformInfo(platformInfoPlist) {
+ var propertyList = new PropertyList();
+ try {
+ propertyList.readFromFile(platformInfoPlist);
+ return propertyList.toObject();
+ } finally {
+ propertyList.clear();
+ }
+}
+
function sdkInfoList(sdksPath) {
var sdkInfo = [];
var sdks = File.directoryEntries(sdksPath, File.Dirs | File.NoDotAndDotDot);
@@ -102,7 +112,7 @@ function sdkInfoList(sdksPath) {
if (!sdks[i].match(/[0-9]+/))
continue;
- if (sdks[i].startsWith("DriverKit"))
+ if (sdks[i].startsWith("DriverKit") || sdks[i].startsWith("AssetRuntime"))
continue;
var settingsPlist = FileInfo.joinPaths(sdksPath, sdks[i], "SDKSettings.plist");
@@ -114,7 +124,7 @@ function sdkInfoList(sdksPath) {
if (!plist || !plist["CanonicalName"] || !plist["Version"])
return false;
- var re = /^([0-9]+)\.([0-9]+)$/;
+ var re = /^[0-9]+\.[0-9]+(\.[0-9]+)?$/;
return plist["Version"].match(re);
}
@@ -132,19 +142,7 @@ function sdkInfoList(sdksPath) {
}
// 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;
- });
+ sdkInfo.sort(function (a, b) { return Utilities.versionCompare(a["Version"], b["Version"]); });
return sdkInfo;
}
@@ -181,7 +179,7 @@ function findSigningIdentities(security, searchString) {
}
function provisioningProfilePlistContents(filePath) {
- if (filePath === undefined)
+ if (filePath === undefined || !File.exists(filePath))
return undefined;
var plist = new PropertyList();
@@ -193,14 +191,52 @@ function provisioningProfilePlistContents(filePath) {
}
}
-function archsSpecsPath(version, targetOS, platformType, platformPath, devicePlatformPath) {
+function boolFromSdkOrPlatform(varName, sdkProps, platformProps, defaultValue) {
+ var values = [(sdkProps || {})[varName], (platformProps || {})[varName]];
+ for (var i = 0; i < values.length; ++i) {
+ if (values[i] === "YES")
+ return true;
+ if (values[i] === "NO")
+ return false;
+ }
+ return defaultValue;
+}
+
+function archsSpecsPath(version, targetOS, platformType, platformPath, devicePlatformPath,
+ developerPath) {
+ if (Utilities.versionCompare(version, "13.3") >= 0) {
+ var pluginsDir;
+ if (Utilities.versionCompare(version, "15.3") >= 0) {
+ pluginsDir = FileInfo.joinPaths(developerPath, "..",
+ "SharedFrameworks", "XCBuild.framework", "PlugIns", "XCBBuildService.bundle",
+ "Contents", "PlugIns");
+ } else if (Utilities.versionCompare(version, "14.3") >= 0) {
+ pluginsDir = FileInfo.joinPaths(developerPath, "Library", "Xcode", "Plug-ins");
+ } else {
+ pluginsDir = FileInfo.joinPaths(developerPath, "..", "PlugIns");
+ }
+ var baseDir = FileInfo.joinPaths(pluginsDir,
+ "XCBSpecifications.ideplugin", "Contents", "Resources");
+
+ var baseName = targetOS.includes("macos") ? "MacOSX Architectures"
+ : targetOS.includes("ios-simulator") ? "iOS Simulator"
+ : targetOS.includes("ios") ? "iOS Device"
+ : targetOS.includes("tvos-simulator") ? "tvOS Simulator"
+ : targetOS.includes("tvos") ? "tvOS Device"
+ : targetOS.includes("watchos-simulator") ? "watchOS Simulator" : "watchOS Device";
+ return FileInfo.joinPaths(baseDir, baseName + ".xcspec");
+ }
var _specsPluginBaseName;
+ if (Utilities.versionCompare(version, "12") >= 0) {
+ if (targetOS.includes("macos"))
+ _specsPluginBaseName = "OSX";
+ }
if (Utilities.versionCompare(version, "7") >= 0) {
- if (targetOS.contains("ios"))
+ if (targetOS.includes("ios"))
_specsPluginBaseName = "iOSPlatform";
- if (targetOS.contains("tvos"))
+ if (targetOS.includes("tvos"))
_specsPluginBaseName = "AppleTV";
- if (targetOS.contains("watchos"))
+ if (targetOS.includes("watchos"))
_specsPluginBaseName = "Watch";
}
@@ -211,8 +247,8 @@ function archsSpecsPath(version, targetOS, platformType, platformPath, devicePla
"Resources")
: FileInfo.joinPaths(platformPath, "Developer", "Library", "Xcode", "Specifications");
- var _archSpecsFileBaseName = targetOS.contains("ios")
- ? (targetOS.contains("ios-simulator") ? "iPhone Simulator " : "iPhoneOS")
+ var _archSpecsFileBaseName = targetOS.includes("ios")
+ ? (targetOS.includes("ios-simulator") ? "iPhone Simulator " : "iPhoneOS")
: DarwinTools.applePlatformDirectoryName(targetOS, platformType) + " ";
if (_specsPluginBaseName) {
diff --git a/share/qbs/modules/xcode/xcode.qbs b/share/qbs/modules/xcode/xcode.qbs
index 922580505..c052da44c 100644
--- a/share/qbs/modules/xcode/xcode.qbs
+++ b/share/qbs/modules/xcode/xcode.qbs
@@ -6,11 +6,16 @@ import qbs.DarwinTools
import qbs.ModUtils
import qbs.Probes
import qbs.PropertyList
-import 'xcode.js' as Xcode
+import qbs.Utilities
Module {
id: xcodeModule
+ Probes.XcodeLocationProbe {
+ id: xcodeLocationProbe
+ condition: !xcodeModule.developerPath
+ }
+
Probes.XcodeProbe {
id: xcodeProbe
developerPath: xcodeModule.developerPath
@@ -21,12 +26,14 @@ Module {
sdksPath: xcodeModule.sdksPath
}
- condition: qbs.targetOS.contains("darwin") &&
- qbs.toolchain && qbs.toolchain.contains("xcode")
+ condition: qbs.targetOS.includes("darwin") &&
+ qbs.toolchain && qbs.toolchain.includes("xcode")
version: xcodeProbe.xcodeVersion
- property path developerPath: "/Applications/Xcode.app/Contents/Developer"
+ property path developerPath: xcodeLocationProbe.found
+ ? xcodeLocationProbe.developerPath
+ : undefined
property string sdk: DarwinTools.applePlatformName(qbs.targetOS, platformType)
property stringList targetDevices: DarwinTools.targetDevices(qbs.targetOS)
@@ -48,6 +55,12 @@ Module {
return _sdkSettings["Version"];
}
}
+ readonly property string shortSdkVersion: {
+ var v = sdkVersion;
+ if (v && v.split('.').length > 2)
+ v = v.slice(0, v.lastIndexOf('.'));
+ return v;
+ }
readonly property string latestSdkName: {
if (_latestSdk) {
@@ -73,31 +86,6 @@ Module {
}
}
- 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 xcodebuildName: "xcodebuild"
- property string xcodebuildPath: FileInfo.joinPaths(developerPath, "usr", "bin", xcodebuildName)
-
- 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,
@@ -116,8 +104,8 @@ Module {
+ ".platform")
readonly property path sdkPath: FileInfo.joinPaths(sdksPath,
DarwinTools.applePlatformDirectoryName(
- qbs.targetOS, platformType, sdkVersion)
- + ".sdk")
+ qbs.targetOS, platformType,
+ shortSdkVersion) + ".sdk")
// private properties
readonly property path toolchainsPath: FileInfo.joinPaths(developerPath, "Toolchains")
@@ -129,21 +117,11 @@ Module {
readonly property path toolchainInfoPlist: FileInfo.joinPaths(toolchainPath,
"ToolchainInfo.plist")
- readonly property stringList _actualSigningIdentity: {
- if (/^[A-Fa-f0-9]{40}$/.test(signingIdentity)) {
- return signingIdentity;
- }
+ readonly property var _platformSettings: xcodeProbe.platformSettings
- 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 _platformProps: {
+ if (_platformSettings)
+ return _platformSettings["DefaultProperties"];
}
readonly property stringList standardArchitectures: _architectureSettings["ARCHS_STANDARD"]
@@ -156,6 +134,7 @@ Module {
readonly property var _sdkSettings: {
if (_availableSdks) {
+ // see 'sdk' property doc to understand why this loop is needed
for (var i in _availableSdks) {
if (_availableSdks[i]["Version"] === sdk)
return _availableSdks[i];
@@ -163,12 +142,18 @@ Module {
return _availableSdks[i];
}
- // Latest SDK available for the platform
+ // Latest SDK available for the platform (default case)
if (DarwinTools.applePlatformName(qbs.targetOS, platformType) === sdk)
return _latestSdk;
}
}
+ readonly property var _sdkProps: {
+ if (_sdkSettings) {
+ return _sdkSettings["DefaultProperties"];
+ }
+ }
+
qbs.sysroot: sdkPath
validate: {
@@ -191,38 +176,25 @@ Module {
validator.setRequiredProperty("platformPath", platformPath);
validator.setRequiredProperty("sdksPath", sdkPath);
validator.setRequiredProperty("sdkPath", sdkPath);
- validator.addVersionValidator("sdkVersion", sdkVersion, 2, 2);
+ validator.addVersionValidator("sdkVersion", sdkVersion, 2, 3);
validator.addCustomValidator("sdkName", sdkName, function (value) {
return value === DarwinTools.applePlatformDirectoryName(
- qbs.targetOS, platformType, sdkVersion, false).toLowerCase();
+ qbs.targetOS, platformType, shortSdkVersion, 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;
+ return value === sdkName || (value + shortSdkVersion) === 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;
- }
+ property var buildEnv: ({
+ "DEVELOPER_DIR": developerPath,
+ "SDKROOT": sdkPath
+ })
setupBuildEnvironment: {
- var v = new ModUtils.EnvironmentVariable("PATH", product.qbs.pathListSeparator, false);
+ var v = new ModUtils.EnvironmentVariable("PATH", FileInfo.pathListSeparator(), false);
v.prepend(product.xcode.platformPath + "/Developer/usr/bin");
v.prepend(product.xcode.developerPath + "/usr/bin");
v.set();
@@ -233,190 +205,4 @@ Module {
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", "bundle.input"]
- }
-
- 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
index 9349d5c8b..75e1e83ba 100644
--- a/share/share.qbs
+++ b/share/share.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.File
import qbs.FileInfo
import qbs.TextFile
@@ -19,22 +18,13 @@ Product {
}
Group {
- name: "Python executables"
- files: ["../src/3rdparty/python/bin/dmgbuild"]
- fileTags: ["qbs resources"]
- qbs.install: true
- qbs.installDir: qbsbuildconfig.libexecInstallDir
- qbs.installSourceBase: "../src/3rdparty/python/bin"
- }
-
- Group {
name: "Python packages"
prefix: "../src/3rdparty/python/**/"
files: ["*.py"]
fileTags: ["qbs resources"]
qbs.install: true
qbs.installDir: qbsbuildconfig.resourcesInstallDir + "/share/qbs/python"
- qbs.installSourceBase: "../src/3rdparty/python/lib/python2.7/site-packages"
+ qbs.installSourceBase: "../src/3rdparty/python/lib/python3.9/site-packages"
}
Group {
@@ -57,7 +47,7 @@ Product {
Group {
name: "Module providers"
- files: ["qbs/module-providers/**/*"]
+ files: ["qbs/module-providers/*", "qbs/module-providers/**/*"]
fileTags: ["qbs resources"]
qbs.install: true
qbs.installDir: qbsbuildconfig.resourcesInstallDir + "/share"
@@ -82,7 +72,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Generating " + output.fileName;
+ cmd.description = "generating " + output.fileName;
cmd.highlight = "codegen";
cmd.sourceCode = function() {
var tf;
@@ -107,7 +97,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Generating " + output.fileName;
+ cmd.description = "generating " + output.fileName;
cmd.highlight = "codegen";
cmd.sourceCode = function() {
var tf;
diff --git a/src/3rdparty/python/bin/dmgbuild b/src/3rdparty/python/bin/dmgbuild
deleted file mode 100755
index 4647448da..000000000
--- a/src/3rdparty/python/bin/dmgbuild
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from __future__ import unicode_literals
-from __future__ import print_function
-
-import dmgbuild
-import sys
-import argparse
-
-parser = argparse.ArgumentParser(description='Construct a disk image file.')
-parser.add_argument('volume_name', metavar='volume-name',
- help='The name to give to the volume (this will appear in the title bar when the user mounts the disk image).')
-parser.add_argument('filename', metavar='output.dmg',
- help='The filename of the disk image to create.')
-parser.add_argument('-s', '--settings',
- help='The path of the settings file.')
-parser.add_argument('-D', dest='defines', action='append', default=[],
- help='Define a value for the settings file (e.g. -Dfoo=bar).')
-parser.add_argument('--no-hidpi', dest='lookForHiDPI', action='store_false', default=True,
- help='Do not search for HiDPI versions of the background image (if specified)')
-
-
-args = parser.parse_args()
-
-defines = {}
-for d in args.defines:
- k,v = d.split('=', 1)
- k = k.strip()
- v = v.strip()
- if (v.startswith("'") and v.endswith("'")) \
- or (v.startswith('"') and v.endswith('"')):
- v = v[1:-1]
- defines[k] = v
-
-dmgbuild.build_dmg(
- args.filename.decode('utf_8'),
- args.volume_name.decode('utf_8'),
- args.settings.decode('utf_8'),
- defines=defines,
- lookForHiDPI=args.lookForHiDPI)
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/biplist/LICENSE b/src/3rdparty/python/lib/python2.7/site-packages/biplist/LICENSE
deleted file mode 100644
index 1c7ba6cc1..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/biplist/LICENSE
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) 2010, Andrew Wooster
-All rights reserved.
-
-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 biplist 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 HOLDER 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.
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/biplist/__init__.py b/src/3rdparty/python/lib/python2.7/site-packages/biplist/__init__.py
deleted file mode 100644
index f9d5836dd..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/biplist/__init__.py
+++ /dev/null
@@ -1,977 +0,0 @@
-"""biplist -- a library for reading and writing binary property list files.
-
-Binary Property List (plist) files provide a faster and smaller serialization
-format for property lists on OS X. This is a library for generating binary
-plists which can be read by OS X, iOS, or other clients.
-
-The API models the plistlib API, and will call through to plistlib when
-XML serialization or deserialization is required.
-
-To generate plists with UID values, wrap the values with the Uid object. The
-value must be an int.
-
-To generate plists with NSData/CFData values, wrap the values with the
-Data object. The value must be a string.
-
-Date values can only be datetime.datetime objects.
-
-The exceptions InvalidPlistException and NotBinaryPlistException may be
-thrown to indicate that the data cannot be serialized or deserialized as
-a binary plist.
-
-Plist generation example:
-
- from biplist import *
- from datetime import datetime
- plist = {'aKey':'aValue',
- '0':1.322,
- 'now':datetime.now(),
- 'list':[1,2,3],
- 'tuple':('a','b','c')
- }
- try:
- writePlist(plist, "example.plist")
- except (InvalidPlistException, NotBinaryPlistException), e:
- print "Something bad happened:", e
-
-Plist parsing example:
-
- from biplist import *
- try:
- plist = readPlist("example.plist")
- print plist
- except (InvalidPlistException, NotBinaryPlistException), e:
- print "Not a plist:", e
-"""
-
-from collections import namedtuple
-import datetime
-import io
-import math
-import plistlib
-from struct import pack, unpack, unpack_from
-from struct import error as struct_error
-import sys
-import time
-
-try:
- unicode
- unicodeEmpty = r''
-except NameError:
- unicode = str
- unicodeEmpty = ''
-try:
- long
-except NameError:
- long = int
-try:
- {}.iteritems
- iteritems = lambda x: x.iteritems()
-except AttributeError:
- iteritems = lambda x: x.items()
-
-__all__ = [
- 'Uid', 'Data', 'readPlist', 'writePlist', 'readPlistFromString',
- 'writePlistToString', 'InvalidPlistException', 'NotBinaryPlistException'
-]
-
-# Apple uses Jan 1, 2001 as a base for all plist date/times.
-apple_reference_date = datetime.datetime.utcfromtimestamp(978307200)
-
-class Uid(object):
- """Wrapper around integers for representing UID values. This
- is used in keyed archiving."""
- integer = 0
- def __init__(self, integer):
- self.integer = integer
-
- def __repr__(self):
- return "Uid(%d)" % self.integer
-
- def __eq__(self, other):
- if isinstance(self, Uid) and isinstance(other, Uid):
- return self.integer == other.integer
- return False
-
- def __cmp__(self, other):
- return self.integer - other.integer
-
- def __lt__(self, other):
- return self.integer < other.integer
-
- def __hash__(self):
- return self.integer
-
- def __int__(self):
- return int(self.integer)
-
-class Data(bytes):
- """Wrapper around bytes to distinguish Data values."""
-
-class InvalidPlistException(Exception):
- """Raised when the plist is incorrectly formatted."""
-
-class NotBinaryPlistException(Exception):
- """Raised when a binary plist was expected but not encountered."""
-
-def readPlist(pathOrFile):
- """Raises NotBinaryPlistException, InvalidPlistException"""
- didOpen = False
- result = None
- if isinstance(pathOrFile, (bytes, unicode)):
- pathOrFile = open(pathOrFile, 'rb')
- didOpen = True
- try:
- reader = PlistReader(pathOrFile)
- result = reader.parse()
- except NotBinaryPlistException as e:
- try:
- pathOrFile.seek(0)
- result = None
- if hasattr(plistlib, 'loads'):
- contents = None
- if isinstance(pathOrFile, (bytes, unicode)):
- with open(pathOrFile, 'rb') as f:
- contents = f.read()
- else:
- contents = pathOrFile.read()
- result = plistlib.loads(contents)
- else:
- result = plistlib.readPlist(pathOrFile)
- result = wrapDataObject(result, for_binary=True)
- except Exception as e:
- raise InvalidPlistException(e)
- finally:
- if didOpen:
- pathOrFile.close()
- return result
-
-def wrapDataObject(o, for_binary=False):
- if isinstance(o, Data) and not for_binary:
- v = sys.version_info
- if not (v[0] >= 3 and v[1] >= 4):
- o = plistlib.Data(o)
- elif isinstance(o, (bytes, plistlib.Data)) and for_binary:
- if hasattr(o, 'data'):
- o = Data(o.data)
- elif isinstance(o, tuple):
- o = wrapDataObject(list(o), for_binary)
- o = tuple(o)
- elif isinstance(o, list):
- for i in range(len(o)):
- o[i] = wrapDataObject(o[i], for_binary)
- elif isinstance(o, dict):
- for k in o:
- o[k] = wrapDataObject(o[k], for_binary)
- return o
-
-def writePlist(rootObject, pathOrFile, binary=True):
- if not binary:
- rootObject = wrapDataObject(rootObject, binary)
- if hasattr(plistlib, "dump"):
- if isinstance(pathOrFile, (bytes, unicode)):
- with open(pathOrFile, 'wb') as f:
- return plistlib.dump(rootObject, f)
- else:
- return plistlib.dump(rootObject, pathOrFile)
- else:
- return plistlib.writePlist(rootObject, pathOrFile)
- else:
- didOpen = False
- if isinstance(pathOrFile, (bytes, unicode)):
- pathOrFile = open(pathOrFile, 'wb')
- didOpen = True
- writer = PlistWriter(pathOrFile)
- result = writer.writeRoot(rootObject)
- if didOpen:
- pathOrFile.close()
- return result
-
-def readPlistFromString(data):
- return readPlist(io.BytesIO(data))
-
-def writePlistToString(rootObject, binary=True):
- if not binary:
- rootObject = wrapDataObject(rootObject, binary)
- if hasattr(plistlib, "dumps"):
- return plistlib.dumps(rootObject)
- elif hasattr(plistlib, "writePlistToBytes"):
- return plistlib.writePlistToBytes(rootObject)
- else:
- return plistlib.writePlistToString(rootObject)
- else:
- ioObject = io.BytesIO()
- writer = PlistWriter(ioObject)
- writer.writeRoot(rootObject)
- return ioObject.getvalue()
-
-def is_stream_binary_plist(stream):
- stream.seek(0)
- header = stream.read(7)
- if header == b'bplist0':
- return True
- else:
- return False
-
-PlistTrailer = namedtuple('PlistTrailer', 'offsetSize, objectRefSize, offsetCount, topLevelObjectNumber, offsetTableOffset')
-PlistByteCounts = namedtuple('PlistByteCounts', 'nullBytes, boolBytes, intBytes, realBytes, dateBytes, dataBytes, stringBytes, uidBytes, arrayBytes, setBytes, dictBytes')
-
-class PlistReader(object):
- file = None
- contents = ''
- offsets = None
- trailer = None
- currentOffset = 0
- # Used to detect recursive object references.
- offsetsStack = []
-
- def __init__(self, fileOrStream):
- """Raises NotBinaryPlistException."""
- self.reset()
- self.file = fileOrStream
-
- def parse(self):
- return self.readRoot()
-
- def reset(self):
- self.trailer = None
- self.contents = ''
- self.offsets = []
- self.currentOffset = 0
- self.offsetsStack = []
-
- def readRoot(self):
- result = None
- self.reset()
- # Get the header, make sure it's a valid file.
- if not is_stream_binary_plist(self.file):
- raise NotBinaryPlistException()
- self.file.seek(0)
- self.contents = self.file.read()
- if len(self.contents) < 32:
- raise InvalidPlistException("File is too short.")
- trailerContents = self.contents[-32:]
- try:
- self.trailer = PlistTrailer._make(unpack("!xxxxxxBBQQQ", trailerContents))
-
- if pow(2, self.trailer.offsetSize*8) < self.trailer.offsetTableOffset:
- raise InvalidPlistException("Offset size insufficient to reference all objects.")
-
- if pow(2, self.trailer.objectRefSize*8) < self.trailer.offsetCount:
- raise InvalidPlistException("Too many offsets to represent in size of object reference representation.")
-
- offset_size = self.trailer.offsetSize * self.trailer.offsetCount
- offset = self.trailer.offsetTableOffset
-
- if offset + offset_size > pow(2, 64):
- raise InvalidPlistException("Offset table is excessively long.")
-
- if self.trailer.offsetSize > 16:
- raise InvalidPlistException("Offset size is greater than maximum integer size.")
-
- if self.trailer.objectRefSize == 0:
- raise InvalidPlistException("Object reference size is zero.")
-
- if offset >= len(self.contents) - 32:
- raise InvalidPlistException("Offset table offset is too large.")
-
- if offset < len("bplist00x"):
- raise InvalidPlistException("Offset table offset is too small.")
-
- if self.trailer.topLevelObjectNumber >= self.trailer.offsetCount:
- raise InvalidPlistException("Top level object number is larger than the number of objects.")
-
- offset_contents = self.contents[offset:offset+offset_size]
- offset_i = 0
- offset_table_length = len(offset_contents)
-
- while offset_i < self.trailer.offsetCount:
- begin = self.trailer.offsetSize*offset_i
- end = begin+self.trailer.offsetSize
- if end > offset_table_length:
- raise InvalidPlistException("End of object is at invalid offset %d in offset table of length %d" % (end, offset_table_length))
- tmp_contents = offset_contents[begin:end]
- tmp_sized = self.getSizedInteger(tmp_contents, self.trailer.offsetSize)
- self.offsets.append(tmp_sized)
- offset_i += 1
- self.setCurrentOffsetToObjectNumber(self.trailer.topLevelObjectNumber)
- result = self.readObject()
- except TypeError as e:
- raise InvalidPlistException(e)
- return result
-
- def setCurrentOffsetToObjectNumber(self, objectNumber):
- if objectNumber > len(self.offsets) - 1:
- raise InvalidPlistException("Invalid offset number: %d" % objectNumber)
- self.currentOffset = self.offsets[objectNumber]
- if self.currentOffset in self.offsetsStack:
- raise InvalidPlistException("Recursive data structure detected in object: %d" % objectNumber)
-
- def beginOffsetProtection(self):
- self.offsetsStack.append(self.currentOffset)
- return self.currentOffset
-
- def endOffsetProtection(self, offset):
- try:
- index = self.offsetsStack.index(offset)
- self.offsetsStack = self.offsetsStack[:index]
- except ValueError as e:
- pass
-
- def readObject(self):
- protection = self.beginOffsetProtection()
- result = None
- tmp_byte = self.contents[self.currentOffset:self.currentOffset+1]
- if len(tmp_byte) != 1:
- raise InvalidPlistException("No object found at offset: %d" % self.currentOffset)
- marker_byte = unpack("!B", tmp_byte)[0]
- format = (marker_byte >> 4) & 0x0f
- extra = marker_byte & 0x0f
- self.currentOffset += 1
-
- def proc_extra(extra):
- if extra == 0b1111:
- extra = self.readObject()
- return extra
-
- # bool, null, or fill byte
- if format == 0b0000:
- if extra == 0b0000:
- result = None
- elif extra == 0b1000:
- result = False
- elif extra == 0b1001:
- result = True
- elif extra == 0b1111:
- pass # fill byte
- else:
- raise InvalidPlistException("Invalid object found at offset: %d" % (self.currentOffset - 1))
- # int
- elif format == 0b0001:
- result = self.readInteger(pow(2, extra))
- # real
- elif format == 0b0010:
- result = self.readReal(extra)
- # date
- elif format == 0b0011 and extra == 0b0011:
- result = self.readDate()
- # data
- elif format == 0b0100:
- extra = proc_extra(extra)
- result = self.readData(extra)
- # ascii string
- elif format == 0b0101:
- extra = proc_extra(extra)
- result = self.readAsciiString(extra)
- # Unicode string
- elif format == 0b0110:
- extra = proc_extra(extra)
- result = self.readUnicode(extra)
- # uid
- elif format == 0b1000:
- result = self.readUid(extra)
- # array
- elif format == 0b1010:
- extra = proc_extra(extra)
- result = self.readArray(extra)
- # set
- elif format == 0b1100:
- extra = proc_extra(extra)
- result = set(self.readArray(extra))
- # dict
- elif format == 0b1101:
- extra = proc_extra(extra)
- result = self.readDict(extra)
- else:
- raise InvalidPlistException("Invalid object found: {format: %s, extra: %s}" % (bin(format), bin(extra)))
- self.endOffsetProtection(protection)
- return result
-
- def readContents(self, length, description="Object contents"):
- end = self.currentOffset + length
- if end >= len(self.contents) - 32:
- raise InvalidPlistException("%s extends into trailer" % description)
- elif length < 0:
- raise InvalidPlistException("%s length is less than zero" % length)
- data = self.contents[self.currentOffset:end]
- return data
-
- def readInteger(self, byteSize):
- data = self.readContents(byteSize, "Integer")
- self.currentOffset = self.currentOffset + byteSize
- return self.getSizedInteger(data, byteSize, as_number=True)
-
- def readReal(self, length):
- to_read = pow(2, length)
- data = self.readContents(to_read, "Real")
- if length == 2: # 4 bytes
- result = unpack('>f', data)[0]
- elif length == 3: # 8 bytes
- result = unpack('>d', data)[0]
- else:
- raise InvalidPlistException("Unknown Real of length %d bytes" % to_read)
- return result
-
- def readRefs(self, count):
- refs = []
- i = 0
- while i < count:
- fragment = self.readContents(self.trailer.objectRefSize, "Object reference")
- ref = self.getSizedInteger(fragment, len(fragment))
- refs.append(ref)
- self.currentOffset += self.trailer.objectRefSize
- i += 1
- return refs
-
- def readArray(self, count):
- if not isinstance(count, (int, long)):
- raise InvalidPlistException("Count of entries in dict isn't of integer type.")
- result = []
- values = self.readRefs(count)
- i = 0
- while i < len(values):
- self.setCurrentOffsetToObjectNumber(values[i])
- value = self.readObject()
- result.append(value)
- i += 1
- return result
-
- def readDict(self, count):
- if not isinstance(count, (int, long)):
- raise InvalidPlistException("Count of keys/values in dict isn't of integer type.")
- result = {}
- keys = self.readRefs(count)
- values = self.readRefs(count)
- i = 0
- while i < len(keys):
- self.setCurrentOffsetToObjectNumber(keys[i])
- key = self.readObject()
- self.setCurrentOffsetToObjectNumber(values[i])
- value = self.readObject()
- result[key] = value
- i += 1
- return result
-
- def readAsciiString(self, length):
- if not isinstance(length, (int, long)):
- raise InvalidPlistException("Length of ASCII string isn't of integer type.")
- data = self.readContents(length, "ASCII string")
- result = unpack("!%ds" % length, data)[0]
- self.currentOffset += length
- return str(result.decode('ascii'))
-
- def readUnicode(self, length):
- if not isinstance(length, (int, long)):
- raise InvalidPlistException("Length of Unicode string isn't of integer type.")
- actual_length = length*2
- data = self.readContents(actual_length, "Unicode string")
- self.currentOffset += actual_length
- return data.decode('utf_16_be')
-
- def readDate(self):
- data = self.readContents(8, "Date")
- x = unpack(">d", data)[0]
- if math.isnan(x):
- raise InvalidPlistException("Date is NaN")
- # Use timedelta to workaround time_t size limitation on 32-bit python.
- try:
- result = datetime.timedelta(seconds=x) + apple_reference_date
- except OverflowError:
- if x > 0:
- result = datetime.datetime.max
- else:
- result = datetime.datetime.min
- self.currentOffset += 8
- return result
-
- def readData(self, length):
- if not isinstance(length, (int, long)):
- raise InvalidPlistException("Length of data isn't of integer type.")
- result = self.readContents(length, "Data")
- self.currentOffset += length
- return Data(result)
-
- def readUid(self, length):
- if not isinstance(length, (int, long)):
- raise InvalidPlistException("Uid length isn't of integer type.")
- return Uid(self.readInteger(length+1))
-
- def getSizedInteger(self, data, byteSize, as_number=False):
- """Numbers of 8 bytes are signed integers when they refer to numbers, but unsigned otherwise."""
- result = 0
- if byteSize == 0:
- raise InvalidPlistException("Encountered integer with byte size of 0.")
- # 1, 2, and 4 byte integers are unsigned
- elif byteSize == 1:
- result = unpack('>B', data)[0]
- elif byteSize == 2:
- result = unpack('>H', data)[0]
- elif byteSize == 4:
- result = unpack('>L', data)[0]
- elif byteSize == 8:
- if as_number:
- result = unpack('>q', data)[0]
- else:
- result = unpack('>Q', data)[0]
- elif byteSize <= 16:
- # Handle odd-sized or integers larger than 8 bytes
- # Don't naively go over 16 bytes, in order to prevent infinite loops.
- result = 0
- if hasattr(int, 'from_bytes'):
- result = int.from_bytes(data, 'big')
- else:
- for byte in data:
- if not isinstance(byte, int): # Python3.0-3.1.x return ints, 2.x return str
- byte = unpack_from('>B', byte)[0]
- result = (result << 8) | byte
- else:
- raise InvalidPlistException("Encountered integer longer than 16 bytes.")
- return result
-
-class HashableWrapper(object):
- def __init__(self, value):
- self.value = value
- def __repr__(self):
- return "<HashableWrapper: %s>" % [self.value]
-
-class BoolWrapper(object):
- def __init__(self, value):
- self.value = value
- def __repr__(self):
- return "<BoolWrapper: %s>" % self.value
-
-class FloatWrapper(object):
- _instances = {}
- def __new__(klass, value):
- # Ensure FloatWrapper(x) for a given float x is always the same object
- wrapper = klass._instances.get(value)
- if wrapper is None:
- wrapper = object.__new__(klass)
- wrapper.value = value
- klass._instances[value] = wrapper
- return wrapper
- def __repr__(self):
- return "<FloatWrapper: %s>" % self.value
-
-class StringWrapper(object):
- __instances = {}
-
- encodedValue = None
- encoding = None
-
- def __new__(cls, value):
- '''Ensure we only have a only one instance for any string,
- and that we encode ascii as 1-byte-per character when possible'''
-
- encodedValue = None
-
- for encoding in ('ascii', 'utf_16_be'):
- try:
- encodedValue = value.encode(encoding)
- except: pass
- if encodedValue is not None:
- if encodedValue not in cls.__instances:
- cls.__instances[encodedValue] = super(StringWrapper, cls).__new__(cls)
- cls.__instances[encodedValue].encodedValue = encodedValue
- cls.__instances[encodedValue].encoding = encoding
- return cls.__instances[encodedValue]
-
- raise ValueError('Unable to get ascii or utf_16_be encoding for %s' % repr(value))
-
- def __len__(self):
- '''Return roughly the number of characters in this string (half the byte length)'''
- if self.encoding == 'ascii':
- return len(self.encodedValue)
- else:
- return len(self.encodedValue)//2
-
- def __lt__(self, other):
- return self.encodedValue < other.encodedValue
-
- @property
- def encodingMarker(self):
- if self.encoding == 'ascii':
- return 0b0101
- else:
- return 0b0110
-
- def __repr__(self):
- return '<StringWrapper (%s): %s>' % (self.encoding, self.encodedValue)
-
-class PlistWriter(object):
- header = b'bplist00bybiplist1.0'
- file = None
- byteCounts = None
- trailer = None
- computedUniques = None
- writtenReferences = None
- referencePositions = None
- wrappedTrue = None
- wrappedFalse = None
- # Used to detect recursive object references.
- objectsStack = []
-
- def __init__(self, file):
- self.reset()
- self.file = file
- self.wrappedTrue = BoolWrapper(True)
- self.wrappedFalse = BoolWrapper(False)
-
- def reset(self):
- self.byteCounts = PlistByteCounts(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
- self.trailer = PlistTrailer(0, 0, 0, 0, 0)
-
- # A set of all the uniques which have been computed.
- self.computedUniques = set()
- # A list of all the uniques which have been written.
- self.writtenReferences = {}
- # A dict of the positions of the written uniques.
- self.referencePositions = {}
-
- self.objectsStack = []
-
- def positionOfObjectReference(self, obj):
- """If the given object has been written already, return its
- position in the offset table. Otherwise, return None."""
- return self.writtenReferences.get(obj)
-
- def writeRoot(self, root):
- """
- Strategy is:
- - write header
- - wrap root object so everything is hashable
- - compute size of objects which will be written
- - need to do this in order to know how large the object refs
- will be in the list/dict/set reference lists
- - write objects
- - keep objects in writtenReferences
- - keep positions of object references in referencePositions
- - write object references with the length computed previously
- - computer object reference length
- - write object reference positions
- - write trailer
- """
- output = self.header
- wrapped_root = self.wrapRoot(root)
- self.computeOffsets(wrapped_root, asReference=True, isRoot=True)
- self.trailer = self.trailer._replace(**{'objectRefSize':self.intSize(len(self.computedUniques))})
- self.writeObjectReference(wrapped_root, output)
- output = self.writeObject(wrapped_root, output, setReferencePosition=True)
-
- # output size at this point is an upper bound on how big the
- # object reference offsets need to be.
- self.trailer = self.trailer._replace(**{
- 'offsetSize':self.intSize(len(output)),
- 'offsetCount':len(self.computedUniques),
- 'offsetTableOffset':len(output),
- 'topLevelObjectNumber':0
- })
-
- output = self.writeOffsetTable(output)
- output += pack('!xxxxxxBBQQQ', *self.trailer)
- self.file.write(output)
-
- def beginRecursionProtection(self, obj):
- if not isinstance(obj, (set, dict, list, tuple)):
- return
- if id(obj) in self.objectsStack:
- raise InvalidPlistException("Recursive containers are not allowed in plists.")
- self.objectsStack.append(id(obj))
-
- def endRecursionProtection(self, obj):
- if not isinstance(obj, (set, dict, list, tuple)):
- return
- try:
- index = self.objectsStack.index(id(obj))
- self.objectsStack = self.objectsStack[:index]
- except ValueError as e:
- pass
-
- def wrapRoot(self, root):
- result = None
- self.beginRecursionProtection(root)
-
- if isinstance(root, bool):
- if root is True:
- result = self.wrappedTrue
- else:
- result = self.wrappedFalse
- elif isinstance(root, float):
- result = FloatWrapper(root)
- elif isinstance(root, set):
- n = set()
- for value in root:
- n.add(self.wrapRoot(value))
- result = HashableWrapper(n)
- elif isinstance(root, dict):
- n = {}
- for key, value in iteritems(root):
- n[self.wrapRoot(key)] = self.wrapRoot(value)
- result = HashableWrapper(n)
- elif isinstance(root, list):
- n = []
- for value in root:
- n.append(self.wrapRoot(value))
- result = HashableWrapper(n)
- elif isinstance(root, tuple):
- n = tuple([self.wrapRoot(value) for value in root])
- result = HashableWrapper(n)
- elif isinstance(root, (str, unicode)) and not isinstance(root, Data):
- result = StringWrapper(root)
- elif isinstance(root, bytes):
- result = Data(root)
- else:
- result = root
-
- self.endRecursionProtection(root)
- return result
-
- def incrementByteCount(self, field, incr=1):
- self.byteCounts = self.byteCounts._replace(**{field:self.byteCounts.__getattribute__(field) + incr})
-
- def computeOffsets(self, obj, asReference=False, isRoot=False):
- def check_key(key):
- if key is None:
- raise InvalidPlistException('Dictionary keys cannot be null in plists.')
- elif isinstance(key, Data):
- raise InvalidPlistException('Data cannot be dictionary keys in plists.')
- elif not isinstance(key, StringWrapper):
- raise InvalidPlistException('Keys must be strings.')
-
- def proc_size(size):
- if size > 0b1110:
- size += self.intSize(size)
- return size
- # If this should be a reference, then we keep a record of it in the
- # uniques table.
- if asReference:
- if obj in self.computedUniques:
- return
- else:
- self.computedUniques.add(obj)
-
- if obj is None:
- self.incrementByteCount('nullBytes')
- elif isinstance(obj, BoolWrapper):
- self.incrementByteCount('boolBytes')
- elif isinstance(obj, Uid):
- size = self.intSize(obj.integer)
- self.incrementByteCount('uidBytes', incr=1+size)
- elif isinstance(obj, (int, long)):
- size = self.intSize(obj)
- self.incrementByteCount('intBytes', incr=1+size)
- elif isinstance(obj, FloatWrapper):
- size = self.realSize(obj)
- self.incrementByteCount('realBytes', incr=1+size)
- elif isinstance(obj, datetime.datetime):
- self.incrementByteCount('dateBytes', incr=2)
- elif isinstance(obj, Data):
- size = proc_size(len(obj))
- self.incrementByteCount('dataBytes', incr=1+size)
- elif isinstance(obj, StringWrapper):
- size = proc_size(len(obj))
- self.incrementByteCount('stringBytes', incr=1+size)
- elif isinstance(obj, HashableWrapper):
- obj = obj.value
- if isinstance(obj, set):
- size = proc_size(len(obj))
- self.incrementByteCount('setBytes', incr=1+size)
- for value in obj:
- self.computeOffsets(value, asReference=True)
- elif isinstance(obj, (list, tuple)):
- size = proc_size(len(obj))
- self.incrementByteCount('arrayBytes', incr=1+size)
- for value in obj:
- asRef = True
- self.computeOffsets(value, asReference=True)
- elif isinstance(obj, dict):
- size = proc_size(len(obj))
- self.incrementByteCount('dictBytes', incr=1+size)
- for key, value in iteritems(obj):
- check_key(key)
- self.computeOffsets(key, asReference=True)
- self.computeOffsets(value, asReference=True)
- else:
- raise InvalidPlistException("Unknown object type: %s (%s)" % (type(obj).__name__, repr(obj)))
-
- def writeObjectReference(self, obj, output):
- """Tries to write an object reference, adding it to the references
- table. Does not write the actual object bytes or set the reference
- position. Returns a tuple of whether the object was a new reference
- (True if it was, False if it already was in the reference table)
- and the new output.
- """
- position = self.positionOfObjectReference(obj)
- if position is None:
- self.writtenReferences[obj] = len(self.writtenReferences)
- output += self.binaryInt(len(self.writtenReferences) - 1, byteSize=self.trailer.objectRefSize)
- return (True, output)
- else:
- output += self.binaryInt(position, byteSize=self.trailer.objectRefSize)
- return (False, output)
-
- def writeObject(self, obj, output, setReferencePosition=False):
- """Serializes the given object to the output. Returns output.
- If setReferencePosition is True, will set the position the
- object was written.
- """
- def proc_variable_length(format, length):
- result = b''
- if length > 0b1110:
- result += pack('!B', (format << 4) | 0b1111)
- result = self.writeObject(length, result)
- else:
- result += pack('!B', (format << 4) | length)
- return result
-
- def timedelta_total_seconds(td):
- # Shim for Python 2.6 compatibility, which doesn't have total_seconds.
- # Make one argument a float to ensure the right calculation.
- return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10.0**6) / 10.0**6
-
- if setReferencePosition:
- self.referencePositions[obj] = len(output)
-
- if obj is None:
- output += pack('!B', 0b00000000)
- elif isinstance(obj, BoolWrapper):
- if obj.value is False:
- output += pack('!B', 0b00001000)
- else:
- output += pack('!B', 0b00001001)
- elif isinstance(obj, Uid):
- size = self.intSize(obj.integer)
- output += pack('!B', (0b1000 << 4) | size - 1)
- output += self.binaryInt(obj.integer)
- elif isinstance(obj, (int, long)):
- byteSize = self.intSize(obj)
- root = math.log(byteSize, 2)
- output += pack('!B', (0b0001 << 4) | int(root))
- output += self.binaryInt(obj, as_number=True)
- elif isinstance(obj, FloatWrapper):
- # just use doubles
- output += pack('!B', (0b0010 << 4) | 3)
- output += self.binaryReal(obj)
- elif isinstance(obj, datetime.datetime):
- try:
- timestamp = (obj - apple_reference_date).total_seconds()
- except AttributeError:
- timestamp = timedelta_total_seconds(obj - apple_reference_date)
- output += pack('!B', 0b00110011)
- output += pack('!d', float(timestamp))
- elif isinstance(obj, Data):
- output += proc_variable_length(0b0100, len(obj))
- output += obj
- elif isinstance(obj, StringWrapper):
- output += proc_variable_length(obj.encodingMarker, len(obj))
- output += obj.encodedValue
- elif isinstance(obj, bytes):
- output += proc_variable_length(0b0101, len(obj))
- output += obj
- elif isinstance(obj, HashableWrapper):
- obj = obj.value
- if isinstance(obj, (set, list, tuple)):
- if isinstance(obj, set):
- output += proc_variable_length(0b1100, len(obj))
- else:
- output += proc_variable_length(0b1010, len(obj))
-
- objectsToWrite = []
- for objRef in sorted(obj) if isinstance(obj, set) else obj:
- (isNew, output) = self.writeObjectReference(objRef, output)
- if isNew:
- objectsToWrite.append(objRef)
- for objRef in objectsToWrite:
- output = self.writeObject(objRef, output, setReferencePosition=True)
- elif isinstance(obj, dict):
- output += proc_variable_length(0b1101, len(obj))
- keys = []
- values = []
- objectsToWrite = []
- for key, value in sorted(iteritems(obj)):
- keys.append(key)
- values.append(value)
- for key in keys:
- (isNew, output) = self.writeObjectReference(key, output)
- if isNew:
- objectsToWrite.append(key)
- for value in values:
- (isNew, output) = self.writeObjectReference(value, output)
- if isNew:
- objectsToWrite.append(value)
- for objRef in objectsToWrite:
- output = self.writeObject(objRef, output, setReferencePosition=True)
- return output
-
- def writeOffsetTable(self, output):
- """Writes all of the object reference offsets."""
- all_positions = []
- writtenReferences = list(self.writtenReferences.items())
- writtenReferences.sort(key=lambda x: x[1])
- for obj,order in writtenReferences:
- # Porting note: Elsewhere we deliberately replace empty unicdoe strings
- # with empty binary strings, but the empty unicode string
- # goes into writtenReferences. This isn't an issue in Py2
- # because u'' and b'' have the same hash; but it is in
- # Py3, where they don't.
- if bytes != str and obj == unicodeEmpty:
- obj = b''
- position = self.referencePositions.get(obj)
- if position is None:
- raise InvalidPlistException("Error while writing offsets table. Object not found. %s" % obj)
- output += self.binaryInt(position, self.trailer.offsetSize)
- all_positions.append(position)
- return output
-
- def binaryReal(self, obj):
- # just use doubles
- result = pack('>d', obj.value)
- return result
-
- def binaryInt(self, obj, byteSize=None, as_number=False):
- result = b''
- if byteSize is None:
- byteSize = self.intSize(obj)
- if byteSize == 1:
- result += pack('>B', obj)
- elif byteSize == 2:
- result += pack('>H', obj)
- elif byteSize == 4:
- result += pack('>L', obj)
- elif byteSize == 8:
- if as_number:
- result += pack('>q', obj)
- else:
- result += pack('>Q', obj)
- elif byteSize <= 16:
- try:
- result = pack('>Q', 0) + pack('>Q', obj)
- except struct_error as e:
- raise InvalidPlistException("Unable to pack integer %d: %s" % (obj, e))
- else:
- raise InvalidPlistException("Core Foundation can't handle integers with size greater than 16 bytes.")
- return result
-
- def intSize(self, obj):
- """Returns the number of bytes necessary to store the given integer."""
- # SIGNED
- if obj < 0: # Signed integer, always 8 bytes
- return 8
- # UNSIGNED
- elif obj <= 0xFF: # 1 byte
- return 1
- elif obj <= 0xFFFF: # 2 bytes
- return 2
- elif obj <= 0xFFFFFFFF: # 4 bytes
- return 4
- # SIGNED
- # 0x7FFFFFFFFFFFFFFF is the max.
- elif obj <= 0x7FFFFFFFFFFFFFFF: # 8 bytes signed
- return 8
- elif obj <= 0xffffffffffffffff: # 8 bytes unsigned
- return 16
- else:
- raise InvalidPlistException("Core Foundation can't handle integers with size greater than 8 bytes.")
-
- def realSize(self, obj):
- return 8
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/biplist/qt_attribution.json b/src/3rdparty/python/lib/python2.7/site-packages/biplist/qt_attribution.json
deleted file mode 100644
index 266e9dd89..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/biplist/qt_attribution.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "Id": "biplist",
- "Name": "biplist",
- "QDocModule": "qbs",
- "QtUsage": "Used in the qbs dmg module for building Apple disk images.",
- "Description": "biplist is a library for reading/writing binary plists.",
- "Homepage": "https://bitbucket.org/wooster/biplist",
- "Version": "1.0.2",
- "License": "BSD 3-clause \"New\" or \"Revised\" License",
- "LicenseId": "BSD-3-Clause",
- "LicenseFile": "LICENSE",
- "Copyright": "Copyright (c) 2010, Andrew Wooster"
-}
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/__init__.py b/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/__init__.py
deleted file mode 100644
index e7f985c32..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .core import build_dmg
-
-__all__ = ['dmgbuild']
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/badge.py b/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/badge.py
deleted file mode 100644
index 159a53708..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/badge.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from Quartz import *
-import math
-
-_REMOVABLE_DISK_PATH = '/System/Library/Extensions/IOStorageFamily.kext/Contents/Resources/Removable.icns'
-
-def badge_disk_icon(badge_file, output_file):
- # Load the Removable disk icon
- url = CFURLCreateWithFileSystemPath(None, _REMOVABLE_DISK_PATH,
- kCFURLPOSIXPathStyle, False)
- backdrop = CGImageSourceCreateWithURL(url, None)
- backdropCount = CGImageSourceGetCount(backdrop)
-
- # Load the badge
- url = CFURLCreateWithFileSystemPath(None, badge_file,
- kCFURLPOSIXPathStyle, False)
- badge = CGImageSourceCreateWithURL(url, None)
- assert badge is not None, 'Unable to process image file: %s' % badge_file
- badgeCount = CGImageSourceGetCount(badge)
-
- # Set up a destination for our target
- url = CFURLCreateWithFileSystemPath(None, output_file,
- kCFURLPOSIXPathStyle, False)
- target = CGImageDestinationCreateWithURL(url, 'com.apple.icns',
- backdropCount, None)
-
- # Get the RGB colorspace
- rgbColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)
-
- # Scale
- scale = 1.0
-
- # Perspective transform
- corners = ((0.2, 0.95), (0.8, 0.95), (0.85, 0.35), (0.15, 0.35))
-
- # Translation
- position = (0.5, 0.5)
-
- for n in range(backdropCount):
- props = CGImageSourceCopyPropertiesAtIndex(backdrop, n, None)
- width = props['PixelWidth']
- height = props['PixelHeight']
- dpi = props['DPIWidth']
- depth = props['Depth']
-
- # Choose the best sized badge image
- bestWidth = None
- bestHeight = None
- bestBadge = None
- bestDepth = None
- bestDPI = None
- for m in range(badgeCount):
- badgeProps = CGImageSourceCopyPropertiesAtIndex(badge, m, None)
- badgeWidth = badgeProps['PixelWidth']
- badgeHeight = badgeProps['PixelHeight']
- badgeDPI = badgeProps['DPIWidth']
- badgeDepth = badgeProps['Depth']
-
- if bestBadge is None or (badgeWidth <= width
- and (bestWidth > width
- or badgeWidth > bestWidth
- or (badgeWidth == bestWidth
- and badgeDPI == dpi
- and badgeDepth <= depth
- and (bestDepth is None
- or badgeDepth > bestDepth)))):
- bestBadge = m
- bestWidth = badgeWidth
- bestHeight = badgeHeight
- bestDPI = badgeDPI
- bestDepth = badgeDepth
-
- badgeImage = CGImageSourceCreateImageAtIndex(badge, bestBadge, None)
- badgeCI = CIImage.imageWithCGImage_(badgeImage)
-
- backgroundImage = CGImageSourceCreateImageAtIndex(backdrop, n, None)
- backgroundCI = CIImage.imageWithCGImage_(backgroundImage)
-
- compositor = CIFilter.filterWithName_('CISourceOverCompositing')
- lanczos = CIFilter.filterWithName_('CILanczosScaleTransform')
- perspective = CIFilter.filterWithName_('CIPerspectiveTransform')
- transform = CIFilter.filterWithName_('CIAffineTransform')
-
- lanczos.setValue_forKey_(badgeCI, kCIInputImageKey)
- lanczos.setValue_forKey_(scale * float(width)/bestWidth, kCIInputScaleKey)
- lanczos.setValue_forKey_(1.0, kCIInputAspectRatioKey)
-
- topLeft = (width * scale * corners[0][0],
- width * scale * corners[0][1])
- topRight = (width * scale * corners[1][0],
- width * scale * corners[1][1])
- bottomRight = (width * scale * corners[2][0],
- width * scale * corners[2][1])
- bottomLeft = (width * scale * corners[3][0],
- width * scale * corners[3][1])
-
- out = lanczos.valueForKey_(kCIOutputImageKey)
- if width >= 16:
- perspective.setValue_forKey_(out, kCIInputImageKey)
- perspective.setValue_forKey_(CIVector.vectorWithX_Y_(*topLeft),
- 'inputTopLeft')
- perspective.setValue_forKey_(CIVector.vectorWithX_Y_(*topRight),
- 'inputTopRight')
- perspective.setValue_forKey_(CIVector.vectorWithX_Y_(*bottomRight),
- 'inputBottomRight')
- perspective.setValue_forKey_(CIVector.vectorWithX_Y_(*bottomLeft),
- 'inputBottomLeft')
- out = perspective.valueForKey_(kCIOutputImageKey)
-
- tfm = NSAffineTransform.transform()
- tfm.translateXBy_yBy_(math.floor((position[0] - 0.5 * scale) * width),
- math.floor((position[1] - 0.5 * scale) * height))
-
- transform.setValue_forKey_(out, kCIInputImageKey)
- transform.setValue_forKey_(tfm, 'inputTransform')
- out = transform.valueForKey_(kCIOutputImageKey)
-
- compositor.setValue_forKey_(out, kCIInputImageKey)
- compositor.setValue_forKey_(backgroundCI, kCIInputBackgroundImageKey)
-
- result = compositor.valueForKey_(kCIOutputImageKey)
-
- cgContext = CGBitmapContextCreate(None,
- width,
- height,
- 8,
- 0,
- rgbColorSpace,
- kCGImageAlphaPremultipliedLast)
- context = CIContext.contextWithCGContext_options_(cgContext, None)
-
- context.drawImage_inRect_fromRect_(result,
- ((0, 0), (width, height)),
- ((0, 0), (width, height)))
-
- image = CGBitmapContextCreateImage(cgContext)
-
- CGImageDestinationAddImage(target, image, props)
-
- CGImageDestinationFinalize(target)
-
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/colors.py b/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/colors.py
deleted file mode 100644
index 1d252a6bd..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/colors.py
+++ /dev/null
@@ -1,494 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import re
-import math
-
-class Color (object):
- def to_rgb(self):
- raise Exception('Must implement to_rgb() in subclasses')
-
-class RGB (Color):
- def __init__(self, r, g, b):
- self.r = r
- self.g = g
- self.b = b
-
- def to_rgb(self):
- return self
-
-class HSL (Color):
- def __init__(self, h, s, l):
- self.h = h
- self.s = s
- self.l = l
-
- @staticmethod
- def _hue_to_rgb(t1, t2, hue):
- if hue < 0:
- hue += 6
- elif hue >= 6:
- hue -= 6
-
- if hue < 1:
- return (t2 - t1) * hue + t1
- elif hue < 3:
- return t2
- elif hue < 4:
- return (t2 - t1) * (4 - hue) + t1
- else:
- return t1
-
- def to_rgb(self):
- hue = self.h / 60.0
- if self.l <= 0.5:
- t2 = self.l * (self.s + 1)
- else:
- t2 = self.l + self.s - (self.l * self.s)
- t1 = self.l * 2 - t2
- r = self._hue_to_rgb(t1, t2, hue + 2)
- g = self._hue_to_rgb(t1, t2, hue)
- b = self._hue_to_rgb(t1, t2, hue - 2)
- return RGB(r, g, b)
-
-class HWB (Color):
- def __init__(self, h, w, b):
- self.h = h
- self.w = w
- self.b = b
-
- @staticmethod
- def _hue_to_rgb(hue):
- if hue < 0:
- hue += 6
- elif hue >= 6:
- hue -= 6
-
- if hue < 1:
- return hue
- elif hue < 3:
- return 1
- elif hue < 4:
- return (4 - hue)
- else:
- return 0
-
- def to_rgb(self):
- hue = self.h / 60.0
- t1 = 1 - self.w - self.b
- r = self._hue_to_rgb(hue + 2) * t1 + self.w
- g = self._hue_to_rgb(hue) * t1 + self.w
- b = self._hue_to_rgb(hue - 2) * t1 + self.w
- return RGB(r, g, b)
-
-class CMYK (Color):
- def __init__(self, c, m, y, k):
- self.c = c
- self.m = m
- self.y = y
- self.k = k
-
- def to_rgb(self):
- r = 1.0 - min(1.0, self.c + self.k)
- g = 1.0 - min(1.0, self.m + self.k)
- b = 1.0 - min(1.0, self.y + self.k)
- return RGB(r, g, b)
-
-class Gray (Color):
- def __init__(self, g):
- self.g = g
-
- def to_rgb(self):
- return RGB(g, g, g)
-
-_x11_colors = {
- 'aliceblue': (240, 248, 255),
- 'antiquewhite': (250, 235, 215),
- 'aqua': ( 0, 255, 255),
- 'aquamarine': (127, 255, 212),
- 'azure': (240, 255, 255),
- 'beige': (245, 245, 220),
- 'bisque': (255, 228, 196),
- 'black': ( 0, 0, 0),
- 'blanchedalmond': (255, 235, 205),
- 'blue': ( 0, 0, 255),
- 'blueviolet': (138, 43, 226),
- 'brown': (165, 42, 42),
- 'burlywood': (222, 184, 135),
- 'cadetblue': ( 95, 158, 160),
- 'chartreuse': (127, 255, 0),
- 'chocolate': (210, 105, 30),
- 'coral': (255, 127, 80),
- 'cornflowerblue': (100, 149, 237),
- 'cornsilk': (255, 248, 220),
- 'crimson': (220, 20, 60),
- 'cyan': ( 0, 255, 255),
- 'darkblue': ( 0, 0, 139),
- 'darkcyan': ( 0, 139, 139),
- 'darkgoldenrod': (184, 134, 11),
- 'darkgray': (169, 169, 169),
- 'darkgreen': ( 0, 100, 0),
- 'darkgrey': (169, 169, 169),
- 'darkkhaki': (189, 183, 107),
- 'darkmagenta': (139, 0, 139),
- 'darkolivegreen': ( 85, 107, 47),
- 'darkorange': (255, 140, 0),
- 'darkorchid': (153, 50, 204),
- 'darkred': (139, 0, 0),
- 'darksalmon': (233, 150, 122),
- 'darkseagreen': (143, 188, 143),
- 'darkslateblue': ( 72, 61, 139),
- 'darkslategray': ( 47, 79, 79),
- 'darkslategrey': ( 47, 79, 79),
- 'darkturquoise': ( 0, 206, 209),
- 'darkviolet': (148, 0, 211),
- 'deeppink': (255, 20, 147),
- 'deepskyblue': ( 0, 191, 255),
- 'dimgray': (105, 105, 105),
- 'dimgrey': (105, 105, 105),
- 'dodgerblue': ( 30, 144, 255),
- 'firebrick': (178, 34, 34),
- 'floralwhite': (255, 250, 240),
- 'forestgreen': ( 34, 139, 34),
- 'fuchsia': (255, 0, 255),
- 'gainsboro': (220, 220, 220),
- 'ghostwhite': (248, 248, 255),
- 'gold': (255, 215, 0),
- 'goldenrod': (218, 165, 32),
- 'gray': (128, 128, 128),
- 'grey': (128, 128, 128),
- 'green': ( 0, 128, 0),
- 'greenyellow': (173, 255, 47),
- 'honeydew': (240, 255, 240),
- 'hotpink': (255, 105, 180),
- 'indianred': (205, 92, 92),
- 'indigo': ( 75, 0, 130),
- 'ivory': (255, 255, 240),
- 'khaki': (240, 230, 140),
- 'lavender': (230, 230, 250),
- 'lavenderblush': (255, 240, 245),
- 'lawngreen': (124, 252, 0),
- 'lemonchiffon': (255, 250, 205),
- 'lightblue': (173, 216, 230),
- 'lightcoral': (240, 128, 128),
- 'lightcyan': (224, 255, 255),
- 'lightgoldenrodyellow': (250, 250, 210),
- 'lightgray': (211, 211, 211),
- 'lightgreen': (144, 238, 144),
- 'lightgrey': (211, 211, 211),
- 'lightpink': (255, 182, 193),
- 'lightsalmon': (255, 160, 122),
- 'lightseagreen': ( 32, 178, 170),
- 'lightskyblue': (135, 206, 250),
- 'lightslategray': (119, 136, 153),
- 'lightslategrey': (119, 136, 153),
- 'lightsteelblue': (176, 196, 222),
- 'lightyellow': (255, 255, 224),
- 'lime': ( 0, 255, 0),
- 'limegreen': ( 50, 205, 50),
- 'linen': (250, 240, 230),
- 'magenta': (255, 0, 255),
- 'maroon': (128, 0, 0),
- 'mediumaquamarine': (102, 205, 170),
- 'mediumblue': ( 0, 0, 205),
- 'mediumorchid': (186, 85, 211),
- 'mediumpurple': (147, 112, 219),
- 'mediumseagreen': ( 60, 179, 113),
- 'mediumslateblue': (123, 104, 238),
- 'mediumspringgreen': ( 0, 250, 154),
- 'mediumturquoise': ( 72, 209, 204),
- 'mediumvioletred': (199, 21, 133),
- 'midnightblue': ( 25, 25, 112),
- 'mintcream': (245, 255, 250),
- 'mistyrose': (255, 228, 225),
- 'moccasin': (255, 228, 181),
- 'navajowhite': (255, 222, 173),
- 'navy': ( 0, 0, 128),
- 'oldlace': (253, 245, 230),
- 'olive': (128, 128, 0),
- 'olivedrab': (107, 142, 35),
- 'orange': (255, 165, 0),
- 'orangered': (255, 69, 0),
- 'orchid': (218, 112, 214),
- 'palegoldenrod': (238, 232, 170),
- 'palegreen': (152, 251, 152),
- 'paleturquoise': (175, 238, 238),
- 'palevioletred': (219, 112, 147),
- 'papayawhip': (255, 239, 213),
- 'peachpuff': (255, 218, 185),
- 'peru': (205, 133, 63),
- 'pink': (255, 192, 203),
- 'plum': (221, 160, 221),
- 'powderblue': (176, 224, 230),
- 'purple': (128, 0, 128),
- 'red': (255, 0, 0),
- 'rosybrown': (188, 143, 143),
- 'royalblue': ( 65, 105, 225),
- 'saddlebrown': (139, 69, 19),
- 'salmon': (250, 128, 114),
- 'sandybrown': (244, 164, 96),
- 'seagreen': ( 46, 139, 87),
- 'seashell': (255, 245, 238),
- 'sienna': (160, 82, 45),
- 'silver': (192, 192, 192),
- 'skyblue': (135, 206, 235),
- 'slateblue': (106, 90, 205),
- 'slategray': (112, 128, 144),
- 'slategrey': (112, 128, 144),
- 'snow': (255, 250, 250),
- 'springgreen': ( 0, 255, 127),
- 'steelblue': ( 70, 130, 180),
- 'tan': (210, 180, 140),
- 'teal': ( 0, 128, 128),
- 'thistle': (216, 191, 216),
- 'tomato': (255, 99, 71),
- 'turquoise': ( 64, 224, 208),
- 'violet': (238, 130, 238),
- 'wheat': (245, 222, 179),
- 'white': (255, 255, 255),
- 'whitesmoke': (245, 245, 245),
- 'yellow': (255, 255, 0),
- 'yellowgreen': (154, 205, 50)
- }
-
-_ws_re = re.compile('\s+')
-_token_re = re.compile('[A-Za-z_][A-Za-z0-9_]*')
-_hex_re = re.compile('#([0-9a-f]{3}(?:[0-9a-f]{3})?)$')
-_number_re = re.compile('[0-9]*(\.[0-9]*)')
-
-class ColorParser (object):
- def __init__(self, s):
- self._string = s
- self._pos = 0
-
- def skipws(self):
- m = _ws_re.match(self._string, self._pos)
- if m:
- self._pos = m.end(0)
-
- def expect(self, s, context=''):
- if len(self._string) - self._pos < len(s) \
- or self._string[self._pos:self._pos + len(s)] != s:
- raise ValueError('bad color "%s" - expected "%s"%s'
- % (self._string, s, context))
- self._pos += len(s)
-
- def expectEnd(self):
- if self._pos != len(self._string):
- raise ValueError('junk at end of color "%s"' % self._string)
-
- def getToken(self):
- m = _token_re.match(self._string, self._pos)
- if m:
- token = m.group(0)
-
- self._pos = m.end(0)
- return token
- return None
-
- def parseNumber(self, context=''):
- m = _number_re.match(self._string, self._pos)
- if m:
- self._pos = m.end(0)
- return float(m.group(0))
- raise ValueError('bad color "%s" - expected a number%s'
- % (self._string, context))
-
- def parseColor(self):
- self.skipws()
-
- token = self.getToken()
- if token:
- if token == 'rgb':
- return self.parseRGB()
- elif token == 'hsl':
- return self.parseHSL()
- elif token == 'hwb':
- return self.parseHWB()
- elif token == 'cmyk':
- return self.parseCMYK()
- elif token == 'gray' or token == 'grey':
- return self.parseGray()
-
- try:
- r, g, b = _x11_colors[token]
- except KeyError:
- raise ValueError('unknown color name "%s"' % token)
-
- self.expectEnd()
-
- return RGB(r / 255.0, g / 255.0, b / 255.0)
-
- m = _hex_re.match(self._string, self._pos)
- if m:
- hrgb = m.group(1)
-
- if len(hrgb) == 3:
- r = int('0x' + 2 * hrgb[0], 16)
- g = int('0x' + 2 * hrgb[1], 16)
- b = int('0x' + 2 * hrgb[2], 16)
- else:
- r = int('0x' + hrgb[0:2], 16)
- g = int('0x' + hrgb[2:4], 16)
- b = int('0x' + hrgb[4:6], 16)
-
- self._pos = m.end(0)
- self.skipws()
-
- self.expectEnd()
-
- return RGB(r / 255.0, g / 255.0, b / 255.0)
-
- raise ValueError('bad color syntax "%s"' % self._string)
-
- def parseRGB(self):
- self.expect('(', 'after "rgb"')
- self.skipws()
-
- r = self.parseValue()
-
- self.skipws()
- self.expect(',', 'in "rgb"')
- self.skipws()
-
- g = self.parseValue()
-
- self.skipws()
- self.expect(',', 'in "rgb"')
- self.skipws()
-
- b = self.parseValue()
-
- self.skipws()
- self.expect(')', 'at end of "rgb"')
-
- self.skipws()
- self.expectEnd()
-
- return RGB(r, g, b)
-
- def parseHSL(self):
- self.expect('(', 'after "hsl"')
- self.skipws()
-
- h = self.parseAngle()
-
- self.skipws()
- self.expect(',', 'in "hsl"')
- self.skipws()
-
- s = self.parseValue()
-
- self.skipws()
- self.expect(',', 'in "hsl"')
- self.skipws()
-
- l = self.parseValue()
-
- self.skipws()
- self.expect(')', 'at end of "hsl"')
-
- self.skipws()
- self.expectEnd()
-
- return HSL(h, s, l)
-
- def parseHWB(self):
- self.expect('(', 'after "hwb"')
- self.skipws()
-
- h = self.parseAngle()
-
- self.skipws()
- self.expect(',', 'in "hwb"')
- self.skipws()
-
- w = self.parseValue()
-
- self.skipws()
- self.expect(',', 'in "hwb"')
- self.skipws()
-
- b = self.parseValue()
-
- self.skipws()
- self.expect(')', 'at end of "hwb"')
-
- self.skipws()
- self.expectEnd()
-
- return HWB(h, w, b)
-
- def parseCMYK(self):
- self.expect('(', 'after "cmyk"')
- self.skipws()
-
- c = self.parseValue()
-
- self.skipws()
- self.expect(',', 'in "cmyk"')
- self.skipws()
-
- m = self.parseValue()
-
- self.skipws()
- self.expect(',', 'in "cmyk"')
- self.skipws()
-
- y = self.parseValue()
-
- self.skipws()
- self.expect(',', 'in "cmyk"')
- self.skipws()
-
- k = self.parseValue()
-
- self.skipws()
- self.expect(')', 'at end of "cmyk"')
-
- self.skipws()
- self.expectEnd()
-
- return CMYK(c, m, y, k)
-
- def parseGray(self):
- self.expect('(', 'after "gray"')
- self.skipws()
-
- g = self.parseValue()
-
- self.skipws()
- self.expect(')', 'at end of "gray')
-
- self.skipws()
- self.expectEnd()
-
- return Gray(g)
-
- def parseValue(self):
- n = self.parseNumber()
- self.skipws()
- if self._string[self._pos] == '%':
- n = n / 100.0
- self.pos += 1
- return n
-
- def parseAngle(self):
- n = self.parseNumber()
- self.skipws()
- tok = self.getToken()
- if tok == 'rad':
- n = n * 180.0 / math.pi
- elif tok == 'grad' or tok == 'gon':
- n = n * 0.9
- elif tok != 'deg':
- raise ValueError('bad angle unit "%s"' % tok)
- return n
-
-_color_re = re.compile('\s*(#|rgb|hsl|hwb|cmyk|gray|grey|%s)'
- % '|'.join(_x11_colors.keys()))
-def isAColor(s):
- return _color_re.match(s)
-
-def parseColor(s):
- return ColorParser(s).parseColor()
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/core.py b/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/core.py
deleted file mode 100644
index 463730b68..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/core.py
+++ /dev/null
@@ -1,597 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import os
-import pkg_resources
-import re
-import shutil
-import stat
-import subprocess
-import sys
-import tempfile
-import tokenize
-import json
-
-try:
- {}.iteritems
- iteritems = lambda x: x.iteritems()
- iterkeys = lambda x: x.iterkeys()
-except AttributeError:
- iteritems = lambda x: x.items()
- iterkeys = lambda x: x.keys()
-try:
- unicode
-except NameError:
- unicode = str
-
-import biplist
-from mac_alias import *
-from ds_store import *
-
-from . import colors
-from . import licensing
-
-try:
- from . import badge
-except ImportError:
- badge = None
-
-_hexcolor_re = re.compile(r'#[0-9a-f]{3}(?:[0-9a-f]{3})?')
-
-class DMGError(Exception):
- pass
-
-def hdiutil(cmd, *args, **kwargs):
- plist = kwargs.get('plist', True)
- all_args = ['/usr/bin/hdiutil', cmd]
- all_args.extend(args)
- if plist:
- all_args.append('-plist')
- p = subprocess.Popen(all_args, stdout=subprocess.PIPE, close_fds=True)
- output, errors = p.communicate()
- if plist:
- results = biplist.readPlistFromString(output)
- else:
- results = output
- retcode = p.wait()
- return retcode, results
-
-# On Python 2 we can just execfile() it, but Python 3 deprecated that
-def load_settings(filename, settings):
- if sys.version_info[0] == 2:
- execfile(filename, settings, settings)
- else:
- encoding = 'utf-8'
- with open(filename, 'rb') as fp:
- try:
- encoding = tokenize.detect_encoding(fp.readline)[0]
- except SyntaxError:
- pass
-
- with open(filename, 'r', encoding=encoding) as fp:
- exec(compile(fp.read(), filename, 'exec'), settings, settings)
-
-def load_json(filename, settings):
- """Read an appdmg .json spec. Uses the defaults for appdmg, rather than
- the usual defaults for dmgbuild. """
-
- with open(filename, 'r') as fp:
- json_data = json.load(fp)
-
- if 'title' not in json_data:
- raise ValueError('missing \'title\' in JSON settings file')
- if 'contents' not in json_data:
- raise ValueError('missing \'contents\' in JSON settings file')
-
- settings['volume_name'] = json_data['title']
- settings['icon'] = json_data.get('icon', None)
- settings['badge_icon'] = json_data.get('badge-icon', None)
- bk = json_data.get('background', None)
- if bk is None:
- bk = json_data.get('background-color', None)
- if bk is not None:
- settings['background'] = bk
- settings['icon_size'] = json_data.get('icon-size', 80)
- wnd = json_data.get('window', { 'position': (100, 100),
- 'size': (640, 480) })
- pos = wnd.get('position', { 'x': 100, 'y': 100 })
- siz = wnd.get('size', { 'width': 640, 'height': 480 })
- settings['window_rect'] = ((pos.get('x', 100), pos.get('y', 100)),
- (siz.get('width', 640), siz.get('height', 480)))
- settings['format'] = json_data.get('format', 'UDZO')
- settings['compression_level'] = json_data.get('compression-level', None)
- settings['license'] = json_data.get('license', None)
- files = []
- symlinks = {}
- icon_locations = {}
- for fileinfo in json_data.get('contents', []):
- if 'path' not in fileinfo:
- raise ValueError('missing \'path\' in contents in JSON settings file')
- if 'x' not in fileinfo:
- raise ValueError('missing \'x\' in contents in JSON settings file')
- if 'y' not in fileinfo:
- raise ValueError('missing \'y\' in contents in JSON settings file')
-
- kind = fileinfo.get('type', 'file')
- path = fileinfo['path']
- name = fileinfo.get('name', os.path.basename(path.rstrip('/')))
- if kind == 'file':
- files.append((path, name))
- elif kind == 'link':
- symlinks[name] = path
- elif kind == 'position':
- pass
- icon_locations[name] = (fileinfo['x'], fileinfo['y'])
-
- settings['files'] = files
- settings['symlinks'] = symlinks
- settings['icon_locations'] = icon_locations
-
-def build_dmg(filename, volume_name, settings_file=None, settings={},
- defines={}, lookForHiDPI=True):
- options = {
- # Default settings
- 'filename': filename,
- 'volume_name': volume_name,
- 'format': 'UDBZ',
- 'compression_level': None,
- 'size': None,
- 'files': [],
- 'symlinks': {},
- 'icon': None,
- 'badge_icon': None,
- 'background': None,
- 'show_status_bar': False,
- 'show_tab_view': False,
- 'show_toolbar': False,
- 'show_pathbar': False,
- 'show_sidebar': False,
- 'sidebar_width': 180,
- 'arrange_by': None,
- 'grid_offset': (0, 0),
- 'grid_spacing': 100.0,
- 'scroll_position': (0.0, 0.0),
- 'show_icon_preview': False,
- 'show_item_info': False,
- 'label_pos': 'bottom',
- 'text_size': 16.0,
- 'icon_size': 128.0,
- 'include_icon_view_settings': 'auto',
- 'include_list_view_settings': 'auto',
- 'list_icon_size': 16.0,
- 'list_text_size': 12.0,
- 'list_scroll_position': (0, 0),
- 'list_sort_by': 'name',
- 'list_use_relative_dates': True,
- 'list_calculate_all_sizes': False,
- 'list_columns': ('name', 'date-modified', 'size', 'kind', 'date-added'),
- 'list_column_widths': {
- 'name': 300,
- 'date-modified': 181,
- 'date-created': 181,
- 'date-added': 181,
- 'date-last-opened': 181,
- 'size': 97,
- 'kind': 115,
- 'label': 100,
- 'version': 75,
- 'comments': 300,
- },
- 'list_column_sort_directions': {
- 'name': 'ascending',
- 'date-modified': 'descending',
- 'date-created': 'descending',
- 'date-added': 'descending',
- 'date-last-opened': 'descending',
- 'size': 'descending',
- 'kind': 'ascending',
- 'label': 'ascending',
- 'version': 'ascending',
- 'comments': 'ascending',
- },
- 'window_rect': ((100, 100), (640, 280)),
- 'default_view': 'icon-view',
- 'icon_locations': {},
- 'license': None,
- 'defines': defines
- }
-
- # Execute the settings file
- if settings_file:
- # We now support JSON settings files using appdmg's format
- if settings_file.endswith('.json'):
- load_json(settings_file, options)
- else:
- load_settings(settings_file, options)
-
- # Add any overrides
- options.update(settings)
-
- # Set up the finder data
- bounds = options['window_rect']
-
- bounds_string = '{{%s, %s}, {%s, %s}}' % (bounds[0][0],
- bounds[0][1],
- bounds[1][0],
- bounds[1][1])
- bwsp = {
- 'ShowStatusBar': options['show_status_bar'],
- 'WindowBounds': bounds_string,
- 'ContainerShowSidebar': False,
- 'PreviewPaneVisibility': False,
- 'SidebarWidth': options['sidebar_width'],
- 'ShowTabView': options['show_tab_view'],
- 'ShowToolbar': options['show_toolbar'],
- 'ShowPathbar': options['show_pathbar'],
- 'ShowSidebar': options['show_sidebar']
- }
-
- arrange_options = {
- 'name': 'name',
- 'date-modified': 'dateModified',
- 'date-created': 'dateCreated',
- 'date-added': 'dateAdded',
- 'date-last-opened': 'dateLastOpened',
- 'size': 'size',
- 'kind': 'kind',
- 'label': 'label',
- }
-
- icvp = {
- 'viewOptionsVersion': 1,
- 'backgroundType': 0,
- 'backgroundColorRed': 1.0,
- 'backgroundColorGreen': 1.0,
- 'backgroundColorBlue': 1.0,
- 'gridOffsetX': float(options['grid_offset'][0]),
- 'gridOffsetY': float(options['grid_offset'][1]),
- 'gridSpacing': float(options['grid_spacing']),
- 'arrangeBy': str(arrange_options.get(options['arrange_by'], 'none')),
- 'showIconPreview': options['show_icon_preview'] == True,
- 'showItemInfo': options['show_item_info'] == True,
- 'labelOnBottom': options['label_pos'] == 'bottom',
- 'textSize': float(options['text_size']),
- 'iconSize': float(options['icon_size']),
- 'scrollPositionX': float(options['scroll_position'][0]),
- 'scrollPositionY': float(options['scroll_position'][1])
- }
-
- background = options['background']
-
- columns = {
- 'name': 'name',
- 'date-modified': 'dateModified',
- 'date-created': 'dateCreated',
- 'date-added': 'dateAdded',
- 'date-last-opened': 'dateLastOpened',
- 'size': 'size',
- 'kind': 'kind',
- 'label': 'label',
- 'version': 'version',
- 'comments': 'comments'
- }
-
- default_widths = {
- 'name': 300,
- 'date-modified': 181,
- 'date-created': 181,
- 'date-added': 181,
- 'date-last-opened': 181,
- 'size': 97,
- 'kind': 115,
- 'label': 100,
- 'version': 75,
- 'comments': 300,
- }
-
- default_sort_directions = {
- 'name': 'ascending',
- 'date-modified': 'descending',
- 'date-created': 'descending',
- 'date-added': 'descending',
- 'date-last-opened': 'descending',
- 'size': 'descending',
- 'kind': 'ascending',
- 'label': 'ascending',
- 'version': 'ascending',
- 'comments': 'ascending',
- }
-
- lsvp = {
- 'viewOptionsVersion': 1,
- 'sortColumn': columns.get(options['list_sort_by'], 'name'),
- 'textSize': float(options['list_text_size']),
- 'iconSize': float(options['list_icon_size']),
- 'showIconPreview': options['show_icon_preview'],
- 'scrollPositionX': options['list_scroll_position'][0],
- 'scrollPositionY': options['list_scroll_position'][1],
- 'useRelativeDates': options['list_use_relative_dates'],
- 'calculateAllSizes': options['list_calculate_all_sizes'],
- }
-
- lsvp['columns'] = {}
- cndx = {}
-
- for n, column in enumerate(options['list_columns']):
- cndx[column] = n
- width = options['list_column_widths'].get(column,
- default_widths[column])
- asc = 'ascending' == options['list_column_sort_directions'].get(column,
- default_sort_directions[column])
-
- lsvp['columns'][columns[column]] = {
- 'index': n,
- 'width': width,
- 'identifier': columns[column],
- 'visible': True,
- 'ascending': asc
- }
-
- n = len(options['list_columns'])
- for k in iterkeys(columns):
- if cndx.get(k, None) is None:
- cndx[k] = n
- width = default_widths[k]
- asc = 'ascending' == default_sort_directions[k]
-
- lsvp['columns'][columns[column]] = {
- 'index': n,
- 'width': width,
- 'identifier': columns[column],
- 'visible': False,
- 'ascending': asc
- }
-
- n += 1
-
- default_view = options['default_view']
- views = {
- 'icon-view': b'icnv',
- 'column-view': b'clmv',
- 'list-view': b'Nlsv',
- 'coverflow': b'Flwv'
- }
-
- icvl = (b'type', views.get(default_view, 'icnv'))
-
- include_icon_view_settings = default_view == 'icon-view' \
- or options['include_icon_view_settings'] not in \
- ('auto', 'no', 0, False, None)
- include_list_view_settings = default_view in ('list-view', 'coverflow') \
- or options['include_list_view_settings'] not in \
- ('auto', 'no', 0, False, None)
-
- filename = options['filename']
- volume_name = options['volume_name']
-
- # Construct a writeable image to start with
- dirname, basename = os.path.split(os.path.realpath(filename))
- if not basename.endswith('.dmg'):
- basename += '.dmg'
- writableFile = tempfile.NamedTemporaryFile(dir=dirname, prefix='.temp',
- suffix=basename)
-
- total_size = options['size']
- if total_size == None:
- # Start with a size of 128MB - this way we don't need to calculate the
- # size of the background image, volume icon, and .DS_Store file (and
- # 128 MB should be well sufficient for even the most outlandish image
- # sizes, like an uncompressed 5K multi-resolution TIFF)
- total_size = 128 * 1024 * 1024
-
- def roundup(x, n):
- return x if x % n == 0 else x + n - x % n
-
- for path in options['files']:
- if isinstance(path, tuple):
- path = path[0]
-
- if not os.path.islink(path) and os.path.isdir(path):
- for dirpath, dirnames, filenames in os.walk(path):
- for f in filenames:
- fp = os.path.join(dirpath, f)
- total_size += roundup(os.lstat(fp).st_size, 4096)
- else:
- total_size += roundup(os.lstat(path).st_size, 4096)
-
- for name,target in iteritems(options['symlinks']):
- total_size += 4096
-
- total_size = str(max(total_size / 1024, 1024)) + 'K'
-
- ret, output = hdiutil('create',
- '-ov',
- '-volname', volume_name,
- '-fs', 'HFS+',
- '-fsargs', '-c c=64,a=16,e=16',
- '-size', total_size,
- writableFile.name)
-
- if ret:
- raise DMGError('Unable to create disk image')
-
- ret, output = hdiutil('attach',
- '-nobrowse',
- '-owners', 'off',
- '-noidme',
- writableFile.name)
-
- if ret:
- raise DMGError('Unable to attach disk image')
-
- try:
- for info in output['system-entities']:
- if info.get('mount-point', None):
- device = info['dev-entry']
- mount_point = info['mount-point']
-
- icon = options['icon']
- if badge:
- badge_icon = options['badge_icon']
- else:
- badge_icon = None
- icon_target_path = os.path.join(mount_point, '.VolumeIcon.icns')
- if icon:
- shutil.copyfile(icon, icon_target_path)
- elif badge_icon:
- badge.badge_disk_icon(badge_icon, icon_target_path)
-
- if icon or badge_icon:
- subprocess.call(['/usr/bin/SetFile', '-a', 'C', mount_point])
-
- background_bmk = None
-
- if not isinstance(background, (str, unicode)):
- pass
- elif colors.isAColor(background):
- c = colors.parseColor(background).to_rgb()
-
- icvp['backgroundType'] = 1
- icvp['backgroundColorRed'] = float(c.r)
- icvp['backgroundColorGreen'] = float(c.g)
- icvp['backgroundColorBlue'] = float(c.b)
- else:
- if os.path.isfile(background):
- # look to see if there are HiDPI resources available
-
- if lookForHiDPI is True:
- name, extension = os.path.splitext(os.path.basename(background))
- orderedImages = [background]
- imageDirectory = os.path.dirname(background)
- if imageDirectory == '':
- imageDirectory = '.'
- for candidateName in os.listdir(imageDirectory):
- hasScale = re.match(
- '^(?P<name>.+)@(?P<scale>\d+)x(?P<extension>\.\w+)$',
- candidateName)
- if hasScale and name == hasScale.group('name') and \
- extension == hasScale.group('extension'):
- scale = int(hasScale.group('scale'))
- if len(orderedImages) < scale:
- orderedImages += [None] * (scale - len(orderedImages))
- orderedImages[scale - 1] = os.path.join(imageDirectory, candidateName)
-
- if len(orderedImages) > 1:
- # compile the grouped tiff
- backgroundFile = tempfile.NamedTemporaryFile(suffix='.tiff')
- background = backgroundFile.name
- output = tempfile.TemporaryFile(mode='w+')
- try:
- subprocess.check_call(
- ['/usr/bin/tiffutil', '-cathidpicheck'] +
- list(filter(None, orderedImages)) +
- ['-out', background], stdout=output, stderr=output)
- except Exception as e:
- output.seek(0)
- raise ValueError(
- 'unable to compile combined HiDPI file "%s" got error: %s\noutput: %s'
- % (background, str(e), output.read()))
-
- _, kind = os.path.splitext(background)
- path_in_image = os.path.join(mount_point, '.background' + kind)
- shutil.copyfile(background, path_in_image)
- elif pkg_resources.resource_exists('dmgbuild', 'resources/' + background + '.tiff'):
- tiffdata = pkg_resources.resource_string(
- 'dmgbuild',
- 'resources/' + background + '.tiff')
- path_in_image = os.path.join(mount_point, '.background.tiff')
-
- with open(path_in_image, 'wb') as f:
- f.write(tiffdata)
- else:
- raise ValueError('background file "%s" not found' % background)
-
- alias = Alias.for_file(path_in_image)
- background_bmk = Bookmark.for_file(path_in_image)
-
- icvp['backgroundType'] = 2
- icvp['backgroundImageAlias'] = biplist.Data(alias.to_bytes())
-
- for f in options['files']:
- if isinstance(f, tuple):
- f_in_image = os.path.join(mount_point, f[1])
- f = f[0]
- else:
- basename = os.path.basename(f.rstrip('/'))
- f_in_image = os.path.join(mount_point, basename)
-
- # use system ditto command to preserve code signing, etc.
- subprocess.call(['/usr/bin/ditto', f, f_in_image])
-
- for name,target in iteritems(options['symlinks']):
- name_in_image = os.path.join(mount_point, name)
- os.symlink(target, name_in_image)
-
- userfn = options.get('create_hook', None)
- if callable(userfn):
- userfn(mount_point, options)
-
- image_dsstore = os.path.join(mount_point, '.DS_Store')
-
- with DSStore.open(image_dsstore, 'w+') as d:
- d['.']['vSrn'] = ('long', 1)
- d['.']['bwsp'] = bwsp
- if include_icon_view_settings:
- d['.']['icvp'] = icvp
- if background_bmk:
- d['.']['pBBk'] = background_bmk
- if include_list_view_settings:
- d['.']['lsvp'] = lsvp
- d['.']['icvl'] = icvl
-
- for k,v in iteritems(options['icon_locations']):
- d[k]['Iloc'] = v
-
- # Delete .Trashes, if it gets created
- shutil.rmtree(os.path.join(mount_point, '.Trashes'), True)
- except:
- # Always try to detach
- hdiutil('detach', '-force', device, plist=False)
- raise
-
- ret, output = hdiutil('detach', device, plist=False)
-
- if ret:
- hdiutil('detach', '-force', device, plist=False)
- raise DMGError('Unable to detach device cleanly')
-
- # Shrink the output to the minimum possible size
- ret, output = hdiutil('resize',
- '-quiet',
- '-sectors', 'min',
- writableFile.name,
- plist=False)
-
- if ret:
- raise DMGError('Unable to shrink')
-
- key_prefix = {'UDZO': 'zlib', 'UDBZ': 'bzip2', 'ULFO': 'lzfse'}
- compression_level = options['compression_level']
- if options['format'] in key_prefix and compression_level:
- compression_args = [
- '-imagekey',
- key_prefix[options['format']] + '-level=' + str(compression_level)
- ]
- else:
- compression_args = []
-
- ret, output = hdiutil('convert', writableFile.name,
- '-format', options['format'],
- '-ov',
- '-o', filename, *compression_args)
-
- if ret:
- raise DMGError('Unable to convert')
-
- if options['license']:
- ret, output = hdiutil('unflatten', '-quiet', filename, plist=False)
-
- if ret:
- raise DMGError('Unable to unflatten to add license')
-
- licensing.add_license(filename, options['license'])
-
- ret, output = hdiutil('flatten', '-quiet', filename, plist=False)
-
- if ret:
- raise DMGError('Unable to flatten after adding license')
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/licensing.py b/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/licensing.py
deleted file mode 100644
index 5c2679096..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/licensing.py
+++ /dev/null
@@ -1,461 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import os
-import struct
-
-from .resources import *
-
-# ISO language and country codes to Macintosh Region codes (from Script.h)
-# <key> == CFLocaleCreateCanonicalLocaleIdentifierFromScriptManagerCodes(NULL,
-# kTextLanguageDontCare,
-# <value>)
-region_codes = {
- "en_US": 0,
- "fr_FR": 1,
- "en_GB": 2,
- "de_DE": 3,
- "it_IT": 4,
- "nl_NL": 5,
- "nl_BE": 6,
- "sv_SE": 7,
- "es_ES": 8,
- "da_DK": 9,
- "pt_PT": 10,
- "fr_CA": 11,
- "nb_NO": 12,
- "he_IL": 13,
- "ja_JP": 14,
- "en_AU": 15,
- "ar": 16,
- "fi_FI": 17,
- "fr_CH": 18,
- "de_CH": 19,
- "el_GR": 20,
- "is_IS": 21,
- "mt_MT": 22,
- "el_CY": 23,
- "tr_TR": 24,
- "hi_IN": 33,
- "ur_PK": 34,
- "it_CH": 36,
- "ro_RO": 39,
- "grc": 40,
- "lt_LT": 41,
- "pl_PL": 42,
- "hu_HU": 43,
- "et_EE": 44,
- "lv_LV": 45,
- "se": 46,
- "fo_FO": 47,
- "fa_IR": 48,
- "ru_RU": 49,
- "ga_IE": 50,
- "ko_KR": 51,
- "zh_CN": 52,
- "zh_TW": 53,
- "th_TH": 54,
- "cs_CZ": 56,
- "sk_SK": 57,
- "bn": 60,
- "be_BY": 61,
- "uk_UA": 62,
- "sr_RS": 65,
- "sl_SI": 66,
- "mk_MK": 67,
- "hr_HR": 68,
- "pt_BR": 71,
- "bg_BG": 72,
- "ca_ES": 73,
- "gd": 75,
- "gv": 76,
- "br": 77,
- "iu_CA": 78,
- "cy": 79,
- "ga-Latg_IE": 81,
- "en_CA": 82,
- "dz_BT": 83,
- "hy_AM": 84,
- "ka_GE": 85,
- "es_419": 86,
- "to_TO": 88,
- "fr_001": 91,
- "de_AT": 92,
- "gu_IN": 94,
- "pa": 95,
- "ur_IN": 96,
- "vi_VN": 97,
- "fr_BE": 98,
- "uz_UZ": 99,
- "en_SG": 100,
- "nn_NO": 101,
- "af_ZA": 102,
- "eo": 103,
- "mr_IN": 104,
- "bo": 105,
- "ne_NP": 106,
- "kl": 107,
- "en_IE": 108
-}
-
-# Map of region constants to script constants (from Script.h)
-# TextEncoding textEncoding;
-# GetTextEncodingFromScriptInfo(kTextScriptDontCare, kTextLanguageDontCare, <key>, &textEncoding);
-# <value> == GetTextEncodingBase(textEncoding);
-script_codes = {
- 0: 0,
- 1: 0,
- 2: 0,
- 3: 0,
- 4: 0,
- 5: 0,
- 6: 0,
- 7: 0,
- 8: 0,
- 9: 0,
- 10: 0,
- 11: 0,
- 12: 0,
- 13: 5,
- 14: 1,
- 15: 0,
- 16: 4,
- 17: 0,
- 18: 0,
- 19: 0,
- 20: 6,
- 21: 37,
- 22: 0,
- 23: 6,
- 24: 35,
- 25: 36,
- 26: 0,
- 27: 0,
- 30: 0,
- 31: 0,
- 32: 0,
- 33: 9,
- 34: 4,
- 35: 35,
- 36: 0,
- 37: 0,
- 39: 38,
- 40: 6,
- 41: 29,
- 42: 29,
- 43: 29,
- 44: 29,
- 45: 29,
- 46: 0,
- 47: 37,
- 48: 140,
- 49: 7,
- 50: 39,
- 51: 3,
- 52: 25,
- 53: 2,
- 54: 21,
- 56: 29,
- 57: 29,
- 59: 29,
- 60: 13,
- 61: 7,
- 62: 7,
- 64: 6,
- 65: 7,
- 66: 36,
- 67: 7,
- 68: 36,
- 70: 0,
- 71: 0,
- 72: 7,
- 73: 0,
- 75: 39,
- 76: 39,
- 77: 39,
- 78: 236,
- 79: 39,
- 81: 40,
- 82: 0,
- 83: 26,
- 84: 24,
- 85: 23,
- 86: 0,
- 88: 0,
- 91: 0,
- 92: 0,
- 94: 11,
- 95: 10,
- 96: 4,
- 97: 30,
- 98: 0,
- 99: 7,
- 100: 0,
- 101: 0,
- 102: 0,
- 103: 0,
- 104: 9,
- 105: 26,
- 106: 9,
- 107: 0,
- 108: 0
-}
-
-# Map of TextEncodingBase constants to Python encoder names (from TextCommon.h)
-encodings_map = {
- 0: 'mac_roman', # kTextEncodingMacRoman
- 1: 'shift_jis', # kTextEncodingMacJapanese
- 2: 'big5', # kTextEncodingMacChineseTrad
- 3: 'euc_kr', # kTextEncodingMacKorean
- 4: 'mac_arabic', # kTextEncodingMacArabic
- 6: 'mac_greek', # kTextEncodingMacGreek
- 7: 'mac_cyrillic', # kTextEncodingMacCyrillic
- 21: 'iso8859_11', # kTextEncodingMacThai
- 25: 'euc-cn', # kTextEncodingMacChineseSimp
- 29: 'mac_centeuro', # kTextEncodingMacCentralEurRoman
- 35: 'mac_turkish', # kTextEncodingMacTurkish
- 36: 'mac_croatian', # kTextEncodingMacCroatian
- 37: 'mac_iceland', # kTextEncodingMacIcelandic
- 38: 'mac_romanian', # kTextEncodingMacRomanian
- 140: 'mac_farsi' # kTextEncodingMacFarsi
-}
-
-# Standard fonts
-fonts = {
- 'New York': 2,
- 'Geneva': 3,
- 'Monaco': 4,
- 'Venice': 5,
- 'London': 6,
- 'Athens': 7,
- 'San Francisco': 8,
- 'Toronto': 9,
- 'Cairo': 11,
- 'Los Angeles': 12,
- 'Times': 20,
- 'Helvetica': 21,
- 'Courier': 22,
- 'Symbol': 23,
- 'Mobile': 24
-}
-
-# Buttons (these come from the SLAResources file which you can find in the SLA
-# SDK on developer.apple.com)
-default_buttons = {
- 0: (
- b'English',
- b'Agree',
- b'Disagree',
- b'Print',
- b'Save',
- b'If you agree with the terms of this license, press "Agree" to '
- b'install the software. If you do not agree, press "Disagree".'
- ),
-
- 3: (
- b'Deutsch',
- b'Akzeptieren',
- b'Ablehnen',
- b'Drucken',
- b'Sichern...',
- b'Klicken Sie in \xd2Akzeptieren\xd3, wenn Sie mit den Bestimmungen des Software-Lizenzvertrags einverstanden sind. Falls nicht, bitte \xd2Ablehnen\xd3 anklicken. Sie k\x9annen die Software nur installieren, wenn Sie \xd2Akzeptieren\xd3 angeklickt haben.'
- ),
-
- 8: (
- b'Espa\x96ol',
- b'Aceptar',
- b'No aceptar',
- b'Imprimir',
- b'Guardar...',
- b'Si est\x87 de acuerdo con los t\x8erminos de esta licencia, pulse "Aceptar" para instalar el software. En el supuesto de que no est\x8e de acuerdo con los t\x8erminos de esta licencia, pulse "No aceptar."'
- ),
-
- 1: (
- b'Fran\x8dais',
- b'Accepter',
- b'Refuser',
- b'Imprimer',
- b'Enregistrer...',
- b'Si vous acceptez les termes de la pr\x8esente licence, cliquez sur "Accepter" afin d\'installer le logiciel. Si vous n\'\x90tes pas d\'accord avec les termes de la licence, cliquez sur "Refuser".'
- ),
-
- 4: (
- b'Italiano',
- b'Accetto',
- b'Rifiuto',
- b'Stampa',
- b'Registra...',
- b'Se accetti le condizioni di questa licenza, fai clic su "Accetto" per installare il software. Altrimenti fai clic su "Rifiuto".'
- ),
-
- 14: (
- b'Japanese',
- b'\x93\xaf\x88\xd3\x82\xb5\x82\xdc\x82\xb7',
- b'\x93\xaf\x88\xd3\x82\xb5\x82\xdc\x82\xb9\x82\xf1',
- b'\x88\xf3\x8d\xfc\x82\xb7\x82\xe9',
- b'\x95\xdb\x91\xb6...',
- b'\x96{\x83\\\x83t\x83g\x83E\x83G\x83A\x8eg\x97p\x8b\x96\x91\xf8\x8c_\x96\xf1\x82\xcc\x8f\xf0\x8c\x8f\x82\xc9\x93\xaf\x88\xd3\x82\xb3\x82\xea\x82\xe9\x8f\xea\x8d\x87\x82\xc9\x82\xcd\x81A\x83\\\x83t\x83g\x83E\x83G\x83A\x82\xf0\x83C\x83\x93\x83X\x83g\x81[\x83\x8b\x82\xb7\x82\xe9\x82\xbd\x82\xdf\x82\xc9\x81u\x93\xaf\x88\xd3\x82\xb5\x82\xdc\x82\xb7\x81v\x82\xf0\x89\x9f\x82\xb5\x82\xc4\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81B\x81@\x93\xaf\x88\xd3\x82\xb3\x82\xea\x82\xc8\x82\xa2\x8f\xea\x8d\x87\x82\xc9\x82\xcd\x81A\x81u\x93\xaf\x88\xd3\x82\xb5\x82\xdc\x82\xb9\x82\xf1\x81v\x82\xf0\x89\x9f\x82\xb5\x82\xc4\x82\xad\x82\xbe\x82\xb3\x82\xa2\x81B'
- ),
-
- 5: (
- b'Nederlands',
- b'Ja',
- b'Nee',
- b'Print',
- b'Bewaar...',
- b'Indien u akkoord gaat met de voorwaarden van deze licentie, kunt u op \'Ja\' klikken om de programmatuur te installeren. Indien u niet akkoord gaat, klikt u op \'Nee\'.'
- ),
-
- 7: (
- b'Svensk',
- b'Godk\x8anns',
- b'Avb\x9ajs',
- b'Skriv ut',
- b'Spara...',
- b'Om Du godk\x8anner licensvillkoren klicka p\x8c "Godk\x8anns" f\x9ar att installera programprodukten. Om Du inte godk\x8anner licensvillkoren, klicka p\x8c "Avb\x9ajs".'
- ),
-
- 71: (
- b'Portugu\x90s',
- b'Concordar',
- b'Discordar',
- b'Imprimir',
- b'Salvar...',
- b'Se est\x87 de acordo com os termos desta licen\x8da, pressione "Concordar" para instalar o software. Se n\x8bo est\x87 de acordo, pressione "Discordar".'
- ),
-
- 52: (
- b'Simplified Chinese',
- b'\xcd\xac\xd2\xe2',
- b'\xb2\xbb\xcd\xac\xd2\xe2',
- b'\xb4\xf2\xd3\xa1',
- b'\xb4\xe6\xb4\xa2\xa1\xad',
- b'\xc8\xe7\xb9\xfb\xc4\xfa\xcd\xac\xd2\xe2\xb1\xbe\xd0\xed\xbf\xc9\xd0\xad\xd2\xe9\xb5\xc4\xcc\xf5\xbf\xee\xa3\xac\xc7\xeb\xb0\xb4\xa1\xb0\xcd\xac\xd2\xe2\xa1\xb1\xc0\xb4\xb0\xb2\xd7\xb0\xb4\xcb\xc8\xed\xbc\xfe\xa1\xa3\xc8\xe7\xb9\xfb\xc4\xfa\xb2\xbb\xcd\xac\xd2\xe2\xa3\xac\xc7\xeb\xb0\xb4\xa1\xb0\xb2\xbb\xcd\xac\xd2\xe2\xa1\xb1\xa1\xa3'
- ),
-
- 53: (
- b'Traditional Chinese',
- b'\xa6P\xb7N',
- b'\xa4\xa3\xa6P\xb7N',
- b'\xa6C\xa6L',
- b'\xc0x\xa6s\xa1K',
- b'\xa6p\xaaG\xb1z\xa6P\xb7N\xa5\xbb\xb3\\\xa5i\xc3\xd2\xb8\xcc\xaa\xba\xb1\xf8\xb4\xda\xa1A\xbd\xd0\xab\xf6\xa1\xa7\xa6P\xb7N\xa1\xa8\xa5H\xa6w\xb8\xcb\xb3n\xc5\xe9\xa1C\xa6p\xaaG\xa4\xa3\xa6P\xb7N\xa1A\xbd\xd0\xab\xf6\xa1\xa7\xa4\xa3\xa6P\xb7N\xa1\xa8\xa1C'
- ),
-
- 9: (
- b'Dansk',
- b'Enig',
- b'Uenig',
- b'Udskriv',
- b'Arkiver...',
- b'Hvis du accepterer betingelserne i licensaftalen, skal du klikke p\x8c \xd2Enig\xd3 for at installere softwaren. Klik p\x8c \xd2Uenig\xd3 for at annullere installeringen.'
- ),
-
- 17: (
- b'Suomi',
- b'Hyv\x8aksyn',
- b'En hyv\x8aksy',
- b'Tulosta',
- b'Tallenna\xc9',
- b'Hyv\x8aksy lisenssisopimuksen ehdot osoittamalla \xd5Hyv\x8aksy\xd5. Jos et hyv\x8aksy sopimuksen ehtoja, osoita \xd5En hyv\x8aksy\xd5.'
- ),
-
- 51: (
- b'Korean',
- b'\xb5\xbf\xc0\xc7',
- b'\xb5\xbf\xc0\xc7 \xbe\xc8\xc7\xd4',
- b'\xc7\xc1\xb8\xb0\xc6\xae',
- b'\xc0\xfa\xc0\xe5...',
- b'\xbb\xe7\xbf\xeb \xb0\xe8\xbe\xe0\xbc\xad\xc0\xc7 \xb3\xbb\xbf\xeb\xbf\xa1 \xb5\xbf\xc0\xc7\xc7\xcf\xb8\xe9, "\xb5\xbf\xc0\xc7" \xb4\xdc\xc3\xdf\xb8\xa6 \xb4\xad\xb7\xaf \xbc\xd2\xc7\xc1\xc6\xae\xbf\xfe\xbe\xee\xb8\xa6 \xbc\xb3\xc4\xa1\xc7\xcf\xbd\xca\xbd\xc3\xbf\xc0. \xb5\xbf\xc0\xc7\xc7\xcf\xc1\xf6 \xbe\xca\xb4\xc2\xb4\xd9\xb8\xe9, "\xb5\xbf\xc0\xc7 \xbe\xc8\xc7\xd4" \xb4\xdc\xc3\xdf\xb8\xa6 \xb4\xa9\xb8\xa3\xbd\xca\xbd\xc3\xbf\xc0.'
- ),
-
- 12: (
- b'Norsk',
- b'Enig',
- b'Ikke enig',
- b'Skriv ut',
- b'Arkiver...',
- b'Hvis De er enig i bestemmelsene i denne lisensavtalen, klikker De p\x8c "Enig"-knappen for \x8c installere programvaren. Hvis De ikke er enig, klikker De p\x8c "Ikke enig".'
- ),
-}
-
-class LPicResource (Resource):
- def __init__(self, res_id, res_name, default_lang, lpic, res_attrs=0):
- data = []
- data.append(struct.pack(b'>HH', default_lang, len(lpic)))
- for lang,rid,two_byte in lpic:
- data.append(struct.pack(b'>HHH', lang, rid, int(two_byte)))
- super(LPicResource, self).__init__(b'LPic', res_id, res_name,
- b''.join(data), res_attrs)
-
-def get_encoder_name(locale):
- if locale not in region_codes:
- raise Exception("Cannot determine region code for locale '%s'" % locale)
- region_code = region_codes[locale]
-
- if region_code not in script_codes:
- raise Exception("Cannot determine script code for locale '%s'" % locale)
- script_code = script_codes[region_code]
-
- if script_code not in encodings_map:
- raise Exception("Cannot determine Python encoder name for locale '%s' - "
- "encode the string data manually as a byte array instead" % locale)
- return encodings_map[script_code]
-
-def maybe_encode(s, encoding):
- if isinstance(s, bytes):
- return s
- return s.encode(encoding)
-
-def add_license(filename, license_info):
- """Add a license agreement to the specified disk image file, which should
- have been unflattened first."""
-
- fork = ResourceFork.from_file(filename)
-
- default_lang = license_info.get('default-language', 'en_US')
- default_lang_id = region_codes.get(default_lang, 0)
-
- lpic = []
- ndx = 1
- for language,license_data in license_info['licenses'].items():
- if language not in region_codes:
- raise Exception("Unknown language '" + language + "'. Valid languages are: " +
- ", ".join(sorted(region_codes.keys())))
- encoding_name = get_encoder_name(language)
- lang_id = region_codes[language]
-
- is_two_byte = lang_id in (14, 51, 52, 53) # Japanese, Korean, SimpChinese, TradChinese
-
- if os.path.isfile(license_data):
- with open(license_data) as f:
- license_data = f.read()
-
- if license_data.startswith('{\\rtf1'):
- fork.add(Resource(b'RTF ', 5000 + ndx, language + ' SLA',
- str(license_data)))
- else:
- fork.add(TextResource(5000 + ndx, language + ' SLA',
- maybe_encode(license_data, encoding_name)))
- fork.add(StyleResource(5000 + ndx, language + ' SLA',
- [Style(0, 12, 9, Style.Helvetica,
- 0, 0, (0, 0, 0))]))
-
- buttons = license_info.get('buttons', {}).get(language, None)
- if buttons is None:
- buttons = default_buttons.get(lang_id, None)
- if buttons is None:
- buttons = default_buttons[0]
-
- buttons = [maybe_encode(b, encoding_name) for b in buttons]
-
- fork.add(StringListResource(5000 + ndx, language + ' Buttons',
- buttons))
-
- lpic.append((lang_id, ndx, is_two_byte))
-
- ndx += 1
-
- fork.add(LPicResource(5000, None, default_lang_id, lpic))
-
- fork.write_to_file(filename)
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/resources.py b/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/resources.py
deleted file mode 100644
index d2f58e64a..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/resources.py
+++ /dev/null
@@ -1,355 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import struct
-
-class Resource (object):
- def __init__(self, res_type, res_id, res_name, data=None, res_attrs=0):
- self.res_type = str(res_type)
- self.res_id = res_id
- if isinstance(res_name, basestring):
- res_name = str(res_name)
- self.res_name = res_name
- self.res_attrs = res_attrs
- if data is None:
- self.data = None
- self.data = str(data)
-
- self.data_offset = None
- self.name_offset = None
-
- def __repr__(self):
- return 'Resource(%r, %r, %r, data=%r, res_attrs=%r)' % (self.res_type,
- self.res_id,
- self.res_name,
- self.data,
- self.res_attrs)
-
-class TMPLResource (Resource):
- def __init__(self, res_id, res_name, tmpl, res_attrs=0):
- data = []
- for name,typecode in tmpl:
- data.append(struct.pack(b'B', len(name)))
- data.append(str(name))
- data.append(str(typecode))
- super(TMPLResource, self).__init__(b'TMPL', res_id, res_name,
- b''.join(data), res_attrs)
-
-class StringListResource (Resource):
- def __init__(self, res_id, res_name, strings, res_attrs=0):
- data = []
- data.append(struct.pack(b'>H', len(strings)))
- for s in strings:
- data.append(struct.pack(b'B', len(s)))
- data.append(str(s))
- super(StringListResource, self).__init__(b'STR#', res_id, res_name,
- b''.join(data), res_attrs)
-
-class TextResource (Resource):
- def __init__(self, res_id, res_name, string, res_attrs=0):
- super(TextResource, self).__init__(b'TEXT', res_id, res_name,
- str(string), res_attrs)
-
-class Style (object):
- # Fonts
- NewYork = 2
- Geneva = 3
- Monaco = 4
- Venice = 5
- London = 6
- Athens = 7
- SanFrancisco = 8
- Toronto = 9
- Cairo = 11
- LosAngeles = 12
- Times = 20
- Helvetica = 21
- Courier = 22
- Symbol = 23
- Mobile = 24
-
- # Styles
- Bold = 0x0100
- Italic = 0x0200
- Underline = 0x0400
- Outline = 0x0800
- Shadow = 0x1000
- Condense = 0x2000
- Expand = 0x4000
-
- def __init__(self, start_character, height, ascent, font_id, face,
- size, color):
- self.start_character = start_character
- self.height = height
- self.ascent = ascent
- self.font_id = font_id
- self.face = face
- self.size = size
- self.color = color
-
- def __repr__(self):
- styles = []
- if self.face & Style.Bold:
- styles.append('Style.Bold')
- if self.face & Style.Italic:
- styles.append('Style.Italic')
- if self.face & Style.Underline:
- styles.append('Style.Underline')
- if self.face & Style.Outline:
- styles.append('Style.Outline')
- if self.face & Style.Shadow:
- styles.append('Style.Shadow')
- if self.face & Style.Condense:
- styles.append('Style.Condense')
- if self.face & Style.Expand:
- styles.append('Style.Expand')
- if self.face & ~0x4f00:
- styles.append('%#06x' % (self.face & ~0x4f00))
- if styles:
- styles = '|'.join(styles)
- else:
- styles = '0'
-
- font_revmap = {
- 2: 'Style.NewYork',
- 3: 'Style.Geneva',
- 4: 'Style.Monaco',
- 5: 'Style.Venice',
- 6: 'Style.London',
- 7: 'Style.Athens',
- 8: 'Style.SanFrancisco',
- 9: 'Style.Toronto',
- 11: 'Style.Cairo',
- 12: 'Style.LosAngeles',
- 20: 'Style.Times',
- 21: 'Style.Helvetica',
- 22: 'Style.Courier',
- 23: 'Style.Symbol',
- 24: 'Style.Mobile'
- }
-
- font = font_revmap.get(self.font_id, '%s' % self.font_id)
-
- return 'Style(%r, %r, %r, %s, %s, %r, %r)' % (
- self.start_character,
- self.height,
- self.ascent,
- font,
- styles,
- self.size,
- self.color)
-
-class StyleResource (Resource):
- def __init__(self, res_id, res_name, styles, res_attrs=0):
- data = []
- data.append(struct.pack(b'>H', len(styles)))
- for style in styles:
- data.append(struct.pack(b'>LHHHHHHHH',
- style.start_character,
- style.height,
- style.ascent,
- style.font_id,
- style.face,
- style.size,
- style.color[0],
- style.color[1],
- style.color[2]))
- super(StyleResource, self).__init__(b'styl', res_id, res_name,
- b''.join(data), res_attrs)
-
-class ResourceFork (object):
- def __init__(self, resources=None):
- self.types = {}
- self.attrs = 0
- if resources is not None:
- for res in resources:
- self.add(res)
-
- @classmethod
- def from_data(clss, data):
- if len(data) < 16:
- raise ValueError('Bad resource data - data too short')
-
- # Read the header
- data_start, map_start, data_len, map_len = struct.unpack(b'>LLLL',
- data[0:16])
-
- if data_start + data_len > len(data):
- raise ValueError('Bad resource data - data out of range')
- if map_start + map_len > len(data):
- raise ValueError('Bad resource data - map out of range')
- if map_len < 30:
- raise ValueError('Bad resource data - map too short')
-
- # Read the map header
- fork_attrs, type_offset, name_offset, max_type_ndx \
- = struct.unpack(b'>HHHH', data[map_start + 22:map_start + 30])
- num_types = max_type_ndx + 1
-
- if type_offset + 8 * num_types > map_len:
- raise ValueError('Bad resource data - type data outside map')
-
- if name_offset > map_len:
- raise ValueError('Bad resource data - names outside map')
-
- type_offset += map_start
- name_offset += map_start
-
- result = ResourceFork()
-
- # Now read the type list
- for ntype in range(0, num_types):
- type_pos = 2 + type_offset + 8 * ntype
- res_type, max_item_ndx, ref_offset \
- = struct.unpack(b'>4sHH', data[type_pos:type_pos+8])
- num_items = max_item_ndx + 1
-
- result.types[res_type] = []
-
- ref_list_offset = type_offset + ref_offset
- if ref_list_offset + 12 * num_items > map_start + map_len:
- raise ValueError('Bad resource data - ref list outside map')
-
- for nitem in range(0, num_items):
- ref_elt = ref_list_offset + 12 * nitem
- res_id, res_name_offset, data_offset \
- = struct.unpack(b'>hHL', data[ref_elt:ref_elt+8])
-
- res_attrs = data_offset >> 24
- data_offset &= 0xffffff
-
- if data_offset >= data_len:
- raise ValueError('Bad resource data - item data out of range')
-
- data_offset += data_start
- res_len = struct.unpack(b'>L', data[data_offset:data_offset+4])[0]
- if data_offset + res_len >= data_start + data_len:
- raise ValueError('Bad resource data - item data too large')
-
- res_data = data[data_offset + 4:data_offset + res_len + 4]
-
- if res_name_offset == 0xffff:
- res_name = None
- else:
- res_name_offset += name_offset
- if res_name_offset >= map_start + map_len:
- raise ValueError('Bad resource data - name out of range')
- res_name_len = struct.unpack(b'B', data[res_name_offset])[0]
- res_name = data[res_name_offset + 1:res_name_offset + res_name_len + 1]
-
- result.types[res_type].append(Resource(res_type, res_id,
- res_name,
- res_data, res_attrs))
-
- return result
-
- @classmethod
- def from_file(clss, filename):
- with open(filename + '/..namedfork/rsrc', 'rb') as f:
- data = f.read()
- return clss.from_data(data)
-
- def to_data(self):
- data = []
- data_len = 0
- names = []
- names_len = 0
- types_len = len(self.types) * 8
- types_data = []
- reflist_data = []
- reflist_len = 0
-
- for res_type, items in self.types.items():
- types_data.append(struct.pack(b'>4sHH',
- res_type,
- len(items) - 1,
- 2 + types_len + reflist_len))
- for item in items:
- data_offset = data_len
-
- if item.res_name is None:
- name_offset = 65535
- else:
- name_offset = names_len
- n = str(item.res_name)
- names.append(struct.pack(b'B', len(n)) + n)
- names_len += 1 + len(n)
-
- if item.data is None:
- data_len += 4
- else:
- data_len += 4 + (len(item.data) + 3) & ~3
-
- reflist_len += 12
- reflist_data.append(struct.pack(b'>hHLL',
- item.res_id,
- name_offset,
- (item.res_attrs << 24) \
- | data_offset,
- 0))
-
- # Header
- data.append(struct.pack(b'>LLLL240s', 256, 256 + data_len, data_len,
- 30 + types_len + reflist_len + names_len,
- b''))
-
- # Resource data
- for res_type, items in self.types.items():
- for item in items:
- if item.data is None:
- dlen = 0
- else:
- dlen = len(item.data)
- plen = (dlen + 3) & ~3
- data.append(struct.pack(b'>L', dlen))
- if item.data is not None:
- data.append(item.data)
- if plen != dlen:
- data.append(b'\0' * (plen - dlen))
-
- # Resource map header
- data.append(struct.pack(b'>16sLHHHHH',
- b'', 0, 0,
- self.attrs, 28, 30 + types_len + reflist_len,
- len(self.types) - 1))
-
- # Type list
- data.append(b''.join(types_data))
-
- # Reference lists
- data.append(b''.join(reflist_data))
-
- # Name list
- data.append(b''.join(names))
-
- return b''.join(data)
-
- def write_to_file(self, filename):
- with open(filename + '/..namedfork/rsrc', 'wb') as f:
- f.write(self.to_data())
-
- def __len__(self):
- return len(self.types)
-
- def __getitem__(self, key):
- return self.types[key]
-
- def __iter__(self):
- for res_type, items in self.types.items():
- for item in items:
- yield item
-
- def __repr__(self):
- output = []
- for item in self:
- output.append(repr(item))
- return 'ResourceFork([%s])' % ', '.join(output)
-
- def add(self, res):
- if res.res_type in self.types:
- self.types[res.res_type].append(res)
- else:
- self.types[res.res_type] = [res]
-
- def remove(self, res):
- self.types[res.res_type].remove(res)
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/__init__.py b/src/3rdparty/python/lib/python2.7/site-packages/ds_store/__init__.py
deleted file mode 100644
index a6b812104..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .store import DSStore, DSStoreEntry
-
-__all__ = ['DSStore', 'DSStoreEntry']
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/buddy.py b/src/3rdparty/python/lib/python2.7/site-packages/ds_store/buddy.py
deleted file mode 100644
index 320768cd3..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/buddy.py
+++ /dev/null
@@ -1,478 +0,0 @@
-# -*- coding: utf-8 -*-
-import os
-import bisect
-import struct
-import binascii
-
-try:
- {}.iterkeys
- iterkeys = lambda x: x.iterkeys()
-except AttributeError:
- iterkeys = lambda x: x.keys()
-try:
- unicode
-except NameError:
- unicode = str
-
-class BuddyError(Exception):
- pass
-
-class Block(object):
- def __init__(self, allocator, offset, size):
- self._allocator = allocator
- self._offset = offset
- self._size = size
- self._value = bytearray(allocator.read(offset, size))
- self._pos = 0
- self._dirty = False
-
- def __len__(self):
- return self._size
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.close()
-
- def close(self):
- if self._dirty:
- self.flush()
-
- def flush(self):
- if self._dirty:
- self._dirty = False
- self._allocator.write(self._offset, self._value)
-
- def invalidate(self):
- self._dirty = False
-
- def zero_fill(self):
- len = self._size - self._pos
- zeroes = b'\0' * len
- self._value[self._pos:self._size] = zeroes
- self._dirty = True
-
- def tell(self):
- return self._pos
-
- def seek(self, pos, whence=os.SEEK_SET):
- if whence == os.SEEK_CUR:
- pos += self._pos
- elif whence == os.SEEK_END:
- pos = self._size - pos
-
- if pos < 0 or pos > self._size:
- raise ValueError('Seek out of range in Block instance')
-
- self._pos = pos
-
- def read(self, size_or_format):
- if isinstance(size_or_format, (str, unicode, bytes)):
- size = struct.calcsize(size_or_format)
- fmt = size_or_format
- else:
- size = size_or_format
- fmt = None
-
- if self._size - self._pos < size:
- raise BuddyError('Unable to read %lu bytes in block' % size)
-
- data = self._value[self._pos:self._pos + size]
- self._pos += size
-
- if fmt is not None:
- if isinstance(data, bytearray):
- return struct.unpack_from(fmt, bytes(data))
- else:
- return struct.unpack(fmt, data)
- else:
- return data
-
- def write(self, data_or_format, *args):
- if len(args):
- data = struct.pack(data_or_format, *args)
- else:
- data = data_or_format
-
- if self._pos + len(data) > self._size:
- raise ValueError('Attempt to write past end of Block')
-
- self._value[self._pos:self._pos + len(data)] = data
- self._pos += len(data)
-
- self._dirty = True
-
- def insert(self, data_or_format, *args):
- if len(args):
- data = struct.pack(data_or_format, *args)
- else:
- data = data_or_format
-
- del self._value[-len(data):]
- self._value[self._pos:self._pos] = data
- self._pos += len(data)
-
- self._dirty = True
-
- def delete(self, size):
- if self._pos + size > self._size:
- raise ValueError('Attempt to delete past end of Block')
- del self._value[self._pos:self._pos + size]
- self._value += b'\0' * size
- self._dirty = True
-
- def __str__(self):
- return binascii.b2a_hex(self._value)
-
-class Allocator(object):
- def __init__(self, the_file):
- self._file = the_file
- self._dirty = False
-
- self._file.seek(0)
-
- # Read the header
- magic1, magic2, offset, size, offset2, self._unknown1 \
- = self.read(-4, '>I4sIII16s')
-
- if magic2 != b'Bud1' or magic1 != 1:
- raise BuddyError('Not a buddy file')
-
- if offset != offset2:
- raise BuddyError('Root addresses differ')
-
- self._root = Block(self, offset, size)
-
- # Read the block offsets
- count, self._unknown2 = self._root.read('>II')
- self._offsets = []
- c = (count + 255) & ~255
- while c:
- self._offsets += self._root.read('>256I')
- c -= 256
- self._offsets = self._offsets[:count]
-
- # Read the TOC
- self._toc = {}
- count = self._root.read('>I')[0]
- for n in range(count):
- nlen = self._root.read('B')[0]
- name = bytes(self._root.read(nlen))
- value = self._root.read('>I')[0]
- self._toc[name] = value
-
- # Read the free lists
- self._free = []
- for n in range(32):
- count = self._root.read('>I')
- self._free.append(list(self._root.read('>%uI' % count)))
-
- @classmethod
- def open(cls, file_or_name, mode='r+'):
- if isinstance(file_or_name, (str, unicode)):
- if not 'b' in mode:
- mode = mode[:1] + 'b' + mode[1:]
- f = open(file_or_name, mode)
- else:
- f = file_or_name
-
- if 'w' in mode:
- # Create an empty file in this case
- f.truncate()
-
- # An empty root block needs 1264 bytes:
- #
- # 0 4 offset count
- # 4 4 unknown
- # 8 4 root block offset (2048)
- # 12 255 * 4 padding (offsets are in multiples of 256)
- # 1032 4 toc count (0)
- # 1036 228 free list
- # total 1264
-
- # The free list will contain the following:
- #
- # 0 5 * 4 no blocks of width less than 5
- # 20 6 * 8 1 block each of widths 5 to 10
- # 68 4 no blocks of width 11 (allocated for the root)
- # 72 19 * 8 1 block each of widths 12 to 30
- # 224 4 no blocks of width 31
- # total 228
- #
- # (The reason for this layout is that we allocate 2**5 bytes for
- # the header, which splits the initial 2GB region into every size
- # below 2**31, including *two* blocks of size 2**5, one of which
- # we take. The root block itself then needs a block of size
- # 2**11. Conveniently, each of these initial blocks will be
- # located at offset 2**n where n is its width.)
-
- # Write the header
- header = struct.pack(b'>I4sIII16s',
- 1, b'Bud1',
- 2048, 1264, 2048,
- b'\x00\x00\x10\x0c'
- b'\x00\x00\x00\x87'
- b'\x00\x00\x20\x0b'
- b'\x00\x00\x00\x00')
- f.write(header)
- f.write(b'\0' * 2016)
-
- # Write the root block
- free_list = [struct.pack(b'>5I', 0, 0, 0, 0, 0)]
- for n in range(5, 11):
- free_list.append(struct.pack(b'>II', 1, 2**n))
- free_list.append(struct.pack(b'>I', 0))
- for n in range(12, 31):
- free_list.append(struct.pack(b'>II', 1, 2**n))
- free_list.append(struct.pack(b'>I', 0))
-
- root = b''.join([struct.pack(b'>III', 1, 0, 2048 | 5),
- struct.pack(b'>I', 0) * 255,
- struct.pack(b'>I', 0)] + free_list)
- f.write(root)
-
- return Allocator(f)
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.close()
-
- def close(self):
- self.flush()
- self._file.close()
-
- def flush(self):
- if self._dirty:
- size = self._root_block_size()
- self.allocate(size, 0)
- with self.get_block(0) as rblk:
- self._write_root_block_into(rblk)
-
- addr = self._offsets[0]
- offset = addr & ~0x1f
- size = 1 << (addr & 0x1f)
-
- self._file.seek(0, os.SEEK_SET)
- self._file.write(struct.pack(b'>I4sIII16s',
- 1, b'Bud1',
- offset, size, offset,
- self._unknown1))
-
- self._dirty = False
-
- self._file.flush()
-
- def read(self, offset, size_or_format):
- """Read data at `offset', or raise an exception. `size_or_format'
- may either be a byte count, in which case we return raw data,
- or a format string for `struct.unpack', in which case we
- work out the size and unpack the data before returning it."""
- # N.B. There is a fixed offset of four bytes(!)
- self._file.seek(offset + 4, os.SEEK_SET)
-
- if isinstance(size_or_format, (str, unicode)):
- size = struct.calcsize(size_or_format)
- fmt = size_or_format
- else:
- size = size_or_format
- fmt = None
-
- ret = self._file.read(size)
- if len(ret) < size:
- ret += b'\0' * (size - len(ret))
-
- if fmt is not None:
- if isinstance(ret, bytearray):
- ret = struct.unpack_from(fmt, bytes(ret))
- else:
- ret = struct.unpack(fmt, ret)
-
- return ret
-
- def write(self, offset, data_or_format, *args):
- """Write data at `offset', or raise an exception. `data_or_format'
- may either be the data to write, or a format string for `struct.pack',
- in which case we pack the additional arguments and write the
- resulting data."""
- # N.B. There is a fixed offset of four bytes(!)
- self._file.seek(offset + 4, os.SEEK_SET)
-
- if len(args):
- data = struct.pack(data_or_format, *args)
- else:
- data = data_or_format
-
- self._file.write(data)
-
- def get_block(self, block):
- try:
- addr = self._offsets[block]
- except IndexError:
- return None
-
- offset = addr & ~0x1f
- size = 1 << (addr & 0x1f)
-
- return Block(self, offset, size)
-
- def _root_block_size(self):
- """Return the number of bytes required by the root block."""
- # Offsets
- size = 8
- size += 4 * ((len(self._offsets) + 255) & ~255)
-
- # TOC
- size += 4
- size += sum([5 + len(s) for s in self._toc])
-
- # Free list
- size += sum([4 + 4 * len(fl) for fl in self._free])
-
- return size
-
- def _write_root_block_into(self, block):
- # Offsets
- block.write('>II', len(self._offsets), self._unknown2)
- block.write('>%uI' % len(self._offsets), *self._offsets)
- extra = len(self._offsets) & 255
- if extra:
- block.write(b'\0\0\0\0' * (256 - extra))
-
- # TOC
- keys = list(self._toc.keys())
- keys.sort()
-
- block.write('>I', len(keys))
- for k in keys:
- block.write('B', len(k))
- block.write(k)
- block.write('>I', self._toc[k])
-
- # Free list
- for w, f in enumerate(self._free):
- block.write('>I', len(f))
- if len(f):
- block.write('>%uI' % len(f), *f)
-
- def _buddy(self, offset, width):
- f = self._free[width]
- b = offset ^ (1 << width)
-
- try:
- ndx = f.index(b)
- except ValueError:
- ndx = None
-
- return (f, b, ndx)
-
- def _release(self, offset, width):
- # Coalesce
- while True:
- f,b,ndx = self._buddy(offset, width)
-
- if ndx is None:
- break
-
- offset &= b
- width += 1
- del f[ndx]
-
- # Add to the list
- bisect.insort(f, offset)
-
- # Mark as dirty
- self._dirty = True
-
- def _alloc(self, width):
- w = width
- while not self._free[w]:
- w += 1
- while w > width:
- offset = self._free[w].pop(0)
- w -= 1
- self._free[w] = [offset, offset ^ (1 << w)]
- self._dirty = True
- return self._free[width].pop(0)
-
- def allocate(self, bytes, block=None):
- """Allocate or reallocate a block such that it has space for at least
- `bytes' bytes."""
- if block is None:
- # Find the first unused block
- try:
- block = self._offsets.index(0)
- except ValueError:
- block = len(self._offsets)
- self._offsets.append(0)
-
- # Compute block width
- width = max(bytes.bit_length(), 5)
-
- addr = self._offsets[block]
- offset = addr & ~0x1f
-
- if addr:
- blkwidth = addr & 0x1f
- if blkwidth == width:
- return block
- self._release(offset, width)
- self._offsets[block] = 0
-
- offset = self._alloc(width)
- self._offsets[block] = offset | width
- return block
-
- def release(self, block):
- addr = self._offsets[block]
-
- if addr:
- width = addr & 0x1f
- offset = addr & ~0x1f
- self._release(offset, width)
-
- if block == len(self._offsets):
- del self._offsets[block]
- else:
- self._offsets[block] = 0
-
- def __len__(self):
- return len(self._toc)
-
- def __getitem__(self, key):
- if not isinstance(key, (str, unicode)):
- raise TypeError('Keys must be of string type')
- if not isinstance(key, bytes):
- key = key.encode('latin_1')
- return self._toc[key]
-
- def __setitem__(self, key, value):
- if not isinstance(key, (str, unicode)):
- raise TypeError('Keys must be of string type')
- if not isinstance(key, bytes):
- key = key.encode('latin_1')
- self._toc[key] = value
- self._dirty = True
-
- def __delitem__(self, key):
- if not isinstance(key, (str, unicode)):
- raise TypeError('Keys must be of string type')
- if not isinstance(key, bytes):
- key = key.encode('latin_1')
- del self._toc[key]
- self._dirty = True
-
- def iterkeys(self):
- return iterkeys(self._toc)
-
- def keys(self):
- return iterkeys(self._toc)
-
- def __iter__(self):
- return iterkeys(self._toc)
-
- def __contains__(self, key):
- return key in self._toc
-
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/store.py b/src/3rdparty/python/lib/python2.7/site-packages/ds_store/store.py
deleted file mode 100644
index b6f805b24..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/store.py
+++ /dev/null
@@ -1,1251 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-from __future__ import print_function
-from __future__ import division
-
-import binascii
-import struct
-import biplist
-import mac_alias
-
-try:
- next
-except NameError:
- next = lambda x: x.next()
-try:
- unicode
-except NameError:
- unicode = str
-
-from . import buddy
-
-class ILocCodec(object):
- @staticmethod
- def encode(point):
- return struct.pack(b'>IIII', point[0], point[1],
- 0xffffffff, 0xffff0000)
-
- @staticmethod
- def decode(bytesData):
- if isinstance(bytesData, bytearray):
- x, y = struct.unpack_from(b'>II', bytes(bytesData[:8]))
- else:
- x, y = struct.unpack(b'>II', bytesData[:8])
- return (x, y)
-
-class PlistCodec(object):
- @staticmethod
- def encode(plist):
- return biplist.writePlistToString(plist)
-
- @staticmethod
- def decode(bytes):
- return biplist.readPlistFromString(bytes)
-
-class BookmarkCodec(object):
- @staticmethod
- def encode(bmk):
- return bmk.to_bytes()
-
- @staticmethod
- def decode(bytes):
- return mac_alias.Bookmark.from_bytes(bytes)
-
-# This list tells the code how to decode particular kinds of entry in the
-# .DS_Store file. This is really a convenience, and we currently only
-# support a tiny subset of the possible entry types.
-codecs = {
- b'Iloc': ILocCodec,
- b'bwsp': PlistCodec,
- b'lsvp': PlistCodec,
- b'lsvP': PlistCodec,
- b'icvp': PlistCodec,
- b'pBBk': BookmarkCodec
- }
-
-class DSStoreEntry(object):
- """Holds the data from an entry in a ``.DS_Store`` file. Note that this is
- not meant to represent the entry itself---i.e. if you change the type
- or value, your changes will *not* be reflected in the underlying file.
-
- If you want to make a change, you should either use the :class:`DSStore`
- object's :meth:`DSStore.insert` method (which will replace a key if it
- already exists), or the mapping access mode for :class:`DSStore` (often
- simpler anyway).
- """
- def __init__(self, filename, code, typecode, value=None):
- if str != bytes and type(filename) == bytes:
- filename = filename.decode('utf-8')
-
- if not isinstance(code, bytes):
- code = code.encode('latin_1')
-
- self.filename = filename
- self.code = code
- self.type = typecode
- self.value = value
-
- @classmethod
- def read(cls, block):
- """Read a ``.DS_Store`` entry from the containing Block"""
- # First read the filename
- nlen = block.read(b'>I')[0]
- filename = block.read(2 * nlen).decode('utf-16be')
-
- # Next, read the code and type
- code, typecode = block.read(b'>4s4s')
-
- # Finally, read the data
- if typecode == b'bool':
- value = block.read(b'>?')[0]
- elif typecode == b'long' or typecode == b'shor':
- value = block.read(b'>I')[0]
- elif typecode == b'blob':
- vlen = block.read(b'>I')[0]
- value = block.read(vlen)
-
- codec = codecs.get(code, None)
- if codec:
- value = codec.decode(value)
- typecode = codec
- elif typecode == b'ustr':
- vlen = block.read(b'>I')[0]
- value = block.read(2 * vlen).decode('utf-16be')
- elif typecode == b'type':
- value = block.read(b'>4s')[0]
- elif typecode == b'comp' or typecode == b'dutc':
- value = block.read(b'>Q')[0]
- else:
- raise ValueError('Unknown type code "%s"' % typecode)
-
- return DSStoreEntry(filename, code, typecode, value)
-
- def __lt__(self, other):
- if not isinstance(other, DSStoreEntry):
- raise TypeError('Can only compare against other DSStoreEntry objects')
- sfl = self.filename.lower()
- ofl = other.filename.lower()
- return (sfl < ofl
- or (self.filename == other.filename
- and self.code < other.code))
-
- def __le__(self, other):
- if not isinstance(other, DSStoreEntry):
- raise TypeError('Can only compare against other DSStoreEntry objects')
- sfl = self.filename.lower()
- ofl = other.filename.lower()
- return (sfl < ofl
- or (sfl == ofl
- and self.code <= other.code))
-
- def __eq__(self, other):
- if not isinstance(other, DSStoreEntry):
- raise TypeError('Can only compare against other DSStoreEntry objects')
- sfl = self.filename.lower()
- ofl = other.filename.lower()
- return (sfl == ofl
- and self.code == other.code)
-
- def __ne__(self, other):
- if not isinstance(other, DSStoreEntry):
- raise TypeError('Can only compare against other DSStoreEntry objects')
- sfl = self.filename.lower()
- ofl = other.filename.lower()
- return (sfl != ofl
- or self.code != other.code)
-
- def __gt__(self, other):
- if not isinstance(other, DSStoreEntry):
- raise TypeError('Can only compare against other DSStoreEntry objects')
- sfl = self.filename.lower()
- ofl = other.filename.lower()
-
- selfCode = self.code
- if str != bytes and type(selfCode) is bytes:
- selfCode = selfCode.decode('utf-8')
- otherCode = other.code
- if str != bytes and type(otherCode) is bytes:
- otherCode = otherCode.decode('utf-8')
-
- return (sfl > ofl or (sfl == ofl and selfCode > otherCode))
-
- def __ge__(self, other):
- if not isinstance(other, DSStoreEntry):
- raise TypeError('Can only compare against other DSStoreEntry objects')
- sfl = self.filename.lower()
- ofl = other.filename.lower()
- return (sfl > ofl
- or (sfl == ofl
- and self.code >= other.code))
-
- def __cmp__(self, other):
- if not isinstance(other, DSStoreEntry):
- raise TypeError('Can only compare against other DSStoreEntry objects')
- r = cmp(self.filename.lower(), other.filename.lower())
- if r:
- return r
- return cmp(self.code, other.code)
-
- def byte_length(self):
- """Compute the length of this entry, in bytes"""
- utf16 = self.filename.encode('utf-16be')
- l = 4 + len(utf16) + 8
-
- if isinstance(self.type, unicode):
- entry_type = self.type.encode('latin_1')
- value = self.value
- elif isinstance(self.type, (bytes, str)):
- entry_type = self.type
- value = self.value
- else:
- entry_type = b'blob'
- value = self.type.encode(self.value)
-
- if entry_type == b'bool':
- l += 1
- elif entry_type == b'long' or entry_type == b'shor':
- l += 4
- elif entry_type == b'blob':
- l += 4 + len(value)
- elif entry_type == b'ustr':
- utf16 = value.encode('utf-16be')
- l += 4 + len(utf16)
- elif entry_type == b'type':
- l += 4
- elif entry_type == b'comp' or entry_type == b'dutc':
- l += 8
- else:
- raise ValueError('Unknown type code "%s"' % entry_type)
-
- return l
-
- def write(self, block, insert=False):
- """Write this entry to the specified Block"""
- if insert:
- w = block.insert
- else:
- w = block.write
-
- if isinstance(self.type, unicode):
- entry_type = self.type.encode('latin_1')
- value = self.value
- elif isinstance(self.type, (bytes, str)):
- entry_type = self.type
- value = self.value
- else:
- entry_type = b'blob'
- value = self.type.encode(self.value)
-
- utf16 = self.filename.encode('utf-16be')
- w(b'>I', len(utf16) // 2)
- w(utf16)
- w(b'>4s4s', self.code, entry_type)
-
- if entry_type == b'bool':
- w(b'>?', value)
- elif entry_type == b'long' or entry_type == b'shor':
- w(b'>I', value)
- elif entry_type == b'blob':
- w(b'>I', len(value))
- w(value)
- elif entry_type == b'ustr':
- utf16 = value.encode('utf-16be')
- w(b'>I', len(utf16) // 2)
- w(utf16)
- elif entry_type == b'type':
- if isinstance(value, unicode):
- value = value.encode('latin_1')
- w(b'>4s', value)
- elif entry_type == b'comp' or entry_type == b'dutc':
- w(b'>Q', value)
- else:
- raise ValueError('Unknown type code "%s"' % entry_type)
-
- def __repr__(self):
- return '<%s %s>' % (self.filename, self.code)
-
-class DSStore(object):
- """Python interface to a ``.DS_Store`` file. Works by manipulating the file
- on the disk---so this code will work with ``.DS_Store`` files for *very*
- large directories.
-
- A :class:`DSStore` object can be used as if it was a mapping, e.g.::
-
- d['foobar.dat']['Iloc']
-
- will fetch the "Iloc" record for "foobar.dat", or raise :class:`KeyError` if
- there is no such record. If used in this manner, the :class:`DSStore` object
- will return (type, value) tuples, unless the type is "blob" and the module
- knows how to decode it.
-
- Currently, we know how to decode "Iloc", "bwsp", "lsvp", "lsvP" and "icvp"
- blobs. "Iloc" decodes to an (x, y) tuple, while the others are all decoded
- using ``biplist``.
-
- Assignment also works, e.g.::
-
- d['foobar.dat']['note'] = ('ustr', u'Hello World!')
-
- as does deletion with ``del``::
-
- del d['foobar.dat']['note']
-
- This is usually going to be the most convenient interface, though
- occasionally (for instance when creating a new ``.DS_Store`` file) you
- may wish to drop down to using :class:`DSStoreEntry` objects directly."""
- def __init__(self, store):
- self._store = store
- self._superblk = self._store['DSDB']
- with self._get_block(self._superblk) as s:
- self._rootnode, self._levels, self._records, \
- self._nodes, self._page_size = s.read(b'>IIIII')
- self._min_usage = 2 * self._page_size // 3
- self._dirty = False
-
- @classmethod
- def open(cls, file_or_name, mode='r+', initial_entries=None):
- """Open a ``.DS_Store`` file; pass either a Python file object, or a
- filename in the ``file_or_name`` argument and a file access mode in
- the ``mode`` argument. If you are creating a new file using the "w"
- or "w+" modes, you may also specify a list of entries with which
- to initialise the file."""
- store = buddy.Allocator.open(file_or_name, mode)
-
- if mode == 'w' or mode == 'w+':
- superblk = store.allocate(20)
- store['DSDB'] = superblk
- page_size = 4096
-
- if not initial_entries:
- root = store.allocate(page_size)
-
- with store.get_block(root) as rootblk:
- rootblk.zero_fill()
-
- with store.get_block(superblk) as s:
- s.write(b'>IIIII', root, 0, 0, 1, page_size)
- else:
- # Make sure they're in sorted order
- initial_entries = list(initial_entries)
- initial_entries.sort()
-
- # Construct the tree
- current_level = initial_entries
- next_level = []
- levels = []
- ptr_size = 0
- node_count = 0
- while True:
- total = 8
- nodes = []
- node = []
- for e in current_level:
- new_total = total + ptr_size + e.byte_length()
- if new_total > page_size:
- nodes.append(node)
- next_level.append(e)
- total = 8
- node = []
- else:
- total = new_total
- node.append(e)
- if node:
- nodes.append(node)
-
- node_count += len(nodes)
- levels.append(nodes)
-
- if len(nodes) == 1:
- break
-
- current_level = next_level
- next_level = []
- ptr_size = 4
-
- # Allocate nodes
- ptrs = [store.allocate(page_size) for n in range(node_count)]
-
- # Generate nodes
- pointers = []
- prev_pointers = None
- for level in levels:
- ppndx = 0
- lptrs = ptrs[-len(level):]
- del ptrs[-len(level):]
- for node in level:
- ndx = lptrs.pop(0)
- if prev_pointers is None:
- with store.get_block(ndx) as block:
- block.write(b'>II', 0, len(node))
- for e in node:
- e.write(block)
- else:
- next_node = prev_pointers[ppndx + len(node)]
- node_ptrs = prev_pointers[ppndx:ppndx+len(node)]
-
- with store.get_block(ndx) as block:
- block.write(b'>II', next_node, len(node))
- for ptr, e in zip(node_ptrs, node):
- block.write(b'>I', ptr)
- e.write(block)
-
- pointers.append(ndx)
- prev_pointers = pointers
- pointers = []
-
- root = prev_pointers[0]
-
- with store.get_block(superblk) as s:
- s.write(b'>IIIII', root, len(levels), len(initial_entries),
- node_count, page_size)
-
- return DSStore(store)
-
- def _get_block(self, number):
- return self._store.get_block(number)
-
- def flush(self):
- """Flush any dirty data back to the file."""
- if self._dirty:
- self._dirty = False
-
- with self._get_block(self._superblk) as s:
- s.write(b'>IIIII', self._rootnode, self._levels, self._records,
- self._nodes, self._page_size)
- self._store.flush()
-
- def close(self):
- """Flush dirty data and close the underlying file."""
- self.flush()
- self._store.close()
-
- def __enter__(self):
- return self
-
- def __exit__(self, exc_type, exc_value, traceback):
- self.close()
-
- # Internal B-Tree nodes look like this:
- #
- # [ next | count | (ptr0 | rec0) | (ptr1 | rec1) ... (ptrN | recN) ]
-
- # Leaf nodes look like this:
- #
- # [ 0 | count | rec0 | rec1 ... recN ]
-
- # Iterate over the tree, starting at `node'
- def _traverse(self, node):
- if node is None:
- node = self._rootnode
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
- if next_node:
- for n in range(count):
- ptr = block.read(b'>I')[0]
- for e in self._traverse(ptr):
- yield e
- e = DSStoreEntry.read(block)
- yield e
- for e in self._traverse(next_node):
- yield e
- else:
- for n in range(count):
- e = DSStoreEntry.read(block)
- yield e
-
- # Display the data in `node'
- def _dump_node(self, node):
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
- print('next: %u\ncount: %u\n' % (next_node, count))
- for n in range(count):
- if next_node:
- ptr = block.read(b'>I')[0]
- print('%8u ' % ptr, end=' ')
- else:
- print(' ', end=' ')
- e = DSStoreEntry.read(block)
- print(e, ' (%u)' % e.byte_length())
- print('used: %u' % block.tell())
-
- # Display the data in the super block
- def _dump_super(self):
- print('root: %u\nlevels: %u\nrecords: %u\nnodes: %u\npage-size: %u' \
- % (self._rootnode, self._levels, self._records,
- self._nodes, self._page_size))
-
- # Splits entries across two blocks, returning one pivot
- #
- # Tries to balance the block usage across the two as best it can
- def _split2(self, blocks, entries, pointers, before, internal):
- left_block = blocks[0]
- right_block = blocks[1]
-
- count = len(entries)
-
- # Find the feasible splits
- best_split = None
- best_diff = None
- total = before[count]
-
- if 8 + total <= self._page_size:
- # We can use a *single* node for this
- best_split = count
- else:
- # Split into two nodes
- for n in range(1, count - 1):
- left_size = 8 + before[n]
- right_size = 8 + total - before[n + 1]
-
- if left_size > self._page_size:
- break
- if right_size > self._page_size:
- continue
-
- diff = abs(left_size - right_size)
-
- if best_split is None or diff < best_diff:
- best_split = n
- best_diff = diff
-
- if best_split is None:
- return None
-
- # Write the nodes
- left_block.seek(0)
- if internal:
- next_node = pointers[best_split]
- else:
- next_node = 0
- left_block.write(b'>II', next_node, best_split)
-
- for n in range(best_split):
- if internal:
- left_block.write(b'>I', pointers[n])
- entries[n].write(left_block)
-
- left_block.zero_fill()
-
- if best_split == count:
- return []
-
- right_block.seek(0)
- if internal:
- next_node = pointers[count]
- else:
- next_node = 0
- right_block.write(b'>II', next_node, count - best_split - 1)
-
- for n in range(best_split + 1, count):
- if internal:
- right_block.write(b'>I', pointers[n])
- entries[n].write(right_block)
-
- right_block.zero_fill()
-
- pivot = entries[best_split]
-
- return [pivot]
-
- def _split(self, node, entry, right_ptr=0):
- self._nodes += 1
- self._dirty = True
- new_right = self._store.allocate(self._page_size)
- with self._get_block(node) as block, \
- self._get_block(new_right) as right_block:
-
- # First, measure and extract all the elements
- entry_size = entry.byte_length()
- entry_pos = None
- next_node, count = block.read(b'>II')
- if next_node:
- entry_size += 4
- pointers = []
- entries = []
- before = []
- total = 0
- for n in range(count):
- pos = block.tell()
- if next_node:
- ptr = block.read(b'>I')[0]
- pointers.append(ptr)
- e = DSStoreEntry.read(block)
- if e > entry:
- entry_pos = n
- entries.append(entry)
- pointers.append(right_ptr)
- before.append(total)
- total += entry_size
- entries.append(e)
- before.append(total)
- total += block.tell() - pos
- before.append(total)
- if next_node:
- pointers.append(next_node)
-
- pivot = self._split2([block, right_block],
- entries, pointers, before,
- bool(next_node))[0]
-
- self._records += 1
- self._nodes += 1
- self._dirty = True
-
- return (pivot, new_right)
-
- # Allocate a new root node containing the element `pivot' and the pointers
- # `left' and `right'
- def _new_root(self, left, pivot, right):
- new_root = self._store.allocate(self._page_size)
- with self._get_block(new_root) as block:
- block.write(b'>III', right, 1, left)
- pivot.write(block)
- self._rootnode = new_root
- self._levels += 1
- self._nodes += 1
- self._dirty = True
-
- # Insert an entry into an inner node; `path' is the path from the root
- # to `node', not including `node' itself. `right_ptr' is the new node
- # pointer (inserted to the RIGHT of `entry')
- def _insert_inner(self, path, node, entry, right_ptr):
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
- insert_pos = None
- insert_ndx = None
- n = 0
- while n < count:
- pos = block.tell()
- ptr = block.read(b'>I')[0]
- e = DSStoreEntry.read(block)
- if e == entry:
- if n == count - 1:
- right_ptr = next_node
- next_node = ptr
- block_seek(pos)
- else:
- right_ptr = block.read(b'>I')[0]
- block.seek(pos + 4)
- insert_pos = pos
- insert_ndx = n
- block.delete(e.byte_length() + 4)
- count -= 1
- self._records += 1
- self._dirty = True
- continue
- elif insert_pos is None and e > entry:
- insert_pos = pos
- insert_ndx = n
- n += 1
- if insert_pos is None:
- insert_pos = block.tell()
- insert_ndx = count
- remaining = self._page_size - block.tell()
-
- if remaining < entry.byte_length() + 4:
- pivot, new_right = self._split(node, entry, right_ptr)
- if path:
- self._insert_inner(path[:-1], path[-1], pivot, new_right)
- else:
- self._new_root(node, pivot, new_right)
- else:
- if insert_ndx == count:
- block.seek(insert_pos)
- block.write(b'>I', next_node)
- entry.write(block)
- next_node = right_ptr
- else:
- block.seek(insert_pos + 4)
- entry.write(block, True)
- block.insert('>I', right_ptr)
- block.seek(0)
- count += 1
- block.write(b'>II', next_node, count)
- self._records += 1
- self._dirty = True
-
- # Insert `entry' into the leaf node `node'
- def _insert_leaf(self, path, node, entry):
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
- insert_pos = None
- insert_ndx = None
- n = 0
- while n < count:
- pos = block.tell()
- e = DSStoreEntry.read(block)
- if e == entry:
- insert_pos = pos
- insert_ndx = n
- block.seek(pos)
- block.delete(e.byte_length())
- count -= 1
- self._records += 1
- self._dirty = True
- continue
- elif insert_pos is None and e > entry:
- insert_pos = pos
- insert_ndx = n
- n += 1
- if insert_pos is None:
- insert_pos = block.tell()
- insert_ndx = count
- remaining = self._page_size - block.tell()
-
- if remaining < entry.byte_length():
- pivot, new_right = self._split(node, entry)
- if path:
- self._insert_inner(path[:-1], path[-1], pivot, new_right)
- else:
- self._new_root(node, pivot, new_right)
- else:
- block.seek(insert_pos)
- entry.write(block, True)
- block.seek(0)
- count += 1
- block.write(b'>II', next_node, count)
- self._records += 1
- self._dirty = True
-
- def insert(self, entry):
- """Insert ``entry`` (which should be a :class:`DSStoreEntry`)
- into the B-Tree."""
- path = []
- node = self._rootnode
- while True:
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
- if next_node:
- for n in range(count):
- ptr = block.read(b'>I')[0]
- e = DSStoreEntry.read(block)
- if entry < e:
- next_node = ptr
- break
- elif entry == e:
- # If we find an existing entry the same, replace it
- self._insert_inner(path, node, entry, None)
- return
- path.append(node)
- node = next_node
- else:
- self._insert_leaf(path, node, entry)
- return
-
- # Return usage information for the specified `node'
- def _block_usage(self, node):
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
-
- for n in range(count):
- if next_node:
- ptr = block.read(b'>I')[0]
- e = DSStoreEntry.read(block)
-
- used = block.tell()
-
- return (count, used)
-
- # Splits entries across three blocks, returning two pivots
- def _split3(self, blocks, entries, pointers, before, internal):
- count = len(entries)
-
- # Find the feasible splits
- best_split = None
- best_diff = None
- total = before[count]
- for n in range(1, count - 3):
- left_size = 8 + before[n]
- remaining = 16 + total - before[n + 1]
-
- if left_size > self._page_size:
- break
- if remaining > 2 * self._page_size:
- continue
-
- for m in range(n + 2, count - 1):
- mid_size = 8 + before[m] - before[n + 1]
- right_size = 8 + total - before[m + 1]
-
- if mid_size > self._page_size:
- break
- if right_size > self._page_size:
- continue
-
- diff = abs(left_size - mid_size) * abs(right_size - mid_size)
-
- if best_split is None or diff < best_diff:
- best_split = (n, m, count)
- best_diff = diff
-
- if best_split is None:
- return None
-
- # Write the nodes
- prev_split = -1
- for block, split in zip(blocks, best_split):
- block.seek(0)
- if internal:
- next_node = pointers[split]
- else:
- next_node = 0
- block.write(b'>II', next_node, split)
-
- for n in range(prev_split + 1, split):
- if internal:
- block.write(b'>I', pointers[n])
- entries[n].write(block)
-
- block.zero_fill()
-
- prev_split = split
-
- return (entries[best_split[0]], entries[best_split[1]])
-
- # Extract all of the entries from the specified list of `blocks',
- # separating them by the specified `pivots'. Also computes the
- # amount of space used before each entry.
- def _extract(self, blocks, pivots):
- pointers = []
- entries = []
- before = []
- total = 0
- ppivots = pivots + [None]
- for b,p in zip(blocks, ppivots):
- b.seek(0)
- next_node, count = b.read(b'>II')
- for n in range(count):
- pos = b.tell()
- if next_node:
- ptr = b.read(b'>I')[0]
- pointers.append(ptr)
- e = DSStoreEntry.read(b)
- entries.append(e)
- before.append(total)
- total += b.tell() - pos
- if next_node:
- pointers.append(next_node)
- if p:
- entries.append(p)
- before.append(total)
- total += p.byte_length()
- if next_node:
- total += 4
- before.append(total)
-
- return (entries, pointers, before)
-
- # Rebalance the specified `node', whose path from the root is `path'.
- def _rebalance(self, path, node):
- # Can't rebalance the root
- if not path:
- return
-
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
-
- with self._get_block(path[-1]) as parent:
- # Find the left and right siblings and respective pivots
- parent_next, parent_count = parent.read(b'>II')
- left_pos = None
- left_node = None
- left_pivot = None
- node_pos = None
- right_pos = None
- right_node = None
- right_pivot = None
- prev_e = prev_ptr = prev_pos = None
- for n in range(parent_count):
- pos = parent.tell()
- ptr = parent.read(b'>I')[0]
- e = DSStoreEntry.read(parent)
-
- if ptr == node:
- node_pos = pos
- right_pivot = e
- left_pos = prev_pos
- left_pivot = prev_e
- left_node = prev_ptr
- elif prev_ptr == node:
- right_node = ptr
- right_pos = pos
- break
-
- prev_e = e
- prev_ptr = ptr
- prev_pos = pos
-
- if parent_next == node:
- node_pos = parent.tell()
- left_pos = prev_pos
- left_pivot = prev_e
- left_node = prev_ptr
- elif right_node is None:
- right_node = parent_next
- right_pos = parent.tell()
-
- parent_used = parent.tell()
-
- if left_node and right_node:
- with self._get_block(left_node) as left, \
- self._get_block(right_node) as right:
- blocks = [left, block, right]
- pivots = [left_pivot, right_pivot]
-
- entries, pointers, before = self._extract(blocks, pivots)
-
- # If there's a chance that we could use two pages instead
- # of three, go for it
- pivots = self._split2(blocks, entries, pointers,
- before, bool(next_node))
- if pivots is None:
- ptrs = [left_node, node, right_node]
- pivots = self._split3(blocks, entries, pointers,
- before, bool(next_node))
- else:
- if pivots:
- ptrs = [left_node, node]
- else:
- ptrs = [left_node]
- self._store.release(node)
- self._nodes -= 1
- node = left_node
- self._store.release(right_node)
- self._nodes -= 1
- self._dirty = True
-
- # Remove the pivots from the parent
- with self._get_block(path[-1]) as parent:
- if right_node == parent_next:
- parent.seek(left_pos)
- parent.delete(right_pos - left_pos)
- parent_next = left_node
- else:
- parent.seek(left_pos + 4)
- parent.delete(right_pos - left_pos)
- parent.seek(0)
- parent_count -= 2
- parent.write(b'>II', parent_next, parent_count)
- self._records -= 2
-
- # Replace with those in pivots
- for e,rp in zip(pivots, ptrs[1:]):
- self._insert_inner(path[:-1], path[-1], e, rp)
- elif left_node:
- with self._get_block(left_node) as left:
- blocks = [left, block]
- pivots = [left_pivot]
-
- entries, pointers, before = self._extract(blocks, pivots)
-
- pivots = self._split2(blocks, entries, pointers,
- before, bool(next_node))
-
- # Remove the pivot from the parent
- with self._get_block(path[-1]) as parent:
- if node == parent_next:
- parent.seek(left_pos)
- parent.delete(node_pos - left_pos)
- parent_next = left_node
- else:
- parent.seek(left_pos + 4)
- parent.delete(node_pos - left_pos)
- parent.seek(0)
- parent_count -= 1
- parent.write(b'>II', parent_next, parent_count)
- self._records -= 1
-
- # Replace the pivot
- if pivots:
- self._insert_inner(path[:-1], path[-1], pivots[0], node)
- elif right_node:
- with self._get_block(right_node) as right:
- blocks = [block, right]
- pivots = [right_pivot]
-
- entries, pointers, before = self._extract(blocks, pivots)
-
- pivots = self._split2(blocks, entries, pointers,
- before, bool(next_node))
-
- # Remove the pivot from the parent
- with self._get_block(path[-1]) as parent:
- if right_node == parent_next:
- parent.seek(pos)
- parent.delete(right_pos - node_pos)
- parent_next = node
- else:
- parent.seek(pos + 4)
- parent.delete(right_pos - node_pos)
- parent.seek(0)
- parent_count -= 1
- parent.write(b'>II', parent_next, parent_count)
- self._records -= 1
-
- # Replace the pivot
- if pivots:
- self._insert_inner(path[:-1], path[-1], pivots[0],
- right_node)
-
- if not path and not parent_count:
- self._store.release(path[-1])
- self._nodes -= 1
- self._dirty = True
- self._rootnode = node
- else:
- count, used = self._block_usage(path[-1])
-
- if used < self._page_size // 2:
- self._rebalance(path[:-1], path[-1])
-
- # Delete from the leaf node `node'. `filename_lc' has already been
- # lower-cased.
- def _delete_leaf(self, node, filename_lc, code):
- found = False
-
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
-
- for n in range(count):
- pos = block.tell()
- e = DSStoreEntry.read(block)
- if e.filename.lower() == filename_lc \
- and (code is None or e.code == code):
- block.seek(pos)
- block.delete(e.byte_length())
- found = True
-
- # This does not affect the loop; THIS IS NOT A BUG
- count -= 1
-
- self._records -= 1
- self._dirty = True
-
- if found:
- used = block.tell()
-
- block.seek(0)
- block.write(b'>II', next_node, count)
-
- return used < self._page_size // 2
- else:
- return False
-
- # Remove the largest entry from the subtree starting at `node' (with
- # path from root `path'). Returns a tuple (rebalance, entry) where
- # rebalance is either None if no rebalancing is required, or a
- # (path, node) tuple giving the details of the node to rebalance.
- def _take_largest(self, path, node):
- path = list(path)
- rebalance = None
- while True:
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
-
- if next_node:
- path.append(node)
- node = next_node
- continue
-
- for n in range(count):
- pos = block.tell()
- e = DSStoreEntry.read(block)
-
- count -= 1
- block.seek(0)
- block.write(b'>II', next_node, count)
-
- if pos < self._page_size // 2:
- rebalance = (path, node)
- break
-
- return rebalance, e
-
- # Delete an entry from an inner node, `node'
- def _delete_inner(self, path, node, filename_lc, code):
- rebalance = False
-
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
-
- for n in range(count):
- pos = block.tell()
- ptr = block.read(b'>I')[0]
- e = DSStoreEntry.read(block)
- if e.filename.lower() == filename_lc \
- and (code is None or e.code == code):
- # Take the largest from the left subtree
- rebalance, largest = self._take_largest(path, ptr)
-
- # Delete this entry
- if n == count - 1:
- right_ptr = next_node
- next_node = ptr
- block.seek(pos)
- else:
- right_ptr = block.read(b'>I')[0]
- block.seek(pos + 4)
-
- block.delete(e.byte_length() + 4)
-
- count -= 1
- block.seek(0)
- block.write(b'>II', next_node, count)
-
- self._records -= 1
- self._dirty = True
-
- break
-
- # Replace the pivot value
- self._insert_inner(path, node, largest, right_ptr)
-
- # Rebalance from the node we stole from
- if rebalance:
- self._rebalance(rebalance[0], rebalance[1])
- return True
- return False
-
- def delete(self, filename, code):
- """Delete an item, identified by ``filename`` and ``code``
- from the B-Tree."""
- if isinstance(filename, DSStoreEntry):
- code = filename.code
- filename = filename.filename
-
- # If we're deleting *every* node for "filename", we must recurse
- if code is None:
- ###TODO: Fix this so we can do bulk deletes
- raise ValueError('You must delete items individually. Sorry')
-
- # Otherwise, we're deleting *one* specific node
- filename_lc = filename.lower()
- path = []
- node = self._rootnode
- while True:
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
- if next_node:
- for n in range(count):
- ptr = block.read(b'>I')[0]
- e = DSStoreEntry.read(block)
- e_lc = e.filename.lower()
- if filename_lc < e_lc \
- or (filename_lc == e_lc and code < e.code):
- next_node = ptr
- break
- elif filename_lc == e_lc and code == e.code:
- self._delete_inner(path, node, filename_lc, code)
- return
- path.append(node)
- node = next_node
- else:
- if self._delete_leaf(node, filename_lc, code):
- self._rebalance(path, node)
- return
-
- # Find implementation
- def _find(self, node, filename_lc, code=None):
- if not isinstance(code, bytes):
- code = code.encode('latin_1')
- with self._get_block(node) as block:
- next_node, count = block.read(b'>II')
- if next_node:
- for n in range(count):
- ptr = block.read(b'>I')[0]
- e = DSStoreEntry.read(block)
- if filename_lc < e.filename.lower():
- for e in self._find(ptr, filename_lc, code):
- yield e
- return
- elif filename_lc == e.filename.lower():
- if code is None or (code and code < e.code):
- for e in self._find(ptr, filename_lc, code):
- yield e
- if code is None or code == e.code:
- yield e
- elif code < e.code:
- return
- for e in self._find(next_node, filename_lc, code):
- yield e
- else:
- for n in range(count):
- e = DSStoreEntry.read(block)
- if filename_lc == e.filename.lower():
- if code is None or code == e.code:
- yield e
- elif code < e.code:
- return
-
- def find(self, filename, code=None):
- """Returns a generator that will iterate over matching entries in
- the B-Tree."""
- if isinstance(filename, DSStoreEntry):
- code = filename.code
- filename = filename.filename
-
- filename_lc = filename.lower()
-
- return self._find(self._rootnode, filename_lc, code)
-
- def __len__(self):
- return self._records
-
- def __iter__(self):
- return self._traverse(self._rootnode)
-
- class Partial(object):
- """This is used to implement indexing."""
- def __init__(self, store, filename):
- self._store = store
- self._filename = filename
-
- def __getitem__(self, code):
- if code is None:
- raise KeyError('no such key - [%s][None]' % self._filename)
-
- if not isinstance(code, bytes):
- code = code.encode('latin_1')
-
- try:
- item = next(self._store.find(self._filename, code))
- except StopIteration:
- raise KeyError('no such key - [%s][%s]' % (self._filename,
- code))
-
- if not isinstance(item.type, (bytes, str, unicode)):
- return item.value
-
- return (item.type, item.value)
-
- def __setitem__(self, code, value):
- if code is None:
- raise KeyError('bad key - [%s][None]' % self._filename)
-
- if not isinstance(code, bytes):
- code = code.encode('latin_1')
-
- codec = codecs.get(code, None)
- if codec:
- entry_type = codec
- entry_value = value
- else:
- entry_type = value[0]
- entry_value = value[1]
-
- self._store.insert(DSStoreEntry(self._filename, code,
- entry_type, entry_value))
-
- def __delitem__(self, code):
- if code is None:
- raise KeyError('no such key - [%s][None]' % self._filename)
-
- self._store.delete(self._filename, code)
-
- def __iter__(self):
- for item in self._store.find(self._filename):
- yield item
-
- def __getitem__(self, filename):
- return self.Partial(self, filename)
-
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/__init__.py b/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/__init__.py
deleted file mode 100644
index 7eb314107..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/__init__.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from .alias import *
-from .bookmark import *
-
-__all__ = [ 'ALIAS_KIND_FILE', 'ALIAS_KIND_FOLDER',
- 'ALIAS_HFS_VOLUME_SIGNATURE',
- 'ALIAS_FIXED_DISK', 'ALIAS_NETWORK_DISK', 'ALIAS_400KB_FLOPPY_DISK',
- 'ALIAS_800KB_FLOPPY_DISK', 'ALIAS_1_44MB_FLOPPY_DISK',
- 'ALIAS_EJECTABLE_DISK',
- 'ALIAS_NO_CNID',
- 'kBookmarkPath', 'kBookmarkCNIDPath', 'kBookmarkFileProperties',
- 'kBookmarkFileName', 'kBookmarkFileID', 'kBookmarkFileCreationDate',
- 'kBookmarkTOCPath', 'kBookmarkVolumePath',
- 'kBookmarkVolumeURL', 'kBookmarkVolumeName', 'kBookmarkVolumeUUID',
- 'kBookmarkVolumeSize', 'kBookmarkVolumeCreationDate',
- 'kBookmarkVolumeProperties', 'kBookmarkContainingFolder',
- 'kBookmarkUserName', 'kBookmarkUID', 'kBookmarkWasFileReference',
- 'kBookmarkCreationOptions', 'kBookmarkURLLengths',
- 'kBookmarkSecurityExtension',
- 'AppleShareInfo',
- 'VolumeInfo',
- 'TargetInfo',
- 'Alias',
- 'Bookmark',
- 'Data',
- 'URL' ]
-
-
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/alias.py b/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/alias.py
deleted file mode 100644
index f74b949ea..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/alias.py
+++ /dev/null
@@ -1,607 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-from __future__ import division
-
-import struct
-import datetime
-import io
-import re
-import os
-import os.path
-import stat
-import sys
-
-if sys.platform == 'darwin':
- from . import osx
-
-try:
- long
-except NameError:
- long = int
-
-from .utils import *
-
-ALIAS_KIND_FILE = 0
-ALIAS_KIND_FOLDER = 1
-
-ALIAS_HFS_VOLUME_SIGNATURE = b'H+'
-
-ALIAS_FIXED_DISK = 0
-ALIAS_NETWORK_DISK = 1
-ALIAS_400KB_FLOPPY_DISK = 2
-ALIAS_800KB_FLOPPY_DISK = 3
-ALIAS_1_44MB_FLOPPY_DISK = 4
-ALIAS_EJECTABLE_DISK = 5
-
-ALIAS_NO_CNID = 0xffffffff
-
-def encode_utf8(s):
- if isinstance(s, bytes):
- return s
- return s.encode('utf-8')
-
-def decode_utf8(s):
- if isinstance(s, bytes):
- return s.decode('utf-8')
- return s
-
-class AppleShareInfo (object):
- def __init__(self, zone=None, server=None, user=None):
- #: The AppleShare zone
- self.zone = zone
- #: The AFP server
- self.server = server
- #: The username
- self.user = user
-
- def __repr__(self):
- return 'AppleShareInfo(%r,%r,%r)' % (self.zone, self.server, self.user)
-
-class VolumeInfo (object):
- def __init__(self, name, creation_date, fs_type, disk_type,
- attribute_flags, fs_id, appleshare_info=None,
- driver_name=None, posix_path=None, disk_image_alias=None,
- dialup_info=None, network_mount_info=None):
- #: The name of the volume on which the target resides
- self.name = name
-
- #: The creation date of the target's volume
- self.creation_date = creation_date
-
- #: The filesystem type (a two character code, e.g. ``b'H+'`` for HFS+)
- self.fs_type = fs_type
-
- #: The type of disk; should be one of
- #:
- #: * ALIAS_FIXED_DISK
- #: * ALIAS_NETWORK_DISK
- #: * ALIAS_400KB_FLOPPY_DISK
- #: * ALIAS_800KB_FLOPPY_DISK
- #: * ALIAS_1_44MB_FLOPPY_DISK
- #: * ALIAS_EJECTABLE_DISK
- self.disk_type = disk_type
-
- #: Filesystem attribute flags (from HFS volume header)
- self.attribute_flags = attribute_flags
-
- #: Filesystem identifier
- self.fs_id = fs_id
-
- #: AppleShare information (for automatic remounting of network shares)
- #: *(optional)*
- self.appleshare_info = appleshare_info
-
- #: Driver name (*probably* contains a disk driver name on older Macs)
- #: *(optional)*
- self.driver_name = driver_name
-
- #: POSIX path of the mount point of the target's volume
- #: *(optional)*
- self.posix_path = posix_path
-
- #: :class:`Alias` object pointing at the disk image on which the
- #: target's volume resides *(optional)*
- self.disk_image_alias = disk_image_alias
-
- #: Dialup information (for automatic establishment of dialup connections)
- self.dialup_info = dialup_info
-
- #: Network mount information (for automatic remounting)
- self.network_mount_info = network_mount_info
-
- def __repr__(self):
- args = ['name', 'creation_date', 'fs_type', 'disk_type',
- 'attribute_flags', 'fs_id']
- values = []
- for a in args:
- v = getattr(self, a)
- values.append(repr(v))
-
- kwargs = ['appleshare_info', 'driver_name', 'posix_path',
- 'disk_image_alias', 'dialup_info', 'network_mount_info']
- for a in kwargs:
- v = getattr(self, a)
- if v is not None:
- values.append('%s=%r' % (a, v))
- return 'VolumeInfo(%s)' % ','.join(values)
-
-class TargetInfo (object):
- def __init__(self, kind, filename, folder_cnid, cnid, creation_date,
- creator_code, type_code, levels_from=-1, levels_to=-1,
- folder_name=None, cnid_path=None, carbon_path=None,
- posix_path=None, user_home_prefix_len=None):
- #: Either ALIAS_KIND_FILE or ALIAS_KIND_FOLDER
- self.kind = kind
-
- #: The filename of the target
- self.filename = filename
-
- #: The CNID (Catalog Node ID) of the target's containing folder;
- #: CNIDs are similar to but different than traditional UNIX inode
- #: numbers
- self.folder_cnid = folder_cnid
-
- #: The CNID (Catalog Node ID) of the target
- self.cnid = cnid
-
- #: The target's *creation* date.
- self.creation_date = creation_date
-
- #: The target's Mac creator code (a four-character binary string)
- self.creator_code = creator_code
-
- #: The target's Mac type code (a four-character binary string)
- self.type_code = type_code
-
- #: The depth of the alias? Always seems to be -1 on OS X.
- self.levels_from = levels_from
-
- #: The depth of the target? Always seems to be -1 on OS X.
- self.levels_to = levels_to
-
- #: The (POSIX) name of the target's containing folder. *(optional)*
- self.folder_name = folder_name
-
- #: The path from the volume root as a sequence of CNIDs. *(optional)*
- self.cnid_path = cnid_path
-
- #: The Carbon path of the target *(optional)*
- self.carbon_path = carbon_path
-
- #: The POSIX path of the target relative to the volume root. Note
- #: that this may or may not have a leading '/' character, but it is
- #: always relative to the containing volume. *(optional)*
- self.posix_path = posix_path
-
- #: If the path points into a user's home folder, the number of folders
- #: deep that we go before we get to that home folder. *(optional)*
- self.user_home_prefix_len = user_home_prefix_len
-
- def __repr__(self):
- args = ['kind', 'filename', 'folder_cnid', 'cnid', 'creation_date',
- 'creator_code', 'type_code']
- values = []
- for a in args:
- v = getattr(self, a)
- values.append(repr(v))
-
- if self.levels_from != -1:
- values.append('levels_from=%r' % self.levels_from)
- if self.levels_to != -1:
- values.append('levels_to=%r' % self.levels_to)
-
- kwargs = ['folder_name', 'cnid_path', 'carbon_path',
- 'posix_path', 'user_home_prefix_len']
- for a in kwargs:
- v = getattr(self, a)
- values.append('%s=%r' % (a, v))
-
- return 'TargetInfo(%s)' % ','.join(values)
-
-TAG_CARBON_FOLDER_NAME = 0
-TAG_CNID_PATH = 1
-TAG_CARBON_PATH = 2
-TAG_APPLESHARE_ZONE = 3
-TAG_APPLESHARE_SERVER_NAME = 4
-TAG_APPLESHARE_USERNAME = 5
-TAG_DRIVER_NAME = 6
-TAG_NETWORK_MOUNT_INFO = 9
-TAG_DIALUP_INFO = 10
-TAG_UNICODE_FILENAME = 14
-TAG_UNICODE_VOLUME_NAME = 15
-TAG_HIGH_RES_VOLUME_CREATION_DATE = 16
-TAG_HIGH_RES_CREATION_DATE = 17
-TAG_POSIX_PATH = 18
-TAG_POSIX_PATH_TO_MOUNTPOINT = 19
-TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE = 20
-TAG_USER_HOME_LENGTH_PREFIX = 21
-
-class Alias (object):
- def __init__(self, appinfo=b'\0\0\0\0', version=2, volume=None,
- target=None, extra=[]):
- """Construct a new :class:`Alias` object with the specified
- contents."""
-
- #: Application specific information (four byte byte-string)
- self.appinfo = appinfo
-
- #: Version (we support only version 2)
- self.version = version
-
- #: A :class:`VolumeInfo` object describing the target's volume
- self.volume = volume
-
- #: A :class:`TargetInfo` object describing the target
- self.target = target
-
- #: A list of extra `(tag, value)` pairs
- self.extra = list(extra)
-
- @classmethod
- def _from_fd(cls, b):
- appinfo, recsize, version = struct.unpack(b'>4shh', b.read(8))
-
- if recsize < 150:
- raise ValueError('Incorrect alias length')
-
- if version != 2:
- raise ValueError('Unsupported alias version %u' % version)
-
- kind, volname, voldate, fstype, disktype, \
- folder_cnid, filename, cnid, crdate, creator_code, type_code, \
- levels_from, levels_to, volattrs, volfsid, reserved = \
- struct.unpack(b'>h28pI2shI64pII4s4shhI2s10s', b.read(142))
-
- voldate = mac_epoch + datetime.timedelta(seconds=voldate)
- crdate = mac_epoch + datetime.timedelta(seconds=crdate)
-
- alias = Alias()
- alias.appinfo = appinfo
-
- alias.volume = VolumeInfo (volname.decode().replace('/',':'),
- voldate, fstype, disktype,
- volattrs, volfsid)
- alias.target = TargetInfo (kind, filename.decode().replace('/',':'),
- folder_cnid, cnid,
- crdate, creator_code, type_code)
- alias.target.levels_from = levels_from
- alias.target.levels_to = levels_to
-
- tag = struct.unpack(b'>h', b.read(2))[0]
-
- while tag != -1:
- length = struct.unpack(b'>h', b.read(2))[0]
- value = b.read(length)
- if length & 1:
- b.read(1)
-
- if tag == TAG_CARBON_FOLDER_NAME:
- alias.target.folder_name = value.decode().replace('/',':')
- elif tag == TAG_CNID_PATH:
- alias.target.cnid_path = struct.unpack('>%uI' % (length // 4),
- value)
- elif tag == TAG_CARBON_PATH:
- alias.target.carbon_path = value
- elif tag == TAG_APPLESHARE_ZONE:
- if alias.volume.appleshare_info is None:
- alias.volume.appleshare_info = AppleShareInfo()
- alias.volume.appleshare_info.zone = value
- elif tag == TAG_APPLESHARE_SERVER_NAME:
- if alias.volume.appleshare_info is None:
- alias.volume.appleshare_info = AppleShareInfo()
- alias.volume.appleshare_info.server = value
- elif tag == TAG_APPLESHARE_USERNAME:
- if alias.volume.appleshare_info is None:
- alias.volume.appleshare_info = AppleShareInfo()
- alias.volume.appleshare_info.user = value
- elif tag == TAG_DRIVER_NAME:
- alias.volume.driver_name = value
- elif tag == TAG_NETWORK_MOUNT_INFO:
- alias.volume.network_mount_info = value
- elif tag == TAG_DIALUP_INFO:
- alias.volume.dialup_info = value
- elif tag == TAG_UNICODE_FILENAME:
- alias.target.filename = value[2:].decode('utf-16be')
- elif tag == TAG_UNICODE_VOLUME_NAME:
- alias.volume.name = value[2:].decode('utf-16be')
- elif tag == TAG_HIGH_RES_VOLUME_CREATION_DATE:
- seconds = struct.unpack(b'>Q', value)[0] / 65536.0
- alias.volume.creation_date \
- = mac_epoch + datetime.timedelta(seconds=seconds)
- elif tag == TAG_HIGH_RES_CREATION_DATE:
- seconds = struct.unpack(b'>Q', value)[0] / 65536.0
- alias.target.creation_date \
- = mac_epoch + datetime.timedelta(seconds=seconds)
- elif tag == TAG_POSIX_PATH:
- alias.target.posix_path = value.decode()
- elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT:
- alias.volume.posix_path = value.decode()
- elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE:
- alias.volume.disk_image_alias = Alias.from_bytes(value)
- elif tag == TAG_USER_HOME_LENGTH_PREFIX:
- alias.target.user_home_prefix_len = struct.unpack(b'>h', value)[0]
- else:
- alias.extra.append((tag, value))
-
- tag = struct.unpack(b'>h', b.read(2))[0]
-
- return alias
-
- @classmethod
- def from_bytes(cls, bytes):
- """Construct an :class:`Alias` object given binary Alias data."""
- with io.BytesIO(bytes) as b:
- return cls._from_fd(b)
-
- @classmethod
- def for_file(cls, path):
- """Create an :class:`Alias` that points at the specified file."""
- if sys.platform != 'darwin':
- raise Exception('Not implemented (requires special support)')
-
- path = encode_utf8(path)
-
- a = Alias()
-
- # Find the filesystem
- st = osx.statfs(path)
- vol_path = st.f_mntonname
-
- # Grab its attributes
- attrs = [osx.ATTR_CMN_CRTIME,
- osx.ATTR_VOL_NAME,
- 0, 0, 0]
- volinfo = osx.getattrlist(vol_path, attrs, 0)
-
- vol_crtime = volinfo[0]
- vol_name = encode_utf8(volinfo[1])
-
- # Also grab various attributes of the file
- attrs = [(osx.ATTR_CMN_OBJTYPE
- | osx.ATTR_CMN_CRTIME
- | osx.ATTR_CMN_FNDRINFO
- | osx.ATTR_CMN_FILEID
- | osx.ATTR_CMN_PARENTID), 0, 0, 0, 0]
- info = osx.getattrlist(path, attrs, osx.FSOPT_NOFOLLOW)
-
- if info[0] == osx.VDIR:
- kind = ALIAS_KIND_FOLDER
- else:
- kind = ALIAS_KIND_FILE
-
- cnid = info[3]
- folder_cnid = info[4]
-
- dirname, filename = os.path.split(path)
-
- if dirname == b'' or dirname == b'.':
- dirname = os.getcwd()
-
- foldername = os.path.basename(dirname)
-
- creation_date = info[1]
-
- if kind == ALIAS_KIND_FILE:
- creator_code = struct.pack(b'I', info[2].fileInfo.fileCreator)
- type_code = struct.pack(b'I', info[2].fileInfo.fileType)
- else:
- creator_code = b'\0\0\0\0'
- type_code = b'\0\0\0\0'
-
- a.target = TargetInfo(kind, filename, folder_cnid, cnid, creation_date,
- creator_code, type_code)
- a.volume = VolumeInfo(vol_name, vol_crtime, b'H+',
- ALIAS_FIXED_DISK, 0, b'\0\0')
-
- a.target.folder_name = foldername
- a.volume.posix_path = vol_path
-
- rel_path = os.path.relpath(path, vol_path)
-
- # Leave off the initial '/' if vol_path is '/' (no idea why)
- if vol_path == b'/':
- a.target.posix_path = rel_path
- else:
- a.target.posix_path = b'/' + rel_path
-
- # Construct the Carbon and CNID paths
- carbon_path = []
- cnid_path = []
- head, tail = os.path.split(rel_path)
- if not tail:
- head, tail = os.path.split(head)
- while head or tail:
- if head:
- attrs = [osx.ATTR_CMN_FILEID, 0, 0, 0, 0]
- info = osx.getattrlist(os.path.join(vol_path, head), attrs, 0)
- cnid_path.append(info[0])
- carbon_tail = tail.replace(b':',b'/')
- carbon_path.insert(0, carbon_tail)
- head, tail = os.path.split(head)
-
- carbon_path = vol_name + b':' + b':\0'.join(carbon_path)
-
- a.target.carbon_path = carbon_path
- a.target.cnid_path = cnid_path
-
- return a
-
- def _to_fd(self, b):
- # We'll come back and fix the length when we're done
- pos = b.tell()
- b.write(struct.pack(b'>4shh', self.appinfo, 0, self.version))
-
- carbon_volname = encode_utf8(self.volume.name).replace(b':',b'/')
- carbon_filename = encode_utf8(self.target.filename).replace(b':',b'/')
- voldate = (self.volume.creation_date - mac_epoch).total_seconds()
- crdate = (self.target.creation_date - mac_epoch).total_seconds()
-
- # NOTE: crdate should be in local time, but that's system dependent
- # (so doing so is ridiculous, and nothing could rely on it).
- b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s',
- self.target.kind,
- carbon_volname, int(voldate),
- self.volume.fs_type,
- self.volume.disk_type,
- self.target.folder_cnid,
- carbon_filename,
- self.target.cnid,
- int(crdate),
- self.target.creator_code,
- self.target.type_code,
- self.target.levels_from,
- self.target.levels_to,
- self.volume.attribute_flags,
- self.volume.fs_id,
- b'\0'*10))
-
- # Excuse the odd order; we're copying Finder
- if self.target.folder_name:
- carbon_foldername = encode_utf8(self.target.folder_name)\
- .replace(b':',b'/')
- b.write(struct.pack(b'>hh', TAG_CARBON_FOLDER_NAME,
- len(carbon_foldername)))
- b.write(carbon_foldername)
- if len(carbon_foldername) & 1:
- b.write(b'\0')
-
- b.write(struct.pack(b'>hhQhhQ',
- TAG_HIGH_RES_VOLUME_CREATION_DATE,
- 8, int(voldate * 65536),
- TAG_HIGH_RES_CREATION_DATE,
- 8, int(crdate * 65536)))
-
- if self.target.cnid_path:
- cnid_path = struct.pack('>%uI' % len(self.target.cnid_path),
- *self.target.cnid_path)
- b.write(struct.pack(b'>hh', TAG_CNID_PATH,
- len(cnid_path)))
- b.write(cnid_path)
-
- if self.target.carbon_path:
- carbon_path=encode_utf8(self.target.carbon_path)
- b.write(struct.pack(b'>hh', TAG_CARBON_PATH,
- len(carbon_path)))
- b.write(carbon_path)
- if len(carbon_path) & 1:
- b.write(b'\0')
-
- if self.volume.appleshare_info:
- ai = self.volume.appleshare_info
- if ai.zone:
- b.write(struct.pack(b'>hh', TAG_APPLESHARE_ZONE,
- len(ai.zone)))
- b.write(ai.zone)
- if len(ai.zone) & 1:
- b.write(b'\0')
- if ai.server:
- b.write(struct.pack(b'>hh', TAG_APPLESHARE_SERVER_NAME,
- len(ai.server)))
- b.write(ai.server)
- if len(ai.server) & 1:
- b.write(b'\0')
- if ai.username:
- b.write(struct.pack(b'>hh', TAG_APPLESHARE_USERNAME,
- len(ai.username)))
- b.write(ai.username)
- if len(ai.username) & 1:
- b.write(b'\0')
-
- if self.volume.driver_name:
- driver_name = encode_utf8(self.volume.driver_name)
- b.write(struct.pack(b'>hh', TAG_DRIVER_NAME,
- len(driver_name)))
- b.write(driver_name)
- if len(driver_name) & 1:
- b.write(b'\0')
-
- if self.volume.network_mount_info:
- b.write(struct.pack(b'>hh', TAG_NETWORK_MOUNT_INFO,
- len(self.volume.network_mount_info)))
- b.write(self.volume.network_mount_info)
- if len(self.volume.network_mount_info) & 1:
- b.write(b'\0')
-
- if self.volume.dialup_info:
- b.write(struct.pack(b'>hh', TAG_DIALUP_INFO,
- len(self.volume.network_mount_info)))
- b.write(self.volume.network_mount_info)
- if len(self.volume.network_mount_info) & 1:
- b.write(b'\0')
-
- utf16 = decode_utf8(self.target.filename)\
- .replace(':','/').encode('utf-16-be')
- b.write(struct.pack(b'>hhh', TAG_UNICODE_FILENAME,
- len(utf16) + 2,
- len(utf16) // 2))
- b.write(utf16)
-
- utf16 = decode_utf8(self.volume.name)\
- .replace(':','/').encode('utf-16-be')
- b.write(struct.pack(b'>hhh', TAG_UNICODE_VOLUME_NAME,
- len(utf16) + 2,
- len(utf16) // 2))
- b.write(utf16)
-
- if self.target.posix_path:
- posix_path = encode_utf8(self.target.posix_path)
- b.write(struct.pack(b'>hh', TAG_POSIX_PATH,
- len(posix_path)))
- b.write(posix_path)
- if len(posix_path) & 1:
- b.write(b'\0')
-
- if self.volume.posix_path:
- posix_path = encode_utf8(self.volume.posix_path)
- b.write(struct.pack(b'>hh', TAG_POSIX_PATH_TO_MOUNTPOINT,
- len(posix_path)))
- b.write(posix_path)
- if len(posix_path) & 1:
- b.write(b'\0')
-
- if self.volume.disk_image_alias:
- d = self.volume.disk_image_alias.to_bytes()
- b.write(struct.pack(b'>hh', TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE,
- len(d)))
- b.write(d)
- if len(d) & 1:
- b.write(b'\0')
-
- if self.target.user_home_prefix_len is not None:
- b.write(struct.pack(b'>hhh', TAG_USER_HOME_LENGTH_PREFIX,
- 2, self.target.user_home_prefix_len))
-
- for t,v in self.extra:
- b.write(struct.pack(b'>hh', t, len(v)))
- b.write(v)
- if len(v) & 1:
- b.write(b'\0')
-
- b.write(struct.pack(b'>hh', -1, 0))
-
- blen = b.tell() - pos
- b.seek(pos + 4, os.SEEK_SET)
- b.write(struct.pack(b'>h', blen))
-
- def to_bytes(self):
- """Returns the binary representation for this :class:`Alias`."""
- with io.BytesIO() as b:
- self._to_fd(b)
- return b.getvalue()
-
- def __str__(self):
- return '<Alias target=%s>' % self.target.filename
-
- def __repr__(self):
- values = []
- if self.appinfo != b'\0\0\0\0':
- values.append('appinfo=%r' % self.appinfo)
- if self.version != 2:
- values.append('version=%r' % self.version)
- if self.volume is not None:
- values.append('volume=%r' % self.volume)
- if self.target is not None:
- values.append('target=%r' % self.target)
- if self.extra:
- values.append('extra=%r' % self.extra)
- return 'Alias(%s)' % ','.join(values)
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/bookmark.py b/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/bookmark.py
deleted file mode 100644
index 0de6b9404..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/bookmark.py
+++ /dev/null
@@ -1,665 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# This file implements the Apple "bookmark" format, which is the replacement
-# for the old-fashioned alias format. The details of this format were
-# reverse engineered; some things are still not entirely clear.
-#
-from __future__ import unicode_literals, print_function
-
-import struct
-import uuid
-import datetime
-import os
-import sys
-import pprint
-
-try:
- from urlparse import urljoin
-except ImportError:
- from urllib.parse import urljoin
-
-if sys.platform == 'darwin':
- from . import osx
-
-def iteritems(x):
- return x.iteritems()
-
-try:
- unicode
-except NameError:
- unicode = str
- long = int
- xrange = range
- def iteritems(x):
- return x.items()
-
-from .utils import *
-
-BMK_DATA_TYPE_MASK = 0xffffff00
-BMK_DATA_SUBTYPE_MASK = 0x000000ff
-
-BMK_STRING = 0x0100
-BMK_DATA = 0x0200
-BMK_NUMBER = 0x0300
-BMK_DATE = 0x0400
-BMK_BOOLEAN = 0x0500
-BMK_ARRAY = 0x0600
-BMK_DICT = 0x0700
-BMK_UUID = 0x0800
-BMK_URL = 0x0900
-BMK_NULL = 0x0a00
-
-BMK_ST_ZERO = 0x0000
-BMK_ST_ONE = 0x0001
-
-BMK_BOOLEAN_ST_FALSE = 0x0000
-BMK_BOOLEAN_ST_TRUE = 0x0001
-
-# Subtypes for BMK_NUMBER are really CFNumberType values
-kCFNumberSInt8Type = 1
-kCFNumberSInt16Type = 2
-kCFNumberSInt32Type = 3
-kCFNumberSInt64Type = 4
-kCFNumberFloat32Type = 5
-kCFNumberFloat64Type = 6
-kCFNumberCharType = 7
-kCFNumberShortType = 8
-kCFNumberIntType = 9
-kCFNumberLongType = 10
-kCFNumberLongLongType = 11
-kCFNumberFloatType = 12
-kCFNumberDoubleType = 13
-kCFNumberCFIndexType = 14
-kCFNumberNSIntegerType = 15
-kCFNumberCGFloatType = 16
-
-# Resource property flags (from CFURLPriv.h)
-kCFURLResourceIsRegularFile = 0x00000001
-kCFURLResourceIsDirectory = 0x00000002
-kCFURLResourceIsSymbolicLink = 0x00000004
-kCFURLResourceIsVolume = 0x00000008
-kCFURLResourceIsPackage = 0x00000010
-kCFURLResourceIsSystemImmutable = 0x00000020
-kCFURLResourceIsUserImmutable = 0x00000040
-kCFURLResourceIsHidden = 0x00000080
-kCFURLResourceHasHiddenExtension = 0x00000100
-kCFURLResourceIsApplication = 0x00000200
-kCFURLResourceIsCompressed = 0x00000400
-kCFURLResourceIsSystemCompressed = 0x00000400
-kCFURLCanSetHiddenExtension = 0x00000800
-kCFURLResourceIsReadable = 0x00001000
-kCFURLResourceIsWriteable = 0x00002000
-kCFURLResourceIsExecutable = 0x00004000
-kCFURLIsAliasFile = 0x00008000
-kCFURLIsMountTrigger = 0x00010000
-
-# Volume property flags (from CFURLPriv.h)
-kCFURLVolumeIsLocal = 0x1 #
-kCFURLVolumeIsAutomount = 0x2 #
-kCFURLVolumeDontBrowse = 0x4 #
-kCFURLVolumeIsReadOnly = 0x8 #
-kCFURLVolumeIsQuarantined = 0x10
-kCFURLVolumeIsEjectable = 0x20 #
-kCFURLVolumeIsRemovable = 0x40 #
-kCFURLVolumeIsInternal = 0x80 #
-kCFURLVolumeIsExternal = 0x100 #
-kCFURLVolumeIsDiskImage = 0x200 #
-kCFURLVolumeIsFileVault = 0x400
-kCFURLVolumeIsLocaliDiskMirror = 0x800
-kCFURLVolumeIsiPod = 0x1000 #
-kCFURLVolumeIsiDisk = 0x2000
-kCFURLVolumeIsCD = 0x4000
-kCFURLVolumeIsDVD = 0x8000
-kCFURLVolumeIsDeviceFileSystem = 0x10000
-kCFURLVolumeSupportsPersistentIDs = 0x100000000
-kCFURLVolumeSupportsSearchFS = 0x200000000
-kCFURLVolumeSupportsExchange = 0x400000000
-# reserved 0x800000000
-kCFURLVolumeSupportsSymbolicLinks = 0x1000000000
-kCFURLVolumeSupportsDenyModes = 0x2000000000
-kCFURLVolumeSupportsCopyFile = 0x4000000000
-kCFURLVolumeSupportsReadDirAttr = 0x8000000000
-kCFURLVolumeSupportsJournaling = 0x10000000000
-kCFURLVolumeSupportsRename = 0x20000000000
-kCFURLVolumeSupportsFastStatFS = 0x40000000000
-kCFURLVolumeSupportsCaseSensitiveNames = 0x80000000000
-kCFURLVolumeSupportsCasePreservedNames = 0x100000000000
-kCFURLVolumeSupportsFLock = 0x200000000000
-kCFURLVolumeHasNoRootDirectoryTimes = 0x400000000000
-kCFURLVolumeSupportsExtendedSecurity = 0x800000000000
-kCFURLVolumeSupports2TBFileSize = 0x1000000000000
-kCFURLVolumeSupportsHardLinks = 0x2000000000000
-kCFURLVolumeSupportsMandatoryByteRangeLocks = 0x4000000000000
-kCFURLVolumeSupportsPathFromID = 0x8000000000000
-# reserved 0x10000000000000
-kCFURLVolumeIsJournaling = 0x20000000000000
-kCFURLVolumeSupportsSparseFiles = 0x40000000000000
-kCFURLVolumeSupportsZeroRuns = 0x80000000000000
-kCFURLVolumeSupportsVolumeSizes = 0x100000000000000
-kCFURLVolumeSupportsRemoteEvents = 0x200000000000000
-kCFURLVolumeSupportsHiddenFiles = 0x400000000000000
-kCFURLVolumeSupportsDecmpFSCompression = 0x800000000000000
-kCFURLVolumeHas64BitObjectIDs = 0x1000000000000000
-kCFURLVolumePropertyFlagsAll = 0xffffffffffffffff
-
-BMK_URL_ST_ABSOLUTE = 0x0001
-BMK_URL_ST_RELATIVE = 0x0002
-
-# Bookmark keys
-# = 0x1003
-kBookmarkPath = 0x1004 # Array of path components
-kBookmarkCNIDPath = 0x1005 # Array of CNIDs
-kBookmarkFileProperties = 0x1010 # (CFURL rp flags,
- # CFURL rp flags asked for,
- # 8 bytes NULL)
-kBookmarkFileName = 0x1020
-kBookmarkFileID = 0x1030
-kBookmarkFileCreationDate = 0x1040
-# = 0x1054 # ?
-# = 0x1055 # ?
-# = 0x1056 # ?
-# = 0x1101 # ?
-# = 0x1102 # ?
-kBookmarkTOCPath = 0x2000 # A list of (TOC id, ?) pairs
-kBookmarkVolumePath = 0x2002
-kBookmarkVolumeURL = 0x2005
-kBookmarkVolumeName = 0x2010
-kBookmarkVolumeUUID = 0x2011 # Stored (perversely) as a string
-kBookmarkVolumeSize = 0x2012
-kBookmarkVolumeCreationDate = 0x2013
-kBookmarkVolumeProperties = 0x2020 # (CFURL vp flags,
- # CFURL vp flags asked for,
- # 8 bytes NULL)
-kBookmarkVolumeIsRoot = 0x2030 # True if volume is FS root
-kBookmarkVolumeBookmark = 0x2040 # Embedded bookmark for disk image (TOC id)
-kBookmarkVolumeMountPoint = 0x2050 # A URL
-# = 0x2070
-kBookmarkContainingFolder = 0xc001 # Index of containing folder in path
-kBookmarkUserName = 0xc011 # User that created bookmark
-kBookmarkUID = 0xc012 # UID that created bookmark
-kBookmarkWasFileReference = 0xd001 # True if the URL was a file reference
-kBookmarkCreationOptions = 0xd010
-kBookmarkURLLengths = 0xe003 # See below
-# = 0xf017 # Localized name?
-# = 0xf022
-kBookmarkSecurityExtension = 0xf080
-# = 0xf081
-
-# kBookmarkURLLengths is an array that is set if the URL encoded by the
-# bookmark had a base URL; in that case, each entry is the length of the
-# base URL in question. Thus a URL
-#
-# file:///foo/bar/baz blam/blat.html
-#
-# will result in [3, 2], while the URL
-#
-# file:///foo bar/baz blam blat.html
-#
-# would result in [1, 2, 1, 1]
-
-
-class Data (object):
- def __init__(self, bytedata=None):
- #: The bytes, stored as a byte string
- self.bytes = bytes(bytedata)
-
- def __repr__(self):
- return 'Data(%r)' % self.bytes
-
-class URL (object):
- def __init__(self, base, rel=None):
- if rel is not None:
- #: The base URL, if any (a :class:`URL`)
- self.base = base
- #: The rest of the URL (a string)
- self.relative = rel
- else:
- self.base = None
- self.relative = base
-
- @property
- def absolute(self):
- """Return an absolute URL."""
- if self.base is None:
- return self.relative
- else:
- base_abs = self.base.absolute
- return urljoin(self.base.absolute, self.relative)
-
- def __repr__(self):
- return 'URL(%r)' % self.absolute
-
-class Bookmark (object):
- def __init__(self, tocs=None):
- if tocs is None:
- #: The TOCs for this Bookmark
- self.tocs = []
- else:
- self.tocs = tocs
-
- @classmethod
- def _get_item(cls, data, hdrsize, offset):
- offset += hdrsize
- if offset > len(data) - 8:
- raise ValueError('Offset out of range')
-
- length,typecode = struct.unpack(b'<II', data[offset:offset+8])
-
- if len(data) - offset < 8 + length:
- raise ValueError('Data item truncated')
-
- databytes = data[offset+8:offset+8+length]
-
- dsubtype = typecode & BMK_DATA_SUBTYPE_MASK
- dtype = typecode & BMK_DATA_TYPE_MASK
-
- if dtype == BMK_STRING:
- return databytes.decode('utf-8')
- elif dtype == BMK_DATA:
- return Data(databytes)
- elif dtype == BMK_NUMBER:
- if dsubtype == kCFNumberSInt8Type:
- return ord(databytes[0])
- elif dsubtype == kCFNumberSInt16Type:
- return struct.unpack(b'<h', databytes)[0]
- elif dsubtype == kCFNumberSInt32Type:
- return struct.unpack(b'<i', databytes)[0]
- elif dsubtype == kCFNumberSInt64Type:
- return struct.unpack(b'<q', databytes)[0]
- elif dsubtype == kCFNumberFloat32Type:
- return struct.unpack(b'<f', databytes)[0]
- elif dsubtype == kCFNumberFloat64Type:
- return struct.unpack(b'<d', databytes)[0]
- elif dtype == BMK_DATE:
- # Yes, dates really are stored as *BIG-endian* doubles; everything
- # else is little-endian
- secs = datetime.timedelta(seconds=struct.unpack(b'>d', databytes)[0])
- return osx_epoch + secs
- elif dtype == BMK_BOOLEAN:
- if dsubtype == BMK_BOOLEAN_ST_TRUE:
- return True
- elif dsubtype == BMK_BOOLEAN_ST_FALSE:
- return False
- elif dtype == BMK_UUID:
- return uuid.UUID(bytes=databytes)
- elif dtype == BMK_URL:
- if dsubtype == BMK_URL_ST_ABSOLUTE:
- return URL(databytes.decode('utf-8'))
- elif dsubtype == BMK_URL_ST_RELATIVE:
- baseoff,reloff = struct.unpack(b'<II', databytes)
- base = cls._get_item(data, hdrsize, baseoff)
- rel = cls._get_item(data, hdrsize, reloff)
- return URL(base, rel)
- elif dtype == BMK_ARRAY:
- result = []
- for aoff in xrange(offset+8,offset+8+length,4):
- eltoff, = struct.unpack(b'<I', data[aoff:aoff+4])
- result.append(cls._get_item(data, hdrsize, eltoff))
- return result
- elif dtype == BMK_DICT:
- result = {}
- for eoff in xrange(offset+8,offset+8+length,8):
- keyoff,valoff = struct.unpack(b'<II', data[eoff:eoff+8])
- key = cls._get_item(data, hdrsize, keyoff)
- val = cls._get_item(data, hdrsize, valoff)
- result[key] = val
- return result
- elif dtype == BMK_NULL:
- return None
-
- print('Unknown data type %08x' % typecode)
- return (typecode, databytes)
-
- @classmethod
- def from_bytes(cls, data):
- """Create a :class:`Bookmark` given byte data."""
-
- if len(data) < 16:
- raise ValueError('Not a bookmark file (too short)')
-
- if isinstance(data, bytearray):
- data = bytes(data)
-
- magic,size,dummy,hdrsize = struct.unpack(b'<4sIII', data[0:16])
-
- if magic != b'book':
- raise ValueError('Not a bookmark file (bad magic) %r' % magic)
-
- if hdrsize < 16:
- raise ValueError('Not a bookmark file (header size too short)')
-
- if hdrsize > size:
- raise ValueError('Not a bookmark file (header size too large)')
-
- if size != len(data):
- raise ValueError('Not a bookmark file (truncated)')
-
- tocoffset, = struct.unpack(b'<I', data[hdrsize:hdrsize+4])
-
- tocs = []
-
- while tocoffset != 0:
- tocbase = hdrsize + tocoffset
- if tocoffset > size - hdrsize \
- or size - tocbase < 20:
- raise ValueError('TOC offset out of range')
-
- tocsize,tocmagic,tocid,nexttoc,toccount \
- = struct.unpack(b'<IIIII',
- data[tocbase:tocbase+20])
-
- if tocmagic != 0xfffffffe:
- break
-
- tocsize += 8
-
- if size - tocbase < tocsize:
- raise ValueError('TOC truncated')
-
- if tocsize < 12 * toccount:
- raise ValueError('TOC entries overrun TOC size')
-
- toc = {}
- for n in xrange(0,toccount):
- ebase = tocbase + 20 + 12 * n
- eid,eoffset,edummy = struct.unpack(b'<III',
- data[ebase:ebase+12])
-
- if eid & 0x80000000:
- eid = cls._get_item(data, hdrsize, eid & 0x7fffffff)
-
- toc[eid] = cls._get_item(data, hdrsize, eoffset)
-
- tocs.append((tocid, toc))
-
- tocoffset = nexttoc
-
- return cls(tocs)
-
- def __getitem__(self, key):
- for tid,toc in self.tocs:
- if key in toc:
- return toc[key]
- raise KeyError('Key not found')
-
- def __setitem__(self, key, value):
- if len(self.tocs) == 0:
- self.tocs = [(1, {})]
- self.tocs[0][1][key] = value
-
- def get(self, key, default=None):
- """Lookup the value for a given key, returning a default if not
- present."""
- for tid,toc in self.tocs:
- if key in toc:
- return toc[key]
- return default
-
- @classmethod
- def _encode_item(cls, item, offset):
- if item is True:
- result = struct.pack(b'<II', 0, BMK_BOOLEAN | BMK_BOOLEAN_ST_TRUE)
- elif item is False:
- result = struct.pack(b'<II', 0, BMK_BOOLEAN | BMK_BOOLEAN_ST_FALSE)
- elif isinstance(item, unicode):
- encoded = item.encode('utf-8')
- result = (struct.pack(b'<II', len(encoded), BMK_STRING | BMK_ST_ONE)
- + encoded)
- elif isinstance(item, bytes):
- result = (struct.pack(b'<II', len(item), BMK_STRING | BMK_ST_ONE)
- + item)
- elif isinstance(item, Data):
- result = (struct.pack(b'<II', len(item.bytes),
- BMK_DATA | BMK_ST_ONE)
- + bytes(item.bytes))
- elif isinstance(item, bytearray):
- result = (struct.pack(b'<II', len(item),
- BMK_DATA | BMK_ST_ONE)
- + bytes(item))
- elif isinstance(item, int) or isinstance(item, long):
- if item > -0x80000000 and item < 0x7fffffff:
- result = struct.pack(b'<IIi', 4,
- BMK_NUMBER | kCFNumberSInt32Type, item)
- else:
- result = struct.pack(b'<IIq', 8,
- BMK_NUMBER | kCFNumberSInt64Type, item)
- elif isinstance(item, float):
- result = struct.pack(b'<IId', 8,
- BMK_NUMBER | kCFNumberFloat64Type, item)
- elif isinstance(item, datetime.datetime):
- secs = item - osx_epoch
- result = struct.pack(b'<II', 8, BMK_DATE | BMK_ST_ZERO) \
- + struct.pack(b'>d', float(secs.total_seconds()))
- elif isinstance(item, uuid.UUID):
- result = struct.pack(b'<II', 16, BMK_UUID | BMK_ST_ONE) \
- + item.bytes
- elif isinstance(item, URL):
- if item.base:
- baseoff = offset + 16
- reloff, baseenc = cls._encode_item(item.base, baseoff)
- xoffset, relenc = cls._encode_item(item.relative, reloff)
- result = b''.join([
- struct.pack(b'<IIII', 8, BMK_URL | BMK_URL_ST_RELATIVE,
- baseoff, reloff),
- baseenc,
- relenc])
- else:
- encoded = item.relative.encode('utf-8')
- result = struct.pack(b'<II', len(encoded),
- BMK_URL | BMK_URL_ST_ABSOLUTE) + encoded
- elif isinstance(item, list):
- ioffset = offset + 8 + len(item) * 4
- result = [struct.pack(b'<II', len(item) * 4, BMK_ARRAY | BMK_ST_ONE)]
- enc = []
- for elt in item:
- result.append(struct.pack(b'<I', ioffset))
- ioffset, ienc = cls._encode_item(elt, ioffset)
- enc.append(ienc)
- result = b''.join(result + enc)
- elif isinstance(item, dict):
- ioffset = offset + 8 + len(item) * 8
- result = [struct.pack(b'<II', len(item) * 8, BMK_DICT | BMK_ST_ONE)]
- enc = []
- for k,v in iteritems(item):
- result.append(struct.pack(b'<I', ioffset))
- ioffset, ienc = cls._encode_item(k, ioffset)
- enc.append(ienc)
- result.append(struct.pack(b'<I', ioffset))
- ioffset, ienc = cls._encode_item(v, ioffset)
- enc.append(ienc)
- result = b''.join(result + enc)
- elif item is None:
- result = struct.pack(b'<II', 0, BMK_NULL | BMK_ST_ONE)
- else:
- raise ValueError('Unknown item type when encoding: %s' % item)
-
- offset += len(result)
-
- # Pad to a multiple of 4 bytes
- if offset & 3:
- extra = 4 - (offset & 3)
- result += b'\0' * extra
- offset += extra
-
- return (offset, result)
-
- def to_bytes(self):
- """Convert this :class:`Bookmark` to a byte representation."""
-
- result = []
- tocs = []
- offset = 4 # For the offset to the first TOC
-
- # Generate the data and build the TOCs
- for tid,toc in self.tocs:
- entries = []
-
- for k,v in iteritems(toc):
- if isinstance(k, (str, unicode)):
- noffset = offset
- voffset, enc = self._encode_item(k, offset)
- result.append(enc)
- offset, enc = self._encode_item(v, voffset)
- result.append(enc)
- entries.append((noffset | 0x80000000, voffset))
- else:
- entries.append((k, offset))
- offset, enc = self._encode_item(v, offset)
- result.append(enc)
-
- # TOC entries must be sorted - CoreServicesInternal does a
- # binary search to find data
- entries.sort()
-
- tocs.append((tid, b''.join([struct.pack(b'<III',k,o,0)
- for k,o in entries])))
-
- first_toc_offset = offset
-
- # Now generate the TOC headers
- for ndx,toc in enumerate(tocs):
- tid, data = toc
- if ndx == len(tocs) - 1:
- next_offset = 0
- else:
- next_offset = offset + 20 + len(data)
-
- result.append(struct.pack(b'<IIIII', len(data) - 8,
- 0xfffffffe,
- tid,
- next_offset,
- len(data) // 12))
- result.append(data)
-
- offset += 20 + len(data)
-
- # Finally, add the header (and the first TOC offset, which isn't part
- # of the header, but goes just after it)
- header = struct.pack(b'<4sIIIQQQQI', b'book',
- offset + 48,
- 0x10040000,
- 48,
- 0, 0, 0, 0, first_toc_offset)
-
- result.insert(0, header)
-
- return b''.join(result)
-
- @classmethod
- def for_file(cls, path):
- """Construct a :class:`Bookmark` for a given file."""
-
- # Find the filesystem
- st = osx.statfs(path)
- vol_path = st.f_mntonname.decode('utf-8')
-
- # Grab its attributes
- attrs = [osx.ATTR_CMN_CRTIME,
- osx.ATTR_VOL_SIZE
- | osx.ATTR_VOL_NAME
- | osx.ATTR_VOL_UUID,
- 0, 0, 0]
- volinfo = osx.getattrlist(vol_path, attrs, 0)
-
- vol_crtime = volinfo[0]
- vol_size = volinfo[1]
- vol_name = volinfo[2]
- vol_uuid = volinfo[3]
-
- # Also grab various attributes of the file
- attrs = [(osx.ATTR_CMN_OBJTYPE
- | osx.ATTR_CMN_CRTIME
- | osx.ATTR_CMN_FILEID), 0, 0, 0, 0]
- info = osx.getattrlist(path, attrs, osx.FSOPT_NOFOLLOW)
-
- cnid = info[2]
- crtime = info[1]
-
- if info[0] == osx.VREG:
- flags = kCFURLResourceIsRegularFile
- elif info[0] == osx.VDIR:
- flags = kCFURLResourceIsDirectory
- elif info[0] == osx.VLNK:
- flags = kCFURLResourceIsSymbolicLink
- else:
- flags = kCFURLResourceIsRegularFile
-
- dirname, filename = os.path.split(path)
-
- relcount = 0
- if not os.path.isabs(dirname):
- curdir = os.getcwd()
- head, tail = os.path.split(curdir)
- relcount = 0
- while head and tail:
- relcount += 1
- head, tail = os.path.split(head)
- dirname = os.path.join(curdir, dirname)
-
- foldername = os.path.basename(dirname)
-
- rel_path = os.path.relpath(path, vol_path)
-
- # Build the path arrays
- name_path = []
- cnid_path = []
- head, tail = os.path.split(rel_path)
- if not tail:
- head, tail = os.path.split(head)
- while head or tail:
- if head:
- attrs = [osx.ATTR_CMN_FILEID, 0, 0, 0, 0]
- info = osx.getattrlist(os.path.join(vol_path, head), attrs, 0)
- cnid_path.insert(0, info[0])
- head, tail = os.path.split(head)
- name_path.insert(0, tail)
- else:
- head, tail = os.path.split(head)
- name_path.append(filename)
- cnid_path.append(cnid)
-
- url_lengths = [relcount, len(name_path) - relcount]
-
- fileprops = Data(struct.pack(b'<QQQ', flags, 0x0f, 0))
- volprops = Data(struct.pack(b'<QQQ', 0x81 | kCFURLVolumeSupportsPersistentIDs,
- 0x13ef | kCFURLVolumeSupportsPersistentIDs, 0))
-
- toc = {
- kBookmarkPath: name_path,
- kBookmarkCNIDPath: cnid_path,
- kBookmarkFileCreationDate: crtime,
- kBookmarkFileProperties: fileprops,
- kBookmarkContainingFolder: len(name_path) - 2,
- kBookmarkVolumePath: vol_path,
- kBookmarkVolumeIsRoot: vol_path == '/',
- kBookmarkVolumeURL: URL('file://' + vol_path),
- kBookmarkVolumeName: vol_name,
- kBookmarkVolumeSize: vol_size,
- kBookmarkVolumeCreationDate: vol_crtime,
- kBookmarkVolumeUUID: str(vol_uuid).upper(),
- kBookmarkVolumeProperties: volprops,
- kBookmarkCreationOptions: 512,
- kBookmarkWasFileReference: True,
- kBookmarkUserName: 'unknown',
- kBookmarkUID: 99,
- }
-
- if relcount:
- toc[kBookmarkURLLengths] = url_lengths
-
- return Bookmark([(1, toc)])
-
- def __repr__(self):
- result = ['Bookmark([']
- for tid,toc in self.tocs:
- result.append('(0x%x, {\n' % tid)
- for k,v in iteritems(toc):
- if isinstance(k, (str, unicode)):
- kf = repr(k)
- else:
- kf = '0x%04x' % k
- result.append(' %s: %r\n' % (kf, v))
- result.append('}),\n')
- result.append('])')
-
- return ''.join(result)
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/osx.py b/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/osx.py
deleted file mode 100644
index d4af65d75..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/osx.py
+++ /dev/null
@@ -1,827 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from ctypes import *
-import struct
-import os
-import datetime
-import uuid
-
-from .utils import *
-
-libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
-
-# Constants
-FSOPT_NOFOLLOW = 0x00000001
-FSOPT_NOINMEMUPDATE = 0x00000002
-FSOPT_REPORT_FULLSIZE = 0x00000004
-FSOPT_PACK_INVAL_ATTRS = 0x00000008
-
-VOL_CAPABILITIES_FORMAT = 0
-VOL_CAPABILITIES_INTERFACES = 1
-
-VOL_CAP_FMT_PERSISTENTOBJECTIDS = 0x00000001
-VOL_CAP_FMT_SYMBOLICLINKS = 0x00000002
-VOL_CAP_FMT_HARDLINKS = 0x00000004
-VOL_CAP_FMT_JOURNAL = 0x00000008
-VOL_CAP_FMT_JOURNAL_ACTIVE = 0x00000010
-VOL_CAP_FMT_NO_ROOT_TIMES = 0x00000020
-VOL_CAP_FMT_SPARSE_FILES = 0x00000040
-VOL_CAP_FMT_ZERO_RUNS = 0x00000080
-VOL_CAP_FMT_CASE_SENSITIVE = 0x00000100
-VOL_CAP_FMT_CASE_PRESERVING = 0x00000200
-VOL_CAP_FMT_FAST_STATFS = 0x00000400
-VOL_CAP_FMT_2TB_FILESIZE = 0x00000800
-VOL_CAP_FMT_OPENDENYMODES = 0x00001000
-VOL_CAP_FMT_HIDDEN_FILES = 0x00002000
-VOL_CAP_FMT_PATH_FROM_ID = 0x00004000
-VOL_CAP_FMT_NO_VOLUME_SIZES = 0x00008000
-VOL_CAP_FMT_DECMPFS_COMPRESSION = 0x00010000
-VOL_CAP_FMT_64BIT_OBJECT_IDS = 0x00020000
-
-VOL_CAP_INT_SEARCHFS = 0x00000001
-VOL_CAP_INT_ATTRLIST = 0x00000002
-VOL_CAP_INT_NFSEXPORT = 0x00000004
-VOL_CAP_INT_READDIRATTR = 0x00000008
-VOL_CAP_INT_EXCHANGEDATA = 0x00000010
-VOL_CAP_INT_COPYFILE = 0x00000020
-VOL_CAP_INT_ALLOCATE = 0x00000040
-VOL_CAP_INT_VOL_RENAME = 0x00000080
-VOL_CAP_INT_ADVLOCK = 0x00000100
-VOL_CAP_INT_FLOCK = 0x00000200
-VOL_CAP_INT_EXTENDED_SECURITY = 0x00000400
-VOL_CAP_INT_USERACCESS = 0x00000800
-VOL_CAP_INT_MANLOCK = 0x00001000
-VOL_CAP_INT_NAMEDSTREAMS = 0x00002000
-VOL_CAP_INT_EXTENDED_ATTR = 0x00004000
-
-ATTR_CMN_NAME = 0x00000001
-ATTR_CMN_DEVID = 0x00000002
-ATTR_CMN_FSID = 0x00000004
-ATTR_CMN_OBJTYPE = 0x00000008
-ATTR_CMN_OBJTAG = 0x00000010
-ATTR_CMN_OBJID = 0x00000020
-ATTR_CMN_OBJPERMANENTID = 0x00000040
-ATTR_CMN_PAROBJID = 0x00000080
-ATTR_CMN_SCRIPT = 0x00000100
-ATTR_CMN_CRTIME = 0x00000200
-ATTR_CMN_MODTIME = 0x00000400
-ATTR_CMN_CHGTIME = 0x00000800
-ATTR_CMN_ACCTIME = 0x00001000
-ATTR_CMN_BKUPTIME = 0x00002000
-ATTR_CMN_FNDRINFO = 0x00004000
-ATTR_CMN_OWNERID = 0x00008000
-ATTR_CMN_GRPID = 0x00010000
-ATTR_CMN_ACCESSMASK = 0x00020000
-ATTR_CMN_FLAGS = 0x00040000
-ATTR_CMN_USERACCESS = 0x00200000
-ATTR_CMN_EXTENDED_SECURITY = 0x00400000
-ATTR_CMN_UUID = 0x00800000
-ATTR_CMN_GRPUUID = 0x01000000
-ATTR_CMN_FILEID = 0x02000000
-ATTR_CMN_PARENTID = 0x04000000
-ATTR_CMN_FULLPATH = 0x08000000
-ATTR_CMN_ADDEDTIME = 0x10000000
-ATTR_CMN_RETURNED_ATTRS = 0x80000000
-ATTR_CMN_ALL_ATTRS = 0x9fe7ffff
-
-ATTR_VOL_FSTYPE = 0x00000001
-ATTR_VOL_SIGNATURE = 0x00000002
-ATTR_VOL_SIZE = 0x00000004
-ATTR_VOL_SPACEFREE = 0x00000008
-ATTR_VOL_SPACEAVAIL = 0x00000010
-ATTR_VOL_MINALLOCATION = 0x00000020
-ATTR_VOL_ALLOCATIONCLUMP = 0x00000040
-ATTR_VOL_IOBLOCKSIZE = 0x00000080
-ATTR_VOL_OBJCOUNT = 0x00000100
-ATTR_VOL_FILECOUNT = 0x00000200
-ATTR_VOL_DIRCOUNT = 0x00000400
-ATTR_VOL_MAXOBJCOUNT = 0x00000800
-ATTR_VOL_MOUNTPOINT = 0x00001000
-ATTR_VOL_NAME = 0x00002000
-ATTR_VOL_MOUNTFLAGS = 0x00004000
-ATTR_VOL_MOUNTEDDEVICE = 0x00008000
-ATTR_VOL_ENCODINGSUSED = 0x00010000
-ATTR_VOL_CAPABILITIES = 0x00020000
-ATTR_VOL_UUID = 0x00040000
-ATTR_VOL_ATTRIBUTES = 0x40000000
-ATTR_VOL_INFO = 0x80000000
-ATTR_VOL_ALL_ATTRS = 0xc007ffff
-
-ATTR_DIR_LINKCOUNT = 0x00000001
-ATTR_DIR_ENTRYCOUNT = 0x00000002
-ATTR_DIR_MOUNTSTATUS = 0x00000004
-DIR_MNTSTATUS_MNTPOINT = 0x00000001
-DIR_MNTSTATUS_TRIGGER = 0x00000002
-ATTR_DIR_ALL_ATTRS = 0x00000007
-
-ATTR_FILE_LINKCOUNT = 0x00000001
-ATTR_FILE_TOTALSIZE = 0x00000002
-ATTR_FILE_ALLOCSIZE = 0x00000004
-ATTR_FILE_IOBLOCKSIZE = 0x00000008
-ATTR_FILE_DEVTYPE = 0x00000020
-ATTR_FILE_DATALENGTH = 0x00000200
-ATTR_FILE_DATAALLOCSIZE = 0x00000400
-ATTR_FILE_RSRCLENGTH = 0x00001000
-ATTR_FILE_RSRCALLOCSIZE = 0x00002000
-
-ATTR_FILE_ALL_ATTRS = 0x0000362f
-
-ATTR_FORK_TOTALSIZE = 0x00000001
-ATTR_FORK_ALLOCSIZE = 0x00000002
-ATTR_FORK_ALL_ATTRS = 0x00000003
-
-# These can't be used
-ATTR_FILE_FORKCOUNT = 0x00000080
-ATTR_FILE_FORKLIST = 0x00000100
-ATTR_CMN_NAMEDATTRCOUNT = 0x00080000
-ATTR_CMN_NAMEDATTRLIST = 0x00100000
-ATTR_FILE_DATAEXTENTS = 0x00000800
-ATTR_FILE_RSRCEXTENTS = 0x00004000
-ATTR_FILE_CLUMPSIZE = 0x00000010
-ATTR_FILE_FILETYPE = 0x00000040
-
-class attrlist(Structure):
- _fields_ = [('bitmapcount', c_ushort),
- ('reserved', c_ushort),
- ('commonattr', c_uint),
- ('volattr', c_uint),
- ('dirattr', c_uint),
- ('fileattr', c_uint),
- ('forkattr', c_uint)]
-
-class attribute_set_t(Structure):
- _fields_ = [('commonattr', c_uint),
- ('volattr', c_uint),
- ('dirattr', c_uint),
- ('fileattr', c_uint),
- ('forkattr', c_uint)]
-
-class fsobj_id_t(Structure):
- _fields_ = [('fid_objno', c_uint),
- ('fid_generation', c_uint)]
-
-class timespec(Structure):
- _fields_ = [('tv_sec', c_long),
- ('tv_nsec', c_long)]
-
-class attrreference_t(Structure):
- _fields_ = [('attr_dataoffset', c_int),
- ('attr_length', c_uint)]
-
-class fsid_t(Structure):
- _fields_ = [('val', c_uint * 2)]
-
-class guid_t(Structure):
- _fields_ = [('g_guid', c_byte*16)]
-
-class kauth_ace(Structure):
- _fields_ = [('ace_applicable', guid_t),
- ('ace_flags', c_uint)]
-
-class kauth_acl(Structure):
- _fields_ = [('acl_entrycount', c_uint),
- ('acl_flags', c_uint),
- ('acl_ace', kauth_ace * 128)]
-
-class kauth_filesec(Structure):
- _fields_ = [('fsec_magic', c_uint),
- ('fsec_owner', guid_t),
- ('fsec_group', guid_t),
- ('fsec_acl', kauth_acl)]
-
-class diskextent(Structure):
- _fields_ = [('startblock', c_uint),
- ('blockcount', c_uint)]
-
-OSType = c_uint
-UInt16 = c_ushort
-SInt16 = c_short
-SInt32 = c_int
-
-class Point(Structure):
- _fields_ = [('x', SInt16),
- ('y', SInt16)]
-class Rect(Structure):
- _fields_ = [('x', SInt16),
- ('y', SInt16),
- ('w', SInt16),
- ('h', SInt16)]
-class FileInfo(Structure):
- _fields_ = [('fileType', OSType),
- ('fileCreator', OSType),
- ('finderFlags', UInt16),
- ('location', Point),
- ('reservedField', UInt16),
- ('reserved1', SInt16 * 4),
- ('extendedFinderFlags', UInt16),
- ('reserved2', SInt16),
- ('putAwayFolderID', SInt32)]
-class FolderInfo(Structure):
- _fields_ = [('windowBounds', Rect),
- ('finderFlags', UInt16),
- ('location', Point),
- ('reservedField', UInt16),
- ('scrollPosition', Point),
- ('reserved1', SInt32),
- ('extendedFinderFlags', UInt16),
- ('reserved2', SInt16),
- ('putAwayFolderID', SInt32)]
-class FinderInfo(Union):
- _fields_ = [('fileInfo', FileInfo),
- ('folderInfo', FolderInfo)]
-
-extentrecord = diskextent * 8
-
-vol_capabilities_set_t = c_uint * 4
-
-class vol_capabilities_attr_t(Structure):
- _fields_ = [('capabilities', vol_capabilities_set_t),
- ('valid', vol_capabilities_set_t)]
-
-class vol_attributes_attr_t(Structure):
- _fields_ = [('validattr', attribute_set_t),
- ('nativeattr', attribute_set_t)]
-
-dev_t = c_uint
-
-fsobj_type_t = c_uint
-
-VNON = 0
-VREG = 1
-VDIR = 2
-VBLK = 3
-VCHR = 4
-VLNK = 5
-VSOCK = 6
-VFIFO = 7
-VBAD = 8
-VSTR = 9
-VCPLX = 10
-
-fsobj_tag_t = c_uint
-
-VT_NON = 0
-VT_UFS = 1
-VT_NFS = 2
-VT_MFS = 3
-VT_MSDOSFS = 4
-VT_LFS = 5
-VT_LOFS = 6
-VT_FDESC = 7
-VT_PORTAL = 8
-VT_NULL = 9
-VT_UMAP = 10
-VT_KERNFS = 11
-VT_PROCFS = 12
-VT_AFS = 13
-VT_ISOFS = 14
-VT_UNION = 15
-VT_HFS = 16
-VT_ZFS = 17
-VT_DEVFS = 18
-VT_WEBDAV = 19
-VT_UDF = 20
-VT_AFP = 21
-VT_CDDA = 22
-VT_CIFS = 23
-VT_OTHER = 24
-
-fsfile_type_t = c_uint
-fsvolid_t = c_uint
-text_encoding_t = c_uint
-uid_t = c_uint
-gid_t = c_uint
-int32_t = c_int
-uint32_t = c_uint
-int64_t = c_longlong
-uint64_t = c_ulonglong
-off_t = c_long
-size_t = c_ulong
-uuid_t = c_byte*16
-
-NAME_MAX = 255
-PATH_MAX = 1024
-
-class struct_statfs(Structure):
- _fields_ = [('f_bsize', uint32_t),
- ('f_iosize', int32_t),
- ('f_blocks', uint64_t),
- ('f_bfree', uint64_t),
- ('f_bavail', uint64_t),
- ('f_files', uint64_t),
- ('f_ffree', uint64_t),
- ('f_fsid', fsid_t),
- ('f_owner', uid_t),
- ('f_type', uint32_t),
- ('f_flags', uint32_t),
- ('f_fssubtype', uint32_t),
- ('f_fstypename', c_char * 16),
- ('f_mntonname', c_char * PATH_MAX),
- ('f_mntfromname', c_char * PATH_MAX),
- ('f_reserved', uint32_t * 8)]
-
-# Calculate the maximum number of bytes required for the attribute buffer
-_attr_info = (
- # Common attributes
- (0, ATTR_CMN_RETURNED_ATTRS, sizeof(attribute_set_t)),
- (0, ATTR_CMN_NAME, sizeof(attrreference_t) + NAME_MAX * 3 + 1),
- (0, ATTR_CMN_DEVID, sizeof(dev_t)),
- (0, ATTR_CMN_FSID, sizeof(fsid_t)),
- (0, ATTR_CMN_OBJTYPE, sizeof(fsobj_type_t)),
- (0, ATTR_CMN_OBJTAG, sizeof(fsobj_tag_t)),
- (0, ATTR_CMN_OBJPERMANENTID, sizeof(fsobj_id_t)),
- (0, ATTR_CMN_PAROBJID, sizeof(fsobj_id_t)),
- (0, ATTR_CMN_SCRIPT, sizeof(text_encoding_t)),
- (0, ATTR_CMN_CRTIME, sizeof(timespec)),
- (0, ATTR_CMN_MODTIME, sizeof(timespec)),
- (0, ATTR_CMN_CHGTIME, sizeof(timespec)),
- (0, ATTR_CMN_ACCTIME, sizeof(timespec)),
- (0, ATTR_CMN_BKUPTIME, sizeof(timespec)),
- (0, ATTR_CMN_FNDRINFO, sizeof(FinderInfo)),
- (0, ATTR_CMN_OWNERID, sizeof(uid_t)),
- (0, ATTR_CMN_GRPID, sizeof(gid_t)),
- (0, ATTR_CMN_ACCESSMASK, sizeof(uint32_t)),
- (0, ATTR_CMN_NAMEDATTRCOUNT, None),
- (0, ATTR_CMN_NAMEDATTRLIST, None),
- (0, ATTR_CMN_FLAGS, sizeof(uint32_t)),
- (0, ATTR_CMN_USERACCESS, sizeof(uint32_t)),
- (0, ATTR_CMN_EXTENDED_SECURITY, sizeof(attrreference_t) + sizeof(kauth_filesec)),
- (0, ATTR_CMN_UUID, sizeof(guid_t)),
- (0, ATTR_CMN_GRPUUID, sizeof(guid_t)),
- (0, ATTR_CMN_FILEID, sizeof(uint64_t)),
- (0, ATTR_CMN_PARENTID, sizeof(uint64_t)),
- (0, ATTR_CMN_FULLPATH, sizeof(attrreference_t) + PATH_MAX),
- (0, ATTR_CMN_ADDEDTIME, sizeof(timespec)),
-
- # Volume attributes
- (1, ATTR_VOL_FSTYPE, sizeof(uint32_t)),
- (1, ATTR_VOL_SIGNATURE, sizeof(uint32_t)),
- (1, ATTR_VOL_SIZE, sizeof(off_t)),
- (1, ATTR_VOL_SPACEFREE, sizeof(off_t)),
- (1, ATTR_VOL_SPACEAVAIL, sizeof(off_t)),
- (1, ATTR_VOL_MINALLOCATION, sizeof(off_t)),
- (1, ATTR_VOL_ALLOCATIONCLUMP, sizeof(off_t)),
- (1, ATTR_VOL_IOBLOCKSIZE, sizeof(uint32_t)),
- (1, ATTR_VOL_OBJCOUNT, sizeof(uint32_t)),
- (1, ATTR_VOL_FILECOUNT, sizeof(uint32_t)),
- (1, ATTR_VOL_DIRCOUNT, sizeof(uint32_t)),
- (1, ATTR_VOL_MAXOBJCOUNT, sizeof(uint32_t)),
- (1, ATTR_VOL_MOUNTPOINT, sizeof(attrreference_t) + PATH_MAX),
- (1, ATTR_VOL_NAME, sizeof(attrreference_t) + NAME_MAX + 1),
- (1, ATTR_VOL_MOUNTFLAGS, sizeof(uint32_t)),
- (1, ATTR_VOL_MOUNTEDDEVICE, sizeof(attrreference_t) + PATH_MAX),
- (1, ATTR_VOL_ENCODINGSUSED, sizeof(c_ulonglong)),
- (1, ATTR_VOL_CAPABILITIES, sizeof(vol_capabilities_attr_t)),
- (1, ATTR_VOL_UUID, sizeof(uuid_t)),
- (1, ATTR_VOL_ATTRIBUTES, sizeof(vol_attributes_attr_t)),
-
- # Directory attributes
- (2, ATTR_DIR_LINKCOUNT, sizeof(uint32_t)),
- (2, ATTR_DIR_ENTRYCOUNT, sizeof(uint32_t)),
- (2, ATTR_DIR_MOUNTSTATUS, sizeof(uint32_t)),
-
- # File attributes
- (3, ATTR_FILE_LINKCOUNT, sizeof(uint32_t)),
- (3, ATTR_FILE_TOTALSIZE, sizeof(off_t)),
- (3, ATTR_FILE_ALLOCSIZE, sizeof(off_t)),
- (3, ATTR_FILE_IOBLOCKSIZE, sizeof(uint32_t)),
- (3, ATTR_FILE_CLUMPSIZE, sizeof(uint32_t)),
- (3, ATTR_FILE_DEVTYPE, sizeof(uint32_t)),
- (3, ATTR_FILE_FILETYPE, sizeof(uint32_t)),
- (3, ATTR_FILE_FORKCOUNT, sizeof(uint32_t)),
- (3, ATTR_FILE_FORKLIST, None),
- (3, ATTR_FILE_DATALENGTH, sizeof(off_t)),
- (3, ATTR_FILE_DATAALLOCSIZE, sizeof(off_t)),
- (3, ATTR_FILE_DATAEXTENTS, sizeof(extentrecord)),
- (3, ATTR_FILE_RSRCLENGTH, sizeof(off_t)),
- (3, ATTR_FILE_RSRCALLOCSIZE, sizeof(off_t)),
- (3, ATTR_FILE_RSRCEXTENTS, sizeof(extentrecord)),
-
- # Fork attributes
- (4, ATTR_FORK_TOTALSIZE, sizeof(off_t)),
- (4, ATTR_FORK_ALLOCSIZE, sizeof(off_t))
- )
-
-def _attrbuf_size(attrs):
- size = 4
- for entry in _attr_info:
- if attrs[entry[0]] & entry[1]:
- if entry[2] is None:
- raise ValueError('Unsupported attribute (%u, %x)'
- % (entry[0], entry[1]))
- size += entry[2]
- return size
-
-_getattrlist = libc.getattrlist
-_getattrlist.argtypes = [c_char_p, POINTER(attrlist), c_void_p, c_ulong, c_ulong]
-_getattrlist.restype = c_int
-
-_fgetattrlist = libc.fgetattrlist
-_fgetattrlist.argtypes = [c_int, POINTER(attrlist), c_void_p, c_ulong, c_ulong]
-_fgetattrlist.restype = c_int
-
-_statfs = libc['statfs$INODE64']
-_statfs.argtypes = [c_char_p, POINTER(struct_statfs)]
-_statfs.restype = c_int
-
-_fstatfs = libc['fstatfs$INODE64']
-_fstatfs.argtypes = [c_int, POINTER(struct_statfs)]
-_fstatfs.restype = c_int
-
-def _datetime_from_timespec(ts):
- td = datetime.timedelta(seconds=ts.tv_sec + 1.0e-9 * ts.tv_nsec)
- return unix_epoch + td
-
-def _decode_utf8_nul(sz):
- nul = sz.find(b'\0')
- if nul > -1:
- sz = sz[:nul]
- return sz.decode('utf-8')
-
-def _decode_attrlist_result(buf, attrs, options):
- result = []
-
- assert len(buf) >= 4
- total_size = uint32_t.from_buffer(buf, 0).value
- assert total_size <= len(buf)
-
- offset = 4
-
- # Common attributes
- if attrs[0] & ATTR_CMN_RETURNED_ATTRS:
- a = attribute_set_t.from_buffer(buf, offset)
- result.append(a)
- offset += sizeof (attribute_set_t)
- if not (options & FSOPT_PACK_INVAL_ATTRS):
- attrs = [a.commonattr, a.volattr, a.dirattr, a.fileattr, a.forkattr]
- if attrs[0] & ATTR_CMN_NAME:
- a = attrreference_t.from_buffer(buf, offset)
- ofs = offset + a.attr_dataoffset
- name = _decode_utf8_nul(buf[ofs:ofs+a.attr_length])
- offset += sizeof (attrreference_t)
- result.append(name)
- if attrs[0] & ATTR_CMN_DEVID:
- a = dev_t.from_buffer(buf, offset)
- offset += sizeof(dev_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_FSID:
- a = fsid_t.from_buffer(buf, offset)
- offset += sizeof(fsid_t)
- result.append(a)
- if attrs[0] & ATTR_CMN_OBJTYPE:
- a = fsobj_type_t.from_buffer(buf, offset)
- offset += sizeof(fsobj_type_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_OBJTAG:
- a = fsobj_tag_t.from_buffer(buf, offset)
- offset += sizeof(fsobj_tag_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_OBJID:
- a = fsobj_id_t.from_buffer(buf, offset)
- offset += sizeof(fsobj_id_t)
- result.append(a)
- if attrs[0] & ATTR_CMN_OBJPERMANENTID:
- a = fsobj_id_t.from_buffer(buf, offset)
- offset += sizeof(fsobj_id_t)
- result.append(a)
- if attrs[0] & ATTR_CMN_PAROBJID:
- a = fsobj_id_t.from_buffer(buf, offset)
- offset += sizeof(fsobj_id_t)
- result.append(a)
- if attrs[0] & ATTR_CMN_SCRIPT:
- a = text_encoding_t.from_buffer(buf, offset)
- offset += sizeof(text_encoding_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_CRTIME:
- a = timespec.from_buffer(buf, offset)
- offset += sizeof(timespec)
- result.append(_datetime_from_timespec(a))
- if attrs[0] & ATTR_CMN_MODTIME:
- a = timespec.from_buffer(buf, offset)
- offset += sizeof(timespec)
- result.append(_datetime_from_timespec(a))
- if attrs[0] & ATTR_CMN_CHGTIME:
- a = timespec.from_buffer(buf, offset)
- offset += sizeof(timespec)
- result.append(_datetime_from_timespec(a))
- if attrs[0] & ATTR_CMN_ACCTIME:
- a = timespec.from_buffer(buf, offset)
- offset += sizeof(timespec)
- result.append(_datetime_from_timespec(a))
- if attrs[0] & ATTR_CMN_BKUPTIME:
- a = timespec.from_buffer(buf, offset)
- offset += sizeof(timespec)
- result.append(_datetime_from_timespec(a))
- if attrs[0] & ATTR_CMN_FNDRINFO:
- a = FinderInfo.from_buffer(buf, offset)
- offset += sizeof(FinderInfo)
- result.append(a)
- if attrs[0] & ATTR_CMN_OWNERID:
- a = uid_t.from_buffer(buf, offset)
- offset += sizeof(uid_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_GRPID:
- a = gid_t.from_buffer(buf, offset)
- offset += sizeof(gid_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_ACCESSMASK:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_FLAGS:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_USERACCESS:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_EXTENDED_SECURITY:
- a = attrreference_t.from_buffer(buf, offset)
- ofs = offset + a.attr_dataoffset
- offset += sizeof(attrreference_t)
- ec = uint32_t.from_buffer(buf, ofs + 36).value
- class kauth_acl(Structure):
- _fields_ = [('acl_entrycount', c_uint),
- ('acl_flags', c_uint),
- ('acl_ace', kauth_ace * ec)]
- class kauth_filesec(Structure):
- _fields_ = [('fsec_magic', c_uint),
- ('fsec_owner', guid_t),
- ('fsec_group', guid_t),
- ('fsec_acl', kauth_acl)]
- a = kauth_filesec.from_buffer(buf, ofs)
- result.append(a)
- if attrs[0] & ATTR_CMN_UUID:
- result.append(uuid.UUID(bytes=buf[offset:offset+16]))
- offset += sizeof(guid_t)
- if attrs[0] & ATTR_CMN_GRPUUID:
- result.append(uuid.UUID(bytes=buf[offset:offset+16]))
- offset += sizeof(guid_t)
- if attrs[0] & ATTR_CMN_FILEID:
- a = uint64_t.from_buffer(buf, offset)
- offset += sizeof(uint64_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_PARENTID:
- a = uint64_t.from_buffer(buf, offset)
- offset += sizeof(uint64_t)
- result.append(a.value)
- if attrs[0] & ATTR_CMN_FULLPATH:
- a = attrreference_t.from_buffer(buf, offset)
- ofs = offset + a.attr_dataoffset
- path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length])
- offset += sizeof (attrreference_t)
- result.append(path)
- if attrs[0] & ATTR_CMN_ADDEDTIME:
- a = timespec.from_buffer(buf, offset)
- offset += sizeof(timespec)
- result.append(_datetime_from_timespec(a))
-
- # Volume attributes
- if attrs[1] & ATTR_VOL_FSTYPE:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_SIGNATURE:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_SIZE:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_SPACEFREE:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_SPACEAVAIL:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_MINALLOCATION:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_ALLOCATIONCLUMP:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_IOBLOCKSIZE:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_OBJCOUNT:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_FILECOUNT:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_DIRCOUNT:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_MAXOBJCOUNT:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_MOUNTPOINT:
- a = attrreference_t.from_buffer(buf, offset)
- ofs = offset + a.attr_dataoffset
- path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length])
- offset += sizeof (attrreference_t)
- result.append(path)
- if attrs[1] & ATTR_VOL_NAME:
- a = attrreference_t.from_buffer(buf, offset)
- ofs = offset + a.attr_dataoffset
- name = _decode_utf8_nul(buf[ofs:ofs+a.attr_length])
- offset += sizeof (attrreference_t)
- result.append(name)
- if attrs[1] & ATTR_VOL_MOUNTFLAGS:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_MOUNTEDDEVICE:
- a = attrreference_t.from_buffer(buf, offset)
- ofs = offset + a.attr_dataoffset
- path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length])
- offset += sizeof (attrreference_t)
- result.append(path)
- if attrs[1] & ATTR_VOL_ENCODINGSUSED:
- a = c_ulonglong.from_buffer(buf, offset)
- offset += sizeof(c_ulonglong)
- result.append(a.value)
- if attrs[1] & ATTR_VOL_CAPABILITIES:
- a = vol_capabilities_attr_t.from_buffer(buf, offset)
- offset += sizeof(vol_capabilities_attr_t)
- result.append(a)
- if attrs[1] & ATTR_VOL_UUID:
- result.append(uuid.UUID(bytes=buf[offset:offset+16]))
- offset += sizeof(uuid_t)
- if attrs[1] & ATTR_VOL_ATTRIBUTES:
- a = vol_attributes_attr_t.from_buffer(buf, offset)
- offset += sizeof(vol_attributes_attr_t)
- result.append(a)
-
- # Directory attributes
- if attrs[2] & ATTR_DIR_LINKCOUNT:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[2] & ATTR_DIR_ENTRYCOUNT:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[2] & ATTR_DIR_MOUNTSTATUS:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
-
- # File attributes
- if attrs[3] & ATTR_FILE_LINKCOUNT:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_TOTALSIZE:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_ALLOCSIZE:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_IOBLOCKSIZE:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_CLUMPSIZE:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_DEVTYPE:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_FILETYPE:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_FORKCOUNT:
- a = uint32_t.from_buffer(buf, offset)
- offset += sizeof(uint32_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_DATALENGTH:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_DATAALLOCSIZE:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_DATAEXTENTS:
- a = extentrecord.from_buffer(buf, offset)
- offset += sizeof(extentrecord)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_RSRCLENGTH:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_RSRCALLOCSIZE:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[3] & ATTR_FILE_RSRCEXTENTS:
- a = extentrecord.from_buffer(buf, offset)
- offset += sizeof(extentrecord)
- result.append(a.value)
-
- # Fork attributes
- if attrs[4] & ATTR_FORK_TOTALSIZE:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
- if attrs[4] & ATTR_FORK_ALLOCSIZE:
- a = off_t.from_buffer(buf, offset)
- offset += sizeof(off_t)
- result.append(a.value)
-
- return result
-
-# Sadly, ctypes.get_errno() seems not to work
-__error = libc.__error
-__error.restype = POINTER(c_int)
-
-def _get_errno():
- return __error().contents.value
-
-def getattrlist(path, attrs, options):
- if not isinstance(path, bytes):
- path = path.encode('utf-8')
- attrs = list(attrs)
- if attrs[1]:
- attrs[1] |= ATTR_VOL_INFO
- alist = attrlist(bitmapcount=5,
- commonattr=attrs[0],
- volattr=attrs[1],
- dirattr=attrs[2],
- fileattr=attrs[3],
- forkattr=attrs[4])
-
- bufsize = _attrbuf_size(attrs)
- buf = create_string_buffer(bufsize)
-
- ret = _getattrlist(path, byref(alist), buf, bufsize,
- options | FSOPT_REPORT_FULLSIZE)
-
- if ret < 0:
- err = _get_errno()
- raise OSError(err, os.strerror(err), path)
-
- return _decode_attrlist_result(buf, attrs, options)
-
-def fgetattrlist(fd, attrs, options):
- if hasattr(fd, 'fileno'):
- fd = fd.fileno()
- attrs = list(attrs)
- if attrs[1]:
- attrs[1] |= ATTR_VOL_INFO
- alist = attrlist(bitmapcount=5,
- commonattr=attrs[0],
- volattr=attrs[1],
- dirattr=attrs[2],
- fileattr=attrs[3],
- forkattr=attrs[4])
-
- bufsize = _attrbuf_size(attrs)
- buf = create_string_buffer(bufsize)
-
- ret = _fgetattrlist(fd, byref(alist), buf, bufsize,
- options | FSOPT_REPORT_FULLSIZE)
-
- if ret < 0:
- err = _get_errno()
- raise OSError(err, os.strerror(err))
-
- return _decode_attrlist_result(buf, attrs, options)
-
-def statfs(path):
- if not isinstance(path, bytes):
- path = path.encode('utf-8')
- result = struct_statfs()
- ret = _statfs(path, byref(result))
- if ret < 0:
- err = _get_errno()
- raise OSError(err, os.strerror(err), path)
- return result
-
-def fstatfs(fd):
- if hasattr(fd, 'fileno'):
- fd = fd.fileno()
- result = struct_statfs()
- ret = _fstatfs(fd, byref(result))
- if ret < 0:
- err = _get_errno()
- raise OSError(err, os.strerror(err))
- return result
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/utils.py b/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/utils.py
deleted file mode 100644
index 6a7d0a121..000000000
--- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/utils.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-import datetime
-
-ZERO = datetime.timedelta(0)
-class UTC (datetime.tzinfo):
- def utcoffset(self, dt):
- return ZERO
- def dst(self, dt):
- return ZERO
- def tzname(self, dt):
- return 'UTC'
-
-utc = UTC()
-mac_epoch = datetime.datetime(1904,1,1,0,0,0,0,utc)
-unix_epoch = datetime.datetime(1970,1,1,0,0,0,0,utc)
-osx_epoch = datetime.datetime(2001,1,1,0,0,0,0,utc)
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/LICENSE b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/LICENSE
index e91f4eb38..e91f4eb38 100644
--- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/LICENSE
+++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/LICENSE
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__init__.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__init__.py
new file mode 100644
index 000000000..5fba876a7
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__init__.py
@@ -0,0 +1,5 @@
+from .core import build_dmg
+
+__version__ = "1.6.0"
+
+__all__ = ["__version__", "build_dmg"]
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__main__.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__main__.py
new file mode 100644
index 000000000..ca712a183
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/__main__.py
@@ -0,0 +1,58 @@
+#! /usr/bin/env python
+import argparse
+
+from .core import build_dmg
+
+
+def main():
+ parser = argparse.ArgumentParser(description="Construct a disk image file.")
+ parser.add_argument(
+ "volume_name",
+ metavar="volume-name",
+ help="The name to give to the volume (this will appear in the title bar when the user mounts the disk image).",
+ )
+ parser.add_argument(
+ "filename",
+ metavar="output.dmg",
+ help="The filename of the disk image to create.",
+ )
+ parser.add_argument("-s", "--settings", help="The path of the settings file.")
+ parser.add_argument(
+ "-D",
+ dest="defines",
+ action="append",
+ default=[],
+ help="Define a value for the settings file (e.g. -Dfoo=bar).",
+ )
+ parser.add_argument(
+ "--no-hidpi",
+ dest="lookForHiDPI",
+ action="store_false",
+ default=True,
+ help="Do not search for HiDPI versions of the background image (if specified)",
+ )
+
+ args = parser.parse_args()
+
+ defines = {}
+ for d in args.defines:
+ k, v = d.split("=", 1)
+ k = k.strip()
+ v = v.strip()
+ if (v.startswith("'") and v.endswith("'")) or (
+ v.startswith('"') and v.endswith('"')
+ ):
+ v = v[1:-1]
+ defines[k] = v
+
+ build_dmg(
+ args.filename,
+ args.volume_name,
+ args.settings,
+ defines=defines,
+ lookForHiDPI=args.lookForHiDPI,
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/badge.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/badge.py
new file mode 100644
index 000000000..f9764da01
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/badge.py
@@ -0,0 +1,169 @@
+import math
+
+from Quartz import (
+ CFURLCreateWithFileSystemPath,
+ CGBitmapContextCreate,
+ CGBitmapContextCreateImage,
+ CGColorSpaceCreateWithName,
+ CGImageDestinationAddImage,
+ CGImageDestinationCreateWithURL,
+ CGImageDestinationFinalize,
+ CGImageSourceCopyPropertiesAtIndex,
+ CGImageSourceCreateImageAtIndex,
+ CGImageSourceCreateWithURL,
+ CGImageSourceGetCount,
+ CIContext,
+ CIFilter,
+ CIImage,
+ CIVector,
+ NSAffineTransform,
+ kCFURLPOSIXPathStyle,
+ kCGColorSpaceGenericRGB,
+ kCGImageAlphaPremultipliedLast,
+ kCIInputAspectRatioKey,
+ kCIInputBackgroundImageKey,
+ kCIInputImageKey,
+ kCIInputScaleKey,
+ kCIOutputImageKey,
+)
+
+_REMOVABLE_DISK_PATH = (
+ "/System/Library/Extensions/IOStorageFamily.kext/Contents/Resources/Removable.icns"
+)
+
+
+def badge_disk_icon(badge_file, output_file):
+ # Load the Removable disk icon
+ url = CFURLCreateWithFileSystemPath(
+ None, _REMOVABLE_DISK_PATH, kCFURLPOSIXPathStyle, False
+ )
+ backdrop = CGImageSourceCreateWithURL(url, None)
+ backdropCount = CGImageSourceGetCount(backdrop)
+
+ # Load the badge
+ url = CFURLCreateWithFileSystemPath(None, badge_file, kCFURLPOSIXPathStyle, False)
+ badge = CGImageSourceCreateWithURL(url, None)
+ assert badge is not None, "Unable to process image file: %s" % badge_file
+ badgeCount = CGImageSourceGetCount(badge)
+
+ # Set up a destination for our target
+ url = CFURLCreateWithFileSystemPath(None, output_file, kCFURLPOSIXPathStyle, False)
+ target = CGImageDestinationCreateWithURL(url, "com.apple.icns", backdropCount, None)
+
+ # Get the RGB colorspace
+ rgbColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB)
+
+ # Scale
+ scale = 1.0
+
+ # Perspective transform
+ corners = ((0.2, 0.95), (0.8, 0.95), (0.85, 0.35), (0.15, 0.35))
+
+ # Translation
+ position = (0.5, 0.5)
+
+ for n in range(backdropCount):
+ props = CGImageSourceCopyPropertiesAtIndex(backdrop, n, None)
+ width = props["PixelWidth"]
+ height = props["PixelHeight"]
+ dpi = props["DPIWidth"]
+ depth = props["Depth"]
+
+ # Choose the best sized badge image
+ bestWidth = None
+ # ?? bestHeight = None
+ bestBadge = None
+ bestDepth = None
+ # ?? bestDPI = None
+ for m in range(badgeCount):
+ badgeProps = CGImageSourceCopyPropertiesAtIndex(badge, m, None)
+ badgeWidth = badgeProps["PixelWidth"]
+ # ?? badgeHeight = badgeProps['PixelHeight']
+ badgeDPI = badgeProps["DPIWidth"]
+ badgeDepth = badgeProps["Depth"]
+
+ if bestBadge is None or (
+ badgeWidth <= width
+ and (
+ bestWidth > width
+ or badgeWidth > bestWidth
+ or (
+ badgeWidth == bestWidth
+ and badgeDPI == dpi
+ and badgeDepth <= depth
+ and (bestDepth is None or badgeDepth > bestDepth)
+ )
+ )
+ ):
+ bestBadge = m
+ bestWidth = badgeWidth
+ # ?? bestHeight = badgeHeight
+ # ?? bestDPI = badgeDPI
+ bestDepth = badgeDepth
+
+ badgeImage = CGImageSourceCreateImageAtIndex(badge, bestBadge, None)
+ badgeCI = CIImage.imageWithCGImage_(badgeImage)
+
+ backgroundImage = CGImageSourceCreateImageAtIndex(backdrop, n, None)
+ backgroundCI = CIImage.imageWithCGImage_(backgroundImage)
+
+ compositor = CIFilter.filterWithName_("CISourceOverCompositing")
+ lanczos = CIFilter.filterWithName_("CILanczosScaleTransform")
+ perspective = CIFilter.filterWithName_("CIPerspectiveTransform")
+ transform = CIFilter.filterWithName_("CIAffineTransform")
+
+ lanczos.setValue_forKey_(badgeCI, kCIInputImageKey)
+ lanczos.setValue_forKey_(scale * float(width) / bestWidth, kCIInputScaleKey)
+ lanczos.setValue_forKey_(1.0, kCIInputAspectRatioKey)
+
+ topLeft = (width * scale * corners[0][0], width * scale * corners[0][1])
+ topRight = (width * scale * corners[1][0], width * scale * corners[1][1])
+ bottomRight = (width * scale * corners[2][0], width * scale * corners[2][1])
+ bottomLeft = (width * scale * corners[3][0], width * scale * corners[3][1])
+
+ out = lanczos.valueForKey_(kCIOutputImageKey)
+ if width >= 16:
+ perspective.setValue_forKey_(out, kCIInputImageKey)
+ perspective.setValue_forKey_(
+ CIVector.vectorWithX_Y_(*topLeft), "inputTopLeft"
+ )
+ perspective.setValue_forKey_(
+ CIVector.vectorWithX_Y_(*topRight), "inputTopRight"
+ )
+ perspective.setValue_forKey_(
+ CIVector.vectorWithX_Y_(*bottomRight), "inputBottomRight"
+ )
+ perspective.setValue_forKey_(
+ CIVector.vectorWithX_Y_(*bottomLeft), "inputBottomLeft"
+ )
+ out = perspective.valueForKey_(kCIOutputImageKey)
+
+ tfm = NSAffineTransform.transform()
+ tfm.translateXBy_yBy_(
+ math.floor((position[0] - 0.5 * scale) * width),
+ math.floor((position[1] - 0.5 * scale) * height),
+ )
+
+ transform.setValue_forKey_(out, kCIInputImageKey)
+ transform.setValue_forKey_(tfm, "inputTransform")
+ out = transform.valueForKey_(kCIOutputImageKey)
+
+ compositor.setValue_forKey_(out, kCIInputImageKey)
+ compositor.setValue_forKey_(backgroundCI, kCIInputBackgroundImageKey)
+
+ result = compositor.valueForKey_(kCIOutputImageKey)
+
+ cgContext = CGBitmapContextCreate(
+ None, width, height, 8, 0, rgbColorSpace, kCGImageAlphaPremultipliedLast
+ )
+ context = CIContext.contextWithCGContext_options_(cgContext, None)
+
+ context.drawImage_inRect_fromRect_(
+ result, ((0, 0), (width, height)), ((0, 0), (width, height))
+ )
+
+ image = CGBitmapContextCreateImage(cgContext)
+
+ CGImageDestinationAddImage(target, image, props)
+
+ CGImageDestinationFinalize(target)
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/colors.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/colors.py
new file mode 100644
index 000000000..8cab342a7
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/colors.py
@@ -0,0 +1,504 @@
+import math
+import re
+
+
+class Color:
+ def to_rgb(self):
+ raise Exception("Must implement to_rgb() in subclasses")
+
+
+class RGB:
+ def __init__(self, r, g, b):
+ self.r = r
+ self.g = g
+ self.b = b
+
+ def to_rgb(self):
+ return self
+
+
+class HSL(Color):
+ def __init__(self, h, s, l): # noqa; E741
+ self.h = h
+ self.s = s
+ self.l = l # noqa; E741
+
+ @staticmethod
+ def _hue_to_rgb(t1, t2, hue):
+ if hue < 0:
+ hue += 6
+ elif hue >= 6:
+ hue -= 6
+
+ if hue < 1:
+ return (t2 - t1) * hue + t1
+ elif hue < 3:
+ return t2
+ elif hue < 4:
+ return (t2 - t1) * (4 - hue) + t1
+ else:
+ return t1
+
+ def to_rgb(self):
+ hue = self.h / 60.0
+ if self.l <= 0.5: # noqa; E741
+ t2 = self.l * (self.s + 1)
+ else:
+ t2 = self.l + self.s - (self.l * self.s)
+ t1 = self.l * 2 - t2
+ r = self._hue_to_rgb(t1, t2, hue + 2)
+ g = self._hue_to_rgb(t1, t2, hue)
+ b = self._hue_to_rgb(t1, t2, hue - 2)
+ return RGB(r, g, b)
+
+
+class HWB(Color):
+ def __init__(self, h, w, b):
+ self.h = h
+ self.w = w
+ self.b = b
+
+ @staticmethod
+ def _hue_to_rgb(hue):
+ if hue < 0:
+ hue += 6
+ elif hue >= 6:
+ hue -= 6
+
+ if hue < 1:
+ return hue
+ elif hue < 3:
+ return 1
+ elif hue < 4:
+ return 4 - hue
+ else:
+ return 0
+
+ def to_rgb(self):
+ hue = self.h / 60.0
+ t1 = 1 - self.w - self.b
+ r = self._hue_to_rgb(hue + 2) * t1 + self.w
+ g = self._hue_to_rgb(hue) * t1 + self.w
+ b = self._hue_to_rgb(hue - 2) * t1 + self.w
+ return RGB(r, g, b)
+
+
+class CMYK(Color):
+ def __init__(self, c, m, y, k):
+ self.c = c
+ self.m = m
+ self.y = y
+ self.k = k
+
+ def to_rgb(self):
+ r = 1.0 - min(1.0, self.c + self.k)
+ g = 1.0 - min(1.0, self.m + self.k)
+ b = 1.0 - min(1.0, self.y + self.k)
+ return RGB(r, g, b)
+
+
+class Gray(Color):
+ def __init__(self, g):
+ self.g = g
+
+ def to_rgb(self):
+ return RGB(self.g, self.g, self.g)
+
+
+_x11_colors = {
+ "aliceblue": (240, 248, 255),
+ "antiquewhite": (250, 235, 215),
+ "aqua": (0, 255, 255),
+ "aquamarine": (127, 255, 212),
+ "azure": (240, 255, 255),
+ "beige": (245, 245, 220),
+ "bisque": (255, 228, 196),
+ "black": (0, 0, 0),
+ "blanchedalmond": (255, 235, 205),
+ "blue": (0, 0, 255),
+ "blueviolet": (138, 43, 226),
+ "brown": (165, 42, 42),
+ "burlywood": (222, 184, 135),
+ "cadetblue": (95, 158, 160),
+ "chartreuse": (127, 255, 0),
+ "chocolate": (210, 105, 30),
+ "coral": (255, 127, 80),
+ "cornflowerblue": (100, 149, 237),
+ "cornsilk": (255, 248, 220),
+ "crimson": (220, 20, 60),
+ "cyan": (0, 255, 255),
+ "darkblue": (0, 0, 139),
+ "darkcyan": (0, 139, 139),
+ "darkgoldenrod": (184, 134, 11),
+ "darkgray": (169, 169, 169),
+ "darkgreen": (0, 100, 0),
+ "darkgrey": (169, 169, 169),
+ "darkkhaki": (189, 183, 107),
+ "darkmagenta": (139, 0, 139),
+ "darkolivegreen": (85, 107, 47),
+ "darkorange": (255, 140, 0),
+ "darkorchid": (153, 50, 204),
+ "darkred": (139, 0, 0),
+ "darksalmon": (233, 150, 122),
+ "darkseagreen": (143, 188, 143),
+ "darkslateblue": (72, 61, 139),
+ "darkslategray": (47, 79, 79),
+ "darkslategrey": (47, 79, 79),
+ "darkturquoise": (0, 206, 209),
+ "darkviolet": (148, 0, 211),
+ "deeppink": (255, 20, 147),
+ "deepskyblue": (0, 191, 255),
+ "dimgray": (105, 105, 105),
+ "dimgrey": (105, 105, 105),
+ "dodgerblue": (30, 144, 255),
+ "firebrick": (178, 34, 34),
+ "floralwhite": (255, 250, 240),
+ "forestgreen": (34, 139, 34),
+ "fuchsia": (255, 0, 255),
+ "gainsboro": (220, 220, 220),
+ "ghostwhite": (248, 248, 255),
+ "gold": (255, 215, 0),
+ "goldenrod": (218, 165, 32),
+ "gray": (128, 128, 128),
+ "grey": (128, 128, 128),
+ "green": (0, 128, 0),
+ "greenyellow": (173, 255, 47),
+ "honeydew": (240, 255, 240),
+ "hotpink": (255, 105, 180),
+ "indianred": (205, 92, 92),
+ "indigo": (75, 0, 130),
+ "ivory": (255, 255, 240),
+ "khaki": (240, 230, 140),
+ "lavender": (230, 230, 250),
+ "lavenderblush": (255, 240, 245),
+ "lawngreen": (124, 252, 0),
+ "lemonchiffon": (255, 250, 205),
+ "lightblue": (173, 216, 230),
+ "lightcoral": (240, 128, 128),
+ "lightcyan": (224, 255, 255),
+ "lightgoldenrodyellow": (250, 250, 210),
+ "lightgray": (211, 211, 211),
+ "lightgreen": (144, 238, 144),
+ "lightgrey": (211, 211, 211),
+ "lightpink": (255, 182, 193),
+ "lightsalmon": (255, 160, 122),
+ "lightseagreen": (32, 178, 170),
+ "lightskyblue": (135, 206, 250),
+ "lightslategray": (119, 136, 153),
+ "lightslategrey": (119, 136, 153),
+ "lightsteelblue": (176, 196, 222),
+ "lightyellow": (255, 255, 224),
+ "lime": (0, 255, 0),
+ "limegreen": (50, 205, 50),
+ "linen": (250, 240, 230),
+ "magenta": (255, 0, 255),
+ "maroon": (128, 0, 0),
+ "mediumaquamarine": (102, 205, 170),
+ "mediumblue": (0, 0, 205),
+ "mediumorchid": (186, 85, 211),
+ "mediumpurple": (147, 112, 219),
+ "mediumseagreen": (60, 179, 113),
+ "mediumslateblue": (123, 104, 238),
+ "mediumspringgreen": (0, 250, 154),
+ "mediumturquoise": (72, 209, 204),
+ "mediumvioletred": (199, 21, 133),
+ "midnightblue": (25, 25, 112),
+ "mintcream": (245, 255, 250),
+ "mistyrose": (255, 228, 225),
+ "moccasin": (255, 228, 181),
+ "navajowhite": (255, 222, 173),
+ "navy": (0, 0, 128),
+ "oldlace": (253, 245, 230),
+ "olive": (128, 128, 0),
+ "olivedrab": (107, 142, 35),
+ "orange": (255, 165, 0),
+ "orangered": (255, 69, 0),
+ "orchid": (218, 112, 214),
+ "palegoldenrod": (238, 232, 170),
+ "palegreen": (152, 251, 152),
+ "paleturquoise": (175, 238, 238),
+ "palevioletred": (219, 112, 147),
+ "papayawhip": (255, 239, 213),
+ "peachpuff": (255, 218, 185),
+ "peru": (205, 133, 63),
+ "pink": (255, 192, 203),
+ "plum": (221, 160, 221),
+ "powderblue": (176, 224, 230),
+ "purple": (128, 0, 128),
+ "red": (255, 0, 0),
+ "rosybrown": (188, 143, 143),
+ "royalblue": (65, 105, 225),
+ "saddlebrown": (139, 69, 19),
+ "salmon": (250, 128, 114),
+ "sandybrown": (244, 164, 96),
+ "seagreen": (46, 139, 87),
+ "seashell": (255, 245, 238),
+ "sienna": (160, 82, 45),
+ "silver": (192, 192, 192),
+ "skyblue": (135, 206, 235),
+ "slateblue": (106, 90, 205),
+ "slategray": (112, 128, 144),
+ "slategrey": (112, 128, 144),
+ "snow": (255, 250, 250),
+ "springgreen": (0, 255, 127),
+ "steelblue": (70, 130, 180),
+ "tan": (210, 180, 140),
+ "teal": (0, 128, 128),
+ "thistle": (216, 191, 216),
+ "tomato": (255, 99, 71),
+ "turquoise": (64, 224, 208),
+ "violet": (238, 130, 238),
+ "wheat": (245, 222, 179),
+ "white": (255, 255, 255),
+ "whitesmoke": (245, 245, 245),
+ "yellow": (255, 255, 0),
+ "yellowgreen": (154, 205, 50),
+}
+
+_ws_re = re.compile(r"\s+")
+_token_re = re.compile(r"[A-Za-z_][A-Za-z0-9_]*")
+_hex_re = re.compile(r"#([0-9a-f]{3}(?:[0-9a-f]{3})?)$")
+_number_re = re.compile(r"[0-9]*(\.[0-9]*)")
+
+
+class ColorParser:
+ def __init__(self, s):
+ self._string = s
+ self._pos = 0
+
+ def skipws(self):
+ m = _ws_re.match(self._string, self._pos)
+ if m:
+ self._pos = m.end(0)
+
+ def expect(self, s, context=""):
+ if (
+ len(self._string) - self._pos < len(s)
+ or self._string[self._pos : self._pos + len(s)] != s
+ ):
+ raise ValueError(f'bad color "{self._string}" - expected "{s}"{context}')
+ self._pos += len(s)
+
+ def expectEnd(self):
+ if self._pos != len(self._string):
+ raise ValueError('junk at end of color "%s"' % self._string)
+
+ def getToken(self):
+ m = _token_re.match(self._string, self._pos)
+ if m:
+ token = m.group(0)
+
+ self._pos = m.end(0)
+ return token
+ return None
+
+ def parseNumber(self, context=""):
+ m = _number_re.match(self._string, self._pos)
+ if m:
+ self._pos = m.end(0)
+ return float(m.group(0))
+ raise ValueError(f'bad color "{self._string}" - expected a number{context}')
+
+ def parseColor(self):
+ self.skipws()
+
+ token = self.getToken()
+ if token:
+ if token == "rgb":
+ return self.parseRGB()
+ elif token == "hsl":
+ return self.parseHSL()
+ elif token == "hwb":
+ return self.parseHWB()
+ elif token == "cmyk":
+ return self.parseCMYK()
+ elif token == "gray" or token == "grey":
+ return self.parseGray()
+
+ try:
+ r, g, b = _x11_colors[token]
+ except KeyError:
+ raise ValueError('unknown color name "%s"' % token)
+
+ self.expectEnd()
+
+ return RGB(r / 255.0, g / 255.0, b / 255.0)
+
+ m = _hex_re.match(self._string, self._pos)
+ if m:
+ hrgb = m.group(1)
+
+ if len(hrgb) == 3:
+ r = int("0x" + 2 * hrgb[0], 16)
+ g = int("0x" + 2 * hrgb[1], 16)
+ b = int("0x" + 2 * hrgb[2], 16)
+ else:
+ r = int("0x" + hrgb[0:2], 16)
+ g = int("0x" + hrgb[2:4], 16)
+ b = int("0x" + hrgb[4:6], 16)
+
+ self._pos = m.end(0)
+ self.skipws()
+
+ self.expectEnd()
+
+ return RGB(r / 255.0, g / 255.0, b / 255.0)
+
+ raise ValueError('bad color syntax "%s"' % self._string)
+
+ def parseRGB(self):
+ self.expect("(", 'after "rgb"')
+ self.skipws()
+
+ r = self.parseValue()
+
+ self.skipws()
+ self.expect(",", 'in "rgb"')
+ self.skipws()
+
+ g = self.parseValue()
+
+ self.skipws()
+ self.expect(",", 'in "rgb"')
+ self.skipws()
+
+ b = self.parseValue()
+
+ self.skipws()
+ self.expect(")", 'at end of "rgb"')
+
+ self.skipws()
+ self.expectEnd()
+
+ return RGB(r, g, b)
+
+ def parseHSL(self):
+ self.expect("(", 'after "hsl"')
+ self.skipws()
+
+ h = self.parseAngle()
+
+ self.skipws()
+ self.expect(",", 'in "hsl"')
+ self.skipws()
+
+ s = self.parseValue()
+
+ self.skipws()
+ self.expect(",", 'in "hsl"')
+ self.skipws()
+
+ l = self.parseValue() # noqa; E741
+
+ self.skipws()
+ self.expect(")", 'at end of "hsl"')
+
+ self.skipws()
+ self.expectEnd()
+
+ return HSL(h, s, l)
+
+ def parseHWB(self):
+ self.expect("(", 'after "hwb"')
+ self.skipws()
+
+ h = self.parseAngle()
+
+ self.skipws()
+ self.expect(",", 'in "hwb"')
+ self.skipws()
+
+ w = self.parseValue()
+
+ self.skipws()
+ self.expect(",", 'in "hwb"')
+ self.skipws()
+
+ b = self.parseValue()
+
+ self.skipws()
+ self.expect(")", 'at end of "hwb"')
+
+ self.skipws()
+ self.expectEnd()
+
+ return HWB(h, w, b)
+
+ def parseCMYK(self):
+ self.expect("(", 'after "cmyk"')
+ self.skipws()
+
+ c = self.parseValue()
+
+ self.skipws()
+ self.expect(",", 'in "cmyk"')
+ self.skipws()
+
+ m = self.parseValue()
+
+ self.skipws()
+ self.expect(",", 'in "cmyk"')
+ self.skipws()
+
+ y = self.parseValue()
+
+ self.skipws()
+ self.expect(",", 'in "cmyk"')
+ self.skipws()
+
+ k = self.parseValue()
+
+ self.skipws()
+ self.expect(")", 'at end of "cmyk"')
+
+ self.skipws()
+ self.expectEnd()
+
+ return CMYK(c, m, y, k)
+
+ def parseGray(self):
+ self.expect("(", 'after "gray"')
+ self.skipws()
+
+ g = self.parseValue()
+
+ self.skipws()
+ self.expect(")", 'at end of "gray')
+
+ self.skipws()
+ self.expectEnd()
+
+ return Gray(g)
+
+ def parseValue(self):
+ n = self.parseNumber()
+ self.skipws()
+ if self._string[self._pos] == "%":
+ n = n / 100.0
+ self.pos += 1
+ return n
+
+ def parseAngle(self):
+ n = self.parseNumber()
+ self.skipws()
+ tok = self.getToken()
+ if tok == "rad":
+ n = n * 180.0 / math.pi
+ elif tok == "grad" or tok == "gon":
+ n = n * 0.9
+ elif tok != "deg":
+ raise ValueError('bad angle unit "%s"' % tok)
+ return n
+
+
+_color_re = re.compile(
+ r"\s*(#|rgb|hsl|hwb|cmyk|gray|grey|%s)" % "|".join(_x11_colors.keys())
+)
+
+
+def isAColor(s):
+ return _color_re.match(s)
+
+
+def parseColor(s):
+ return ColorParser(s).parseColor()
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/core.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/core.py
new file mode 100644
index 000000000..b514f77e9
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/core.py
@@ -0,0 +1,943 @@
+import json
+import os
+import platform
+import plistlib
+import re
+import shutil
+import subprocess
+import tempfile
+import time
+import tokenize
+
+try:
+ import importlib_resources as resources
+except ImportError:
+ from importlib import resources
+
+from ds_store import DSStore
+from mac_alias import Alias, Bookmark
+
+from . import colors, licensing
+
+try:
+ from . import badge
+except ImportError:
+ badge = None
+
+
+_hexcolor_re = re.compile(r"#[0-9a-f]{3}(?:[0-9a-f]{3})?")
+
+# The first element in the platform.mac_ver() tuple is a string containing the
+# macOS version (e.g., '10.15.6'). Parse into an integer tuple.
+MACOS_VERSION = tuple(int(v) for v in platform.mac_ver()[0].split("."))
+
+
+class DMGError(Exception):
+ def __init__(self, callback, message):
+ self.message = message
+ callback({"type": "error::fatal", "message": message})
+
+ def __str__(self):
+ return str(self.message)
+
+
+def quiet_callback(info):
+ pass
+
+
+def hdiutil(cmd, *args, **kwargs):
+ plist = kwargs.get("plist", True)
+ all_args = ["/usr/bin/hdiutil", cmd]
+ all_args.extend(args)
+ if plist:
+ all_args.append("-plist")
+ p = subprocess.Popen(all_args, stdout=subprocess.PIPE, close_fds=True)
+ output, errors = p.communicate()
+ if plist:
+ results = plistlib.loads(output)
+ else:
+ results = output
+ retcode = p.wait()
+ return retcode, results
+
+
+def load_settings(filename, settings):
+ encoding = "utf-8"
+ with open(filename, "rb") as fp:
+ try:
+ encoding = tokenize.detect_encoding(fp.readline)[0]
+ except SyntaxError:
+ pass
+
+ with open(filename, encoding=encoding) as fp:
+ exec(compile(fp.read(), filename, "exec"), settings, settings)
+
+
+def load_json(filename, settings):
+ """Read an appdmg .json spec.
+
+ Uses the defaults for appdmg, rather than the usual defaults for
+ dmgbuild.
+ """
+
+ with open(filename) as fp:
+ json_data = json.load(fp)
+
+ if "title" not in json_data:
+ raise ValueError("missing 'title' in JSON settings file")
+ if "contents" not in json_data:
+ raise ValueError("missing 'contents' in JSON settings file")
+
+ settings["volume_name"] = json_data["title"]
+ settings["icon"] = json_data.get("icon", None)
+ settings["badge_icon"] = json_data.get("badge-icon", None)
+ bk = json_data.get("background", None)
+ if bk is None:
+ bk = json_data.get("background-color", None)
+ if bk is not None:
+ settings["background"] = bk
+ settings["icon_size"] = json_data.get("icon-size", 80)
+ wnd = json_data.get("window", {"position": (100, 100), "size": (640, 480)})
+ pos = wnd.get("position", {"x": 100, "y": 100})
+ siz = wnd.get("size", {"width": 640, "height": 480})
+ settings["window_rect"] = (
+ (pos.get("x", 100), pos.get("y", 100)),
+ (siz.get("width", 640), siz.get("height", 480)),
+ )
+ settings["format"] = json_data.get("format", "UDZO")
+ settings["filesystem"] = json_data.get("filesystem", "HFS+")
+ settings["compression_level"] = json_data.get("compression-level", None)
+ settings["license"] = json_data.get("license", None)
+ files = []
+ hide = []
+ hide_extensions = []
+ symlinks = {}
+ icon_locations = {}
+ for fileinfo in json_data.get("contents", []):
+ if "path" not in fileinfo:
+ raise ValueError("missing 'path' in contents in JSON settings file")
+ if "x" not in fileinfo:
+ raise ValueError("missing 'x' in contents in JSON settings file")
+ if "y" not in fileinfo:
+ raise ValueError("missing 'y' in contents in JSON settings file")
+
+ kind = fileinfo.get("type", "file")
+ path = fileinfo["path"]
+ name = fileinfo.get("name", os.path.basename(path.rstrip("/")))
+ if kind == "file":
+ files.append((path, name))
+ elif kind == "link":
+ symlinks[name] = path
+ elif kind == "position":
+ pass
+ icon_locations[name] = (fileinfo["x"], fileinfo["y"])
+ hide_ext = fileinfo.get("hide_extension", False)
+ if hide_ext:
+ hide_extensions.append(name)
+ hidden = fileinfo.get("hidden", False)
+ if hidden:
+ hide.append(name)
+
+ settings["files"] = files
+ settings["hide_extensions"] = hide_extensions
+ settings["hide"] = hide
+ settings["symlinks"] = symlinks
+ settings["icon_locations"] = icon_locations
+
+
+def build_dmg( # noqa; C901
+ filename,
+ volume_name,
+ settings_file=None,
+ settings={},
+ defines={},
+ lookForHiDPI=True,
+ detach_retries=5,
+ callback=quiet_callback,
+):
+ options = {
+ # Default settings
+ "filename": filename,
+ "volume_name": volume_name,
+ "format": "UDBZ",
+ "filesystem": "HFS+",
+ "compression_level": None,
+ "size": None,
+ "files": [],
+ "symlinks": {},
+ "hide": [],
+ "hide_extensions": [],
+ "icon": None,
+ "badge_icon": None,
+ "background": None,
+ "show_status_bar": False,
+ "show_tab_view": False,
+ "show_toolbar": False,
+ "show_pathbar": False,
+ "show_sidebar": False,
+ "sidebar_width": 180,
+ "arrange_by": None,
+ "grid_offset": (0, 0),
+ "grid_spacing": 100.0,
+ "scroll_position": (0.0, 0.0),
+ "show_icon_preview": False,
+ "show_item_info": False,
+ "label_pos": "bottom",
+ "text_size": 16.0,
+ "icon_size": 128.0,
+ "include_icon_view_settings": "auto",
+ "include_list_view_settings": "auto",
+ "list_icon_size": 16.0,
+ "list_text_size": 12.0,
+ "list_scroll_position": (0, 0),
+ "list_sort_by": "name",
+ "list_use_relative_dates": True,
+ "list_calculate_all_sizes": False,
+ "list_columns": ("name", "date-modified", "size", "kind", "date-added"),
+ "list_column_widths": {
+ "name": 300,
+ "date-modified": 181,
+ "date-created": 181,
+ "date-added": 181,
+ "date-last-opened": 181,
+ "size": 97,
+ "kind": 115,
+ "label": 100,
+ "version": 75,
+ "comments": 300,
+ },
+ "list_column_sort_directions": {
+ "name": "ascending",
+ "date-modified": "descending",
+ "date-created": "descending",
+ "date-added": "descending",
+ "date-last-opened": "descending",
+ "size": "descending",
+ "kind": "ascending",
+ "label": "ascending",
+ "version": "ascending",
+ "comments": "ascending",
+ },
+ "window_rect": ((100, 100), (640, 280)),
+ "default_view": "icon-view",
+ "icon_locations": {},
+ "license": None,
+ "defines": defines,
+ }
+
+ callback({"type": "build::started"})
+
+ # Execute the settings file
+ if settings_file:
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "settings::load",
+ }
+ )
+
+ # We now support JSON settings files using appdmg's format
+ if settings_file.endswith(".json"):
+ load_json(settings_file, options)
+ else:
+ load_settings(settings_file, options)
+
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "settings::load",
+ }
+ )
+
+ # Add any overrides
+ options.update(settings)
+
+ # Set up the finder data
+ bounds = options["window_rect"]
+
+ bounds_string = "{{{{{}, {}}}, {{{}, {}}}}}".format(
+ bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]
+ )
+ bwsp = {
+ "ShowStatusBar": options["show_status_bar"],
+ "WindowBounds": bounds_string,
+ "ContainerShowSidebar": False,
+ "PreviewPaneVisibility": False,
+ "SidebarWidth": options["sidebar_width"],
+ "ShowTabView": options["show_tab_view"],
+ "ShowToolbar": options["show_toolbar"],
+ "ShowPathbar": options["show_pathbar"],
+ "ShowSidebar": options["show_sidebar"],
+ }
+
+ arrange_options = {
+ "name": "name",
+ "date-modified": "dateModified",
+ "date-created": "dateCreated",
+ "date-added": "dateAdded",
+ "date-last-opened": "dateLastOpened",
+ "size": "size",
+ "kind": "kind",
+ "label": "label",
+ }
+
+ icvp = {
+ "viewOptionsVersion": 1,
+ "backgroundType": 0,
+ "backgroundColorRed": 1.0,
+ "backgroundColorGreen": 1.0,
+ "backgroundColorBlue": 1.0,
+ "gridOffsetX": float(options["grid_offset"][0]),
+ "gridOffsetY": float(options["grid_offset"][1]),
+ "gridSpacing": float(options["grid_spacing"]),
+ "arrangeBy": str(arrange_options.get(options["arrange_by"], "none")),
+ "showIconPreview": bool(options["show_icon_preview"]),
+ "showItemInfo": bool(options["show_item_info"]),
+ "labelOnBottom": options["label_pos"] == "bottom",
+ "textSize": float(options["text_size"]),
+ "iconSize": float(options["icon_size"]),
+ "scrollPositionX": float(options["scroll_position"][0]),
+ "scrollPositionY": float(options["scroll_position"][1]),
+ }
+
+ background = options["background"]
+
+ columns = {
+ "name": "name",
+ "date-modified": "dateModified",
+ "date-created": "dateCreated",
+ "date-added": "dateAdded",
+ "date-last-opened": "dateLastOpened",
+ "size": "size",
+ "kind": "kind",
+ "label": "label",
+ "version": "version",
+ "comments": "comments",
+ }
+
+ default_widths = {
+ "name": 300,
+ "date-modified": 181,
+ "date-created": 181,
+ "date-added": 181,
+ "date-last-opened": 181,
+ "size": 97,
+ "kind": 115,
+ "label": 100,
+ "version": 75,
+ "comments": 300,
+ }
+
+ default_sort_directions = {
+ "name": "ascending",
+ "date-modified": "descending",
+ "date-created": "descending",
+ "date-added": "descending",
+ "date-last-opened": "descending",
+ "size": "descending",
+ "kind": "ascending",
+ "label": "ascending",
+ "version": "ascending",
+ "comments": "ascending",
+ }
+
+ lsvp = {
+ "viewOptionsVersion": 1,
+ "sortColumn": columns.get(options["list_sort_by"], "name"),
+ "textSize": float(options["list_text_size"]),
+ "iconSize": float(options["list_icon_size"]),
+ "showIconPreview": options["show_icon_preview"],
+ "scrollPositionX": options["list_scroll_position"][0],
+ "scrollPositionY": options["list_scroll_position"][1],
+ "useRelativeDates": options["list_use_relative_dates"],
+ "calculateAllSizes": options["list_calculate_all_sizes"],
+ }
+
+ lsvp["columns"] = {}
+ cndx = {}
+
+ for n, column in enumerate(options["list_columns"]):
+ cndx[column] = n
+ width = options["list_column_widths"].get(column, default_widths[column])
+ asc = "ascending" == options["list_column_sort_directions"].get(
+ column, default_sort_directions[column]
+ )
+
+ lsvp["columns"][columns[column]] = {
+ "index": n,
+ "width": width,
+ "identifier": columns[column],
+ "visible": True,
+ "ascending": asc,
+ }
+
+ n = len(options["list_columns"])
+ for k in columns:
+ if cndx.get(k, None) is None:
+ cndx[k] = n
+ width = default_widths[k]
+ asc = "ascending" == default_sort_directions[k]
+
+ lsvp["columns"][columns[column]] = {
+ "index": n,
+ "width": width,
+ "identifier": columns[column],
+ "visible": False,
+ "ascending": asc,
+ }
+
+ n += 1
+
+ default_view = options["default_view"]
+ views = {
+ "icon-view": b"icnv",
+ "column-view": b"clmv",
+ "list-view": b"Nlsv",
+ "coverflow": b"Flwv",
+ }
+
+ icvl = (b"type", views.get(default_view, "icnv"))
+
+ include_icon_view_settings = default_view == "icon-view" or options[
+ "include_icon_view_settings"
+ ] not in ("auto", "no", 0, False, None)
+ include_list_view_settings = default_view in ("list-view", "coverflow") or options[
+ "include_list_view_settings"
+ ] not in ("auto", "no", 0, False, None)
+
+ filename = options["filename"]
+ volume_name = options["volume_name"]
+
+ # Construct a writeable image to start with
+ dirname, basename = os.path.split(os.path.realpath(filename))
+ if not basename.endswith(".dmg"):
+ basename += ".dmg"
+ writableFile = tempfile.NamedTemporaryFile(
+ dir=dirname, prefix=".temp", suffix=basename
+ )
+
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "size::calculate",
+ }
+ )
+
+ total_size = options["size"]
+ if total_size is None:
+ # Start with a size of 128MB - this way we don't need to calculate the
+ # size of the background image, volume icon, and .DS_Store file (and
+ # 128 MB should be well sufficient for even the most outlandish image
+ # sizes, like an uncompressed 5K multi-resolution TIFF)
+ total_size = 128 * 1024 * 1024
+
+ def roundup(x, n):
+ return x if x % n == 0 else x + n - x % n
+
+ for path in options["files"]:
+ if isinstance(path, tuple):
+ path = path[0]
+
+ if not os.path.islink(path) and os.path.isdir(path):
+ for dirpath, dirnames, filenames in os.walk(path):
+ for f in filenames:
+ fp = os.path.join(dirpath, f)
+ total_size += roundup(os.lstat(fp).st_size, 4096)
+ else:
+ total_size += roundup(os.lstat(path).st_size, 4096)
+
+ for name, target in options["symlinks"].items():
+ total_size += 4096
+
+ total_size = str(max(total_size / 1000, 1024)) + "K"
+
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "size::calculate",
+ "size": total_size,
+ }
+ )
+
+ callback(
+ {
+ "type": "command::start",
+ "command": "hdiutil::create",
+ }
+ )
+
+ filesystem = options["filesystem"].upper()
+ fs_args = "-c c=64,a=16,e=16" if filesystem != "APFS" else ""
+
+ ret, output = hdiutil(
+ "create",
+ "-ov",
+ "-volname",
+ volume_name,
+ "-fs",
+ filesystem,
+ "-fsargs",
+ fs_args,
+ "-size",
+ total_size,
+ writableFile.name,
+ )
+
+ callback(
+ {
+ "type": "command::finished",
+ "command": "hdiutil::create",
+ "ret": ret,
+ "output": output,
+ }
+ )
+
+ if ret:
+ raise DMGError(callback, "Unable to create disk image")
+
+ callback(
+ {
+ "type": "command::start",
+ "command": "hdiutil::attach",
+ }
+ )
+
+ # IDME was deprecated in macOS 10.15/Catalina; as a result, use of -noidme
+ # started raising a warning.
+ if MACOS_VERSION >= (10, 15):
+ ret, output = hdiutil(
+ "attach", "-nobrowse", "-owners", "off", writableFile.name
+ )
+ else:
+ ret, output = hdiutil(
+ "attach", "-nobrowse", "-owners", "off", "-noidme", writableFile.name
+ )
+
+ callback(
+ {
+ "type": "command::finished",
+ "command": "hdiutil::attach",
+ "ret": ret,
+ "output": output,
+ }
+ )
+
+ if ret:
+ raise DMGError(callback, "Unable to attach disk image")
+
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "dmg::create",
+ }
+ )
+
+ try:
+ for info in output["system-entities"]:
+ if info.get("mount-point", None):
+ device = info["dev-entry"]
+ mount_point = info["mount-point"]
+
+ icon = options["icon"]
+ if badge:
+ badge_icon = options["badge_icon"]
+ else:
+ badge_icon = None
+ icon_target_path = os.path.join(mount_point, ".VolumeIcon.icns")
+ if icon:
+ shutil.copyfile(icon, icon_target_path)
+ elif badge_icon:
+ badge.badge_disk_icon(badge_icon, icon_target_path)
+
+ if icon or badge_icon:
+ subprocess.call(["/usr/bin/SetFile", "-a", "C", mount_point])
+
+ background_bmk = None
+
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "background::create",
+ }
+ )
+
+ if not isinstance(background, str):
+ pass
+ elif colors.isAColor(background):
+ c = colors.parseColor(background).to_rgb()
+
+ icvp["backgroundType"] = 1
+ icvp["backgroundColorRed"] = float(c.r)
+ icvp["backgroundColorGreen"] = float(c.g)
+ icvp["backgroundColorBlue"] = float(c.b)
+ else:
+ if os.path.isfile(background):
+ # look to see if there are HiDPI resources available
+
+ if lookForHiDPI is True:
+ name, extension = os.path.splitext(os.path.basename(background))
+ orderedImages = [background]
+ imageDirectory = os.path.dirname(background)
+ if imageDirectory == "":
+ imageDirectory = "."
+ for candidateName in os.listdir(imageDirectory):
+ hasScale = re.match(
+ r"^(?P<name>.+)@(?P<scale>\d+)x(?P<extension>\.\w+)$",
+ candidateName,
+ )
+ if (
+ hasScale
+ and name == hasScale.group("name")
+ and extension == hasScale.group("extension")
+ ):
+ scale = int(hasScale.group("scale"))
+ if len(orderedImages) < scale:
+ orderedImages += [None] * (scale - len(orderedImages))
+ orderedImages[scale - 1] = os.path.join(
+ imageDirectory, candidateName
+ )
+
+ if len(orderedImages) > 1:
+ # compile the grouped tiff
+ backgroundFile = tempfile.NamedTemporaryFile(suffix=".tiff")
+ background = backgroundFile.name
+ output = tempfile.TemporaryFile(mode="w+")
+ try:
+ subprocess.check_call(
+ ["/usr/bin/tiffutil", "-cathidpicheck"]
+ + list(filter(None, orderedImages))
+ + ["-out", background],
+ stdout=output,
+ stderr=output,
+ )
+ except Exception as e:
+ output.seek(0)
+ raise ValueError(
+ 'unable to compile combined HiDPI file "%s" got error: %s\noutput: %s'
+ % (background, str(e), output.read())
+ )
+
+ _, kind = os.path.splitext(background)
+ path_in_image = os.path.join(mount_point, ".background" + kind)
+ shutil.copyfile(background, path_in_image)
+ else:
+ dmg_resources = resources.files("dmgbuild")
+ bg_resource = dmg_resources.joinpath(
+ "resources/" + background + ".tiff"
+ )
+ if bg_resource.is_file():
+ path_in_image = os.path.join(mount_point, ".background.tiff")
+ with bg_resource.open("rb") as in_file:
+ with open(path_in_image, "wb") as out_file:
+ out_file.write(in_file.read())
+ else:
+ raise ValueError('background file "%s" not found' % background)
+
+ alias = Alias.for_file(path_in_image)
+ background_bmk = Bookmark.for_file(path_in_image)
+
+ icvp["backgroundType"] = 2
+ icvp["backgroundImageAlias"] = alias.to_bytes()
+
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "background::create",
+ }
+ )
+
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "files::add",
+ "total": len(options["files"]),
+ }
+ )
+
+ for f in options["files"]:
+ if isinstance(f, tuple):
+ f_in_image = os.path.join(mount_point, f[1])
+ f = f[0]
+ else:
+ basename = os.path.basename(f.rstrip("/"))
+ f_in_image = os.path.join(mount_point, basename)
+
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "file::add",
+ "file": f_in_image,
+ }
+ )
+
+ # use system ditto command to preserve code signing, etc.
+ subprocess.call(["/usr/bin/ditto", f, f_in_image])
+
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "file::add",
+ "file": f_in_image,
+ }
+ )
+
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "files::add",
+ }
+ )
+
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "symlinks::add",
+ "total": len(options["symlinks"]),
+ }
+ )
+
+ for name, target in options["symlinks"].items():
+ name_in_image = os.path.join(mount_point, name)
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "symlink::add",
+ "file": name_in_image,
+ "target": target,
+ }
+ )
+ os.symlink(target, name_in_image)
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "symlink::add",
+ "file": name_in_image,
+ "target": target,
+ }
+ )
+
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "symlinks::add",
+ }
+ )
+
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "extensions::hide",
+ }
+ )
+
+ to_hide = []
+ for name in options["hide_extensions"]:
+ name_in_image = os.path.join(mount_point, name)
+ to_hide.append(name_in_image)
+
+ if to_hide:
+ subprocess.call(["/usr/bin/SetFile", "-a", "E"] + to_hide)
+
+ to_hide = []
+ for name in options["hide"]:
+ name_in_image = os.path.join(mount_point, name)
+ to_hide.append(name_in_image)
+
+ if to_hide:
+ subprocess.call(["/usr/bin/SetFile", "-a", "V"] + to_hide)
+
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "extensions::hide",
+ }
+ )
+
+ userfn = options.get("create_hook", None)
+ if callable(userfn):
+ userfn(mount_point, options)
+
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "dsstore::create",
+ }
+ )
+
+ image_dsstore = os.path.join(mount_point, ".DS_Store")
+
+ with DSStore.open(image_dsstore, "w+") as d:
+ d["."]["vSrn"] = ("long", 1)
+ d["."]["bwsp"] = bwsp
+ if include_icon_view_settings:
+ d["."]["icvp"] = icvp
+ if background_bmk:
+ d["."]["pBBk"] = background_bmk
+ if include_list_view_settings:
+ d["."]["lsvp"] = lsvp
+ d["."]["icvl"] = icvl
+
+ for k, v in options["icon_locations"].items():
+ d[k]["Iloc"] = v
+
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "dsstore::create",
+ }
+ )
+
+ # Delete .Trashes, if it gets created
+ shutil.rmtree(os.path.join(mount_point, ".Trashes"), True)
+ except Exception:
+ # Always try to detach
+ hdiutil("detach", "-force", device, plist=False)
+ raise
+
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "dmg::create",
+ }
+ )
+
+ # Flush writes before attempting to detach.
+ subprocess.check_call(("sync", "--file-system", mount_point))
+
+ for tries in range(detach_retries):
+ callback(
+ {
+ "type": "command::start",
+ "command": "hdiutil::detach",
+ }
+ )
+
+ ret, output = hdiutil("detach", device, plist=False)
+
+ callback(
+ {
+ "type": "command::finished",
+ "command": "hdiutil::detach",
+ "ret:": ret,
+ "output:": output,
+ }
+ )
+
+ if not ret:
+ break
+ time.sleep(1)
+
+ if ret:
+ hdiutil("detach", "-force", device, plist=False)
+ raise DMGError(callback, "Unable to detach device cleanly")
+
+ callback(
+ {
+ "type": "command::start",
+ "command": "hdiutil::resize",
+ }
+ )
+
+ # Shrink the output to the minimum possible size
+ ret, output = hdiutil(
+ "resize", "-quiet", "-sectors", "min", writableFile.name, plist=False
+ )
+
+ callback(
+ {
+ "type": "command::finished",
+ "command": "hdiutil::resize",
+ "ret": ret,
+ "output": output,
+ }
+ )
+
+ if ret:
+ raise DMGError(callback, "Unable to shrink")
+
+ callback(
+ {
+ "type": "operation::start",
+ "operation": "dmg::shrink",
+ }
+ )
+
+ key_prefix = {"UDZO": "zlib", "UDBZ": "bzip2", "ULFO": "lzfse", "ULMO": "lzma"}
+ compression_level = options["compression_level"]
+ if options["format"] in key_prefix and compression_level:
+ compression_args = [
+ "-imagekey",
+ key_prefix[options["format"]] + "-level=" + str(compression_level),
+ ]
+ else:
+ compression_args = []
+
+ callback(
+ {
+ "type": "command::start",
+ "command": "hdiutil::convert",
+ }
+ )
+
+ ret, output = hdiutil(
+ "convert",
+ writableFile.name,
+ "-format",
+ options["format"],
+ "-ov",
+ "-o",
+ filename,
+ *compression_args,
+ )
+
+ callback(
+ {
+ "type": "command::finished",
+ "command": "hdiutil::convert",
+ "ret": ret,
+ "output": output,
+ }
+ )
+
+ if ret:
+ raise DMGError(callback, "Unable to convert")
+
+ callback(
+ {
+ "type": "operation::finished",
+ "operation": "dmg::shrink",
+ }
+ )
+
+ if options["license"]:
+ callback(
+ {
+ "type": "operation::start",
+ "command": "dmg::addlicense",
+ }
+ )
+
+ licenseDict = licensing.build_license(options["license"])
+
+ tempLicenseFile = open(os.path.join(dirname, "license.plist"), "wb")
+ plistlib.dump(licenseDict, tempLicenseFile)
+ tempLicenseFile.close()
+
+ # see https://developer.apple.com/forums/thread/668084
+ ret, output = hdiutil(
+ "udifrez", "-xml", tempLicenseFile.name, "", "-quiet", filename, plist=False
+ )
+
+ os.remove(tempLicenseFile.name)
+
+ if ret:
+ raise DMGError(callback, "Unable to add license")
+
+ callback(
+ {
+ "type": "operation::finished",
+ "command": "dmg::addlicense",
+ }
+ )
+
+ callback({"type": "build::finished"})
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/licensing.py b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/licensing.py
new file mode 100644
index 000000000..fb4254ee3
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/licensing.py
@@ -0,0 +1,329 @@
+import os
+
+# based on https://github.com/argv-minus-one/dmg-license/blob/master/language-info-generator/Languages.tsv
+# note that this table specifies STR# Resource ID but it seems to have no effect
+language_info_map = {
+ "da_DK": {"buttons": "Danish", "name": "Danish", "language_id": 9},
+ "de_AT": {"buttons": "German", "name": "German (Austrian)", "language_id": 92},
+ "de_CH": {"buttons": "German", "name": "German (Swiss)", "language_id": 19},
+ "de_DE": {"buttons": "German", "name": "German", "language_id": 3},
+ "en_AU": {"buttons": "English", "name": "English (Australian)", "language_id": 15},
+ "en_GB": {"buttons": "English", "name": "English (UK)", "language_id": 2},
+ "en_IE": {"buttons": "English", "name": "English (Ireland)", "language_id": 108},
+ "en_SG": {
+ "buttons": "English",
+ "name": "English (Singaporean)",
+ "language_id": 100,
+ },
+ "en_US": {"buttons": "English", "name": "English", "language_id": 0},
+ "es_ES": {"buttons": "Spanish", "name": "Spanish", "language_id": 8},
+ "fi_FI": {"buttons": "Finnish", "name": "Finnish", "language_id": 17},
+ "fr_BE": {"buttons": "French", "name": "French (Belgian)", "language_id": 98},
+ "fr_CA": {"buttons": "French", "name": "French (Canadian)", "language_id": 11},
+ "fr_CH": {"buttons": "French", "name": "French (Swiss)", "language_id": 18},
+ "fr_FR": {"buttons": "French", "name": "French", "language_id": 1},
+ "it_IT": {"buttons": "Italian", "name": "Italian", "language_id": 4},
+ "ja_JP": {
+ "buttons": "Japanese",
+ "name": "Japanese",
+ "language_id": 14,
+ "encoding": "shift_jis", # not sure if this is correct encoding, but seems to be working
+ "multibyte": True,
+ },
+ "ko_KR": {
+ "buttons": "Korean",
+ "name": "Korean",
+ "language_id": 51,
+ "encoding": "ksx1001",
+ "multibyte": True,
+ },
+ "nb_NO": {"buttons": "Norwegian", "name": "Norwegian", "language_id": 12},
+ "nl_BE": {"buttons": "Dutch", "name": "Dutch", "language_id": 6},
+ "nl_NL": {"buttons": "Dutch", "name": "Dutch", "language_id": 5},
+ "pt_BR": {
+ "buttons": "Portuguese",
+ "name": "Portuguese (Brazilian)",
+ "language_id": 71,
+ },
+ "pt_PT": {"buttons": "Portuguese", "name": "Portuguese", "language_id": 10},
+ "ru_RU": {
+ "buttons": "Russian",
+ "name": "Russian",
+ "language_id": 49,
+ "encoding": "mac_cyrillic",
+ },
+ "sv_SE": {"buttons": "Swedish", "name": "Swedish", "language_id": 7},
+ "zh_CN": {
+ "buttons": "Simplified",
+ "name": "Simplified Chinese",
+ "language_id": 52,
+ "encoding": "gb2312",
+ "multibyte": True,
+ },
+ "zh_TW": {
+ "buttons": "Traditional",
+ "name": "Traditional Chinese",
+ "language_id": 53,
+ "encoding": "big5",
+ "multibyte": True,
+ },
+}
+
+# Buttons (these come from the SLAResources file which you can find in the SLA
+# SDK on developer.apple.com)
+default_buttons = {
+ "English": (
+ "English",
+ "Agree",
+ "Disagree",
+ "Print",
+ "Save",
+ 'If you agree with the terms of this license, press "Agree" to install the software. If you do not agree, press "Disagree".', # noqa; E501
+ ),
+ "German": (
+ "Deutsch",
+ "Akzeptieren",
+ "Ablehnen",
+ "Drucken",
+ "Sichern...",
+ 'Klicken Sie in "Akzeptieren", wenn Sie mit den Bestimmungen des Software-Lizenzvertrags einverstanden sind. Falls nicht, bitte "Ablehnen" anklicken. Sie können die Software nur installieren, wenn Sie "Akzeptieren" angeklickt haben.', # noqa; E501
+ ),
+ "Spanish": (
+ "Español",
+ "Aceptar",
+ "No aceptar",
+ "Imprimir",
+ "Guardar...",
+ 'Si está de acuerdo con los términos de esta licencia, pulse "Aceptar" para instalar el software. En el supuesto de que no esté de acuerdo con los términos de esta licencia, pulse "No aceptar."', # noqa; E501
+ ),
+ "French": (
+ "Français",
+ "Accepter",
+ "Refuser",
+ "Imprimer",
+ "Enregistrer...",
+ 'Si vous acceptez les termes de la présente licence, cliquez sur "Accepter" afin d\'installer le logiciel. Si vous n\'êtes pas d\'accord avec les termes de la licence, cliquez sur "Refuser".', # noqa; E501
+ ),
+ "Italian": (
+ "Italiano",
+ "Accetto",
+ "Rifiuto",
+ "Stampa",
+ "Registra...",
+ 'Se accetti le condizioni di questa licenza, fai clic su "Accetto" per installare il software. Altrimenti fai clic su "Rifiuto".', # noqa; E501
+ ),
+ "Japanese": (
+ "日本語",
+ "同意します",
+ "同意しません",
+ "印刷する",
+ "保存...",
+ "本ソフトウエア使用許諾契約の条件に同意される場合には、ソフトウエアをインストールするために「同意します」を押してください。\u3000同意されない場合には、「同意しません」を押してください。", # noqa; E501
+ ),
+ "Dutch": (
+ "Nederlands",
+ "Ja",
+ "Nee",
+ "Print",
+ "Bewaar...",
+ "Indien u akkoord gaat met de voorwaarden van deze licentie, kunt u op 'Ja' klikken om de programmatuur te installeren. Indien u niet akkoord gaat, klikt u op 'Nee'.", # noqa; E501
+ ),
+ "Russian": (
+ "Русский",
+ "Согласен",
+ "Не согласен",
+ "Распечатать",
+ "Сохранить",
+ "Если вы согласны с условиями данной лицензии, нажмите «Согласен», чтобы установить программное обеспечение. Если вы не согласны, нажмите «Не согласен».", # noqa; E501
+ ),
+ "Swedish": (
+ "Svensk",
+ "Godkänns",
+ "Avböjs",
+ "Skriv ut",
+ "Spara...",
+ 'Om Du godkänner licensvillkoren klicka på "Godkänns" för att installera programprodukten. Om Du inte godkänner licensvillkoren, klicka på "Avböjs".', # noqa; E501
+ ),
+ "Portuguese": (
+ "Português",
+ "Concordar",
+ "Discordar",
+ "Imprimir",
+ "Salvar...",
+ 'Se está de acordo com os termos desta licença, pressione "Concordar" para instalar o software. Se não está de acordo, pressione "Discordar".', # noqa; E501
+ ),
+ "Simplified Chinese": (
+ "汉语",
+ "同意",
+ "不同意",
+ "打印",
+ "存储…",
+ "如果您同意本许可协议的条款,请按“同意”来安装此软件。如果您不同意,请按“不同意”。", # noqa; E501
+ ),
+ "Traditional Chinese": (
+ "漢語",
+ "同意",
+ "不同意",
+ "列印",
+ "儲存…",
+ "如果您同意本許可證裡的條款,請按“同意”以安裝軟體。如果不同意,請按“不同意”。", # noqa; E501
+ ),
+ "Danish": (
+ "Dansk",
+ "Enig",
+ "Uenig",
+ "Udskriv",
+ "Arkiver...",
+ "Hvis du accepterer betingelserne i licensaftalen, skal du klikke på “Enig” for at installere softwaren. Klik på “Uenig” for at annullere installeringen.", # noqa; E501
+ ),
+ "Finnish": (
+ "Suomi",
+ "Hyväksyn",
+ "En hyväksy",
+ "Tulosta",
+ "Tallenna…",
+ 'Hyväksy lisenssisopimuksen ehdot osoittamalla "Hyväksy". Jos et hyväksy sopimuksen ehtoja, osoita "En hyväksy".', # noqa; E501
+ ),
+ "Korean": (
+ "한국어",
+ "동의",
+ "동의 안함",
+ "프린트",
+ "저장...",
+ '사용 계약서의 내용에 동의하면, "동의" 단추를 눌러 소프트웨어를 설치하십시오. 동의하지 않는다면, "동의 안함" 단추를 누르십시오.', # noqa; E501
+ ),
+ "Norwegian": (
+ "Norsk",
+ "Enig",
+ "Ikke enig",
+ "Skriv ut",
+ "Arkiver...",
+ 'Hvis De er enig i bestemmelsene i denne lisensavtalen, klikker De på "Enig"-knappen for å installere programvaren. Hvis De ikke er enig, klikker De på "Ikke enig".', # noqa; E501
+ ),
+}
+
+udifrezXMLtemplate = {
+ "STR#": [],
+ # ?? License text would be included in a block like this:
+ # ?? 'TEXT': [
+ # ?? {
+ # ?? 'Attributes': '0x0000',
+ # ?? 'Data': b'license text',
+ # ?? 'ID': '5000',
+ # ?? 'Name': 'English'
+ # ?? }
+ # ?? ],
+ "TMPL": [
+ {
+ "Attributes": "0x0000",
+ "Data": b"\x13Default Language IDDWRD\x05CountOCNT\x04****LSTC\x0bsys lang IDDWRD\x1elocal res ID (offset from 5000DWRD\x102-byte language?DWRD\x04****LSTE", # noqa: E501
+ "ID": "128",
+ "Name": "LPic",
+ }
+ ],
+}
+
+
+# Another implementation in TS:
+# https://github.com/argv-minus-one/dmg-license/blob/4268f2e822944fd670c1e197596396f233d6484e/src/makeLicensePlist.ts
+
+
+def build_license(license_info):
+ """Add a license agreement to the specified disk image file, see
+ https://developer.apple.com/forums/thread/668084."""
+ # Copy the original template
+ xml = dict(udifrezXMLtemplate)
+
+ licenses = license_info.get(
+ "licenses",
+ {"en_US": default_buttons["English"][5].encode("utf-8")},
+ )
+
+ lpic = b""
+ # The first field is the default language ID.
+ lpic += int(5000).to_bytes(2, "big")
+ # The second field is the count of language ID to license resource mappings.
+ lpic += len(licenses.items()).to_bytes(2, "big")
+
+ for language, license_data in licenses.items():
+ if language not in language_info_map:
+ raise Exception(
+ "Unknown language '"
+ + language
+ + "'. Valid languages are: "
+ + ", ".join(sorted(language_info_map.keys()))
+ )
+
+ language_info = language_info_map[language]
+ language_buttons = language_info["buttons"]
+ language_name = language_info["name"]
+ language_id = language_info["language_id"]
+ # for simplicity we use the same id for the resource as system language id + 5000
+ resource_id = language_id + 5000
+ language_encoding = language_info.get("encoding", "mac_roman")
+ multibyte_encoding = language_info.get("multibyte_encoding", False)
+
+ if os.path.isfile(license_data):
+ mode = "rb" if license_data.endswith(".rtf") else "r"
+ with open(license_data, mode=mode) as f:
+ license_data = f.read()
+
+ if isinstance(license_data, bytes):
+ if license_data.startswith(b"{\\rtf1"):
+ licenseDataFormat = "RTF "
+ else:
+ licenseDataFormat = "TEXT"
+ else:
+ licenseDataFormat = "TEXT"
+ license_data = license_data.encode(language_encoding)
+
+ if licenseDataFormat not in xml:
+ xml[licenseDataFormat] = []
+
+ xml[licenseDataFormat].append(
+ {
+ "Attributes": "0x0000",
+ "Data": license_data,
+ "ID": str(resource_id),
+ "Name": f"{language_name} SLA",
+ }
+ )
+
+ language_default_buttons = default_buttons.get(
+ language_buttons, default_buttons["English"]
+ )
+ buttons = license_info.get("buttons", {}).get(
+ language, language_default_buttons
+ )
+
+ assert len(buttons) == 6, "License buttons must have 6 entries."
+
+ buttons = [b.encode(language_encoding) for b in buttons]
+ buttons = [len(b).to_bytes(1, "big") + b for b in buttons]
+ xml["STR#"].append(
+ {
+ "Attributes": "0x0000",
+ # \x06 is apparently the number of buttons which is always 6
+ "Data": b"\x00\x06" + b"".join(buttons),
+ "ID": str(resource_id),
+ "Name": language_name,
+ }
+ )
+
+ # Finally, the list of resource ID mappings:
+ # Mapping field 1: system language ID
+ lpic += language_id.to_bytes(2, "big")
+ # Mapping field 2: local resource ID minus 5000
+ lpic += int(resource_id - 5000).to_bytes(2, "big")
+ # Mapping field 3: 2-byte language?
+ lpic += int(1 if multibyte_encoding else 0).to_bytes(2, "big")
+
+ xml["LPic"] = [
+ {
+ "Attributes": "0x0000",
+ "Data": lpic,
+ "ID": "5000",
+ "Name": "",
+ }
+ ]
+ return xml
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/qt_attribution.json b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/qt_attribution.json
index d9f97ae2c..d9f97ae2c 100644
--- a/src/3rdparty/python/lib/python2.7/site-packages/dmgbuild/qt_attribution.json
+++ b/src/3rdparty/python/lib/python3.9/site-packages/dmgbuild/qt_attribution.json
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/LICENSE b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/LICENSE
index e91f4eb38..e91f4eb38 100644
--- a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/LICENSE
+++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/LICENSE
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__init__.py b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__init__.py
new file mode 100644
index 000000000..bc8702fab
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__init__.py
@@ -0,0 +1,5 @@
+from .store import DSStore, DSStoreEntry
+
+__version__ = "1.3.1"
+
+__all__ = ["__version__", "DSStore", "DSStoreEntry"]
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__main__.py b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__main__.py
new file mode 100644
index 000000000..cf17ab5f6
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/__main__.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+#
+# ds_store: Examine .DS_Store files.
+#
+
+import argparse
+import os
+import os.path
+import pprint
+import re
+import sys
+
+from ds_store import DSStore
+from ds_store.buddy import BuddyError
+
+_not_printable_re = re.compile(rb"[\x00-\x1f\x7f-\x9f]")
+
+
+def usage():
+ print(main.__doc__)
+ sys.exit(0)
+
+
+def chunks(iterable, length):
+ for i in range(0, len(iterable), length):
+ yield i, iterable[i : i + length]
+
+
+def pretty(value):
+ if isinstance(value, dict):
+ return f"{{\n {pprint.pformat(value, indent=4)[1:-1]}\n}}"
+ elif isinstance(value, bytearray):
+ lines = ["["]
+ for offset, chunk in chunks(value, 16):
+ printable_chunk = _not_printable_re.sub(b".", chunk).decode("latin-1")
+ hex_line = " ".join([f"{b:02x}" for b in chunk])
+ line = f" {offset:08x} {hex_line:<47s} {printable_chunk}"
+ lines.append(line)
+ lines.append("]")
+ return "\n".join(lines)
+ elif isinstance(value, bytes):
+ return value.decode("latin-1")
+ else:
+ return value
+
+
+def main(argv):
+ """Display the contents of the .DS_Store file at the specified path.
+
+ If you specify just a directory, ds_store will inspect the .DS_Store
+ file in that directory. If no paths are specified, ds_store will
+ display the contents of the .DS_Store file in the current directory.
+ """
+
+ parser = argparse.ArgumentParser(description=main.__doc__)
+ parser.add_argument("paths", nargs="*")
+ args = parser.parse_args(argv)
+
+ if len(args.paths) == 0:
+ args.paths = ["."]
+
+ failed = False
+ for path in args.paths:
+ if os.path.isdir(path):
+ path = os.path.join(path, ".DS_Store")
+
+ if not os.path.exists(path) or not os.path.isfile(path):
+ print(f"ds_store: {path} not found", file=sys.stderr)
+ failed = True
+ continue
+
+ try:
+ with DSStore.open(path, "r") as d:
+ print(path)
+ print("")
+
+ max_name_len = 0
+ for entry in d:
+ name_len = len(entry.filename)
+ if name_len > max_name_len:
+ max_name_len = name_len
+
+ for entry in d:
+ print(
+ "{:<{width}} {} {}".format(
+ entry.filename,
+ entry.code.decode("latin-1"),
+ pretty(entry.value),
+ width=max_name_len,
+ )
+ )
+ print("")
+ except BuddyError as e:
+ print(f"ds_store: {path}: {e}")
+ failed = True
+
+ if failed:
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main(sys.argv[1:])
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/ds_store/buddy.py b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/buddy.py
new file mode 100644
index 000000000..27b11b3f7
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/buddy.py
@@ -0,0 +1,486 @@
+import binascii
+import bisect
+import os
+import struct
+
+
+class BuddyError(Exception):
+ pass
+
+
+class Block:
+ def __init__(self, allocator, offset, size):
+ self._allocator = allocator
+ self._offset = offset
+ self._size = size
+ self._value = bytearray(allocator.read(offset, size))
+ self._pos = 0
+ self._dirty = False
+
+ def __len__(self):
+ return self._size
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.close()
+
+ def close(self):
+ if self._dirty:
+ self.flush()
+
+ def flush(self):
+ if self._dirty:
+ self._dirty = False
+ self._allocator.write(self._offset, self._value)
+
+ def invalidate(self):
+ self._dirty = False
+
+ def zero_fill(self):
+ len = self._size - self._pos
+ zeroes = b"\0" * len
+ self._value[self._pos : self._size] = zeroes
+ self._dirty = True
+
+ def tell(self):
+ return self._pos
+
+ def seek(self, pos, whence=os.SEEK_SET):
+ if whence == os.SEEK_CUR:
+ pos += self._pos
+ elif whence == os.SEEK_END:
+ pos = self._size - pos
+
+ if pos < 0 or pos > self._size:
+ raise ValueError("Seek out of range in Block instance")
+
+ self._pos = pos
+
+ def read(self, size_or_format):
+ if isinstance(size_or_format, (str, bytes)):
+ size = struct.calcsize(size_or_format)
+ fmt = size_or_format
+ else:
+ size = size_or_format
+ fmt = None
+
+ if self._size - self._pos < size:
+ raise BuddyError("Unable to read %lu bytes in block" % size)
+
+ data = self._value[self._pos : self._pos + size]
+ self._pos += size
+
+ if fmt is not None:
+ if isinstance(data, bytearray):
+ return struct.unpack_from(fmt, bytes(data))
+ else:
+ return struct.unpack(fmt, data)
+ else:
+ return data
+
+ def write(self, data_or_format, *args):
+ if len(args):
+ data = struct.pack(data_or_format, *args)
+ else:
+ data = data_or_format
+
+ if self._pos + len(data) > self._size:
+ raise ValueError("Attempt to write past end of Block")
+
+ self._value[self._pos : self._pos + len(data)] = data
+ self._pos += len(data)
+
+ self._dirty = True
+
+ def insert(self, data_or_format, *args):
+ if len(args):
+ data = struct.pack(data_or_format, *args)
+ else:
+ data = data_or_format
+
+ del self._value[-len(data) :]
+ self._value[self._pos : self._pos] = data
+ self._pos += len(data)
+
+ self._dirty = True
+
+ def delete(self, size):
+ if self._pos + size > self._size:
+ raise ValueError("Attempt to delete past end of Block")
+ del self._value[self._pos : self._pos + size]
+ self._value += b"\0" * size
+ self._dirty = True
+
+ def __str__(self):
+ return binascii.b2a_hex(self._value)
+
+
+class Allocator:
+ def __init__(self, the_file):
+ self._file = the_file
+ self._dirty = False
+
+ self._file.seek(0)
+
+ # Read the header
+ (magic1, magic2, offset, size, offset2, self._unknown1) = self.read(
+ -4, ">I4sIII16s"
+ )
+
+ if magic2 != b"Bud1" or magic1 != 1:
+ raise BuddyError("Not a buddy file")
+
+ if offset != offset2:
+ raise BuddyError("Root addresses differ")
+
+ self._root = Block(self, offset, size)
+
+ # Read the block offsets
+ count, self._unknown2 = self._root.read(">II")
+ self._offsets = []
+ c = (count + 255) & ~255
+ while c:
+ self._offsets += self._root.read(">256I")
+ c -= 256
+ self._offsets = self._offsets[:count]
+
+ # Read the TOC
+ self._toc = {}
+ count = self._root.read(">I")[0]
+ for n in range(count):
+ nlen = self._root.read("B")[0]
+ name = bytes(self._root.read(nlen))
+ value = self._root.read(">I")[0]
+ self._toc[name] = value
+
+ # Read the free lists
+ self._free = []
+ for n in range(32):
+ count = self._root.read(">I")
+ self._free.append(list(self._root.read(">%uI" % count)))
+
+ @classmethod
+ def open(cls, file_or_name, mode="r+"):
+ if isinstance(file_or_name, str):
+ if "b" not in mode:
+ mode = mode[:1] + "b" + mode[1:]
+ f = open(file_or_name, mode)
+ else:
+ f = file_or_name
+
+ if "w" in mode:
+ # Create an empty file in this case
+ f.truncate()
+
+ # An empty root block needs 1264 bytes:
+ #
+ # 0 4 offset count
+ # 4 4 unknown
+ # 8 4 root block offset (2048)
+ # 12 255 * 4 padding (offsets are in multiples of 256)
+ # 1032 4 toc count (0)
+ # 1036 228 free list
+ # total 1264
+
+ # The free list will contain the following:
+ #
+ # 0 5 * 4 no blocks of width less than 5
+ # 20 6 * 8 1 block each of widths 5 to 10
+ # 68 4 no blocks of width 11 (allocated for the root)
+ # 72 19 * 8 1 block each of widths 12 to 30
+ # 224 4 no blocks of width 31
+ # total 228
+ #
+ # (The reason for this layout is that we allocate 2**5 bytes for
+ # the header, which splits the initial 2GB region into every size
+ # below 2**31, including *two* blocks of size 2**5, one of which
+ # we take. The root block itself then needs a block of size
+ # 2**11. Conveniently, each of these initial blocks will be
+ # located at offset 2**n where n is its width.)
+
+ # Write the header
+ header = struct.pack(
+ b">I4sIII16s",
+ 1,
+ b"Bud1",
+ 2048,
+ 1264,
+ 2048,
+ b"\x00\x00\x10\x0c"
+ b"\x00\x00\x00\x87"
+ b"\x00\x00\x20\x0b"
+ b"\x00\x00\x00\x00",
+ )
+ f.write(header)
+ f.write(b"\0" * 2016)
+
+ # Write the root block
+ free_list = [struct.pack(b">5I", 0, 0, 0, 0, 0)]
+ for n in range(5, 11):
+ free_list.append(struct.pack(b">II", 1, 2**n))
+ free_list.append(struct.pack(b">I", 0))
+ for n in range(12, 31):
+ free_list.append(struct.pack(b">II", 1, 2**n))
+ free_list.append(struct.pack(b">I", 0))
+
+ root = b"".join(
+ [
+ struct.pack(b">III", 1, 0, 2048 | 5),
+ struct.pack(b">I", 0) * 255,
+ struct.pack(b">I", 0),
+ ]
+ + free_list
+ )
+ f.write(root)
+
+ return Allocator(f)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.close()
+
+ def close(self):
+ self.flush()
+ self._file.close()
+
+ def flush(self):
+ if self._dirty:
+ size = self._root_block_size()
+ self.allocate(size, 0)
+ with self.get_block(0) as rblk:
+ self._write_root_block_into(rblk)
+
+ addr = self._offsets[0]
+ offset = addr & ~0x1F
+ size = 1 << (addr & 0x1F)
+
+ self._file.seek(0, os.SEEK_SET)
+ self._file.write(
+ struct.pack(
+ b">I4sIII16s", 1, b"Bud1", offset, size, offset, self._unknown1
+ )
+ )
+
+ self._dirty = False
+
+ self._file.flush()
+
+ def read(self, offset, size_or_format):
+ """Read data at `offset', or raise an exception.
+
+ `size_or_format' may either be a byte count, in which case we
+ return raw data, or a format string for `struct.unpack', in
+ which case we work out the size and unpack the data before
+ returning it.
+ """
+ # N.B. There is a fixed offset of four bytes(!)
+ self._file.seek(offset + 4, os.SEEK_SET)
+
+ if isinstance(size_or_format, str):
+ size = struct.calcsize(size_or_format)
+ fmt = size_or_format
+ else:
+ size = size_or_format
+ fmt = None
+
+ ret = self._file.read(size)
+ if len(ret) < size:
+ ret += b"\0" * (size - len(ret))
+
+ if fmt is not None:
+ if isinstance(ret, bytearray):
+ ret = struct.unpack_from(fmt, bytes(ret))
+ else:
+ ret = struct.unpack(fmt, ret)
+
+ return ret
+
+ def write(self, offset, data_or_format, *args):
+ """Write data at `offset', or raise an exception.
+
+ `data_or_format' may either be the data to write, or a format
+ string for `struct.pack', in which case we pack the additional
+ arguments and write the resulting data.
+ """
+ # N.B. There is a fixed offset of four bytes(!)
+ self._file.seek(offset + 4, os.SEEK_SET)
+
+ if len(args):
+ data = struct.pack(data_or_format, *args)
+ else:
+ data = data_or_format
+
+ self._file.write(data)
+
+ def get_block(self, block):
+ try:
+ addr = self._offsets[block]
+ except IndexError:
+ return None
+
+ offset = addr & ~0x1F
+ size = 1 << (addr & 0x1F)
+
+ return Block(self, offset, size)
+
+ def _root_block_size(self):
+ """Return the number of bytes required by the root block."""
+ # Offsets
+ size = 8
+ size += 4 * ((len(self._offsets) + 255) & ~255)
+
+ # TOC
+ size += 4
+ size += sum([5 + len(s) for s in self._toc])
+
+ # Free list
+ size += sum([4 + 4 * len(fl) for fl in self._free])
+
+ return size
+
+ def _write_root_block_into(self, block):
+ # Offsets
+ block.write(">II", len(self._offsets), self._unknown2)
+ block.write(">%uI" % len(self._offsets), *self._offsets)
+ extra = len(self._offsets) & 255
+ if extra:
+ block.write(b"\0\0\0\0" * (256 - extra))
+
+ # TOC
+ keys = list(self._toc.keys())
+ keys.sort()
+
+ block.write(">I", len(keys))
+ for k in keys:
+ block.write("B", len(k))
+ block.write(k)
+ block.write(">I", self._toc[k])
+
+ # Free list
+ for w, f in enumerate(self._free):
+ block.write(">I", len(f))
+ if len(f):
+ block.write(">%uI" % len(f), *f)
+
+ def _buddy(self, offset, width):
+ f = self._free[width]
+ b = offset ^ (1 << width)
+
+ try:
+ ndx = f.index(b)
+ except ValueError:
+ ndx = None
+
+ return (f, b, ndx)
+
+ def _release(self, offset, width):
+ # Coalesce
+ while True:
+ f, b, ndx = self._buddy(offset, width)
+
+ if ndx is None:
+ break
+
+ offset &= b
+ width += 1
+ del f[ndx]
+
+ # Add to the list
+ bisect.insort(f, offset)
+
+ # Mark as dirty
+ self._dirty = True
+
+ def _alloc(self, width):
+ w = width
+ while not self._free[w]:
+ w += 1
+ while w > width:
+ offset = self._free[w].pop(0)
+ w -= 1
+ self._free[w] = [offset, offset ^ (1 << w)]
+ self._dirty = True
+ return self._free[width].pop(0)
+
+ def allocate(self, bytes, block=None):
+ """Allocate or reallocate a block such that it has space for at least
+ `bytes' bytes."""
+ if block is None:
+ # Find the first unused block
+ try:
+ block = self._offsets.index(0)
+ except ValueError:
+ block = len(self._offsets)
+ self._offsets.append(0)
+
+ # Compute block width
+ width = max(bytes.bit_length(), 5)
+
+ addr = self._offsets[block]
+ offset = addr & ~0x1F
+
+ if addr:
+ blkwidth = addr & 0x1F
+ if blkwidth == width:
+ return block
+ self._release(offset, width)
+ self._offsets[block] = 0
+
+ offset = self._alloc(width)
+ self._offsets[block] = offset | width
+ return block
+
+ def release(self, block):
+ addr = self._offsets[block]
+
+ if addr:
+ width = addr & 0x1F
+ offset = addr & ~0x1F
+ self._release(offset, width)
+
+ if block == len(self._offsets):
+ del self._offsets[block]
+ else:
+ self._offsets[block] = 0
+
+ def __len__(self):
+ return len(self._toc)
+
+ def __getitem__(self, key):
+ if not isinstance(key, str):
+ raise TypeError("Keys must be of string type")
+ if not isinstance(key, bytes):
+ key = key.encode("latin_1")
+ return self._toc[key]
+
+ def __setitem__(self, key, value):
+ if not isinstance(key, str):
+ raise TypeError("Keys must be of string type")
+ if not isinstance(key, bytes):
+ key = key.encode("latin_1")
+ self._toc[key] = value
+ self._dirty = True
+
+ def __delitem__(self, key):
+ if not isinstance(key, str):
+ raise TypeError("Keys must be of string type")
+ if not isinstance(key, bytes):
+ key = key.encode("latin_1")
+ del self._toc[key]
+ self._dirty = True
+
+ def iterkeys(self):
+ return self._toc.keys()
+
+ def keys(self):
+ return self._toc.keys()
+
+ def __iter__(self):
+ return self._toc.keys()
+
+ def __contains__(self, key):
+ return key in self._toc
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/qt_attribution.json b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/qt_attribution.json
index a4854d1ed..a4854d1ed 100644
--- a/src/3rdparty/python/lib/python2.7/site-packages/ds_store/qt_attribution.json
+++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/qt_attribution.json
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/ds_store/store.py b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/store.py
new file mode 100644
index 000000000..58e9e1bce
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/ds_store/store.py
@@ -0,0 +1,1262 @@
+import plistlib
+import struct
+
+import mac_alias
+
+from . import buddy
+
+
+class ILocCodec:
+ @staticmethod
+ def encode(point):
+ return struct.pack(b">IIII", point[0], point[1], 0xFFFFFFFF, 0xFFFF0000)
+
+ @staticmethod
+ def decode(bytesData):
+ if isinstance(bytesData, bytearray):
+ x, y = struct.unpack_from(b">II", bytes(bytesData[:8]))
+ else:
+ x, y = struct.unpack(b">II", bytesData[:8])
+ return (x, y)
+
+
+class PlistCodec:
+ @staticmethod
+ def encode(plist):
+ return plistlib.dumps(plist, fmt=plistlib.FMT_BINARY)
+
+ @staticmethod
+ def decode(bytes):
+ return plistlib.loads(bytes)
+
+
+class BookmarkCodec:
+ @staticmethod
+ def encode(bmk):
+ return bmk.to_bytes()
+
+ @staticmethod
+ def decode(bytes):
+ return mac_alias.Bookmark.from_bytes(bytes)
+
+
+# This list tells the code how to decode particular kinds of entry in the
+# .DS_Store file. This is really a convenience, and we currently only
+# support a tiny subset of the possible entry types.
+codecs = {
+ b"Iloc": ILocCodec,
+ b"bwsp": PlistCodec,
+ b"lsvp": PlistCodec,
+ b"lsvP": PlistCodec,
+ b"icvp": PlistCodec,
+ b"pBBk": BookmarkCodec,
+}
+
+
+class DSStoreEntry:
+ """Holds the data from an entry in a ``.DS_Store`` file. Note that this is
+ not meant to represent the entry itself---i.e. if you change the type or
+ value, your changes will *not* be reflected in the underlying file.
+
+ If you want to make a change, you should either use the
+ :class:`DSStore` object's :meth:`DSStore.insert` method (which will
+ replace a key if it already exists), or the mapping access mode for
+ :class:`DSStore` (often simpler anyway).
+ """
+
+ def __init__(self, filename, code, typecode, value=None):
+ if str != bytes and type(filename) == bytes:
+ filename = filename.decode("utf-8")
+
+ if not isinstance(code, bytes):
+ code = code.encode("latin_1")
+
+ self.filename = filename
+ self.code = code
+ self.type = typecode
+ self.value = value
+
+ @classmethod
+ def read(cls, block):
+ """Read a ``.DS_Store`` entry from the containing Block."""
+ # First read the filename
+ nlen = block.read(b">I")[0]
+ filename = block.read(2 * nlen).decode("utf-16be")
+
+ # Next, read the code and type
+ code, typecode = block.read(b">4s4s")
+
+ # Finally, read the data
+ if typecode == b"bool":
+ value = block.read(b">?")[0]
+ elif typecode == b"long" or typecode == b"shor":
+ value = block.read(b">I")[0]
+ elif typecode == b"blob":
+ vlen = block.read(b">I")[0]
+ value = block.read(vlen)
+
+ codec = codecs.get(code, None)
+ if codec:
+ value = codec.decode(value)
+ typecode = codec
+ elif typecode == b"ustr":
+ vlen = block.read(b">I")[0]
+ value = block.read(2 * vlen).decode("utf-16be")
+ elif typecode == b"type":
+ value = block.read(b">4s")[0]
+ elif typecode == b"comp" or typecode == b"dutc":
+ value = block.read(b">Q")[0]
+ else:
+ raise ValueError('Unknown type code "%s"' % typecode)
+
+ return DSStoreEntry(filename, code, typecode, value)
+
+ def __lt__(self, other):
+ if not isinstance(other, DSStoreEntry):
+ raise TypeError("Can only compare against other DSStoreEntry objects")
+ sfl = self.filename.lower()
+ ofl = other.filename.lower()
+ return sfl < ofl or (self.filename == other.filename and self.code < other.code)
+
+ def __le__(self, other):
+ if not isinstance(other, DSStoreEntry):
+ raise TypeError("Can only compare against other DSStoreEntry objects")
+ sfl = self.filename.lower()
+ ofl = other.filename.lower()
+ return sfl < ofl or (sfl == ofl and self.code <= other.code)
+
+ def __eq__(self, other):
+ if not isinstance(other, DSStoreEntry):
+ raise TypeError("Can only compare against other DSStoreEntry objects")
+ sfl = self.filename.lower()
+ ofl = other.filename.lower()
+ return sfl == ofl and self.code == other.code
+
+ def __ne__(self, other):
+ if not isinstance(other, DSStoreEntry):
+ raise TypeError("Can only compare against other DSStoreEntry objects")
+ sfl = self.filename.lower()
+ ofl = other.filename.lower()
+ return sfl != ofl or self.code != other.code
+
+ def __gt__(self, other):
+ if not isinstance(other, DSStoreEntry):
+ raise TypeError("Can only compare against other DSStoreEntry objects")
+ sfl = self.filename.lower()
+ ofl = other.filename.lower()
+
+ selfCode = self.code
+ if str != bytes and type(selfCode) is bytes:
+ selfCode = selfCode.decode("utf-8")
+ otherCode = other.code
+ if str != bytes and type(otherCode) is bytes:
+ otherCode = otherCode.decode("utf-8")
+
+ return sfl > ofl or (sfl == ofl and selfCode > otherCode)
+
+ def __ge__(self, other):
+ if not isinstance(other, DSStoreEntry):
+ raise TypeError("Can only compare against other DSStoreEntry objects")
+ sfl = self.filename.lower()
+ ofl = other.filename.lower()
+ return sfl > ofl or (sfl == ofl and self.code >= other.code)
+
+ def byte_length(self):
+ """Compute the length of this entry, in bytes."""
+ utf16 = self.filename.encode("utf-16be")
+ length = 4 + len(utf16) + 8
+
+ if isinstance(self.type, str):
+ entry_type = self.type.encode("latin_1")
+ value = self.value
+ elif isinstance(self.type, (bytes, str)):
+ entry_type = self.type
+ value = self.value
+ else:
+ entry_type = b"blob"
+ value = self.type.encode(self.value)
+
+ if entry_type == b"bool":
+ length += 1
+ elif entry_type == b"long" or entry_type == b"shor":
+ length += 4
+ elif entry_type == b"blob":
+ length += 4 + len(value)
+ elif entry_type == b"ustr":
+ utf16 = value.encode("utf-16be")
+ length += 4 + len(utf16)
+ elif entry_type == b"type":
+ length += 4
+ elif entry_type == b"comp" or entry_type == b"dutc":
+ length += 8
+ else:
+ raise ValueError('Unknown type code "%s"' % entry_type)
+
+ return length
+
+ def write(self, block, insert=False):
+ """Write this entry to the specified Block."""
+ if insert:
+ w = block.insert
+ else:
+ w = block.write
+
+ if isinstance(self.type, str):
+ entry_type = self.type.encode("latin_1")
+ value = self.value
+ elif isinstance(self.type, (bytes, str)):
+ entry_type = self.type
+ value = self.value
+ else:
+ entry_type = b"blob"
+ value = self.type.encode(self.value)
+
+ utf16 = self.filename.encode("utf-16be")
+ w(b">I", len(utf16) // 2)
+ w(utf16)
+ w(b">4s4s", self.code, entry_type)
+
+ if entry_type == b"bool":
+ w(b">?", value)
+ elif entry_type == b"long" or entry_type == b"shor":
+ w(b">I", value)
+ elif entry_type == b"blob":
+ w(b">I", len(value))
+ w(value)
+ elif entry_type == b"ustr":
+ utf16 = value.encode("utf-16be")
+ w(b">I", len(utf16) // 2)
+ w(utf16)
+ elif entry_type == b"type":
+ if isinstance(value, str):
+ value = value.encode("latin_1")
+ w(b">4s", value)
+ elif entry_type == b"comp" or entry_type == b"dutc":
+ w(b">Q", value)
+ else:
+ raise ValueError('Unknown type code "%s"' % entry_type)
+
+ def __repr__(self):
+ return f"<{self.filename} {self.code}>"
+
+
+class DSStore:
+ """Python interface to a ``.DS_Store`` file. Works by manipulating the
+ file on the disk---so this code will work with ``.DS_Store`` files for.
+
+ *very* large directories.
+
+ A :class:`DSStore` object can be used as if it was a mapping, e.g.::
+
+ d['foobar.dat']['Iloc']
+
+ will fetch the "Iloc" record for "foobar.dat", or raise :class:`KeyError` if
+ there is no such record. If used in this manner, the :class:`DSStore` object
+ will return (type, value) tuples, unless the type is "blob" and the module
+ knows how to decode it.
+
+ Currently, we know how to decode "Iloc", "bwsp", "lsvp", "lsvP" and "icvp"
+ blobs. "Iloc" decodes to an (x, y) tuple, while the others are all decoded
+ using ``biplist`` or ``plistlib`` depending on Python version.
+
+ Assignment also works, e.g.::
+
+ d['foobar.dat']['note'] = ('ustr', u'Hello World!')
+
+ as does deletion with ``del``::
+
+ del d['foobar.dat']['note']
+
+ This is usually going to be the most convenient interface, though
+ occasionally (for instance when creating a new ``.DS_Store`` file) you
+ may wish to drop down to using :class:`DSStoreEntry` objects directly.
+ """
+
+ def __init__(self, store):
+ self._store = store
+ self._superblk = self._store["DSDB"]
+ with self._get_block(self._superblk) as s:
+ (
+ self._rootnode,
+ self._levels,
+ self._records,
+ self._nodes,
+ self._page_size,
+ ) = s.read(b">IIIII")
+ self._min_usage = 2 * self._page_size // 3
+ self._dirty = False
+
+ @classmethod
+ def open(cls, file_or_name, mode="r+", initial_entries=None):
+ """Open a ``.DS_Store`` file; pass either a Python file object, or a
+ filename in the ``file_or_name`` argument and a file access mode in the
+ ``mode`` argument.
+
+ If you are creating a new file using the "w" or "w+" modes, you
+ may also specify a list of entries with which to initialise the
+ file.
+ """
+ store = buddy.Allocator.open(file_or_name, mode)
+
+ if mode == "w" or mode == "w+":
+ superblk = store.allocate(20)
+ store["DSDB"] = superblk
+ page_size = 4096
+
+ if not initial_entries:
+ root = store.allocate(page_size)
+
+ with store.get_block(root) as rootblk:
+ rootblk.zero_fill()
+
+ with store.get_block(superblk) as s:
+ s.write(b">IIIII", root, 0, 0, 1, page_size)
+ else:
+ # Make sure they're in sorted order
+ initial_entries = list(initial_entries)
+ initial_entries.sort()
+
+ # Construct the tree
+ current_level = initial_entries
+ next_level = []
+ levels = []
+ ptr_size = 0
+ node_count = 0
+ while True:
+ total = 8
+ nodes = []
+ node = []
+ for e in current_level:
+ new_total = total + ptr_size + e.byte_length()
+ if new_total > page_size:
+ nodes.append(node)
+ next_level.append(e)
+ total = 8
+ node = []
+ else:
+ total = new_total
+ node.append(e)
+ if node:
+ nodes.append(node)
+
+ node_count += len(nodes)
+ levels.append(nodes)
+
+ if len(nodes) == 1:
+ break
+
+ current_level = next_level
+ next_level = []
+ ptr_size = 4
+
+ # Allocate nodes
+ ptrs = [store.allocate(page_size) for n in range(node_count)]
+
+ # Generate nodes
+ pointers = []
+ prev_pointers = None
+ for level in levels:
+ ppndx = 0
+ lptrs = ptrs[-len(level) :]
+ del ptrs[-len(level) :]
+ for node in level:
+ ndx = lptrs.pop(0)
+ if prev_pointers is None:
+ with store.get_block(ndx) as block:
+ block.write(b">II", 0, len(node))
+ for e in node:
+ e.write(block)
+ else:
+ next_node = prev_pointers[ppndx + len(node)]
+ node_ptrs = prev_pointers[ppndx : ppndx + len(node)]
+
+ with store.get_block(ndx) as block:
+ block.write(b">II", next_node, len(node))
+ for ptr, e in zip(node_ptrs, node):
+ block.write(b">I", ptr)
+ e.write(block)
+
+ pointers.append(ndx)
+ prev_pointers = pointers
+ pointers = []
+
+ root = prev_pointers[0]
+
+ with store.get_block(superblk) as s:
+ s.write(
+ b">IIIII",
+ root,
+ len(levels),
+ len(initial_entries),
+ node_count,
+ page_size,
+ )
+
+ return DSStore(store)
+
+ def _get_block(self, number):
+ return self._store.get_block(number)
+
+ def flush(self):
+ """Flush any dirty data back to the file."""
+ if self._dirty:
+ self._dirty = False
+
+ with self._get_block(self._superblk) as s:
+ s.write(
+ b">IIIII",
+ self._rootnode,
+ self._levels,
+ self._records,
+ self._nodes,
+ self._page_size,
+ )
+ self._store.flush()
+
+ def close(self):
+ """Flush dirty data and close the underlying file."""
+ self.flush()
+ self._store.close()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ self.close()
+
+ # Internal B-Tree nodes look like this:
+ #
+ # [ next | count | (ptr0 | rec0) | (ptr1 | rec1) ... (ptrN | recN) ]
+
+ # Leaf nodes look like this:
+ #
+ # [ 0 | count | rec0 | rec1 ... recN ]
+
+ # Iterate over the tree, starting at `node'
+ def _traverse(self, node):
+ if node is None:
+ node = self._rootnode
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+ if next_node:
+ for n in range(count):
+ ptr = block.read(b">I")[0]
+ for e in self._traverse(ptr):
+ yield e
+ e = DSStoreEntry.read(block)
+ yield e
+ for e in self._traverse(next_node):
+ yield e
+ else:
+ for n in range(count):
+ e = DSStoreEntry.read(block)
+ yield e
+
+ # Display the data in `node'
+ def _dump_node(self, node):
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+ print("next: %u\ncount: %u\n" % (next_node, count))
+ for n in range(count):
+ if next_node:
+ ptr = block.read(b">I")[0]
+ print("%8u " % ptr, end=" ")
+ else:
+ print(" ", end=" ")
+ e = DSStoreEntry.read(block)
+ print(e, " (%u)" % e.byte_length())
+ print("used: %u" % block.tell())
+
+ # Display the data in the super block
+ def _dump_super(self):
+ print(
+ "root: %u\nlevels: %u\nrecords: %u\nnodes: %u\npage-size: %u"
+ % (
+ self._rootnode,
+ self._levels,
+ self._records,
+ self._nodes,
+ self._page_size,
+ )
+ )
+
+ # Splits entries across two blocks, returning one pivot
+ #
+ # Tries to balance the block usage across the two as best it can
+ def _split2(self, blocks, entries, pointers, before, internal):
+ left_block = blocks[0]
+ right_block = blocks[1]
+
+ count = len(entries)
+
+ # Find the feasible splits
+ best_split = None
+ best_diff = None
+ total = before[count]
+
+ if 8 + total <= self._page_size:
+ # We can use a *single* node for this
+ best_split = count
+ else:
+ # Split into two nodes
+ for n in range(1, count - 1):
+ left_size = 8 + before[n]
+ right_size = 8 + total - before[n + 1]
+
+ if left_size > self._page_size:
+ break
+ if right_size > self._page_size:
+ continue
+
+ diff = abs(left_size - right_size)
+
+ if best_split is None or diff < best_diff:
+ best_split = n
+ best_diff = diff
+
+ if best_split is None:
+ return None
+
+ # Write the nodes
+ left_block.seek(0)
+ if internal:
+ next_node = pointers[best_split]
+ else:
+ next_node = 0
+ left_block.write(b">II", next_node, best_split)
+
+ for n in range(best_split):
+ if internal:
+ left_block.write(b">I", pointers[n])
+ entries[n].write(left_block)
+
+ left_block.zero_fill()
+
+ if best_split == count:
+ return []
+
+ right_block.seek(0)
+ if internal:
+ next_node = pointers[count]
+ else:
+ next_node = 0
+ right_block.write(b">II", next_node, count - best_split - 1)
+
+ for n in range(best_split + 1, count):
+ if internal:
+ right_block.write(b">I", pointers[n])
+ entries[n].write(right_block)
+
+ right_block.zero_fill()
+
+ pivot = entries[best_split]
+
+ return [pivot]
+
+ def _split(self, node, entry, right_ptr=0):
+ self._nodes += 1
+ self._dirty = True
+ new_right = self._store.allocate(self._page_size)
+ with self._get_block(node) as block, self._get_block(new_right) as right_block:
+
+ # First, measure and extract all the elements
+ entry_size = entry.byte_length()
+ # ?? entry_pos = None
+ next_node, count = block.read(b">II")
+ if next_node:
+ entry_size += 4
+ pointers = []
+ entries = []
+ before = []
+ total = 0
+ for n in range(count):
+ pos = block.tell()
+ if next_node:
+ ptr = block.read(b">I")[0]
+ pointers.append(ptr)
+ e = DSStoreEntry.read(block)
+ if e > entry:
+ # ?? entry_pos = n
+ entries.append(entry)
+ pointers.append(right_ptr)
+ before.append(total)
+ total += entry_size
+ entries.append(e)
+ before.append(total)
+ total += block.tell() - pos
+ before.append(total)
+ if next_node:
+ pointers.append(next_node)
+
+ pivot = self._split2(
+ [block, right_block], entries, pointers, before, bool(next_node)
+ )[0]
+
+ self._records += 1
+ self._nodes += 1
+ self._dirty = True
+
+ return (pivot, new_right)
+
+ # Allocate a new root node containing the element `pivot' and the pointers
+ # `left' and `right'
+ def _new_root(self, left, pivot, right):
+ new_root = self._store.allocate(self._page_size)
+ with self._get_block(new_root) as block:
+ block.write(b">III", right, 1, left)
+ pivot.write(block)
+ self._rootnode = new_root
+ self._levels += 1
+ self._nodes += 1
+ self._dirty = True
+
+ # Insert an entry into an inner node; `path' is the path from the root
+ # to `node', not including `node' itself. `right_ptr' is the new node
+ # pointer (inserted to the RIGHT of `entry')
+ def _insert_inner(self, path, node, entry, right_ptr):
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+ insert_pos = None
+ insert_ndx = None
+ n = 0
+ while n < count:
+ pos = block.tell()
+ ptr = block.read(b">I")[0]
+ e = DSStoreEntry.read(block)
+ if e == entry:
+ if n == count - 1:
+ right_ptr = next_node
+ next_node = ptr
+ block.seek(pos)
+ else:
+ right_ptr = block.read(b">I")[0]
+ block.seek(pos + 4)
+ insert_pos = pos
+ insert_ndx = n
+ block.delete(e.byte_length() + 4)
+ count -= 1
+ self._records += 1
+ self._dirty = True
+ continue
+ elif insert_pos is None and e > entry:
+ insert_pos = pos
+ insert_ndx = n
+ n += 1
+ if insert_pos is None:
+ insert_pos = block.tell()
+ insert_ndx = count
+ remaining = self._page_size - block.tell()
+
+ if remaining < entry.byte_length() + 4:
+ pivot, new_right = self._split(node, entry, right_ptr)
+ if path:
+ self._insert_inner(path[:-1], path[-1], pivot, new_right)
+ else:
+ self._new_root(node, pivot, new_right)
+ else:
+ if insert_ndx == count:
+ block.seek(insert_pos)
+ block.write(b">I", next_node)
+ entry.write(block)
+ next_node = right_ptr
+ else:
+ block.seek(insert_pos + 4)
+ entry.write(block, True)
+ block.insert(">I", right_ptr)
+ block.seek(0)
+ count += 1
+ block.write(b">II", next_node, count)
+ self._records += 1
+ self._dirty = True
+
+ # Insert `entry' into the leaf node `node'
+ def _insert_leaf(self, path, node, entry):
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+ insert_pos = None
+ # ?? insert_ndx = None
+ n = 0
+ while n < count:
+ pos = block.tell()
+ e = DSStoreEntry.read(block)
+ if e == entry:
+ insert_pos = pos
+ # ?? insert_ndx = n
+ block.seek(pos)
+ block.delete(e.byte_length())
+ count -= 1
+ self._records += 1
+ self._dirty = True
+ continue
+ elif insert_pos is None and e > entry:
+ insert_pos = pos
+ # ?? insert_ndx = n
+ n += 1
+ if insert_pos is None:
+ insert_pos = block.tell()
+ # ?? insert_ndx = count
+ remaining = self._page_size - block.tell()
+
+ if remaining < entry.byte_length():
+ pivot, new_right = self._split(node, entry)
+ if path:
+ self._insert_inner(path[:-1], path[-1], pivot, new_right)
+ else:
+ self._new_root(node, pivot, new_right)
+ else:
+ block.seek(insert_pos)
+ entry.write(block, True)
+ block.seek(0)
+ count += 1
+ block.write(b">II", next_node, count)
+ self._records += 1
+ self._dirty = True
+
+ def insert(self, entry):
+ """Insert ``entry`` (which should be a :class:`DSStoreEntry`) into the
+ B-Tree."""
+ path = []
+ node = self._rootnode
+ while True:
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+ if next_node:
+ for n in range(count):
+ ptr = block.read(b">I")[0]
+ e = DSStoreEntry.read(block)
+ if entry < e:
+ next_node = ptr
+ break
+ elif entry == e:
+ # If we find an existing entry the same, replace it
+ self._insert_inner(path, node, entry, None)
+ return
+ path.append(node)
+ node = next_node
+ else:
+ self._insert_leaf(path, node, entry)
+ return
+
+ # Return usage information for the specified `node'
+ def _block_usage(self, node):
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+
+ for n in range(count):
+ if next_node:
+ block.read(b">I")[0]
+ DSStoreEntry.read(block)
+
+ used = block.tell()
+
+ return (count, used)
+
+ # Splits entries across three blocks, returning two pivots
+ def _split3(self, blocks, entries, pointers, before, internal):
+ count = len(entries)
+
+ # Find the feasible splits
+ best_split = None
+ best_diff = None
+ total = before[count]
+ for n in range(1, count - 3):
+ left_size = 8 + before[n]
+ remaining = 16 + total - before[n + 1]
+
+ if left_size > self._page_size:
+ break
+ if remaining > 2 * self._page_size:
+ continue
+
+ for m in range(n + 2, count - 1):
+ mid_size = 8 + before[m] - before[n + 1]
+ right_size = 8 + total - before[m + 1]
+
+ if mid_size > self._page_size:
+ break
+ if right_size > self._page_size:
+ continue
+
+ diff = abs(left_size - mid_size) * abs(right_size - mid_size)
+
+ if best_split is None or diff < best_diff:
+ best_split = (n, m, count)
+ best_diff = diff
+
+ if best_split is None:
+ return None
+
+ # Write the nodes
+ prev_split = -1
+ for block, split in zip(blocks, best_split):
+ block.seek(0)
+ if internal:
+ next_node = pointers[split]
+ else:
+ next_node = 0
+ block.write(b">II", next_node, split)
+
+ for n in range(prev_split + 1, split):
+ if internal:
+ block.write(b">I", pointers[n])
+ entries[n].write(block)
+
+ block.zero_fill()
+
+ prev_split = split
+
+ return (entries[best_split[0]], entries[best_split[1]])
+
+ # Extract all of the entries from the specified list of `blocks',
+ # separating them by the specified `pivots'. Also computes the
+ # amount of space used before each entry.
+ def _extract(self, blocks, pivots):
+ pointers = []
+ entries = []
+ before = []
+ total = 0
+ ppivots = pivots + [None]
+ for b, p in zip(blocks, ppivots):
+ b.seek(0)
+ next_node, count = b.read(b">II")
+ for n in range(count):
+ pos = b.tell()
+ if next_node:
+ ptr = b.read(b">I")[0]
+ pointers.append(ptr)
+ e = DSStoreEntry.read(b)
+ entries.append(e)
+ before.append(total)
+ total += b.tell() - pos
+ if next_node:
+ pointers.append(next_node)
+ if p:
+ entries.append(p)
+ before.append(total)
+ total += p.byte_length()
+ if next_node:
+ total += 4
+ before.append(total)
+
+ return (entries, pointers, before)
+
+ # Rebalance the specified `node', whose path from the root is `path'.
+ def _rebalance(self, path, node):
+ # Can't rebalance the root
+ if not path:
+ return
+
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+
+ with self._get_block(path[-1]) as parent:
+ # Find the left and right siblings and respective pivots
+ parent_next, parent_count = parent.read(b">II")
+ left_pos = None
+ left_node = None
+ left_pivot = None
+ node_pos = None
+ right_pos = None
+ right_node = None
+ right_pivot = None
+ prev_e = prev_ptr = prev_pos = None
+ for n in range(parent_count):
+ pos = parent.tell()
+ ptr = parent.read(b">I")[0]
+ e = DSStoreEntry.read(parent)
+
+ if ptr == node:
+ node_pos = pos
+ right_pivot = e
+ left_pos = prev_pos
+ left_pivot = prev_e
+ left_node = prev_ptr
+ elif prev_ptr == node:
+ right_node = ptr
+ right_pos = pos
+ break
+
+ prev_e = e
+ prev_ptr = ptr
+ prev_pos = pos
+
+ if parent_next == node:
+ node_pos = parent.tell()
+ left_pos = prev_pos
+ left_pivot = prev_e
+ left_node = prev_ptr
+ elif right_node is None:
+ right_node = parent_next
+ right_pos = parent.tell()
+
+ _ = parent.tell()
+
+ if left_node and right_node:
+ with self._get_block(left_node) as left, self._get_block(
+ right_node
+ ) as right:
+ blocks = [left, block, right]
+ pivots = [left_pivot, right_pivot]
+
+ entries, pointers, before = self._extract(blocks, pivots)
+
+ # If there's a chance that we could use two pages instead
+ # of three, go for it
+ pivots = self._split2(
+ blocks, entries, pointers, before, bool(next_node)
+ )
+ if pivots is None:
+ ptrs = [left_node, node, right_node]
+ pivots = self._split3(
+ blocks, entries, pointers, before, bool(next_node)
+ )
+ else:
+ if pivots:
+ ptrs = [left_node, node]
+ else:
+ ptrs = [left_node]
+ self._store.release(node)
+ self._nodes -= 1
+ node = left_node
+ self._store.release(right_node)
+ self._nodes -= 1
+ self._dirty = True
+
+ # Remove the pivots from the parent
+ with self._get_block(path[-1]) as parent:
+ if right_node == parent_next:
+ parent.seek(left_pos)
+ parent.delete(right_pos - left_pos)
+ parent_next = left_node
+ else:
+ parent.seek(left_pos + 4)
+ parent.delete(right_pos - left_pos)
+ parent.seek(0)
+ parent_count -= 2
+ parent.write(b">II", parent_next, parent_count)
+ self._records -= 2
+
+ # Replace with those in pivots
+ for e, rp in zip(pivots, ptrs[1:]):
+ self._insert_inner(path[:-1], path[-1], e, rp)
+ elif left_node:
+ with self._get_block(left_node) as left:
+ blocks = [left, block]
+ pivots = [left_pivot]
+
+ entries, pointers, before = self._extract(blocks, pivots)
+
+ pivots = self._split2(
+ blocks, entries, pointers, before, bool(next_node)
+ )
+
+ # Remove the pivot from the parent
+ with self._get_block(path[-1]) as parent:
+ if node == parent_next:
+ parent.seek(left_pos)
+ parent.delete(node_pos - left_pos)
+ parent_next = left_node
+ else:
+ parent.seek(left_pos + 4)
+ parent.delete(node_pos - left_pos)
+ parent.seek(0)
+ parent_count -= 1
+ parent.write(b">II", parent_next, parent_count)
+ self._records -= 1
+
+ # Replace the pivot
+ if pivots:
+ self._insert_inner(path[:-1], path[-1], pivots[0], node)
+ elif right_node:
+ with self._get_block(right_node) as right:
+ blocks = [block, right]
+ pivots = [right_pivot]
+
+ entries, pointers, before = self._extract(blocks, pivots)
+
+ pivots = self._split2(
+ blocks, entries, pointers, before, bool(next_node)
+ )
+
+ # Remove the pivot from the parent
+ with self._get_block(path[-1]) as parent:
+ if right_node == parent_next:
+ parent.seek(pos)
+ parent.delete(right_pos - node_pos)
+ parent_next = node
+ else:
+ parent.seek(pos + 4)
+ parent.delete(right_pos - node_pos)
+ parent.seek(0)
+ parent_count -= 1
+ parent.write(b">II", parent_next, parent_count)
+ self._records -= 1
+
+ # Replace the pivot
+ if pivots:
+ self._insert_inner(path[:-1], path[-1], pivots[0], right_node)
+
+ if not path and not parent_count:
+ self._store.release(path[-1])
+ self._nodes -= 1
+ self._dirty = True
+ self._rootnode = node
+ else:
+ count, used = self._block_usage(path[-1])
+
+ if used < self._page_size // 2:
+ self._rebalance(path[:-1], path[-1])
+
+ # Delete from the leaf node `node'. `filename_lc' has already been
+ # lower-cased.
+ def _delete_leaf(self, node, filename_lc, code):
+ found = False
+
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+
+ for n in range(count):
+ pos = block.tell()
+ e = DSStoreEntry.read(block)
+ if e.filename.lower() == filename_lc and (
+ code is None or e.code == code
+ ):
+ block.seek(pos)
+ block.delete(e.byte_length())
+ found = True
+
+ # This does not affect the loop; THIS IS NOT A BUG
+ count -= 1
+
+ self._records -= 1
+ self._dirty = True
+
+ if found:
+ used = block.tell()
+
+ block.seek(0)
+ block.write(b">II", next_node, count)
+
+ return used < self._page_size // 2
+ else:
+ return False
+
+ # Remove the largest entry from the subtree starting at `node' (with
+ # path from root `path'). Returns a tuple (rebalance, entry) where
+ # rebalance is either None if no rebalancing is required, or a
+ # (path, node) tuple giving the details of the node to rebalance.
+ def _take_largest(self, path, node):
+ path = list(path)
+ rebalance = None
+ while True:
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+
+ if next_node:
+ path.append(node)
+ node = next_node
+ continue
+
+ for n in range(count):
+ pos = block.tell()
+ e = DSStoreEntry.read(block)
+
+ count -= 1
+ block.seek(0)
+ block.write(b">II", next_node, count)
+
+ if pos < self._page_size // 2:
+ rebalance = (path, node)
+ break
+
+ return rebalance, e
+
+ # Delete an entry from an inner node, `node'
+ def _delete_inner(self, path, node, filename_lc, code):
+ rebalance = False
+
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+
+ for n in range(count):
+ pos = block.tell()
+ ptr = block.read(b">I")[0]
+ e = DSStoreEntry.read(block)
+ if e.filename.lower() == filename_lc and (
+ code is None or e.code == code
+ ):
+ # Take the largest from the left subtree
+ rebalance, largest = self._take_largest(path, ptr)
+
+ # Delete this entry
+ if n == count - 1:
+ right_ptr = next_node
+ next_node = ptr
+ block.seek(pos)
+ else:
+ right_ptr = block.read(b">I")[0]
+ block.seek(pos + 4)
+
+ block.delete(e.byte_length() + 4)
+
+ count -= 1
+ block.seek(0)
+ block.write(b">II", next_node, count)
+
+ self._records -= 1
+ self._dirty = True
+
+ break
+
+ # Replace the pivot value
+ self._insert_inner(path, node, largest, right_ptr)
+
+ # Rebalance from the node we stole from
+ if rebalance:
+ self._rebalance(rebalance[0], rebalance[1])
+ return True
+ return False
+
+ def delete(self, filename, code):
+ """Delete an item, identified by ``filename`` and ``code`` from the
+ B-Tree."""
+ if isinstance(filename, DSStoreEntry):
+ code = filename.code
+ filename = filename.filename
+
+ # If we're deleting *every* node for "filename", we must recurse
+ if code is None:
+ # TODO: Fix this so we can do bulk deletes
+ raise ValueError("You must delete items individually. Sorry")
+
+ # Otherwise, we're deleting *one* specific node
+ filename_lc = filename.lower()
+ path = []
+ node = self._rootnode
+ while True:
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+ if next_node:
+ for n in range(count):
+ ptr = block.read(b">I")[0]
+ e = DSStoreEntry.read(block)
+ e_lc = e.filename.lower()
+ if filename_lc < e_lc or (
+ filename_lc == e_lc and code < e.code
+ ):
+ next_node = ptr
+ break
+ elif filename_lc == e_lc and code == e.code:
+ self._delete_inner(path, node, filename_lc, code)
+ return
+ path.append(node)
+ node = next_node
+ else:
+ if self._delete_leaf(node, filename_lc, code):
+ self._rebalance(path, node)
+ return
+
+ # Find implementation
+ def _find(self, node, filename_lc, code=None):
+ if code is not None and not isinstance(code, bytes):
+ code = code.encode("latin_1")
+ with self._get_block(node) as block:
+ next_node, count = block.read(b">II")
+ if next_node:
+ for n in range(count):
+ ptr = block.read(b">I")[0]
+ e = DSStoreEntry.read(block)
+ if filename_lc < e.filename.lower():
+ for e in self._find(ptr, filename_lc, code):
+ yield e
+ return
+ elif filename_lc == e.filename.lower():
+ if code is None or (code and code < e.code):
+ for e in self._find(ptr, filename_lc, code):
+ yield e
+ if code is None or code == e.code:
+ yield e
+ elif code < e.code:
+ return
+ for e in self._find(next_node, filename_lc, code):
+ yield e
+ else:
+ for n in range(count):
+ e = DSStoreEntry.read(block)
+ if filename_lc == e.filename.lower():
+ if code is None or code == e.code:
+ yield e
+ elif code < e.code:
+ return
+
+ def find(self, filename, code=None):
+ """Returns a generator that will iterate over matching entries in the
+ B-Tree."""
+ if isinstance(filename, DSStoreEntry):
+ code = filename.code
+ filename = filename.filename
+
+ filename_lc = filename.lower()
+
+ return self._find(self._rootnode, filename_lc, code)
+
+ def __len__(self):
+ return self._records
+
+ def __iter__(self):
+ return self._traverse(self._rootnode)
+
+ class Partial:
+ """This is used to implement indexing."""
+
+ def __init__(self, store, filename):
+ self._store = store
+ self._filename = filename
+
+ def __getitem__(self, code):
+ if code is None:
+ raise KeyError("no such key - [%s][None]" % self._filename)
+
+ if not isinstance(code, bytes):
+ code = code.encode("latin_1")
+
+ try:
+ item = next(self._store.find(self._filename, code))
+ except StopIteration:
+ raise KeyError(f"no such key - [{self._filename}][{code}]")
+
+ if not isinstance(item.type, (bytes, str)):
+ return item.value
+
+ return (item.type, item.value)
+
+ def __setitem__(self, code, value):
+ if code is None:
+ raise KeyError("bad key - [%s][None]" % self._filename)
+
+ if not isinstance(code, bytes):
+ code = code.encode("latin_1")
+
+ codec = codecs.get(code, None)
+ if codec:
+ entry_type = codec
+ entry_value = value
+ else:
+ entry_type = value[0]
+ entry_value = value[1]
+
+ self._store.insert(
+ DSStoreEntry(self._filename, code, entry_type, entry_value)
+ )
+
+ def __delitem__(self, code):
+ if code is None:
+ raise KeyError("no such key - [%s][None]" % self._filename)
+
+ self._store.delete(self._filename, code)
+
+ def __iter__(self):
+ yield from self._store.find(self._filename)
+
+ def __getitem__(self, filename):
+ return self.Partial(self, filename)
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/LICENSE b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/LICENSE
index e91f4eb38..e91f4eb38 100644
--- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/LICENSE
+++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/LICENSE
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/__init__.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/__init__.py
new file mode 100644
index 000000000..0f925e7d1
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/__init__.py
@@ -0,0 +1,47 @@
+# flake8: noqa
+from .alias import *
+from .bookmark import *
+
+__version__ = "2.2.2"
+
+__all__ = [
+ "__version__",
+ "ALIAS_KIND_FILE",
+ "ALIAS_KIND_FOLDER",
+ "ALIAS_HFS_VOLUME_SIGNATURE",
+ "ALIAS_FIXED_DISK",
+ "ALIAS_NETWORK_DISK",
+ "ALIAS_400KB_FLOPPY_DISK",
+ "ALIAS_800KB_FLOPPY_DISK",
+ "ALIAS_1_44MB_FLOPPY_DISK",
+ "ALIAS_EJECTABLE_DISK",
+ "ALIAS_NO_CNID",
+ "kBookmarkPath",
+ "kBookmarkCNIDPath",
+ "kBookmarkFileProperties",
+ "kBookmarkFileName",
+ "kBookmarkFileID",
+ "kBookmarkFileCreationDate",
+ "kBookmarkTOCPath",
+ "kBookmarkVolumePath",
+ "kBookmarkVolumeURL",
+ "kBookmarkVolumeName",
+ "kBookmarkVolumeUUID",
+ "kBookmarkVolumeSize",
+ "kBookmarkVolumeCreationDate",
+ "kBookmarkVolumeProperties",
+ "kBookmarkContainingFolder",
+ "kBookmarkUserName",
+ "kBookmarkUID",
+ "kBookmarkWasFileReference",
+ "kBookmarkCreationOptions",
+ "kBookmarkURLLengths",
+ "kBookmarkSecurityExtension",
+ "AppleShareInfo",
+ "VolumeInfo",
+ "TargetInfo",
+ "Alias",
+ "Bookmark",
+ "Data",
+ "URL",
+]
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py
new file mode 100644
index 000000000..bf34fb01b
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/alias.py
@@ -0,0 +1,772 @@
+import datetime
+import io
+import os
+import os.path
+import struct
+import sys
+from unicodedata import normalize
+
+if sys.platform == "darwin":
+ from . import osx
+
+from .utils import mac_epoch
+
+ALIAS_KIND_FILE = 0
+ALIAS_KIND_FOLDER = 1
+
+ALIAS_HFS_VOLUME_SIGNATURE = b"H+"
+
+ALIAS_FILESYSTEM_UDF = "UDF (CD/DVD)"
+ALIAS_FILESYSTEM_FAT32 = "FAT32"
+ALIAS_FILESYSTEM_EXFAT = "exFAT"
+ALIAS_FILESYSTEM_HFSX = "HFSX"
+ALIAS_FILESYSTEM_HFSPLUS = "HFS+"
+ALIAS_FILESYSTEM_FTP = "FTP"
+ALIAS_FILESYSTEM_NTFS = "NTFS"
+ALIAS_FILESYSTEM_UNKNOWN = "unknown"
+
+ALIAS_FIXED_DISK = 0
+ALIAS_NETWORK_DISK = 1
+ALIAS_400KB_FLOPPY_DISK = 2
+ALIAS_800KB_FLOPPY_DISK = 3
+ALIAS_1_44MB_FLOPPY_DISK = 4
+ALIAS_EJECTABLE_DISK = 5
+
+ALIAS_NO_CNID = 0xFFFFFFFF
+
+ALIAS_FSTYPE_MAP = {
+ # Version 2 aliases
+ b"HX": ALIAS_FILESYSTEM_HFSX,
+ b"H+": ALIAS_FILESYSTEM_HFSPLUS,
+ # Version 3 aliases
+ b"BDcu": ALIAS_FILESYSTEM_UDF,
+ b"BDIS": ALIAS_FILESYSTEM_FAT32,
+ b"BDxF": ALIAS_FILESYSTEM_EXFAT,
+ b"HX\0\0": ALIAS_FILESYSTEM_HFSX,
+ b"H+\0\0": ALIAS_FILESYSTEM_HFSPLUS,
+ b"KG\0\0": ALIAS_FILESYSTEM_FTP,
+ b"NTcu": ALIAS_FILESYSTEM_NTFS,
+}
+
+
+def encode_utf8(s):
+ if isinstance(s, bytes):
+ return s
+ return s.encode("utf-8")
+
+
+def decode_utf8(s):
+ if isinstance(s, bytes):
+ return s.decode("utf-8")
+ return s
+
+
+class AppleShareInfo:
+ def __init__(self, zone=None, server=None, user=None):
+ #: The AppleShare zone
+ self.zone = zone
+ #: The AFP server
+ self.server = server
+ #: The username
+ self.user = user
+
+ def __repr__(self):
+ return "AppleShareInfo({!r},{!r},{!r})".format(
+ self.zone, self.server, self.user
+ )
+
+
+class VolumeInfo:
+ def __init__(
+ self,
+ name,
+ creation_date,
+ fs_type,
+ disk_type,
+ attribute_flags,
+ fs_id,
+ appleshare_info=None,
+ driver_name=None,
+ posix_path=None,
+ disk_image_alias=None,
+ dialup_info=None,
+ network_mount_info=None,
+ ):
+ #: The name of the volume on which the target resides
+ self.name = name
+
+ #: The creation date of the target's volume
+ self.creation_date = creation_date
+
+ #: The filesystem type
+ #: (for v2 aliases, this is a 2-character code; for v3 aliases, a
+ #: 4-character code).
+ self.fs_type = fs_type
+
+ #: The type of disk; should be one of
+ #:
+ #: * ALIAS_FIXED_DISK
+ #: * ALIAS_NETWORK_DISK
+ #: * ALIAS_400KB_FLOPPY_DISK
+ #: * ALIAS_800KB_FLOPPY_DISK
+ #: * ALIAS_1_44MB_FLOPPY_DISK
+ #: * ALIAS_EJECTABLE_DISK
+ self.disk_type = disk_type
+
+ #: Filesystem attribute flags (from HFS volume header)
+ self.attribute_flags = attribute_flags
+
+ #: Filesystem identifier
+ self.fs_id = fs_id
+
+ #: AppleShare information (for automatic remounting of network shares)
+ #: *(optional)*
+ self.appleshare_info = appleshare_info
+
+ #: Driver name (*probably* contains a disk driver name on older Macs)
+ #: *(optional)*
+ self.driver_name = driver_name
+
+ #: POSIX path of the mount point of the target's volume
+ #: *(optional)*
+ self.posix_path = posix_path
+
+ #: :class:`Alias` object pointing at the disk image on which the
+ #: target's volume resides *(optional)*
+ self.disk_image_alias = disk_image_alias
+
+ #: Dialup information (for automatic establishment of dialup connections)
+ self.dialup_info = dialup_info
+
+ #: Network mount information (for automatic remounting)
+ self.network_mount_info = network_mount_info
+
+ @property
+ def filesystem_type(self):
+ return ALIAS_FSTYPE_MAP.get(self.fs_type, ALIAS_FILESYSTEM_UNKNOWN)
+
+ def __repr__(self):
+ args = [
+ "name",
+ "creation_date",
+ "fs_type",
+ "disk_type",
+ "attribute_flags",
+ "fs_id",
+ ]
+ values = []
+ for a in args:
+ v = getattr(self, a)
+ values.append(repr(v))
+
+ kwargs = [
+ "appleshare_info",
+ "driver_name",
+ "posix_path",
+ "disk_image_alias",
+ "dialup_info",
+ "network_mount_info",
+ ]
+ for a in kwargs:
+ v = getattr(self, a)
+ if v is not None:
+ values.append(f"{a}={v!r}")
+ return "VolumeInfo(%s)" % ",".join(values)
+
+
+class TargetInfo:
+ def __init__(
+ self,
+ kind,
+ filename,
+ folder_cnid,
+ cnid,
+ creation_date,
+ creator_code,
+ type_code,
+ levels_from=-1,
+ levels_to=-1,
+ folder_name=None,
+ cnid_path=None,
+ carbon_path=None,
+ posix_path=None,
+ user_home_prefix_len=None,
+ ):
+ #: Either ALIAS_KIND_FILE or ALIAS_KIND_FOLDER
+ self.kind = kind
+
+ #: The filename of the target
+ self.filename = filename
+
+ #: The CNID (Catalog Node ID) of the target's containing folder;
+ #: CNIDs are similar to but different than traditional UNIX inode
+ #: numbers
+ self.folder_cnid = folder_cnid
+
+ #: The CNID (Catalog Node ID) of the target
+ self.cnid = cnid
+
+ #: The target's *creation* date.
+ self.creation_date = creation_date
+
+ #: The target's Mac creator code (a four-character binary string)
+ self.creator_code = creator_code
+
+ #: The target's Mac type code (a four-character binary string)
+ self.type_code = type_code
+
+ #: The depth of the alias? Always seems to be -1 on OS X.
+ self.levels_from = levels_from
+
+ #: The depth of the target? Always seems to be -1 on OS X.
+ self.levels_to = levels_to
+
+ #: The (POSIX) name of the target's containing folder. *(optional)*
+ self.folder_name = folder_name
+
+ #: The path from the volume root as a sequence of CNIDs. *(optional)*
+ self.cnid_path = cnid_path
+
+ #: The Carbon path of the target *(optional)*
+ self.carbon_path = carbon_path
+
+ #: The POSIX path of the target relative to the volume root. Note
+ #: that this may or may not have a leading '/' character, but it is
+ #: always relative to the containing volume. *(optional)*
+ self.posix_path = posix_path
+
+ #: If the path points into a user's home folder, the number of folders
+ #: deep that we go before we get to that home folder. *(optional)*
+ self.user_home_prefix_len = user_home_prefix_len
+
+ def __repr__(self):
+ args = [
+ "kind",
+ "filename",
+ "folder_cnid",
+ "cnid",
+ "creation_date",
+ "creator_code",
+ "type_code",
+ ]
+ values = []
+ for a in args:
+ v = getattr(self, a)
+ values.append(repr(v))
+
+ if self.levels_from != -1:
+ values.append("levels_from=%r" % self.levels_from)
+ if self.levels_to != -1:
+ values.append("levels_to=%r" % self.levels_to)
+
+ kwargs = [
+ "folder_name",
+ "cnid_path",
+ "carbon_path",
+ "posix_path",
+ "user_home_prefix_len",
+ ]
+ for a in kwargs:
+ v = getattr(self, a)
+ values.append(f"{a}={v!r}")
+
+ return "TargetInfo(%s)" % ",".join(values)
+
+
+TAG_CARBON_FOLDER_NAME = 0
+TAG_CNID_PATH = 1
+TAG_CARBON_PATH = 2
+TAG_APPLESHARE_ZONE = 3
+TAG_APPLESHARE_SERVER_NAME = 4
+TAG_APPLESHARE_USERNAME = 5
+TAG_DRIVER_NAME = 6
+TAG_NETWORK_MOUNT_INFO = 9
+TAG_DIALUP_INFO = 10
+TAG_UNICODE_FILENAME = 14
+TAG_UNICODE_VOLUME_NAME = 15
+TAG_HIGH_RES_VOLUME_CREATION_DATE = 16
+TAG_HIGH_RES_CREATION_DATE = 17
+TAG_POSIX_PATH = 18
+TAG_POSIX_PATH_TO_MOUNTPOINT = 19
+TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE = 20
+TAG_USER_HOME_LENGTH_PREFIX = 21
+
+
+class Alias:
+ def __init__(
+ self,
+ appinfo=b"\0\0\0\0",
+ version=2,
+ volume=None,
+ target=None,
+ extra=[],
+ ):
+ """Construct a new :class:`Alias` object with the specified
+ contents."""
+
+ #: Application specific information (four byte byte-string)
+ self.appinfo = appinfo
+
+ #: Version (we support versions 2 and 3)
+ self.version = version
+
+ #: A :class:`VolumeInfo` object describing the target's volume
+ self.volume = volume
+
+ #: A :class:`TargetInfo` object describing the target
+ self.target = target
+
+ #: A list of extra `(tag, value)` pairs
+ self.extra = list(extra)
+
+ @classmethod
+ def _from_fd(cls, b):
+ appinfo, recsize, version = struct.unpack(b">4shh", b.read(8))
+
+ if recsize < 150:
+ raise ValueError("Incorrect alias length")
+
+ if version not in (2, 3):
+ raise ValueError("Unsupported alias version %u" % version)
+
+ if version == 2:
+ (
+ kind, # h
+ volname, # 28p
+ voldate, # I
+ fstype, # 2s
+ disktype, # h
+ folder_cnid, # I
+ filename, # 64p
+ cnid, # I
+ crdate, # I
+ creator_code, # 4s
+ type_code, # 4s
+ levels_from, # h
+ levels_to, # h
+ volattrs, # I
+ volfsid, # 2s
+ reserved, # 10s
+ ) = struct.unpack(b">h28pI2shI64pII4s4shhI2s10s", b.read(142))
+ else:
+ (
+ kind, # h
+ voldate_hr, # Q
+ fstype, # 4s
+ disktype, # h
+ folder_cnid, # I
+ cnid, # I
+ crdate_hr, # Q
+ volattrs, # I
+ reserved, # 14s
+ ) = struct.unpack(b">hQ4shIIQI14s", b.read(46))
+
+ volname = b""
+ filename = b""
+ creator_code = None
+ type_code = None
+ voldate = voldate_hr / 65536.0
+ crdate = crdate_hr / 65536.0
+
+ voldate = mac_epoch + datetime.timedelta(seconds=voldate)
+ crdate = mac_epoch + datetime.timedelta(seconds=crdate)
+
+ alias = Alias()
+ alias.appinfo = appinfo
+
+ alias.volume = VolumeInfo(
+ volname.decode().replace("/", ":"),
+ voldate,
+ fstype,
+ disktype,
+ volattrs,
+ volfsid,
+ )
+ alias.target = TargetInfo(
+ kind,
+ filename.decode().replace("/", ":"),
+ folder_cnid,
+ cnid,
+ crdate,
+ creator_code,
+ type_code,
+ )
+ alias.target.levels_from = levels_from
+ alias.target.levels_to = levels_to
+
+ tag = struct.unpack(b">h", b.read(2))[0]
+
+ while tag != -1:
+ length = struct.unpack(b">h", b.read(2))[0]
+ value = b.read(length)
+ if length & 1:
+ b.read(1)
+
+ if tag == TAG_CARBON_FOLDER_NAME:
+ alias.target.folder_name = value.decode().replace("/", ":")
+ elif tag == TAG_CNID_PATH:
+ alias.target.cnid_path = struct.unpack(">%uI" % (length // 4), value)
+ elif tag == TAG_CARBON_PATH:
+ alias.target.carbon_path = value
+ elif tag == TAG_APPLESHARE_ZONE:
+ if alias.volume.appleshare_info is None:
+ alias.volume.appleshare_info = AppleShareInfo()
+ alias.volume.appleshare_info.zone = value
+ elif tag == TAG_APPLESHARE_SERVER_NAME:
+ if alias.volume.appleshare_info is None:
+ alias.volume.appleshare_info = AppleShareInfo()
+ alias.volume.appleshare_info.server = value
+ elif tag == TAG_APPLESHARE_USERNAME:
+ if alias.volume.appleshare_info is None:
+ alias.volume.appleshare_info = AppleShareInfo()
+ alias.volume.appleshare_info.user = value
+ elif tag == TAG_DRIVER_NAME:
+ alias.volume.driver_name = value
+ elif tag == TAG_NETWORK_MOUNT_INFO:
+ alias.volume.network_mount_info = value
+ elif tag == TAG_DIALUP_INFO:
+ alias.volume.dialup_info = value
+ elif tag == TAG_UNICODE_FILENAME:
+ alias.target.filename = value[2:].decode("utf-16be")
+ elif tag == TAG_UNICODE_VOLUME_NAME:
+ alias.volume.name = value[2:].decode("utf-16be")
+ elif tag == TAG_HIGH_RES_VOLUME_CREATION_DATE:
+ seconds = struct.unpack(b">Q", value)[0] / 65536.0
+ alias.volume.creation_date = mac_epoch + datetime.timedelta(
+ seconds=seconds
+ )
+ elif tag == TAG_HIGH_RES_CREATION_DATE:
+ seconds = struct.unpack(b">Q", value)[0] / 65536.0
+ alias.target.creation_date = mac_epoch + datetime.timedelta(
+ seconds=seconds
+ )
+ elif tag == TAG_POSIX_PATH:
+ alias.target.posix_path = value.decode()
+ elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT:
+ alias.volume.posix_path = value.decode()
+ elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE:
+ alias.volume.disk_image_alias = Alias.from_bytes(value)
+ elif tag == TAG_USER_HOME_LENGTH_PREFIX:
+ alias.target.user_home_prefix_len = struct.unpack(b">h", value)[0]
+ else:
+ alias.extra.append((tag, value))
+
+ tag = struct.unpack(b">h", b.read(2))[0]
+
+ return alias
+
+ @classmethod
+ def from_bytes(cls, bytes):
+ """Construct an :class:`Alias` object given binary Alias data."""
+ with io.BytesIO(bytes) as b:
+ return cls._from_fd(b)
+
+ @classmethod
+ def for_file(cls, path):
+ """Create an :class:`Alias` that points at the specified file."""
+ if sys.platform != "darwin":
+ raise Exception("Not implemented (requires special support)")
+
+ path = encode_utf8(path)
+
+ a = Alias()
+
+ # Find the filesystem
+ st = osx.statfs(path)
+ vol_path = st.f_mntonname
+
+ # File and folder names in HFS+ are normalized to a form similar to NFD.
+ # Must be normalized (NFD->NFC) before use to avoid unicode string comparison issues.
+ vol_path = normalize("NFC", vol_path.decode("utf-8")).encode("utf-8")
+
+ # Grab its attributes
+ attrs = [osx.ATTR_CMN_CRTIME, osx.ATTR_VOL_NAME, 0, 0, 0]
+ volinfo = osx.getattrlist(vol_path, attrs, 0)
+
+ vol_crtime = volinfo[0]
+ vol_name = encode_utf8(volinfo[1])
+
+ # Also grab various attributes of the file
+ attrs = [
+ osx.ATTR_CMN_OBJTYPE
+ | osx.ATTR_CMN_CRTIME
+ | osx.ATTR_CMN_FNDRINFO
+ | osx.ATTR_CMN_FILEID
+ | osx.ATTR_CMN_PARENTID,
+ 0,
+ 0,
+ 0,
+ 0,
+ ]
+ info = osx.getattrlist(path, attrs, osx.FSOPT_NOFOLLOW)
+
+ if info[0] == osx.VDIR:
+ kind = ALIAS_KIND_FOLDER
+ else:
+ kind = ALIAS_KIND_FILE
+
+ cnid = info[3]
+ folder_cnid = info[4]
+
+ dirname, filename = os.path.split(path)
+
+ if dirname == b"" or dirname == b".":
+ dirname = os.getcwd()
+
+ foldername = os.path.basename(dirname)
+
+ creation_date = info[1]
+
+ if kind == ALIAS_KIND_FILE:
+ creator_code = struct.pack(b"I", info[2].fileInfo.fileCreator)
+ type_code = struct.pack(b"I", info[2].fileInfo.fileType)
+ else:
+ creator_code = b"\0\0\0\0"
+ type_code = b"\0\0\0\0"
+
+ a.target = TargetInfo(
+ kind, filename, folder_cnid, cnid, creation_date, creator_code, type_code
+ )
+ a.volume = VolumeInfo(vol_name, vol_crtime, b"H+", ALIAS_FIXED_DISK, 0, b"\0\0")
+
+ a.target.folder_name = foldername
+ a.volume.posix_path = vol_path
+
+ rel_path = os.path.relpath(path, vol_path)
+
+ # Leave off the initial '/' if vol_path is '/' (no idea why)
+ if vol_path == b"/":
+ a.target.posix_path = rel_path
+ else:
+ a.target.posix_path = b"/" + rel_path
+
+ # Construct the Carbon and CNID paths
+ carbon_path = []
+ cnid_path = []
+ head, tail = os.path.split(rel_path)
+ if not tail:
+ head, tail = os.path.split(head)
+ while head or tail:
+ if head:
+ attrs = [osx.ATTR_CMN_FILEID, 0, 0, 0, 0]
+ info = osx.getattrlist(os.path.join(vol_path, head), attrs, 0)
+ cnid_path.append(info[0])
+ carbon_tail = tail.replace(b":", b"/")
+ carbon_path.insert(0, carbon_tail)
+ head, tail = os.path.split(head)
+
+ carbon_path = vol_name + b":" + b":\0".join(carbon_path)
+
+ a.target.carbon_path = carbon_path
+ a.target.cnid_path = cnid_path
+
+ return a
+
+ def _to_fd(self, b):
+ # We'll come back and fix the length when we're done
+ pos = b.tell()
+ b.write(struct.pack(b">4shh", self.appinfo, 0, self.version))
+
+ carbon_volname = encode_utf8(self.volume.name).replace(b":", b"/")
+ carbon_filename = encode_utf8(self.target.filename).replace(b":", b"/")
+ voldate = (self.volume.creation_date - mac_epoch).total_seconds()
+ crdate = (self.target.creation_date - mac_epoch).total_seconds()
+
+ if self.version == 2:
+ # NOTE: crdate should be in local time, but that's system dependent
+ # (so doing so is ridiculous, and nothing could rely on it).
+ b.write(
+ struct.pack(
+ b">h28pI2shI64pII4s4shhI2s10s",
+ self.target.kind, # h
+ carbon_volname, # 28p
+ int(voldate), # I
+ self.volume.fs_type, # 2s
+ self.volume.disk_type, # h
+ self.target.folder_cnid, # I
+ carbon_filename, # 64p
+ self.target.cnid, # I
+ int(crdate), # I
+ self.target.creator_code, # 4s
+ self.target.type_code, # 4s
+ self.target.levels_from, # h
+ self.target.levels_to, # h
+ self.volume.attribute_flags, # I
+ self.volume.fs_id, # 2s
+ b"\0" * 10, # 10s
+ )
+ )
+ else:
+ b.write(
+ struct.pack(
+ b">hQ4shIIQI14s",
+ self.target.kind, # h
+ int(voldate * 65536), # Q
+ self.volume.fs_type, # 4s
+ self.volume.disk_type, # h
+ self.target.folder_cnid, # I
+ self.target.cnid, # I
+ int(crdate * 65536), # Q
+ self.volume.attribute_flags, # I
+ b"\0" * 14, # 14s
+ )
+ )
+
+ # Excuse the odd order; we're copying Finder
+ if self.target.folder_name:
+ carbon_foldername = encode_utf8(self.target.folder_name).replace(b":", b"/")
+ b.write(struct.pack(b">hh", TAG_CARBON_FOLDER_NAME, len(carbon_foldername)))
+ b.write(carbon_foldername)
+ if len(carbon_foldername) & 1:
+ b.write(b"\0")
+
+ b.write(
+ struct.pack(
+ b">hhQhhQ",
+ TAG_HIGH_RES_VOLUME_CREATION_DATE,
+ 8,
+ int(voldate * 65536),
+ TAG_HIGH_RES_CREATION_DATE,
+ 8,
+ int(crdate * 65536),
+ )
+ )
+
+ if self.target.cnid_path:
+ cnid_path = struct.pack(
+ ">%uI" % len(self.target.cnid_path), *self.target.cnid_path
+ )
+ b.write(struct.pack(b">hh", TAG_CNID_PATH, len(cnid_path)))
+ b.write(cnid_path)
+
+ if self.target.carbon_path:
+ carbon_path = encode_utf8(self.target.carbon_path)
+ b.write(struct.pack(b">hh", TAG_CARBON_PATH, len(carbon_path)))
+ b.write(carbon_path)
+ if len(carbon_path) & 1:
+ b.write(b"\0")
+
+ if self.volume.appleshare_info:
+ ai = self.volume.appleshare_info
+ if ai.zone:
+ b.write(struct.pack(b">hh", TAG_APPLESHARE_ZONE, len(ai.zone)))
+ b.write(ai.zone)
+ if len(ai.zone) & 1:
+ b.write(b"\0")
+ if ai.server:
+ b.write(struct.pack(b">hh", TAG_APPLESHARE_SERVER_NAME, len(ai.server)))
+ b.write(ai.server)
+ if len(ai.server) & 1:
+ b.write(b"\0")
+ if ai.username:
+ b.write(struct.pack(b">hh", TAG_APPLESHARE_USERNAME, len(ai.username)))
+ b.write(ai.username)
+ if len(ai.username) & 1:
+ b.write(b"\0")
+
+ if self.volume.driver_name:
+ driver_name = encode_utf8(self.volume.driver_name)
+ b.write(struct.pack(b">hh", TAG_DRIVER_NAME, len(driver_name)))
+ b.write(driver_name)
+ if len(driver_name) & 1:
+ b.write(b"\0")
+
+ if self.volume.network_mount_info:
+ b.write(
+ struct.pack(
+ b">hh", TAG_NETWORK_MOUNT_INFO, len(self.volume.network_mount_info)
+ )
+ )
+ b.write(self.volume.network_mount_info)
+ if len(self.volume.network_mount_info) & 1:
+ b.write(b"\0")
+
+ if self.volume.dialup_info:
+ b.write(
+ struct.pack(
+ b">hh", TAG_DIALUP_INFO, len(self.volume.network_mount_info)
+ )
+ )
+ b.write(self.volume.network_mount_info)
+ if len(self.volume.network_mount_info) & 1:
+ b.write(b"\0")
+
+ utf16 = decode_utf8(self.target.filename).replace(":", "/").encode("utf-16-be")
+ b.write(
+ struct.pack(b">hhh", TAG_UNICODE_FILENAME, len(utf16) + 2, len(utf16) // 2)
+ )
+ b.write(utf16)
+
+ utf16 = decode_utf8(self.volume.name).replace(":", "/").encode("utf-16-be")
+ b.write(
+ struct.pack(
+ b">hhh", TAG_UNICODE_VOLUME_NAME, len(utf16) + 2, len(utf16) // 2
+ )
+ )
+ b.write(utf16)
+
+ if self.target.posix_path:
+ posix_path = encode_utf8(self.target.posix_path)
+ b.write(struct.pack(b">hh", TAG_POSIX_PATH, len(posix_path)))
+ b.write(posix_path)
+ if len(posix_path) & 1:
+ b.write(b"\0")
+
+ if self.volume.posix_path:
+ posix_path = encode_utf8(self.volume.posix_path)
+ b.write(struct.pack(b">hh", TAG_POSIX_PATH_TO_MOUNTPOINT, len(posix_path)))
+ b.write(posix_path)
+ if len(posix_path) & 1:
+ b.write(b"\0")
+
+ if self.volume.disk_image_alias:
+ d = self.volume.disk_image_alias.to_bytes()
+ b.write(struct.pack(b">hh", TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE, len(d)))
+ b.write(d)
+ if len(d) & 1:
+ b.write(b"\0")
+
+ if self.target.user_home_prefix_len is not None:
+ b.write(
+ struct.pack(
+ b">hhh",
+ TAG_USER_HOME_LENGTH_PREFIX,
+ 2,
+ self.target.user_home_prefix_len,
+ )
+ )
+
+ for t, v in self.extra:
+ b.write(struct.pack(b">hh", t, len(v)))
+ b.write(v)
+ if len(v) & 1:
+ b.write(b"\0")
+
+ b.write(struct.pack(b">hh", -1, 0))
+
+ blen = b.tell() - pos
+ b.seek(pos + 4, os.SEEK_SET)
+ b.write(struct.pack(b">h", blen))
+
+ def to_bytes(self):
+ """Returns the binary representation for this :class:`Alias`."""
+ with io.BytesIO() as b:
+ self._to_fd(b)
+ return b.getvalue()
+
+ def __str__(self):
+ return "<Alias target=%s>" % self.target.filename
+
+ def __repr__(self):
+ values = []
+ if self.appinfo != b"\0\0\0\0":
+ values.append("appinfo=%r" % self.appinfo)
+ if self.version != 2:
+ values.append("version=%r" % self.version)
+ if self.volume is not None:
+ values.append("volume=%r" % self.volume)
+ if self.target is not None:
+ values.append("target=%r" % self.target)
+ if self.extra:
+ values.append("extra=%r" % self.extra)
+ return "Alias(%s)" % ",".join(values)
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/bookmark.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/bookmark.py
new file mode 100644
index 000000000..00e16feb1
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/bookmark.py
@@ -0,0 +1,677 @@
+# This file implements the Apple "bookmark" format, which is the replacement
+# for the old-fashioned alias format. The details of this format were
+# reverse engineered; some things are still not entirely clear.
+#
+import datetime
+import os
+import struct
+import sys
+import uuid
+from urllib.parse import urljoin
+
+if sys.platform == "darwin":
+ from . import osx
+
+from .utils import osx_epoch
+
+BMK_DATA_TYPE_MASK = 0xFFFFFF00
+BMK_DATA_SUBTYPE_MASK = 0x000000FF
+
+BMK_STRING = 0x0100
+BMK_DATA = 0x0200
+BMK_NUMBER = 0x0300
+BMK_DATE = 0x0400
+BMK_BOOLEAN = 0x0500
+BMK_ARRAY = 0x0600
+BMK_DICT = 0x0700
+BMK_UUID = 0x0800
+BMK_URL = 0x0900
+BMK_NULL = 0x0A00
+
+BMK_ST_ZERO = 0x0000
+BMK_ST_ONE = 0x0001
+
+BMK_BOOLEAN_ST_FALSE = 0x0000
+BMK_BOOLEAN_ST_TRUE = 0x0001
+
+# Subtypes for BMK_NUMBER are really CFNumberType values
+kCFNumberSInt8Type = 1
+kCFNumberSInt16Type = 2
+kCFNumberSInt32Type = 3
+kCFNumberSInt64Type = 4
+kCFNumberFloat32Type = 5
+kCFNumberFloat64Type = 6
+kCFNumberCharType = 7
+kCFNumberShortType = 8
+kCFNumberIntType = 9
+kCFNumberLongType = 10
+kCFNumberLongLongType = 11
+kCFNumberFloatType = 12
+kCFNumberDoubleType = 13
+kCFNumberCFIndexType = 14
+kCFNumberNSIntegerType = 15
+kCFNumberCGFloatType = 16
+
+# Resource property flags (from CFURLPriv.h)
+kCFURLResourceIsRegularFile = 0x00000001
+kCFURLResourceIsDirectory = 0x00000002
+kCFURLResourceIsSymbolicLink = 0x00000004
+kCFURLResourceIsVolume = 0x00000008
+kCFURLResourceIsPackage = 0x00000010
+kCFURLResourceIsSystemImmutable = 0x00000020
+kCFURLResourceIsUserImmutable = 0x00000040
+kCFURLResourceIsHidden = 0x00000080
+kCFURLResourceHasHiddenExtension = 0x00000100
+kCFURLResourceIsApplication = 0x00000200
+kCFURLResourceIsCompressed = 0x00000400
+kCFURLResourceIsSystemCompressed = 0x00000400
+kCFURLCanSetHiddenExtension = 0x00000800
+kCFURLResourceIsReadable = 0x00001000
+kCFURLResourceIsWriteable = 0x00002000
+kCFURLResourceIsExecutable = 0x00004000
+kCFURLIsAliasFile = 0x00008000
+kCFURLIsMountTrigger = 0x00010000
+
+# Volume property flags (from CFURLPriv.h)
+kCFURLVolumeIsLocal = 0x1
+kCFURLVolumeIsAutomount = 0x2
+kCFURLVolumeDontBrowse = 0x4
+kCFURLVolumeIsReadOnly = 0x8
+kCFURLVolumeIsQuarantined = 0x10
+kCFURLVolumeIsEjectable = 0x20
+kCFURLVolumeIsRemovable = 0x40
+kCFURLVolumeIsInternal = 0x80
+kCFURLVolumeIsExternal = 0x100
+kCFURLVolumeIsDiskImage = 0x200
+kCFURLVolumeIsFileVault = 0x400
+kCFURLVolumeIsLocaliDiskMirror = 0x800
+kCFURLVolumeIsiPod = 0x1000
+kCFURLVolumeIsiDisk = 0x2000
+kCFURLVolumeIsCD = 0x4000
+kCFURLVolumeIsDVD = 0x8000
+kCFURLVolumeIsDeviceFileSystem = 0x10000
+kCFURLVolumeSupportsPersistentIDs = 0x100000000
+kCFURLVolumeSupportsSearchFS = 0x200000000
+kCFURLVolumeSupportsExchange = 0x400000000
+# reserved 0x800000000
+kCFURLVolumeSupportsSymbolicLinks = 0x1000000000
+kCFURLVolumeSupportsDenyModes = 0x2000000000
+kCFURLVolumeSupportsCopyFile = 0x4000000000
+kCFURLVolumeSupportsReadDirAttr = 0x8000000000
+kCFURLVolumeSupportsJournaling = 0x10000000000
+kCFURLVolumeSupportsRename = 0x20000000000
+kCFURLVolumeSupportsFastStatFS = 0x40000000000
+kCFURLVolumeSupportsCaseSensitiveNames = 0x80000000000
+kCFURLVolumeSupportsCasePreservedNames = 0x100000000000
+kCFURLVolumeSupportsFLock = 0x200000000000
+kCFURLVolumeHasNoRootDirectoryTimes = 0x400000000000
+kCFURLVolumeSupportsExtendedSecurity = 0x800000000000
+kCFURLVolumeSupports2TBFileSize = 0x1000000000000
+kCFURLVolumeSupportsHardLinks = 0x2000000000000
+kCFURLVolumeSupportsMandatoryByteRangeLocks = 0x4000000000000
+kCFURLVolumeSupportsPathFromID = 0x8000000000000
+# reserved 0x10000000000000
+kCFURLVolumeIsJournaling = 0x20000000000000
+kCFURLVolumeSupportsSparseFiles = 0x40000000000000
+kCFURLVolumeSupportsZeroRuns = 0x80000000000000
+kCFURLVolumeSupportsVolumeSizes = 0x100000000000000
+kCFURLVolumeSupportsRemoteEvents = 0x200000000000000
+kCFURLVolumeSupportsHiddenFiles = 0x400000000000000
+kCFURLVolumeSupportsDecmpFSCompression = 0x800000000000000
+kCFURLVolumeHas64BitObjectIDs = 0x1000000000000000
+kCFURLVolumePropertyFlagsAll = 0xFFFFFFFFFFFFFFFF
+
+BMK_URL_ST_ABSOLUTE = 0x0001
+BMK_URL_ST_RELATIVE = 0x0002
+
+# Bookmark keys
+kBookmarkURL = 0x1003 # A URL
+kBookmarkPath = 0x1004 # Array of path components
+kBookmarkCNIDPath = 0x1005 # Array of CNIDs
+kBookmarkFileProperties = (
+ 0x1010 # (CFURL rp flags, CFURL rp flags asked for, 8 bytes NULL)
+)
+kBookmarkFileName = 0x1020
+kBookmarkFileID = 0x1030
+kBookmarkFileCreationDate = 0x1040
+# = 0x1054 # ?
+# = 0x1055 # ?
+# = 0x1056 # ?
+# = 0x1101 # ?
+# = 0x1102 # ?
+kBookmarkTOCPath = 0x2000 # A list of (TOC id, ?) pairs
+kBookmarkVolumePath = 0x2002
+kBookmarkVolumeURL = 0x2005
+kBookmarkVolumeName = 0x2010
+kBookmarkVolumeUUID = 0x2011 # Stored (perversely) as a string
+kBookmarkVolumeSize = 0x2012
+kBookmarkVolumeCreationDate = 0x2013
+kBookmarkVolumeProperties = (
+ 0x2020 # (CFURL vp flags, CFURL vp flags asked for, 8 bytes NULL)
+)
+kBookmarkVolumeIsRoot = 0x2030 # True if volume is FS root
+kBookmarkVolumeBookmark = 0x2040 # Embedded bookmark for disk image (TOC id)
+kBookmarkVolumeMountPoint = 0x2050 # A URL
+# = 0x2070
+kBookmarkContainingFolder = 0xC001 # Index of containing folder in path
+kBookmarkUserName = 0xC011 # User that created bookmark
+kBookmarkUID = 0xC012 # UID that created bookmark
+kBookmarkWasFileReference = 0xD001 # True if the URL was a file reference
+kBookmarkCreationOptions = 0xD010
+kBookmarkURLLengths = 0xE003 # See below
+kBookmarkDisplayName = 0xF017
+kBookmarkIconData = 0xF020
+kBookmarkIconRef = 0xF021
+kBookmarkTypeBindingData = 0xF022
+kBookmarkCreationTime = 0xF030
+kBookmarkSandboxRwExtension = 0xF080
+kBookmarkSandboxRoExtension = 0xF081
+kBookmarkAliasData = 0xFE00
+
+# Alias for backwards compatibility
+kBookmarkSecurityExtension = kBookmarkSandboxRwExtension
+
+# kBookmarkURLLengths is an array that is set if the URL encoded by the
+# bookmark had a base URL; in that case, each entry is the length of the
+# base URL in question. Thus a URL
+#
+# file:///foo/bar/baz blam/blat.html
+#
+# will result in [3, 2], while the URL
+#
+# file:///foo bar/baz blam blat.html
+#
+# would result in [1, 2, 1, 1]
+
+
+class Data:
+ def __init__(self, bytedata=None):
+ #: The bytes, stored as a byte string
+ self.bytes = bytes(bytedata)
+
+ def __repr__(self):
+ return "Data(%r)" % self.bytes
+
+
+class URL:
+ def __init__(self, base, rel=None):
+ if rel is not None:
+ #: The base URL, if any (a :class:`URL`)
+ self.base = base
+ #: The rest of the URL (a string)
+ self.relative = rel
+ else:
+ self.base = None
+ self.relative = base
+
+ @property
+ def absolute(self):
+ """Return an absolute URL."""
+ if self.base is None:
+ return self.relative
+ else:
+ return urljoin(self.base.absolute, self.relative)
+
+ def __repr__(self):
+ return "URL(%r)" % self.absolute
+
+
+class Bookmark:
+ def __init__(self, tocs=None):
+ if tocs is None:
+ #: The TOCs for this Bookmark
+ self.tocs = []
+ else:
+ self.tocs = tocs
+
+ @classmethod
+ def _get_item(cls, data, hdrsize, offset):
+ offset += hdrsize
+ if offset > len(data) - 8:
+ raise ValueError("Offset out of range")
+
+ length, typecode = struct.unpack(b"<II", data[offset : offset + 8])
+
+ if len(data) - offset < 8 + length:
+ raise ValueError("Data item truncated")
+
+ databytes = data[offset + 8 : offset + 8 + length]
+
+ dsubtype = typecode & BMK_DATA_SUBTYPE_MASK
+ dtype = typecode & BMK_DATA_TYPE_MASK
+
+ if dtype == BMK_STRING:
+ return databytes.decode("utf-8")
+ elif dtype == BMK_DATA:
+ return Data(databytes)
+ elif dtype == BMK_NUMBER:
+ if dsubtype == kCFNumberSInt8Type:
+ return ord(databytes[0])
+ elif dsubtype == kCFNumberSInt16Type:
+ return struct.unpack(b"<h", databytes)[0]
+ elif dsubtype == kCFNumberSInt32Type:
+ return struct.unpack(b"<i", databytes)[0]
+ elif dsubtype == kCFNumberSInt64Type:
+ return struct.unpack(b"<q", databytes)[0]
+ elif dsubtype == kCFNumberFloat32Type:
+ return struct.unpack(b"<f", databytes)[0]
+ elif dsubtype == kCFNumberFloat64Type:
+ return struct.unpack(b"<d", databytes)[0]
+ elif dtype == BMK_DATE:
+ # Yes, dates really are stored as *BIG-endian* doubles; everything
+ # else is little-endian
+ secs = datetime.timedelta(seconds=struct.unpack(b">d", databytes)[0])
+ return osx_epoch + secs
+ elif dtype == BMK_BOOLEAN:
+ if dsubtype == BMK_BOOLEAN_ST_TRUE:
+ return True
+ elif dsubtype == BMK_BOOLEAN_ST_FALSE:
+ return False
+ elif dtype == BMK_UUID:
+ return uuid.UUID(bytes=databytes)
+ elif dtype == BMK_URL:
+ if dsubtype == BMK_URL_ST_ABSOLUTE:
+ return URL(databytes.decode("utf-8"))
+ elif dsubtype == BMK_URL_ST_RELATIVE:
+ baseoff, reloff = struct.unpack(b"<II", databytes)
+ base = cls._get_item(data, hdrsize, baseoff)
+ rel = cls._get_item(data, hdrsize, reloff)
+ return URL(base, rel)
+ elif dtype == BMK_ARRAY:
+ result = []
+ for aoff in range(offset + 8, offset + 8 + length, 4):
+ (eltoff,) = struct.unpack(b"<I", data[aoff : aoff + 4])
+ result.append(cls._get_item(data, hdrsize, eltoff))
+ return result
+ elif dtype == BMK_DICT:
+ result = {}
+ for eoff in range(offset + 8, offset + 8 + length, 8):
+ keyoff, valoff = struct.unpack(b"<II", data[eoff : eoff + 8])
+ key = cls._get_item(data, hdrsize, keyoff)
+ val = cls._get_item(data, hdrsize, valoff)
+ result[key] = val
+ return result
+ elif dtype == BMK_NULL:
+ return None
+
+ print("Unknown data type %08x" % typecode)
+ return (typecode, databytes)
+
+ @classmethod
+ def from_bytes(cls, data):
+ """Create a :class:`Bookmark` given byte data."""
+
+ if len(data) < 16:
+ raise ValueError("Not a bookmark file (too short)")
+
+ if isinstance(data, bytearray):
+ data = bytes(data)
+
+ magic, size, dummy, hdrsize = struct.unpack(b"<4sIII", data[0:16])
+
+ if magic not in (b"book", b"alis"):
+ raise ValueError("Not a bookmark file (bad magic) %r" % magic)
+
+ if hdrsize < 16:
+ raise ValueError("Not a bookmark file (header size too short)")
+
+ if hdrsize > size:
+ raise ValueError("Not a bookmark file (header size too large)")
+
+ if size != len(data):
+ raise ValueError("Not a bookmark file (truncated)")
+
+ (tocoffset,) = struct.unpack(b"<I", data[hdrsize : hdrsize + 4])
+
+ tocs = []
+
+ while tocoffset != 0:
+ tocbase = hdrsize + tocoffset
+ if (tocoffset > size - hdrsize) or (size - tocbase < 20):
+ raise ValueError("TOC offset out of range")
+
+ (tocsize, tocmagic, tocid, nexttoc, toccount) = struct.unpack(
+ b"<IIIII", data[tocbase : tocbase + 20]
+ )
+
+ if tocmagic != 0xFFFFFFFE:
+ break
+
+ tocsize += 8
+
+ if size - tocbase < tocsize:
+ raise ValueError("TOC truncated")
+
+ if tocsize < 12 * toccount:
+ raise ValueError("TOC entries overrun TOC size")
+
+ toc = {}
+ for n in range(0, toccount):
+ ebase = tocbase + 20 + 12 * n
+ eid, eoffset, edummy = struct.unpack(b"<III", data[ebase : ebase + 12])
+
+ if eid & 0x80000000:
+ eid = cls._get_item(data, hdrsize, eid & 0x7FFFFFFF)
+
+ toc[eid] = cls._get_item(data, hdrsize, eoffset)
+
+ tocs.append((tocid, toc))
+
+ tocoffset = nexttoc
+
+ return cls(tocs)
+
+ def __getitem__(self, key):
+ for tid, toc in self.tocs:
+ if key in toc:
+ return toc[key]
+ raise KeyError("Key not found")
+
+ def __setitem__(self, key, value):
+ if len(self.tocs) == 0:
+ self.tocs = [(1, {})]
+ self.tocs[0][1][key] = value
+
+ def get(self, key, default=None):
+ """Lookup the value for a given key, returning a default if not
+ present."""
+ for tid, toc in self.tocs:
+ if key in toc:
+ return toc[key]
+ return default
+
+ @classmethod
+ def _encode_item(cls, item, offset):
+ if item is True:
+ result = struct.pack(b"<II", 0, BMK_BOOLEAN | BMK_BOOLEAN_ST_TRUE)
+ elif item is False:
+ result = struct.pack(b"<II", 0, BMK_BOOLEAN | BMK_BOOLEAN_ST_FALSE)
+ elif isinstance(item, str):
+ encoded = item.encode("utf-8")
+ result = (
+ struct.pack(b"<II", len(encoded), BMK_STRING | BMK_ST_ONE) + encoded
+ )
+ elif isinstance(item, bytes):
+ result = struct.pack(b"<II", len(item), BMK_STRING | BMK_ST_ONE) + item
+ elif isinstance(item, Data):
+ result = struct.pack(
+ b"<II", len(item.bytes), BMK_DATA | BMK_ST_ONE
+ ) + bytes(item.bytes)
+ elif isinstance(item, bytearray):
+ result = struct.pack(b"<II", len(item), BMK_DATA | BMK_ST_ONE) + bytes(item)
+ elif isinstance(item, int):
+ if item > -0x80000000 and item < 0x7FFFFFFF:
+ result = struct.pack(b"<IIi", 4, BMK_NUMBER | kCFNumberSInt32Type, item)
+ else:
+ result = struct.pack(b"<IIq", 8, BMK_NUMBER | kCFNumberSInt64Type, item)
+ elif isinstance(item, float):
+ result = struct.pack(b"<IId", 8, BMK_NUMBER | kCFNumberFloat64Type, item)
+ elif isinstance(item, datetime.datetime):
+ secs = item - osx_epoch
+ result = struct.pack(b"<II", 8, BMK_DATE | BMK_ST_ZERO) + struct.pack(
+ b">d", float(secs.total_seconds())
+ )
+
+ elif isinstance(item, uuid.UUID):
+ result = struct.pack(b"<II", 16, BMK_UUID | BMK_ST_ONE) + item.bytes
+ elif isinstance(item, URL):
+ if item.base:
+ baseoff = offset + 16
+ reloff, baseenc = cls._encode_item(item.base, baseoff)
+ xoffset, relenc = cls._encode_item(item.relative, reloff)
+ result = b"".join(
+ [
+ struct.pack(
+ b"<IIII", 8, BMK_URL | BMK_URL_ST_RELATIVE, baseoff, reloff
+ ),
+ baseenc,
+ relenc,
+ ]
+ )
+ else:
+ encoded = item.relative.encode("utf-8")
+ result = (
+ struct.pack(b"<II", len(encoded), BMK_URL | BMK_URL_ST_ABSOLUTE)
+ + encoded
+ )
+ elif isinstance(item, list):
+ ioffset = offset + 8 + len(item) * 4
+ result = [struct.pack(b"<II", len(item) * 4, BMK_ARRAY | BMK_ST_ONE)]
+ enc = []
+ for elt in item:
+ result.append(struct.pack(b"<I", ioffset))
+ ioffset, ienc = cls._encode_item(elt, ioffset)
+ enc.append(ienc)
+ result = b"".join(result + enc)
+ elif isinstance(item, dict):
+ ioffset = offset + 8 + len(item) * 8
+ result = [struct.pack(b"<II", len(item) * 8, BMK_DICT | BMK_ST_ONE)]
+ enc = []
+ for k, v in item.items():
+ result.append(struct.pack(b"<I", ioffset))
+ ioffset, ienc = cls._encode_item(k, ioffset)
+ enc.append(ienc)
+ result.append(struct.pack(b"<I", ioffset))
+ ioffset, ienc = cls._encode_item(v, ioffset)
+ enc.append(ienc)
+ result = b"".join(result + enc)
+ elif item is None:
+ result = struct.pack(b"<II", 0, BMK_NULL | BMK_ST_ONE)
+ else:
+ raise ValueError("Unknown item type when encoding: %s" % item)
+
+ offset += len(result)
+
+ # Pad to a multiple of 4 bytes
+ if offset & 3:
+ extra = 4 - (offset & 3)
+ result += b"\0" * extra
+ offset += extra
+
+ return (offset, result)
+
+ def to_bytes(self):
+ """Convert this :class:`Bookmark` to a byte representation."""
+
+ result = []
+ tocs = []
+ offset = 4 # For the offset to the first TOC
+
+ # Generate the data and build the TOCs
+ for tid, toc in self.tocs:
+ entries = []
+
+ for k, v in toc.items():
+ if isinstance(k, str):
+ noffset = offset
+ voffset, enc = self._encode_item(k, offset)
+ result.append(enc)
+ offset, enc = self._encode_item(v, voffset)
+ result.append(enc)
+ entries.append((noffset | 0x80000000, voffset))
+ else:
+ entries.append((k, offset))
+ offset, enc = self._encode_item(v, offset)
+ result.append(enc)
+
+ # TOC entries must be sorted - CoreServicesInternal does a
+ # binary search to find data
+ entries.sort()
+
+ tocs.append(
+ (tid, b"".join([struct.pack(b"<III", k, o, 0) for k, o in entries]))
+ )
+
+ first_toc_offset = offset
+
+ # Now generate the TOC headers
+ for ndx, toc in enumerate(tocs):
+ tid, data = toc
+ if ndx == len(tocs) - 1:
+ next_offset = 0
+ else:
+ next_offset = offset + 20 + len(data)
+
+ result.append(
+ struct.pack(
+ b"<IIIII",
+ len(data) - 8,
+ 0xFFFFFFFE,
+ tid,
+ next_offset,
+ len(data) // 12,
+ )
+ )
+ result.append(data)
+
+ offset += 20 + len(data)
+
+ # Finally, add the header (and the first TOC offset, which isn't part
+ # of the header, but goes just after it)
+ header = struct.pack(
+ b"<4sIIIQQQQI",
+ b"book",
+ offset + 48,
+ 0x10040000,
+ 48,
+ 0,
+ 0,
+ 0,
+ 0,
+ first_toc_offset,
+ )
+
+ result.insert(0, header)
+
+ return b"".join(result)
+
+ @classmethod
+ def for_file(cls, path):
+ """Construct a :class:`Bookmark` for a given file."""
+
+ # Find the filesystem
+ st = osx.statfs(path)
+ vol_path = st.f_mntonname.decode("utf-8")
+
+ # Grab its attributes
+ attrs = [
+ osx.ATTR_CMN_CRTIME,
+ osx.ATTR_VOL_SIZE | osx.ATTR_VOL_NAME | osx.ATTR_VOL_UUID,
+ 0,
+ 0,
+ 0,
+ ]
+ volinfo = osx.getattrlist(vol_path, attrs, 0)
+
+ vol_crtime = volinfo[0]
+ vol_size = volinfo[1]
+ vol_name = volinfo[2]
+ vol_uuid = volinfo[3]
+
+ # Also grab various attributes of the file
+ attrs = [
+ (osx.ATTR_CMN_OBJTYPE | osx.ATTR_CMN_CRTIME | osx.ATTR_CMN_FILEID),
+ 0,
+ 0,
+ 0,
+ 0,
+ ]
+ info = osx.getattrlist(path, attrs, osx.FSOPT_NOFOLLOW)
+
+ cnid = info[2]
+ crtime = info[1]
+
+ if info[0] == osx.VREG:
+ flags = kCFURLResourceIsRegularFile
+ elif info[0] == osx.VDIR:
+ flags = kCFURLResourceIsDirectory
+ elif info[0] == osx.VLNK:
+ flags = kCFURLResourceIsSymbolicLink
+ else:
+ flags = kCFURLResourceIsRegularFile
+
+ dirname, filename = os.path.split(path)
+
+ relcount = 0
+ if not os.path.isabs(dirname):
+ curdir = os.getcwd()
+ head, tail = os.path.split(curdir)
+ relcount = 0
+ while head and tail:
+ relcount += 1
+ head, tail = os.path.split(head)
+ dirname = os.path.join(curdir, dirname)
+
+ # ?? foldername = os.path.basename(dirname)
+
+ rel_path = os.path.relpath(path, vol_path)
+
+ # Build the path arrays
+ name_path = []
+ cnid_path = []
+ head, tail = os.path.split(rel_path)
+ if not tail:
+ head, tail = os.path.split(head)
+ while head or tail:
+ if head:
+ attrs = [osx.ATTR_CMN_FILEID, 0, 0, 0, 0]
+ info = osx.getattrlist(os.path.join(vol_path, head), attrs, 0)
+ cnid_path.insert(0, info[0])
+ head, tail = os.path.split(head)
+ name_path.insert(0, tail)
+ else:
+ head, tail = os.path.split(head)
+ name_path.append(filename)
+ cnid_path.append(cnid)
+
+ url_lengths = [relcount, len(name_path) - relcount]
+
+ fileprops = Data(struct.pack(b"<QQQ", flags, 0x0F, 0))
+ volprops = Data(
+ struct.pack(
+ b"<QQQ",
+ 0x81 | kCFURLVolumeSupportsPersistentIDs,
+ 0x13EF | kCFURLVolumeSupportsPersistentIDs,
+ 0,
+ )
+ )
+
+ toc = {
+ kBookmarkPath: name_path,
+ kBookmarkCNIDPath: cnid_path,
+ kBookmarkFileCreationDate: crtime,
+ kBookmarkFileProperties: fileprops,
+ kBookmarkContainingFolder: len(name_path) - 2,
+ kBookmarkVolumePath: vol_path,
+ kBookmarkVolumeIsRoot: vol_path == "/",
+ kBookmarkVolumeURL: URL("file://" + vol_path),
+ kBookmarkVolumeName: vol_name,
+ kBookmarkVolumeSize: vol_size,
+ kBookmarkVolumeCreationDate: vol_crtime,
+ kBookmarkVolumeUUID: str(vol_uuid).upper(),
+ kBookmarkVolumeProperties: volprops,
+ kBookmarkCreationOptions: 512,
+ kBookmarkWasFileReference: True,
+ kBookmarkUserName: "unknown",
+ kBookmarkUID: 99,
+ }
+
+ if relcount:
+ toc[kBookmarkURLLengths] = url_lengths
+
+ return Bookmark([(1, toc)])
+
+ def __repr__(self):
+ result = ["Bookmark(["]
+ for tid, toc in self.tocs:
+ result.append("(0x%x, {\n" % tid)
+ for k, v in toc.items():
+ if isinstance(k, str):
+ kf = repr(k)
+ else:
+ kf = "0x%04x" % k
+ result.append(f" {kf}: {v!r}\n")
+ result.append("}),\n")
+ result.append("])")
+
+ return "".join(result)
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/osx.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/osx.py
new file mode 100644
index 000000000..1b2feabaa
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/osx.py
@@ -0,0 +1,1074 @@
+import datetime
+import os
+import uuid
+from ctypes import (
+ POINTER,
+ Structure,
+ Union,
+ byref,
+ c_byte,
+ c_char,
+ c_char_p,
+ c_int,
+ c_long,
+ c_longlong,
+ c_short,
+ c_uint,
+ c_ulong,
+ c_ulonglong,
+ c_ushort,
+ c_void_p,
+ cdll,
+ create_string_buffer,
+ sizeof,
+)
+
+from .utils import unix_epoch
+
+libc = cdll.LoadLibrary("/usr/lib/libc.dylib")
+
+# Constants
+FSOPT_NOFOLLOW = 0x00000001
+FSOPT_NOINMEMUPDATE = 0x00000002
+FSOPT_REPORT_FULLSIZE = 0x00000004
+FSOPT_PACK_INVAL_ATTRS = 0x00000008
+FSOPT_ATTR_CMN_EXTENDED = 0x00000020
+FSOPT_RETURN_REALDEV = 0x00000200
+
+VOL_CAPABILITIES_FORMAT = 0
+VOL_CAPABILITIES_INTERFACES = 1
+
+VOL_CAP_FMT_PERSISTENTOBJECTIDS = 0x00000001
+VOL_CAP_FMT_SYMBOLICLINKS = 0x00000002
+VOL_CAP_FMT_HARDLINKS = 0x00000004
+VOL_CAP_FMT_JOURNAL = 0x00000008
+VOL_CAP_FMT_JOURNAL_ACTIVE = 0x00000010
+VOL_CAP_FMT_NO_ROOT_TIMES = 0x00000020
+VOL_CAP_FMT_SPARSE_FILES = 0x00000040
+VOL_CAP_FMT_ZERO_RUNS = 0x00000080
+VOL_CAP_FMT_CASE_SENSITIVE = 0x00000100
+VOL_CAP_FMT_CASE_PRESERVING = 0x00000200
+VOL_CAP_FMT_FAST_STATFS = 0x00000400
+VOL_CAP_FMT_2TB_FILESIZE = 0x00000800
+VOL_CAP_FMT_OPENDENYMODES = 0x00001000
+VOL_CAP_FMT_HIDDEN_FILES = 0x00002000
+VOL_CAP_FMT_PATH_FROM_ID = 0x00004000
+VOL_CAP_FMT_NO_VOLUME_SIZES = 0x00008000
+VOL_CAP_FMT_DECMPFS_COMPRESSION = 0x00010000
+VOL_CAP_FMT_64BIT_OBJECT_IDS = 0x00020000
+
+VOL_CAP_INT_SEARCHFS = 0x00000001
+VOL_CAP_INT_ATTRLIST = 0x00000002
+VOL_CAP_INT_NFSEXPORT = 0x00000004
+VOL_CAP_INT_READDIRATTR = 0x00000008
+VOL_CAP_INT_EXCHANGEDATA = 0x00000010
+VOL_CAP_INT_COPYFILE = 0x00000020
+VOL_CAP_INT_ALLOCATE = 0x00000040
+VOL_CAP_INT_VOL_RENAME = 0x00000080
+VOL_CAP_INT_ADVLOCK = 0x00000100
+VOL_CAP_INT_FLOCK = 0x00000200
+VOL_CAP_INT_EXTENDED_SECURITY = 0x00000400
+VOL_CAP_INT_USERACCESS = 0x00000800
+VOL_CAP_INT_MANLOCK = 0x00001000
+VOL_CAP_INT_NAMEDSTREAMS = 0x00002000
+VOL_CAP_INT_EXTENDED_ATTR = 0x00004000
+VOL_CAP_INT_CLONE = 0x00010000
+VOL_CAP_INT_SNAPSHOT = 0x00020000
+VOL_CAP_INT_RENAME_SWAP = 0x00040000
+VOL_CAP_INT_RENAME_EXCL = 0x00080000
+VOL_CAP_INT_RENAME_OPENFAIL = 0x00100000
+
+ATTR_CMN_NAME = 0x00000001
+ATTR_CMN_DEVID = 0x00000002
+ATTR_CMN_FSID = 0x00000004
+ATTR_CMN_OBJTYPE = 0x00000008
+ATTR_CMN_OBJTAG = 0x00000010
+ATTR_CMN_OBJID = 0x00000020
+ATTR_CMN_OBJPERMANENTID = 0x00000040
+ATTR_CMN_PAROBJID = 0x00000080
+ATTR_CMN_SCRIPT = 0x00000100
+ATTR_CMN_CRTIME = 0x00000200
+ATTR_CMN_MODTIME = 0x00000400
+ATTR_CMN_CHGTIME = 0x00000800
+ATTR_CMN_ACCTIME = 0x00001000
+ATTR_CMN_BKUPTIME = 0x00002000
+ATTR_CMN_FNDRINFO = 0x00004000
+ATTR_CMN_OWNERID = 0x00008000
+ATTR_CMN_GRPID = 0x00010000
+ATTR_CMN_ACCESSMASK = 0x00020000
+ATTR_CMN_FLAGS = 0x00040000
+ATTR_CMN_GEN_COUNT = 0x00080000
+ATTR_CMN_DOCUMENT_ID = 0x00100000
+ATTR_CMN_USERACCESS = 0x00200000
+ATTR_CMN_EXTENDED_SECURITY = 0x00400000
+ATTR_CMN_UUID = 0x00800000
+ATTR_CMN_GRPUUID = 0x01000000
+ATTR_CMN_FILEID = 0x02000000
+ATTR_CMN_PARENTID = 0x04000000
+ATTR_CMN_FULLPATH = 0x08000000
+ATTR_CMN_ADDEDTIME = 0x10000000
+ATTR_CMN_ERROR = 0x20000000
+ATTR_CMN_DATA_PROTECT_FLAGS = 0x40000000
+ATTR_CMN_RETURNED_ATTRS = 0x80000000
+ATTR_CMN_ALL_ATTRS = 0xFFFFFFFF
+
+ATTR_CMN_VALIDMASK = 0xFFFFFFFF
+ATTR_CMN_SETMASK = 0x51C7FF00
+ATTR_CMN_VOLSETMASK = 0x00006700
+
+ATTR_VOL_FSTYPE = 0x00000001
+ATTR_VOL_SIGNATURE = 0x00000002
+ATTR_VOL_SIZE = 0x00000004
+ATTR_VOL_SPACEFREE = 0x00000008
+ATTR_VOL_SPACEAVAIL = 0x00000010
+ATTR_VOL_MINALLOCATION = 0x00000020
+ATTR_VOL_ALLOCATIONCLUMP = 0x00000040
+ATTR_VOL_IOBLOCKSIZE = 0x00000080
+ATTR_VOL_OBJCOUNT = 0x00000100
+ATTR_VOL_FILECOUNT = 0x00000200
+ATTR_VOL_DIRCOUNT = 0x00000400
+ATTR_VOL_MAXOBJCOUNT = 0x00000800
+ATTR_VOL_MOUNTPOINT = 0x00001000
+ATTR_VOL_NAME = 0x00002000
+ATTR_VOL_MOUNTFLAGS = 0x00004000
+ATTR_VOL_MOUNTEDDEVICE = 0x00008000
+ATTR_VOL_ENCODINGSUSED = 0x00010000
+ATTR_VOL_CAPABILITIES = 0x00020000
+ATTR_VOL_UUID = 0x00040000
+ATTR_VOL_QUOTA_SIZE = 0x10000000
+ATTR_VOL_RESERVED_SIZE = 0x20000000
+ATTR_VOL_ATTRIBUTES = 0x40000000
+ATTR_VOL_INFO = 0x80000000
+ATTR_VOL_ALL_ATTRS = 0xF007FFFF
+
+ATTR_DIR_LINKCOUNT = 0x00000001
+ATTR_DIR_ENTRYCOUNT = 0x00000002
+ATTR_DIR_MOUNTSTATUS = 0x00000004
+DIR_MNTSTATUS_MNTPOINT = 0x00000001
+DIR_MNTSTATUS_TRIGGER = 0x00000002
+ATTR_DIR_ALLOCSIZE = 0x00000008
+ATTR_DIR_IOBLOCKSIZE = 0x00000010
+ATTR_DIR_DATALENGTH = 0x00000020
+ATTR_DIR_ALL_ATTRS = 0x0000003F
+
+ATTR_DIR_VALIDMASK = 0x0000003F
+ATTR_DIR_SETMASK = 0x00000000
+
+ATTR_FILE_LINKCOUNT = 0x00000001
+ATTR_FILE_TOTALSIZE = 0x00000002
+ATTR_FILE_ALLOCSIZE = 0x00000004
+ATTR_FILE_IOBLOCKSIZE = 0x00000008
+ATTR_FILE_DEVTYPE = 0x00000020
+ATTR_FILE_FORKCOUNT = 0x00000080
+ATTR_FILE_FORKLIST = 0x00000100
+ATTR_FILE_DATALENGTH = 0x00000200
+ATTR_FILE_DATAALLOCSIZE = 0x00000400
+ATTR_FILE_RSRCLENGTH = 0x00001000
+ATTR_FILE_RSRCALLOCSIZE = 0x00002000
+ATTR_FILE_ALL_ATTRS = 0x000037FF
+
+ATTR_FILE_VALIDMASK = 0x000037FF
+ATTR_FILE_SETMASK = 0x00000020
+
+# These are deprecated
+ATTR_FORK_TOTALSIZE = 0x00000001
+ATTR_FORK_ALLOCSIZE = 0x00000002
+ATTR_FORK_ALL_ATTRS = 0x00000003
+
+# These go in the fork attribute field
+ATTR_CMNEXT_RELPATH = 0x00000004
+ATTR_CMNEXT_PRIVATESIZE = 0x00000008
+ATTR_CMNEXT_LINKID = 0x0000010
+ATTR_CMNEXT_NOFIRMLINKPATH = 0x00000020
+ATTR_CMNEXT_REALDEVID = 0x00000040
+ATTR_CMNEXT_REALFSID = 0x00000080
+ATTR_CMNEXT_CLONEID = 0x00000100
+ATTR_CMNEXT_EXT_FLAGS = 0x00000200
+ATTR_CMNEXT_RECURSIVE_GENCOUNT = 0x00000400
+ATTR_CMNEXT_ALL_ATTRS = 0x000007FC
+
+ATTR_CMNEXT_VALIDMASK = 0x000007FC
+ATTR_CMNEXT_SETMASK = 0x00000000
+
+ATTR_FORK_VALIDMASK = 0x00000003
+ATTR_FORK_SETMASK = 0x00000000
+
+# These can't be used
+ATTR_CMN_NAMEDATTRCOUNT = 0x00080000
+ATTR_CMN_NAMEDATTRLIST = 0x00100000
+ATTR_FILE_CLUMPSIZE = 0x00000010
+ATTR_FILE_FILETYPE = 0x00000040
+ATTR_FILE_DATAEXTENTS = 0x00000800
+ATTR_FILE_RSRCEXTENTS = 0x00004000
+
+
+class attrlist(Structure):
+ _fields_ = [
+ ("bitmapcount", c_ushort),
+ ("reserved", c_ushort),
+ ("commonattr", c_uint),
+ ("volattr", c_uint),
+ ("dirattr", c_uint),
+ ("fileattr", c_uint),
+ ("forkattr", c_uint),
+ ]
+
+
+class attribute_set_t(Structure):
+ _fields_ = [
+ ("commonattr", c_uint),
+ ("volattr", c_uint),
+ ("dirattr", c_uint),
+ ("fileattr", c_uint),
+ ("forkattr", c_uint),
+ ]
+
+
+class fsobj_id_t(Structure):
+ _fields_ = [
+ ("fid_objno", c_uint),
+ ("fid_generation", c_uint),
+ ]
+
+
+class timespec(Structure):
+ _fields_ = [
+ ("tv_sec", c_long),
+ ("tv_nsec", c_long),
+ ]
+
+
+class attrreference_t(Structure):
+ _fields_ = [
+ ("attr_dataoffset", c_int),
+ ("attr_length", c_uint),
+ ]
+
+
+class fsid_t(Structure):
+ _fields_ = [
+ ("val", c_uint * 2),
+ ]
+
+
+class guid_t(Structure):
+ _fields_ = [
+ ("g_guid", c_byte * 16),
+ ]
+
+
+class kauth_ace(Structure):
+ _fields_ = [
+ ("ace_applicable", guid_t),
+ ("ace_flags", c_uint),
+ ]
+
+
+class kauth_acl(Structure):
+ _fields_ = [
+ ("acl_entrycount", c_uint),
+ ("acl_flags", c_uint),
+ ("acl_ace", kauth_ace * 128),
+ ]
+
+
+class kauth_filesec(Structure):
+ _fields_ = [
+ ("fsec_magic", c_uint),
+ ("fsec_owner", guid_t),
+ ("fsec_group", guid_t),
+ ("fsec_acl", kauth_acl),
+ ]
+
+
+class diskextent(Structure):
+ _fields_ = [
+ ("startblock", c_uint),
+ ("blockcount", c_uint),
+ ]
+
+
+OSType = c_uint
+UInt16 = c_ushort
+SInt16 = c_short
+SInt32 = c_int
+
+
+class Point(Structure):
+ _fields_ = [
+ ("x", SInt16),
+ ("y", SInt16),
+ ]
+
+
+class Rect(Structure):
+ _fields_ = [
+ ("x", SInt16),
+ ("y", SInt16),
+ ("w", SInt16),
+ ("h", SInt16),
+ ]
+
+
+class FileInfo(Structure):
+ _fields_ = [
+ ("fileType", OSType),
+ ("fileCreator", OSType),
+ ("finderFlags", UInt16),
+ ("location", Point),
+ ("reservedField", UInt16),
+ ("reserved1", SInt16 * 4),
+ ("extendedFinderFlags", UInt16),
+ ("reserved2", SInt16),
+ ("putAwayFolderID", SInt32),
+ ]
+
+
+class FolderInfo(Structure):
+ _fields_ = [
+ ("windowBounds", Rect),
+ ("finderFlags", UInt16),
+ ("location", Point),
+ ("reservedField", UInt16),
+ ("scrollPosition", Point),
+ ("reserved1", SInt32),
+ ("extendedFinderFlags", UInt16),
+ ("reserved2", SInt16),
+ ("putAwayFolderID", SInt32),
+ ]
+
+
+class FinderInfo(Union):
+ _fields_ = [
+ ("fileInfo", FileInfo),
+ ("folderInfo", FolderInfo),
+ ]
+
+
+extentrecord = diskextent * 8
+vol_capabilities_set_t = c_uint * 4
+
+
+class vol_capabilities_attr_t(Structure):
+ _fields_ = [
+ ("capabilities", vol_capabilities_set_t),
+ ("valid", vol_capabilities_set_t),
+ ]
+
+
+class vol_attributes_attr_t(Structure):
+ _fields_ = [
+ ("validattr", attribute_set_t),
+ ("nativeattr", attribute_set_t),
+ ]
+
+
+dev_t = c_uint
+
+fsobj_type_t = c_uint
+
+VNON = 0
+VREG = 1
+VDIR = 2
+VBLK = 3
+VCHR = 4
+VLNK = 5
+VSOCK = 6
+VFIFO = 7
+VBAD = 8
+VSTR = 9
+VCPLX = 10
+
+fsobj_tag_t = c_uint
+
+VT_NON = 0
+VT_UFS = 1
+VT_NFS = 2
+VT_MFS = 3
+VT_MSDOSFS = 4
+VT_LFS = 5
+VT_LOFS = 6
+VT_FDESC = 7
+VT_PORTAL = 8
+VT_NULL = 9
+VT_UMAP = 10
+VT_KERNFS = 11
+VT_PROCFS = 12
+VT_AFS = 13
+VT_ISOFS = 14
+VT_UNION = 15
+VT_HFS = 16
+VT_ZFS = 17
+VT_DEVFS = 18
+VT_WEBDAV = 19
+VT_UDF = 20
+VT_AFP = 21
+VT_CDDA = 22
+VT_CIFS = 23
+VT_OTHER = 24
+
+fsfile_type_t = c_uint
+fsvolid_t = c_uint
+text_encoding_t = c_uint
+uid_t = c_uint
+gid_t = c_uint
+int32_t = c_int
+uint32_t = c_uint
+int64_t = c_longlong
+uint64_t = c_ulonglong
+off_t = c_long
+size_t = c_ulong
+uuid_t = c_byte * 16
+
+NAME_MAX = 255
+PATH_MAX = 1024
+FSTYPE_MAX = 16
+
+
+class struct_statfs(Structure):
+ _fields_ = [
+ ("f_bsize", uint32_t),
+ ("f_iosize", int32_t),
+ ("f_blocks", uint64_t),
+ ("f_bfree", uint64_t),
+ ("f_bavail", uint64_t),
+ ("f_files", uint64_t),
+ ("f_ffree", uint64_t),
+ ("f_fsid", fsid_t),
+ ("f_owner", uid_t),
+ ("f_type", uint32_t),
+ ("f_flags", uint32_t),
+ ("f_fssubtype", uint32_t),
+ ("f_fstypename", c_char * FSTYPE_MAX),
+ ("f_mntonname", c_char * PATH_MAX),
+ ("f_mntfromname", c_char * PATH_MAX),
+ ("f_flags_ext", uint32_t),
+ ("f_reserved", uint32_t * 7),
+ ]
+
+
+# Calculate the maximum number of bytes required for the attribute buffer
+_attr_info = (
+ # Common attributes
+ (0, ATTR_CMN_RETURNED_ATTRS, sizeof(attribute_set_t)),
+ (0, ATTR_CMN_NAME, sizeof(attrreference_t) + NAME_MAX * 3 + 1),
+ (0, ATTR_CMN_DEVID, sizeof(dev_t)),
+ (0, ATTR_CMN_FSID, sizeof(fsid_t)),
+ (0, ATTR_CMN_OBJTYPE, sizeof(fsobj_type_t)),
+ (0, ATTR_CMN_OBJTAG, sizeof(fsobj_tag_t)),
+ (0, ATTR_CMN_OBJID, sizeof(fsobj_id_t)),
+ (0, ATTR_CMN_OBJPERMANENTID, sizeof(fsobj_id_t)),
+ (0, ATTR_CMN_PAROBJID, sizeof(fsobj_id_t)),
+ (0, ATTR_CMN_SCRIPT, sizeof(text_encoding_t)),
+ (0, ATTR_CMN_CRTIME, sizeof(timespec)),
+ (0, ATTR_CMN_MODTIME, sizeof(timespec)),
+ (0, ATTR_CMN_CHGTIME, sizeof(timespec)),
+ (0, ATTR_CMN_ACCTIME, sizeof(timespec)),
+ (0, ATTR_CMN_BKUPTIME, sizeof(timespec)),
+ (0, ATTR_CMN_FNDRINFO, sizeof(FinderInfo)),
+ (0, ATTR_CMN_OWNERID, sizeof(uid_t)),
+ (0, ATTR_CMN_GRPID, sizeof(gid_t)),
+ (0, ATTR_CMN_ACCESSMASK, sizeof(uint32_t)),
+ (0, ATTR_CMN_NAMEDATTRCOUNT, None),
+ (0, ATTR_CMN_NAMEDATTRLIST, None),
+ (0, ATTR_CMN_FLAGS, sizeof(uint32_t)),
+ (0, ATTR_CMN_GEN_COUNT, sizeof(uint32_t)),
+ (0, ATTR_CMN_DOCUMENT_ID, sizeof(uint32_t)),
+ (0, ATTR_CMN_USERACCESS, sizeof(uint32_t)),
+ (0, ATTR_CMN_EXTENDED_SECURITY, sizeof(attrreference_t) + sizeof(kauth_filesec)),
+ (0, ATTR_CMN_UUID, sizeof(guid_t)),
+ (0, ATTR_CMN_GRPUUID, sizeof(guid_t)),
+ (0, ATTR_CMN_FILEID, sizeof(uint64_t)),
+ (0, ATTR_CMN_PARENTID, sizeof(uint64_t)),
+ (0, ATTR_CMN_FULLPATH, sizeof(attrreference_t) + PATH_MAX),
+ (0, ATTR_CMN_ADDEDTIME, sizeof(timespec)),
+ (0, ATTR_CMN_DATA_PROTECT_FLAGS, sizeof(uint32_t)),
+ # Volume attributes
+ (1, ATTR_VOL_FSTYPE, sizeof(uint32_t)),
+ (1, ATTR_VOL_SIGNATURE, sizeof(uint32_t)),
+ (1, ATTR_VOL_SIZE, sizeof(off_t)),
+ (1, ATTR_VOL_SPACEFREE, sizeof(off_t)),
+ (1, ATTR_VOL_SPACEAVAIL, sizeof(off_t)),
+ (1, ATTR_VOL_MINALLOCATION, sizeof(off_t)),
+ (1, ATTR_VOL_ALLOCATIONCLUMP, sizeof(off_t)),
+ (1, ATTR_VOL_IOBLOCKSIZE, sizeof(uint32_t)),
+ (1, ATTR_VOL_OBJCOUNT, sizeof(uint32_t)),
+ (1, ATTR_VOL_FILECOUNT, sizeof(uint32_t)),
+ (1, ATTR_VOL_DIRCOUNT, sizeof(uint32_t)),
+ (1, ATTR_VOL_MAXOBJCOUNT, sizeof(uint32_t)),
+ (1, ATTR_VOL_MOUNTPOINT, sizeof(attrreference_t) + PATH_MAX),
+ (1, ATTR_VOL_NAME, sizeof(attrreference_t) + NAME_MAX + 1),
+ (1, ATTR_VOL_MOUNTFLAGS, sizeof(uint32_t)),
+ (1, ATTR_VOL_MOUNTEDDEVICE, sizeof(attrreference_t) + PATH_MAX),
+ (1, ATTR_VOL_ENCODINGSUSED, sizeof(c_ulonglong)),
+ (1, ATTR_VOL_CAPABILITIES, sizeof(vol_capabilities_attr_t)),
+ (1, ATTR_VOL_UUID, sizeof(uuid_t)),
+ (1, ATTR_VOL_QUOTA_SIZE, sizeof(off_t)),
+ (1, ATTR_VOL_RESERVED_SIZE, sizeof(off_t)),
+ (1, ATTR_VOL_ATTRIBUTES, sizeof(vol_attributes_attr_t)),
+ # Directory attributes
+ (2, ATTR_DIR_LINKCOUNT, sizeof(uint32_t)),
+ (2, ATTR_DIR_ENTRYCOUNT, sizeof(uint32_t)),
+ (2, ATTR_DIR_MOUNTSTATUS, sizeof(uint32_t)),
+ (2, ATTR_DIR_ALLOCSIZE, sizeof(off_t)),
+ (2, ATTR_DIR_IOBLOCKSIZE, sizeof(uint32_t)),
+ (2, ATTR_DIR_DATALENGTH, sizeof(off_t)),
+ # File attributes
+ (3, ATTR_FILE_LINKCOUNT, sizeof(uint32_t)),
+ (3, ATTR_FILE_TOTALSIZE, sizeof(off_t)),
+ (3, ATTR_FILE_ALLOCSIZE, sizeof(off_t)),
+ (3, ATTR_FILE_IOBLOCKSIZE, sizeof(uint32_t)),
+ (3, ATTR_FILE_CLUMPSIZE, sizeof(uint32_t)),
+ (3, ATTR_FILE_DEVTYPE, sizeof(uint32_t)),
+ (3, ATTR_FILE_FILETYPE, sizeof(uint32_t)),
+ (3, ATTR_FILE_FORKCOUNT, sizeof(uint32_t)),
+ (3, ATTR_FILE_FORKLIST, None),
+ (3, ATTR_FILE_DATALENGTH, sizeof(off_t)),
+ (3, ATTR_FILE_DATAALLOCSIZE, sizeof(off_t)),
+ (3, ATTR_FILE_DATAEXTENTS, sizeof(extentrecord)),
+ (3, ATTR_FILE_RSRCLENGTH, sizeof(off_t)),
+ (3, ATTR_FILE_RSRCALLOCSIZE, sizeof(off_t)),
+ (3, ATTR_FILE_RSRCEXTENTS, sizeof(extentrecord)),
+ # Fork attributes
+ (4, ATTR_FORK_TOTALSIZE, sizeof(off_t)),
+ (4, ATTR_FORK_ALLOCSIZE, sizeof(off_t)),
+ # Extended common attributes
+ (4, ATTR_CMNEXT_RELPATH, sizeof(attrreference_t) + PATH_MAX),
+ (4, ATTR_CMNEXT_PRIVATESIZE, sizeof(off_t)),
+ (4, ATTR_CMNEXT_LINKID, sizeof(uint64_t)),
+ (4, ATTR_CMNEXT_NOFIRMLINKPATH, sizeof(attrreference_t) + PATH_MAX),
+ (4, ATTR_CMNEXT_REALDEVID, sizeof(dev_t)),
+ (4, ATTR_CMNEXT_REALFSID, sizeof(fsid_t)),
+ (4, ATTR_CMNEXT_CLONEID, sizeof(uint64_t)),
+ (4, ATTR_CMNEXT_EXT_FLAGS, sizeof(uint64_t)),
+)
+
+
+def _attrbuf_size(attrs):
+ size = 4
+ for entry in _attr_info:
+ if attrs[entry[0]] & entry[1]:
+ if entry[2] is None:
+ raise ValueError(
+ "Unsupported attribute (%u, %x)" % (entry[0], entry[1])
+ )
+ size += entry[2]
+ return size
+
+
+_getattrlist = libc.getattrlist
+_getattrlist.argtypes = [c_char_p, POINTER(attrlist), c_void_p, c_ulong, c_ulong]
+_getattrlist.restype = c_int
+
+_fgetattrlist = libc.fgetattrlist
+_fgetattrlist.argtypes = [c_int, POINTER(attrlist), c_void_p, c_ulong, c_ulong]
+_fgetattrlist.restype = c_int
+
+try:
+ _statfs = libc["statfs$INODE64"]
+except (KeyError, AttributeError):
+ _statfs = libc["statfs"]
+
+_statfs.argtypes = [
+ c_char_p,
+ POINTER(struct_statfs),
+]
+_statfs.restype = c_int
+
+try:
+ _fstatfs = libc["fstatfs$INODE64"]
+except (KeyError, AttributeError):
+ _fstatfs = libc["fstatfs"]
+
+_fstatfs.argtypes = [
+ c_int,
+ POINTER(struct_statfs),
+]
+_fstatfs.restype = c_int
+
+
+def _datetime_from_timespec(ts):
+ td = datetime.timedelta(seconds=ts.tv_sec + 1.0e-9 * ts.tv_nsec)
+ return unix_epoch + td
+
+
+def _decode_utf8_nul(sz):
+ nul = sz.find(b"\0")
+ if nul > -1:
+ sz = sz[:nul]
+ return sz.decode("utf-8")
+
+
+def _decode_attrlist_result(buf, attrs, options):
+ result = []
+
+ assert len(buf) >= 4
+ total_size = uint32_t.from_buffer(buf, 0).value
+ assert total_size <= len(buf)
+
+ offset = 4
+
+ # Common attributes
+ if attrs[0] & ATTR_CMN_RETURNED_ATTRS:
+ a = attribute_set_t.from_buffer(buf, offset)
+ result.append(a)
+ offset += sizeof(attribute_set_t)
+ if not (options & FSOPT_PACK_INVAL_ATTRS):
+ attrs = [a.commonattr, a.volattr, a.dirattr, a.fileattr, a.forkattr]
+ if attrs[0] & ATTR_CMN_NAME:
+ a = attrreference_t.from_buffer(buf, offset)
+ ofs = offset + a.attr_dataoffset
+ name = _decode_utf8_nul(buf[ofs : ofs + a.attr_length])
+ offset += sizeof(attrreference_t)
+ result.append(name)
+ if attrs[0] & ATTR_CMN_DEVID:
+ a = dev_t.from_buffer(buf, offset)
+ offset += sizeof(dev_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_FSID:
+ a = fsid_t.from_buffer(buf, offset)
+ offset += sizeof(fsid_t)
+ result.append(a)
+ if attrs[0] & ATTR_CMN_OBJTYPE:
+ a = fsobj_type_t.from_buffer(buf, offset)
+ offset += sizeof(fsobj_type_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_OBJTAG:
+ a = fsobj_tag_t.from_buffer(buf, offset)
+ offset += sizeof(fsobj_tag_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_OBJID:
+ a = fsobj_id_t.from_buffer(buf, offset)
+ offset += sizeof(fsobj_id_t)
+ result.append(a)
+ if attrs[0] & ATTR_CMN_OBJPERMANENTID:
+ a = fsobj_id_t.from_buffer(buf, offset)
+ offset += sizeof(fsobj_id_t)
+ result.append(a)
+ if attrs[0] & ATTR_CMN_PAROBJID:
+ a = fsobj_id_t.from_buffer(buf, offset)
+ offset += sizeof(fsobj_id_t)
+ result.append(a)
+ if attrs[0] & ATTR_CMN_SCRIPT:
+ a = text_encoding_t.from_buffer(buf, offset)
+ offset += sizeof(text_encoding_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_CRTIME:
+ a = timespec.from_buffer(buf, offset)
+ offset += sizeof(timespec)
+ result.append(_datetime_from_timespec(a))
+ if attrs[0] & ATTR_CMN_MODTIME:
+ a = timespec.from_buffer(buf, offset)
+ offset += sizeof(timespec)
+ result.append(_datetime_from_timespec(a))
+ if attrs[0] & ATTR_CMN_CHGTIME:
+ a = timespec.from_buffer(buf, offset)
+ offset += sizeof(timespec)
+ result.append(_datetime_from_timespec(a))
+ if attrs[0] & ATTR_CMN_ACCTIME:
+ a = timespec.from_buffer(buf, offset)
+ offset += sizeof(timespec)
+ result.append(_datetime_from_timespec(a))
+ if attrs[0] & ATTR_CMN_BKUPTIME:
+ a = timespec.from_buffer(buf, offset)
+ offset += sizeof(timespec)
+ result.append(_datetime_from_timespec(a))
+ if attrs[0] & ATTR_CMN_FNDRINFO:
+ a = FinderInfo.from_buffer(buf, offset)
+ offset += sizeof(FinderInfo)
+ result.append(a)
+ if attrs[0] & ATTR_CMN_OWNERID:
+ a = uid_t.from_buffer(buf, offset)
+ offset += sizeof(uid_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_GRPID:
+ a = gid_t.from_buffer(buf, offset)
+ offset += sizeof(gid_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_ACCESSMASK:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_FLAGS:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_GEN_COUNT:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_DOCUMENT_ID:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_USERACCESS:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_EXTENDED_SECURITY:
+ a = attrreference_t.from_buffer(buf, offset)
+ ofs = offset + a.attr_dataoffset
+ offset += sizeof(attrreference_t)
+ ec = uint32_t.from_buffer(buf, ofs + 36).value
+
+ class kauth_acl(Structure):
+ _fields_ = [
+ ("acl_entrycount", c_uint),
+ ("acl_flags", c_uint),
+ ("acl_ace", kauth_ace * ec),
+ ]
+
+ class kauth_filesec(Structure):
+ _fields_ = [
+ ("fsec_magic", c_uint),
+ ("fsec_owner", guid_t),
+ ("fsec_group", guid_t),
+ ("fsec_acl", kauth_acl),
+ ]
+
+ a = kauth_filesec.from_buffer(buf, ofs)
+ result.append(a)
+ if attrs[0] & ATTR_CMN_UUID:
+ result.append(uuid.UUID(bytes=buf[offset : offset + 16]))
+ offset += sizeof(guid_t)
+ if attrs[0] & ATTR_CMN_GRPUUID:
+ result.append(uuid.UUID(bytes=buf[offset : offset + 16]))
+ offset += sizeof(guid_t)
+ if attrs[0] & ATTR_CMN_FILEID:
+ a = uint64_t.from_buffer(buf, offset)
+ offset += sizeof(uint64_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_PARENTID:
+ a = uint64_t.from_buffer(buf, offset)
+ offset += sizeof(uint64_t)
+ result.append(a.value)
+ if attrs[0] & ATTR_CMN_FULLPATH:
+ a = attrreference_t.from_buffer(buf, offset)
+ ofs = offset + a.attr_dataoffset
+ path = _decode_utf8_nul(buf[ofs : ofs + a.attr_length])
+ offset += sizeof(attrreference_t)
+ result.append(path)
+ if attrs[0] & ATTR_CMN_ADDEDTIME:
+ a = timespec.from_buffer(buf, offset)
+ offset += sizeof(timespec)
+ result.append(_datetime_from_timespec(a))
+ if attrs[0] & ATTR_CMN_DATA_PROTECT_FLAGS:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+
+ # Volume attributes
+ if attrs[1] & ATTR_VOL_FSTYPE:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_SIGNATURE:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_SIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_SPACEFREE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_SPACEAVAIL:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_MINALLOCATION:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_ALLOCATIONCLUMP:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_IOBLOCKSIZE:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_OBJCOUNT:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_FILECOUNT:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_DIRCOUNT:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_MAXOBJCOUNT:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_MOUNTPOINT:
+ a = attrreference_t.from_buffer(buf, offset)
+ ofs = offset + a.attr_dataoffset
+ path = _decode_utf8_nul(buf[ofs : ofs + a.attr_length])
+ offset += sizeof(attrreference_t)
+ result.append(path)
+ if attrs[1] & ATTR_VOL_NAME:
+ a = attrreference_t.from_buffer(buf, offset)
+ ofs = offset + a.attr_dataoffset
+ name = _decode_utf8_nul(buf[ofs : ofs + a.attr_length])
+ offset += sizeof(attrreference_t)
+ result.append(name)
+ if attrs[1] & ATTR_VOL_MOUNTFLAGS:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_MOUNTEDDEVICE:
+ a = attrreference_t.from_buffer(buf, offset)
+ ofs = offset + a.attr_dataoffset
+ path = _decode_utf8_nul(buf[ofs : ofs + a.attr_length])
+ offset += sizeof(attrreference_t)
+ result.append(path)
+ if attrs[1] & ATTR_VOL_ENCODINGSUSED:
+ a = c_ulonglong.from_buffer(buf, offset)
+ offset += sizeof(c_ulonglong)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_CAPABILITIES:
+ a = vol_capabilities_attr_t.from_buffer(buf, offset)
+ offset += sizeof(vol_capabilities_attr_t)
+ result.append(a)
+ if attrs[1] & ATTR_VOL_UUID:
+ result.append(uuid.UUID(bytes=buf[offset : offset + 16]))
+ offset += sizeof(uuid_t)
+ if attrs[1] & ATTR_VOL_QUOTA_SIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_RESERVED_SIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[1] & ATTR_VOL_ATTRIBUTES:
+ a = vol_attributes_attr_t.from_buffer(buf, offset)
+ offset += sizeof(vol_attributes_attr_t)
+ result.append(a)
+
+ # Directory attributes
+ if attrs[2] & ATTR_DIR_LINKCOUNT:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[2] & ATTR_DIR_ENTRYCOUNT:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[2] & ATTR_DIR_MOUNTSTATUS:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[2] & ATTR_DIR_ALLOCSIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[2] & ATTR_DIR_IOBLOCKSIZE:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[2] & ATTR_DIR_DATALENGTH:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+
+ # File attributes
+ if attrs[3] & ATTR_FILE_LINKCOUNT:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_TOTALSIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_ALLOCSIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_IOBLOCKSIZE:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_CLUMPSIZE:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_DEVTYPE:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_FILETYPE:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_FORKCOUNT:
+ a = uint32_t.from_buffer(buf, offset)
+ offset += sizeof(uint32_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_DATALENGTH:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_DATAALLOCSIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_DATAEXTENTS:
+ a = extentrecord.from_buffer(buf, offset)
+ offset += sizeof(extentrecord)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_RSRCLENGTH:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_RSRCALLOCSIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[3] & ATTR_FILE_RSRCEXTENTS:
+ a = extentrecord.from_buffer(buf, offset)
+ offset += sizeof(extentrecord)
+ result.append(a.value)
+
+ # Fork attributes
+ if attrs[4] & ATTR_FORK_TOTALSIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[4] & ATTR_FORK_ALLOCSIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+
+ # Extended common attributes
+ if attrs[4] & ATTR_CMNEXT_RELPATH:
+ a = attrreference_t.from_buffer(buf, offset)
+ ofs = offset + a.attr_dataoffset
+ path = _decode_utf8_nul(buf[ofs : ofs + a.attr_length])
+ offset += sizeof(attrreference_t)
+ result.append(path)
+ if attrs[4] & ATTR_CMNEXT_PRIVATESIZE:
+ a = off_t.from_buffer(buf, offset)
+ offset += sizeof(off_t)
+ result.append(a.value)
+ if attrs[4] & ATTR_CMNEXT_LINKID:
+ a = uint64_t.from_buffer(buf, offset)
+ offset += sizeof(uint64_t)
+ result.append(a.value)
+ if attrs[4] & ATTR_CMNEXT_NOFIRMLINKPATH:
+ a = attrreference_t.from_buffer(buf, offset)
+ ofs = offset + a.attr_dataoffset
+ path = _decode_utf8_nul(buf[ofs : ofs + a.attr_length])
+ offset += sizeof(attrreference_t)
+ result.append(path)
+ if attrs[4] & ATTR_CMNEXT_REALDEVID:
+ a = dev_t.from_buffer(buf, offset)
+ offset += sizeof(dev_t)
+ result.append(a.value)
+ if attrs[4] & ATTR_CMNEXT_REALFSID:
+ a = fsid_t.from_buffer(buf, offset)
+ offset += sizeof(fsid_t)
+ result.append(a.value)
+ if attrs[4] & ATTR_CMNEXT_CLONEID:
+ a = uint64_t.from_buffer(buf, offset)
+ offset += sizeof(uint64_t)
+ result.append(a.value)
+ if attrs[4] & ATTR_CMNEXT_EXT_FLAGS:
+ a = uint64_t.from_buffer(buf, offset)
+ offset += sizeof(uint64_t)
+ result.append(a.value)
+
+ return result
+
+
+# Sadly, ctypes.get_errno() seems not to work
+__error = libc.__error
+__error.restype = POINTER(c_int)
+
+
+def _get_errno():
+ return __error().contents.value
+
+
+def getattrlist(path, attrs, options):
+ if not isinstance(path, bytes):
+ path = path.encode("utf-8")
+ attrs = list(attrs)
+ if attrs[1]:
+ attrs[1] |= ATTR_VOL_INFO
+ alist = attrlist(
+ bitmapcount=5,
+ commonattr=attrs[0],
+ volattr=attrs[1],
+ dirattr=attrs[2],
+ fileattr=attrs[3],
+ forkattr=attrs[4],
+ )
+
+ bufsize = _attrbuf_size(attrs)
+ buf = create_string_buffer(bufsize)
+
+ ret = _getattrlist(
+ path, byref(alist), buf, bufsize, options | FSOPT_REPORT_FULLSIZE
+ )
+
+ if ret < 0:
+ err = _get_errno()
+ raise OSError(err, os.strerror(err), path)
+
+ return _decode_attrlist_result(buf, attrs, options)
+
+
+def fgetattrlist(fd, attrs, options):
+ if hasattr(fd, "fileno"):
+ fd = fd.fileno()
+ attrs = list(attrs)
+ if attrs[1]:
+ attrs[1] |= ATTR_VOL_INFO
+ alist = attrlist(
+ bitmapcount=5,
+ commonattr=attrs[0],
+ volattr=attrs[1],
+ dirattr=attrs[2],
+ fileattr=attrs[3],
+ forkattr=attrs[4],
+ )
+
+ bufsize = _attrbuf_size(attrs)
+ buf = create_string_buffer(bufsize)
+
+ ret = _fgetattrlist(fd, byref(alist), buf, bufsize, options | FSOPT_REPORT_FULLSIZE)
+
+ if ret < 0:
+ err = _get_errno()
+ raise OSError(err, os.strerror(err))
+
+ return _decode_attrlist_result(buf, attrs, options)
+
+
+def statfs(path):
+ if not isinstance(path, bytes):
+ path = path.encode("utf-8")
+ result = struct_statfs()
+ ret = _statfs(path, byref(result))
+ if ret < 0:
+ err = _get_errno()
+ raise OSError(err, os.strerror(err), path)
+ return result
+
+
+def fstatfs(fd):
+ if hasattr(fd, "fileno"):
+ fd = fd.fileno()
+ result = struct_statfs()
+ ret = _fstatfs(fd, byref(result))
+ if ret < 0:
+ err = _get_errno()
+ raise OSError(err, os.strerror(err))
+ return result
diff --git a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/qt_attribution.json b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/qt_attribution.json
index 968b6318c..968b6318c 100644
--- a/src/3rdparty/python/lib/python2.7/site-packages/mac_alias/qt_attribution.json
+++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/qt_attribution.json
diff --git a/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/utils.py b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/utils.py
new file mode 100644
index 000000000..3bf45155c
--- /dev/null
+++ b/src/3rdparty/python/lib/python3.9/site-packages/mac_alias/utils.py
@@ -0,0 +1,20 @@
+import datetime
+
+ZERO = datetime.timedelta(0)
+
+
+class UTC(datetime.tzinfo):
+ def utcoffset(self, dt):
+ return ZERO
+
+ def dst(self, dt):
+ return ZERO
+
+ def tzname(self, dt):
+ return "UTC"
+
+
+utc = UTC()
+mac_epoch = datetime.datetime(1904, 1, 1, 0, 0, 0, 0, utc)
+unix_epoch = datetime.datetime(1970, 1, 1, 0, 0, 0, 0, utc)
+osx_epoch = datetime.datetime(2001, 1, 1, 0, 0, 0, 0, utc)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 000000000..278b86ad1
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_subdirectory(app)
+add_subdirectory(lib)
+add_subdirectory(libexec)
+add_subdirectory(plugins)
+add_subdirectory(shared)
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
new file mode 100644
index 000000000..3a4ab38e0
--- /dev/null
+++ b/src/app/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_subdirectory(config)
+add_subdirectory(config-ui)
+add_subdirectory(qbs)
+add_subdirectory(qbs-create-project)
+add_subdirectory(qbs-setup-android)
+add_subdirectory(qbs-setup-qt)
+add_subdirectory(qbs-setup-toolchains)
+add_subdirectory(shared)
diff --git a/src/app/app.pri b/src/app/app.pri
deleted file mode 100644
index d0c94a770..000000000
--- a/src/app/app.pri
+++ /dev/null
@@ -1,23 +0,0 @@
-include(../install_prefix.pri)
-
-QT = core
-TEMPLATE = app
-DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_PROCESS_COMBINED_ARGUMENT_START
-!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++14
-
-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
deleted file mode 100644
index 935ce1776..000000000
--- a/src/app/app.pro
+++ /dev/null
@@ -1,10 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS =\
- qbs\
- qbs-create-project \
- qbs-setup-android \
- qbs-setup-toolchains \
- qbs-setup-qt \
- config
-
-!isEmpty(QT.widgets.name):SUBDIRS += config-ui
diff --git a/src/app/apps.qbs b/src/app/apps.qbs
index 1fcb15e43..4ea0a2c87 100644
--- a/src/app/apps.qbs
+++ b/src/app/apps.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
references: [
"config/config.qbs",
@@ -9,5 +7,6 @@ Project {
"qbs-setup-android/qbs-setup-android.qbs",
"qbs-setup-qt/qbs-setup-qt.qbs",
"qbs-setup-toolchains/qbs-setup-toolchains.qbs",
+ "shared/shared.qbs",
]
}
diff --git a/src/app/config-ui/CMakeLists.txt b/src/app/config-ui/CMakeLists.txt
new file mode 100644
index 000000000..f3375d5ea
--- /dev/null
+++ b/src/app/config-ui/CMakeLists.txt
@@ -0,0 +1,19 @@
+set(SOURCES
+ commandlineparser.cpp
+ commandlineparser.h
+ main.cpp
+ mainwindow.cpp
+ mainwindow.h
+ mainwindow.ui
+ )
+
+# TODO: support Info.plist
+if(APPLE)
+ set(MACOS_SOURCES fgapp.mm)
+ set(MACOS_FRAMEWORKS "-framework ApplicationServices" "-framework Cocoa")
+endif()
+
+add_qbs_app(qbs-config-ui
+ DEPENDS qbscore qbsconsolelogger Qt${QT_VERSION_MAJOR}::Widgets ${MACOS_FRAMEWORKS}
+ SOURCES ${SOURCES} ${MACOS_SOURCES}
+ )
diff --git a/src/app/config-ui/config-ui.pro b/src/app/config-ui/config-ui.pro
deleted file mode 100644
index 84d3b3e4d..000000000
--- a/src/app/config-ui/config-ui.pro
+++ /dev/null
@@ -1,26 +0,0 @@
-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
index 2222e6a17..544542ca4 100644
--- a/src/app/config-ui/config-ui.qbs
+++ b/src/app/config-ui/config-ui.qbs
@@ -1,5 +1,3 @@
-import qbs 1.0
-
QbsApp {
Depends { name: "Qt.widgets" }
name: "qbs-config-ui"
@@ -24,6 +22,7 @@ QbsApp {
Properties {
condition: qbs.targetOS.contains("macos")
cpp.frameworks: ["ApplicationServices", "Cocoa"]
+ bundle.isBundle: false
}
Properties {
diff --git a/src/app/config-ui/main.cpp b/src/app/config-ui/main.cpp
index a7c2662e6..dcf538989 100644
--- a/src/app/config-ui/main.cpp
+++ b/src/app/config-ui/main.cpp
@@ -47,8 +47,6 @@
#include <cstdlib>
#include <iostream>
-using qbs::Internal::Tr;
-
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
diff --git a/src/app/config-ui/mainwindow.cpp b/src/app/config-ui/mainwindow.cpp
index 2bf7fad5e..31239dc41 100644
--- a/src/app/config-ui/mainwindow.cpp
+++ b/src/app/config-ui/mainwindow.cpp
@@ -48,7 +48,12 @@
#include <QtGui/qevent.h>
#include <QtGui/qkeysequence.h>
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QtGui/qaction.h>
+#else
#include <QtWidgets/qaction.h>
+#endif
+
#include <QtWidgets/qlineedit.h>
#include <QtWidgets/qmenu.h>
#include <QtWidgets/qmenubar.h>
@@ -62,7 +67,7 @@ class TrimValidator : public QValidator
public:
explicit TrimValidator(QObject *parent = nullptr) : QValidator(parent) {}
-public: // QValidator interface
+ // QValidator interface
State validate(QString &input, int &pos) const override
{
Q_UNUSED(pos);
@@ -119,10 +124,10 @@ MainWindow::MainWindow(const QString &settingsDir, qbs::Settings::Scope scope, Q
saveAction->setShortcut(QKeySequence::Save);
connect(saveAction, &QAction::triggered, this, &MainWindow::saveSettings);
const auto expandAllAction = new QAction(tr("&Expand All"), this);
- expandAllAction->setShortcut(Qt::CTRL | Qt::Key_E);
+ expandAllAction->setShortcut(int(Qt::CTRL) | int(Qt::Key_E));
connect(expandAllAction, &QAction::triggered, this, &MainWindow::expandAll);
const auto collapseAllAction = new QAction(tr("C&ollapse All"), this);
- collapseAllAction->setShortcut(Qt::CTRL | Qt::Key_O);
+ collapseAllAction->setShortcut(int(Qt::CTRL) | int(Qt::Key_O));
connect(collapseAllAction, &QAction::triggered, this, &MainWindow::collapseAll);
const auto exitAction = new QAction(tr("E&xit"), this);
exitAction->setShortcut(QKeySequence::Quit);
diff --git a/src/app/config/CMakeLists.txt b/src/app/config/CMakeLists.txt
new file mode 100644
index 000000000..064d8fb22
--- /dev/null
+++ b/src/app/config/CMakeLists.txt
@@ -0,0 +1,13 @@
+set(SOURCES
+ configcommand.h
+ configcommandexecutor.cpp
+ configcommandexecutor.h
+ configcommandlineparser.cpp
+ configcommandlineparser.h
+ configmain.cpp
+ )
+
+add_qbs_app(qbs-config
+ DEPENDS qbscore qbsconsolelogger
+ SOURCES ${SOURCES}
+ )
diff --git a/src/app/config/config.pro b/src/app/config/config.pro
deleted file mode 100644
index 278c481d1..000000000
--- a/src/app/config/config.pro
+++ /dev/null
@@ -1,13 +0,0 @@
-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
index 9daa701f6..2744b93f7 100644
--- a/src/app/config/config.qbs
+++ b/src/app/config/config.qbs
@@ -1,5 +1,3 @@
-import qbs 1.0
-
QbsApp {
name: "qbs-config"
files: [
diff --git a/src/app/config/configcommand.h b/src/app/config/configcommand.h
index 1574e3745..f8e2a8ae9 100644
--- a/src/app/config/configcommand.h
+++ b/src/app/config/configcommand.h
@@ -47,7 +47,7 @@
class ConfigCommand
{
public:
- enum Command { CfgSet, CfgUnset, CfgList, CfgExport, CfgImport, CfgNone };
+ enum Command { CfgSet, CfgUnset, CfgList, CfgExport, CfgImport, CfgAddProfile, CfgNone };
ConfigCommand() : command(CfgNone) {}
diff --git a/src/app/config/configcommandexecutor.cpp b/src/app/config/configcommandexecutor.cpp
index 1290ba2f0..cbcb5ee73 100644
--- a/src/app/config/configcommandexecutor.cpp
+++ b/src/app/config/configcommandexecutor.cpp
@@ -41,17 +41,60 @@
#include "configcommand.h"
#include "../shared/logging/consolelogger.h"
-#include <tools/settingsrepresentation.h>
#include <tools/error.h>
+#include <tools/profile.h>
+#include <tools/qttools.h>
+#include <tools/settingsrepresentation.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
#include <QtCore/qtextstream.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
#include <cstdio>
using namespace qbs;
+static QJsonObject settingsToJSONObject(
+ Settings &settings, qbs::Settings::Scopes scopes, const QString &parentGroup = {})
+{
+ QJsonObject result;
+
+ const auto allKeys = settings.directChildren(parentGroup, scopes);
+ for (const auto& key : allKeys) {
+ const auto fullKey = parentGroup.isEmpty()
+ ? key
+ : QStringLiteral("%1.%2").arg(parentGroup, key);
+ const auto value = settings.value(fullKey, scopes);
+ if (value.isValid()) { // looks like a real value
+ result[key] = QJsonValue::fromVariant(value);
+ } else { // looks like a group
+ result[key] = settingsToJSONObject(settings, scopes, fullKey);
+ }
+ }
+
+ return result;
+}
+
+static void settingsFromJSONObject(
+ Settings &settings, const QJsonObject &object, const QString &parentGroup = {})
+{
+ for (auto it = object.begin(), end = object.end(); it != end; ++it) {
+ const auto key = it.key();
+ const auto value = it.value();
+ const auto fullKey = parentGroup.isEmpty()
+ ? key
+ : QStringLiteral("%1.%2").arg(parentGroup, key);
+ if (value.isObject()) {
+ settingsFromJSONObject(settings, it.value().toObject(), fullKey);
+ } else {
+ settings.setValue(fullKey, value.toVariant());
+ }
+ }
+}
+
+
ConfigCommandExecutor::ConfigCommandExecutor(Settings *settings, Settings::Scopes scope)
: m_settings(settings), m_scope(scope)
{
@@ -72,15 +115,26 @@ void ConfigCommandExecutor::execute(const ConfigCommand &command)
for (const QString &varName : command.varNames)
m_settings->remove(varName);
break;
+ case ConfigCommand::CfgAddProfile: {
+ Profile profile(command.varValue, m_settings);
+ profile.removeProfile();
+ Q_ASSERT(command.varNames.size() % 2 == 0);
+ for (int i = 0; i < command.varNames.size(); i += 2) {
+ const QString &key = command.varNames.at(i);
+ const QString &rawValue = command.varNames.at(i + 1);
+ profile.setValue(key, representationToSettingsValue(rawValue));
+ }
+ 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:"
+ std::printf("old "); // Will end up as "old settings:"
printSettings(command);
importSettings(command.fileName);
- printf("\nnew ");
+ std::printf("\nnew ");
printSettings(command);
break;
case ConfigCommand::CfgNone:
@@ -115,7 +169,7 @@ void ConfigCommandExecutor::printSettings(const ConfigCommand &command)
void ConfigCommandExecutor::printOneSetting(const QString &key)
{
- printf("%s: %s\n", qPrintable(key),
+ std::printf("%s: %s\n", qPrintable(key),
qPrintable(qbs::settingsValueToRepresentation(m_settings->value(key, m_scope))));
}
@@ -126,12 +180,20 @@ void ConfigCommandExecutor::exportSettings(const QString &filename)
throw ErrorInfo(tr("Could not open file '%1' for writing: %2")
.arg(QDir::toNativeSeparators(filename), file.errorString()));
}
- QTextStream stream(&file);
- stream.setCodec("UTF-8");
- const auto keys = m_settings->allKeys(m_scope);
- for (const QString &key : keys)
- stream << key << ": " << qbs::settingsValueToRepresentation(m_settings->value(key, m_scope))
- << endl;
+
+ if (QFileInfo(filename).suffix() == u"json") {
+ QJsonDocument doc;
+ doc.setObject(settingsToJSONObject(*m_settings, m_scope));
+ file.write(doc.toJson());
+ } else {
+ QTextStream stream(&file);
+ setupDefaultCodec(stream);
+ const auto keys = m_settings->allKeys(m_scope);
+ for (const QString &key : keys)
+ stream << key << ": "
+ << qbs::settingsValueToRepresentation(m_settings->value(key, m_scope))
+ << Qt::endl;
+ }
}
void ConfigCommandExecutor::importSettings(const QString &filename)
@@ -146,15 +208,21 @@ void ConfigCommandExecutor::importSettings(const QString &filename)
for (const QString &key : keys)
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));
+ if (QFileInfo(filename).suffix() == u"json") {
+ const auto doc = QJsonDocument::fromJson(file.readAll());
+ const auto object = doc.object();
+ settingsFromJSONObject(*m_settings, doc.object());
+ } else {
+ QTextStream stream(&file);
+ setupDefaultCodec(stream);
+ 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/configcommandlineparser.cpp b/src/app/config/configcommandlineparser.cpp
index 2f2d1610e..1ad0cc4b1 100644
--- a/src/app/config/configcommandlineparser.cpp
+++ b/src/app/config/configcommandlineparser.cpp
@@ -71,6 +71,8 @@ void ConfigCommandLineParser::parse(const QStringList &commandLine)
setCommand(ConfigCommand::CfgExport);
else if (arg == QLatin1String("import"))
setCommand(ConfigCommand::CfgImport);
+ else if (arg == QLatin1String("add-profile"))
+ setCommand(ConfigCommand::CfgAddProfile);
else if (arg == QLatin1String("settings-dir"))
assignOptionArgument(arg, m_settingsDir);
else if (arg == QLatin1String("user"))
@@ -113,6 +115,22 @@ void ConfigCommandLineParser::parse(const QStringList &commandLine)
case ConfigCommand::CfgList:
m_command.varNames = m_commandLine;
break;
+ case ConfigCommand::CfgAddProfile:
+ if (m_commandLine.empty())
+ throw Error(Tr::tr("Profile name missing."));
+ m_command.varValue = m_commandLine.takeFirst();
+ if (m_command.varValue.isEmpty())
+ throw Error(Tr::tr("Profile name must not be empty."));
+ m_command.varNames = m_commandLine;
+ if (m_command.varNames.isEmpty())
+ throw Error(Tr::tr("Profile properties must be provided."));
+ if (m_command.varNames.size() % 2 != 0)
+ throw Error(Tr::tr("Profile properties must be key/value pairs."));
+ for (const auto &varName : std::as_const(m_command.varNames)) {
+ if (varName.isEmpty())
+ throw Error(Tr::tr("Property names must not be empty."));
+ }
+ break;
default:
break;
}
@@ -134,18 +152,19 @@ void ConfigCommandLineParser::setScope(Settings::Scope scope)
void ConfigCommandLineParser::printUsage() const
{
- puts("Usage:\n"
+ std::puts("Usage:\n"
" qbs config [--settings-dir <settings directory] <options>\n"
" qbs config [--settings-dir <settings directory] <key>\n"
" qbs config [--settings-dir <settings directory] <key> <value>"
"\n"
"Options:\n"
- " --list [<root> ...] list keys under key <root> or all keys\n"
- " --user consider only user-level settings\n"
- " --system consider only system-level settings\n"
- " --unset <name> remove key with given name\n"
- " --import <file> import settings from given file\n"
- " --export <file> export settings to given file\n");
+ " --list [<root> ...] list keys under key <root> or all keys\n"
+ " --user consider only user-level settings\n"
+ " --system consider only system-level settings\n"
+ " --unset <name> remove key with given name\n"
+ " --add-profile <name> <key> <value> ... add profile with the given name and properties\n"
+ " --import <file> import settings from given file\n"
+ " --export <file> export settings to given file\n");
}
void ConfigCommandLineParser::assignOptionArgument(const QString &option, QString &argument)
diff --git a/src/app/config/configcommandlineparser.h b/src/app/config/configcommandlineparser.h
index b567134fd..f1f026678 100644
--- a/src/app/config/configcommandlineparser.h
+++ b/src/app/config/configcommandlineparser.h
@@ -60,7 +60,7 @@ public:
class Error
{
public:
- Error(const QString &message) : m_message(message) { }
+ Error(QString message) : m_message(std::move(message)) { }
QString message() const { return m_message; }
private:
QString m_message;
diff --git a/src/app/qbs-create-project/CMakeLists.txt b/src/app/qbs-create-project/CMakeLists.txt
new file mode 100644
index 000000000..a627d11ff
--- /dev/null
+++ b/src/app/qbs-create-project/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(SOURCES
+ createproject.cpp
+ createproject.h
+ create-project-main.cpp
+ )
+
+add_qbs_app(qbs-create-project
+ DEPENDS qbscore qbsconsolelogger
+ SOURCES ${SOURCES}
+ )
diff --git a/src/app/qbs-create-project/create-project-main.cpp b/src/app/qbs-create-project/create-project-main.cpp
index f26df3dbf..51e7ce514 100644
--- a/src/app/qbs-create-project/create-project-main.cpp
+++ b/src/app/qbs-create-project/create-project-main.cpp
@@ -41,6 +41,7 @@
#include <logging/translator.h>
#include <tools/error.h>
+#include <tools/qttools.h>
#include <QtCore/qcommandlineoption.h>
#include <QtCore/qcommandlineparser.h>
@@ -79,9 +80,9 @@ int main(int argc, char *argv[])
const ProjectStructure projectStructure = parser.isSet(flatOpt)
? ProjectStructure::Flat : ProjectStructure::Composite;
const QStringList whiteList = parser.value(whiteListOpt).split(QLatin1Char(','),
- QString::SkipEmptyParts);
+ Qt::SkipEmptyParts);
const QStringList blackList = parser.value(blackListOpt).split(QLatin1Char(','),
- QString::SkipEmptyParts);
+ Qt::SkipEmptyParts);
try {
ProjectCreator().run(QDir::currentPath(), projectStructure, whiteList, blackList);
} catch (const ErrorInfo &e) {
diff --git a/src/app/qbs-create-project/createproject.cpp b/src/app/qbs-create-project/createproject.cpp
index 26147b484..22c845994 100644
--- a/src/app/qbs-create-project/createproject.cpp
+++ b/src/app/qbs-create-project/createproject.cpp
@@ -43,6 +43,7 @@
#include <tools/error.h>
#include <tools/jsliterals.h>
#include <tools/qttools.h>
+#include <tools/stlutils.h>
#include <QtCore/qdebug.h>
#include <QtCore/qdiriterator.h>
@@ -61,10 +62,10 @@ void ProjectCreator::run(const QString &topLevelDir, ProjectStructure projectStr
const QStringList &whiteList, const QStringList &blackList)
{
m_projectStructure = projectStructure;
- for (const QString &s : whiteList)
- m_whiteList.push_back(QRegExp(s, Qt::CaseSensitive, QRegExp::Wildcard));
- for (const QString &s : blackList)
- m_blackList.push_back(QRegExp(s, Qt::CaseSensitive, QRegExp::Wildcard));
+ qbs::Internal::transform(whiteList, m_whiteList, [](const QString &s) {
+ return QRegularExpression(QRegularExpression::wildcardToRegularExpression(s)); });
+ qbs::Internal::transform(blackList, m_blackList, [](const QString &s) {
+ return QRegularExpression(QRegularExpression::wildcardToRegularExpression(s)); });
m_topLevelProject.dirPath = topLevelDir;
setupProject(&m_topLevelProject);
serializeProject(m_topLevelProject);
@@ -103,8 +104,7 @@ void ProjectCreator::serializeProject(const ProjectCreator::Project &project)
.arg(projectFile.fileName(), projectFile.errorString()));
}
QTextStream fileContents(&projectFile);
- fileContents.setCodec("UTF-8");
- fileContents << "import qbs\n\n";
+ qbs::setupDefaultCodec(fileContents);
if (!project.fileNames.empty() || m_projectStructure == ProjectStructure::Flat) {
fileContents << "Product {\n";
const ProductFlags productFlags = getFlags(project);
@@ -124,7 +124,7 @@ void ProjectCreator::serializeProject(const ProjectCreator::Project &project)
fileContents << indent << "Depends { name: \"cpp\" }\n";
}
fileContents << indent << "files: [\n";
- for (const QString &fileName : qAsConst(project.fileNames))
+ for (const QString &fileName : std::as_const(project.fileNames))
fileContents << indent << indent << qbs::toJSLiteral(fileName) << ",\n";
fileContents << indent << "]\n";
for (const ProjectPtr &p : project.subProjects)
@@ -152,7 +152,7 @@ void ProjectCreator::addGroups(QTextStream &stream, const QDir &baseDir,
<< qbs::toJSLiteral(baseDir.relativeFilePath(subProject.dirPath) + QLatin1Char('/'))
<< '\n';
stream << indent << indent << "files: [\n";
- for (const QString &fileName : qAsConst(subProject.fileNames))
+ for (const QString &fileName : std::as_const(subProject.fileNames))
stream << indent << indent << indent << qbs::toJSLiteral(fileName) << ",\n";
stream << indent << indent << "]\n";
stream << indent << "}\n";
@@ -162,10 +162,11 @@ void ProjectCreator::addGroups(QTextStream &stream, const QDir &baseDir,
bool ProjectCreator::isSourceFile(const QString &fileName)
{
- const auto isMatch = [fileName](const QRegExp &rex) { return rex.exactMatch(fileName); };
- return !std::any_of(m_blackList.cbegin(), m_blackList.cend(), isMatch)
- && (m_whiteList.empty()
- || std::any_of(m_whiteList.cbegin(), m_whiteList.cend(), isMatch));
+ const auto isMatch = [fileName](const QRegularExpression &rex) {
+ return rex.match(fileName).hasMatch();
+ };
+ return !qbs::Internal::any_of(m_blackList, isMatch)
+ && (m_whiteList.empty() || qbs::Internal::any_of(m_whiteList, isMatch));
}
ProjectCreator::ProductFlags ProjectCreator::getFlags(const ProjectCreator::Project &project)
@@ -183,7 +184,7 @@ ProjectCreator::ProductFlags ProjectCreator::getFlags(const ProjectCreator::Proj
void ProjectCreator::getFlagsFromFileNames(const ProjectCreator::Project &project,
ProductFlags &flags)
{
- for (const QString &fileName : qAsConst(project.fileNames)) {
+ for (const QString &fileName : std::as_const(project.fileNames)) {
if (flags.testFlag(IsApp) && flags.testFlag(NeedsQt))
return;
const QFileInfo fi(project.dirPath + QLatin1Char('/') + fileName);
@@ -209,7 +210,7 @@ void ProjectCreator::getFlagsFromFileNames(const ProjectCreator::Project &projec
void ProjectCreator::getFlagsFromFileContents(const ProjectCreator::Project &project,
ProductFlags &flags)
{
- for (const QString &fileName : qAsConst(project.fileNames)) {
+ for (const QString &fileName : std::as_const(project.fileNames)) {
QFile f (project.dirPath + QLatin1Char('/') + fileName);
if (!f.open(QIODevice::ReadOnly)) {
qDebug() << "Ignoring failure to read" << f.fileName();
diff --git a/src/app/qbs-create-project/createproject.h b/src/app/qbs-create-project/createproject.h
index efc217b68..ac7d53e1a 100644
--- a/src/app/qbs-create-project/createproject.h
+++ b/src/app/qbs-create-project/createproject.h
@@ -42,7 +42,7 @@
#include <QtCore/qflags.h>
#include <QtCore/qhash.h>
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qstringlist.h>
#include <memory>
@@ -83,8 +83,8 @@ private:
};
Project m_topLevelProject;
ProjectStructure m_projectStructure = ProjectStructure::Flat;
- QList<QRegExp> m_whiteList;
- QList<QRegExp> m_blackList;
+ QList<QRegularExpression> m_whiteList;
+ QList<QRegularExpression> m_blackList;
};
#endif // QBS_CREATEPROJECT_H
diff --git a/src/app/qbs-create-project/qbs-create-project.pro b/src/app/qbs-create-project/qbs-create-project.pro
deleted file mode 100644
index 1edb85214..000000000
--- a/src/app/qbs-create-project/qbs-create-project.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-include(../app.pri)
-
-TARGET = qbs-create-project
-
-HEADERS += \
- createproject.h
-SOURCES += \
- createproject.cpp \
- create-project-main.cpp
diff --git a/src/app/qbs-create-project/qbs-create-project.qbs b/src/app/qbs-create-project/qbs-create-project.qbs
index b65f9177f..ae79001ee 100644
--- a/src/app/qbs-create-project/qbs-create-project.qbs
+++ b/src/app/qbs-create-project/qbs-create-project.qbs
@@ -1,5 +1,3 @@
-import qbs
-
QbsApp {
name: "qbs-create-project"
files: [
diff --git a/src/app/qbs-setup-android/CMakeLists.txt b/src/app/qbs-setup-android/CMakeLists.txt
new file mode 100644
index 000000000..46930318b
--- /dev/null
+++ b/src/app/qbs-setup-android/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(SOURCES
+ android-setup.cpp
+ android-setup.h
+ commandlineparser.cpp
+ commandlineparser.h
+ main.cpp
+ )
+
+add_qbs_app(qbs-setup-android
+ DEPENDS qbscore qbsconsolelogger
+ SOURCES ${SOURCES}
+ )
diff --git a/src/app/qbs-setup-android/android-setup.cpp b/src/app/qbs-setup-android/android-setup.cpp
index 029628419..fad24d018 100644
--- a/src/app/qbs-setup-android/android-setup.cpp
+++ b/src/app/qbs-setup-android/android-setup.cpp
@@ -45,6 +45,7 @@
#include <tools/profile.h>
#include <tools/settings.h>
#include <tools/version.h>
+#include <tools/qttools.h>
#include <QtCore/qbytearraylist.h>
#include <QtCore/qcoreapplication.h>
@@ -94,10 +95,10 @@ static QString mapArch(const QString &androidName)
}
struct QtAndroidInfo {
- bool isValid() const { return !arch.isEmpty(); }
+ bool isValid() const { return !archs.isEmpty(); }
QString qmakePath;
- QString arch;
+ QStringList archs;
QString platform;
};
@@ -111,19 +112,31 @@ static QtAndroidInfo getInfoForQtDir(const QString &qtDir)
if (!qdevicepri.open(QIODevice::ReadOnly))
return info;
while (!qdevicepri.atEnd()) {
+ // For Qt < 5.14 use DEFAULT_ANDROID_TARGET_ARCH (which is the abi) to compute
+ // the architecture
+ // DEFAULT_ANDROID_ABIS doesn't exit
+ // For Qt >= 5.14:
+ // DEFAULT_ANDROID_TARGET_ARCH doesn't exist, use DEFAULT_ANDROID_ABIS to compute
+ // the architectures
const QByteArray line = qdevicepri.readLine().simplified();
const bool isArchLine = line.startsWith("DEFAULT_ANDROID_TARGET_ARCH");
+ const bool isAbisLine = line.startsWith("DEFAULT_ANDROID_ABIS");
const bool isPlatformLine = line.startsWith("DEFAULT_ANDROID_PLATFORM");
- if (!isArchLine && !isPlatformLine)
+ if (!isArchLine && !isPlatformLine && !isAbisLine)
continue;
const QList<QByteArray> elems = line.split('=');
if (elems.size() != 2)
continue;
const QString rhs = QString::fromLatin1(elems.at(1).trimmed());
- if (isArchLine)
- info.arch = mapArch(rhs);
- else
+ if (isArchLine) {
+ info.archs << mapArch(rhs);
+ } else if (isAbisLine) {
+ const auto abis = rhs.split(QLatin1Char(' '));
+ for (const QString &abi: abis)
+ info.archs << mapArch(abi);
+ } else {
info.platform = rhs;
+ }
}
return info;
}
@@ -136,13 +149,16 @@ static QtInfoPerArch getQtAndroidInfo(const QString &qtSdkDir)
return archs;
QStringList qtDirs(qtSdkDir);
- QDirIterator dit(qtSdkDir, QStringList() << QStringLiteral("android_*"), QDir::Dirs);
+ const QStringList nameFilters{QStringLiteral("android_*"), QStringLiteral("android")};
+ QDirIterator dit(qtSdkDir, nameFilters, QDir::Dirs);
while (dit.hasNext())
qtDirs << dit.next();
- for (const auto &qtDir : qtDirs) {
+ for (const auto &qtDir : std::as_const(qtDirs)) {
const QtAndroidInfo info = getInfoForQtDir(qtDir);
- if (info.isValid())
- archs.insert(info.arch, info);
+ if (info.isValid()) {
+ for (const QString &arch: info.archs)
+ archs.insert(arch, info);
+ }
}
return archs;
}
@@ -224,8 +240,10 @@ static void setupNdk(qbs::Settings *settings, const QString &profileName, const
qmakeFilePaths << qtAndroidInfo.qmakePath;
platform = maximumPlatform(platform, qtAndroidInfo.platform);
}
- if (!qmakeFilePaths.empty())
+ if (!qmakeFilePaths.empty()) {
+ qmakeFilePaths.removeDuplicates();
mainProfile.setValue(qls("moduleProviders.Qt.qmakeFilePaths"), qmakeFilePaths);
+ }
if (!platform.isEmpty())
mainProfile.setValue(qls("Android.ndk.platform"), platform);
}
diff --git a/src/app/qbs-setup-android/commandlineparser.cpp b/src/app/qbs-setup-android/commandlineparser.cpp
index 8beeeb601..c4ff09947 100644
--- a/src/app/qbs-setup-android/commandlineparser.cpp
+++ b/src/app/qbs-setup-android/commandlineparser.cpp
@@ -53,6 +53,7 @@ static QString settingsDirOption() { return QStringLiteral("--settings-dir"); }
static QString sdkDirOption() { return QStringLiteral("--sdk-dir"); }
static QString ndkDirOption() { return QStringLiteral("--ndk-dir"); }
static QString qtSdkDirOption() { return QStringLiteral("--qt-dir"); }
+static QString systemOption() { return QStringLiteral("--system"); }
void CommandLineParser::parse(const QStringList &commandLine)
{
@@ -77,6 +78,8 @@ void CommandLineParser::parse(const QStringList &commandLine)
m_helpRequested = true;
else if (arg == settingsDirOption())
assignOptionArgument(settingsDirOption(), m_settingsDir);
+ else if (arg == systemOption())
+ m_settingsScope = qbs::Settings::SystemScope;
else if (arg == sdkDirOption())
assignOptionArgument(sdkDirOption(), m_sdkDir);
else if (arg == ndkDirOption())
@@ -116,9 +119,9 @@ 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 <settings dir>] [%3 <NDK dir>] [%4 <SDK dir>] [%5 <Qt dir>] "
+ s += Tr::tr(" %1 [%2 <settings dir>] [%6] [%3 <NDK dir>] [%4 <SDK dir>] [%5 <Qt dir>] "
"<profile name>\n")
- .arg(m_command, settingsDirOption(), ndkDirOption(), sdkDirOption(), qtSdkDirOption());
+ .arg(m_command, settingsDirOption(), ndkDirOption(), sdkDirOption(), qtSdkDirOption(), systemOption());
s += Tr::tr(" %1 %2|%3\n").arg(m_command, helpOptionShort(), helpOptionLong());
s += Tr::tr("If an NDK path is given, the profile will be suitable for use with Android "
"projects that contain native C/C++ code.\n");
diff --git a/src/app/qbs-setup-android/commandlineparser.h b/src/app/qbs-setup-android/commandlineparser.h
index 3ce3eeb40..47f424634 100644
--- a/src/app/qbs-setup-android/commandlineparser.h
+++ b/src/app/qbs-setup-android/commandlineparser.h
@@ -39,6 +39,8 @@
#ifndef QBS_SETUP_ANDROID_COMMANDLINEPARSER_H
#define QBS_SETUP_ANDROID_COMMANDLINEPARSER_H
+#include <tools/settings.h>
+
#include <QtCore/qstringlist.h>
class CommandLineParser
@@ -55,6 +57,7 @@ public:
QString qtSdkDir() const { return m_qtSdkDir; }
QString profileName() const { return m_profileName; }
QString settingsDir() const { return m_settingsDir; }
+ qbs::Settings::Scope settingsScope() const { return m_settingsScope; }
QString usageString() const;
@@ -64,6 +67,7 @@ private:
[[noreturn]] void complainAboutExtraArguments();
bool m_helpRequested = false;
+ qbs::Settings::Scope m_settingsScope = qbs::Settings::UserScope;
QString m_sdkDir;
QString m_ndkDir;
QString m_qtSdkDir;
diff --git a/src/app/qbs-setup-android/main.cpp b/src/app/qbs-setup-android/main.cpp
index 8822751f4..18e5dbe41 100644
--- a/src/app/qbs-setup-android/main.cpp
+++ b/src/app/qbs-setup-android/main.cpp
@@ -60,6 +60,7 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS;
}
qbs::Settings settings(clParser.settingsDir());
+ settings.setScopeForWriting(clParser.settingsScope());
setupAndroid(&settings, clParser.profileName(), clParser.sdkDir(), clParser.ndkDir(),
clParser.qtSdkDir());
} catch (const qbs::ErrorInfo &e) {
diff --git a/src/app/qbs-setup-android/qbs-setup-android.pro b/src/app/qbs-setup-android/qbs-setup-android.pro
deleted file mode 100644
index b5e57761d..000000000
--- a/src/app/qbs-setup-android/qbs-setup-android.pro
+++ /dev/null
@@ -1,12 +0,0 @@
-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
index edadd5dd1..b18e152b0 100644
--- a/src/app/qbs-setup-android/qbs-setup-android.qbs
+++ b/src/app/qbs-setup-android/qbs-setup-android.qbs
@@ -1,5 +1,3 @@
-import qbs
-
QbsApp {
name: "qbs-setup-android"
files: [
@@ -12,6 +10,11 @@ QbsApp {
Group {
name: "MinGW specific files"
condition: qbs.toolchain.contains("mingw")
- files: ["qbs-setup-android.exe.manifest", "qbs-setup-android.rc"]
+ files: "qbs-setup-android.rc"
+ Group {
+ name: "qbs-setup-android manifest"
+ files: "qbs-setup-android.exe.manifest"
+ fileTags: [] // the manifest is referenced by the rc file
+ }
}
}
diff --git a/src/app/qbs-setup-qt/CMakeLists.txt b/src/app/qbs-setup-qt/CMakeLists.txt
new file mode 100644
index 000000000..a91ab1b26
--- /dev/null
+++ b/src/app/qbs-setup-qt/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(SOURCES
+ commandlineparser.cpp
+ commandlineparser.h
+ main.cpp
+ setupqt.cpp
+ setupqt.h
+ )
+
+add_qbs_app(qbs-setup-qt
+ DEPENDS qbscore qbsconsolelogger
+ SOURCES ${SOURCES}
+ )
diff --git a/src/app/qbs-setup-qt/main.cpp b/src/app/qbs-setup-qt/main.cpp
index 94185c4d7..b739bff2d 100644
--- a/src/app/qbs-setup-qt/main.cpp
+++ b/src/app/qbs-setup-qt/main.cpp
@@ -41,6 +41,7 @@
#include "commandlineparser.h"
#include <logging/translator.h>
+#include <tools/qttools.h>
#include <tools/settings.h>
#include <QtCore/qcoreapplication.h>
@@ -81,7 +82,7 @@ int main(int argc, char *argv[])
QString profileName = QLatin1String("qt-") + qtEnvironment.qtVersion.toString();
if (SetupQt::checkIfMoreThanOneQtWithTheSameVersion(qtEnvironment.qtVersion, qtEnvironments)) {
QStringList prefixPathParts = QFileInfo(qtEnvironment.qmakeFilePath).path()
- .split(QLatin1Char('/'), QString::SkipEmptyParts);
+ .split(QLatin1Char('/'), Qt::SkipEmptyParts);
if (!prefixPathParts.empty())
profileName += QLatin1String("-") + prefixPathParts.last();
}
diff --git a/src/app/qbs-setup-qt/qbs-setup-qt.pro b/src/app/qbs-setup-qt/qbs-setup-qt.pro
deleted file mode 100644
index a5694d6b7..000000000
--- a/src/app/qbs-setup-qt/qbs-setup-qt.pro
+++ /dev/null
@@ -1,16 +0,0 @@
-include(../app.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
index f4cd9d6b8..3be090155 100644
--- a/src/app/qbs-setup-qt/qbs-setup-qt.qbs
+++ b/src/app/qbs-setup-qt/qbs-setup-qt.qbs
@@ -1,5 +1,3 @@
-import qbs 1.0
-
QbsApp {
name: "qbs-setup-qt"
files: [
@@ -12,7 +10,12 @@ QbsApp {
Group {
name: "MinGW specific files"
condition: qbs.toolchain.contains("mingw")
- files: ["qbs-setup-qt.exe.manifest", "qbs-setup-qt.rc"]
+ files: "qbs-setup-qt.rc"
+ Group {
+ name: "qbs-setup-qt manifest"
+ files: "qbs-setup-qt.exe.manifest"
+ fileTags: [] // the manifest is referenced by the rc file
+ }
}
}
diff --git a/src/app/qbs-setup-qt/setupqt.cpp b/src/app/qbs-setup-qt/setupqt.cpp
index 947cbc5fc..bac766482 100644
--- a/src/app/qbs-setup-qt/setupqt.cpp
+++ b/src/app/qbs-setup-qt/setupqt.cpp
@@ -47,6 +47,7 @@
#include <tools/set.h>
#include <tools/settings.h>
#include <tools/stlutils.h>
+#include <tools/toolchains.h>
#include <tools/version.h>
#include <QtCore/qbytearraymatcher.h>
@@ -64,6 +65,7 @@ using Internal::none_of;
using Internal::contains;
using Internal::HostOsInfo;
using Internal::Tr;
+using Internal::rangeTo;
static QStringList qmakeExecutableNames()
{
@@ -154,7 +156,7 @@ static QString archFromDirName(const QString &dir)
const std::string dirString = dir.toStdString();
if (!std::regex_match(dirString, match, regexp))
return {};
- const QString arch = QString::fromStdString(match[1]);
+ QString arch = QString::fromStdString(match[1]);
if (arch == QLatin1String("32"))
return QStringLiteral("x86");
if (arch == QLatin1String("64"))
@@ -170,7 +172,7 @@ static QString platformFromDirName(const QString &dir)
return QStringLiteral("android");
if (dir == QLatin1String("Boot2Qt"))
return QStringLiteral("linux");
- return QString::fromStdString(HostOsInfo::hostOSIdentifier());
+ return HostOsInfo::hostOSIdentifier();
}
QtEnvironment SetupQt::fetchEnvironment(const QString &qmakePath)
@@ -194,9 +196,9 @@ QtEnvironment SetupQt::fetchEnvironment(const QString &qmakePath)
static bool isToolchainProfile(const Profile &profile)
{
- const auto actual = Internal::Set<QString>::fromList(
+ const auto actual = rangeTo<Internal::Set<QString>>(
profile.allKeys(Profile::KeySelectionRecursive));
- Internal::Set<QString> expected = Internal::Set<QString> { QStringLiteral("qbs.toolchain") };
+ Internal::Set<QString> expected{ QStringLiteral("qbs.toolchainType") };
if (HostOsInfo::isMacosHost())
expected.insert(QStringLiteral("qbs.targetPlatform")); // match only Xcode profiles
return Internal::Set<QString>(actual).unite(expected) == actual;
@@ -212,7 +214,7 @@ static bool isQtProfile(const Profile &profile)
// For Profiles created with setup-qt < 5.13.
const QStringList searchPaths
= profile.value(QStringLiteral("preferences.qbsSearchPaths")).toStringList();
- return std::any_of(searchPaths.cbegin(), searchPaths.cend(), [] (const QString &path) {
+ return Internal::any_of(searchPaths, [] (const QString &path) {
return QFileInfo(path + QStringLiteral("/modules/Qt")).isDir();
});
}
@@ -230,9 +232,14 @@ static Match compatibility(const QtEnvironment &env, const Profile &toolchainPro
{
Match match = MatchFull;
- const auto toolchainNames = Internal::Set<QString>::fromList(
- toolchainProfile.value(QStringLiteral("qbs.toolchain")).toStringList());
- const auto qtToolchainNames = Internal::Set<QString>::fromList(env.qbsToolchain);
+ const auto toolchainType =
+ toolchainProfile.value(QStringLiteral("qbs.toolchainType")).toString();
+ const auto toolchain = !toolchainType.isEmpty()
+ ? canonicalToolchain(toolchainType)
+ : toolchainProfile.value(QStringLiteral("qbs.toolchain")).toStringList();
+
+ const auto toolchainNames = rangeTo<Internal::Set<QString>>(toolchain);
+ const auto qtToolchainNames = rangeTo<Internal::Set<QString>>(env.qbsToolchain);
if (areProfilePropertiesIncompatible(toolchainNames, qtToolchainNames)) {
auto intersection = toolchainNames;
intersection.intersect(qtToolchainNames);
@@ -292,15 +299,12 @@ QString profileNameWithoutHostArch(const QString &profileName)
// then we drop the crosscompiling toolchain MSVC2017-x86_x64.
static void compressMsvcProfiles(QStringList &profiles)
{
- auto it = std::remove_if(profiles.begin(), profiles.end(),
- [&profiles] (const QString &profileName) {
+ Internal::removeIf(profiles, [&profiles] (const QString &profileName) {
int idx = profileName.indexOf(QLatin1Char('_'));
if (idx == -1)
return false;
return contains(profiles, profileNameWithoutHostArch(profileName));
});
- if (it != profiles.end())
- profiles.erase(it, profiles.end());
}
void SetupQt::saveToQbsSettings(const QString &qtVersionName,
@@ -310,7 +314,7 @@ void SetupQt::saveToQbsSettings(const QString &qtVersionName,
const QString cleanQtVersionName = Profile::cleanName(qtVersionName);
QString msg = QCoreApplication::translate("SetupQt", "Creating profile '%1'.")
.arg(cleanQtVersionName);
- printf("%s\n", qPrintable(msg));
+ std::printf("%s\n", qPrintable(msg));
Profile profile(cleanQtVersionName, settings);
profile.removeProfile();
diff --git a/src/app/qbs-setup-toolchains/CMakeLists.txt b/src/app/qbs-setup-toolchains/CMakeLists.txt
new file mode 100644
index 000000000..64347cd6c
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(SOURCES
+ clangclprobe.cpp
+ clangclprobe.h
+ commandlineparser.cpp
+ commandlineparser.h
+ cosmicprobe.cpp
+ cosmicprobe.h
+ dmcprobe.cpp
+ dmcprobe.h
+ gccprobe.cpp
+ gccprobe.h
+ iarewprobe.cpp
+ iarewprobe.h
+ keilprobe.cpp
+ keilprobe.h
+ main.cpp
+ msvcprobe.cpp
+ msvcprobe.h
+ probe.cpp
+ probe.h
+ sdccprobe.cpp
+ sdccprobe.h
+ watcomprobe.cpp
+ watcomprobe.h
+ xcodeprobe.cpp
+ xcodeprobe.h
+ )
+
+add_qbs_app(qbs-setup-toolchains
+ DEPENDS qbscore qbsconsolelogger
+ SOURCES ${SOURCES}
+ )
diff --git a/src/app/qbs-setup-toolchains/clangclprobe.cpp b/src/app/qbs-setup-toolchains/clangclprobe.cpp
index 4cb38b774..272f953fc 100644
--- a/src/app/qbs-setup-toolchains/clangclprobe.cpp
+++ b/src/app/qbs-setup-toolchains/clangclprobe.cpp
@@ -44,27 +44,26 @@
#include "../shared/logging/consolelogger.h"
#include <logging/translator.h>
+#include <tools/clangclinfo.h>
#include <tools/hostosinfo.h>
+#include <tools/msvcinfo.h>
#include <tools/profile.h>
#include <tools/qttools.h>
#include <tools/settings.h>
+#include <tools/stlutils.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
using qbs::Settings;
using qbs::Profile;
+using qbs::Internal::ClangClInfo;
using qbs::Internal::HostOsInfo;
using qbs::Internal::Tr;
namespace {
-QString getToolchainInstallPath(const QFileInfo &compiler)
-{
- return compiler.path(); // 1 level up
-}
-
Profile createProfileHelper(
Settings *settings,
const QString &profileName,
@@ -75,9 +74,7 @@ Profile createProfileHelper(
Profile profile(profileName, settings);
profile.removeProfile();
profile.setValue(QStringLiteral("qbs.architecture"), architecture);
- profile.setValue(
- QStringLiteral("qbs.toolchain"),
- QStringList{QStringLiteral("clang-cl"), QStringLiteral("msvc")});
+ profile.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("clang-cl"));
profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), toolchainInstallPath);
profile.setValue(QStringLiteral("cpp.vcvarsallPath"), vcvarsallPath);
qbsInfo() << Tr::tr("Profile '%1' created for '%2'.")
@@ -85,75 +82,13 @@ Profile createProfileHelper(
return profile;
}
-std::vector<MSVCInstallInfo> compatibleMsvcs()
-{
- auto msvcs = installedMSVCs();
- auto filter = [](const MSVCInstallInfo &info)
- {
- const auto versions = info.version.split(QLatin1Char('.'));
- if (versions.empty())
- return true;
- bool ok = false;
- const int major = versions.at(0).toInt(&ok);
- return !(ok && major >= 15); // support MSVC2017 and above
- };
- const auto it = std::remove_if(msvcs.begin(), msvcs.end(), filter);
- msvcs.erase(it, msvcs.end());
- for (const auto &msvc: msvcs) {
- auto vcvarsallPath = msvc.findVcvarsallBat();
- if (vcvarsallPath.isEmpty())
- continue;
- }
- return msvcs;
-}
-
-QString findCompatibleVcsarsallBat()
-{
- for (const auto &msvc: compatibleMsvcs()) {
- const auto vcvarsallPath = msvc.findVcvarsallBat();
- if (!vcvarsallPath.isEmpty())
- return vcvarsallPath;
- }
- return {};
-}
-
-QString wow6432Key()
-{
-#ifdef Q_OS_WIN64
- return QStringLiteral("\\Wow6432Node");
-#else
- return {};
-#endif
-}
-
QString findClangCl()
{
const auto compilerName = HostOsInfo::appendExecutableSuffix(QStringLiteral("clang-cl"));
- const auto compilerFromPath = findExecutable(compilerName);
+ auto compilerFromPath = findExecutable(compilerName);
if (!compilerFromPath.isEmpty())
return compilerFromPath;
- const QSettings registry(
- QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\LLVM\\LLVM").arg(wow6432Key()),
- QSettings::NativeFormat);
- const auto key = QStringLiteral(".");
- if (registry.contains(key)) {
- const auto compilerPath = QDir::fromNativeSeparators(registry.value(key).toString())
- + QStringLiteral("/bin/") + compilerName;
- if (QFileInfo(compilerPath).exists())
- return compilerPath;
- }
-
- // this branch can be useful in case user had two LLVM installations (e.g. 32bit & 64bit)
- // but uninstalled one - in that case, registry will be empty
- static const char * const envVarCandidates[] = {"ProgramFiles", "ProgramFiles(x86)"};
- for (const auto &envVar : envVarCandidates) {
- const auto value
- = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv(envVar)));
- const auto compilerPath = value + QStringLiteral("/LLVM/bin/") + compilerName;
- if (QFileInfo(compilerPath).exists())
- return compilerPath;
- }
return {};
}
@@ -162,18 +97,13 @@ QString findClangCl()
void createClangClProfile(const QFileInfo &compiler, Settings *settings,
const QString &profileName)
{
- const auto compilerName = QStringLiteral("clang-cl");
- const auto vcvarsallPath = findCompatibleVcsarsallBat();
- if (vcvarsallPath.isEmpty()) {
- qbsWarning()
- << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.")
- .arg(compilerName);
+ const auto clangCl = ClangClInfo::fromCompilerFilePath(
+ compiler.filePath(), ConsoleLogger::instance());
+ if (clangCl.isEmpty())
return;
- }
-
- const auto toolchainInstallPath = getToolchainInstallPath(compiler);
- const auto hostArch = QString::fromStdString(HostOsInfo::hostOSArchitecture());
- createProfileHelper(settings, profileName, toolchainInstallPath, vcvarsallPath, hostArch);
+ const auto hostArch = HostOsInfo::hostOSArchitecture();
+ createProfileHelper(
+ settings, profileName, clangCl.toolchainInstallPath, clangCl.vcvarsallPath, hostArch);
}
/*!
@@ -184,29 +114,30 @@ void clangClProbe(Settings *settings, std::vector<Profile> &profiles)
{
const auto compilerName = QStringLiteral("clang-cl");
qbsInfo() << Tr::tr("Trying to detect %1...").arg(compilerName);
- const QString compilerFilePath = findClangCl();
- if (compilerFilePath.isEmpty()) {
+ const auto clangCls = ClangClInfo::installedCompilers(
+ {findClangCl()}, ConsoleLogger::instance());
+ if (clangCls.empty()) {
qbsInfo() << Tr::tr("%1 was not found.").arg(compilerName);
return;
}
- const QFileInfo compiler(compilerFilePath);
- const auto vcvarsallPath = findCompatibleVcsarsallBat();
- if (vcvarsallPath.isEmpty()) {
- qbsWarning()
- << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.")
- .arg(compilerName);
- return;
- }
const QString architectures[] = {
QStringLiteral("x86_64"),
QStringLiteral("x86")
};
- const auto toolchainInstallPath = getToolchainInstallPath(compiler);
- for (const auto &arch: architectures) {
- const auto profileName = QStringLiteral("clang-cl-%1").arg(arch);
- auto profile = createProfileHelper(
- settings, profileName, toolchainInstallPath, vcvarsallPath, arch);
- profiles.push_back(std::move(profile));
+ for (size_t index = 0; index < clangCls.size(); ++index) {
+ const auto &clangCl = clangCls[index];
+ const QString suffix = index == 0 ? QString() : QStringLiteral("-%1").arg(index);
+ qbs::Internal::transform(
+ architectures, profiles, [settings, clangCl, suffix](const auto &arch) {
+ const auto profileName = QStringLiteral("clang-cl") + suffix
+ + QStringLiteral("-%1").arg(arch);
+ return createProfileHelper(
+ settings,
+ profileName,
+ clangCl.toolchainInstallPath,
+ clangCl.vcvarsallPath,
+ arch);
+ });
}
}
diff --git a/src/app/qbs-setup-toolchains/cosmicprobe.cpp b/src/app/qbs-setup-toolchains/cosmicprobe.cpp
new file mode 100644
index 000000000..82b7193bc
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/cosmicprobe.cpp
@@ -0,0 +1,179 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "cosmicprobe.h"
+
+#include "../shared/logging/consolelogger.h"
+
+#include <logging/translator.h>
+
+#include <tools/hostosinfo.h>
+#include <tools/profile.h>
+
+#include <QtCore/qprocess.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qsettings.h>
+#include <QtCore/qstandardpaths.h>
+
+using namespace qbs;
+using Internal::Tr;
+using Internal::HostOsInfo;
+
+static QStringList knownCosmicCompilerNames()
+{
+ return {QStringLiteral("cxcorm"), QStringLiteral("cxstm8"),
+ QStringLiteral("cx6808"), QStringLiteral("cx6812"),
+ QStringLiteral("cx332")};
+}
+
+static QString guessCosmicArchitecture(const QFileInfo &compiler)
+{
+ const auto baseName = compiler.baseName();
+ if (baseName == QLatin1String("cxcorm"))
+ return QStringLiteral("arm");
+ if (baseName == QLatin1String("cxstm8"))
+ return QStringLiteral("stm8");
+ if (baseName == QLatin1String("cx6808"))
+ return QStringLiteral("hcs8");
+ if (baseName == QLatin1String("cx6812"))
+ return QStringLiteral("hcs12");
+ if (baseName == QLatin1String("cx332"))
+ return QStringLiteral("m68k");
+ return {};
+}
+
+static Profile createCosmicProfileHelper(const ToolchainInstallInfo &info,
+ Settings *settings,
+ QString profileName = QString())
+{
+ const QFileInfo compiler = info.compilerPath;
+ const QString architecture = guessCosmicArchitecture(compiler);
+
+ // In case the profile is auto-detected.
+ if (profileName.isEmpty()) {
+ if (!info.compilerVersion.isValid()) {
+ profileName = QStringLiteral("cosmic-unknown-%1").arg(architecture);
+ } else {
+ const QString version = info.compilerVersion.toString(QLatin1Char('_'),
+ QLatin1Char('_'));
+ profileName = QStringLiteral("cosmic-%1-%2").arg(
+ version, architecture);
+ }
+ }
+
+ Profile profile(profileName, settings);
+ profile.setValue(QLatin1String("cpp.toolchainInstallPath"), compiler.absolutePath());
+ profile.setValue(QLatin1String("qbs.toolchainType"), QLatin1String("cosmic"));
+ if (!architecture.isEmpty())
+ profile.setValue(QLatin1String("qbs.architecture"), architecture);
+
+ qbsInfo() << Tr::tr("Profile '%1' created for '%2'.").arg(
+ profile.name(), compiler.absoluteFilePath());
+ return profile;
+}
+
+static Version dumpCosmicCompilerVersion(const QFileInfo &compiler)
+{
+ QProcess p;
+ QStringList args;
+ p.start(compiler.absoluteFilePath(), args);
+ p.waitForFinished(3000);
+ const auto es = p.exitStatus();
+ if (es != QProcess::NormalExit) {
+ const QByteArray out = p.readAll();
+ qbsWarning() << Tr::tr("Compiler dumping failed:\n%1")
+ .arg(QString::fromUtf8(out));
+ return Version{};
+ }
+
+ const QByteArray output = p.readAllStandardError();
+ const QRegularExpression re(QLatin1String("^COSMIC.+V(\\d+)\\.?(\\d+)\\.?(\\*|\\d+)?"));
+ const QRegularExpressionMatch match = re.match(QString::fromLatin1(output));
+ if (!match.hasMatch())
+ return Version{};
+
+ const auto major = match.captured(1).toInt();
+ const auto minor = match.captured(2).toInt();
+ const auto patch = match.captured(3).toInt();
+ return Version{major, minor, patch};
+}
+
+static std::vector<ToolchainInstallInfo> installedCosmicsFromPath()
+{
+ std::vector<ToolchainInstallInfo> infos;
+ const auto compilerNames = knownCosmicCompilerNames();
+ for (const QString &compilerName : compilerNames) {
+ const QFileInfo cosmicPath(
+ findExecutable(
+ HostOsInfo::appendExecutableSuffix(compilerName)));
+ if (!cosmicPath.exists())
+ continue;
+ const Version version = dumpCosmicCompilerVersion(cosmicPath);
+ infos.push_back({cosmicPath, version});
+ }
+ std::sort(infos.begin(), infos.end());
+ return infos;
+}
+
+bool isCosmicCompiler(const QString &compilerName)
+{
+ return Internal::any_of(knownCosmicCompilerNames(), [compilerName](const QString &knownName) {
+ return compilerName.contains(knownName);
+ });
+}
+
+void createCosmicProfile(const QFileInfo &compiler, Settings *settings,
+ QString profileName)
+{
+ const ToolchainInstallInfo info = {compiler, Version{}};
+ createCosmicProfileHelper(info, settings, std::move(profileName));
+}
+
+void cosmicProbe(Settings *settings, std::vector<Profile> &profiles)
+{
+ qbsInfo() << Tr::tr("Trying to detect COSMIC toolchains...");
+
+ const std::vector<ToolchainInstallInfo> allInfos = installedCosmicsFromPath();
+ qbs::Internal::transform(allInfos, profiles, [settings](const auto &info) {
+ return createCosmicProfileHelper(info, settings); });
+
+ if (allInfos.empty())
+ qbsInfo() << Tr::tr("No COSMIC toolchains found.");
+}
diff --git a/src/app/qbs-setup-toolchains/cosmicprobe.h b/src/app/qbs-setup-toolchains/cosmicprobe.h
new file mode 100644
index 000000000..9b7b4bb10
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/cosmicprobe.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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_COSMICPROBE_H
+#define QBS_SETUPTOOLCHAINS_COSMICPROBE_H
+
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+class QFileInfo;
+QT_END_NAMESPACE
+
+namespace qbs {
+class Profile;
+class Settings;
+}
+
+bool isCosmicCompiler(const QString &compilerName);
+
+void createCosmicProfile(const QFileInfo &compiler, qbs::Settings *settings,
+ QString profileName);
+
+void cosmicProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles);
+
+#endif // Header guard
diff --git a/src/app/qbs-setup-toolchains/dmcprobe.cpp b/src/app/qbs-setup-toolchains/dmcprobe.cpp
new file mode 100644
index 000000000..097b1caed
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/dmcprobe.cpp
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "dmcprobe.h"
+
+#include "../shared/logging/consolelogger.h"
+
+#include <logging/translator.h>
+
+#include <tools/hostosinfo.h>
+#include <tools/profile.h>
+
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qsettings.h>
+#include <QtCore/qstandardpaths.h>
+
+#include <optional>
+
+using namespace qbs;
+using Internal::Tr;
+using Internal::HostOsInfo;
+
+namespace {
+struct Target {
+ QString platform;
+ QString architecture;
+ QString extender;
+};
+}
+
+static QStringList knownDmcCompilerNames()
+{
+ return {QStringLiteral("dmc")};
+}
+
+static QStringList dumpOutput(const QFileInfo &compiler, const QStringList &flags,
+ const QStringList &keys)
+{
+ const QString filePath = QDir(QDir::tempPath()).absoluteFilePath(
+ QLatin1String("dmc-dump.c"));
+ QFile fakeIn(filePath);
+ if (!fakeIn.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)) {
+ qbsWarning() << Tr::tr("Unable to open temporary file %1 for output: %2")
+ .arg(fakeIn.fileName(), fakeIn.errorString());
+ return {};
+ }
+ fakeIn.write("#define VALUE_TO_STRING(x) #x\n");
+ fakeIn.write("#define VALUE(x) VALUE_TO_STRING(x)\n");
+ fakeIn.write("#define VAR_NAME_VALUE(var) \"#define \" #var\" \"VALUE(var)\n");
+ for (const auto &key : keys) {
+ const auto content = key.toLatin1();
+ fakeIn.write("#if defined(" + content + ")\n");
+ fakeIn.write("#pragma message (VAR_NAME_VALUE(" + content + "))\n");
+ fakeIn.write("#endif\n");
+ }
+ fakeIn.close();
+
+ QStringList args = {QStringLiteral("-e")};
+ args.reserve(args.size() + int(flags.size()));
+ std::copy(flags.cbegin(), flags.cend(), std::back_inserter(args));
+ args.push_back(QDir::toNativeSeparators(filePath));
+
+ QProcess p;
+ p.start(compiler.absoluteFilePath(), args);
+ p.waitForFinished(3000);
+ fakeIn.remove();
+ static QRegularExpression re(QLatin1String("\\r?\\n"));
+ return QString::fromUtf8(p.readAllStandardOutput()).split(re);
+}
+
+static std::optional<Target> dumpDmcTarget(const QFileInfo &compiler, const QStringList &flags)
+{
+ const QStringList keys = {
+ QStringLiteral("DOS16RM"),
+ QStringLiteral("DOS386"),
+ QStringLiteral("MSDOS"),
+ QStringLiteral("__NT__"),
+ QStringLiteral("_WINDOWS"),
+ };
+ const auto macros = dumpMacros([&compiler, &flags, &keys]() {
+ return dumpOutput(compiler, flags, keys); });
+
+ if (macros.contains(QLatin1String("__NT__"))) {
+ return Target{QLatin1String("windows"), QLatin1String("x86"), QLatin1String("")};
+ } else if (macros.contains(QLatin1String("_WINDOWS"))) {
+ return Target{QLatin1String("windows"), QLatin1String("x86_16"), QLatin1String("")};
+ } else if (macros.contains(QLatin1String("DOS386"))) {
+ if (flags.contains(QLatin1String("-mx")))
+ return Target{QLatin1String("dos"), QLatin1String("x86"), QLatin1String("dosx")};
+ else if (flags.contains(QLatin1String("-mp")))
+ return Target{QLatin1String("dos"), QLatin1String("x86"), QLatin1String("dosp")};
+ } else if (macros.contains(QLatin1String("DOS16RM"))) {
+ if (flags.contains(QLatin1String("-mz")))
+ return Target{QLatin1String("dos"), QLatin1String("x86_16"), QLatin1String("dosz")};
+ else if (flags.contains(QLatin1String("-mr")))
+ return Target{QLatin1String("dos"), QLatin1String("x86_16"), QLatin1String("dosr")};
+ } else if (macros.contains(QLatin1String("MSDOS"))) {
+ return Target{QLatin1String("dos"), QLatin1String("x86_16"), QLatin1String("")};
+ }
+
+ return {};
+}
+
+static std::vector<Profile> createDmcProfileHelper(const ToolchainInstallInfo &info,
+ Settings *settings,
+ QStringView profileName = {})
+{
+ const QFileInfo compiler = info.compilerPath;
+ std::vector<Profile> profiles;
+
+ const QVector<QStringList> probes = {
+ { QStringLiteral("-mn"), QStringLiteral("-WA") }, // Windows NT 32 bit.
+ { QStringLiteral("-ml"), QStringLiteral("-WA") }, // Windows 3.x 16 bit.
+ { QStringLiteral("-mx") }, // DOS with DOSX extender 32 bit.
+ { QStringLiteral("-mp") }, // DOS with Phar Lap extender 32 bit.
+ { QStringLiteral("-mr") }, // DOS with Rational DOS Extender 16 bit.
+ { QStringLiteral("-mz") }, // DOS with ZPM DOS Extender 16 bit.
+ { QStringLiteral("-mc") }, // DOS 16 bit.
+ };
+
+ for (const auto &flags : probes) {
+ const auto target = dumpDmcTarget(compiler, flags);
+ if (!target)
+ continue;
+
+ QString fullProfilename;
+ QString platform = target->extender.isEmpty() ? target->platform : target->extender;
+ if (profileName.isEmpty()) {
+ // Create a full profile name in case we is in auto-detecting mode.
+ if (!info.compilerVersion.isValid()) {
+ fullProfilename = QStringLiteral("dmc-unknown-%1-%2")
+ .arg(platform, target->architecture);
+ } else {
+ const QString version = info.compilerVersion.toString(QLatin1Char('_'),
+ QLatin1Char('_'));
+ fullProfilename = QStringLiteral("dmc-%1-%2-%3")
+ .arg(version, platform, target->architecture);
+ }
+ } else {
+ // Append the detected actual architecture name to the proposed profile name.
+ fullProfilename = QStringLiteral("%1-%2-%3")
+ .arg(profileName, platform, target->architecture);
+ }
+
+ Profile profile(fullProfilename, settings);
+ profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), compiler.absolutePath());
+ profile.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("dmc"));
+ profile.setValue(QStringLiteral("qbs.architecture"), target->architecture);
+ profile.setValue(QStringLiteral("qbs.targetPlatform"), target->platform);
+ if (!target->extender.isEmpty())
+ profile.setValue(QStringLiteral("cpp.extenderName"), target->extender);
+
+ qbsInfo() << Tr::tr("Profile '%1' created for '%2'.")
+ .arg(profile.name(), compiler.absoluteFilePath());
+
+ profiles.push_back(std::move(profile));
+ }
+
+ return profiles;
+}
+
+static Version dumpDmcVersion(const QFileInfo &compiler)
+{
+ const QStringList keys = {QStringLiteral("__DMC__")};
+ const auto macros = dumpMacros([&compiler, keys]() {
+ return dumpOutput(compiler, {}, keys); });
+ for (const auto &macro : macros) {
+ if (!macro.startsWith(QLatin1String("0x")))
+ continue;
+ const int verCode = macro.mid(2).toInt();
+ return Version{(verCode / 100), (verCode % 100), 0};
+ }
+ qbsWarning() << Tr::tr("No __DMC__ token was found in the compiler dump");
+ return Version{};
+}
+
+static std::vector<ToolchainInstallInfo> installedDmcsFromPath()
+{
+ std::vector<ToolchainInstallInfo> infos;
+ const auto compilerNames = knownDmcCompilerNames();
+ for (const QString &compilerName : compilerNames) {
+ const QFileInfo dmcPath(
+ findExecutable(
+ HostOsInfo::appendExecutableSuffix(compilerName)));
+ if (!dmcPath.exists())
+ continue;
+ const Version version = dumpDmcVersion(dmcPath);
+ infos.push_back({dmcPath, version});
+ }
+ std::sort(infos.begin(), infos.end());
+ return infos;
+}
+
+bool isDmcCompiler(const QString &compilerName)
+{
+ return Internal::any_of(knownDmcCompilerNames(),
+ [compilerName](const QString &knownName) {
+ return compilerName.contains(knownName);
+ });
+}
+
+void createDmcProfile(const QFileInfo &compiler, Settings *settings,
+ QStringView profileName)
+{
+ const ToolchainInstallInfo info = {compiler, Version{}};
+ createDmcProfileHelper(info, settings, profileName);
+}
+
+void dmcProbe(Settings *settings, std::vector<Profile> &profiles)
+{
+ qbsInfo() << Tr::tr("Trying to detect DMC toolchains...");
+
+ const std::vector<ToolchainInstallInfo> allInfos = installedDmcsFromPath();
+ if (allInfos.empty()) {
+ qbsInfo() << Tr::tr("No DMC toolchains found.");
+ return;
+ }
+
+ for (const ToolchainInstallInfo &info : allInfos) {
+ const auto newProfiles = createDmcProfileHelper(info, settings);
+ profiles.reserve(profiles.size() + int(newProfiles.size()));
+ std::copy(newProfiles.cbegin(), newProfiles.cend(), std::back_inserter(profiles));
+ }
+}
diff --git a/src/app/qbs-setup-toolchains/dmcprobe.h b/src/app/qbs-setup-toolchains/dmcprobe.h
new file mode 100644
index 000000000..3159c2df5
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/dmcprobe.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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_DMCPROBE_H
+#define QBS_SETUPTOOLCHAINS_DMCPROBE_H
+
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+class QFileInfo;
+QT_END_NAMESPACE
+
+namespace qbs {
+class Profile;
+class Settings;
+}
+
+bool isDmcCompiler(const QString &compilerName);
+
+void createDmcProfile(const QFileInfo &compiler, qbs::Settings *settings,
+ QStringView profileName);
+
+void dmcProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles);
+
+#endif // Header guard
diff --git a/src/app/qbs-setup-toolchains/gccprobe.cpp b/src/app/qbs-setup-toolchains/gccprobe.cpp
index 1e0f9c142..c8c04b0cc 100644
--- a/src/app/qbs-setup-toolchains/gccprobe.cpp
+++ b/src/app/qbs-setup-toolchains/gccprobe.cpp
@@ -47,6 +47,7 @@
#include <tools/hostosinfo.h>
#include <tools/profile.h>
#include <tools/settings.h>
+#include <tools/stlutils.h>
#include <tools/toolchains.h>
#include <QtCore/qdir.h>
@@ -111,7 +112,7 @@ class ToolchainDetails
public:
explicit ToolchainDetails(const QFileInfo &compiler)
{
- auto baseName = compiler.completeBaseName();
+ auto baseName = HostOsInfo::stripExecutableSuffix(compiler.fileName());
// Extract the version sub-string if it exists. We assume that a version
// sub-string located after the compiler prefix && suffix. E.g. this code
// parses a version from the compiler names, like this:
@@ -138,9 +139,9 @@ public:
};
static void setCommonProperties(Profile &profile, const QFileInfo &compiler,
- const QStringList &toolchainTypes, const ToolchainDetails &details)
+ const QString &toolchainType, const ToolchainDetails &details)
{
- if (toolchainTypes.contains(QStringLiteral("mingw")))
+ if (toolchainType == QStringLiteral("mingw"))
profile.setValue(QStringLiteral("qbs.targetPlatform"),
QStringLiteral("windows"));
@@ -149,7 +150,7 @@ static void setCommonProperties(Profile &profile, const QFileInfo &compiler,
profile.setValue(QStringLiteral("cpp.toolchainInstallPath"),
compiler.absolutePath());
- profile.setValue(QStringLiteral("qbs.toolchain"), toolchainTypes);
+ profile.setValue(QStringLiteral("qbs.toolchainType"), toolchainType);
if (!standardCompilerFileNames().contains(
HostOsInfo::appendExecutableSuffix(details.suffix))) {
@@ -230,12 +231,9 @@ private:
static bool doesProfileTargetOS(const Profile &profile, const QString &os)
{
const auto target = profile.value(QStringLiteral("qbs.targetPlatform"));
- if (target.isValid()) {
- return Internal::contains(HostOsInfo::canonicalOSIdentifiers(
- target.toString().toStdString()),
- os.toStdString());
- }
- return Internal::contains(HostOsInfo::hostOSIdentifiers(), os.toStdString());
+ if (target.isValid())
+ return Internal::contains(HostOsInfo::canonicalOSIdentifiers(target.toString()), os);
+ return Internal::contains(HostOsInfo::hostOSIdentifiers(), os);
}
static QString buildProfileName(const QFileInfo &cfi)
@@ -269,11 +267,8 @@ static QStringList buildCompilerNameFilters(const QString &compilerName)
QLatin1String("*-*-*-*-") + compilerName + QLatin1String("-[1-9]*")
};
- std::transform(filters.begin(), filters.end(), filters.begin(),
- [](const auto &filter) {
- return HostOsInfo::appendExecutableSuffix(filter);
- });
-
+ Internal::transform(filters, [](const auto &filter) {
+ return HostOsInfo::appendExecutableSuffix(filter); });
return filters;
}
@@ -282,30 +277,28 @@ static QStringList gnuRegistrySearchPaths()
if (!HostOsInfo::isWindowsHost())
return {};
- QStringList searchPaths;
+#ifdef Q_OS_WIN64
+ static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\ARM";
+#else
+ static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\ARM";
+#endif
- QSettings registry(QLatin1String(kUninstallRegistryKey), QSettings::NativeFormat);
- const auto productGroups = registry.childGroups();
- for (const QString &productKey : productGroups) {
- // Registry token for the "GNU Tools for ARM Embedded Processors".
- if (!productKey.startsWith(
- QLatin1String("GNU Tools for ARM Embedded Processors"))) {
- continue;
+ QStringList searchPaths;
+ QSettings registry(QLatin1String(kRegistryNode), QSettings::NativeFormat);
+ const auto groupKeys = registry.childGroups();
+ for (const QString &groupKey : groupKeys) {
+ registry.beginGroup(groupKey);
+ const QString rootPath = registry.value(QStringLiteral("InstallFolder")).toString();
+ if (!rootPath.isEmpty()) {
+ const QFileInfo toolchainPath(rootPath + QLatin1String("/bin"));
+ if (toolchainPath.exists()) {
+ const auto filePath = toolchainPath.absoluteFilePath();
+ if (!searchPaths.contains(filePath))
+ searchPaths.push_back(filePath);
+ }
}
- registry.beginGroup(productKey);
- QString uninstallFilePath = registry.value(
- QLatin1String("UninstallString")).toString();
- if (uninstallFilePath.startsWith(QLatin1Char('"')))
- uninstallFilePath.remove(0, 1);
- if (uninstallFilePath.endsWith(QLatin1Char('"')))
- uninstallFilePath.remove(uninstallFilePath.size() - 1, 1);
registry.endGroup();
-
- const QString toolkitRootPath = QFileInfo(uninstallFilePath).path();
- const QString toolchainPath = toolkitRootPath + QLatin1String("/bin");
- searchPaths.push_back(toolchainPath);
}
-
return searchPaths;
}
@@ -410,8 +403,8 @@ static QStringList renesasRl78RegistrySearchPaths()
if (installLocation.isEmpty())
continue;
- const QFileInfo toolchainPath = QDir(installLocation).absolutePath()
- + QLatin1String("/rl78-elf/rl78-elf/bin");
+ const QFileInfo toolchainPath(QDir(installLocation).absolutePath()
+ + QLatin1String("/rl78-elf/rl78-elf/bin"));
if (!toolchainPath.exists())
continue;
searchPaths.push_back(toolchainPath.absoluteFilePath());
@@ -442,8 +435,8 @@ static QStringList mplabX32RegistrySearchPaths()
if (installLocation.isEmpty())
continue;
- const QFileInfo toolchainPath = QDir(installLocation).absolutePath()
- + QLatin1String("/bin");
+ const QFileInfo toolchainPath(QDir(installLocation).absolutePath()
+ + QLatin1String("/bin"));
if (!toolchainPath.exists())
continue;
searchPaths.push_back(toolchainPath.absoluteFilePath());
@@ -453,12 +446,12 @@ static QStringList mplabX32RegistrySearchPaths()
}
Profile createGccProfile(const QFileInfo &compiler, Settings *settings,
- const QStringList &toolchainTypes,
+ const QString &toolchainType,
const QString &profileName)
{
const QString machineName = gccMachineName(compiler);
- if (toolchainTypes.contains(QLatin1String("mingw"))) {
+ if (toolchainType == QLatin1String("mingw")) {
if (!validMinGWMachines().contains(machineName)) {
throw ErrorInfo(Tr::tr("Detected gcc platform '%1' is not supported.")
.arg(machineName));
@@ -470,9 +463,9 @@ Profile createGccProfile(const QFileInfo &compiler, Settings *settings,
const ToolchainDetails details(compiler);
- setCommonProperties(profile, compiler, toolchainTypes, details);
+ setCommonProperties(profile, compiler, toolchainType, details);
- if (HostOsInfo::isWindowsHost() && toolchainTypes.contains(QLatin1String("clang"))) {
+ if (HostOsInfo::isWindowsHost() && toolchainType == QLatin1String("clang")) {
const QStringList profileNames = settings->profiles();
bool foundMingw = false;
for (const QString &profileName : profileNames) {
@@ -497,7 +490,7 @@ Profile createGccProfile(const QFileInfo &compiler, Settings *settings,
}
}
- if (!toolchainTypes.contains(QLatin1String("clang"))) {
+ if (toolchainType != QLatin1String("clang")) {
// 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 = compiler.absolutePath();
@@ -533,7 +526,7 @@ void gccProbe(Settings *settings, std::vector<Profile> &profiles, const QString
std::vector<QFileInfo> candidates;
const auto filters = buildCompilerNameFilters(compilerName);
- for (const auto &searchPath : searchPaths) {
+ for (const auto &searchPath : std::as_const(searchPaths)) {
const QDir dir(searchPath);
const QStringList fileNames = dir.entryList(
filters, QDir::Files | QDir::Executable);
@@ -543,7 +536,7 @@ void gccProbe(Settings *settings, std::vector<Profile> &profiles, const QString
|| fileName.startsWith(QLatin1String("c99-gcc"))) {
continue;
}
- const QFileInfo candidate = dir.filePath(fileName);
+ const QFileInfo candidate(dir.filePath(fileName));
// Filter duplicates.
const auto existingEnd = candidates.end();
const auto existingIt = std::find_if(
@@ -581,12 +574,16 @@ void gccProbe(Settings *settings, std::vector<Profile> &profiles, const QString
});
}
- for (const auto &candidate : qAsConst(candidates)) {
- const QStringList toolchainTypes = toolchainTypeFromCompilerName(
+ for (const auto &candidate : std::as_const(candidates)) {
+ const QString toolchainType = toolchainTypeFromCompilerName(
candidate.baseName());
const QString profileName = buildProfileName(candidate);
- auto profile = createGccProfile(candidate, settings,
- toolchainTypes, profileName);
- profiles.push_back(std::move(profile));
+ try {
+ auto profile = createGccProfile(candidate, settings,
+ toolchainType, profileName);
+ profiles.push_back(std::move(profile));
+ } catch (const qbs::ErrorInfo &info) {
+ qbsWarning() << Tr::tr("Skipping %1: %2").arg(profileName, info.toString());
+ }
}
}
diff --git a/src/app/qbs-setup-toolchains/gccprobe.h b/src/app/qbs-setup-toolchains/gccprobe.h
index ae389dafc..98e7eaa1f 100644
--- a/src/app/qbs-setup-toolchains/gccprobe.h
+++ b/src/app/qbs-setup-toolchains/gccprobe.h
@@ -53,7 +53,7 @@ class Settings;
qbs::Profile createGccProfile(const QFileInfo &compiler,
qbs::Settings *settings,
- const QStringList &toolchainTypes,
+ const QString &toolchainType,
const QString &profileName = QString());
void gccProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles,
diff --git a/src/app/qbs-setup-toolchains/iarewprobe.cpp b/src/app/qbs-setup-toolchains/iarewprobe.cpp
index 7f710532a..adf5febb3 100644
--- a/src/app/qbs-setup-toolchains/iarewprobe.cpp
+++ b/src/app/qbs-setup-toolchains/iarewprobe.cpp
@@ -59,7 +59,14 @@ static QStringList knownIarCompilerNames()
{
return {QStringLiteral("icc8051"), QStringLiteral("iccarm"),
QStringLiteral("iccavr"), QStringLiteral("iccstm8"),
- QStringLiteral("icc430"), QStringLiteral("iccrl78")};
+ QStringLiteral("icc430"), QStringLiteral("iccrl78"),
+ QStringLiteral("iccrx"), QStringLiteral("iccrh850"),
+ QStringLiteral("iccv850"), QStringLiteral("icc78k"),
+ QStringLiteral("iccavr32"), QStringLiteral("iccsh"),
+ QStringLiteral("iccriscv"), QStringLiteral("icccf"),
+ QStringLiteral("iccm32c"), QStringLiteral("iccr32c"),
+ QStringLiteral("iccm16c"), QStringLiteral("icccr16c"),
+ QStringLiteral("icchcs12"), QStringLiteral("iccs08")};
}
static QString guessIarArchitecture(const QFileInfo &compiler)
@@ -77,6 +84,34 @@ static QString guessIarArchitecture(const QFileInfo &compiler)
return QStringLiteral("msp430");
if (baseName == QLatin1String("iccrl78"))
return QStringLiteral("rl78");
+ if (baseName == QLatin1String("iccrx"))
+ return QStringLiteral("rx");
+ if (baseName == QLatin1String("iccrh850"))
+ return QStringLiteral("rh850");
+ if (baseName == QLatin1String("iccv850"))
+ return QStringLiteral("v850");
+ if (baseName == QLatin1String("icc78k"))
+ return QStringLiteral("78k");
+ if (baseName == QLatin1String("iccavr32"))
+ return QStringLiteral("avr32");
+ if (baseName == QLatin1String("iccsh"))
+ return QStringLiteral("sh");
+ if (baseName == QLatin1String("iccriscv"))
+ return QStringLiteral("riscv");
+ if (baseName == QLatin1String("icccf"))
+ return QStringLiteral("m68k");
+ if (baseName == QLatin1String("iccm32c"))
+ return QStringLiteral("m32c");
+ if (baseName == QLatin1String("iccr32c"))
+ return QStringLiteral("r32c");
+ if (baseName == QLatin1String("iccm16c"))
+ return QStringLiteral("m16c");
+ if (baseName == QLatin1String("icccr16c"))
+ return QStringLiteral("cr16");
+ if (baseName == QLatin1String("icchcs12"))
+ return QStringLiteral("hcs12");
+ if (baseName == QLatin1String("iccs08"))
+ return QStringLiteral("hcs8");
return {};
}
@@ -142,13 +177,27 @@ static Version dumpIarCompilerVersion(const QFileInfo &compiler)
}
const QString arch = guessIarArchitecture(compiler);
- if (arch == QLatin1String("arm")) {
+ if (arch == QLatin1String("arm"))
return Version{verCode / 1000000, (verCode / 1000) % 1000, verCode % 1000};
- } else if (arch == QLatin1String("avr")
+ if (arch == QLatin1String("avr")
|| arch == QLatin1String("mcs51")
|| arch == QLatin1String("stm8")
|| arch == QLatin1String("msp430")
- || arch == QLatin1String("rl78")) {
+ || arch == QLatin1String("rl78")
+ || arch == QLatin1String("rx")
+ || arch == QLatin1String("rh850")
+ || arch == QLatin1String("v850")
+ || arch == QLatin1String("78k")
+ || arch == QLatin1String("avr32")
+ || arch == QLatin1String("sh")
+ || arch == QLatin1String("riscv")
+ || arch == QLatin1String("m68k")
+ || arch == QLatin1String("m32c")
+ || arch == QLatin1String("r32c")
+ || arch == QLatin1String("m16c")
+ || arch == QLatin1String("rc16")
+ || arch == QLatin1String("hcs12")
+ || arch == QLatin1String("hcs8")) {
return Version{verCode / 100, verCode % 100};
}
@@ -189,12 +238,26 @@ static std::vector<ToolchainInstallInfo> installedIarsFromRegistry()
QString registryKey;
QString subExePath;
} knowToolchains[] = {
- {QStringLiteral("EWARM"), QStringLiteral("\\arm\\bin\\iccarm.exe")},
- {QStringLiteral("EWAVR"), QStringLiteral("\\avr\\bin\\iccavr.exe")},
- {QStringLiteral("EW8051"), QStringLiteral("\\8051\\bin\\icc8051.exe")},
- {QStringLiteral("EWSTM8"), QStringLiteral("\\stm8\\bin\\iccstm8.exe")},
- {QStringLiteral("EW430"), QStringLiteral("\\430\\bin\\icc430.exe")},
- {QStringLiteral("EWRL78"), QStringLiteral("\\rl78\\bin\\iccrl78.exe")},
+ {QStringLiteral("EWARM"), QStringLiteral("/arm/bin/iccarm.exe")},
+ {QStringLiteral("EWAVR"), QStringLiteral("/avr/bin/iccavr.exe")},
+ {QStringLiteral("EW8051"), QStringLiteral("/8051/bin/icc8051.exe")},
+ {QStringLiteral("EWSTM8"), QStringLiteral("/stm8/bin/iccstm8.exe")},
+ {QStringLiteral("EW430"), QStringLiteral("/430/bin/icc430.exe")},
+ {QStringLiteral("EWRL78"), QStringLiteral("/rl78/bin/iccrl78.exe")},
+ {QStringLiteral("EWRX"), QStringLiteral("/rx/bin/iccrx.exe")},
+ {QStringLiteral("EWRH850"), QStringLiteral("/rh850/bin/iccrh850.exe")},
+ {QStringLiteral("EWV850"), QStringLiteral("/v850/bin/iccv850.exe")},
+ {QStringLiteral("EW78K"), QStringLiteral("/78k/bin/icc78k.exe")},
+ {QStringLiteral("EWAVR32"), QStringLiteral("/avr32/bin/iccavr32.exe")},
+ {QStringLiteral("EWSH"), QStringLiteral("/sh/bin/iccsh.exe")},
+ {QStringLiteral("EWRISCV"), QStringLiteral("/riscv/bin/iccriscv.exe")},
+ {QStringLiteral("EWCF"), QStringLiteral("/cf/bin/icccf.exe")},
+ {QStringLiteral("EWM32C"), QStringLiteral("/m32c/bin/iccm32c.exe")},
+ {QStringLiteral("EWR32C"), QStringLiteral("/r32c/bin/iccr32c.exe")},
+ {QStringLiteral("EWM16C"), QStringLiteral("/m16c/bin/iccm16c.exe")},
+ {QStringLiteral("EWCR16C"), QStringLiteral("/cr16c/bin/icccr16c.exe")},
+ {QStringLiteral("EWHCS12"), QStringLiteral("/hcs12/bin/icchcs12.exe")},
+ {QStringLiteral("EWS08"), QStringLiteral("/s08/bin/iccs08.exe")},
};
QSettings registry(QLatin1String(kRegistryNode), QSettings::NativeFormat);
@@ -215,8 +278,7 @@ static std::vector<ToolchainInstallInfo> installedIarsFromRegistry()
const QFileInfo iarPath(rootPath + entry.subExePath);
if (iarPath.exists()) {
// Note: threeLevelKey is a guessed toolchain version.
- const QString version = threeLevelKey;
- infos.push_back({iarPath, Version::fromString(version)});
+ infos.push_back({iarPath, Version::fromString(threeLevelKey, true)});
}
}
registry.endGroup();
@@ -245,7 +307,7 @@ void createIarProfile(const QFileInfo &compiler, Settings *settings,
QString profileName)
{
const ToolchainInstallInfo info = {compiler, Version{}};
- createIarProfileHelper(info, settings, profileName);
+ createIarProfileHelper(info, settings, std::move(profileName));
}
void iarProbe(Settings *settings, std::vector<Profile> &profiles)
@@ -261,10 +323,8 @@ void iarProbe(Settings *settings, std::vector<Profile> &profiles)
pathInfos.cbegin(), pathInfos.cend(),
std::back_inserter(allInfos));
- for (const ToolchainInstallInfo &info : allInfos) {
- const auto profile = createIarProfileHelper(info, settings);
- profiles.push_back(profile);
- }
+ qbs::Internal::transform(allInfos, profiles, [settings](const auto &info) {
+ return createIarProfileHelper(info, settings); });
if (allInfos.empty())
qbsInfo() << Tr::tr("No IAR toolchains found.");
diff --git a/src/app/qbs-setup-toolchains/keilprobe.cpp b/src/app/qbs-setup-toolchains/keilprobe.cpp
index 728c5b3f2..4bab54c92 100644
--- a/src/app/qbs-setup-toolchains/keilprobe.cpp
+++ b/src/app/qbs-setup-toolchains/keilprobe.cpp
@@ -47,6 +47,7 @@
#include <tools/hostosinfo.h>
#include <tools/profile.h>
+#include <QtCore/qdir.h>
#include <QtCore/qprocess.h>
#include <QtCore/qsettings.h>
#include <QtCore/qtemporaryfile.h>
@@ -57,7 +58,9 @@ using Internal::HostOsInfo;
static QStringList knownKeilCompilerNames()
{
- return {QStringLiteral("c51"), QStringLiteral("armcc")};
+ return {QStringLiteral("c51"), QStringLiteral("c251"),
+ QStringLiteral("c166"), QStringLiteral("armcc"),
+ QStringLiteral("armclang")};
}
static QString guessKeilArchitecture(const QFileInfo &compiler)
@@ -65,11 +68,22 @@ static QString guessKeilArchitecture(const QFileInfo &compiler)
const auto baseName = compiler.baseName();
if (baseName == QLatin1String("c51"))
return QStringLiteral("mcs51");
+ if (baseName == QLatin1String("c251"))
+ return QStringLiteral("mcs251");
+ if (baseName == QLatin1String("c166"))
+ return QStringLiteral("c166");
if (baseName == QLatin1String("armcc"))
return QStringLiteral("arm");
+ if (baseName == QLatin1String("armclang"))
+ return QStringLiteral("arm");
return {};
}
+static bool isArmClangCompiler(const QFileInfo &compiler)
+{
+ return compiler.baseName() == QLatin1String("armclang");
+}
+
static Profile createKeilProfileHelper(const ToolchainInstallInfo &info,
Settings *settings,
QString profileName = QString())
@@ -84,13 +98,19 @@ static Profile createKeilProfileHelper(const ToolchainInstallInfo &info,
} else {
const QString version = info.compilerVersion.toString(QLatin1Char('_'),
QLatin1Char('_'));
- profileName = QStringLiteral("keil-%1-%2").arg(
- version, architecture);
+ if (architecture == QLatin1String("arm") && isArmClangCompiler(compiler)) {
+ profileName = QStringLiteral("keil-llvm-%1-%2").arg(
+ version, architecture);
+ } else {
+ profileName = QStringLiteral("keil-%1-%2").arg(
+ version, architecture);
+ }
}
}
Profile profile(profileName, settings);
profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), compiler.absolutePath());
+ profile.setValue(QStringLiteral("cpp.compilerName"), compiler.fileName());
profile.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("keil"));
if (!architecture.isEmpty())
profile.setValue(QStringLiteral("qbs.architecture"), architecture);
@@ -100,74 +120,202 @@ static Profile createKeilProfileHelper(const ToolchainInstallInfo &info,
return profile;
}
-static Version dumpKeilCompilerVersion(const QFileInfo &compiler)
+static Version dumpMcsCompilerVersion(const QFileInfo &compiler)
{
- const QString arch = guessKeilArchitecture(compiler);
- if (arch == QLatin1String("mcs51")) {
- QTemporaryFile fakeIn;
- if (!fakeIn.open()) {
- qbsWarning() << Tr::tr("Unable to open temporary file %1 for output: %2")
- .arg(fakeIn.fileName(), fakeIn.errorString());
- return Version{};
- }
- fakeIn.write("#define VALUE_TO_STRING(x) #x\n");
- fakeIn.write("#define VALUE(x) VALUE_TO_STRING(x)\n");
- fakeIn.write("#define VAR_NAME_VALUE(var) \"\"\"|\"#var\"|\"VALUE(var)\n");
- fakeIn.write("#ifdef __C51__\n");
- fakeIn.write("#pragma message(VAR_NAME_VALUE(__C51__))\n");
- fakeIn.write("#endif\n");
- fakeIn.write("#ifdef __CX51__\n");
- fakeIn.write("#pragma message(VAR_NAME_VALUE(__CX51__))\n");
- fakeIn.write("#endif\n");
- fakeIn.close();
-
- const QStringList args = {fakeIn.fileName()};
- QProcess p;
- p.start(compiler.absoluteFilePath(), args);
- p.waitForFinished(3000);
- const auto es = p.exitStatus();
- if (es != QProcess::NormalExit) {
- const QByteArray out = p.readAll();
- qbsWarning() << Tr::tr("Compiler dumping failed:\n%1")
- .arg(QString::fromUtf8(out));
- return Version{};
- }
+ QTemporaryFile fakeIn;
+ if (!fakeIn.open()) {
+ qbsWarning() << Tr::tr("Unable to open temporary file %1 for output: %2")
+ .arg(fakeIn.fileName(), fakeIn.errorString());
+ return Version{};
+ }
- const QByteArray dump = p.readAllStandardOutput();
- const int verCode = extractVersion(dump, "\"__C51__\"|\"");
- if (verCode < 0) {
- qbsWarning() << Tr::tr("No '__C51__' token was found"
- " in the compiler dump:\n%1")
- .arg(QString::fromUtf8(dump));
- return Version{};
- }
- return Version{verCode / 100, verCode % 100};
- } else if (arch == QLatin1String("arm")) {
- const QStringList args = {QStringLiteral("-E"),
- QStringLiteral("--list-macros"),
- QStringLiteral("nul")};
- QProcess p;
- p.start(compiler.absoluteFilePath(), args);
- p.waitForFinished(3000);
- const auto es = p.exitStatus();
- if (es != QProcess::NormalExit) {
- const QByteArray out = p.readAll();
- qbsWarning() << Tr::tr("Compiler dumping failed:\n%1")
- .arg(QString::fromUtf8(out));
- return Version{};
+ fakeIn.write("#define VALUE_TO_STRING(x) #x\n");
+ fakeIn.write("#define VALUE(x) VALUE_TO_STRING(x)\n");
+
+ // Prepare for C51 compiler.
+ fakeIn.write("#if defined(__C51__) || defined(__CX51__)\n");
+ fakeIn.write("# define VAR_NAME_VALUE(var) \"(\"\"\"\"|\"#var\"|\"VALUE(var)\"|\"\"\"\")\"\n");
+ fakeIn.write("# if defined (__C51__)\n");
+ fakeIn.write("# pragma message (VAR_NAME_VALUE(__C51__))\n");
+ fakeIn.write("# endif\n");
+ fakeIn.write("# if defined(__CX51__)\n");
+ fakeIn.write("# pragma message (VAR_NAME_VALUE(__CX51__))\n");
+ fakeIn.write("# endif\n");
+ fakeIn.write("#endif\n");
+
+ // Prepare for C251 compiler.
+ fakeIn.write("#if defined(__C251__)\n");
+ fakeIn.write("# define VAR_NAME_VALUE(var) \"\"|#var|VALUE(var)|\"\"\n");
+ fakeIn.write("# if defined(__C251__)\n");
+ fakeIn.write("# warning (VAR_NAME_VALUE(__C251__))\n");
+ fakeIn.write("# endif\n");
+ fakeIn.write("#endif\n");
+
+ fakeIn.close();
+
+ const QStringList args = {fakeIn.fileName()};
+ QProcess p;
+ p.start(compiler.absoluteFilePath(), args);
+ p.waitForFinished(3000);
+
+ const QStringList knownKeys = {QStringLiteral("__C51__"),
+ QStringLiteral("__CX51__"),
+ QStringLiteral("__C251__")};
+
+ auto extractVersion = [&knownKeys](const QByteArray &output) {
+ QTextStream stream(output);
+ QString line;
+ while (stream.readLineInto(&line)) {
+ if (!line.startsWith(QLatin1String("***")))
+ continue;
+ enum { KEY_INDEX = 1, VALUE_INDEX = 2, ALL_PARTS = 4 };
+ const QStringList parts = line.split(QLatin1String("\"|\""));
+ if (parts.count() != ALL_PARTS)
+ continue;
+ if (!knownKeys.contains(parts.at(KEY_INDEX)))
+ continue;
+ return parts.at(VALUE_INDEX).toInt();
}
+ return -1;
+ };
+
+ const QByteArray dump = p.readAllStandardOutput();
+ const int verCode = extractVersion(dump);
+ if (verCode < 0) {
+ qbsWarning() << Tr::tr("No %1 tokens was found"
+ " in the compiler dump:\n%2")
+ .arg(knownKeys.join(QLatin1Char(',')), QString::fromUtf8(dump));
+ return Version{};
+ }
+ return Version{verCode / 100, verCode % 100};
+}
- const QByteArray dump = p.readAll();
- const int verCode = extractVersion(dump, "__ARMCC_VERSION ");
- if (verCode < 0) {
- qbsWarning() << Tr::tr("No '__ARMCC_VERSION' token was found "
- "in the compiler dump:\n%1")
- .arg(QString::fromUtf8(dump));
- return Version{};
+static Version dumpC166CompilerVersion(const QFileInfo &compiler)
+{
+ QTemporaryFile fakeIn;
+ if (!fakeIn.open()) {
+ qbsWarning() << Tr::tr("Unable to open temporary file %1 for output: %2")
+ .arg(fakeIn.fileName(), fakeIn.errorString());
+ return Version{};
+ }
+
+ fakeIn.write("#if defined(__C166__)\n");
+ fakeIn.write("# warning __C166__\n");
+ fakeIn.write("# pragma __C166__\n");
+ fakeIn.write("#endif\n");
+
+ fakeIn.close();
+
+ const QStringList args = {fakeIn.fileName()};
+ QProcess p;
+ p.start(compiler.absoluteFilePath(), args);
+ p.waitForFinished(3000);
+
+ // Extract the compiler version pattern in the form, like:
+ //
+ // *** WARNING C320 IN LINE 41 OF c51.c: __C166__
+ // *** WARNING C2 IN LINE 42 OF c51.c: '757': unknown #pragma/control, line ignored
+ //
+ // where the '__C166__' is a key, and the '757' is a value (aka version).
+ auto extractVersion = [](const QString &output) {
+ const QStringList lines = output.split(QStringLiteral("\r\n"));
+ for (auto it = lines.cbegin(); it != lines.cend();) {
+ if (it->startsWith(QLatin1String("***")) && it->endsWith(QLatin1String("__C166__"))) {
+ ++it;
+ if (it == lines.cend() || !it->startsWith(QLatin1String("***")))
+ break;
+ const int startIndex = it->indexOf(QLatin1Char('\''));
+ if (startIndex == -1)
+ break;
+ const int stopIndex = it->indexOf(QLatin1Char('\''), startIndex + 1);
+ if (stopIndex == -1)
+ break;
+ const QString v = it->mid(startIndex + 1, stopIndex - startIndex - 1);
+ return v.toInt();
+ }
+ ++it;
}
- return Version{verCode / 1000000, (verCode / 10000) % 100, verCode % 10000};
+ return -1;
+ };
+
+ const QByteArray dump = p.readAllStandardOutput();
+ const int verCode = extractVersion(QString::fromUtf8(dump));
+ if (verCode < 0) {
+ qbsWarning() << Tr::tr("No __C166__ token was found"
+ " in the compiler dump:\n%1")
+ .arg(QString::fromUtf8(dump));
+ return Version{};
+ }
+ return Version{verCode / 100, verCode % 100};
+}
+
+static Version dumpArmCCCompilerVersion(const QFileInfo &compiler)
+{
+ const QStringList args = {QStringLiteral("-E"),
+ QStringLiteral("--list-macros"),
+ QStringLiteral("nul")};
+ QProcess p;
+ p.start(compiler.absoluteFilePath(), args);
+ p.waitForFinished(3000);
+ const auto es = p.exitStatus();
+ if (es != QProcess::NormalExit) {
+ const QByteArray out = p.readAll();
+ qbsWarning() << Tr::tr("Compiler dumping failed:\n%1")
+ .arg(QString::fromUtf8(out));
+ return Version{};
}
+ const QByteArray dump = p.readAll();
+ const int verCode = extractVersion(dump, "__ARMCC_VERSION ");
+ if (verCode < 0) {
+ qbsWarning() << Tr::tr("No '__ARMCC_VERSION' token was found "
+ "in the compiler dump:\n%1")
+ .arg(QString::fromUtf8(dump));
+ return Version{};
+ }
+ return Version{verCode / 1000000, (verCode / 10000) % 100, verCode % 10000};
+}
+
+static Version dumpArmClangCompilerVersion(const QFileInfo &compiler)
+{
+ const QStringList args = {QStringLiteral("-dM"), QStringLiteral("-E"),
+ QStringLiteral("-xc"),
+ QStringLiteral("--target=arm-arm-none-eabi"),
+ QStringLiteral("-mcpu=cortex-m0"),
+ QStringLiteral("nul")};
+ QProcess p;
+ p.start(compiler.absoluteFilePath(), args);
+ p.waitForFinished(3000);
+ const auto es = p.exitStatus();
+ if (es != QProcess::NormalExit) {
+ const QByteArray out = p.readAll();
+ qbsWarning() << Tr::tr("Compiler dumping failed:\n%1")
+ .arg(QString::fromUtf8(out));
+ return Version{};
+ }
+ const QByteArray dump = p.readAll();
+ const int verCode = extractVersion(dump, "__ARMCC_VERSION ");
+ if (verCode < 0) {
+ qbsWarning() << Tr::tr("No '__ARMCC_VERSION' token was found "
+ "in the compiler dump:\n%1")
+ .arg(QString::fromUtf8(dump));
+ return Version{};
+ }
+ return Version{verCode / 1000000, (verCode / 10000) % 100, verCode % 10000};
+}
+
+static Version dumpKeilCompilerVersion(const QFileInfo &compiler)
+{
+ const QString arch = guessKeilArchitecture(compiler);
+ if (arch == QLatin1String("mcs51") || arch == QLatin1String("mcs251"))
+ return dumpMcsCompilerVersion(compiler);
+ if (arch == QLatin1String("c166"))
+ return dumpC166CompilerVersion(compiler);
+ if (arch == QLatin1String("arm")) {
+ if (isArmClangCompiler(compiler))
+ return dumpArmClangCompilerVersion(compiler);
+ return dumpArmCCCompilerVersion(compiler);
+ }
return Version{};
}
@@ -200,43 +348,40 @@ static std::vector<ToolchainInstallInfo> installedKeilsFromRegistry()
static const char kRegistryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Keil\\Products";
#endif
- // Dictionary for know toolchains.
- static const struct Entry {
- QString productKey;
- QString subExePath;
- } knowToolchains[] = {
- {QStringLiteral("MDK"), QStringLiteral("\\ARMCC\\bin\\armcc.exe")},
- {QStringLiteral("C51"), QStringLiteral("\\BIN\\c51.exe")},
- };
-
QSettings registry(QLatin1String(kRegistryNode), QSettings::NativeFormat);
const auto productGroups = registry.childGroups();
for (const QString &productKey : productGroups) {
- const auto entryEnd = std::end(knowToolchains);
- const auto entryIt = std::find_if(std::begin(knowToolchains), entryEnd,
- [productKey](const Entry &entry) {
- return entry.productKey == productKey;
- });
- if (entryIt == entryEnd)
- continue;
-
registry.beginGroup(productKey);
- const QString rootPath = registry.value(QStringLiteral("Path"))
- .toString();
- if (!rootPath.isEmpty()) {
- // Build full compiler path.
- const QFileInfo keilPath(rootPath + entryIt->subExePath);
- if (keilPath.exists()) {
- QString version = registry.value(QStringLiteral("Version"))
- .toString();
- if (version.startsWith(QLatin1Char('V')))
- version.remove(0, 1);
- infos.push_back({keilPath, Version::fromString(version)});
- }
+ const QString productPath = registry.value(QStringLiteral("Path"))
+ .toString();
+ QString productVersion = registry.value(QStringLiteral("Version"))
+ .toString();
+ if (productVersion.startsWith(QLatin1Char('V')))
+ productVersion.remove(0, 1);
+
+ if (productKey == QLatin1String("MDK")) {
+ const QFileInfo ccPath(productPath + QStringLiteral("/ARMCC/bin/armcc.exe"));
+ if (ccPath.exists())
+ infos.push_back({ccPath, Version::fromString(productVersion)});
+ const QFileInfo clangPath(productPath + QStringLiteral("/ARMCLANG/bin/armclang.exe"));
+ if (clangPath.exists())
+ infos.push_back({clangPath, Version::fromString(productVersion)});
+ } if (productKey == QLatin1String("C51")) {
+ const QFileInfo cPath(productPath + QStringLiteral("/BIN/c51.exe"));
+ if (cPath.exists())
+ infos.push_back({cPath, Version::fromString(productVersion)});
+ } if (productKey == QLatin1String("C251")) {
+ const QFileInfo cPath(productPath + QStringLiteral("/BIN/c251.exe"));
+ if (cPath.exists())
+ infos.push_back({cPath, Version::fromString(productVersion)});
+ } if (productKey == QLatin1String("C166")) {
+ const QFileInfo cPath(productPath + QStringLiteral("/BIN/c166.exe"));
+ if (cPath.exists())
+ infos.push_back({cPath, Version::fromString(productVersion)});
}
+
registry.endGroup();
}
-
}
std::sort(infos.begin(), infos.end());
@@ -255,7 +400,7 @@ void createKeilProfile(const QFileInfo &compiler, Settings *settings,
QString profileName)
{
const ToolchainInstallInfo info = {compiler, Version{}};
- createKeilProfileHelper(info, settings, profileName);
+ createKeilProfileHelper(info, settings, std::move(profileName));
}
void keilProbe(Settings *settings, std::vector<Profile> &profiles)
@@ -271,10 +416,8 @@ void keilProbe(Settings *settings, std::vector<Profile> &profiles)
pathInfos.cbegin(), pathInfos.cend(),
std::back_inserter(allInfos));
- for (const ToolchainInstallInfo &info : allInfos) {
- const auto profile = createKeilProfileHelper(info, settings);
- profiles.push_back(profile);
- }
+ qbs::Internal::transform(allInfos, profiles, [settings](const auto &info) {
+ return createKeilProfileHelper(info, settings); });
if (allInfos.empty())
qbsInfo() << Tr::tr("No KEIL toolchains found.");
diff --git a/src/app/qbs-setup-toolchains/main.cpp b/src/app/qbs-setup-toolchains/main.cpp
index 87a2a842a..475bcf07b 100644
--- a/src/app/qbs-setup-toolchains/main.cpp
+++ b/src/app/qbs-setup-toolchains/main.cpp
@@ -49,7 +49,6 @@
#include <cstdlib>
#include <iostream>
-using qbs::Internal::Tr;
using qbs::Settings;
static void printUsage(const QString &usageString)
diff --git a/src/app/qbs-setup-toolchains/msvcprobe.cpp b/src/app/qbs-setup-toolchains/msvcprobe.cpp
index 9f2faaa99..84d36753e 100644
--- a/src/app/qbs-setup-toolchains/msvcprobe.cpp
+++ b/src/app/qbs-setup-toolchains/msvcprobe.cpp
@@ -50,19 +50,15 @@
#include <tools/qttools.h>
#include <tools/settings.h>
#include <tools/version.h>
-#include <tools/visualstudioversioninfo.h>
#include <tools/vsenvironmentdetector.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
-#include <QtCore/qjsonarray.h>
-#include <QtCore/qjsondocument.h>
-#include <QtCore/qjsonobject.h>
-#include <QtCore/qprocess.h>
#include <QtCore/qsettings.h>
#include <QtCore/qstringlist.h>
#include <algorithm>
+#include <set>
#include <vector>
using namespace qbs;
@@ -90,67 +86,15 @@ static void setQtHelperProperties(Profile &p, const MSVC *msvc)
static void addMSVCPlatform(Settings *settings, std::vector<Profile> &profiles, QString name, MSVC *msvc)
{
qbsInfo() << Tr::tr("Setting up profile '%1'.").arg(name);
- Profile p(name, settings);
+ Profile p(std::move(name), settings);
p.removeProfile();
p.setValue(QStringLiteral("qbs.targetPlatform"), QStringLiteral("windows"));
- p.setValue(QStringLiteral("qbs.toolchain"), QStringList(QStringLiteral("msvc")));
+ p.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("msvc"));
p.setValue(QStringLiteral("cpp.toolchainInstallPath"), msvc->binPath);
setQtHelperProperties(p, msvc);
profiles.push_back(p);
}
-struct MSVCArchInfo
-{
- QString arch;
- QString binPath;
-};
-
-static std::vector<MSVCArchInfo> findSupportedArchitectures(const MSVC &msvc)
-{
- std::vector<MSVCArchInfo> result;
- auto addResult = [&result](const MSVCArchInfo &ai) {
- if (QFile::exists(ai.binPath + QLatin1String("/cl.exe")))
- result.push_back(ai);
- };
- 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);
- addResult(ai);
- }
- } else {
- QDir vcInstallDir(msvc.vcInstallPath);
- const auto hostArchs = vcInstallDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
- for (const QString &hostArch : hostArchs) {
- QDir subdir = vcInstallDir;
- if (!subdir.cd(hostArch))
- continue;
- const QString shortHostArch = hostArch.mid(4).toLower();
- const auto archs = subdir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
- for (const QString &arch : archs) {
- MSVCArchInfo ai;
- ai.binPath = subdir.absoluteFilePath(arch);
- if (shortHostArch == arch)
- ai.arch = arch;
- else
- ai.arch = shortHostArch + QLatin1Char('_') + arch;
- addResult(ai);
- }
- }
- }
- return result;
-}
-
static QString wow6432Key()
{
#ifdef Q_OS_WIN64
@@ -160,202 +104,6 @@ static QString wow6432Key()
#endif
}
-static QString vswhereFilePath()
-{
- static const std::vector<const char *> envVarCandidates{"ProgramFiles", "ProgramFiles(x86)"};
- for (const char * const envVar : envVarCandidates) {
- const QString value = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv(envVar)));
- const QString cmd = value
- + QStringLiteral("/Microsoft Visual Studio/Installer/vswhere.exe");
- if (QFileInfo(cmd).exists())
- return cmd;
- }
- return {};
-}
-
-enum class ProductType { VisualStudio, BuildTools };
-static std::vector<MSVCInstallInfo> retrieveInstancesFromVSWhere(ProductType productType)
-{
- std::vector<MSVCInstallInfo> result;
- const QString cmd = vswhereFilePath();
- if (cmd.isEmpty())
- return result;
- QProcess vsWhere;
- QStringList args = productType == ProductType::VisualStudio
- ? QStringList({QStringLiteral("-all"), QStringLiteral("-legacy"),
- QStringLiteral("-prerelease")})
- : QStringList({QStringLiteral("-products"),
- QStringLiteral("Microsoft.VisualStudio.Product.BuildTools")});
- args << QStringLiteral("-format") << QStringLiteral("json") << QStringLiteral("-utf8");
- vsWhere.start(cmd, args);
- if (!vsWhere.waitForStarted(-1))
- return result;
- if (!vsWhere.waitForFinished(-1)) {
- qbsWarning() << Tr::tr("The vswhere tool failed to run: %1").arg(vsWhere.errorString());
- return result;
- }
- if (vsWhere.exitCode() != 0) {
- qbsWarning() << Tr::tr("The vswhere tool failed to run: %1")
- .arg(QString::fromLocal8Bit(vsWhere.readAllStandardError()));
- return result;
- }
- QJsonParseError parseError;
- QJsonDocument jsonOutput = QJsonDocument::fromJson(vsWhere.readAllStandardOutput(),
- &parseError);
- if (parseError.error != QJsonParseError::NoError) {
- qbsWarning() << Tr::tr("The vswhere tool produced invalid JSON output: %1")
- .arg(parseError.errorString());
- return result;
- }
- const auto jsonArray = jsonOutput.array();
- for (const QJsonValue &v : jsonArray) {
- const QJsonObject o = v.toObject();
- MSVCInstallInfo info;
- info.version = o.value(QStringLiteral("installationVersion")).toString();
- if (productType == ProductType::BuildTools) {
- // For build tools, the version is e.g. "15.8.28010.2036", rather than "15.0".
- const int dotIndex = info.version.indexOf(QLatin1Char('.'));
- if (dotIndex != -1)
- info.version = info.version.left(dotIndex);
- }
- info.installDir = o.value(QStringLiteral("installationPath")).toString();
- if (!info.version.isEmpty() && !info.installDir.isEmpty())
- result.push_back(info);
- }
- return result;
-}
-
-static std::vector<MSVCInstallInfo> installedMSVCsFromVsWhere()
-{
- const std::vector<MSVCInstallInfo> vsInstallations
- = retrieveInstancesFromVSWhere(ProductType::VisualStudio);
- const std::vector<MSVCInstallInfo> buildToolInstallations
- = retrieveInstancesFromVSWhere(ProductType::BuildTools);
- std::vector<MSVCInstallInfo> all;
- std::copy(vsInstallations.begin(), vsInstallations.end(), std::back_inserter(all));
- std::copy(buildToolInstallations.begin(), buildToolInstallations.end(),
- std::back_inserter(all));
- return all;
-}
-
-static std::vector<MSVCInstallInfo> installedMSVCsFromRegistry()
-{
- std::vector<MSVCInstallInfo> result;
-
- // Detect Visual Studio
- const QSettings vsRegistry(
- QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key()
- + QStringLiteral("\\Microsoft\\VisualStudio\\SxS\\VS7"),
- QSettings::NativeFormat);
- const auto vsNames = vsRegistry.childKeys();
- for (const QString &vsName : vsNames) {
- MSVCInstallInfo entry;
- entry.version = vsName;
- entry.installDir = vsRegistry.value(vsName).toString();
- result.push_back(entry);
- }
-
- // Detect Visual C++ Build Tools
- QSettings vcbtRegistry(
- QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key()
- + QStringLiteral("\\Microsoft\\VisualCppBuildTools"),
- QSettings::NativeFormat);
- const QStringList &vcbtRegistryChildGroups = vcbtRegistry.childGroups();
- for (const QString &childGroup : vcbtRegistryChildGroups) {
- vcbtRegistry.beginGroup(childGroup);
- bool ok;
- int installed = vcbtRegistry.value(QStringLiteral("Installed")).toInt(&ok);
- if (ok && installed) {
- MSVCInstallInfo entry;
- entry.version = childGroup;
- const QSettings vsRegistry(
- QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key()
- + QStringLiteral("\\Microsoft\\VisualStudio\\") + childGroup
- + QStringLiteral("\\Setup\\VC"),
- QSettings::NativeFormat);
- entry.installDir = vsRegistry.value(QStringLiteral("ProductDir")).toString();
- result.push_back(entry);
- }
- vcbtRegistry.endGroup();
- }
-
- return result;
-}
-
-QString MSVCInstallInfo::findVcvarsallBat() const
-{
- static const auto vcvarsall2017 = QStringLiteral("VC/Auxiliary/Build/vcvarsall.bat");
- // 2015, 2013 and 2012
- static const auto vcvarsallOld = QStringLiteral("VC/vcvarsall.bat");
- QDir dir(installDir);
- if (dir.exists(vcvarsall2017))
- return dir.absoluteFilePath(vcvarsall2017);
- if (dir.exists(vcvarsallOld))
- return dir.absoluteFilePath(vcvarsallOld);
- return {};
-}
-
-std::vector<MSVCInstallInfo> installedMSVCs()
-{
- const auto installInfos = installedMSVCsFromVsWhere();
- if (installInfos.empty())
- return installedMSVCsFromRegistry();
- return installInfos;
-}
-
-static std::vector<MSVC> installedCompilers()
-{
- std::vector<MSVC> msvcs;
- std::vector<MSVCInstallInfo> installInfos = installedMSVCsFromVsWhere();
- if (installInfos.empty())
- installInfos = installedMSVCsFromRegistry();
- for (const MSVCInstallInfo &installInfo : installInfos) {
- MSVC msvc;
- msvc.internalVsVersion = Version::fromString(installInfo.version, true);
- if (!msvc.internalVsVersion.isValid())
- continue;
-
- QDir vsInstallDir(installInfo.installDir);
- msvc.vsInstallPath = vsInstallDir.absolutePath();
- if (vsInstallDir.dirName() != QStringLiteral("VC")
- && !vsInstallDir.cd(QStringLiteral("VC"))) {
- continue;
- }
-
- msvc.version = QString::number(Internal::VisualStudioVersionInfo(
- msvc.internalVsVersion).marketingVersion());
- if (msvc.version.isEmpty()) {
- qbsWarning() << Tr::tr("Unknown MSVC version %1 found.").arg(installInfo.version);
- continue;
- }
-
- if (msvc.internalVsVersion.majorVersion() < 15) {
- QDir vcInstallDir = vsInstallDir;
- if (!vcInstallDir.cd(QStringLiteral("bin")))
- continue;
- msvc.vcInstallPath = vcInstallDir.absolutePath();
- msvcs.push_back(msvc);
- } else {
- QDir vcInstallDir = vsInstallDir;
- vcInstallDir.cd(QStringLiteral("Tools/MSVC"));
- const auto vcVersionStrs = vcInstallDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
- for (const QString &vcVersionStr : vcVersionStrs) {
- 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.push_back(msvc);
- }
- }
- }
- return msvcs;
-}
-
void msvcProbe(Settings *settings, std::vector<Profile> &profiles)
{
qbsInfo() << Tr::tr("Detecting MSVC toolchains...");
@@ -381,17 +129,18 @@ void msvcProbe(Settings *settings, std::vector<Profile> &profiles)
sdk.vcInstallPath.chop(1);
if (sdk.isDefault)
defaultWinSDK = sdk;
- const auto ais = findSupportedArchitectures(sdk);
- for (const MSVCArchInfo &ai : ais) {
+ const auto ais = MSVC::findSupportedArchitectures(sdk);
+
+ qbs::Internal::transform(ais, winSDKs, [&sdk](const auto &ai) {
WinSDK specificSDK = sdk;
specificSDK.architecture = ai.arch;
specificSDK.binPath = ai.binPath;
- winSDKs.push_back(specificSDK);
- }
+ return specificSDK;
+ });
}
}
- for (const WinSDK &sdk : qAsConst(winSDKs)) {
+ for (const WinSDK &sdk : std::as_const(winSDKs)) {
qbsInfo() << Tr::tr(" Windows SDK %1 detected:\n"
" installed in %2").arg(sdk.version, sdk.vcInstallPath);
if (sdk.isDefault)
@@ -399,26 +148,9 @@ void msvcProbe(Settings *settings, std::vector<Profile> &profiles)
}
// 2) Installed MSVCs
- std::vector<MSVC> msvcs;
- const auto instMsvcs = installedCompilers();
- for (const MSVC &msvc : instMsvcs) {
- if (msvc.internalVsVersion.majorVersion() < 15) {
- // Check existence of various install scripts
- const QString vcvars32bat = msvc.vcInstallPath + QLatin1String("/vcvars32.bat");
- if (!QFileInfo(vcvars32bat).isFile())
- continue;
- }
-
- const auto ais = findSupportedArchitectures(msvc);
- for (const MSVCArchInfo &ai : ais) {
- MSVC specificMSVC = msvc;
- specificMSVC.architecture = ai.arch;
- specificMSVC.binPath = ai.binPath;
- msvcs.push_back(specificMSVC);
- }
- }
+ std::vector<MSVC> msvcs = MSVC::installedCompilers(ConsoleLogger::instance());
- for (const MSVC &msvc : qAsConst(msvcs)) {
+ for (const MSVC &msvc : std::as_const(msvcs)) {
qbsInfo() << Tr::tr(" MSVC %1 (%2) detected in\n"
" %3").arg(msvc.version, msvc.architecture,
QDir::toNativeSeparators(msvc.binPath));
@@ -432,10 +164,10 @@ void msvcProbe(Settings *settings, std::vector<Profile> &profiles)
qbsInfo() << Tr::tr("Detecting build environment...");
std::vector<MSVC *> msvcPtrs;
- msvcPtrs.resize(winSDKs.size() + msvcs.size());
- std::transform(winSDKs.begin(), winSDKs.end(), msvcPtrs.begin(),
+ msvcPtrs.reserve(winSDKs.size() + msvcs.size());
+ std::transform(winSDKs.begin(), winSDKs.end(), std::back_inserter(msvcPtrs),
[] (WinSDK &sdk) -> MSVC * { return &sdk; });
- std::transform(msvcs.begin(), msvcs.end(), msvcPtrs.begin() + winSDKs.size(),
+ std::transform(msvcs.begin(), msvcs.end(), std::back_inserter(msvcPtrs),
[] (MSVC &msvc) -> MSVC * { return &msvc; });
VsEnvironmentDetector envDetector;
@@ -452,8 +184,20 @@ void msvcProbe(Settings *settings, std::vector<Profile> &profiles)
}
}
+ // we want the same MSVC version share the same suffix in profiles, thus use
+ // a set to know the number of versions processed so far
+ std::map<QString /*VS*/, std::set<QString /*vcInstallPath*/>> msvcCounters;
for (MSVC &msvc : msvcs) {
- const QString name = QLatin1String("MSVC") + msvc.version + QLatin1Char('-')
+ // each VS needs its own counter
+ auto &msvcVersions = msvcCounters[msvc.version];
+ // vcInstallPath is "Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.16.27023/bin"
+ // Since msvcs are sorted by version, when the new vcInstallPath is inserted, we start
+ // a new group of compilers of the same version incrementing the set size
+ msvcVersions.insert(msvc.vcInstallPath);
+ // index is the number of specific vcInstallPaths (e.g. compiler versions) seen so far
+ const size_t index = msvcVersions.size() - 1;
+ const QString suffix = index == 0 ? QString() : QStringLiteral("-%1").arg(index);
+ const QString name = QLatin1String("MSVC") + msvc.version + suffix + QLatin1Char('-')
+ msvc.architecture;
try {
msvc.init();
diff --git a/src/app/qbs-setup-toolchains/msvcprobe.h b/src/app/qbs-setup-toolchains/msvcprobe.h
index 3120a96f2..981bbc561 100644
--- a/src/app/qbs-setup-toolchains/msvcprobe.h
+++ b/src/app/qbs-setup-toolchains/msvcprobe.h
@@ -53,16 +53,6 @@ class Profile;
class Settings;
}
-struct MSVCInstallInfo
-{
- QString version;
- QString installDir;
-
- QString findVcvarsallBat() const;
-};
-
-std::vector<MSVCInstallInfo> installedMSVCs();
-
void createMsvcProfile(const QFileInfo &compiler, qbs::Settings *settings,
const QString &profileName);
diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp
index 513008fd3..ceb95948b 100644
--- a/src/app/qbs-setup-toolchains/probe.cpp
+++ b/src/app/qbs-setup-toolchains/probe.cpp
@@ -39,17 +39,21 @@
#include "probe.h"
#include "clangclprobe.h"
+#include "cosmicprobe.h"
+#include "dmcprobe.h"
#include "gccprobe.h"
#include "iarewprobe.h"
#include "keilprobe.h"
#include "msvcprobe.h"
#include "sdccprobe.h"
+#include "watcomprobe.h"
#include "xcodeprobe.h"
#include <logging/translator.h>
#include <tools/error.h>
#include <tools/hostosinfo.h>
#include <tools/profile.h>
+#include <tools/qttools.h>
#include <tools/settings.h>
#include <tools/toolchains.h>
@@ -96,26 +100,32 @@ QString findExecutable(const QString &fileName)
return {};
}
-QStringList toolchainTypeFromCompilerName(const QString &compilerName)
+QString toolchainTypeFromCompilerName(const QString &compilerName)
{
if (compilerName == QLatin1String("cl.exe"))
- return canonicalToolchain(QStringLiteral("msvc"));
+ return QStringLiteral("msvc");
if (compilerName == QLatin1String("clang-cl.exe"))
- return canonicalToolchain(QLatin1String("clang-cl"));
+ return QStringLiteral("clang-cl");
const auto types = { QStringLiteral("clang"), QStringLiteral("llvm"),
QStringLiteral("mingw"), QStringLiteral("gcc") };
for (const auto &type : types) {
if (compilerName.contains(type))
- return canonicalToolchain(type);
+ return type;
}
if (compilerName == QLatin1String("g++"))
- return canonicalToolchain(QStringLiteral("gcc"));
+ return QStringLiteral("gcc");
if (isIarCompiler(compilerName))
- return canonicalToolchain(QStringLiteral("iar"));
+ return QStringLiteral("iar");
if (isKeilCompiler(compilerName))
- return canonicalToolchain(QStringLiteral("keil"));
+ return QStringLiteral("keil");
if (isSdccCompiler(compilerName))
- return canonicalToolchain(QStringLiteral("sdcc"));
+ return QStringLiteral("sdcc");
+ if (isCosmicCompiler(compilerName))
+ return QStringLiteral("cosmic");
+ if (isDmcCompiler(compilerName))
+ return QStringLiteral("dmc");
+ if (isWatcomCompiler(compilerName))
+ return QStringLiteral("watcom");
return {};
}
@@ -135,12 +145,15 @@ void probe(Settings *settings)
iarProbe(settings, profiles);
keilProbe(settings, profiles);
sdccProbe(settings, profiles);
+ cosmicProbe(settings, profiles);
+ dmcProbe(settings, profiles);
+ watcomProbe(settings, profiles);
if (profiles.empty()) {
- qStderr << Tr::tr("Could not detect any toolchains. No profile created.") << endl;
+ qStderr << Tr::tr("Could not detect any toolchains. No profile created.") << Qt::endl;
} else if (profiles.size() == 1 && settings->defaultProfile().isEmpty()) {
const QString profileName = profiles.front().name();
- qStdout << Tr::tr("Making profile '%1' the default.").arg(profileName) << endl;
+ qStdout << Tr::tr("Making profile '%1' the default.").arg(profileName) << Qt::endl;
settings->setValue(QStringLiteral("defaultProfile"), profileName);
}
}
@@ -157,24 +170,29 @@ void createProfile(const QString &profileName, const QString &toolchainType,
.arg(compilerFilePath));
}
- QStringList toolchainTypes;
- if (toolchainType.isEmpty())
- toolchainTypes = toolchainTypeFromCompilerName(compiler.fileName());
- else
- toolchainTypes = canonicalToolchain(toolchainType);
+ const QString realToolchainType = !toolchainType.isEmpty()
+ ? toolchainType
+ : toolchainTypeFromCompilerName(compiler.fileName());
+ const QStringList toolchain = canonicalToolchain(realToolchainType);
- if (toolchainTypes.contains(QLatin1String("msvc")))
- createMsvcProfile(compiler, settings, profileName);
- else if (toolchainTypes.contains(QLatin1String("clang-cl")))
+ if (toolchain.contains(QLatin1String("clang-cl")))
createClangClProfile(compiler, settings, profileName);
- else if (toolchainTypes.contains(QLatin1String("gcc")))
- createGccProfile(compiler, settings, toolchainTypes, profileName);
- else if (toolchainTypes.contains(QLatin1String("iar")))
+ else if (toolchain.contains(QLatin1String("msvc")))
+ createMsvcProfile(compiler, settings, profileName);
+ else if (toolchain.contains(QLatin1String("gcc")))
+ createGccProfile(compiler, settings, realToolchainType, profileName);
+ else if (toolchain.contains(QLatin1String("iar")))
createIarProfile(compiler, settings, profileName);
- else if (toolchainTypes.contains(QLatin1String("keil")))
+ else if (toolchain.contains(QLatin1String("keil")))
createKeilProfile(compiler, settings, profileName);
- else if (toolchainTypes.contains(QLatin1String("sdcc")))
+ else if (toolchain.contains(QLatin1String("sdcc")))
createSdccProfile(compiler, settings, profileName);
+ else if (toolchain.contains(QLatin1String("cosmic")))
+ createCosmicProfile(compiler, settings, profileName);
+ else if (toolchain.contains(QLatin1String("dmc")))
+ createDmcProfile(compiler, settings, profileName);
+ else if (toolchain.contains(QLatin1String("watcom")))
+ createWatcomProfile(compiler, settings, profileName);
else
throw qbs::ErrorInfo(Tr::tr("Cannot create profile: Unknown toolchain type."));
}
@@ -301,3 +319,21 @@ bool isSameExecutable(const QString &filePath1, const QString &filePath2)
return false;
}
+
+MacrosMap dumpMacros(const std::function<QStringList()> &func)
+{
+ MacrosMap macros;
+ const QStringList lines = func();
+ for (const QString &line : lines) {
+ const QString prefix = QLatin1String("#define ");
+ if (!line.startsWith(prefix))
+ return macros;
+ const auto index = line.indexOf(QLatin1String(" "), prefix.length());
+ if (index != -1) {
+ const auto key = line.mid(prefix.length(), index - prefix.length());
+ const auto value = line.mid(index + 1);
+ macros.insert(key, value);
+ }
+ }
+ return macros;
+}
diff --git a/src/app/qbs-setup-toolchains/probe.h b/src/app/qbs-setup-toolchains/probe.h
index 235d7a899..827171fb2 100644
--- a/src/app/qbs-setup-toolchains/probe.h
+++ b/src/app/qbs-setup-toolchains/probe.h
@@ -43,21 +43,19 @@
#include <tools/version.h>
#include <QtCore/qfileinfo.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <functional> // for std::function
#include <tuple> // for std::tie
-QT_BEGIN_NAMESPACE
-class QString;
-class QStringList;
-QT_END_NAMESPACE
-
namespace qbs { class Settings; }
QStringList systemSearchPaths();
QString findExecutable(const QString &fileName);
-QStringList toolchainTypeFromCompilerName(const QString &compilerName);
+QString toolchainTypeFromCompilerName(const QString &compilerName);
void createProfile(const QString &profileName, const QString &toolchainType,
const QString &compilerFilePath, qbs::Settings *settings);
@@ -81,4 +79,7 @@ int extractVersion(const QByteArray &macroDump, const QByteArray &keyToken);
bool isSameExecutable(const QString &exe1, const QString &exe2);
+using MacrosMap = QMap<QString, QString>;
+MacrosMap dumpMacros(const std::function<QStringList()> &func);
+
#endif // Header guard
diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro
deleted file mode 100644
index 6581bec63..000000000
--- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro
+++ /dev/null
@@ -1,30 +0,0 @@
-include(../app.pri)
-
-TARGET = qbs-setup-toolchains
-
-HEADERS += \
- clangclprobe.h \
- commandlineparser.h \
- gccprobe.h \
- iarewprobe.h \
- keilprobe.h \
- msvcprobe.h \
- probe.h \
- sdccprobe.h \
- xcodeprobe.h \
-
-SOURCES += \
- clangclprobe.cpp \
- commandlineparser.cpp \
- gccprobe.cpp \
- iarewprobe.cpp \
- keilprobe.cpp \
- main.cpp \
- msvcprobe.cpp \
- probe.cpp \
- sdccprobe.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
index 554891fc4..6987f3717 100644
--- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs
+++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs
@@ -1,5 +1,3 @@
-import qbs 1.0
-
QbsApp {
name: "qbs-setup-toolchains"
cpp.dynamicLibraries: qbs.targetOS.contains("windows") ? base.concat("shell32") : base
@@ -8,6 +6,10 @@ QbsApp {
"clangclprobe.h",
"commandlineparser.cpp",
"commandlineparser.h",
+ "cosmicprobe.cpp",
+ "cosmicprobe.h",
+ "dmcprobe.cpp",
+ "dmcprobe.h",
"gccprobe.cpp",
"gccprobe.h",
"iarewprobe.cpp",
@@ -21,12 +23,19 @@ QbsApp {
"probe.h",
"sdccprobe.cpp",
"sdccprobe.h",
+ "watcomprobe.cpp",
+ "watcomprobe.h",
"xcodeprobe.cpp",
"xcodeprobe.h",
]
Group {
name: "MinGW specific files"
condition: qbs.toolchain.contains("mingw")
- files: ["qbs-setup-toolchains.exe.manifest", "qbs-setup-toolchains.rc"]
+ files: "qbs-setup-toolchains.rc"
+ Group {
+ name: "qbs-setup-toolchains manifest"
+ files: "qbs-setup-toolchains.exe.manifest"
+ fileTags: [] // the manifest is referenced by the rc file
+ }
}
}
diff --git a/src/app/qbs-setup-toolchains/sdccprobe.cpp b/src/app/qbs-setup-toolchains/sdccprobe.cpp
index 3899ac3c9..25a3d9751 100644
--- a/src/app/qbs-setup-toolchains/sdccprobe.cpp
+++ b/src/app/qbs-setup-toolchains/sdccprobe.cpp
@@ -48,6 +48,7 @@
#include <tools/profile.h>
#include <QtCore/qprocess.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qsettings.h>
#include <QtCore/qtemporaryfile.h>
@@ -60,8 +61,7 @@ static QStringList knownSdccCompilerNames()
return {QStringLiteral("sdcc")};
}
-static QByteArray dumpSdccMacros(const QFileInfo &compiler,
- const QString &targetFlag = QString())
+static QStringList dumpOutput(const QFileInfo &compiler, const QString &targetFlag = QString())
{
QTemporaryFile fakeIn(QStringLiteral("XXXXXX.c"));
if (!fakeIn.open()) {
@@ -71,103 +71,92 @@ static QByteArray dumpSdccMacros(const QFileInfo &compiler,
}
fakeIn.close();
- const QStringList args = {QStringLiteral("-dM"),
- QStringLiteral("-E"),
- targetFlag,
- fakeIn.fileName()};
+ const QStringList args = {QStringLiteral("-dM"), QStringLiteral("-E"),
+ targetFlag, fakeIn.fileName()};
QProcess p;
p.start(compiler.absoluteFilePath(), args);
p.waitForFinished(3000);
const auto es = p.exitStatus();
if (es != QProcess::NormalExit) {
const QByteArray out = p.readAll();
- qbsWarning() << Tr::tr("Compiler dumping failed:\n%1")
- .arg(QString::fromUtf8(out));
+ qbsWarning() << Tr::tr("Compiler dumping failed:\n%1").arg(QString::fromUtf8(out));
return {};
}
- return p.readAllStandardOutput();
+ static QRegularExpression re(QStringLiteral("\\r?\\n"));
+ return QString::fromUtf8(p.readAllStandardOutput()).split(re);
}
-static QString dumpSdccArchitecture(const QFileInfo &compiler,
- const QString &arch)
+static bool supportsSdccArchitecture(const QFileInfo &compiler, QStringView flag)
{
- const auto targetFlag = QStringLiteral("-m%1").arg(arch);
- const auto token = QStringLiteral("__SDCC_%1").arg(arch);
- const auto macros = dumpSdccMacros(compiler, targetFlag);
- return macros.contains(token.toLatin1()) ? arch : QString();
+ const auto target = QStringLiteral("-m%1").arg(flag);
+ const auto macros = dumpMacros([&compiler, &target]() {
+ return dumpOutput(compiler, target); });
+ const auto token = QStringLiteral("__SDCC_%1").arg(flag);
+ return macros.contains(token);
}
-static std::vector<Profile> createSdccProfileHelper(
- const ToolchainInstallInfo &info,
- Settings *settings,
- const QString &profileName = QString())
+static std::vector<Profile> createSdccProfileHelper(const ToolchainInstallInfo &info,
+ Settings *settings,
+ const QString &profileName = QString())
{
const QFileInfo compiler = info.compilerPath;
-
std::vector<Profile> profiles;
- const char *knownArchs[] = {"mcs51", "stm8"};
+ static constexpr struct KnownArch {
+ QStringView architecture;
+ QStringView flag;
+ } knownArchs[] = {{u"mcs51", u"mcs51"}, {u"stm8", u"stm8"}, {u"hcs8", u"hc08"}};
+
for (const auto &knownArch : knownArchs) {
- const auto actualArch = dumpSdccArchitecture(
- compiler, QString::fromLatin1(knownArch));
// Don't create a profile in case the compiler does
// not support the proposed architecture.
- if (actualArch != QString::fromLatin1(knownArch))
+ if (!supportsSdccArchitecture(compiler, knownArch.flag))
continue;
- QString fullProfileName = profileName;
- if (fullProfileName.isEmpty()) {
+ QString fullProfileName;
+ if (profileName.isEmpty()) {
// Create a full profile name in case we is
// in auto-detecting mode.
if (!info.compilerVersion.isValid()) {
- fullProfileName = QStringLiteral("sdcc-unknown-%1")
- .arg(actualArch);
+ fullProfileName = QStringLiteral("sdcc-unknown-%1").arg(knownArch.architecture);
} else {
const QString version = info.compilerVersion.toString(
QLatin1Char('_'), QLatin1Char('_'));
fullProfileName = QStringLiteral("sdcc-%1-%2").arg(
- version, actualArch);
+ version, knownArch.architecture);
}
} else {
// Append the detected actual architecture name
// to the proposed profile name.
- fullProfileName = QStringLiteral("%1-%2").arg(
- fullProfileName, actualArch);
+ fullProfileName = QStringLiteral("%1-%2").arg(profileName, knownArch.architecture);
}
Profile profile(fullProfileName, settings);
- profile.setValue(QStringLiteral("cpp.toolchainInstallPath"),
- compiler.absolutePath());
- profile.setValue(QStringLiteral("qbs.toolchainType"),
- QStringLiteral("sdcc"));
- if (!actualArch.isEmpty())
- profile.setValue(QStringLiteral("qbs.architecture"), actualArch);
+ profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), compiler.absolutePath());
+ profile.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("sdcc"));
+ profile.setValue(QStringLiteral("qbs.architecture"), knownArch.architecture.toString());
qbsInfo() << Tr::tr("Profile '%1' created for '%2'.").arg(
profile.name(), compiler.absoluteFilePath());
+
+ profiles.push_back(std::move(profile));
}
return profiles;
}
-static Version dumpSdccCompilerVersion(const QFileInfo &compiler)
+static Version dumpSdccVersion(const QFileInfo &compiler)
{
- const QByteArray dump = dumpSdccMacros(compiler);
- if (dump.isEmpty())
- return Version{};
-
- const int major = extractVersion(dump, "__SDCC_VERSION_MAJOR ");
- const int minor = extractVersion(dump, "__SDCC_VERSION_MINOR ");
- const int patch = extractVersion(dump, "__SDCC_VERSION_PATCH ");
- if (major < 0 || minor < 0 || patch < 0) {
- qbsWarning() << Tr::tr("No '__SDCC_VERSION_xxx' token was found "
- "in the compiler dump:\n%1")
- .arg(QString::fromUtf8(dump));
+ const auto macros = dumpMacros([&compiler]() { return dumpOutput(compiler); });
+ if (!macros.contains(QLatin1String("__SDCC"))) {
+ qbsWarning() << Tr::tr("No __SDCC token was found in the compiler dump");
return Version{};
}
- return Version{major, minor, patch};
+ auto value = macros.value(QLatin1String("__SDCC"));
+ value.replace(QLatin1Char('_'), QLatin1Char('.'));
+ return Version::fromString(value);
}
static std::vector<ToolchainInstallInfo> installedSdccsFromPath()
@@ -180,7 +169,7 @@ static std::vector<ToolchainInstallInfo> installedSdccsFromPath()
HostOsInfo::appendExecutableSuffix(compilerName)));
if (!sdccPath.exists())
continue;
- const Version version = dumpSdccCompilerVersion(sdccPath);
+ const Version version = dumpSdccVersion(sdccPath);
infos.push_back({sdccPath, version});
}
std::sort(infos.begin(), infos.end());
@@ -234,8 +223,10 @@ static std::vector<ToolchainInstallInfo> installedSdccsFromRegistry()
return candidate == SdccInstallInfo{
info.compilerPath.filePath(), info.compilerVersion.toString()};
});
- if (infosIt == infosEnd)
- infos.push_back({candidate.compilerPath, Version::fromString(candidate.version)});
+ if (infosIt == infosEnd) {
+ infos.push_back({QFileInfo(candidate.compilerPath),
+ Version::fromString(candidate.version)});
+ }
}
}
@@ -252,7 +243,7 @@ bool isSdccCompiler(const QString &compilerName)
}
void createSdccProfile(const QFileInfo &compiler, Settings *settings,
- QString profileName)
+ const QString &profileName)
{
const ToolchainInstallInfo info = {compiler, Version{}};
createSdccProfileHelper(info, settings, profileName);
@@ -271,13 +262,13 @@ void sdccProbe(Settings *settings, std::vector<Profile> &profiles)
pathInfos.cbegin(), pathInfos.cend(),
std::back_inserter(allInfos));
+ if (allInfos.empty())
+ qbsInfo() << Tr::tr("No SDCC toolchains found.");
+
for (const ToolchainInstallInfo &info : allInfos) {
const auto newProfiles = createSdccProfileHelper(info, settings);
profiles.reserve(profiles.size() + int(newProfiles.size()));
std::copy(newProfiles.cbegin(), newProfiles.cend(),
std::back_inserter(profiles));
}
-
- if (allInfos.empty())
- qbsInfo() << Tr::tr("No SDCC toolchains found.");
}
diff --git a/src/app/qbs-setup-toolchains/sdccprobe.h b/src/app/qbs-setup-toolchains/sdccprobe.h
index 3d26583d9..4c913ddeb 100644
--- a/src/app/qbs-setup-toolchains/sdccprobe.h
+++ b/src/app/qbs-setup-toolchains/sdccprobe.h
@@ -68,7 +68,7 @@ inline bool operator==(const SdccInstallInfo &lhs, const SdccInstallInfo &rhs)
bool isSdccCompiler(const QString &compilerName);
void createSdccProfile(const QFileInfo &compiler, qbs::Settings *settings,
- QString profileName);
+ const QString &profileName);
void sdccProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles);
diff --git a/src/app/qbs-setup-toolchains/watcomprobe.cpp b/src/app/qbs-setup-toolchains/watcomprobe.cpp
new file mode 100644
index 000000000..cc9fee2b4
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/watcomprobe.cpp
@@ -0,0 +1,255 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "watcomprobe.h"
+#include "probe.h"
+
+#include "../shared/logging/consolelogger.h"
+
+#include <logging/translator.h>
+
+#include <tools/hostosinfo.h>
+#include <tools/profile.h>
+
+#include <QtCore/qdir.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qprocess.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qsettings.h>
+#include <QtCore/qtemporaryfile.h>
+
+using namespace qbs;
+using Internal::HostOsInfo;
+using Internal::Tr;
+
+namespace {
+
+struct Details {
+ QStringView architecture;
+ QStringView platform;
+};
+
+constexpr struct Platform {
+ QStringView flag;
+ Details keys;
+ Details target;
+} knownPlatforms[] = {
+ // DOS 16/32 bit.
+ {u"-bdos", {u"__I86__", u"__DOS__"}, {u"x86_16", u"dos"}},
+ {u"-bdos4g", {u"__386__", u"__DOS__"}, {u"x86", u"dos"}},
+ // Windows 16/32 bit.
+ {u"-bwindows", {u"__I86__", u"__WINDOWS__"}, {u"x86_16", u"windows"}},
+ {u"-bnt", {u"__386__", u"__NT__"}, {u"x86", u"windows"}},
+ // OS/2 16/32 bit.
+ {u"-bos2", {u"__I86__", u"__OS2__"}, {u"x86_16", u"os2"}},
+ {u"-bos2v2", {u"__386__", u"__OS2__"}, {u"x86", u"os2"}},
+ // Linux 32 bit.
+ {u"-blinux", {u"__386__", u"__LINUX__"}, {u"x86", u"linux"}},
+};
+
+} // namespace
+
+static QStringList knownWatcomCompilerNames()
+{
+ return {QStringLiteral("owcc")};
+}
+
+static QStringList dumpOutput(const QFileInfo &compiler, QStringView flag,
+ const QList<QStringView> &keys)
+{
+ const QString filePath = QDir(QDir::tempPath()).absoluteFilePath(
+ QLatin1String("watcom-dump.c"));
+ QFile fakeIn(filePath);
+ if (!fakeIn.open(QIODevice::Truncate | QIODevice::WriteOnly | QIODevice::Text)) {
+ qbsWarning() << Tr::tr("Unable to open temporary file %1 for output: %2")
+ .arg(fakeIn.fileName(), fakeIn.errorString());
+ return {};
+ }
+ fakeIn.write("#define VALUE_TO_STRING(x) #x\n");
+ fakeIn.write("#define VALUE(x) VALUE_TO_STRING(x)\n");
+ fakeIn.write("#define VAR_NAME_VALUE(var) \"#define \" #var\" \"VALUE(var)\n");
+ for (const auto &key : keys) {
+ fakeIn.write("#if defined(" + key.toLatin1() + ")\n");
+ fakeIn.write("#pragma message (VAR_NAME_VALUE(" + key.toLatin1() + "))\n");
+ fakeIn.write("#endif\n");
+ }
+ fakeIn.close();
+ QProcess p;
+ QStringList args;
+ if (!flag.isEmpty())
+ args.push_back(flag.toString());
+ args.push_back(QDir::toNativeSeparators(filePath));
+ p.start(compiler.absoluteFilePath(), args);
+ p.waitForFinished(3000);
+ fakeIn.remove();
+ QStringList lines = QString::fromUtf8(p.readAllStandardOutput())
+ .split(QRegularExpression(QLatin1String("\\r?\\n")));
+ return lines;
+}
+
+static bool supportsWatcomPlatform(const QFileInfo &compiler, const Platform &platform)
+{
+ const auto macros = dumpMacros([&compiler, &platform]() {
+ const QList<QStringView> keys = {platform.keys.architecture, platform.keys.platform};
+ return dumpOutput(compiler, platform.flag, keys); });
+
+ auto matches = [&macros](QStringView key) {
+ const auto k = key.toString();
+ if (!macros.contains(k))
+ return false;
+ return macros.value(k) == QLatin1String("1");
+ };
+
+ return matches(platform.keys.architecture) && matches(platform.keys.platform);
+}
+
+static std::vector<Profile> createWatcomProfileHelper(const ToolchainInstallInfo &info,
+ Settings *settings,
+ QStringView profileName = {})
+{
+ const QFileInfo compiler = info.compilerPath;
+ std::vector<Profile> profiles;
+
+ for (const auto &knownPlatform : knownPlatforms) {
+ // Don't create a profile in case the compiler does
+ // not support the proposed architecture.
+ if (!supportsWatcomPlatform(compiler, knownPlatform))
+ continue;
+
+ QString fullProfilename;
+ if (profileName.isEmpty()) {
+ // Create a full profile name in case we is in auto-detecting mode.
+ if (!info.compilerVersion.isValid()) {
+ fullProfilename = QStringLiteral("watcom-unknown-%1-%2")
+ .arg(knownPlatform.target.platform)
+ .arg(knownPlatform.target.architecture);
+ } else {
+ const QString version= info.compilerVersion.toString(QLatin1Char('_'),
+ QLatin1Char('_'));
+ fullProfilename = QStringLiteral("watcom-%1-%2-%3")
+ .arg(version)
+ .arg(knownPlatform.target.platform)
+ .arg(knownPlatform.target.architecture);
+ }
+ } else {
+ // Append the detected actual architecture name to the proposed profile name.
+ fullProfilename = QStringLiteral("%1-%2-%3")
+ .arg(profileName)
+ .arg(knownPlatform.target.platform)
+ .arg(knownPlatform.target.architecture);
+ }
+
+ Profile profile(fullProfilename, settings);
+ profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), compiler.absolutePath());
+ profile.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("watcom"));
+ profile.setValue(QStringLiteral("qbs.architecture"),
+ knownPlatform.target.architecture.toString());
+ profile.setValue(QStringLiteral("qbs.targetPlatform"),
+ knownPlatform.target.platform.toString());
+
+ qbsInfo() << Tr::tr("Profile '%1' created for '%2'.")
+ .arg(profile.name(), compiler.absoluteFilePath());
+
+ profiles.push_back(std::move(profile));
+ }
+
+ return profiles;
+}
+
+static Version dumpWatcomVersion(const QFileInfo &compiler)
+{
+ const QList<QStringView> keys = {u"__WATCOMC__", u"__WATCOM_CPLUSPLUS__"};
+ const auto macros = dumpMacros([&compiler, &keys]() {
+ return dumpOutput(compiler, u"", keys); });
+ for (const auto &macro : macros) {
+ const int verCode = macro.toInt();
+ return Version{(verCode - 1100) / 100,
+ (verCode / 10) % 10,
+ ((verCode % 10) > 0) ? (verCode % 10) : 0};
+ }
+ qbsWarning() << Tr::tr("No __WATCOMC__ or __WATCOM_CPLUSPLUS__ tokens was found"
+ " in the compiler dump");
+ return Version{};
+}
+
+static std::vector<ToolchainInstallInfo> installedWatcomsFromPath()
+{
+ std::vector<ToolchainInstallInfo> infos;
+ const auto compilerNames = knownWatcomCompilerNames();
+ for (const QString &compilerName : compilerNames) {
+ const QFileInfo watcomPath(findExecutable(
+ HostOsInfo::appendExecutableSuffix(compilerName)));
+ if (!watcomPath.exists())
+ continue;
+ const Version version = dumpWatcomVersion(watcomPath);
+ infos.push_back({watcomPath, version});
+ }
+ std::sort(infos.begin(), infos.end());
+ return infos;
+}
+
+bool isWatcomCompiler(const QString &compilerName)
+{
+ return Internal::any_of(knownWatcomCompilerNames(), [compilerName](const QString &knownName) {
+ return compilerName.contains(knownName);
+ });
+}
+
+void createWatcomProfile(const QFileInfo &compiler, Settings *settings, QStringView profileName)
+{
+ const ToolchainInstallInfo info = {compiler, Version{}};
+ createWatcomProfileHelper(info, settings, profileName);
+}
+
+void watcomProbe(Settings *settings, std::vector<Profile> &profiles)
+{
+ qbsInfo() << Tr::tr("Trying to detect WATCOM toolchains...");
+
+ const std::vector<ToolchainInstallInfo> allInfos = installedWatcomsFromPath();
+ if (allInfos.empty()) {
+ qbsInfo() << Tr::tr("No WATCOM toolchains found.");
+ return;
+ }
+
+ for (const ToolchainInstallInfo &info : allInfos) {
+ const auto newProfiles = createWatcomProfileHelper(info, settings);
+ profiles.reserve(profiles.size() + int(newProfiles.size()));
+ std::copy(newProfiles.cbegin(), newProfiles.cend(), std::back_inserter(profiles));
+ }
+}
diff --git a/src/app/qbs-setup-toolchains/watcomprobe.h b/src/app/qbs-setup-toolchains/watcomprobe.h
new file mode 100644
index 000000000..ea010461d
--- /dev/null
+++ b/src/app/qbs-setup-toolchains/watcomprobe.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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_WATCOMPROBE_H
+#define QBS_SETUPTOOLCHAINS_WATCOMPROBE_H
+
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+class QFileInfo;
+QT_END_NAMESPACE
+
+namespace qbs {
+class Profile;
+class Settings;
+} // namespace qbs
+
+bool isWatcomCompiler(const QString &compilerName);
+void createWatcomProfile(
+ const QFileInfo &compiler, qbs::Settings *settings, QStringView profileName);
+void watcomProbe(qbs::Settings *settings, std::vector<qbs::Profile> &profiles);
+
+#endif // Header guard
diff --git a/src/app/qbs-setup-toolchains/xcodeprobe.cpp b/src/app/qbs-setup-toolchains/xcodeprobe.cpp
index 3ac243028..ea27e4143 100644
--- a/src/app/qbs-setup-toolchains/xcodeprobe.cpp
+++ b/src/app/qbs-setup-toolchains/xcodeprobe.cpp
@@ -45,6 +45,7 @@
#include <logging/translator.h>
#include <tools/architectures.h>
#include <tools/profile.h>
+#include <tools/qttools.h>
#include <tools/settings.h>
#include <QtCore/qstringlist.h>
@@ -62,7 +63,7 @@ using Internal::Tr;
static const QString defaultDeveloperPath =
QStringLiteral("/Applications/Xcode.app/Contents/Developer");
static const std::regex defaultDeveloperPathRegex(
- "^/Applications/Xcode([a-zA-Z0-9 _-]+)\\.app/Contents/Developer$");
+ "^/Applications/Xcode([a-zA-Z0-9 ._-]+)\\.app/Contents/Developer$");
static QString targetOS(const QString &applePlatformName)
{
@@ -94,6 +95,8 @@ static QStringList archList(const QString &applePlatformName)
archs << QStringLiteral("x86");
if (applePlatformName != QStringLiteral("watchsimulator"))
archs << QStringLiteral("x86_64");
+ if (applePlatformName == QStringLiteral("macosx"))
+ archs << QStringLiteral("arm64");
} else if (applePlatformName == QStringLiteral("iphoneos")
|| applePlatformName == QStringLiteral("appletvos")) {
if (applePlatformName != QStringLiteral("appletvos"))
@@ -147,7 +150,7 @@ void XcodeProbe::detectDeveloperPaths()
if (!selectedXcode.waitForFinished(-1) || selectedXcode.exitCode()) {
qbsInfo() << Tr::tr("Could not detect selected Xcode with /usr/bin/xcode-select");
} else {
- QString path = QString::fromLocal8Bit(selectedXcode.readAllStandardOutput());
+ QString path = QString::fromLocal8Bit(selectedXcode.readAllStandardOutput().trimmed());
addDeveloperPath(path);
}
addDeveloperPath(defaultDeveloperPath);
@@ -160,7 +163,7 @@ void XcodeProbe::detectDeveloperPaths()
qbsInfo() << Tr::tr("Could not detect additional Xcode installations with /usr/bin/mdfind");
} else {
const auto paths = QString::fromLocal8Bit(launchServices.readAllStandardOutput())
- .split(QLatin1Char('\n'), QString::SkipEmptyParts);
+ .split(QLatin1Char('\n'), Qt::SkipEmptyParts);
for (const QString &path : paths)
addDeveloperPath(path + QStringLiteral("/Contents/Developer"));
}
@@ -168,15 +171,11 @@ void XcodeProbe::detectDeveloperPaths()
void XcodeProbe::setupDefaultToolchains(const QString &devPath, const QString &xcodeName)
{
- qbsInfo() << Tr::tr("Profile '%1' created for '%2'.").arg(xcodeName).arg(devPath);
+ qbsInfo() << Tr::tr("Profile '%1' created for '%2'.").arg(xcodeName, devPath);
Profile installationProfile(xcodeName, settings);
installationProfile.removeProfile();
- installationProfile.setValue(QStringLiteral("qbs.toolchain"), QStringList()
- << QStringLiteral("xcode")
- << QStringLiteral("clang")
- << QStringLiteral("llvm")
- << QStringLiteral("gcc"));
+ installationProfile.setValue(QStringLiteral("qbs.toolchainType"), QStringLiteral("xcode"));
if (devPath != defaultDeveloperPath)
installationProfile.setValue(QStringLiteral("xcode.developerPath"), devPath);
profiles.push_back(installationProfile);
@@ -189,7 +188,7 @@ void XcodeProbe::setupDefaultToolchains(const QString &devPath, const QString &x
<< QStringLiteral("appletvsimulator")
<< QStringLiteral("watchos")
<< QStringLiteral("watchsimulator");
- for (const QString &platform : qAsConst(platforms)) {
+ for (const QString &platform : std::as_const(platforms)) {
Profile platformProfile(xcodeName + QLatin1Char('-') + platform, settings);
platformProfile.removeProfile();
platformProfile.setBaseProfile(installationProfile.name());
@@ -213,14 +212,15 @@ void XcodeProbe::detectAll()
{
int i = 1;
detectDeveloperPaths();
- for (const QString &developerPath : qAsConst(developerPaths)) {
+ for (const QString &developerPath : std::as_const(developerPaths)) {
QString profileName = QStringLiteral("xcode");
if (developerPath != defaultDeveloperPath) {
const auto devPath = developerPath.toStdString();
std::smatch match;
if (std::regex_match(devPath, match, defaultDeveloperPathRegex))
- profileName += QString::fromStdString(match[1]).toLower().replace(QLatin1Char(' '),
- QLatin1Char('-'));
+ profileName += QString::fromStdString(match[1]).toLower().
+ replace(QLatin1Char(' '), QLatin1Char('-')).
+ replace(QLatin1Char('.'), QLatin1Char('_'));
else
profileName += QString::number(i++);
}
diff --git a/src/app/qbs/CMakeLists.txt b/src/app/qbs/CMakeLists.txt
new file mode 100644
index 000000000..cd9240f98
--- /dev/null
+++ b/src/app/qbs/CMakeLists.txt
@@ -0,0 +1,56 @@
+set(SOURCES
+ application.cpp
+ application.h
+ commandlinefrontend.cpp
+ commandlinefrontend.h
+ consoleprogressobserver.cpp
+ consoleprogressobserver.h
+ ctrlchandler.cpp
+ ctrlchandler.h
+ lspserver.cpp
+ lspserver.h
+ main.cpp
+ qbstool.cpp
+ qbstool.h
+ session.cpp
+ session.h
+ sessionpacket.cpp
+ sessionpacket.h
+ sessionpacketreader.cpp
+ sessionpacketreader.h
+ status.cpp
+ status.h
+ stdinreader.cpp
+ stdinreader.h
+ )
+
+set(PARSER_SOURCES
+ commandlineoption.cpp
+ commandlineoption.h
+ commandlineoptionpool.cpp
+ commandlineoptionpool.h
+ commandlineparser.cpp
+ commandlineparser.h
+ commandpool.cpp
+ commandpool.h
+ commandtype.h
+ parsercommand.cpp
+ parsercommand.h
+ )
+list_transform_prepend(PARSER_SOURCES parser/)
+
+add_qbs_app(qbs
+ DEFINES
+ "QBS_VERSION=\"${QBS_VERSION}\""
+ "QBS_RELATIVE_LIBEXEC_PATH=\"${QBS_RELATIVE_LIBEXEC_PATH}\""
+ "QBS_RELATIVE_SEARCH_PATH=\"${QBS_RELATIVE_SEARCH_PATH}\""
+ "QBS_RELATIVE_PLUGINS_PATH=\"${QBS_RELATIVE_PLUGINS_PATH}\""
+ DEPENDS
+ qbscore
+ qbsconsolelogger
+ qtclsp
+ Qt${QT_VERSION_MAJOR}::Network
+ SOURCES ${SOURCES} ${PARSER_SOURCES}
+ )
+
+add_dependencies(qbs qbs_cpp_scanner qbs_qt_scanner)
diff --git a/src/app/qbs/commandlinefrontend.cpp b/src/app/qbs/commandlinefrontend.cpp
index f204602a6..4a4781207 100644
--- a/src/app/qbs/commandlinefrontend.cpp
+++ b/src/app/qbs/commandlinefrontend.cpp
@@ -78,6 +78,7 @@ CommandLineFrontend::CommandLineFrontend(const CommandLineParser &parser, Settin
CommandLineFrontend::~CommandLineFrontend()
{
m_cancelTimer->stop();
+ delete m_observer;
}
// Called from interrupt handler. Don't do anything non-trivial here.
@@ -96,9 +97,9 @@ void CommandLineFrontend::checkCancelStatus()
m_cancelTimer->stop();
if (m_resolveJobs.empty() && m_buildJobs.empty())
std::exit(EXIT_FAILURE);
- for (AbstractJob * const job : qAsConst(m_resolveJobs))
+ for (AbstractJob * const job : std::as_const(m_resolveJobs))
job->cancel();
- for (AbstractJob * const job : qAsConst(m_buildJobs))
+ for (AbstractJob * const job : std::as_const(m_buildJobs))
job->cancel();
break;
case CancelStatusCanceling:
@@ -139,7 +140,7 @@ void CommandLineFrontend::start()
}
if (m_parser.showVersion()) {
- puts(QBS_VERSION);
+ std::puts(QBS_VERSION);
qApp->exit(EXIT_SUCCESS);
return;
}
@@ -151,11 +152,11 @@ void CommandLineFrontend::start()
params.setDryRun(m_parser.dryRun());
params.setForceProbeExecution(m_parser.forceProbesExecution());
params.setWaitLockBuildGraph(m_parser.waitLockBuildGraph());
- params.setFallbackProviderEnabled(!m_parser.disableFallbackProvider());
params.setLogElapsedTime(m_parser.logTime());
params.setSettingsDirectory(m_settings->baseDirectory());
params.setOverrideBuildGraphData(m_parser.command() == ResolveCommandType);
params.setPropertyCheckingMode(ErrorHandlingMode::Strict);
+ params.setDeprecationWarningMode(m_parser.deprecationWarningMode());
if (!m_parser.buildBeforeInstalling() || !m_parser.commandCanResolve())
params.setRestoreBehavior(SetupProjectParameters::RestoreOnly);
const auto buildConfigs = m_parser.buildConfigurations();
@@ -182,6 +183,7 @@ void CommandLineFrontend::start()
params.setConfigurationName(configurationName);
params.setBuildRoot(buildDirectory(profileName));
params.setOverriddenValues(userConfig);
+ params.setMaxJobCount(m_parser.jobCount(profileName));
SetupProjectJob * const job = Project().setupProject(params,
ConsoleLogger::instance().logSink(), this);
connectJob(job);
@@ -347,13 +349,12 @@ CommandLineFrontend::ProductMap CommandLineFrontend::productsToUse() const
ProductMap products;
QStringList productNames;
const bool useAll = m_parser.products().empty();
- for (const Project &project : qAsConst(m_projects)) {
+ for (const Project &project : std::as_const(m_projects)) {
QList<ProductData> &productList = products[project];
const ProjectData projectData = project.projectData();
- const auto products = projectData.allProducts();
- for (const ProductData &product : products) {
- productNames << product.name();
- if (useAll || m_parser.products().contains(product.name())) {
+ for (const ProductData &product : projectData.allProducts()) {
+ productNames << product.fullDisplayName();
+ if (useAll || m_parser.products().contains(product.fullDisplayName())) {
productList.push_back(product);
}
}
@@ -431,7 +432,7 @@ void CommandLineFrontend::handleProjectsResolved()
void CommandLineFrontend::makeClean()
{
if (m_parser.products().empty()) {
- for (const Project &project : qAsConst(m_projects)) {
+ for (const Project &project : std::as_const(m_projects)) {
m_buildJobs << project.cleanAllProducts(m_parser.cleanOptions(project.profile()), this);
}
} else {
@@ -503,7 +504,7 @@ void CommandLineFrontend::build()
if (m_parser.products().empty()) {
const Project::ProductSelection productSelection = m_parser.withNonDefaultProducts()
? Project::ProductSelectionWithNonDefault : Project::ProductSelectionDefaultOnly;
- for (const Project &project : qAsConst(m_projects))
+ for (const Project &project : std::as_const(m_projects))
m_buildJobs << project.buildAllProducts(buildOptions(project), productSelection, this);
} else {
const ProductMap &products = productsToUse();
@@ -564,7 +565,7 @@ int CommandLineFrontend::runTarget()
const QString executableFilePath = productToRun.targetExecutable();
if (executableFilePath.isEmpty()) {
throw ErrorInfo(Tr::tr("Cannot run: Product '%1' is not an application.")
- .arg(productToRun.name()));
+ .arg(productToRun.fullDisplayName()));
}
RunEnvironment runEnvironment = m_projects.front().getRunEnvironment(productToRun,
m_parser.installOptions(m_projects.front().profile()),
@@ -609,7 +610,7 @@ void CommandLineFrontend::listProducts()
void CommandLineFrontend::connectBuildJobs()
{
- for (AbstractJob * const job : qAsConst(m_buildJobs))
+ for (AbstractJob * const job : std::as_const(m_buildJobs))
connectBuildJob(job);
}
@@ -646,9 +647,8 @@ ProductData CommandLineFrontend::getTheOneRunnableProduct()
QBS_CHECK(m_projects.size() == 1); // Has been checked earlier.
if (m_parser.products().size() == 1) {
- const auto products = m_projects.front().projectData().allProducts();
- for (const ProductData &p : products) {
- if (p.name() == m_parser.products().constFirst())
+ for (const ProductData &p : m_projects.front().projectData().allProducts()) {
+ if (p.fullDisplayName() == m_parser.products().constFirst())
return p;
}
QBS_CHECK(false);
@@ -656,8 +656,7 @@ ProductData CommandLineFrontend::getTheOneRunnableProduct()
QBS_CHECK(m_parser.products().isEmpty());
QList<ProductData> runnableProducts;
- const auto products = m_projects.front().projectData().allProducts();
- for (const ProductData &p : products) {
+ for (const ProductData &p : m_projects.front().projectData().allProducts()) {
if (p.isRunnable())
runnableProducts.push_back(p);
}
@@ -673,14 +672,8 @@ ProductData CommandLineFrontend::getTheOneRunnableProduct()
ErrorInfo error(Tr::tr("Ambiguous use of command '%1': No product given, but project "
"has more than one runnable product.").arg(m_parser.commandName()));
error.append(Tr::tr("Use the '--products' option with one of the following products:"));
- for (const ProductData &p : qAsConst(runnableProducts)) {
- QString productRepr = QLatin1String("\t") + p.name();
- if (p.profile() != m_projects.front().profile()) {
- productRepr.append(QLatin1String(" [")).append(p.profile())
- .append(QLatin1Char(']'));
- }
- error.append(productRepr);
- }
+ for (const ProductData &p : std::as_const(runnableProducts))
+ error.append(QLatin1String("\t") + p.fullDisplayName());
throw error;
}
diff --git a/src/app/qbs/lspserver.cpp b/src/app/qbs/lspserver.cpp
new file mode 100644
index 000000000..c6cce6706
--- /dev/null
+++ b/src/app/qbs/lspserver.cpp
@@ -0,0 +1,629 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "lspserver.h"
+
+#include <api/projectdata.h>
+#include <logging/translator.h>
+#include <lsp/basemessage.h>
+#include <lsp/completion.h>
+#include <lsp/initializemessages.h>
+#include <lsp/jsonrpcmessages.h>
+#include <lsp/messages.h>
+#include <lsp/servercapabilities.h>
+#include <lsp/textsynchronization.h>
+#include <parser/qmljsastvisitor_p.h>
+#include <parser/qmljslexer_p.h>
+#include <parser/qmljsparser_p.h>
+#include <tools/qbsassert.h>
+#include <tools/stlutils.h>
+
+#include <QBuffer>
+#include <QLocalServer>
+#include <QLocalSocket>
+#include <QMap>
+
+#include <unordered_map>
+#ifdef Q_OS_WINDOWS
+#include <process.h>
+#else
+#include <unistd.h>
+#endif
+
+namespace qbs::Internal {
+
+static int getPid()
+{
+#ifdef Q_OS_WINDOWS
+ return _getpid();
+#else
+ return getpid();
+#endif
+}
+
+using LspErrorResponse = lsp::ResponseError<std::nullptr_t>;
+using LspErrorCode = LspErrorResponse::ErrorCodes;
+
+class Document {
+public:
+ bool isPositionUpToDate(const CodePosition &pos) const;
+ bool isPositionUpToDate(const lsp::Position &pos) const;
+
+ QString savedContent;
+ QString currentContent;
+};
+
+static CodePosition posFromLspPos(const lsp::Position &pos)
+{
+ return {pos.line() + 1, pos.character() + 1};
+}
+
+static lsp::Position lspPosFromCodeLocation(const CodeLocation &loc)
+{
+ return {loc.line() - 1, loc.column() - 1};
+}
+
+static QString uriToString(const lsp::DocumentUri &uri)
+{
+ return uri.toFilePath([](const lsp::Utils::FilePath &fp) { return fp; });
+}
+
+static int posToOffset(const CodePosition &pos, const QString &doc);
+static int posToOffset(const lsp::Position &pos, const QString &doc)
+{
+ return posToOffset(posFromLspPos(pos), doc);
+}
+
+class AstNodeLocator : public QbsQmlJS::AST::Visitor
+{
+public:
+ AstNodeLocator(int position, QbsQmlJS::AST::UiProgram &ast)
+ : m_position(position)
+ {
+ ast.accept(this);
+ }
+
+ QList<QbsQmlJS::AST::Node *> path() const { return m_path; }
+
+private:
+ bool preVisit(QbsQmlJS::AST::Node *node) override
+ {
+ if (int(node->firstSourceLocation().offset) > m_position)
+ return false;
+ if (int(node->lastSourceLocation().offset) < m_position)
+ return false;
+ m_path << node;
+ return true;
+ }
+
+ const int m_position;
+ QList<QbsQmlJS::AST::Node *> m_path;
+};
+
+class LspServer::Private
+{
+public:
+ void setupConnection();
+ void handleIncomingData();
+ void discardSocket();
+ template<typename T> void sendResponse(const T &msg);
+ void sendErrorResponse(LspErrorCode code, const QString &message);
+ void sendErrorNotification(const QString &message);
+ void sendNoSuchDocumentError(const QString &filePath);
+ void sendMessage(const lsp::JsonObject &msg);
+ void sendMessage(const lsp::JsonRpcMessage &msg);
+ void handleCurrentMessage();
+ void handleInitializeRequest();
+ void handleInitializedNotification();
+ void handleGotoDefinitionRequest();
+ void handleCompletionRequest();
+ void handleShutdownRequest();
+ void handleDidOpenNotification();
+ void handleDidChangeNotification();
+ void handleDidSaveNotification();
+ void handleDidCloseNotification();
+
+ QLocalServer server;
+ QBuffer incomingData;
+ lsp::BaseMessage currentMessage;
+ QJsonObject messageObject;
+ QLocalSocket *socket = nullptr;
+ ProjectData projectData;
+ CodeLinks codeLinks;
+ std::unordered_map<QString, Document> documents;
+
+ enum class State { None, InitRequest, InitNotification, Shutdown };
+ State state = State::None;
+};
+
+LspServer::LspServer() : d(new Private)
+{
+ if (!d->server.listen(QString::fromLatin1("qbs-lsp-%1").arg(getPid()))) {
+ // This is not fatal, qbs main operation can continue.
+ qWarning() << "failed to open lsp socket:" << d->server.errorString();
+ return;
+ }
+ QObject::connect(&d->server, &QLocalServer::newConnection, [this] {
+ d->socket = d->server.nextPendingConnection();
+ d->setupConnection();
+ d->server.close();
+ });
+}
+
+LspServer::~LspServer() { delete d; }
+
+void LspServer::updateProjectData(const ProjectData &projectData, const CodeLinks &codeLinks)
+{
+ d->projectData = projectData;
+ d->codeLinks = codeLinks;
+}
+
+QString LspServer::socketPath() const { return d->server.fullServerName(); }
+
+void LspServer::Private::setupConnection()
+{
+ QBS_ASSERT(socket, return);
+
+ QObject::connect(socket, &QLocalSocket::errorOccurred, [this] { discardSocket(); });
+ QObject::connect(socket, &QLocalSocket::disconnected, [this] { discardSocket(); });
+ QObject::connect(socket, &QLocalSocket::readyRead, [this] { handleIncomingData(); });
+ incomingData.open(QIODevice::ReadWrite | QIODevice::Append);
+ handleIncomingData();
+}
+
+void LspServer::Private::handleIncomingData()
+{
+ const int pos = incomingData.pos();
+ incomingData.write(socket->readAll());
+ incomingData.seek(pos);
+ QString parseError;
+ lsp::BaseMessage::parse(&incomingData, parseError, currentMessage);
+ if (!parseError.isEmpty())
+ return sendErrorResponse(LspErrorResponse::ParseError, parseError);
+ if (currentMessage.isComplete()) {
+ incomingData.buffer().remove(0, incomingData.pos());
+ incomingData.seek(0);
+ handleCurrentMessage();
+ currentMessage = {};
+ messageObject = {};
+ if (socket)
+ handleIncomingData();
+ }
+}
+
+void LspServer::Private::discardSocket()
+{
+ socket->disconnect();
+ socket->deleteLater();
+ socket = nullptr;
+}
+
+template<typename T> void LspServer::Private::sendResponse(const T &msg)
+{
+ lsp::Response<T, std::nullptr_t> response(lsp::MessageId(messageObject.value(lsp::idKey)));
+ response.setResult(msg);
+ sendMessage(response);
+}
+
+void LspServer::Private::sendErrorResponse(LspErrorCode code, const QString &message)
+{
+ lsp::Response<lsp::JsonObject, std::nullptr_t> response(lsp::MessageId(
+ messageObject.value(lsp::idKey)));
+ lsp::ResponseError<std::nullptr_t> error;
+ error.setCode(code);
+ error.setMessage(message);
+ response.setError(error);
+ sendMessage(response);
+}
+
+void LspServer::Private::sendErrorNotification(const QString &message)
+{
+ lsp::ShowMessageParams params;
+ params.setType(lsp::Error);
+ params.setMessage(message);
+ sendMessage(lsp::ShowMessageNotification(params));
+}
+
+void LspServer::Private::sendNoSuchDocumentError(const QString &filePath)
+{
+ sendErrorNotification(Tr::tr("No such document: '%1'").arg(filePath));
+}
+
+void LspServer::Private::sendMessage(const lsp::JsonObject &msg)
+{
+ sendMessage(lsp::JsonRpcMessage(msg));
+}
+
+void LspServer::Private::sendMessage(const lsp::JsonRpcMessage &msg)
+{
+ lsp::BaseMessage baseMsg = msg.toBaseMessage();
+ socket->write(baseMsg.header());
+ socket->write(baseMsg.content);
+}
+
+void LspServer::Private::handleCurrentMessage()
+{
+ messageObject = lsp::JsonRpcMessage(currentMessage).toJsonObject();
+ const QString method = messageObject.value(lsp::methodKey).toString();
+ if (method == QLatin1String("exit"))
+ return discardSocket();
+ if (state == State::Shutdown) {
+ return sendErrorResponse(LspErrorResponse::InvalidRequest,
+ Tr::tr("Method '%1' not allowed after shutdown."));
+ }
+ if (method == "shutdown")
+ return handleShutdownRequest();
+ if (method == QLatin1String("initialize"))
+ return handleInitializeRequest();
+ if (state == State::None) {
+ return sendErrorResponse(LspErrorResponse::ServerNotInitialized,
+ Tr::tr("First message must be initialize request."));
+ }
+ if (method == "initialized")
+ return handleInitializedNotification();
+ if (method == "textDocument/didOpen")
+ return handleDidOpenNotification();
+ if (method == "textDocument/didChange")
+ return handleDidChangeNotification();
+ if (method == "textDocument/didSave")
+ return handleDidSaveNotification();
+ if (method == "textDocument/didClose")
+ return handleDidCloseNotification();
+ if (method == "textDocument/definition")
+ return handleGotoDefinitionRequest();
+ if (method == "textDocument/completion")
+ return handleCompletionRequest();
+
+ sendErrorResponse(LspErrorResponse::MethodNotFound, Tr::tr("This server can do very little."));
+}
+
+void LspServer::Private::handleInitializeRequest()
+{
+ if (state != State::None) {
+ return sendErrorResponse(LspErrorResponse::InvalidRequest,
+ Tr::tr("Received initialize request in initialized state."));
+ }
+ state = State::InitRequest;
+
+ lsp::ServerInfo serverInfo;
+ serverInfo.insert(lsp::nameKey, "qbs");
+ serverInfo.insert(lsp::versionKey, QBS_VERSION);
+ lsp::InitializeResult result;
+ result.insert(lsp::serverInfoKey, serverInfo);
+ lsp::ServerCapabilities capabilities; // TODO: hover
+ capabilities.setDefinitionProvider(true);
+ capabilities.setTextDocumentSync({int(lsp::TextDocumentSyncKind::Incremental)});
+ lsp::ServerCapabilities::CompletionOptions completionOptions;
+ completionOptions.setTriggerCharacters({"."});
+ capabilities.setCompletionProvider(completionOptions);
+ result.setCapabilities(capabilities);
+ sendResponse(result);
+}
+
+void LspServer::Private::handleInitializedNotification()
+{
+ if (state != State::InitRequest) {
+ return sendErrorResponse(LspErrorResponse::InvalidRequest,
+ Tr::tr("Unexpected initialized notification."));
+ }
+ state = State::InitNotification;
+}
+
+void LspServer::Private::handleGotoDefinitionRequest()
+{
+ const lsp::TextDocumentPositionParams params(messageObject.value(lsp::paramsKey));
+ const QString sourceFile = params.textDocument().uri().toLocalFile();
+ const Document *sourceDoc = nullptr;
+ if (const auto it = documents.find(sourceFile); it != documents.end())
+ sourceDoc = &it->second;
+ const auto fileEntry = codeLinks.constFind(sourceFile);
+ if (fileEntry == codeLinks.constEnd())
+ return sendResponse(nullptr);
+ const CodePosition sourcePos = posFromLspPos(params.position());
+ if (sourceDoc && !sourceDoc->isPositionUpToDate(sourcePos))
+ return sendResponse(nullptr);
+ for (auto it = fileEntry->cbegin(); it != fileEntry->cend(); ++it) {
+ if (!it.key().contains(sourcePos))
+ continue;
+ if (sourceDoc && !sourceDoc->isPositionUpToDate(it.key().end()))
+ return sendResponse(nullptr);
+ QList<CodeLocation> targets = it.value();
+ QBS_ASSERT(!targets.isEmpty(), return sendResponse(nullptr));
+ for (auto it = targets.begin(); it != targets.end();) {
+ const Document *targetDoc = nullptr;
+ if (it->filePath() == sourceFile)
+ targetDoc = sourceDoc;
+ else if (const auto docIt = documents.find(it->filePath()); docIt != documents.end())
+ targetDoc = &docIt->second;
+ if (targetDoc && !targetDoc->isPositionUpToDate(CodePosition(it->line(), it->column())))
+ it = targets.erase(it);
+ else
+ ++it;
+ }
+ struct JsonArray : public QJsonArray { void reserve(std::size_t) {}};
+ const auto locations = transformed<JsonArray>(targets,
+ [](const CodeLocation &loc) {
+ const lsp::Position startPos = lspPosFromCodeLocation(loc);
+ const lsp::Position endPos(startPos.line(), startPos.character() + 1);
+ lsp::Location targetLocation;
+ targetLocation.setUri(lsp::DocumentUri::fromProtocol(
+ QUrl::fromLocalFile(loc.filePath()).toString()));
+ targetLocation.setRange({startPos, endPos});
+ return QJsonObject(targetLocation);
+ });
+ if (locations.size() == 1)
+ return sendResponse(locations.first().toObject());
+ return sendResponse(locations);
+ }
+ sendResponse(nullptr);
+}
+
+// We operate under the assumption that the client has basic QML support.
+// Therefore, we only provide completion for qbs modules and their properties.
+// Only a simple prefix match is implemented, with no regard to the contents after the cursor.
+void LspServer::Private::handleCompletionRequest()
+{
+ if (!projectData.isValid())
+ return sendResponse(nullptr);
+
+ const lsp::CompletionParams params(messageObject.value(lsp::paramsKey));
+ const QString sourceFile = params.textDocument().uri().toLocalFile();
+ const Document *sourceDoc = nullptr;
+ if (const auto it = documents.find(sourceFile); it != documents.end())
+ sourceDoc = &it->second;
+ if (!sourceDoc)
+ return sendResponse(nullptr);
+
+ // If there are products corresponding to this file, check only these when looking for modules.
+ // Otherwise, check all products.
+ const QList<ProductData> allProducts = projectData.allProducts();
+ if (allProducts.isEmpty())
+ return sendResponse(nullptr);
+ QList<ProductData> relevantProducts;
+ for (const ProductData &p : allProducts) {
+ if (p.location().filePath() == sourceFile)
+ relevantProducts << p;
+ }
+ if (relevantProducts.isEmpty())
+ relevantProducts = allProducts;
+
+ QString identifierPrefix;
+ QStringList modulePrefixes;
+ const int offset = posToOffset(params.position(), sourceDoc->currentContent) - 1;
+ if (offset < 0 || offset >= sourceDoc->currentContent.length())
+ return sendResponse(nullptr);
+ const auto collectFromRawString = [&] {
+ int currentPos = offset;
+ const auto constructIdentifier = [&] {
+ QString id;
+ while (currentPos >= 0) {
+ const QChar c = sourceDoc->currentContent.at(currentPos);
+ if (!c.isLetterOrNumber() && c != '_')
+ break;
+ id.prepend(c);
+ --currentPos;
+ }
+ return id;
+ };
+ identifierPrefix = constructIdentifier();
+ while (true) {
+ if (currentPos <= 0 || sourceDoc->currentContent.at(currentPos) != '.')
+ return;
+ --currentPos;
+ const QString modulePrefix = constructIdentifier();
+ if (modulePrefix.isEmpty())
+ return;
+ modulePrefixes.prepend(modulePrefix);
+ }
+ };
+
+ // Parse the current file. Note that completion usually happens on invalid code, which
+ // often confuses the parser so much that it yields unusable results. Therefore, we always
+ // gather our input parameters from the raw string. We only use the parse result to skip
+ // completion in contexts where it is undesirable.
+ QbsQmlJS::Engine engine;
+ QbsQmlJS::Lexer lexer(&engine);
+ lexer.setCode(sourceDoc->currentContent, 1);
+ QbsQmlJS::Parser parser(&engine);
+ parser.parse();
+ if (parser.ast()) {
+ AstNodeLocator locator(offset, *parser.ast());
+ const QList<QbsQmlJS::AST::Node *> &astPath = locator.path();
+ if (!astPath.isEmpty()) {
+ switch (astPath.last()->kind) {
+ case QbsQmlJS::AST::Node::Kind_FieldMemberExpression:
+ case QbsQmlJS::AST::Node::Kind_UiObjectDefinition:
+ case QbsQmlJS::AST::Node::Kind_UiQualifiedId:
+ case QbsQmlJS::AST::Node::Kind_UiScriptBinding:
+ break;
+ default:
+ return sendResponse(nullptr);
+ }
+ }
+ }
+
+ collectFromRawString();
+ if (modulePrefixes.isEmpty() && identifierPrefix.isEmpty())
+ return sendResponse(nullptr); // We do not want to start completion from nothing.
+
+ QJsonArray results;
+ QMap<QString, QString> namesAndTypes;
+ for (const ProductData &product : std::as_const(relevantProducts)) {
+ const PropertyMap &moduleProps = product.moduleProperties();
+ const QStringList allModules = moduleProps.allModules();
+ const QString moduleNameOrPrefix = modulePrefixes.join('.');
+
+ // Case 1: Prefixes match a module name. Identifier can only expand to the name
+ // of a module property.
+ // Example: "Qt.core.a^" -> "Qt.core.availableBuildVariants"
+ if (!modulePrefixes.isEmpty() && allModules.contains(moduleNameOrPrefix)) {
+ for (const PropertyMap::PropertyInfo &info :
+ moduleProps.allPropertiesForModule(moduleNameOrPrefix)) {
+ if (info.isBuiltin)
+ continue;
+ if (!identifierPrefix.isEmpty() && !info.name.startsWith(identifierPrefix))
+ continue;
+ namesAndTypes.insert(info.name, info.type);
+ }
+ continue;
+ }
+
+ // Case 2: Isolated identifier. Can only expand to a module name.
+ // Example: "Q^" -> "Qt.core", "Qt.widgets", ...
+ // Case 3: Prefixes match a module prefix. Identifier can only expand to a module name.
+ // Example: "Qt.c^" -> "Qt.core", "Qt.concurrent", ...
+ QString fullPrefix = identifierPrefix;
+ int nameOffset = 0;
+ if (!modulePrefixes.isEmpty()) {
+ fullPrefix.prepend(moduleNameOrPrefix + '.');
+ nameOffset = moduleNameOrPrefix.length() + 1;
+ }
+ for (const QString &module : allModules) {
+ if (module.startsWith(fullPrefix))
+ namesAndTypes.insert(module.mid(nameOffset), {});
+ }
+ }
+
+ for (auto it = namesAndTypes.cbegin(); it != namesAndTypes.cend(); ++it) {
+ lsp::CompletionItem item;
+ item.setLabel(it.key());
+ if (!it.value().isEmpty())
+ item.setDetail(it.value());
+ results.append(QJsonObject(item));
+ };
+ sendResponse(results);
+}
+
+void LspServer::Private::handleShutdownRequest()
+{
+ state = State::Shutdown;
+ sendResponse(nullptr);
+}
+
+void LspServer::Private::handleDidOpenNotification()
+{
+ const lsp::TextDocumentItem docItem = lsp::DidOpenTextDocumentNotification(messageObject)
+ .params().value_or(lsp::DidOpenTextDocumentParams())
+ .textDocument();
+ if (!docItem.isValid())
+ return sendErrorNotification(Tr::tr("Invalid didOpen parameters."));
+ const QString filePath = uriToString(docItem.uri());
+ Document &doc = documents[filePath];
+ doc.savedContent = doc.currentContent = docItem.text();
+}
+
+void LspServer::Private::handleDidChangeNotification()
+{
+ const auto params = lsp::DidChangeTextDocumentNotification(messageObject)
+ .params().value_or(lsp::DidChangeTextDocumentParams());
+ if (!params.isValid())
+ return sendErrorNotification(Tr::tr("Invalid didChange parameters."));
+ const QString filePath = uriToString(params.textDocument().uri());
+ const auto docIt = documents.find(filePath);
+ if (docIt == documents.end())
+ return sendNoSuchDocumentError(filePath);
+ Document &doc = docIt->second;
+ const auto changes = params.contentChanges();
+ for (const auto &change : changes) {
+ const auto range = change.range();
+ if (!range) {
+ doc.currentContent = change.text();
+ continue;
+ }
+ const int startPos = posToOffset(range->start(), doc.currentContent);
+ const int endPos = posToOffset(range->end(), doc.currentContent);
+ if (startPos == -1 || endPos == -1 || startPos > endPos)
+ return sendErrorResponse(LspErrorResponse::InvalidParams, Tr::tr("Invalid range."));
+ doc.currentContent.replace(startPos, endPos - startPos, change.text());
+ }
+}
+
+void LspServer::Private::handleDidSaveNotification()
+{
+ const QString filePath = uriToString(lsp::DidSaveTextDocumentNotification(messageObject)
+ .params().value_or(lsp::DidSaveTextDocumentParams())
+ .textDocument().uri());
+ const auto docIt = documents.find(filePath);
+ if (docIt == documents.end())
+ return sendNoSuchDocumentError(filePath);
+ docIt->second.savedContent = docIt->second.currentContent;
+
+ // TODO: Mark the project data as out of date until the next update(),if the document
+ // is in buildSystemFiles().
+}
+
+void LspServer::Private::handleDidCloseNotification()
+{
+ const QString filePath = uriToString(lsp::DidCloseTextDocumentNotification(messageObject)
+ .params().value_or(lsp::DidCloseTextDocumentParams())
+ .textDocument().uri());
+ const auto docIt = documents.find(filePath);
+ if (docIt == documents.end())
+ return sendNoSuchDocumentError(filePath);
+ documents.erase(docIt);
+}
+
+static int posToOffset(const CodePosition &pos, const QString &doc)
+{
+ int offset = 0;
+ for (int newlines = 0, next = 0; newlines < pos.line() - 1; ++newlines) {
+ offset = doc.indexOf('\n', next);
+ if (offset == -1)
+ return -1;
+ next = offset + 1;
+ }
+ return offset + pos.column();
+}
+
+bool Document::isPositionUpToDate(const CodePosition &pos) const
+{
+ const int origOffset = posToOffset(pos, savedContent);
+ if (origOffset > int(currentContent.size()))
+ return false;
+ return QStringView(currentContent).left(origOffset)
+ == QStringView(savedContent).left(origOffset);
+}
+
+bool Document::isPositionUpToDate(const lsp::Position &pos) const
+{
+ return isPositionUpToDate(posFromLspPos(pos));
+}
+
+} // namespace qbs::Internal
+
diff --git a/src/app/qbs/lspserver.h b/src/app/qbs/lspserver.h
new file mode 100644
index 000000000..566808309
--- /dev/null
+++ b/src/app/qbs/lspserver.h
@@ -0,0 +1,65 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <tools/codelocation.h>
+
+#include <QString>
+
+namespace qbs {
+class ProjectData;
+namespace Internal {
+
+class LspServer
+{
+public:
+ LspServer();
+ ~LspServer();
+
+ void updateProjectData(const ProjectData &projectData, const CodeLinks &codeLinks);
+ QString socketPath() const;
+
+private:
+ class Private;
+ Private * const d;
+};
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/app/qbs/parser/commandlineoption.cpp b/src/app/qbs/parser/commandlineoption.cpp
index a09f36c2c..7572b4e66 100644
--- a/src/app/qbs/parser/commandlineoption.cpp
+++ b/src/app/qbs/parser/commandlineoption.cpp
@@ -334,7 +334,7 @@ void StringListOption::doParse(const QString &representation, QStringList &input
throw ErrorInfo(Tr::tr("Invalid use of option '%1': Argument list must not be empty.\n"
"Usage: %2").arg(representation, description(command())));
}
- for (const QString &element : qAsConst(m_arguments)) {
+ for (const QString &element : std::as_const(m_arguments)) {
if (element.isEmpty()) {
throw ErrorInfo(Tr::tr("Invalid use of option '%1': Argument list must not contain "
"empty elements.\nUsage: %2")
@@ -663,27 +663,51 @@ void CommandEchoModeOption::doParse(const QString &representation, QStringList &
m_echoMode = commandEchoModeFromName(mode);
}
-QString WaitLockOption::description(CommandType command) const
+QString DeprecationWarningsOption::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());
+ return Tr::tr("%1 <mode>\n"
+ "\tWhat to do when encountering deprecated items or properties.\n"
+ "\tPossible values are '%2'.\n"
+ "\tThe default is '%3'.\n")
+ .arg(longRepresentation(),
+ allDeprecationWarningModeStrings().join(QLatin1String("', '")),
+ deprecationWarningModeName(defaultDeprecationWarningMode()));
}
-QString WaitLockOption::longRepresentation() const
+QString DeprecationWarningsOption::longRepresentation() const
{
- return QStringLiteral("--wait-lock");
+ return QStringLiteral("--deprecation-warnings");
}
-QString DisableFallbackProviderOption::description(CommandType) const
+void DeprecationWarningsOption::doParse(const QString &representation, QStringList &input)
{
- return Tr::tr("%1\n\tDo not fall back to pkg-config if a dependency is not found.\n")
+ const QString mode = getArgument(representation, input);
+ if (mode.isEmpty()) {
+ throw ErrorInfo(Tr::tr("Invalid use of option '%1': No deprecation warning mode given.\n"
+ "Usage: %2")
+ .arg(representation, description(command())));
+ }
+
+ if (!allDeprecationWarningModeStrings().contains(mode)) {
+ throw ErrorInfo(Tr::tr("Invalid use of option '%1': "
+ "Invalid deprecation warning mode '%2' given.\nUsage: %3")
+ .arg(representation, mode, description(command())));
+ }
+
+ m_mode = deprecationWarningModeFromName(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 DisableFallbackProviderOption::longRepresentation() const
+QString WaitLockOption::longRepresentation() const
{
- return QStringLiteral("--no-fallback-module-provider");
+ return QStringLiteral("--wait-lock");
}
QString RunEnvConfigOption::description(CommandType command) const
diff --git a/src/app/qbs/parser/commandlineoption.h b/src/app/qbs/parser/commandlineoption.h
index f8ec1c735..7b8baeae2 100644
--- a/src/app/qbs/parser/commandlineoption.h
+++ b/src/app/qbs/parser/commandlineoption.h
@@ -42,6 +42,7 @@
#include "commandtype.h"
#include <tools/commandechomode.h>
+#include <tools/deprecationwarningmode.h>
#include <tools/joblimits.h>
#include <QtCore/qstringlist.h>
@@ -54,7 +55,9 @@ public:
enum Type {
FileOptionType,
BuildDirectoryOptionType,
- LogLevelOptionType, VerboseOptionType, QuietOptionType,
+ LogLevelOptionType,
+ VerboseOptionType,
+ QuietOptionType,
JobsOptionType,
KeepGoingOptionType,
DryRunOptionType,
@@ -63,7 +66,9 @@ public:
ChangedFilesOptionType,
ProductsOptionType,
NoInstallOptionType,
- InstallRootOptionType, RemoveFirstOptionType, NoBuildOptionType,
+ InstallRootOptionType,
+ RemoveFirstOptionType,
+ NoBuildOptionType,
ForceTimestampCheckOptionType,
ForceOutputCheckOptionType,
BuildNonDefaultOptionType,
@@ -75,7 +80,7 @@ public:
GeneratorOptionType,
WaitLockOptionType,
RunEnvConfigOptionType,
- DisableFallbackProviderType,
+ DeprecationWarningsOptionType,
};
virtual ~CommandLineOption();
@@ -367,6 +372,20 @@ private:
CommandEchoMode m_echoMode = CommandEchoModeInvalid;
};
+class DeprecationWarningsOption : public CommandLineOption
+{
+public:
+ QString description(CommandType command) const override;
+ QString shortRepresentation() const override { return {}; }
+ QString longRepresentation() const override;
+ DeprecationWarningMode mode() const { return m_mode; }
+
+private:
+ void doParse(const QString &representation, QStringList &input) override;
+
+ DeprecationWarningMode m_mode = defaultDeprecationWarningMode();
+};
+
class SettingsDirOption : public CommandLineOption
{
public:
@@ -415,14 +434,6 @@ public:
QString longRepresentation() const override;
};
-class DisableFallbackProviderOption : public OnOffOption
-{
-public:
- QString description(CommandType command) const override;
- QString shortRepresentation() const override { return {}; }
- QString longRepresentation() const override;
-};
-
} // namespace qbs
#endif // QBS_COMMANDLINEOPTION_H
diff --git a/src/app/qbs/parser/commandlineoptionpool.cpp b/src/app/qbs/parser/commandlineoptionpool.cpp
index 63711f623..692c9c737 100644
--- a/src/app/qbs/parser/commandlineoptionpool.cpp
+++ b/src/app/qbs/parser/commandlineoptionpool.cpp
@@ -128,12 +128,12 @@ CommandLineOption *CommandLineOptionPool::getOption(CommandLineOption::Type type
case CommandLineOption::WaitLockOptionType:
option = new WaitLockOption;
break;
- case CommandLineOption::DisableFallbackProviderType:
- option = new DisableFallbackProviderOption;
- break;
case CommandLineOption::RunEnvConfigOptionType:
option = new RunEnvConfigOption;
break;
+ case CommandLineOption::DeprecationWarningsOptionType:
+ option = new DeprecationWarningsOption;
+ break;
default:
qFatal("Unknown option type %d", type);
}
@@ -276,15 +276,15 @@ WaitLockOption *CommandLineOptionPool::waitLockOption() const
return static_cast<WaitLockOption *>(getOption(CommandLineOption::WaitLockOptionType));
}
-DisableFallbackProviderOption *CommandLineOptionPool::disableFallbackProviderOption() const
+RunEnvConfigOption *CommandLineOptionPool::runEnvConfigOption() const
{
- return static_cast<DisableFallbackProviderOption *>(
- getOption(CommandLineOption::DisableFallbackProviderType));
+ return static_cast<RunEnvConfigOption *>(getOption(CommandLineOption::RunEnvConfigOptionType));
}
-RunEnvConfigOption *CommandLineOptionPool::runEnvConfigOption() const
+DeprecationWarningsOption *CommandLineOptionPool::deprecationWarningsOption() const
{
- return static_cast<RunEnvConfigOption *>(getOption(CommandLineOption::RunEnvConfigOptionType));
+ return static_cast<DeprecationWarningsOption *>
+ (getOption(CommandLineOption::DeprecationWarningsOptionType));
}
} // namespace qbs
diff --git a/src/app/qbs/parser/commandlineoptionpool.h b/src/app/qbs/parser/commandlineoptionpool.h
index c7ac263e1..022e9fd09 100644
--- a/src/app/qbs/parser/commandlineoptionpool.h
+++ b/src/app/qbs/parser/commandlineoptionpool.h
@@ -77,8 +77,8 @@ public:
RespectProjectJobLimitsOption *respectProjectJobLimitsOption() const;
GeneratorOption *generatorOption() const;
WaitLockOption *waitLockOption() const;
- DisableFallbackProviderOption *disableFallbackProviderOption() const;
RunEnvConfigOption *runEnvConfigOption() const;
+ DeprecationWarningsOption *deprecationWarningsOption() const;
private:
mutable QHash<CommandLineOption::Type, CommandLineOption *> m_options;
diff --git a/src/app/qbs/parser/commandlineparser.cpp b/src/app/qbs/parser/commandlineparser.cpp
index 052f6b92f..c548cf2b5 100644
--- a/src/app/qbs/parser/commandlineparser.cpp
+++ b/src/app/qbs/parser/commandlineparser.cpp
@@ -58,12 +58,14 @@
#include <tools/qttools.h>
#include <tools/settings.h>
#include <tools/settingsrepresentation.h>
+#include <tools/stlutils.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdir.h>
#include <QtCore/qmap.h>
#include <QtCore/qtextstream.h>
+#include <algorithm>
#include <utility>
#ifdef Q_OS_UNIX
@@ -111,14 +113,9 @@ public:
bool logTime;
};
-CommandLineParser::CommandLineParser() : d(nullptr)
-{
-}
+CommandLineParser::CommandLineParser() = default;
-CommandLineParser::~CommandLineParser()
-{
- delete d;
-}
+CommandLineParser::~CommandLineParser() = default;
void CommandLineParser::printHelp() const
{
@@ -159,17 +156,11 @@ QString CommandLineParser::projectBuildDirectory() const
BuildOptions CommandLineParser::buildOptions(const QString &profile) const
{
- Settings settings(settingsDir());
- Preferences preferences(&settings, profile);
-
- if (d->buildOptions.maxJobCount() <= 0) {
- d->buildOptions.setMaxJobCount(preferences.jobs());
- }
-
+ d->buildOptions.setMaxJobCount(jobCount(profile));
if (d->buildOptions.echoMode() < 0) {
- d->buildOptions.setEchoMode(preferences.defaultEchoMode());
+ Settings settings(settingsDir());
+ d->buildOptions.setEchoMode(Preferences(&settings, profile).defaultEchoMode());
}
-
return d->buildOptions;
}
@@ -206,6 +197,15 @@ InstallOptions CommandLineParser::installOptions(const QString &profile) const
return options;
}
+int CommandLineParser::jobCount(const QString &profile) const
+{
+ if (const int explicitJobCount = d->optionPool.jobsOption()->jobCount(); explicitJobCount > 0)
+ return explicitJobCount;
+
+ Settings settings(settingsDir());
+ return Preferences(&settings, profile).jobs();
+}
+
bool CommandLineParser::forceTimestampCheck() const
{
return d->optionPool.forceTimestampCheckOption()->enabled();
@@ -231,11 +231,6 @@ bool CommandLineParser::waitLockBuildGraph() const
return d->optionPool.waitLockOption()->enabled();
}
-bool CommandLineParser::disableFallbackProvider() const
-{
- return d->optionPool.disableFallbackProviderOption()->enabled();
-}
-
bool CommandLineParser::logTime() const
{
return d->logTime;
@@ -282,6 +277,11 @@ QString CommandLineParser::settingsDir() const
return d->settingsDir();
}
+DeprecationWarningMode CommandLineParser::deprecationWarningMode() const
+{
+ return d->optionPool.deprecationWarningsOption()->mode();
+}
+
QString CommandLineParser::commandName() const
{
return d->command->representation();
@@ -309,8 +309,7 @@ QList<QVariantMap> CommandLineParser::buildConfigurations() const
bool CommandLineParser::parseCommandLine(const QStringList &args)
{
- delete d;
- d = new CommandLineParserPrivate;
+ d = std::make_unique<CommandLineParserPrivate>();
d->commandLine = args;
try {
d->doParse();
@@ -334,7 +333,19 @@ void CommandLineParser::CommandLineParserPrivate::doParse()
} else {
command = commandFromString(commandLine.front());
if (command) {
- commandLine.removeFirst();
+ const QString commandName = commandLine.takeFirst();
+
+ // if the command line contains a `<command>` with
+ // either `-h` or `--help` switch, we transform
+ // it to corresponding `help <command>` instead
+ const QStringList helpSwitches = {QStringLiteral("-h"), QStringLiteral("--help")};
+ if (auto it = std::find_first_of(
+ commandLine.begin(), commandLine.end(),
+ helpSwitches.begin(), helpSwitches.end());
+ it != commandLine.end()) {
+ command = commandPool.getCommand(HelpCommandType);
+ commandLine = QList{commandName}; // keep only command's name
+ }
} else { // No command given.
if (commandLine.front() == QLatin1String("-h")
|| commandLine.front() == QLatin1String("--help")) {
@@ -413,7 +424,7 @@ QString CommandLineParser::CommandLineParserPrivate::generalHelp() const
for (const Command * command : commands)
commandMap.insert(command->representation(), command);
- for (const Command * command : qAsConst(commandMap)) {
+ for (const Command * command : std::as_const(commandMap)) {
help.append(QLatin1String(" ")).append(command->representation());
const QString whitespace
= QString(rhsIndentation - 2 - command->representation().size(), QLatin1Char(' '));
@@ -424,7 +435,7 @@ QString CommandLineParser::CommandLineParserPrivate::generalHelp() const
toolNames.sort();
if (!toolNames.empty()) {
help.append(QLatin1Char('\n')).append(Tr::tr("Auxiliary commands:\n"));
- for (const QString &toolName : qAsConst(toolNames)) {
+ for (const QString &toolName : std::as_const(toolNames)) {
help.append(QLatin1String(" ")).append(toolName);
const QString whitespace = QString(rhsIndentation - 2 - toolName.size(),
QLatin1Char(' '));
@@ -507,7 +518,7 @@ void CommandLineParser::CommandLineParserPrivate::setupBuildConfigurations()
const QVariantMap globalProperties = propertiesPerConfiguration.takeFirst().second;
QList<QVariantMap> buildConfigs;
- for (const PropertyListItem &item : qAsConst(propertiesPerConfiguration)) {
+ for (const PropertyListItem &item : std::as_const(propertiesPerConfiguration)) {
QVariantMap properties = item.second;
for (QVariantMap::ConstIterator globalPropIt = globalProperties.constBegin();
globalPropIt != globalProperties.constEnd(); ++globalPropIt) {
@@ -588,18 +599,15 @@ QString CommandLineParser::CommandLineParserPrivate::propertyName(const QString
// Make fully-qualified, ie "platform" -> "qbs.platform"
if (aCommandLineName.contains(QLatin1Char('.')))
return aCommandLineName;
- else
- return QLatin1String("qbs.") + aCommandLineName;
+ return QLatin1String("qbs.") + aCommandLineName;
}
bool CommandLineParser::CommandLineParserPrivate::checkForExistingBuildConfiguration(
const QList<QVariantMap> &buildConfigs, const QString &configurationName)
{
- for (const QVariantMap &buildConfig : buildConfigs) {
- if (configurationName == getBuildConfigurationName(buildConfig))
- return true;
- }
- return false;
+ return Internal::any_of(buildConfigs, [&configurationName](const auto &buildConfig) {
+ return configurationName == getBuildConfigurationName(buildConfig);
+ });
}
bool CommandLineParser::CommandLineParserPrivate::withNonDefaultProducts() const
diff --git a/src/app/qbs/parser/commandlineparser.h b/src/app/qbs/parser/commandlineparser.h
index d47657b16..4df8829a2 100644
--- a/src/app/qbs/parser/commandlineparser.h
+++ b/src/app/qbs/parser/commandlineparser.h
@@ -41,9 +41,13 @@
#include "commandtype.h"
+#include <tools/deprecationwarningmode.h>
+
#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
+#include <memory>
+
namespace qbs {
class BuildOptions;
class CleanOptions;
@@ -71,12 +75,12 @@ public:
CleanOptions cleanOptions(const QString &profile) const;
GenerateOptions generateOptions() const;
InstallOptions installOptions(const QString &profile) const;
+ int jobCount(const QString &profile) const;
bool forceTimestampCheck() const;
bool forceOutputCheck() const;
bool dryRun() const;
bool forceProbesExecution() const;
bool waitLockBuildGraph() const;
- bool disableFallbackProvider() const;
bool logTime() const;
bool withNonDefaultProducts() const;
bool buildBeforeInstalling() const;
@@ -87,10 +91,11 @@ public:
bool showProgress() const;
bool showVersion() const;
QString settingsDir() const;
+ DeprecationWarningMode deprecationWarningMode() const;
private:
class CommandLineParserPrivate;
- CommandLineParserPrivate *d;
+ std::unique_ptr<CommandLineParserPrivate> d;
};
} // namespace qbs
diff --git a/src/app/qbs/parser/parser.pri b/src/app/qbs/parser/parser.pri
deleted file mode 100644
index f708f1135..000000000
--- a/src/app/qbs/parser/parser.pri
+++ /dev/null
@@ -1,16 +0,0 @@
-SOURCES += \
- $$PWD/commandlineparser.cpp \
- $$PWD/commandpool.cpp \
- $$PWD/commandlineoption.cpp \
- $$PWD/commandlineoptionpool.cpp \
- $$PWD/parsercommand.cpp
-
-HEADERS += \
- $$PWD/commandlineparser.h \
- $$PWD/commandpool.h \
- $$PWD/commandlineoption.h \
- $$PWD/commandlineoptionpool.h \
- $$PWD/commandtype.h \
- $$PWD/parsercommand.h
-
-include(../../../../qbs_version.pri)
diff --git a/src/app/qbs/parser/parsercommand.cpp b/src/app/qbs/parser/parsercommand.cpp
index 799bf5dcf..ef8da9551 100644
--- a/src/app/qbs/parser/parsercommand.cpp
+++ b/src/app/qbs/parser/parsercommand.cpp
@@ -164,7 +164,7 @@ QString Command::supportedOptionsDescription() const
}
QString s = Tr::tr("The possible options are:\n");
- for (const CommandLineOption *option : qAsConst(optionMap))
+ for (const CommandLineOption *option : std::as_const(optionMap))
s += option->description(type());
return s;
}
@@ -199,16 +199,18 @@ QString ResolveCommand::representation() const
static QList<CommandLineOption::Type> resolveOptions()
{
- return {CommandLineOption::FileOptionType,
- CommandLineOption::BuildDirectoryOptionType,
- CommandLineOption::LogLevelOptionType,
- CommandLineOption::VerboseOptionType,
- CommandLineOption::QuietOptionType,
- CommandLineOption::ShowProgressOptionType,
- CommandLineOption::DryRunOptionType,
- CommandLineOption::ForceProbesOptionType,
- CommandLineOption::LogTimeOptionType,
- CommandLineOption::DisableFallbackProviderType};
+ return {
+ CommandLineOption::FileOptionType,
+ CommandLineOption::BuildDirectoryOptionType,
+ CommandLineOption::LogLevelOptionType,
+ CommandLineOption::VerboseOptionType,
+ CommandLineOption::QuietOptionType,
+ CommandLineOption::ShowProgressOptionType,
+ CommandLineOption::DryRunOptionType,
+ CommandLineOption::ForceProbesOptionType,
+ CommandLineOption::LogTimeOptionType,
+ CommandLineOption::DeprecationWarningsOptionType,
+ CommandLineOption::JobsOptionType};
}
QList<CommandLineOption::Type> ResolveCommand::supportedOptions() const
@@ -278,7 +280,6 @@ static QList<CommandLineOption::Type> buildOptions()
<< CommandLineOption::ForceTimestampCheckOptionType
<< CommandLineOption::ForceOutputCheckOptionType
<< CommandLineOption::BuildNonDefaultOptionType
- << CommandLineOption::JobsOptionType
<< CommandLineOption::CommandEchoModeOptionType
<< CommandLineOption::NoInstallOptionType
<< CommandLineOption::RemoveFirstOptionType
diff --git a/src/app/qbs/qbs.pro b/src/app/qbs/qbs.pro
deleted file mode 100644
index 6ce449aad..000000000
--- a/src/app/qbs/qbs.pro
+++ /dev/null
@@ -1,54 +0,0 @@
-include(../app.pri)
-include(parser/parser.pri)
-
-TARGET = qbs
-
-SOURCES += main.cpp \
- ctrlchandler.cpp \
- application.cpp \
- session.cpp \
- sessionpacket.cpp \
- sessionpacketreader.cpp \
- stdinreader.cpp \
- status.cpp \
- consoleprogressobserver.cpp \
- commandlinefrontend.cpp \
- qbstool.cpp
-
-HEADERS += \
- ctrlchandler.h \
- application.h \
- session.h \
- sessionpacket.h \
- sessionpacketreader.h \
- stdinreader.h \
- status.h \
- consoleprogressobserver.h \
- commandlinefrontend.h \
- qbstool.h
-
-include(../../library_dirname.pri)
-isEmpty(QBS_RELATIVE_LIBEXEC_PATH) {
- win32:QBS_RELATIVE_LIBEXEC_PATH=.
- else: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}\\\"
-
-CONFIG(static, static|shared) {
- include(../../plugins/qbs_plugin_common.pri)
- LIBS += -L$$qbsPluginDestDir
- scannerPlugins = cpp qt
- for (scannerPlugin, scannerPlugins) {
- include(../../plugins/scanner/$$scannerPlugin/$${scannerPlugin}.pri) \
- include(../../plugins/use_plugin.pri)
- }
- generatorPlugins = clangcompilationdb iarew keiluv makefilegenerator visualstudio
- for (generatorPlugin, generatorPlugins) {
- include(../../plugins/generator/$$generatorPlugin/$${generatorPlugin}.pri) \
- include(../../plugins/use_plugin.pri)
- }
-}
diff --git a/src/app/qbs/qbs.qbs b/src/app/qbs/qbs.qbs
index 530036f3a..52c29e9b2 100644
--- a/src/app/qbs/qbs.qbs
+++ b/src/app/qbs/qbs.qbs
@@ -1,14 +1,17 @@
-import qbs 1.0
import qbs.Utilities
QbsApp {
name: "qbs_app"
- Depends { name: "qbs resources" }
targetName: "qbs"
+
+ Depends { name: "qbs resources" }
+ Depends { name: "qtclsp" }
+ Depends { name: "Qt.network" }
Depends {
condition: Qt.core.staticBuild || qbsbuildconfig.staticBuild
productTypes: ["qbsplugin"]
}
+
cpp.defines: base.concat([
"QBS_VERSION=" + Utilities.cStringQuote(qbsversion.version),
"QBS_RELATIVE_LIBEXEC_PATH=" + Utilities.cStringQuote(qbsbuildconfig.relativeLibexecPath),
@@ -55,5 +58,13 @@ QbsApp {
"parsercommand.h",
]
}
+ Group {
+ name: "lsp"
+ cpp.defines: outer.filter(function(d) { return d !== "QT_NO_CAST_FROM_ASCII"; })
+ files: [
+ "lspserver.cpp",
+ "lspserver.h",
+ ]
+ }
}
diff --git a/src/app/qbs/session.cpp b/src/app/qbs/session.cpp
index 7e141b4a6..2cdcf2b63 100644
--- a/src/app/qbs/session.cpp
+++ b/src/app/qbs/session.cpp
@@ -39,6 +39,7 @@
#include "session.h"
+#include "lspserver.h"
#include "sessionpacket.h"
#include "sessionpacketreader.h"
@@ -66,7 +67,6 @@
#include <QtCore/qobject.h>
#include <QtCore/qprocess.h>
-#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <memory>
@@ -148,7 +148,7 @@ private:
struct ProductSelection {
ProductSelection(Project::ProductSelection s) : selection(s) {}
- ProductSelection(const QList<ProductData> &p) : products(p) {}
+ ProductSelection(QList<ProductData> p) : products(std::move(p)) {}
Project::ProductSelection selection = Project::ProductSelectionDefaultOnly;
QList<ProductData> products;
@@ -166,6 +166,7 @@ private:
FileUpdateData prepareFileUpdate(const QJsonObject &request);
SessionPacketReader m_packetReader;
+ LspServer m_lspServer;
Project m_project;
ProjectData m_projectData;
SessionLogSink m_logSink;
@@ -186,11 +187,14 @@ Session::Session()
#ifdef Q_OS_WIN32
// Make sure the line feed character appears as itself.
if (_setmode(_fileno(stdout), _O_BINARY) == -1) {
- std::cerr << "Failed to set stdout to binary mode: " << std::strerror(errno) << std::endl;
+ constexpr size_t errmsglen = FILENAME_MAX;
+ char errmsg[errmsglen];
+ strerror_s(errmsg, errmsglen, errno);
+ std::cerr << "Failed to set stdout to binary mode: " << errmsg << std::endl;
qApp->exit(EXIT_FAILURE);
}
#endif
- sendPacket(SessionPacket::helloMessage());
+ sendPacket(SessionPacket::helloMessage(m_lspServer.socketPath()));
connect(&m_logSink, &SessionLogSink::newMessage, this, &Session::sendPacket);
connect(&m_packetReader, &SessionPacketReader::errorOccurred,
this, [](const QString &msg) {
@@ -284,6 +288,7 @@ void Session::setupProject(const QJsonObject &request)
const ProjectData oldProjectData = m_projectData;
m_project = setupJob->project();
m_projectData = m_project.projectData();
+ m_lspServer.updateProjectData(m_projectData, m_project.codeLinks());
QJsonObject reply;
reply.insert(StringConstants::type(), QLatin1String("project-resolved"));
if (success)
@@ -298,7 +303,7 @@ void Session::setupProject(const QJsonObject &request)
void Session::buildProject(const QJsonObject &request)
{
- if (!checkNormalRequestPrerequisites("build-done"))
+ if (!checkNormalRequestPrerequisites("project-built"))
return;
const ProductSelection productSelection = getProductSelection(request);
setLogLevelFromRequest(request);
@@ -396,7 +401,6 @@ void Session::addFiles(const QJsonObject &request)
}
ErrorInfo error;
QStringList failedFiles;
-#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES
for (const QString &filePath : data.filePaths) {
const ErrorInfo e = m_project.addFiles(data.product, data.group, {filePath});
if (e.hasError()) {
@@ -405,7 +409,6 @@ void Session::addFiles(const QJsonObject &request)
failedFiles.push_back(filePath);
}
}
-#endif
QJsonObject reply;
reply.insert(StringConstants::type(), QLatin1String("files-added"));
insertErrorInfoIfNecessary(reply, error);
@@ -432,7 +435,6 @@ void Session::removeFiles(const QJsonObject &request)
}
ErrorInfo error;
QStringList failedFiles;
-#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES
for (const QString &filePath : data.filePaths) {
const ErrorInfo e = m_project.removeFiles(data.product, data.group, {filePath});
if (e.hasError()) {
@@ -441,7 +443,6 @@ void Session::removeFiles(const QJsonObject &request)
failedFiles.push_back(filePath);
}
}
-#endif
QJsonObject reply;
reply.insert(StringConstants::type(), QLatin1String("files-removed"));
insertErrorInfoIfNecessary(reply, error);
@@ -555,7 +556,7 @@ void Session::getGeneratedFilesForSources(const QJsonObject &request)
reply.insert(StringConstants::type(), QLatin1String(replyType));
const QJsonArray specs = request.value(StringConstants::productsKey()).toArray();
QJsonArray resultProducts;
- for (const QJsonValue &p : specs) {
+ for (const auto &p : specs) {
const QJsonObject productObject = p.toObject();
const ProductData product = getProductByName(
productObject.value(StringConstants::fullDisplayNameKey()).toString());
@@ -565,7 +566,7 @@ void Session::getGeneratedFilesForSources(const QJsonObject &request)
resultProduct.insert(StringConstants::fullDisplayNameKey(), product.fullDisplayName());
QJsonArray results;
const QJsonArray requests = productObject.value(QLatin1String("requests")).toArray();
- for (const QJsonValue &r : requests) {
+ for (const auto &r : requests) {
const QJsonObject request = r.toObject();
const QString filePath = request.value(QLatin1String("source-file")).toString();
const QStringList tags = fromJson<QStringList>(request.value(QLatin1String("tags")));
@@ -622,10 +623,11 @@ Session::ProductSelection Session::getProductSelection(const QJsonObject &reques
{
const QJsonValue productSelection = request.value(StringConstants::productsKey());
if (productSelection.isArray())
- return ProductSelection(getProductsByName(fromJson<QStringList>(productSelection)));
- return ProductSelection(productSelection.toString() == QLatin1String("all")
- ? Project::ProductSelectionWithNonDefault
- : Project::ProductSelectionDefaultOnly);
+ return {getProductsByName(fromJson<QStringList>(productSelection))};
+ return {
+ productSelection.toString() == QLatin1String("all")
+ ? Project::ProductSelectionWithNonDefault
+ : Project::ProductSelectionDefaultOnly};
}
Session::FileUpdateData Session::prepareFileUpdate(const QJsonObject &request)
@@ -647,15 +649,12 @@ Session::FileUpdateData Session::prepareFileUpdate(const QJsonObject &request)
data.error = tr("Product '%1' not found in project.").arg(productName);
}
const QJsonArray filesArray = request.value(QLatin1String("files")).toArray();
- for (const QJsonValue &v : filesArray)
+ for (const auto &v : filesArray)
data.filePaths << v.toString();
if (m_currentJob)
data.error = tr("Cannot update the list of source files while a job is running.");
if (!m_project.isValid())
data.error = tr("No valid project. You need to resolve first.");
-#ifndef QBS_ENABLE_PROJECT_FILE_UPDATES
- data.error = ErrorInfo(tr("Project file updates are not enabled in this build of qbs."));
-#endif
return data;
}
diff --git a/src/app/qbs/sessionpacket.cpp b/src/app/qbs/sessionpacket.cpp
index ce9fdaf76..470e27091 100644
--- a/src/app/qbs/sessionpacket.cpp
+++ b/src/app/qbs/sessionpacket.cpp
@@ -82,7 +82,7 @@ SessionPacket::Status SessionPacket::parseInput(QByteArray &input)
QJsonObject SessionPacket::retrievePacket()
{
QBS_ASSERT(isComplete(), return QJsonObject());
- const auto packet = QJsonDocument::fromJson(QByteArray::fromBase64(m_payload)).object();
+ auto packet = QJsonDocument::fromJson(QByteArray::fromBase64(m_payload)).object();
m_payload.clear();
m_expectedPayloadLength = -1;
return packet;
@@ -95,12 +95,13 @@ QByteArray SessionPacket::createPacket(const QJsonObject &packet)
.append(jsonData);
}
-QJsonObject SessionPacket::helloMessage()
+QJsonObject SessionPacket::helloMessage(const QString &lspSocket)
{
return QJsonObject{
{StringConstants::type(), QLatin1String("hello")},
- {QLatin1String("api-level"), 1},
- {QLatin1String("api-compat-level"), 1}
+ {QLatin1String("api-level"), 5},
+ {QLatin1String("api-compat-level"), 2},
+ {QLatin1String("lsp-socket"), lspSocket}
};
}
diff --git a/src/app/qbs/sessionpacket.h b/src/app/qbs/sessionpacket.h
index d919ff340..e77b30b75 100644
--- a/src/app/qbs/sessionpacket.h
+++ b/src/app/qbs/sessionpacket.h
@@ -55,7 +55,7 @@ public:
QJsonObject retrievePacket();
static QByteArray createPacket(const QJsonObject &packet);
- static QJsonObject helloMessage();
+ static QJsonObject helloMessage(const QString &lspSocket);
private:
bool isComplete() const;
diff --git a/src/app/qbs/sessionpacketreader.cpp b/src/app/qbs/sessionpacketreader.cpp
index e99ea01ed..daba30d7e 100644
--- a/src/app/qbs/sessionpacketreader.cpp
+++ b/src/app/qbs/sessionpacketreader.cpp
@@ -42,6 +42,8 @@
#include "sessionpacket.h"
#include "stdinreader.h"
+#include <QPointer>
+
namespace qbs {
namespace Internal {
@@ -64,8 +66,13 @@ void SessionPacketReader::start()
StdinReader * const stdinReader = StdinReader::create(this);
connect(stdinReader, &StdinReader::errorOccurred, this, &SessionPacketReader::errorOccurred);
connect(stdinReader, &StdinReader::dataAvailable, this, [this](const QByteArray &data) {
+ /* Because this SessionPacketReader can be destroyed in the emit packetReceived,
+ * use a `QPointer self(this)` to check whether this instance still exists.
+ * When self evaluates to false, this instance should no longer be referenced,
+ * so the parent QObject and d should no longer be used in any way. */
+ QPointer self(this);
d->incomingData += data;
- while (!d->incomingData.isEmpty()) {
+ while (self && !d->incomingData.isEmpty()) {
switch (d->currentPacket.parseInput(d->incomingData)) {
case SessionPacket::Status::Invalid:
emit errorOccurred(tr("Received invalid input."));
diff --git a/src/app/qbs/status.cpp b/src/app/qbs/status.cpp
index 3dc540ff5..8ee39e46f 100644
--- a/src/app/qbs/status.cpp
+++ b/src/app/qbs/status.cpp
@@ -47,20 +47,20 @@
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qstring.h>
-#include <QtCore/qregexp.h>
namespace qbs {
-static QList<QRegExp> createIgnoreList(const QString &projectRootPath)
+static QList<QRegularExpression> createIgnoreList(const QString &projectRootPath)
{
- QList<QRegExp> ignoreRegularExpressionList {
- QRegExp(projectRootPath + QLatin1String("/build.*")),
- QRegExp(QStringLiteral("*.qbs"), Qt::CaseSensitive, QRegExp::Wildcard),
- QRegExp(QStringLiteral("*.pro"), Qt::CaseSensitive, QRegExp::Wildcard),
- QRegExp(QStringLiteral("*Makefile"), Qt::CaseSensitive, QRegExp::Wildcard),
- QRegExp(QStringLiteral("*.so*"), Qt::CaseSensitive, QRegExp::Wildcard),
- QRegExp(QStringLiteral("*.o"), Qt::CaseSensitive, QRegExp::Wildcard)
+ QList<QRegularExpression> ignoreRegularExpressionList {
+ QRegularExpression(QRegularExpression::anchoredPattern(projectRootPath + QLatin1String("/build.*"))),
+ QRegularExpression(QRegularExpression::wildcardToRegularExpression(QStringLiteral("*.qbs"))),
+ QRegularExpression(QRegularExpression::wildcardToRegularExpression(QStringLiteral("*.pro"))),
+ QRegularExpression(QRegularExpression::wildcardToRegularExpression(QStringLiteral("*Makefile"))),
+ QRegularExpression(QRegularExpression::wildcardToRegularExpression(QStringLiteral("*.so*"))),
+ QRegularExpression(QRegularExpression::wildcardToRegularExpression(QStringLiteral("*.o")))
};
QString ignoreFilePath = projectRootPath + QLatin1String("/.qbsignore");
@@ -70,12 +70,13 @@ static QList<QRegExp> createIgnoreList(const QString &projectRootPath)
const QList<QByteArray> ignoreTokenList = ignoreFile.readAll().split('\n');
for (const QByteArray &btoken : ignoreTokenList) {
const QString token = QString::fromLatin1(btoken);
- if (token.left(1) == QLatin1String("/"))
- ignoreRegularExpressionList.push_back(QRegExp(projectRootPath
- + token + QLatin1String(".*"),
- Qt::CaseSensitive, QRegExp::RegExp2));
+ if (token.startsWith(QLatin1String("/")))
+ ignoreRegularExpressionList.push_back(
+ QRegularExpression(QRegularExpression::anchoredPattern(
+ projectRootPath + token + QLatin1String(".*"))));
else if (!token.isEmpty())
- ignoreRegularExpressionList.push_back(QRegExp(token, Qt::CaseSensitive, QRegExp::RegExp2));
+ ignoreRegularExpressionList.push_back(
+ QRegularExpression(QRegularExpression::anchoredPattern(token)));
}
}
@@ -83,7 +84,8 @@ static QList<QRegExp> createIgnoreList(const QString &projectRootPath)
return ignoreRegularExpressionList;
}
-static QStringList allFilesInDirectoryRecursive(const QDir &rootDirecory, const QList<QRegExp> &ignoreRegularExpressionList)
+static QStringList allFilesInDirectoryRecursive(
+ const QDir &rootDirecory, const QList<QRegularExpression> &ignoreRegularExpressionList)
{
QStringList fileList;
@@ -91,8 +93,8 @@ static QStringList allFilesInDirectoryRecursive(const QDir &rootDirecory, const
for (const QFileInfo &fileInfo : fileInfos) {
QString absoluteFilePath = fileInfo.absoluteFilePath();
bool inIgnoreList = false;
- for (const QRegExp &ignoreRegularExpression : ignoreRegularExpressionList) {
- if (ignoreRegularExpression.exactMatch(absoluteFilePath)) {
+ for (const QRegularExpression &ignoreRegularExpression : ignoreRegularExpressionList) {
+ if (ignoreRegularExpression.match(absoluteFilePath).hasMatch()) {
inIgnoreList = true;
break;
}
@@ -112,7 +114,7 @@ static QStringList allFilesInDirectoryRecursive(const QDir &rootDirecory, const
static QStringList allFilesInProject(const QString &projectRootPath)
{
- QList<QRegExp> ignoreRegularExpressionList = createIgnoreList(projectRootPath);
+ QList<QRegularExpression> ignoreRegularExpressionList = createIgnoreList(projectRootPath);
return allFilesInDirectoryRecursive(QDir(projectRootPath), ignoreRegularExpressionList);
}
@@ -120,8 +122,7 @@ static QStringList allFilesInProject(const QString &projectRootPath)
QStringList allFiles(const ProductData &product)
{
QStringList files;
- const auto groups = product.groups();
- for (const GroupData &group : groups)
+ for (const GroupData &group : product.groups())
files += group.allFilePaths();
return files;
}
@@ -134,20 +135,17 @@ int printStatus(const ProjectData &project)
QStringList untrackedFilesInProject = allFilesInProject(projectDirectory);
QStringList missingFiles;
- const auto products = project.allProducts();
- for (const ProductData &product : products) {
+ for (const ProductData &product : project.allProducts()) {
qbsInfo() << "\nProduct: " << product.name()
<< " (" << product.location().filePath() << ":"
<< product.location().line() << ")";
- const auto groups = product.groups();
- for (const GroupData &group : groups) {
+ for (const GroupData &group : product.groups()) {
qbsInfo() << " Group: " << group.name()
<< " (" << group.location().filePath() << ":"
<< group.location().line() << ")";
- QStringList sourceFiles = group.allFilePaths();
- std::sort(sourceFiles.begin(), sourceFiles.end());
- for (const QString &sourceFile : qAsConst(sourceFiles)) {
- if (!QFileInfo(sourceFile).exists())
+ const QStringList sourceFiles = Internal::sorted(group.allFilePaths());
+ for (const QString &sourceFile : sourceFiles) {
+ if (!QFileInfo::exists(sourceFile))
missingFiles.push_back(sourceFile);
qbsInfo() << " " << sourceFile.mid(projectDirectoryPathLength + 1);
untrackedFilesInProject.removeOne(sourceFile);
@@ -156,11 +154,11 @@ int printStatus(const ProjectData &project)
}
qbsInfo() << "\nMissing files:";
- for (const QString &untrackedFile : qAsConst(missingFiles))
+ for (const QString &untrackedFile : std::as_const(missingFiles))
qbsInfo() << " " << untrackedFile.mid(projectDirectoryPathLength + 1);
qbsInfo() << "\nUntracked files:";
- for (const QString &missingFile : qAsConst(untrackedFilesInProject))
+ for (const QString &missingFile : std::as_const(untrackedFilesInProject))
qbsInfo() << " " << missingFile.mid(projectDirectoryPathLength + 1);
return 0;
diff --git a/src/app/qbs/stdinreader.cpp b/src/app/qbs/stdinreader.cpp
index b90abf9d1..4708ff53c 100644
--- a/src/app/qbs/stdinreader.cpp
+++ b/src/app/qbs/stdinreader.cpp
@@ -43,13 +43,14 @@
#include <QtCore/qfile.h>
#include <QtCore/qsocketnotifier.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qtimer.h>
#include <cerrno>
#include <cstring>
#ifdef Q_OS_WIN32
#include <qt_windows.h>
-#include <QtCore/qtimer.h>
#else
#include <fcntl.h>
#endif
@@ -87,6 +88,18 @@ private:
connect(&m_notifier, &QSocketNotifier::activated, this, [this] {
emit dataAvailable(m_stdIn.readAll());
});
+
+ // Neither the aboutToClose() nor the readChannelFinished() signals
+ // are triggering, so we need a timer to check whether the controlling
+ // process disappeared.
+ const auto stdinClosedChecker = new QTimer(this);
+ connect(stdinClosedChecker, &QTimer::timeout, this, [this, stdinClosedChecker] {
+ if (m_stdIn.atEnd()) {
+ stdinClosedChecker->stop();
+ emit errorOccurred(tr("Input channel closed unexpectedly."));
+ }
+ });
+ stdinClosedChecker->start(1000);
}
QFile m_stdIn;
@@ -99,38 +112,183 @@ public:
WindowsStdinReader(QObject *parent) : StdinReader(parent) {}
private:
- void start() override
- {
#ifdef Q_OS_WIN32
- m_stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
- if (!m_stdinHandle) {
- emit errorOccurred(tr("Failed to create handle for standard input."));
- return;
+ class FileReaderThread : public QThread
+ {
+ public:
+ FileReaderThread(WindowsStdinReader &parent, HANDLE stdInHandle, HANDLE exitEventHandle)
+ : QThread(&parent), m_stdIn{stdInHandle}, m_exitEvent{exitEventHandle} { }
+ ~FileReaderThread()
+ {
+ wait();
+ CloseHandle(m_exitEvent);
}
- // A timer seems slightly less awful than to block in a thread
- // (how would we abort that one?), but ideally we'd like
- // to have a signal-based approach like in the Unix variant.
- const auto timer = new QTimer(this);
- connect(timer, &QTimer::timeout, this, [this] {
+ void run() override
+ {
+ WindowsStdinReader *r = static_cast<WindowsStdinReader *>(parent());
+
char buf[1024];
- DWORD bytesAvail;
- PeekNamedPipe(m_stdinHandle, nullptr, 0, nullptr, &bytesAvail, nullptr);
- while (bytesAvail > 0) {
- DWORD bytesRead;
- ReadFile(m_stdinHandle, buf, std::min<DWORD>(bytesAvail, sizeof buf), &bytesRead,
- nullptr);
- emit dataAvailable(QByteArray(buf, bytesRead));
- bytesAvail -= bytesRead;
+ while (true) {
+ DWORD bytesRead = 0;
+ if (!ReadFile(m_stdIn, buf, sizeof buf, &bytesRead, nullptr)) {
+ emit r->errorOccurred(tr("Failed to read from input channel."));
+ break;
+ }
+ if (!bytesRead)
+ break;
+ emit r->dataAvailable(QByteArray(buf, bytesRead));
}
- });
- timer->start(10);
+ }
+ private:
+ HANDLE m_stdIn;
+ HANDLE m_exitEvent;
+ };
+
+ class ConsoleReaderThread : public QThread
+ {
+ public:
+ ConsoleReaderThread(WindowsStdinReader &parent, HANDLE stdInHandle, HANDLE exitEventHandle)
+ : QThread(&parent), m_stdIn{stdInHandle}, m_exitEvent{exitEventHandle} { }
+ virtual ~ConsoleReaderThread() override
+ {
+ SetEvent(m_exitEvent);
+ wait();
+ CloseHandle(m_exitEvent);
+ }
+
+ void run() override
+ {
+ WindowsStdinReader *r = static_cast<WindowsStdinReader *>(parent());
+
+ DWORD origConsoleMode;
+ GetConsoleMode(m_stdIn, &origConsoleMode);
+ DWORD consoleMode = ENABLE_PROCESSED_INPUT;
+ SetConsoleMode(m_stdIn, consoleMode);
+
+ HANDLE handles[2] = {m_exitEvent, m_stdIn};
+ char buf[1024];
+ while (true) {
+ auto result = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ if (result == WAIT_OBJECT_0)
+ break;
+ INPUT_RECORD consoleInput;
+ DWORD inputsRead = 0;
+ if (!PeekConsoleInputA(m_stdIn, &consoleInput, 1, &inputsRead)) {
+ emit r->errorOccurred(tr("Failed to read from input channel."));
+ break;
+ }
+ if (inputsRead) {
+ if (consoleInput.EventType != KEY_EVENT
+ || !consoleInput.Event.KeyEvent.bKeyDown
+ || !consoleInput.Event.KeyEvent.uChar.AsciiChar) {
+ if (!ReadConsoleInputA(m_stdIn, &consoleInput, 1, &inputsRead)) {
+ emit r->errorOccurred(tr("Failed to read console input."));
+ break;
+ }
+ } else {
+ DWORD bytesRead = 0;
+ if (!ReadConsoleA(m_stdIn, buf, sizeof buf, &bytesRead, nullptr)) {
+ emit r->errorOccurred(tr("Failed to read console."));
+ break;
+ }
+ emit r->dataAvailable(QByteArray(buf, bytesRead));
+ }
+ }
+ }
+ SetConsoleMode(m_stdIn, origConsoleMode);
+ }
+ private:
+ HANDLE m_stdIn;
+ HANDLE m_exitEvent;
+ };
+
+ class PipeReaderThread : public QThread
+ {
+ public:
+ PipeReaderThread(WindowsStdinReader &parent, HANDLE stdInHandle, HANDLE exitEventHandle)
+ : QThread(&parent), m_stdIn{stdInHandle}, m_exitEvent{exitEventHandle} { }
+ virtual ~PipeReaderThread() override
+ {
+ SetEvent(m_exitEvent);
+ wait();
+ CloseHandle(m_exitEvent);
+ }
+
+ void run() override
+ {
+ WindowsStdinReader *r = static_cast<WindowsStdinReader *>(parent());
+
+ OVERLAPPED overlapped = {};
+ overlapped.hEvent = CreateEventA(NULL, TRUE, TRUE, NULL);
+ if (!overlapped.hEvent) {
+ emit r->errorOccurred(StdinReader::tr("Failed to create handle for overlapped event."));
+ return;
+ }
+
+ char buf[1024];
+ DWORD bytesRead;
+ HANDLE handles[2] = {m_exitEvent, overlapped.hEvent};
+ while (true) {
+ bytesRead = 0;
+ auto readResult = ReadFile(m_stdIn, buf, sizeof buf, NULL, &overlapped);
+ if (!readResult) {
+ if (GetLastError() != ERROR_IO_PENDING) {
+ emit r->errorOccurred(StdinReader::tr("ReadFile Failed."));
+ break;
+ }
+
+ auto result = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+ if (result == WAIT_OBJECT_0)
+ break;
+ }
+ if (!GetOverlappedResult(m_stdIn, &overlapped, &bytesRead, FALSE)) {
+ if (GetLastError() != ERROR_HANDLE_EOF)
+ emit r->errorOccurred(StdinReader::tr("Error GetOverlappedResult."));
+ break;
+ }
+ emit r->dataAvailable(QByteArray(buf, bytesRead));
+ }
+ CancelIo(m_stdIn);
+ CloseHandle(overlapped.hEvent);
+ }
+ private:
+ HANDLE m_stdIn;
+ HANDLE m_exitEvent;
+ };
#endif
- }
+ void start() override
+ {
#ifdef Q_OS_WIN32
- HANDLE m_stdinHandle;
+ HANDLE stdInHandle = GetStdHandle(STD_INPUT_HANDLE);
+ if (!stdInHandle) {
+ emit errorOccurred(StdinReader::tr("Failed to create handle for standard input."));
+ return;
+ }
+ HANDLE exitEventHandle = CreateEventA(NULL, TRUE, FALSE, NULL);
+ if (!exitEventHandle) {
+ emit errorOccurred(StdinReader::tr("Failed to create handle for exit event."));
+ return;
+ }
+
+ auto result = GetFileType(stdInHandle);
+ switch (result) {
+ case FILE_TYPE_CHAR:
+ (new ConsoleReaderThread(*this, stdInHandle, exitEventHandle))->start();
+ return;
+ case FILE_TYPE_PIPE:
+ (new PipeReaderThread(*this, stdInHandle, exitEventHandle))->start();
+ return;
+ case FILE_TYPE_DISK:
+ (new FileReaderThread(*this, stdInHandle, exitEventHandle))->start();
+ return;
+ default:
+ emit errorOccurred(StdinReader::tr("Unable to handle unknown input type"));
+ return;
+ }
#endif
+ }
};
StdinReader *StdinReader::create(QObject *parent)
diff --git a/src/app/shared/CMakeLists.txt b/src/app/shared/CMakeLists.txt
new file mode 100644
index 000000000..504db2b5b
--- /dev/null
+++ b/src/app/shared/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(logging)
diff --git a/src/app/shared/logging/CMakeLists.txt b/src/app/shared/logging/CMakeLists.txt
new file mode 100644
index 000000000..f6e4fc135
--- /dev/null
+++ b/src/app/shared/logging/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(SOURCES
+ coloredoutput.cpp
+ coloredoutput.h
+ consolelogger.cpp
+ consolelogger.h
+ )
+
+add_qbs_library(qbsconsolelogger
+ STATIC
+ DEPENDS qbscore
+ SOURCES ${SOURCES}
+ )
diff --git a/src/app/shared/logging/coloredoutput.cpp b/src/app/shared/logging/coloredoutput.cpp
index 0f1bb26da..d5e45cada 100644
--- a/src/app/shared/logging/coloredoutput.cpp
+++ b/src/app/shared/logging/coloredoutput.cpp
@@ -62,15 +62,15 @@ void printfColored(TextColor color, const char *str, ...)
va_end(vl);
}
-void fprintfColored(TextColor color, FILE *file, const char *str, va_list vl)
+void fprintfColored(TextColor color, std::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");
+ std::fprintf(file, "\033[%d;%dm", bright, 30 + (color & ~TextColorBright));
+ std::vfprintf(file, str, vl);
+ std::fprintf(stdout, "\033[0m");
+ std::fprintf(stderr, "\033[0m");
} else
#elif defined(Q_OS_WIN32)
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -83,16 +83,16 @@ void fprintfColored(TextColor color, FILE *file, const char *str, va_list vl)
if (color & TextColorBright)
bgrColor += FOREGROUND_INTENSITY;
SetConsoleTextAttribute(hStdout, (csbiInfo.wAttributes & 0xf0) | bgrColor);
- vfprintf(file, str, vl);
+ std::vfprintf(file, str, vl);
SetConsoleTextAttribute(hStdout, csbiInfo.wAttributes);
} else
#endif
{
- vfprintf(file, str, vl);
+ std::vfprintf(file, str, vl);
}
}
-void fprintfColored(TextColor color, FILE *file, const char *str, ...)
+void fprintfColored(TextColor color, std::FILE *file, const char *str, ...)
{
va_list vl;
va_start(vl, str);
diff --git a/src/app/shared/logging/coloredoutput.h b/src/app/shared/logging/coloredoutput.h
index a7b145ba5..5b98a4ba2 100644
--- a/src/app/shared/logging/coloredoutput.h
+++ b/src/app/shared/logging/coloredoutput.h
@@ -66,8 +66,8 @@ enum TextColor {
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, ...);
+void fprintfColored(TextColor color, std::FILE *file, const char *str, va_list vl);
+void fprintfColored(TextColor color, std::FILE *file, const char *str, ...);
bool terminalSupportsColor();
#endif // QBS_COLOREDOUTPUT_H
diff --git a/src/app/shared/logging/consolelogger.cpp b/src/app/shared/logging/consolelogger.cpp
index 63b7dc9e3..8850ba488 100644
--- a/src/app/shared/logging/consolelogger.cpp
+++ b/src/app/shared/logging/consolelogger.cpp
@@ -64,7 +64,7 @@ void ConsoleLogSink::doPrintMessage(qbs::LoggerLevel level, const QString &messa
if (!m_enabled)
return;
- FILE * const file = level == qbs::LoggerInfo && tag != QStringLiteral("stdErr")
+ std::FILE * const file = level == qbs::LoggerInfo && tag != QStringLiteral("stdErr")
? stdout : stderr;
const QString levelTag = logLevelTag(level);
@@ -84,17 +84,17 @@ void ConsoleLogSink::doPrintMessage(qbs::LoggerLevel level, const QString &messa
static QHash<QString, TextColor> colorTable = setupColorTable();
fprintfWrapper(colorTable.value(tag, TextColorDefault), file, "%s\n",
message.toLocal8Bit().constData());
- fflush(file);
+ std::fflush(file);
}
-void ConsoleLogSink::fprintfWrapper(TextColor color, FILE *file, const char *str, ...)
+void ConsoleLogSink::fprintfWrapper(TextColor color, std::FILE *file, const char *str, ...)
{
va_list vl;
va_start(vl, str);
if (m_coloredOutputEnabled && terminalSupportsColor())
fprintfColored(color, file, str, vl);
else
- vfprintf(file, str, vl);
+ std::vfprintf(file, str, vl);
va_end(vl);
}
diff --git a/src/app/shared/logging/logging.pri b/src/app/shared/logging/logging.pri
deleted file mode 100644
index e24f33e10..000000000
--- a/src/app/shared/logging/logging.pri
+++ /dev/null
@@ -1,2 +0,0 @@
-HEADERS += $$PWD/consolelogger.h $$PWD/coloredoutput.h
-SOURCES += $$PWD/consolelogger.cpp $$PWD/coloredoutput.cpp
diff --git a/src/app/shared/logging/logging.qbs b/src/app/shared/logging/logging.qbs
new file mode 100644
index 000000000..4e0738a4f
--- /dev/null
+++ b/src/app/shared/logging/logging.qbs
@@ -0,0 +1,14 @@
+QbsStaticLibrary {
+ Depends { name: "qbscore" }
+ name: "qbsconsolelogger"
+ files: [
+ "coloredoutput.cpp",
+ "coloredoutput.h",
+ "consolelogger.cpp",
+ "consolelogger.h"
+ ]
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: "."
+ }
+}
diff --git a/src/app/shared/shared.qbs b/src/app/shared/shared.qbs
new file mode 100644
index 000000000..8f9641686
--- /dev/null
+++ b/src/app/shared/shared.qbs
@@ -0,0 +1,5 @@
+Project {
+ references: [
+ "logging/logging.qbs",
+ ]
+}
diff --git a/src/install_prefix.pri b/src/install_prefix.pri
deleted file mode 100644
index 34aef1d5f..000000000
--- a/src/install_prefix.pri
+++ /dev/null
@@ -1 +0,0 @@
-unix: isEmpty(QBS_INSTALL_PREFIX): QBS_INSTALL_PREFIX = /usr/local
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
new file mode 100644
index 000000000..f06c3168e
--- /dev/null
+++ b/src/lib/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_subdirectory(pkgconfig)
+add_subdirectory(corelib)
+add_subdirectory(msbuild)
diff --git a/src/lib/bundledlibs.pri b/src/lib/bundledlibs.pri
deleted file mode 100644
index 6ffedbbd8..000000000
--- a/src/lib/bundledlibs.pri
+++ /dev/null
@@ -1,3 +0,0 @@
-!qbs_use_bundled_qtscript:!qtHaveModule(script) {
- CONFIG += qbs_use_bundled_qtscript
-}
diff --git a/src/lib/corelib/CMakeLists.txt b/src/lib/corelib/CMakeLists.txt
new file mode 100644
index 000000000..97b5e6fad
--- /dev/null
+++ b/src/lib/corelib/CMakeLists.txt
@@ -0,0 +1,493 @@
+set(QBS_HEADERS qbs.h)
+
+set(FILE_UPDATE_SOURCES
+ changeset.cpp
+ changeset.h
+ projectfileupdater.cpp
+ projectfileupdater.h
+ qmljsrewriter.cpp
+ qmljsrewriter.h
+ )
+list_transform_prepend(FILE_UPDATE_SOURCES api/)
+
+set(API_SOURCES
+ 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
+ transformerdata.cpp
+ transformerdata_p.h
+ )
+list_transform_prepend(API_SOURCES api/)
+
+set(API_HEADERS
+ jobs.h
+ languageinfo.h
+ project.h
+ projectdata.h
+ rulecommand.h
+ runenvironment.h
+ transformerdata.h
+ )
+list_transform_prepend(API_HEADERS api/)
+
+set(BUILD_GRAPH_SOURCES
+ abstractcommandexecutor.cpp
+ abstractcommandexecutor.h
+ artifact.cpp
+ artifact.h
+ artifactcleaner.cpp
+ artifactcleaner.h
+ artifactsscriptvalue.cpp
+ artifactsscriptvalue.h
+ artifactvisitor.cpp
+ artifactvisitor.h
+ buildgraph.cpp
+ buildgraph.h
+ buildgraphnode.cpp
+ buildgraphnode.h
+ buildgraphloader.cpp
+ buildgraphloader.h
+ buildgraphvisitor.h
+ cycledetector.cpp
+ cycledetector.h
+ dependencyparametersscriptvalue.cpp
+ dependencyparametersscriptvalue.h
+ depscanner.cpp
+ depscanner.h
+ emptydirectoriesremover.cpp
+ emptydirectoriesremover.h
+ environmentscriptrunner.cpp
+ environmentscriptrunner.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
+ rawscanneddependency.cpp
+ rawscanneddependency.h
+ rawscanresults.cpp
+ rawscanresults.h
+ requestedartifacts.cpp
+ requestedartifacts.h
+ requesteddependencies.cpp
+ requesteddependencies.h
+ rescuableartifactdata.h
+ rulecommands.cpp
+ rulecommands.h
+ rulegraph.cpp
+ rulegraph.h
+ rulenode.cpp
+ rulenode.h
+ rulesapplicator.cpp
+ rulesapplicator.h
+ rulesevaluationcontext.cpp
+ rulesevaluationcontext.h
+ timestampsupdater.cpp
+ timestampsupdater.h
+ transformer.cpp
+ transformer.h
+ transformerchangetracking.cpp
+ transformerchangetracking.h
+ )
+list_transform_prepend(BUILD_GRAPH_SOURCES buildgraph/)
+
+set(BUILD_GRAPH_HEADERS buildgraph/forward_decls.h)
+
+set(GENERATORS_SOURCES
+ generatableprojectiterator.cpp
+ generatableprojectiterator.h
+ generator.cpp
+ generatordata.cpp
+ generatorutils.cpp
+ generatorutils.h
+ generatorversioninfo.cpp
+ generatorversioninfo.h
+ igeneratableprojectvisitor.h
+ ixmlnodevisitor.h
+ xmlproject.cpp
+ xmlproject.h
+ xmlprojectwriter.cpp
+ xmlprojectwriter.h
+ xmlproperty.cpp
+ xmlproperty.h
+ xmlpropertygroup.cpp
+ xmlpropertygroup.h
+ xmlworkspace.cpp
+ xmlworkspace.h
+ xmlworkspacewriter.cpp
+ xmlworkspacewriter.h
+ )
+list_transform_prepend(GENERATORS_SOURCES generators/)
+
+set(GENERATORS_HEADERS generators/generator.h generators/generatordata.h)
+
+set(JS_EXTENSIONS_SOURCES
+ environmentextension.cpp
+ file.cpp
+ fileinfoextension.cpp
+ host.cpp
+ jsextensions.cpp
+ jsextensions.h
+ moduleproperties.cpp
+ moduleproperties.h
+ pkgconfigjs.cpp
+ pkgconfigjs.h
+ process.cpp
+ temporarydir.cpp
+ textfile.cpp
+ binaryfile.cpp
+ utilitiesextension.cpp
+ domxml.cpp
+ )
+list_transform_prepend(JS_EXTENSIONS_SOURCES jsextensions/)
+
+if(APPLE)
+ set(JS_EXTENSIONS_MACOS_SOURCES
+ propertylist_darwin.mm
+ propertylistutils.h
+ propertylistutils.mm
+ )
+else()
+ set(JS_EXTENSIONS_MACOS_SOURCES propertylist.cpp)
+endif()
+list_transform_prepend(JS_EXTENSIONS_MACOS_SOURCES jsextensions/)
+
+set(LANGUAGE_SOURCES
+ artifactproperties.cpp
+ artifactproperties.h
+ asttools.cpp
+ asttools.h
+ builtindeclarations.cpp
+ builtindeclarations.h
+ deprecationinfo.h
+ evaluator.cpp
+ evaluator.h
+ filecontext.cpp
+ filecontext.h
+ filecontextbase.cpp
+ filecontextbase.h
+ filetags.cpp
+ filetags.h
+ identifiersearch.cpp
+ identifiersearch.h
+ item.cpp
+ item.h
+ itemdeclaration.cpp
+ itemdeclaration.h
+ itemobserver.h
+ itempool.cpp
+ itempool.h
+ itemtype.h
+ jsimports.h
+ language.cpp
+ language.h
+ moduleproviderinfo.h
+ preparescriptobserver.cpp
+ preparescriptobserver.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.cpp
+ scriptpropertyobserver.h
+ value.cpp
+ value.h
+ )
+list_transform_prepend(LANGUAGE_SOURCES language/)
+
+set(LANGUAGE_HEADERS language/forward_decls.h)
+
+set(LOADER_SOURCES
+ astimportshandler.cpp
+ astimportshandler.h
+ astpropertiesitemhandler.cpp
+ astpropertiesitemhandler.h
+ dependenciesresolver.cpp
+ dependenciesresolver.h
+ groupshandler.cpp
+ groupshandler.h
+ itemreader.cpp
+ itemreader.h
+ itemreaderastvisitor.cpp
+ itemreaderastvisitor.h
+ itemreadervisitorstate.cpp
+ itemreadervisitorstate.h
+ loaderutils.cpp
+ loaderutils.h
+ localprofiles.cpp
+ localprofiles.h
+ moduleinstantiator.cpp
+ moduleinstantiator.h
+ moduleloader.cpp
+ moduleloader.h
+ modulepropertymerger.cpp
+ modulepropertymerger.h
+ moduleproviderloader.cpp
+ moduleproviderloader.h
+ probesresolver.cpp
+ probesresolver.h
+ productitemmultiplexer.cpp
+ productitemmultiplexer.h
+ productresolver.cpp
+ productresolver.h
+ productscollector.cpp
+ productscollector.h
+ productsresolver.cpp
+ productsresolver.h
+ projectresolver.cpp
+ projectresolver.h
+ )
+list_transform_prepend(LOADER_SOURCES loader/)
+
+set(LOGGING_SOURCES
+ categories.cpp
+ categories.h
+ ilogsink.cpp
+ logger.cpp
+ logger.h
+ translator.h
+ )
+list_transform_prepend(LOGGING_SOURCES logging/)
+
+set(LOGGING_HEADERS logging/ilogsink.h)
+
+set(PARSER_SOURCES
+ 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
+ )
+list_transform_prepend(PARSER_SOURCES parser/)
+
+set(TOOLS_SOURCES
+ architectures.cpp
+ buildgraphlocker.cpp
+ buildgraphlocker.h
+ buildoptions.cpp
+ clangclinfo.cpp
+ clangclinfo.h
+ cleanoptions.cpp
+ codelocation.cpp
+ commandechomode.cpp
+ deprecationwarningmode.cpp
+ dynamictypecheck.h
+ error.cpp
+ executablefinder.cpp
+ executablefinder.h
+ fileinfo.cpp
+ fileinfo.h
+ filesaver.cpp
+ filesaver.h
+ filetime.cpp
+ filetime.h
+ generateoptions.cpp
+ hostosinfo.h
+ id.cpp
+ id.h
+ iosutils.h
+ joblimits.cpp
+ jsliterals.cpp
+ jsliterals.h
+ jsonhelper.h
+ installoptions.cpp
+ launcherinterface.cpp
+ launcherinterface.h
+ launcherpackets.cpp
+ launcherpackets.h
+ launchersocket.cpp
+ launchersocket.h
+ msvcinfo.cpp
+ msvcinfo.h
+ pathutils.h
+ pimpl.h
+ persistence.cpp
+ persistence.h
+ porting.h
+ preferences.cpp
+ processresult.cpp
+ processresult_p.h
+ processutils.cpp
+ processutils.h
+ profile.cpp
+ profiling.cpp
+ profiling.h
+ progressobserver.cpp
+ progressobserver.h
+ projectgeneratormanager.cpp
+ propagate_const.h
+ qbsassert.cpp
+ qbsassert.h
+ qbspluginmanager.cpp
+ qbspluginmanager.h
+ qbsprocess.cpp
+ qbsprocess.h
+ qttools.cpp
+ qttools.h
+ scannerpluginmanager.cpp
+ scannerpluginmanager.h
+ scripttools.cpp
+ scripttools.h
+ set.h
+ settings.cpp
+ settingscreator.cpp
+ settingscreator.h
+ settingsmodel.cpp
+ settingsrepresentation.cpp
+ setupprojectparameters.cpp
+ shellutils.cpp
+ shellutils.h
+ stlutils.h
+ stringconstants.h
+ stringutils.h
+ toolchains.cpp
+ version.cpp
+ visualstudioversioninfo.cpp
+ visualstudioversioninfo.h
+ vsenvironmentdetector.cpp
+ vsenvironmentdetector.h
+ weakpointer.h
+ )
+list_transform_prepend(TOOLS_SOURCES tools/)
+
+set(TOOLS_HEADERS
+ architectures.h
+ buildoptions.h
+ cleanoptions.h
+ codelocation.h
+ commandechomode.h
+ deprecationwarningmode.h
+ error.h
+ generateoptions.h
+ installoptions.h
+ joblimits.h
+ mutexdata.h
+ preferences.h
+ processresult.h
+ profile.h
+ projectgeneratormanager.h
+ qbs_export.h
+ settings.h
+ settingsmodel.h
+ settingsrepresentation.h
+ setupprojectparameters.h
+ toolchains.h
+ version.h
+ )
+list_transform_prepend(TOOLS_HEADERS tools/)
+
+set(EXTERNAL_DEPENDS "")
+if(APPLE)
+ set(TOOLS_MACOS_SOURCES
+ applecodesignutils.cpp
+ applecodesignutils.h
+ )
+ list_transform_prepend(TOOLS_MACOS_SOURCES tools/)
+ set(EXTERNAL_DEPENDS "-framework Foundation" "-framework Security")
+endif()
+
+if(WIN32)
+ set(EXTERNAL_DEPENDS "psapi" "shell32")
+endif()
+
+add_qbs_library(qbscore
+ DEFINES
+ "QBS_VERSION=\"${QBS_VERSION}\""
+ "QBS_RELATIVE_LIBEXEC_PATH=\"${QBS_RELATIVE_LIBEXEC_PATH}\""
+ "QBS_LIBRARY"
+ ${QBS_UNIT_TESTS_DEFINES}
+ DEPENDS
+ Qt${QT_VERSION_MAJOR}::CorePrivate
+ Qt${QT_VERSION_MAJOR}::Network
+ Qt${QT_VERSION_MAJOR}::Xml
+ Qt6Core5Compat
+ qbspkgconfig
+ qbsquickjs
+ PUBLIC_DEPENDS
+ Qt${QT_VERSION_MAJOR}::Core
+ ${EXTERNAL_DEPENDS}
+ INCLUDES
+ "${CMAKE_CURRENT_SOURCE_DIR}/../.."
+ SOURCES
+ ${QBS_HEADERS}
+ ${FILE_UPDATE_SOURCES}
+ ${API_SOURCES}
+ ${API_HEADERS}
+ ${BUILD_GRAPH_SOURCES}
+ ${BUILD_GRAPH_HEADERS}
+ ${GENERATORS_SOURCES}
+ ${GENERATORS_HEADERS}
+ ${JS_EXTENSIONS_SOURCES}
+ ${JS_EXTENSIONS_MACOS_SOURCES}
+ ${LANGUAGE_SOURCES}
+ ${LANGUAGE_HEADERS}
+ ${LOADER_SOURCES}
+ ${LOGGING_SOURCES}
+ ${LOGGING_HEADERS}
+ ${PARSER_SOURCES}
+ ${TOOLS_SOURCES}
+ ${TOOLS_HEADERS}
+ ${TOOLS_MACOS_SOURCES}
+ )
+
+# not sure if there's a better way to do this
+if(INSTALL_PUBLIC_HEADERS)
+ install(FILES ${QBS_HEADERS} DESTINATION ${QBS_HEADERS_INSTALL_DIR})
+ install(FILES ${API_HEADERS} DESTINATION ${QBS_HEADERS_INSTALL_DIR}/api)
+ install(FILES ${BUILD_GRAPH_HEADERS} DESTINATION ${QBS_HEADERS_INSTALL_DIR}/buildgraph)
+ install(FILES ${GENERATORS_HEADERS} DESTINATION ${QBS_HEADERS_INSTALL_DIR}/generators)
+ install(FILES ${LOGGING_HEADERS} DESTINATION ${QBS_HEADERS_INSTALL_DIR}/logging)
+ install(FILES ${LANGUAGE_HEADERS} DESTINATION ${QBS_HEADERS_INSTALL_DIR}/language)
+ install(FILES ${TOOLS_HEADERS} DESTINATION ${QBS_HEADERS_INSTALL_DIR}/tools)
+endif()
diff --git a/src/lib/corelib/api/api.pri b/src/lib/corelib/api/api.pri
deleted file mode 100644
index ddb1171d4..000000000
--- a/src/lib/corelib/api/api.pri
+++ /dev/null
@@ -1,52 +0,0 @@
-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 \
- $$PWD/transformerdata.h \
- $$PWD/transformerdata_p.h
-
-SOURCES += \
- $$PWD/internaljobs.cpp \
- $$PWD/runenvironment.cpp \
- $$PWD/projectdata.cpp \
- $$PWD/jobs.cpp \
- $$PWD/languageinfo.cpp \
- $$PWD/project.cpp \
- $$PWD/rulecommand.cpp \
- $$PWD/transformerdata.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 \
- $$PWD/transformerdata.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
index 5d375fd65..218e18fb2 100644
--- a/src/lib/corelib/api/changeset.cpp
+++ b/src/lib/corelib/api/changeset.cpp
@@ -39,17 +39,15 @@
#include "changeset.h"
-#include <QtGui/qtextcursor.h>
-
namespace QbsQmlJS {
ChangeSet::ChangeSet()
- : m_string(nullptr), m_cursor(nullptr), m_error(false)
+ : m_string(nullptr), m_error(false)
{
}
-ChangeSet::ChangeSet(const QList<EditOp> &operations)
- : m_string(nullptr), m_cursor(nullptr), m_operationList(operations), m_error(false)
+ChangeSet::ChangeSet(QList<EditOp> operations)
+ : m_string(nullptr), m_operationList(std::move(operations)), m_error(false)
{
}
@@ -62,9 +60,8 @@ static bool overlaps(int posA, int lengthA, int posB, int lengthB) {
|| (posA <= posB && posA + lengthA > posB)
// A contained in B
|| (posB < posA && posB + lengthB > posA + lengthA);
- } else {
- return (posB > posA && posB < posA + lengthA);
}
+ return (posB > posA && posB < posA + lengthA);
}
bool ChangeSet::hasOverlap(int pos, int length)
@@ -131,7 +128,6 @@ QList<ChangeSet::EditOp> ChangeSet::operationList() const
void ChangeSet::clear()
{
m_string = nullptr;
- m_cursor = nullptr;
m_operationList.clear();
m_error = false;
}
@@ -274,10 +270,6 @@ void ChangeSet::doReplace(const EditOp &replace_helper, QList<EditOp> *replaceLi
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);
}
}
@@ -348,21 +340,10 @@ void ChangeSet::apply(QString *s)
m_string = nullptr;
}
-void ChangeSet::apply(QTextCursor *textCursor)
-{
- m_cursor = textCursor;
- apply_helper();
- m_cursor = nullptr;
-}
-
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 {};
}
@@ -380,18 +361,12 @@ void ChangeSet::apply_helper()
}
// execute replaces
- if (m_cursor)
- m_cursor->beginEditBlock();
-
while (!replaceList.empty()) {
const EditOp cmd(replaceList.front());
replaceList.removeFirst();
doReplace(cmd, &replaceList);
}
-
- if (m_cursor)
- m_cursor->endEditBlock();
}
-} // namespace Internal {
+} // namespace QbsQmlJS
diff --git a/src/lib/corelib/api/changeset.h b/src/lib/corelib/api/changeset.h
index 13d3908d4..9182cb8e4 100644
--- a/src/lib/corelib/api/changeset.h
+++ b/src/lib/corelib/api/changeset.h
@@ -43,8 +43,6 @@
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
-QT_FORWARD_DECLARE_CLASS(QTextCursor)
-
namespace QbsQmlJS {
class ChangeSet
@@ -86,7 +84,7 @@ public:
public:
ChangeSet();
- ChangeSet(const QList<EditOp> &operations);
+ ChangeSet(QList<EditOp> operations);
bool empty() const;
@@ -109,7 +107,6 @@ public:
bool hadErrors();
void apply(QString *s);
- void apply(QTextCursor *textCursor);
private:
// length-based API.
@@ -129,7 +126,6 @@ private:
private:
QString *m_string;
- QTextCursor *m_cursor;
QList<EditOp> m_operationList;
bool m_error;
diff --git a/src/lib/corelib/api/internaljobs.cpp b/src/lib/corelib/api/internaljobs.cpp
index 3b68e01b5..35766efa4 100644
--- a/src/lib/corelib/api/internaljobs.cpp
+++ b/src/lib/corelib/api/internaljobs.cpp
@@ -49,7 +49,8 @@
#include <buildgraph/productinstaller.h>
#include <buildgraph/rulesevaluationcontext.h>
#include <language/language.h>
-#include <language/loader.h>
+#include <language/scriptengine.h>
+#include <loader/projectresolver.h>
#include <logging/logger.h>
#include <logging/translator.h>
#include <tools/buildgraphlocker.h>
@@ -59,7 +60,6 @@
#include <tools/preferences.h>
#include <tools/qbsassert.h>
-#include <QtCore/qeventloop.h>
#include <QtCore/qtimer.h>
#include <mutex>
@@ -77,6 +77,8 @@ public:
{
std::lock_guard<std::mutex> lock(m_cancelMutex);
m_canceled = true;
+ for (ScriptEngine * const engine : scriptEngines())
+ engine->cancel();
}
private:
@@ -194,14 +196,14 @@ InternalJobThreadWrapper::InternalJobThreadWrapper(InternalJob *synchronousJob,
InternalJobThreadWrapper::~InternalJobThreadWrapper()
{
if (m_running) {
- QEventLoop loop;
- connect(m_job, &InternalJob::finished, &loop, &QEventLoop::quit);
cancel();
- loop.exec();
+ while (m_running)
+ QCoreApplication::processEvents();
}
m_thread.quit();
m_thread.wait();
delete m_job;
+ m_job = nullptr;
}
void InternalJobThreadWrapper::start()
@@ -322,23 +324,21 @@ void InternalSetupProjectJob::execute()
void InternalSetupProjectJob::resolveProjectFromScratch(ScriptEngine *engine)
{
- Loader loader(engine, logger());
- loader.setSearchPaths(m_parameters.searchPaths());
- loader.setProgressObserver(observer());
- m_newProject = loader.loadProject(m_parameters);
+ ProjectResolver resolver(m_parameters, engine, logger());
+ resolver.setProgressObserver(observer());
+ m_newProject = resolver.resolve();
QBS_CHECK(m_newProject);
}
void InternalSetupProjectJob::resolveBuildDataFromScratch(const RulesEvaluationContextPtr &evalContext)
{
- TimedActivityLogger resolveLogger(logger(), QStringLiteral("Resolving build project"), timed());
BuildDataResolver(logger()).resolveBuildData(m_newProject, evalContext);
}
BuildGraphLoadResult InternalSetupProjectJob::restoreProject(const RulesEvaluationContextPtr &evalContext)
{
BuildGraphLoader bgLoader(logger());
- const BuildGraphLoadResult loadResult
+ BuildGraphLoadResult loadResult
= bgLoader.load(m_existingProject, m_parameters, evalContext);
return loadResult;
}
@@ -351,7 +351,7 @@ BuildGraphTouchingJob::BuildGraphTouchingJob(const Logger &logger, QObject *pare
BuildGraphTouchingJob::~BuildGraphTouchingJob() = default;
void BuildGraphTouchingJob::setup(const TopLevelProjectPtr &project,
- const QList<ResolvedProductPtr> &products, bool dryRun)
+ const QVector<ResolvedProductPtr> &products, bool dryRun)
{
m_project = project;
m_products = products;
@@ -370,14 +370,14 @@ InternalBuildJob::InternalBuildJob(const Logger &logger, QObject *parent)
}
void InternalBuildJob::build(const TopLevelProjectPtr &project,
- const QList<ResolvedProductPtr> &products, const BuildOptions &buildOptions)
+ const QVector<ResolvedProductPtr> &products, const BuildOptions &buildOptions)
{
setup(project, products, buildOptions.dryRun());
setTimed(buildOptions.logElapsedTime());
m_executor = new Executor(logger());
m_executor->setProject(project);
- m_executor->setProducts(std::vector<ResolvedProductPtr>(products.cbegin(), products.cend()));
+ m_executor->setProducts(products);
m_executor->setBuildOptions(buildOptions);
m_executor->setProgressObserver(observer());
@@ -414,7 +414,8 @@ InternalCleanJob::InternalCleanJob(const Logger &logger, QObject *parent)
}
void InternalCleanJob::init(const TopLevelProjectPtr &project,
- const QList<ResolvedProductPtr> &products, const CleanOptions &options)
+ const QVector<ResolvedProductPtr> &products,
+ const CleanOptions &options)
{
setup(project, products, options.dryRun());
setTimed(options.logElapsedTime());
@@ -442,7 +443,7 @@ InternalInstallJob::InternalInstallJob(const Logger &logger)
InternalInstallJob::~InternalInstallJob() = default;
void InternalInstallJob::init(const TopLevelProjectPtr &project,
- const std::vector<ResolvedProductPtr> &products, const InstallOptions &options)
+ const QVector<ResolvedProductPtr> &products, const InstallOptions &options)
{
m_project = project;
m_products = products;
diff --git a/src/lib/corelib/api/internaljobs.h b/src/lib/corelib/api/internaljobs.h
index 58127eb05..d66835941 100644
--- a/src/lib/corelib/api/internaljobs.h
+++ b/src/lib/corelib/api/internaljobs.h
@@ -151,7 +151,7 @@ class BuildGraphTouchingJob : public InternalJob
{
Q_OBJECT
public:
- const QList<ResolvedProductPtr> &products() const { return m_products; }
+ const QVector<ResolvedProductPtr> &products() const { return m_products; }
const TopLevelProjectPtr &project() const { return m_project; }
signals:
@@ -162,13 +162,13 @@ protected:
BuildGraphTouchingJob(const Logger &logger, QObject *parent = nullptr);
~BuildGraphTouchingJob() override;
- void setup(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ void setup(const TopLevelProjectPtr &project, const QVector<ResolvedProductPtr> &products,
bool dryRun);
void storeBuildGraph();
private:
TopLevelProjectPtr m_project;
- QList<ResolvedProductPtr> m_products;
+ QVector<ResolvedProductPtr> m_products;
bool m_dryRun;
};
@@ -179,7 +179,7 @@ class InternalBuildJob : public BuildGraphTouchingJob
public:
InternalBuildJob(const Logger &logger, QObject *parent = nullptr);
- void build(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ void build(const TopLevelProjectPtr &project, const QVector<ResolvedProductPtr> &products,
const BuildOptions &buildOptions);
private:
@@ -196,8 +196,8 @@ class InternalCleanJob : public BuildGraphTouchingJob
public:
InternalCleanJob(const Logger &logger, QObject *parent = nullptr);
- void init(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
- const CleanOptions &options);
+ void init(const TopLevelProjectPtr &project, const QVector<ResolvedProductPtr> &products,
+ const CleanOptions &options);
private:
void start() override;
@@ -213,14 +213,14 @@ public:
InternalInstallJob(const Logger &logger);
~InternalInstallJob() override;
- void init(const TopLevelProjectPtr &project, const std::vector<ResolvedProductPtr> &products,
+ void init(const TopLevelProjectPtr &project, const QVector<ResolvedProductPtr> &products,
const InstallOptions &options);
private:
void start() override;
TopLevelProjectPtr m_project;
- std::vector<ResolvedProductPtr> m_products;
+ QVector<ResolvedProductPtr> m_products;
InstallOptions m_options;
};
diff --git a/src/lib/corelib/api/jobs.cpp b/src/lib/corelib/api/jobs.cpp
index 32b7accc7..10c96bfee 100644
--- a/src/lib/corelib/api/jobs.cpp
+++ b/src/lib/corelib/api/jobs.cpp
@@ -231,7 +231,7 @@ Project SetupProjectJob::project() const
{
auto const wrapper = qobject_cast<const InternalJobThreadWrapper *>(internalJob());
auto const job = qobject_cast<const InternalSetupProjectJob *>(wrapper->synchronousJob());
- return Project(job->project(), job->logger());
+ return {job->project(), job->logger()};
}
void SetupProjectJob::resolve(const Project &existingProject,
@@ -309,7 +309,7 @@ BuildJob::BuildJob(const Logger &logger, QObject *parent)
this, &BuildJob::reportProcessResult);
}
-void BuildJob::build(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+void BuildJob::build(const TopLevelProjectPtr &project, const QVector<ResolvedProductPtr> &products,
const BuildOptions &options)
{
if (!lockProject(project))
@@ -340,7 +340,7 @@ CleanJob::CleanJob(const Logger &logger, QObject *parent)
{
}
-void CleanJob::clean(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+void CleanJob::clean(const TopLevelProjectPtr &project, const QVector<ResolvedProductPtr> &products,
const qbs::CleanOptions &options)
{
if (!lockProject(project))
@@ -361,15 +361,14 @@ InstallJob::InstallJob(const Logger &logger, QObject *parent)
}
void InstallJob::install(const TopLevelProjectPtr &project,
- const QList<ResolvedProductPtr> &products, const InstallOptions &options)
+ const QVector<ResolvedProductPtr> &products,
+ const InstallOptions &options)
{
if (!lockProject(project))
return;
auto wrapper = qobject_cast<InternalJobThreadWrapper *>(internalJob());
auto installJob = qobject_cast<InternalInstallJob *>(wrapper->synchronousJob());
- installJob->init(project,
- std::vector<ResolvedProductPtr>(products.cbegin(), products.cend()),
- options);
+ installJob->init(project, products, options);
wrapper->start();
}
diff --git a/src/lib/corelib/api/jobs.h b/src/lib/corelib/api/jobs.h
index 36c6b7a80..64929489e 100644
--- a/src/lib/corelib/api/jobs.h
+++ b/src/lib/corelib/api/jobs.h
@@ -136,7 +136,7 @@ private:
BuildJob(const Internal::Logger &logger, QObject *parent);
void build(const Internal::TopLevelProjectPtr &project,
- const QList<qbs::Internal::ResolvedProductPtr> &products,
+ const QVector<Internal::ResolvedProductPtr> &products,
const BuildOptions &options);
void handleLauncherError(const ErrorInfo &error);
@@ -153,7 +153,7 @@ private:
CleanJob(const Internal::Logger &logger, QObject *parent);
void clean(const Internal::TopLevelProjectPtr &project,
- const QList<Internal::ResolvedProductPtr> &products, const CleanOptions &options);
+ const QVector<Internal::ResolvedProductPtr> &products, const CleanOptions &options);
};
class QBS_EXPORT InstallJob : public AbstractJob
@@ -164,7 +164,8 @@ private:
InstallJob(const Internal::Logger &logger, QObject *parent);
void install(const Internal::TopLevelProjectPtr &project,
- const QList<Internal::ResolvedProductPtr> &products, const InstallOptions &options);
+ const QVector<Internal::ResolvedProductPtr> &products,
+ const InstallOptions &options);
};
} // namespace qbs
diff --git a/src/lib/corelib/api/languageinfo.cpp b/src/lib/corelib/api/languageinfo.cpp
index cb023927a..f20a68ac3 100644
--- a/src/lib/corelib/api/languageinfo.cpp
+++ b/src/lib/corelib/api/languageinfo.cpp
@@ -61,29 +61,27 @@ std::string LanguageInfo::qmlTypeInfo()
// Individual Components:
auto typeNames = builtins.allTypeNames();
typeNames.sort();
- for (const QString &typeName : qAsConst(typeNames)) {
- QByteArray utf8TypeName = typeName.toUtf8();
+ for (const QString &typeName : std::as_const(typeNames)) {
+ const auto typeNameString = typeName.toStdString();
result.append(" Component {\n");
- result.append(QByteArray(" name: \"") + utf8TypeName + QByteArray("\"\n"));
+ result.append(" name: \"" + typeNameString + "\"\n");
result.append(" exports: [ \"qbs/");
- result.append(utf8TypeName);
+ result.append(typeNameString);
result.append(" ");
const auto v = builtins.languageVersion();
result.append(QStringLiteral("%1.%2")
- .arg(v.majorVersion()).arg(v.minorVersion()).toUtf8());
+ .arg(v.majorVersion()).arg(v.minorVersion()).toUtf8().data());
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();
- });
- for (const Internal::PropertyDeclaration &property : qAsConst(properties)) {
+ const auto properties = Internal::sorted(
+ itemDecl.properties(),
+ [](const auto &lhs, const auto &rhs) { return lhs.name() < rhs.name(); });
+ for (const Internal::PropertyDeclaration &property : properties) {
result.append(" Property { name: \"");
- result.append(property.name().toUtf8());
+ result.append(property.name().toUtf8().data());
result.append("\"; ");
switch (property.type()) {
case qbs::Internal::PropertyDeclaration::UnknownType:
diff --git a/src/lib/corelib/api/project.cpp b/src/lib/corelib/api/project.cpp
index d8525a4b6..53c711b49 100644
--- a/src/lib/corelib/api/project.cpp
+++ b/src/lib/corelib/api/project.cpp
@@ -39,13 +39,10 @@
#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 "projectfileupdater.h"
#include "propertymap_p.h"
#include "rulecommand_p.h"
#include "runenvironment.h"
@@ -64,7 +61,6 @@
#include <buildgraph/timestampsupdater.h>
#include <buildgraph/transformer.h>
#include <language/language.h>
-#include <language/projectresolver.h>
#include <language/propertymapinternal.h>
#include <logging/logger.h>
#include <logging/translator.h>
@@ -82,7 +78,7 @@
#include <tools/qttools.h>
#include <QtCore/qdir.h>
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qshareddata.h>
#include <mutex>
@@ -131,22 +127,22 @@ ProjectData ProjectPrivate::projectData()
return m_projectData;
}
-static void addDependencies(QList<ResolvedProductPtr> &products)
+static void addDependencies(QVector<ResolvedProductPtr> &products)
{
for (int i = 0; i < products.size(); ++i) {
const ResolvedProductPtr &product = products.at(i);
- for (const ResolvedProductPtr &dependency : qAsConst(product->dependencies)) {
+ for (const ResolvedProductPtr &dependency : std::as_const(product->dependencies)) {
if (!products.contains(dependency))
products.push_back(dependency);
}
}
}
-BuildJob *ProjectPrivate::buildProducts(const QList<ResolvedProductPtr> &products,
+BuildJob *ProjectPrivate::buildProducts(const QVector<ResolvedProductPtr> &products,
const BuildOptions &options, bool needsDepencencyResolving,
QObject *jobOwner)
{
- QList<ResolvedProductPtr> productsToBuild = products;
+ QVector<ResolvedProductPtr> productsToBuild = products;
if (needsDepencencyResolving)
addDependencies(productsToBuild);
@@ -156,7 +152,7 @@ BuildJob *ProjectPrivate::buildProducts(const QList<ResolvedProductPtr> &product
return job;
}
-CleanJob *ProjectPrivate::cleanProducts(const QList<ResolvedProductPtr> &products,
+CleanJob *ProjectPrivate::cleanProducts(const QVector<ResolvedProductPtr> &products,
const CleanOptions &options, QObject *jobOwner)
{
const auto job = new CleanJob(logger, jobOwner);
@@ -165,10 +161,10 @@ CleanJob *ProjectPrivate::cleanProducts(const QList<ResolvedProductPtr> &product
return job;
}
-InstallJob *ProjectPrivate::installProducts(const QList<ResolvedProductPtr> &products,
+InstallJob *ProjectPrivate::installProducts(const QVector<ResolvedProductPtr> &products,
const InstallOptions &options, bool needsDepencencyResolving, QObject *jobOwner)
{
- QList<ResolvedProductPtr> productsToInstall = products;
+ QVector<ResolvedProductPtr> productsToInstall = products;
if (needsDepencencyResolving)
addDependencies(productsToInstall);
const auto job = new InstallJob(logger, jobOwner);
@@ -177,9 +173,9 @@ InstallJob *ProjectPrivate::installProducts(const QList<ResolvedProductPtr> &pro
return job;
}
-QList<ResolvedProductPtr> ProjectPrivate::internalProducts(const QList<ProductData> &products) const
+QVector<ResolvedProductPtr> ProjectPrivate::internalProducts(const QList<ProductData> &products) const
{
- QList<ResolvedProductPtr> internalProducts;
+ QVector<ResolvedProductPtr> internalProducts;
for (const ProductData &product : products) {
if (product.isEnabled())
internalProducts.push_back(internalProduct(product));
@@ -187,20 +183,21 @@ QList<ResolvedProductPtr> ProjectPrivate::internalProducts(const QList<ProductDa
return internalProducts;
}
-static QList<ResolvedProductPtr> enabledInternalProducts(const ResolvedProjectConstPtr &project,
+static QVector<ResolvedProductPtr> enabledInternalProducts(const ResolvedProjectConstPtr &project,
bool includingNonDefault)
{
- QList<ResolvedProductPtr> products;
+ QVector<ResolvedProductPtr> products;
for (const ResolvedProductPtr &p : project->products) {
if (p->enabled && (includingNonDefault || p->builtByDefault()))
products.push_back(p);
}
- for (const ResolvedProjectConstPtr &subProject : qAsConst(project->subProjects))
+ for (const auto &subProject : std::as_const(project->subProjects))
products << enabledInternalProducts(subProject, includingNonDefault);
return products;
}
-QList<ResolvedProductPtr> ProjectPrivate::allEnabledInternalProducts(bool includingNonDefault) const
+QVector<ResolvedProductPtr> ProjectPrivate::allEnabledInternalProducts(
+ bool includingNonDefault) const
{
return enabledInternalProducts(internalProject, includingNonDefault);
}
@@ -218,7 +215,7 @@ static ResolvedProductPtr internalProductForProject(const ResolvedProjectConstPt
if (matches(product, resolvedProduct))
return resolvedProduct;
}
- for (const ResolvedProjectConstPtr &subProject : qAsConst(project->subProjects)) {
+ for (const auto &subProject : std::as_const(project->subProjects)) {
const ResolvedProductPtr &p = internalProductForProject(subProject, product);
if (p)
return p;
@@ -233,8 +230,7 @@ ResolvedProductPtr ProjectPrivate::internalProduct(const ProductData &product) c
ProductData ProjectPrivate::findProductData(const ProductData &product) const
{
- const auto products = m_projectData.allProducts();
- for (const ProductData &p : products) {
+ for (const ProductData &p : m_projectData.allProducts()) {
if (p.name() == product.name()
&& p.profile() == product.profile()
&& p.multiplexConfigurationId() == product.multiplexConfigurationId()) {
@@ -247,8 +243,7 @@ ProductData ProjectPrivate::findProductData(const ProductData &product) const
QList<ProductData> ProjectPrivate::findProductsByName(const QString &name) const
{
QList<ProductData> list;
- const auto products = m_projectData.allProducts();
- for (const ProductData &p : products) {
+ for (const ProductData &p : m_projectData.allProducts()) {
if (p.name() == name)
list.push_back(p);
}
@@ -257,8 +252,7 @@ QList<ProductData> ProjectPrivate::findProductsByName(const QString &name) const
GroupData ProjectPrivate::findGroupData(const ProductData &product, const QString &groupName) const
{
- const auto groups = product.groups();
- for (const GroupData &g : groups) {
+ for (const GroupData &g : product.groups()) {
if (g.name() == groupName)
return g;
}
@@ -272,17 +266,13 @@ GroupData ProjectPrivate::createGroupDataFromGroup(const GroupPtr &resolvedGroup
group.d->name = resolvedGroup->name;
group.d->prefix = resolvedGroup->prefix;
group.d->location = resolvedGroup->location;
- for (const SourceArtifactConstPtr &sa : resolvedGroup->files) {
+ for (const auto &sa : resolvedGroup->files) {
ArtifactData artifact = createApiSourceArtifact(sa);
setupInstallData(artifact, product);
- group.d->sourceArtifacts.push_back(artifact);
- }
- if (resolvedGroup->wildcards) {
- for (const SourceArtifactConstPtr &sa : resolvedGroup->wildcards->files) {
- ArtifactData artifact = createApiSourceArtifact(sa);
- setupInstallData(artifact, product);
+ if (sa->fromWildcard)
group.d->sourceArtifactsFromWildcards.push_back(artifact);
- }
+ else
+ group.d->sourceArtifacts.push_back(artifact);
}
std::sort(group.d->sourceArtifacts.begin(),
group.d->sourceArtifacts.end());
@@ -345,7 +335,6 @@ void ProjectPrivate::setupInstallData(ArtifactData &artifact,
}
}
-#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES
void ProjectPrivate::addGroup(const ProductData &product, const QString &groupName)
{
if (groupName.isEmpty())
@@ -355,7 +344,7 @@ void ProjectPrivate::addGroup(const ProductData &product, const QString &groupNa
QList<ProductData> products = findProductsByName(product.name());
if (products.empty())
throw ErrorInfo(Tr::tr("Product '%1' does not exist.").arg(product.name()));
- const QList<ResolvedProductPtr> resolvedProducts = internalProducts(products);
+ const auto resolvedProducts = internalProducts(products);
QBS_CHECK(products.size() == resolvedProducts.size());
for (const GroupPtr &resolvedGroup : resolvedProducts.front()->groups) {
@@ -367,28 +356,6 @@ void ProjectPrivate::addGroup(const ProductData &product, const QString &groupNa
ProjectFileGroupInserter groupInserter(products.front(), 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.front().name()); // These are new objects.
- QBS_CHECK(products.size() == resolvedProducts.size());
- for (int i = 0; i < products.size(); ++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;
- QList<GroupData> &groupData = products.at(i).d->groups;
- groupData << createGroupDataFromGroup(resolvedGroup, resolvedProducts.at(i));
- std::sort(groupData.begin(), groupData.end());
- }
}
ProjectPrivate::GroupUpdateContext ProjectPrivate::getGroupContext(const ProductData &product,
@@ -403,7 +370,7 @@ ProjectPrivate::GroupUpdateContext ProjectPrivate::getGroupContext(const Product
context.resolvedProducts = internalProducts(context.products);
const QString groupName = group.isValid() ? group.name() : product.name();
- for (const ResolvedProductPtr &p : qAsConst(context.resolvedProducts)) {
+ for (const ResolvedProductPtr &p : std::as_const(context.resolvedProducts)) {
for (const GroupPtr &g : p->groups) {
if (g->name == groupName) {
context.resolvedGroups << g;
@@ -413,7 +380,7 @@ ProjectPrivate::GroupUpdateContext ProjectPrivate::getGroupContext(const Product
}
if (context.resolvedGroups.empty())
throw ErrorInfo(Tr::tr("Group '%1' does not exist.").arg(groupName));
- for (const ProductData &p : qAsConst(context.products)) {
+ for (const ProductData &p : std::as_const(context.products)) {
const GroupData &g = findGroupData(p, groupName);
QBS_CHECK(p.isValid());
context.groups << g;
@@ -428,7 +395,7 @@ static bool matchesWildcard(const QString &filePath, const GroupConstPtr &group)
{
if (!group->wildcards)
return false;
- for (const QString &pattern : qAsConst(group->wildcards->patterns)) {
+ for (const QString &pattern : std::as_const(group->wildcards->patterns)) {
QString fullPattern;
if (QFileInfo(group->prefix).isAbsolute()) {
fullPattern = group->prefix;
@@ -438,7 +405,7 @@ static bool matchesWildcard(const QString &filePath, const GroupConstPtr &group)
}
fullPattern.append(QLatin1Char('/')).append(pattern);
fullPattern = QDir::cleanPath(fullPattern);
- if (QRegExp(fullPattern, Qt::CaseSensitive, QRegExp::Wildcard).exactMatch(filePath))
+ if (QRegularExpression(QRegularExpression::wildcardToRegularExpression(fullPattern)).match(filePath).hasMatch())
return true;
}
return false;
@@ -484,15 +451,6 @@ ProjectPrivate::FileListUpdateContext ProjectPrivate::getFileListContext(const P
return filesContext;
}
-static SourceArtifactPtr createSourceArtifact(const QString &filePath,
- const ResolvedProductPtr &product, const GroupPtr &group, bool wildcard)
-{
- const SourceArtifactPtr artifact
- = ProjectResolver::createSourceArtifact(product, filePath, group, wildcard);
- ProjectResolver::applyFileTaggers(artifact, product);
- return artifact;
-}
-
void ProjectPrivate::addFiles(const ProductData &product, const GroupData &group,
const QStringList &filePaths)
{
@@ -501,9 +459,9 @@ void ProjectPrivate::addFiles(const ProductData &product, const GroupData &group
// We do not check for entries in other groups, because such doublettes might be legitimate
// due to conditions.
- for (const GroupPtr &group : qAsConst(groupContext.resolvedGroups)) {
- for (const QString &filePath : qAsConst(filesContext.absoluteFilePaths)) {
- for (const SourceArtifactConstPtr &sa : group->files) {
+ for (const GroupPtr &group : std::as_const(groupContext.resolvedGroups)) {
+ for (const QString &filePath : std::as_const(filesContext.absoluteFilePaths)) {
+ for (const auto &sa : group->files) {
if (sa->absoluteFilePath == filePath) {
throw ErrorInfo(Tr::tr("File '%1' already exists in group '%2'.")
.arg(filePath, group->name));
@@ -516,57 +474,6 @@ void ProjectPrivate::addFiles(const ProductData &product, const GroupData &group
group.isValid() ? groupContext.groups.front() : GroupData(),
filesContext.relativeFilePaths);
adder.apply();
-
- m_projectData.d.detach();
- updateInternalCodeLocations(internalProject, adder.itemPosition(), adder.lineOffset());
- updateExternalCodeLocations(m_projectData, adder.itemPosition(), adder.lineOffset());
-
- QHash<QString, std::pair<SourceArtifactPtr, ResolvedProductPtr>> addedSourceArtifacts;
- for (int i = 0; i < groupContext.resolvedGroups.size(); ++i) {
- const ResolvedProductPtr &resolvedProduct = groupContext.resolvedProducts.at(i);
- const GroupPtr &resolvedGroup = groupContext.resolvedGroups.at(i);
- for (const QString &file : qAsConst(filesContext.absoluteFilePaths)) {
- const SourceArtifactPtr sa = createSourceArtifact(file, resolvedProduct, resolvedGroup,
- false);
- addedSourceArtifacts.insert(file, std::make_pair(sa, resolvedProduct));
- }
- for (const QString &file : qAsConst(filesContext.absoluteFilePathsFromWildcards)) {
- QBS_CHECK(resolvedGroup->wildcards);
- const SourceArtifactPtr sa = createSourceArtifact(file, resolvedProduct, resolvedGroup,
- true);
- addedSourceArtifacts.insert(file, std::make_pair(sa, resolvedProduct));
- }
- if (resolvedProduct->enabled) {
- for (const auto &pair : qAsConst(addedSourceArtifacts))
- createArtifact(resolvedProduct, pair.first);
- }
- }
- doSanityChecks(internalProject, logger);
- QList<ArtifactData> sourceArtifacts;
- QList<ArtifactData> sourceArtifactsFromWildcards;
- for (const QString &fp : qAsConst(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;
- }
- for (const QString &fp : qAsConst(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;
- }
- for (const GroupData &g : qAsConst(groupContext.groups)) {
- g.d->sourceArtifacts << sourceArtifacts;
- std::sort(g.d->sourceArtifacts.begin(), g.d->sourceArtifacts.end());
- g.d->sourceArtifactsFromWildcards << sourceArtifactsFromWildcards;
- std::sort(g.d->sourceArtifactsFromWildcards.begin(),
- g.d->sourceArtifactsFromWildcards.end());
- }
}
void ProjectPrivate::removeFiles(const ProductData &product, const GroupData &group,
@@ -595,132 +502,14 @@ void ProjectPrivate::removeFiles(const ProductData &product, const GroupData &gr
group.isValid() ? groupContext.groups.front() : GroupData(),
filesContext.relativeFilePaths);
remover.apply();
-
- for (int i = 0; i < groupContext.resolvedProducts.size(); ++i) {
- removeFilesFromBuildGraph(groupContext.resolvedProducts.at(i), sourceArtifacts);
- for (const SourceArtifactPtr &sa : sourceArtifacts)
- removeOne(groupContext.resolvedGroups.at(i)->files, sa);
- }
- doSanityChecks(internalProject, logger);
-
- m_projectData.d.detach();
- updateInternalCodeLocations(internalProject, remover.itemPosition(), remover.lineOffset());
- updateExternalCodeLocations(m_projectData, remover.itemPosition(), remover.lineOffset());
- for (const GroupData &g : qAsConst(groupContext.groups)) {
- for (int i = g.d->sourceArtifacts.size() - 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.front(), context.groups.front());
remover.apply();
- for (int i = 0; i < context.resolvedProducts.size(); ++i) {
- const ResolvedProductPtr &product = context.resolvedProducts.at(i);
- const GroupPtr &group = context.resolvedGroups.at(i);
- removeFilesFromBuildGraph(product, group->allFiles());
- const bool removed = removeOne(product->groups, 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.size(); ++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 std::vector<SourceArtifactPtr> &files)
-{
- if (!product->enabled)
- return;
- QBS_CHECK(internalProject->buildData);
- ArtifactSet allRemovedArtifacts;
- for (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);
- for (const ResolvedProjectPtr &subProject : qAsConst(project->subProjects))
- updateInternalCodeLocations(subProject, changeLocation, lineOffset);
- for (const ResolvedProductPtr &product : project->products) {
- updateLocationIfNecessary(product->location, changeLocation, lineOffset);
- for (const GroupPtr &group : product->groups)
- updateLocationIfNecessary(group->location, changeLocation, lineOffset);
- for (const RulePtr &rule : qAsConst(product->rules)) {
- updateLocationIfNecessary(rule->prepareScript.location(), changeLocation, lineOffset);
- for (const RuleArtifactPtr &artifact : rule->artifacts) {
- for (auto &binding : artifact->bindings) {
- updateLocationIfNecessary(binding.location, changeLocation, lineOffset);
- }
- }
- }
- for (const ResolvedScannerConstPtr &scanner : product->scanners) {
- updateLocationIfNecessary(scanner->searchPathsScript.location(), changeLocation,
- lineOffset);
- updateLocationIfNecessary(scanner->scanScript.location(), changeLocation, lineOffset);
- }
- for (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);
- const auto subProjects = project.subProjects();
- for (const ProjectData &subProject : subProjects)
- updateExternalCodeLocations(subProject, changeLocation, lineOffset);
- const auto products = project.products();
- for (const ProductData &product : products) {
- updateLocationIfNecessary(product.d->location, changeLocation, lineOffset);
- const auto groups = product.groups();
- for (const GroupData &group : groups)
- updateLocationIfNecessary(group.d->location, changeLocation, lineOffset);
- }
}
void ProjectPrivate::prepareChangeToProject()
@@ -734,7 +523,7 @@ void ProjectPrivate::prepareChangeToProject()
RuleCommandList ProjectPrivate::ruleCommandListForTransformer(const Transformer *transformer)
{
RuleCommandList list;
- for (const AbstractCommandPtr &internalCommand : qAsConst(transformer->commands.commands())) {
+ for (const AbstractCommandPtr &internalCommand : std::as_const(transformer->commands.commands())) {
RuleCommand externalCommand;
externalCommand.d->description = internalCommand->description();
externalCommand.d->extendedDescription = internalCommand->extendedDescription();
@@ -775,11 +564,11 @@ RuleCommandList ProjectPrivate::ruleCommands(const ProductData &product,
QBS_CHECK(resolvedProduct->buildData);
const ArtifactSet &outputArtifacts = resolvedProduct->buildData->artifactsByFileTag()
.value(FileTag(outputFileTag.toLocal8Bit()));
- for (const Artifact * const outputArtifact : qAsConst(outputArtifacts)) {
+ for (const Artifact * const outputArtifact : std::as_const(outputArtifacts)) {
const TransformerConstPtr transformer = outputArtifact->transformer;
if (!transformer)
continue;
- for (const Artifact * const inputArtifact : qAsConst(transformer->inputs)) {
+ for (const Artifact * const inputArtifact : std::as_const(transformer->inputs)) {
if (inputArtifact->filePath() == inputFilePath)
return ruleCommandListForTransformer(transformer.get());
}
@@ -794,8 +583,7 @@ ProjectTransformerData ProjectPrivate::transformerData()
if (!m_projectData.isValid())
retrieveProjectData(m_projectData, internalProject);
ProjectTransformerData projectTransformerData;
- const auto allProducts = m_projectData.allProducts();
- for (const ProductData &productData : allProducts) {
+ for (const ProductData &productData : m_projectData.allProducts()) {
if (!productData.isEnabled())
continue;
const ResolvedProductConstPtr product = internalProduct(productData);
@@ -837,7 +625,10 @@ static bool productIsRunnable(const ResolvedProductConstPtr &product)
{
const bool isBundle = product->moduleProperties->moduleProperty(
QStringLiteral("bundle"), QStringLiteral("isBundle")).toBool();
- return isRunnableArtifact(product->fileTags, isBundle);
+ const QString androidSdkPackageType = product->moduleProperties->moduleProperty(
+ QStringLiteral("Android.sdk"), QStringLiteral("packageType")).toString();
+ const bool isAndroidApk = androidSdkPackageType == QStringLiteral("apk");
+ return isRunnableArtifact(product->fileTags, isBundle, isAndroidApk);
}
static bool productIsMultiplexed(const ResolvedProductConstPtr &product)
@@ -851,7 +642,7 @@ void ProjectPrivate::retrieveProjectData(ProjectData &projectData,
projectData.d->name = internalProject->name;
projectData.d->location = internalProject->location;
projectData.d->enabled = internalProject->enabled;
- for (const ResolvedProductConstPtr &resolvedProduct : internalProject->products) {
+ for (const auto &resolvedProduct : internalProject->products) {
ProductData product;
product.d->type = resolvedProduct->fileTags.toStringList();
product.d->name = resolvedProduct->name;
@@ -895,8 +686,8 @@ void ProjectPrivate::retrieveProjectData(ProjectData &projectData,
}
}
for (const ResolvedProductPtr &resolvedDependentProduct
- : qAsConst(resolvedProduct->dependencies)) {
- product.d->dependencies << resolvedDependentProduct->name; // FIXME: Shouldn't this be a unique name?
+ : std::as_const(resolvedProduct->dependencies)) {
+ product.d->dependencies << resolvedDependentProduct->fullDisplayName();
}
std::sort(product.d->type.begin(), product.d->type.end());
std::sort(product.d->groups.begin(), product.d->groups.end());
@@ -904,8 +695,7 @@ void ProjectPrivate::retrieveProjectData(ProjectData &projectData,
product.d->isValid = true;
projectData.d->products << product;
}
- for (const ResolvedProjectConstPtr &internalSubProject
- : qAsConst(internalProject->subProjects)) {
+ for (const auto &internalSubProject : std::as_const(internalProject->subProjects)) {
if (!internalSubProject->enabled)
continue;
ProjectData subProject;
@@ -1007,8 +797,14 @@ RunEnvironment Project::getRunEnvironment(const ProductData &product,
const QStringList &setupRunEnvConfig, Settings *settings) const
{
const ResolvedProductPtr resolvedProduct = d->internalProduct(product);
- return RunEnvironment(resolvedProduct, d->internalProject, installOptions, environment,
- setupRunEnvConfig, settings, d->logger);
+ return {
+ resolvedProduct,
+ d->internalProject,
+ installOptions,
+ environment,
+ setupRunEnvConfig,
+ settings,
+ d->logger};
}
/*!
@@ -1163,7 +959,13 @@ QVariantMap Project::projectConfiguration() const
std::set<QString> Project::buildSystemFiles() const
{
QBS_ASSERT(isValid(), return {});
- return d->internalProject->buildSystemFiles.toStdSet();
+ return rangeTo<std::set<QString>>(d->internalProject->buildSystemFiles);
+}
+
+CodeLinks Project::codeLinks() const
+{
+ QBS_ASSERT(isValid(), return {});
+ return d->internalProject->codeLinks;
}
RuleCommandList Project::ruleCommands(const ProductData &product,
@@ -1211,14 +1013,14 @@ Project::BuildGraphInfo Project::getBuildGraphInfo(const QString &bgFilePath,
const Internal::TopLevelProjectConstPtr project = BuildGraphLoader::loadProject(bgFilePath);
info.bgFilePath = bgFilePath;
info.overriddenProperties = project->overriddenValues;
- info.profileData = project->profileConfigs;
+ info.profileData = project->fullProfileConfigsTree();
std::vector<std::pair<QString, QString>> props;
for (const QString &prop : requestedProperties) {
QStringList components = prop.split(QLatin1Char('.'));
const QString propName = components.takeLast();
props.emplace_back(components.join(QLatin1Char('.')), propName);
}
- for (const ResolvedProductConstPtr &product : project->allProducts()) {
+ for (const auto &product : project->allProducts()) {
if (props.empty())
break;
if (product->profile() != project->profile())
@@ -1250,14 +1052,13 @@ Project::BuildGraphInfo Project::getBuildGraphInfo() const
throw ErrorInfo(Tr::tr("A job is currently in progress."));
info.bgFilePath = d->internalProject->buildGraphFilePath();
info.overriddenProperties = d->internalProject->overriddenValues;
- info.profileData = d->internalProject->profileConfigs;
+ info.profileData = d->internalProject->fullProfileConfigsTree();
} catch (const ErrorInfo &e) {
info.error = e;
}
return info;
}
-#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
@@ -1272,7 +1073,6 @@ ErrorInfo Project::addGroup(const ProductData &product, const QString &groupName
QBS_CHECK(isValid());
d->prepareChangeToProject();
d->addGroup(product, groupName);
- d->internalProject->lastStartResolveTime = FileTime::currentTime();
d->internalProject->store(d->logger);
return {};
} catch (const ErrorInfo &exception) {
@@ -1300,7 +1100,6 @@ ErrorInfo Project::addFiles(const ProductData &product, const GroupData &group,
QBS_CHECK(isValid());
d->prepareChangeToProject();
d->addFiles(product, group, filePaths);
- d->internalProject->lastStartResolveTime = FileTime::currentTime();
d->internalProject->store(d->logger);
return {};
} catch (const ErrorInfo &exception) {
@@ -1327,7 +1126,6 @@ ErrorInfo Project::removeFiles(const ProductData &product, const GroupData &grou
QBS_CHECK(isValid());
d->prepareChangeToProject();
d->removeFiles(product, group, filePaths);
- d->internalProject->lastStartResolveTime = FileTime::currentTime();
d->internalProject->store(d->logger);
return {};
} catch (const ErrorInfo &exception) {
@@ -1349,7 +1147,6 @@ ErrorInfo Project::removeGroup(const ProductData &product, const GroupData &grou
QBS_CHECK(isValid());
d->prepareChangeToProject();
d->removeGroup(product, group);
- d->internalProject->lastStartResolveTime = FileTime::currentTime();
d->internalProject->store(d->logger);
return {};
} catch (const ErrorInfo &exception) {
@@ -1359,6 +1156,5 @@ ErrorInfo Project::removeGroup(const ProductData &product, const GroupData &grou
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
index 9000d6548..7490eb41e 100644
--- a/src/lib/corelib/api/project.h
+++ b/src/lib/corelib/api/project.h
@@ -43,6 +43,7 @@
#include "transformerdata.h"
#include "../language/forward_decls.h"
#include "../tools/error.h"
+#include "../tools/porting.h"
#include "../tools/qbs_export.h"
#include <QtCore/qshareddata.h>
@@ -83,7 +84,7 @@ class ProjectPrivate;
class QBS_EXPORT Project
{
friend class SetupProjectJob;
- friend uint qHash(const Project &p);
+ friend QHashValueType qHash(const Project &p);
public:
SetupProjectJob *setupProject(const SetupProjectParameters &parameters,
ILogSink *logSink, QObject *jobOwner);
@@ -135,6 +136,7 @@ public:
QVariantMap projectConfiguration() const;
std::set<QString> buildSystemFiles() const;
+ CodeLinks codeLinks() const;
RuleCommandList ruleCommands(const ProductData &product, const QString &inputFilePath,
const QString &outputFileTag, ErrorInfo *error = nullptr) const;
@@ -159,14 +161,12 @@ public:
BuildGraphInfo getBuildGraphInfo() const;
-#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);
@@ -175,7 +175,7 @@ private:
};
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()); }
+inline QHashValueType qHash(const Project &p) { return QT_PREPEND_NAMESPACE(qHash)(p.d.data()); }
} // namespace qbs
diff --git a/src/lib/corelib/api/project_p.h b/src/lib/corelib/api/project_p.h
index 58f61a5bc..60c8311f3 100644
--- a/src/lib/corelib/api/project_p.h
+++ b/src/lib/corelib/api/project_p.h
@@ -62,22 +62,22 @@ namespace Internal {
class ProjectPrivate : public QSharedData
{
public:
- ProjectPrivate(const TopLevelProjectPtr &internalProject, const Logger &logger)
- : internalProject(internalProject), logger(logger)
+ ProjectPrivate(TopLevelProjectPtr internalProject, Logger logger)
+ : internalProject(std::move(internalProject)), logger(std::move(logger))
{
}
ProjectData projectData();
- BuildJob *buildProducts(const QList<ResolvedProductPtr> &products, const BuildOptions &options,
+ BuildJob *buildProducts(const QVector<ResolvedProductPtr> &products, const BuildOptions &options,
bool needsDepencencyResolving,
QObject *jobOwner);
- CleanJob *cleanProducts(const QList<ResolvedProductPtr> &products, const CleanOptions &options,
+ CleanJob *cleanProducts(const QVector<ResolvedProductPtr> &products, const CleanOptions &options,
QObject *jobOwner);
- InstallJob *installProducts(const QList<ResolvedProductPtr> &products,
+ InstallJob *installProducts(const QVector<ResolvedProductPtr> &products,
const InstallOptions &options, bool needsDepencencyResolving,
QObject *jobOwner);
- QList<ResolvedProductPtr> internalProducts(const QList<ProductData> &products) const;
- QList<ResolvedProductPtr> allEnabledInternalProducts(bool includingNonDefault) const;
+ QVector<ResolvedProductPtr> internalProducts(const QList<ProductData> &products) const;
+ QVector<ResolvedProductPtr> allEnabledInternalProducts(bool includingNonDefault) const;
ResolvedProductPtr internalProduct(const ProductData &product) const;
ProductData findProductData(const ProductData &product) const;
QList<ProductData> findProductsByName(const QString &name) const;
@@ -92,7 +92,7 @@ public:
void setupInstallData(ArtifactData &artifact, const ResolvedProductConstPtr &product);
struct GroupUpdateContext {
- QList<ResolvedProductPtr> resolvedProducts;
+ QVector<ResolvedProductPtr> resolvedProducts;
QList<GroupPtr> resolvedGroups;
QList<ProductData> products;
QList<GroupData> groups;
@@ -114,12 +114,7 @@ public:
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 std::vector<SourceArtifactPtr> &files);
- void updateInternalCodeLocations(const ResolvedProjectPtr &project,
- const CodeLocation &changeLocation, int lineOffset);
- void updateExternalCodeLocations(const ProjectData &project,
- const CodeLocation &changeLocation, int lineOffset);
+
void prepareChangeToProject();
RuleCommandList ruleCommandListForTransformer(const Transformer *transformer);
diff --git a/src/lib/corelib/api/projectdata.cpp b/src/lib/corelib/api/projectdata.cpp
index 7c64bf6ff..628fe2a5d 100644
--- a/src/lib/corelib/api/projectdata.cpp
+++ b/src/lib/corelib/api/projectdata.cpp
@@ -40,13 +40,15 @@
#include "projectdata_p.h"
#include "propertymap_p.h"
+#include <language/builtindeclarations.h>
#include <language/language.h>
#include <language/propertymapinternal.h>
+#include <loader/loaderutils.h>
#include <tools/fileinfo.h>
#include <tools/jsliterals.h>
#include <tools/qbsassert.h>
-#include <tools/stringconstants.h>
#include <tools/qttools.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
#include <QtCore/qdir.h>
@@ -62,9 +64,10 @@ using namespace Internal;
template<typename T> static QJsonArray toJsonArray(const QList<T> &list,
const QStringList &moduleProperties)
{
+ // We can't use transformed from stlutils.h here as QJsonArray has not the reserve() method.
QJsonArray jsonArray;
- std::transform(list.begin(), list.end(), std::back_inserter(jsonArray),
- [&moduleProperties](const T &v) { return v.toJson(moduleProperties);});
+ transform(list, jsonArray, [&moduleProperties](const T &v) {
+ return v.toJson(moduleProperties);});
return jsonArray;
}
@@ -72,7 +75,7 @@ static QVariant getModuleProperty(const PropertyMap &properties, const QString &
{
const int lastDotIndex = fullPropertyName.lastIndexOf(QLatin1Char('.'));
if (lastDotIndex == -1)
- return QVariant();
+ return {};
return properties.getModuleProperty(fullPropertyName.left(lastDotIndex),
fullPropertyName.mid(lastDotIndex + 1));
}
@@ -218,11 +221,7 @@ bool GroupData::isEnabled() const
QStringList GroupData::allFilePaths() const
{
const QList<ArtifactData> &artifacts = allSourceArtifacts();
- QStringList paths;
- paths.reserve(artifacts.size());
- std::transform(artifacts.cbegin(), artifacts.cend(), std::back_inserter(paths),
- [](const ArtifactData &sa) { return sa.filePath(); });
- return paths;
+ return transformed<QStringList>(artifacts, [](const auto &sa) { return sa.filePath(); });
}
bool operator!=(const GroupData &lhs, const GroupData &rhs)
@@ -323,8 +322,10 @@ bool ArtifactData::isExecutable() const
{
const bool isBundle = d->properties.getModuleProperty(
QStringLiteral("bundle"), QStringLiteral("isBundle")).toBool();
- return isRunnableArtifact(
- FileTags::fromStringList(d->fileTags), isBundle);
+ const QString androidSdkPackageType = d->properties.getModuleProperty(
+ QStringLiteral("Android.sdk"), QStringLiteral("packageType")).toString();
+ const bool isAndroidApk = androidSdkPackageType == QStringLiteral("apk");
+ return isRunnableArtifact(FileTags::fromStringList(d->fileTags), isBundle, isAndroidApk);
}
/*!
@@ -530,7 +531,7 @@ QJsonObject ProductData::toJson(const QStringList &propertyNames) const
/*!
* \brief The product type, which is the list of file tags matching the product's target artifacts.
*/
-QStringList ProductData::type() const
+const QStringList &ProductData::type() const
{
return d->type;
}
@@ -538,7 +539,7 @@ QStringList ProductData::type() const
/*!
* \brief The names of dependent products.
*/
-QStringList ProductData::dependencies() const
+const QStringList &ProductData::dependencies() const
{
return d->dependencies;
}
@@ -546,7 +547,7 @@ QStringList ProductData::dependencies() const
/*!
* \brief The name of the product as given in the qbs source file.
*/
-QString ProductData::name() const
+const QString &ProductData::name() const
{
return d->name;
}
@@ -558,13 +559,13 @@ QString ProductData::name() const
*/
QString ProductData::fullDisplayName() const
{
- return ResolvedProduct::fullDisplayName(name(), multiplexConfigurationId());
+ return fullProductDisplayName(name(), multiplexConfigurationId());
}
/*!
* \brief The base name of the product's target file as given in the qbs source file.
*/
-QString ProductData::targetName() const
+const QString &ProductData::targetName() const
{
return d->targetName;
}
@@ -572,7 +573,7 @@ QString ProductData::targetName() const
/*!
* \brief The version number of the product.
*/
-QString ProductData::version() const
+const QString &ProductData::version() const
{
return d->version;
}
@@ -587,7 +588,7 @@ QString ProductData::profile() const
StringConstants::profileProperty()).toString();
}
-QString ProductData::multiplexConfigurationId() const
+const QString &ProductData::multiplexConfigurationId() const
{
return d->multiplexConfigurationId;
}
@@ -595,7 +596,7 @@ QString ProductData::multiplexConfigurationId() const
/*!
* \brief The location at which the product is defined in the source file.
*/
-CodeLocation ProductData::location() const
+const CodeLocation &ProductData::location() const
{
return d->location;
}
@@ -603,7 +604,7 @@ CodeLocation ProductData::location() const
/*!
* \brief The directory under which the product's generated artifacts are located.
*/
-QString ProductData::buildDirectory() const
+const QString &ProductData::buildDirectory() const
{
return d->buildDirectory;
}
@@ -611,7 +612,7 @@ QString ProductData::buildDirectory() const
/*!
* \brief All artifacts that are generated when building this product.
*/
-QList<ArtifactData> ProductData::generatedArtifacts() const
+const QList<ArtifactData> &ProductData::generatedArtifacts() const
{
return d->generatedArtifacts;
}
@@ -620,7 +621,7 @@ QList<ArtifactData> ProductData::generatedArtifacts() const
\brief This product's target artifacts.
This is a subset of \c generatedArtifacts()
*/
-QList<ArtifactData> ProductData::targetArtifacts() const
+const QList<ArtifactData> ProductData::targetArtifacts() const
{
QList<ArtifactData> list;
std::copy_if(d->generatedArtifacts.cbegin(), d->generatedArtifacts.cend(),
@@ -632,17 +633,17 @@ QList<ArtifactData> ProductData::targetArtifacts() const
/*!
* \brief The list of artifacts in this product that are to be installed.
*/
-QList<ArtifactData> ProductData::installableArtifacts() const
+const QList<ArtifactData> ProductData::installableArtifacts() const
{
QList<ArtifactData> artifacts;
- for (const GroupData &g : qAsConst(d->groups)) {
+ for (const GroupData &g : std::as_const(d->groups)) {
const auto sourceArtifacts = g.allSourceArtifacts();
for (const ArtifactData &a : sourceArtifacts) {
if (a.installData().isInstallable())
artifacts << a;
}
}
- for (const ArtifactData &a : qAsConst(d->generatedArtifacts)) {
+ for (const ArtifactData &a : std::as_const(d->generatedArtifacts)) {
if (a.installData().isInstallable())
artifacts << a;
}
@@ -681,7 +682,7 @@ QString ProductData::targetExecutable() const
/*!
* \brief The list of \c GroupData in this product.
*/
-QList<GroupData> ProductData::groups() const
+const QList<GroupData> &ProductData::groups() const
{
return d->groups;
}
@@ -689,7 +690,7 @@ QList<GroupData> ProductData::groups() const
/*!
* \brief The product properties.
*/
-QVariantMap ProductData::properties() const
+const QVariantMap &ProductData::properties() const
{
return d->properties;
}
@@ -697,7 +698,7 @@ QVariantMap ProductData::properties() const
/*!
* \brief The set of properties inherited from dependent products and modules.
*/
-PropertyMap ProductData::moduleProperties() const
+const PropertyMap &ProductData::moduleProperties() const
{
return d->moduleProperties;
}
@@ -731,21 +732,16 @@ 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.multiplexConfigurationId() == rhs.multiplexConfigurationId()
- && lhs.location() == rhs.location()
- && lhs.groups() == rhs.groups()
- && lhs.generatedArtifacts() == rhs.generatedArtifacts()
- && lhs.properties() == rhs.properties()
- && lhs.moduleProperties() == rhs.moduleProperties()
- && lhs.isEnabled() == rhs.isEnabled()
- && lhs.isMultiplexed() == rhs.isMultiplexed();
+ 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.multiplexConfigurationId() == rhs.multiplexConfigurationId()
+ && lhs.location() == rhs.location() && lhs.groups() == rhs.groups()
+ && lhs.generatedArtifacts() == rhs.generatedArtifacts()
+ && qVariantMapsEqual(lhs.properties(), rhs.properties())
+ && lhs.moduleProperties() == rhs.moduleProperties() && lhs.isEnabled() == rhs.isEnabled()
+ && lhs.isMultiplexed() == rhs.isMultiplexed();
}
bool operator!=(const ProductData &lhs, const ProductData &rhs)
@@ -812,7 +808,7 @@ QJsonObject ProjectData::toJson(const QStringList &moduleProperties) const
/*!
* \brief The name of this project.
*/
-QString ProjectData::name() const
+const QString &ProjectData::name() const
{
return d->name;
}
@@ -820,7 +816,7 @@ QString ProjectData::name() const
/*!
* \brief The location at which the project is defined in a qbs source file.
*/
-CodeLocation ProjectData::location() const
+const CodeLocation &ProjectData::location() const
{
return d->location;
}
@@ -839,7 +835,7 @@ bool ProjectData::isEnabled() const
* \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
+const QString &ProjectData::buildDirectory() const
{
return d->buildDir;
}
@@ -848,7 +844,7 @@ QString ProjectData::buildDirectory() const
* The products in this project.
* \note This also includes disabled products.
*/
-QList<ProductData> ProjectData::products() const
+const QList<ProductData> &ProjectData::products() const
{
return d->products;
}
@@ -856,7 +852,7 @@ QList<ProductData> ProjectData::products() const
/*!
* The sub-projects of this project.
*/
-QList<ProjectData> ProjectData::subProjects() const
+const QList<ProjectData> &ProjectData::subProjects() const
{
return d->subProjects;
}
@@ -864,10 +860,10 @@ QList<ProjectData> ProjectData::subProjects() const
/*!
* All products in this projects and its direct and indirect sub-projects.
*/
-QList<ProductData> ProjectData::allProducts() const
+const QList<ProductData> ProjectData::allProducts() const
{
QList<ProductData> productList = products();
- for (const ProjectData &pd : qAsConst(d->subProjects))
+ for (const ProjectData &pd : std::as_const(d->subProjects))
productList << pd.allProducts();
return productList;
}
@@ -875,7 +871,7 @@ QList<ProductData> ProjectData::allProducts() const
/*!
* The artifacts of all products in this project that are to be installed.
*/
-QList<ArtifactData> ProjectData::installableArtifacts() const
+const QList<ArtifactData> ProjectData::installableArtifacts() const
{
QList<ArtifactData> artifacts;
const auto products = allProducts();
@@ -953,6 +949,52 @@ QStringList PropertyMap::allProperties() const
}
/*!
+ * \brief Returns the names of all modules whose properties can be requested.
+ */
+QStringList PropertyMap::allModules() const
+{
+ QStringList modules;
+ for (auto it = d->m_map->value().constBegin(); it != d->m_map->value().constEnd(); ++it) {
+ if (it.value().canConvert<QVariantMap>())
+ modules << it.key();
+ }
+ return modules;
+}
+
+/*!
+ * \brief Returns information about all properties of the given module.
+ */
+QList<PropertyMap::PropertyInfo> PropertyMap::allPropertiesForModule(const QString &module) const
+{
+ const QVariantMap moduleProps = d->m_map->value().value(module).toMap();
+ QList<PropertyInfo> properties;
+ const auto builtinProps = transformed<QStringList>(
+ BuiltinDeclarations::instance().declarationsForType(ItemType::Module).properties(),
+ [](const PropertyDeclaration &decl) { return decl.name(); });
+ for (auto it = moduleProps.begin(); it != moduleProps.end(); ++it) {
+ static const auto getType = [](const QVariant &v) -> QString {
+ switch (qVariantType(v)) {
+ case QMetaType::Bool:
+ return PropertyDeclaration::typeString(PropertyDeclaration::Boolean);
+ case QMetaType::Int:
+ return PropertyDeclaration::typeString(PropertyDeclaration::Integer);
+ case QMetaType::QVariantList:
+ return PropertyDeclaration::typeString(PropertyDeclaration::VariantList);
+ case QMetaType::QString:
+ return PropertyDeclaration::typeString(PropertyDeclaration::String);
+ case QMetaType::QStringList:
+ return PropertyDeclaration::typeString(PropertyDeclaration::StringList);
+ default:
+ return PropertyDeclaration::typeString(PropertyDeclaration::Variant);
+ }
+ };
+ properties << PropertyInfo{
+ it.key(), getType(it.value()), it.value(), builtinProps.contains(it.key())};
+ }
+ return properties;
+}
+
+/*!
* \brief Returns the value of the given property of a product or group.
*/
QVariant PropertyMap::getProperty(const QString &name) const
@@ -987,12 +1029,11 @@ QVariant PropertyMap::getModuleProperty(const QString &moduleName,
static QString mapToString(const QVariantMap &map, const QString &prefix)
{
- QStringList keys(map.keys());
- std::sort(keys.begin(), keys.end());
QString stringRep;
- for (const QString &key : qAsConst(keys)) {
- const QVariant &val = map.value(key);
- if (val.type() == QVariant::Map) {
+ for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) {
+ const QString &key = it.key();
+ const QVariant &val = it.value();
+ if (val.userType() == QMetaType::QVariantMap) {
stringRep += mapToString(val.value<QVariantMap>(), prefix + key + QLatin1Char('.'));
} else {
stringRep += QStringLiteral("%1%2: %3\n")
diff --git a/src/lib/corelib/api/projectdata.h b/src/lib/corelib/api/projectdata.h
index a285f8570..4b7bc2803 100644
--- a/src/lib/corelib/api/projectdata.h
+++ b/src/lib/corelib/api/projectdata.h
@@ -74,6 +74,14 @@ class QBS_EXPORT PropertyMap
friend QBS_EXPORT bool operator!=(const PropertyMap &, const PropertyMap &);
public:
+ struct PropertyInfo
+ {
+ QString name;
+ QString type;
+ QVariant value;
+ bool isBuiltin = false;
+ };
+
PropertyMap();
PropertyMap(const PropertyMap &other);
PropertyMap(PropertyMap &&other) Q_DECL_NOEXCEPT;
@@ -83,6 +91,8 @@ public:
PropertyMap &operator =(PropertyMap &&other) Q_DECL_NOEXCEPT;
QStringList allProperties() const;
+ QStringList allModules() const;
+ QList<PropertyInfo> allPropertiesForModule(const QString &module) const;
QVariant getProperty(const QString &name) const;
QStringList getModulePropertiesAsStringList(const QString &moduleName,
@@ -198,23 +208,23 @@ public:
bool isValid() const;
QJsonObject toJson(const QStringList &propertyNames = {}) const;
- QStringList type() const;
- QStringList dependencies() const;
- QString name() const;
+ const QStringList &type() const;
+ const QStringList &dependencies() const;
+ const QString &name() const;
QString fullDisplayName() const;
- QString targetName() const;
- QString version() const;
+ const QString &targetName() const;
+ const QString &version() const;
QString profile() const;
- QString multiplexConfigurationId() const;
- CodeLocation location() const;
- QString buildDirectory() const;
- QList<ArtifactData> generatedArtifacts() const;
- QList<ArtifactData> targetArtifacts() const;
- QList<ArtifactData> installableArtifacts() const;
+ const QString &multiplexConfigurationId() const;
+ const CodeLocation &location() const;
+ const QString &buildDirectory() const;
+ const QList<ArtifactData> &generatedArtifacts() const;
+ const QList<ArtifactData> targetArtifacts() const;
+ const QList<ArtifactData> installableArtifacts() const;
QString targetExecutable() const;
- QList<GroupData> groups() const;
- QVariantMap properties() const;
- PropertyMap moduleProperties() const;
+ const QList<GroupData> &groups() const;
+ const QVariantMap &properties() const;
+ const PropertyMap &moduleProperties() const;
bool isEnabled() const;
bool isRunnable() const;
bool isMultiplexed() const;
@@ -241,14 +251,14 @@ public:
bool isValid() const;
QJsonObject toJson(const QStringList &moduleProperties = {}) const;
- QString name() const;
- CodeLocation location() const;
+ const QString &name() const;
+ const CodeLocation &location() const;
bool isEnabled() const;
- QString buildDirectory() const;
- QList<ProductData> products() const;
- QList<ProjectData> subProjects() const;
- QList<ProductData> allProducts() const;
- QList<ArtifactData> installableArtifacts() const;
+ const QString &buildDirectory() const;
+ const QList<ProductData> &products() const;
+ const QList<ProjectData> &subProjects() const;
+ const QList<ProductData> allProducts() const;
+ const QList<ArtifactData> installableArtifacts() const;
private:
QExplicitlySharedDataPointer<Internal::ProjectDataPrivate> d;
diff --git a/src/lib/corelib/api/projectdata_p.h b/src/lib/corelib/api/projectdata_p.h
index 834aeec23..e241ea92c 100644
--- a/src/lib/corelib/api/projectdata_p.h
+++ b/src/lib/corelib/api/projectdata_p.h
@@ -123,11 +123,11 @@ public:
QString buildDir;
};
-inline bool isRunnableArtifact(const FileTags &fileTags, bool isBundle)
+inline bool isRunnableArtifact(const FileTags &fileTags, bool isBundle, bool isAndroidApk)
{
return (fileTags.contains("application") && (!isBundle || fileTags.contains("bundle.content")))
|| fileTags.contains("bundle.application-executable")
- || fileTags.contains("android.apk")
+ || (fileTags.contains("android.package") && isAndroidApk)
|| fileTags.contains("msi");
}
diff --git a/src/lib/corelib/api/projectfileupdater.cpp b/src/lib/corelib/api/projectfileupdater.cpp
index 04f8e630f..604600f8e 100644
--- a/src/lib/corelib/api/projectfileupdater.cpp
+++ b/src/lib/corelib/api/projectfileupdater.cpp
@@ -66,7 +66,7 @@ namespace Internal {
class ItemFinder : public Visitor
{
public:
- ItemFinder(const CodeLocation &cl) : m_cl(cl), m_item(nullptr) { }
+ ItemFinder(CodeLocation cl) : m_cl(std::move(cl)), m_item(nullptr) { }
UiObjectDefinition *item() const { return m_item; }
@@ -115,7 +115,7 @@ private:
};
-ProjectFileUpdater::ProjectFileUpdater(const QString &projectFile) : m_projectFile(projectFile)
+ProjectFileUpdater::ProjectFileUpdater(QString projectFile) : m_projectFile(std::move(projectFile))
{
}
@@ -182,7 +182,7 @@ void ProjectFileUpdater::apply()
if (!parserMessages.empty()) {
ErrorInfo errorInfo;
errorInfo.append(Tr::tr("Failure parsing project file."));
- for (const DiagnosticMessage &msg : qAsConst(parserMessages))
+ for (const DiagnosticMessage &msg : std::as_const(parserMessages))
errorInfo.append(msg.message, toCodeLocation(file.fileName(), msg.loc));
throw errorInfo;
}
@@ -201,14 +201,18 @@ void ProjectFileUpdater::apply()
}
-ProjectFileGroupInserter::ProjectFileGroupInserter(const ProductData &product,
- const QString &groupName)
+ProjectFileGroupInserter::ProjectFileGroupInserter(ProductData product, QString groupName)
: ProjectFileUpdater(product.location().filePath())
- , m_product(product)
- , m_groupName(groupName)
+ , m_product(std::move(product))
+ , m_groupName(std::move(groupName))
{
}
+static int extractLine(const QString &fileContent, int pos)
+{
+ return QStringView{fileContent}.left(pos).count(QLatin1Char('\n'));
+}
+
void ProjectFileGroupInserter::doApply(QString &fileContent, UiProgram *ast)
{
ItemFinder itemFinder(m_product.location());
@@ -222,7 +226,7 @@ void ProjectFileGroupInserter::doApply(QString &fileContent, UiProgram *ast)
Rewriter rewriter(fileContent, &changeSet, QStringList());
QString groupItemString;
const int productItemIndentation
- = itemFinder.item()->qualifiedTypeNameId->firstSourceLocation().startColumn - 1;
+ = int(itemFinder.item()->qualifiedTypeNameId->firstSourceLocation().startColumn - 1);
const int groupItemIndentation = productItemIndentation + 4;
const QString groupItemIndentationString = QString(groupItemIndentation, QLatin1Char(' '));
groupItemString += groupItemIndentationString + QLatin1String("Group {\n");
@@ -239,7 +243,7 @@ void ProjectFileGroupInserter::doApply(QString &fileContent, UiProgram *ast)
const ChangeSet::EditOp &insertOp = editOps.front();
setLineOffset(lineOffset);
- int insertionLine = fileContent.left(insertOp.pos1).count(QLatin1Char('\n'));
+ int insertionLine = extractLine(fileContent, insertOp.pos1);
for (int i = 0; i < insertOp.text.size() && insertOp.text.at(i) == QLatin1Char('\n'); ++i)
++insertionLine; // To account for newlines prepended by the rewriter.
++insertionLine; // To account for zero-based indexing.
@@ -252,7 +256,7 @@ static QString getNodeRepresentation(const QString &fileContent, const QbsQmlJS:
{
const quint32 start = node->firstSourceLocation().offset;
const quint32 end = node->lastSourceLocation().end();
- return fileContent.mid(start, end - start);
+ return fileContent.mid(start, int(end - start));
}
static const ChangeSet::EditOp &getEditOp(const ChangeSet &changeSet)
@@ -269,16 +273,16 @@ static int getLineOffsetForChangedBinding(const ChangeSet &changeSet, const QStr
static int getBindingLine(const ChangeSet &changeSet, const QString &fileContent)
{
- return fileContent.left(getEditOp(changeSet).pos1 + 1).count(QLatin1Char('\n')) + 1;
+ return extractLine(fileContent, getEditOp(changeSet).pos1 + 1) + 1;
}
-ProjectFileFilesAdder::ProjectFileFilesAdder(const ProductData &product, const GroupData &group,
- const QStringList &files)
+ProjectFileFilesAdder::ProjectFileFilesAdder(ProductData product, GroupData group,
+ QStringList files)
: ProjectFileUpdater(product.location().filePath())
- , m_product(product)
- , m_group(group)
- , m_files(files)
+ , m_product(std::move(product))
+ , m_group(std::move(group))
+ , m_files(std::move(files))
{
}
@@ -319,7 +323,7 @@ void ProjectFileFilesAdder::doApply(QString &fileContent, UiProgram *ast)
}
const int itemIndentation
- = itemFinder.item()->qualifiedTypeNameId->firstSourceLocation().startColumn - 1;
+ = int(itemFinder.item()->qualifiedTypeNameId->firstSourceLocation().startColumn - 1);
const int bindingIndentation = itemIndentation + 4;
const int arrayElemIndentation = bindingIndentation + 4;
@@ -405,12 +409,12 @@ void ProjectFileFilesAdder::doApply(QString &fileContent, UiProgram *ast)
changeSet.apply(&fileContent);
}
-ProjectFileFilesRemover::ProjectFileFilesRemover(const ProductData &product, const GroupData &group,
- const QStringList &files)
+ProjectFileFilesRemover::ProjectFileFilesRemover(ProductData product, GroupData group,
+ QStringList files)
: ProjectFileUpdater(product.location().filePath())
- , m_product(product)
- , m_group(group)
- , m_files(files)
+ , m_product(std::move(product))
+ , m_group(std::move(group))
+ , m_files(std::move(files))
{
}
@@ -444,7 +448,7 @@ void ProjectFileFilesRemover::doApply(QString &fileContent, UiProgram *ast)
Rewriter rewriter(fileContent, &changeSet, QStringList());
const int itemIndentation
- = itemFinder.item()->qualifiedTypeNameId->firstSourceLocation().startColumn - 1;
+ = int(itemFinder.item()->qualifiedTypeNameId->firstSourceLocation().startColumn - 1);
const int bindingIndentation = itemIndentation + 4;
const int arrayElemIndentation = bindingIndentation + 4;
@@ -472,7 +476,7 @@ void ProjectFileFilesRemover::doApply(QString &fileContent, UiProgram *ast)
}
QString filesString;
filesString += QLatin1String("[\n");
- for (const QString &file : qAsConst(newFilesList)) {
+ for (const QString &file : std::as_const(newFilesList)) {
filesString += QString(arrayElemIndentation, QLatin1Char(' '));
filesString += QStringLiteral("\"%1\",\n").arg(file);
}
@@ -512,10 +516,10 @@ void ProjectFileFilesRemover::doApply(QString &fileContent, UiProgram *ast)
}
-ProjectFileGroupRemover::ProjectFileGroupRemover(const ProductData &product, const GroupData &group)
+ProjectFileGroupRemover::ProjectFileGroupRemover(ProductData product, GroupData group)
: ProjectFileUpdater(product.location().filePath())
- , m_product(product)
- , m_group(group)
+ , m_product(std::move(product))
+ , m_group(std::move(group))
{
}
diff --git a/src/lib/corelib/api/projectfileupdater.h b/src/lib/corelib/api/projectfileupdater.h
index c0d46c747..ded4e08a9 100644
--- a/src/lib/corelib/api/projectfileupdater.h
+++ b/src/lib/corelib/api/projectfileupdater.h
@@ -61,7 +61,7 @@ public:
int lineOffset() const { return m_lineOffset; }
protected:
- ProjectFileUpdater(const QString &projectFile);
+ ProjectFileUpdater(QString projectFile);
QString projectFile() const { return m_projectFile; }
@@ -85,14 +85,14 @@ private:
const QString m_projectFile;
CodeLocation m_itemPosition;
- int m_lineOffset;
+ int m_lineOffset = 0;
};
class ProjectFileGroupInserter : public ProjectFileUpdater
{
public:
- ProjectFileGroupInserter(const ProductData &product, const QString &groupName);
+ ProjectFileGroupInserter(ProductData product, QString groupName);
private:
void doApply(QString &fileContent, QbsQmlJS::AST::UiProgram *ast) override;
@@ -105,8 +105,7 @@ private:
class ProjectFileFilesAdder : public ProjectFileUpdater
{
public:
- ProjectFileFilesAdder(const ProductData &product, const GroupData &group,
- const QStringList &files);
+ ProjectFileFilesAdder(ProductData product, GroupData group, QStringList files);
private:
void doApply(QString &fileContent, QbsQmlJS::AST::UiProgram *ast) override;
@@ -119,8 +118,8 @@ private:
class ProjectFileFilesRemover : public ProjectFileUpdater
{
public:
- ProjectFileFilesRemover(const ProductData &product, const GroupData &group,
- const QStringList &files);
+ ProjectFileFilesRemover(ProductData product, GroupData group,
+ QStringList files);
private:
void doApply(QString &fileContent, QbsQmlJS::AST::UiProgram *ast) override;
@@ -133,7 +132,7 @@ private:
class ProjectFileGroupRemover : public ProjectFileUpdater
{
public:
- ProjectFileGroupRemover(const ProductData &product, const GroupData &group);
+ ProjectFileGroupRemover(ProductData product, GroupData group);
private:
void doApply(QString &fileContent, QbsQmlJS::AST::UiProgram *ast) override;
diff --git a/src/lib/corelib/api/qmljsrewriter.cpp b/src/lib/corelib/api/qmljsrewriter.cpp
index 16817d682..d98fe62e0 100644
--- a/src/lib/corelib/api/qmljsrewriter.cpp
+++ b/src/lib/corelib/api/qmljsrewriter.cpp
@@ -41,10 +41,6 @@
#include <parser/qmljsast_p.h>
-#include <QtGui/qtextobject.h>
-#include <QtGui/qtextcursor.h>
-#include <QtGui/qtextdocument.h>
-
namespace QbsQmlJS {
using namespace AST;
@@ -63,12 +59,12 @@ static QString toString(UiQualifiedId *qualifiedId, QChar delimiter = QLatin1Cha
}
-Rewriter::Rewriter(const QString &originalText,
+Rewriter::Rewriter(QString originalText,
ChangeSet *changeSet,
- const QStringList &propertyOrder)
- : m_originalText(originalText)
+ QStringList propertyOrder)
+ : m_originalText(std::move(originalText))
, m_changeSet(changeSet)
- , m_propertyOrder(propertyOrder)
+ , m_propertyOrder(std::move(propertyOrder))
{
Q_ASSERT(changeSet);
}
@@ -136,8 +132,7 @@ Rewriter::Range Rewriter::addBinding(AST::UiObjectInitializer *ast,
break;
case ObjectBinding:
- newPropertyTemplate = QStringLiteral("%1: %2");
- break;
+ Q_FALLTHROUGH();
case ScriptBinding:
newPropertyTemplate = QStringLiteral("%1: %2");
@@ -192,8 +187,7 @@ UiObjectMemberList *Rewriter::searchMemberToInsertAfter(UiObjectMemberList *memb
if (lastObjectDef)
return lastObjectDef;
- else
- return lastNonObjectDef;
+ return lastNonObjectDef;
}
UiArrayMemberList *Rewriter::searchMemberToInsertAfter(UiArrayMemberList *members,
@@ -225,8 +219,7 @@ UiArrayMemberList *Rewriter::searchMemberToInsertAfter(UiArrayMemberList *member
if (lastObjectDef)
return lastObjectDef;
- else
- return lastNonObjectDef;
+ return lastNonObjectDef;
}
UiObjectMemberList *Rewriter::searchMemberToInsertAfter(UiObjectMemberList *members,
@@ -260,7 +253,7 @@ UiObjectMemberList *Rewriter::searchMemberToInsertAfter(UiObjectMemberList *memb
idx = propertyOrder.size() - 1;
for (; idx > 0; --idx) {
- const QString prop = propertyOrder.at(idx - 1);
+ const QString &prop = propertyOrder.at(idx - 1);
UiObjectMemberList *candidate = orderedMembers.value(prop, 0);
if (candidate != nullptr)
return candidate;
@@ -305,7 +298,8 @@ void Rewriter::changeBinding(UiObjectInitializer *ast,
break;
// for grouped properties:
- } else if (!prefix.isEmpty()) {
+ }
+ if (!prefix.isEmpty()) {
if (auto def = cast<UiObjectDefinition *>(member)) {
if (toString(def->qualifiedTypeNameId) == prefix)
changeBinding(def->initializer, suffix, newValue, binding);
@@ -359,22 +353,20 @@ bool Rewriter::isMatchingPropertyMember(const QString &propertyName,
{
if (auto publicMember = cast<UiPublicMember*>(member))
return publicMember->name == propertyName;
- else if (auto objectBinding = cast<UiObjectBinding*>(member))
+ if (auto objectBinding = cast<UiObjectBinding*>(member))
return toString(objectBinding->qualifiedId) == propertyName;
- else if (auto scriptBinding = cast<UiScriptBinding*>(member))
+ if (auto scriptBinding = cast<UiScriptBinding*>(member))
return toString(scriptBinding->qualifiedId) == propertyName;
- else if (auto arrayBinding = cast<UiArrayBinding*>(member))
+ if (auto arrayBinding = cast<UiArrayBinding*>(member))
return toString(arrayBinding->qualifiedId) == propertyName;
- else
- return false;
+ 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;
+ return false;
}
void Rewriter::insertIntoArray(UiArrayBinding *ast, const QString &newValue)
@@ -469,7 +461,8 @@ bool Rewriter::includeSurroundingWhitespace(const QString &source, int &start, i
paragraphFound = true;
paragraphSkipped = true;
break;
- } else if (end == source.length()) {
+ }
+ if (end == source.length()) {
break;
}
@@ -500,28 +493,19 @@ bool Rewriter::includeSurroundingWhitespace(const QString &source, int &start, i
return paragraphFound;
}
-void Rewriter::includeLeadingEmptyLine(const QString &source, int &start)
+void Rewriter::includeLeadingEmptyLine(QStringView source, int &start)
{
- QTextDocument doc(source);
-
if (start == 0)
return;
- if (doc.characterAt(start - 1) != QChar::ParagraphSeparator)
+ const qsizetype lineEnd = source.lastIndexOf(QChar::LineFeed, start);
+ if (lineEnd <= 0)
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())
+ const qsizetype lineStart = source.lastIndexOf(QChar::LineFeed, lineEnd - 1) + 1;
+ const auto line = source.mid(lineStart, lineEnd - lineStart);
+ if (!line.trimmed().isEmpty())
return;
-
- start = prevBlock.position();
+ start = lineStart;
}
void Rewriter::includeEmptyGroupedProperty(UiObjectDefinition *groupedProperty, UiObjectMember *memberToBeRemoved, int &start, int &end)
@@ -649,7 +633,7 @@ Rewriter::Range Rewriter::addObject(UiObjectInitializer *ast, const QString &con
textToInsert += content;
m_changeSet->insert(insertionPoint, QLatin1String("\n") + textToInsert);
- return Range(insertionPoint, insertionPoint);
+ return {insertionPoint, insertionPoint};
}
Rewriter::Range Rewriter::addObject(UiArrayBinding *ast, const QString &content)
@@ -672,7 +656,7 @@ Rewriter::Range Rewriter::addObject(UiArrayBinding *ast, const QString &content,
m_changeSet->insert(insertionPoint, textToInsert);
- return Range(insertionPoint, insertionPoint);
+ return {insertionPoint, insertionPoint};
}
void Rewriter::removeObjectMember(UiObjectMember *member, UiObjectMember *parent)
diff --git a/src/lib/corelib/api/qmljsrewriter.h b/src/lib/corelib/api/qmljsrewriter.h
index 797b05459..7cba699e2 100644
--- a/src/lib/corelib/api/qmljsrewriter.h
+++ b/src/lib/corelib/api/qmljsrewriter.h
@@ -60,9 +60,9 @@ public:
using Range = ChangeSet::Range;
public:
- Rewriter(const QString &originalText,
+ Rewriter(QString originalText,
ChangeSet *changeSet,
- const QStringList &propertyOrder);
+ QStringList propertyOrder);
Range addBinding(AST::UiObjectInitializer *ast,
const QString &propertyName,
@@ -97,7 +97,7 @@ public:
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 includeLeadingEmptyLine(QStringView source, int &start);
static void includeEmptyGroupedProperty(AST::UiObjectDefinition *groupedProperty, AST::UiObjectMember *memberToBeRemoved, int &start, int &end);
private:
diff --git a/src/lib/corelib/api/rulecommand.h b/src/lib/corelib/api/rulecommand.h
index 438849604..cebcfa949 100644
--- a/src/lib/corelib/api/rulecommand.h
+++ b/src/lib/corelib/api/rulecommand.h
@@ -44,11 +44,10 @@
#include <QtCore/qlist.h>
#include <QtCore/qshareddata.h>
+#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
class QProcessEnvironment;
-class QString;
-class QStringList;
QT_END_NAMESPACE
namespace qbs {
diff --git a/src/lib/corelib/api/runenvironment.cpp b/src/lib/corelib/api/runenvironment.cpp
index 94a74dac2..23d0359b0 100644
--- a/src/lib/corelib/api/runenvironment.cpp
+++ b/src/lib/corelib/api/runenvironment.cpp
@@ -109,15 +109,12 @@ RunEnvironment::RunEnvironment(const ResolvedProductPtr &product,
const TopLevelProjectConstPtr &project, const InstallOptions &installOptions,
const QProcessEnvironment &environment, const QStringList &setupRunEnvConfig,
Settings *settings, const Logger &logger)
- : d(new RunEnvironmentPrivate(product, project, installOptions, environment, setupRunEnvConfig,
- settings, logger))
+ : d(std::make_unique<RunEnvironmentPrivate>(product, project, installOptions, environment,
+ setupRunEnvConfig, settings, logger))
{
}
-RunEnvironment::~RunEnvironment()
-{
- delete d;
-}
+RunEnvironment::~RunEnvironment() = default;
int RunEnvironment::runShell(ErrorInfo *error)
{
@@ -228,7 +225,7 @@ int RunEnvironment::doRunShell()
static QString findExecutable(const QStringList &fileNames)
{
const QStringList path = QString::fromLocal8Bit(qgetenv("PATH"))
- .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+ .split(HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts);
for (const QString &fileName : fileNames) {
const QString exeFileName = HostOsInfo::appendExecutableSuffix(fileName);
@@ -316,13 +313,9 @@ int RunEnvironment::doRunTarget(const QString &targetBin, const QStringList &arg
if (!process.waitForFinished(-1)) {
if (process.error() == QProcess::FailedToStart) {
throw ErrorInfo(Tr::tr("The process '%1' could not be started: %2")
- .arg(targetExecutable)
- .arg(process.errorString()));
- } else {
- d->logger.qbsWarning()
- << "QProcess error: " << process.errorString();
+ .arg(targetExecutable, process.errorString()));
}
-
+ d->logger.qbsWarning() << "QProcess error: " << process.errorString();
return EXIT_FAILURE;
}
@@ -356,8 +349,7 @@ int RunEnvironment::doRunTarget(const QString &targetBin, const QStringList &arg
if (!process.waitForFinished(-1)) {
if (process.error() == QProcess::FailedToStart) {
throw ErrorInfo(Tr::tr("The process '%1' could not be started: %2")
- .arg(targetExecutable)
- .arg(process.errorString()));
+ .arg(targetExecutable, process.errorString()));
}
return EXIT_FAILURE;
@@ -371,16 +363,16 @@ int RunEnvironment::doRunTarget(const QString &targetBin, const QStringList &arg
<< arguments;
}
} else {
- if (QFileInfo(targetExecutable = findExecutable(QStringList()
- << QStringLiteral("iostool"))).isExecutable()) {
+ if (targetExecutable = findExecutable(QStringList{QStringLiteral("iostool")});
+ QFileInfo(targetExecutable).isExecutable()) {
targetArguments = QStringList()
<< QStringLiteral("-run")
<< QStringLiteral("-bundle")
<< QDir::cleanPath(bundlePath);
if (!arguments.empty())
targetArguments << QStringLiteral("-extra-args") << arguments;
- } else if (QFileInfo(targetExecutable = findExecutable(QStringList()
- << QStringLiteral("ios-deploy"))).isExecutable()) {
+ } else if (targetExecutable = findExecutable(QStringList{QStringLiteral("ios-deploy")});
+ QFileInfo(targetExecutable).isExecutable()) {
targetArguments = QStringList()
<< QStringLiteral("--no-wifi")
<< QStringLiteral("--noninteractive")
@@ -461,13 +453,9 @@ int RunEnvironment::doRunTarget(const QString &targetBin, const QStringList &arg
}
#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();
+ .arg(targetExecutable, errorPrefixString + process.errorString()));
}
-
+ d->logger.qbsWarning() << "QProcess error: " << process.errorString();
return EXIT_FAILURE;
}
return process.exitCode();
diff --git a/src/lib/corelib/api/runenvironment.h b/src/lib/corelib/api/runenvironment.h
index 69603bf76..4a967ba63 100644
--- a/src/lib/corelib/api/runenvironment.h
+++ b/src/lib/corelib/api/runenvironment.h
@@ -44,14 +44,17 @@
#include <tools/qbs_export.h>
#include <QtCore/qglobal.h>
+#include <QtCore/qstringlist.h>
+
+#include <memory>
QT_BEGIN_NAMESPACE
class QProcess;
class QProcessEnvironment;
-class QString;
-class QStringList;
QT_END_NAMESPACE
+class TestApi;
+
namespace qbs {
class ErrorInfo;
class InstallOptions;
@@ -66,7 +69,7 @@ class QBS_EXPORT RunEnvironment
{
friend class CommandLineFrontend;
friend class Project;
- friend class TestApi;
+ friend class ::TestApi;
public:
~RunEnvironment();
@@ -94,7 +97,7 @@ private:
const QProcessEnvironment getBuildEnvironment() const;
class RunEnvironmentPrivate;
- RunEnvironmentPrivate * const d;
+ const std::unique_ptr<RunEnvironmentPrivate> d;
};
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/artifact.cpp b/src/lib/corelib/buildgraph/artifact.cpp
index e82919560..8f94353e6 100644
--- a/src/lib/corelib/buildgraph/artifact.cpp
+++ b/src/lib/corelib/buildgraph/artifact.cpp
@@ -51,15 +51,21 @@
namespace qbs {
namespace Internal {
-Artifact::Artifact()
+Artifact::Artifact() :
+ artifactType(ArtifactType::Unknown),
+ inputsScanned(false),
+ timestampRetrieved(false),
+ alwaysUpdated(false),
+ oldDataPossiblyPresent(true)
{
- initialize();
}
Artifact::~Artifact()
{
for (Artifact *p : parentArtifacts())
p->childrenAddedByScanner.remove(this);
+ if (m_deregister)
+ m_deregister(this);
}
void Artifact::accept(BuildGraphVisitor *visitor)
@@ -120,15 +126,6 @@ RuleNode *Artifact::producer() const
return *ruleNodes.begin();
}
-void Artifact::initialize()
-{
- artifactType = Unknown;
- inputsScanned = false;
- timestampRetrieved = false;
- alwaysUpdated = true;
- oldDataPossiblyPresent = true;
-}
-
const TypeFilter<Artifact> Artifact::parentArtifacts() const
{
return TypeFilter<Artifact>(parents);
diff --git a/src/lib/corelib/buildgraph/artifact.h b/src/lib/corelib/buildgraph/artifact.h
index ee3acea59..fce34b6ef 100644
--- a/src/lib/corelib/buildgraph/artifact.h
+++ b/src/lib/corelib/buildgraph/artifact.h
@@ -50,6 +50,7 @@
#include <QtCore/qstring.h>
+#include <functional>
#include <utility>
#include <vector>
@@ -114,7 +115,6 @@ public:
bool alwaysUpdated : 1;
bool oldDataPossiblyPresent : 1;
- void initialize();
const TypeFilter<Artifact> parentArtifacts() const;
const TypeFilter<Artifact> childArtifacts() const;
void onChildDisconnected(BuildGraphNode *child) override;
@@ -124,8 +124,12 @@ public:
void load(PersistentPool &pool) override;
void store(PersistentPool &pool) override;
+ using Deregister = std::function<void(const Artifact *)>;
+ void setDeregister(const Deregister &deregister) { m_deregister = deregister; }
+
private:
FileTags m_fileTags;
+ Deregister m_deregister;
};
template<> inline QString Set<Artifact *>::toString(Artifact * const &artifact) const
diff --git a/src/lib/corelib/buildgraph/artifactcleaner.cpp b/src/lib/corelib/buildgraph/artifactcleaner.cpp
index 000dfda02..6239a138b 100644
--- a/src/lib/corelib/buildgraph/artifactcleaner.cpp
+++ b/src/lib/corelib/buildgraph/artifactcleaner.cpp
@@ -162,7 +162,7 @@ ArtifactCleaner::ArtifactCleaner(Logger logger, ProgressObserver *observer)
}
void ArtifactCleaner::cleanup(const TopLevelProjectPtr &project,
- const QList<ResolvedProductPtr> &products, const CleanOptions &options)
+ const QVector<ResolvedProductPtr> &products, const CleanOptions &options)
{
m_hasError = false;
@@ -181,7 +181,7 @@ void ArtifactCleaner::cleanup(const TopLevelProjectPtr &project,
// Directories created during the build are not artifacts (TODO: should they be?),
// so we have to clean them up manually.
- QList<QString> dirList = directories.toList();
+ auto dirList = rangeTo<QStringList>(directories);
for (int i = 0; i < dirList.size(); ++i) {
const QString &dir = dirList.at(i);
if (!dir.startsWith(project->buildDirectory))
diff --git a/src/lib/corelib/buildgraph/artifactcleaner.h b/src/lib/corelib/buildgraph/artifactcleaner.h
index 5112a75d6..8d0bef275 100644
--- a/src/lib/corelib/buildgraph/artifactcleaner.h
+++ b/src/lib/corelib/buildgraph/artifactcleaner.h
@@ -54,7 +54,7 @@ class ArtifactCleaner
{
public:
ArtifactCleaner(Logger logger, ProgressObserver *observer);
- void cleanup(const TopLevelProjectPtr &project, const QList<ResolvedProductPtr> &products,
+ void cleanup(const TopLevelProjectPtr &project, const QVector<ResolvedProductPtr> &products,
const CleanOptions &options);
private:
diff --git a/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp b/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp
index 2adb77d47..6d0ea738f 100644
--- a/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp
+++ b/src/lib/corelib/buildgraph/artifactsscriptvalue.cpp
@@ -45,171 +45,178 @@
#include <language/language.h>
#include <language/scriptengine.h>
-#include <QtScript/qscriptclass.h>
-#include <QtScript/qscriptcontext.h>
+#include <tools/stlutils.h>
+#include <tools/stringconstants.h>
namespace qbs {
namespace Internal {
-enum BuildGraphScriptValueCommonPropertyKeys : quint32 {
- CachedValueKey,
- FileTagKey,
- ProductPtrKey,
-};
-
-class ArtifactsScriptClass : public QScriptClass
+template<class ProductOrModule>
+static bool isRelevantArtifact(const ProductOrModule *productOrModule, const Artifact *artifact)
{
-public:
- ArtifactsScriptClass(QScriptEngine *engine) : QScriptClass(engine) { }
-
-private:
- QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name,
- QueryFlags flags, uint *id) override
- {
- getProduct(object);
- qbsEngine()->setNonExistingArtifactSetRequested(m_product, name.toString());
- return QScriptClass::queryProperty(object, name, flags, id);
- }
-
- QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override
- {
- getProduct(object);
- qbsEngine()->setArtifactsEnumerated(m_product);
- return QScriptClass::newIterator(object);
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) {
+ Q_UNUSED(productOrModule)
+ return !artifact->isTargetOfModule();
+ } else {
+ return artifact->targetOfModule == productOrModule->name;
}
-
- void getProduct(const QScriptValue &object)
- {
- if (m_lastObjectId != object.objectId()) {
- m_lastObjectId = object.objectId();
- m_product = reinterpret_cast<const ResolvedProduct *>(
- object.data().property(ProductPtrKey).toVariant().value<quintptr>());
- }
- }
-
- ScriptEngine *qbsEngine() const { return static_cast<ScriptEngine *>(engine()); }
-
- qint64 m_lastObjectId = 0;
- const ResolvedProduct *m_product = nullptr;
-};
-
-static bool isRelevantArtifact(const ResolvedProduct *, const Artifact *artifact)
-{
- return !artifact->isTargetOfModule();
-}
-static bool isRelevantArtifact(const ResolvedModule *module, const Artifact *artifact)
-{
- return artifact->targetOfModule == module->name;
}
-static ArtifactSetByFileTag artifactsMap(const ResolvedProduct *product)
+template<class ProductOrModule>
+static ArtifactSetByFileTag artifactsMap(const ProductOrModule *productOrModule)
{
- return product->buildData->artifactsByFileTag();
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ return productOrModule->buildData->artifactsByFileTag();
+ else
+ return artifactsMap(productOrModule->product);
}
-static ArtifactSetByFileTag artifactsMap(const ResolvedModule *module)
+template<class ProductOrModule> static int scriptClassIndex()
{
- return artifactsMap(module->product);
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ return 0;
+ return 1;
}
-static QScriptValue createArtifactsObject(const ResolvedProduct *product, ScriptEngine *engine)
+template<class ProductOrModule>
+std::unique_lock<std::mutex> getArtifactsMapLock(ProductOrModule *productOrModule)
{
- QScriptClass *scriptClass = engine->artifactsScriptClass();
- if (!scriptClass) {
- scriptClass = new ArtifactsScriptClass(engine);
- engine->setArtifactsScriptClass(scriptClass);
- }
- QScriptValue artifactsObj = engine->newObject(scriptClass);
- QScriptValue data = engine->newObject();
- QVariant v;
- v.setValue<quintptr>(reinterpret_cast<quintptr>(product));
- data.setProperty(ProductPtrKey, engine->newVariant(v));
- artifactsObj.setData(data);
- return artifactsObj;
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ return productOrModule->buildData->getArtifactsMapLock();
+ else
+ return getArtifactsMapLock(productOrModule->product);
}
-static QScriptValue createArtifactsObject(const ResolvedModule *, ScriptEngine *engine)
+template<class ProductOrModule>
+static bool checkAndSetArtifactsMapUpToDateFlag(const ProductOrModule *productOrModule)
{
- return engine->newObject();
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ return productOrModule->buildData->checkAndSetJsArtifactsMapUpToDateFlag();
+ else
+ return checkAndSetArtifactsMapUpToDateFlag(productOrModule->product);
}
-static bool checkAndSetArtifactsMapUpToDateFlag(const ResolvedProduct *p)
+// Must be called with artifacts map lock held!
+template<class ProductOrModule>
+void registerArtifactsMapAccess(ScriptEngine *engine, ProductOrModule *productOrModule)
{
- return p->buildData->checkAndSetJsArtifactsMapUpToDateFlag();
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>) {
+ if (!checkAndSetArtifactsMapUpToDateFlag(productOrModule))
+ engine->setArtifactsMapRequested(productOrModule, true);
+ else
+ engine->setArtifactsMapRequested(productOrModule, false);
+ } else {
+ registerArtifactsMapAccess(engine, productOrModule->product);
+ }
}
-static bool checkAndSetArtifactsMapUpToDateFlag(const ResolvedModule *) { return true; }
-static void registerArtifactsMapAccess(const ResolvedProduct *p, ScriptEngine *e, bool forceUpdate)
-{
- e->setArtifactsMapRequested(p, forceUpdate);
-}
-static void registerArtifactsMapAccess(const ResolvedModule *, ScriptEngine *, bool) {}
-static void registerArtifactsSetAccess(const ResolvedProduct *p, const FileTag &t, ScriptEngine *e)
+template<class ProductOrModule>
+static int getArtifactsPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj)
{
- e->setArtifactSetRequestedForTag(p, t);
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto productOrModule = attachedPointer<ProductOrModule>(
+ obj, engine->artifactsScriptClass(scriptClassIndex<ProductOrModule>()));
+ const std::unique_lock lock = getArtifactsMapLock(productOrModule);
+ registerArtifactsMapAccess(engine, productOrModule);
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ engine->setArtifactsEnumerated(productOrModule);
+ const auto &map = artifactsMap(productOrModule);
+ const auto filter = [productOrModule](const Artifact *a) {
+ return isRelevantArtifact(productOrModule, a);
+ };
+ QStringList tags;
+ for (auto it = map.cbegin(); it != map.cend(); ++it) {
+ if (any_of(it.value(), filter)) {
+ tags << it.key().toString();
+ }
+ }
+ *plen = tags.size();
+ if (!tags.isEmpty()) {
+ *ptab = reinterpret_cast<JSPropertyEnum *>(js_malloc(ctx, *plen * sizeof **ptab));
+ JSPropertyEnum *entry = *ptab;
+ for (const QString &tag : std::as_const(tags)) {
+ entry->atom = JS_NewAtom(ctx, tag.toUtf8().constData());
+ entry->is_enumerable = 1;
+ ++entry;
+ }
+ } else {
+ *ptab = nullptr;
+ }
+ return 0;
}
-static void registerArtifactsSetAccess(const ResolvedModule *, const FileTag &, ScriptEngine *) {}
-template<class ProductOrModule> static QScriptValue js_artifactsForFileTag(
- QScriptContext *ctx, ScriptEngine *engine, const ProductOrModule *productOrModule)
+template<class ProductOrModule>
+static int getArtifactsProperty(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop)
{
- const FileTag fileTag = FileTag(ctx->callee().property(FileTagKey).toString().toUtf8());
- registerArtifactsSetAccess(productOrModule, fileTag, engine);
- QScriptValue result = ctx->callee().property(CachedValueKey);
- if (result.isArray())
- return result;
- auto artifacts = artifactsMap(productOrModule).value(fileTag);
- const auto filter = [productOrModule](const Artifact *a) {
+ if (!desc)
+ return 1;
+
+ desc->flags = JS_PROP_ENUMERABLE;
+ desc->value = desc->getter = desc->setter = JS_UNDEFINED;
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto productOrModule = attachedPointer<ProductOrModule>(
+ obj, engine->artifactsScriptClass(scriptClassIndex<ProductOrModule>()));
+ const std::unique_lock lock = getArtifactsMapLock(productOrModule);
+ registerArtifactsMapAccess(engine, productOrModule);
+ const QString tagString = getJsString(ctx, prop);
+ const FileTag fileTag(tagString.toUtf8());
+ const auto &map = artifactsMap(productOrModule);
+ const auto it = map.constFind(fileTag);
+ if (it == map.constEnd()) {
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ engine->setNonExistingArtifactSetRequested(productOrModule, tagString);
+ return 1;
+ }
+ if constexpr (std::is_same_v<ProductOrModule, ResolvedProduct>)
+ engine->setArtifactSetRequestedForTag(productOrModule, fileTag);
+ ArtifactSet artifacts = it.value();
+ removeIf(artifacts, [productOrModule](const Artifact *a) {
return !isRelevantArtifact(productOrModule, a);
- };
- artifacts.erase(std::remove_if(artifacts.begin(), artifacts.end(), filter), artifacts.end());
- result = engine->newArray(uint(artifacts.size()));
- ctx->callee().setProperty(CachedValueKey, result);
- int k = 0;
- for (const Artifact * const artifact : artifacts)
- result.setProperty(k++, Transformer::translateFileConfig(engine, artifact, QString()));
- return result;
+ });
+ if (!artifacts.empty()) {
+ desc->value = JS_NewArray(ctx); // TODO: Also cache this list?
+ int k = 0;
+ for (Artifact * const artifact : artifacts) {
+ JS_SetPropertyUint32(ctx, desc->value, k++,
+ Transformer::translateFileConfig(engine, artifact, QString()));
+ }
+ }
+ return 1;
}
-template<class ProductOrModule> static QScriptValue js_artifacts(
- QScriptContext *ctx, ScriptEngine *engine, const ProductOrModule *productOrModule)
+template<class ProductOrModule> static JSValue js_artifacts(JSContext *ctx,
+ JSValue jsProductOrModule)
{
- QScriptValue artifactsObj = ctx->callee().property(CachedValueKey);
- if (artifactsObj.isObject() && checkAndSetArtifactsMapUpToDateFlag(productOrModule)) {
- registerArtifactsMapAccess(productOrModule, engine, false);
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto productOrModule = attachedPointer<ProductOrModule>(jsProductOrModule,
+ engine->dataWithPtrClass());
+ JSValue artifactsObj = engine->artifactsMapScriptValue(productOrModule);
+ if (!JS_IsUndefined(artifactsObj))
return artifactsObj;
+ const int classIndex = scriptClassIndex<ProductOrModule>();
+ JSClassID scriptClass = engine->artifactsScriptClass(classIndex);
+ if (scriptClass == 0) {
+ const QByteArray className = "ArtifactsScriptClass" + QByteArray::number(classIndex);
+ scriptClass = engine->registerClass(className.constData(), nullptr, nullptr, JS_UNDEFINED,
+ &getArtifactsPropertyNames<ProductOrModule>,
+ &getArtifactsProperty<ProductOrModule>);
+ engine->setArtifactsScriptClass(classIndex, scriptClass);
}
- registerArtifactsMapAccess(productOrModule, engine, true);
- artifactsObj = createArtifactsObject(productOrModule, engine);
- ctx->callee().setProperty(CachedValueKey, artifactsObj);
- const auto &map = artifactsMap(productOrModule);
- for (auto it = map.cbegin(); it != map.cend(); ++it) {
- const auto filter = [productOrModule](const Artifact *a) {
- return isRelevantArtifact(productOrModule, a);
- };
- if (std::none_of(it.value().cbegin(), it.value().cend(), filter))
- continue;
- QScriptValue fileTagFunc = engine->newFunction(&js_artifactsForFileTag<ProductOrModule>,
- productOrModule);
- const QString fileTag = it.key().toString();
- fileTagFunc.setProperty(FileTagKey, fileTag);
- artifactsObj.setProperty(fileTag, fileTagFunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
- }
+ artifactsObj = JS_NewObjectClass(engine->context(), scriptClass);
+ attachPointerTo(artifactsObj, productOrModule);
return artifactsObj;
}
-QScriptValue artifactsScriptValueForProduct(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedProduct *product)
+JSValue artifactsScriptValueForProduct(JSContext *ctx, JSValue this_val, int, JSValue *)
{
- return js_artifacts(ctx, engine, product);
+ return js_artifacts<ResolvedProduct>(ctx, this_val);
}
-QScriptValue artifactsScriptValueForModule(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedModule *module)
+JSValue artifactsScriptValueForModule(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- return js_artifacts(ctx, engine, module);
+ return js_artifacts<ResolvedModule>(ctx, this_val);
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/artifactsscriptvalue.h b/src/lib/corelib/buildgraph/artifactsscriptvalue.h
index c0da8a05e..dce457758 100644
--- a/src/lib/corelib/buildgraph/artifactsscriptvalue.h
+++ b/src/lib/corelib/buildgraph/artifactsscriptvalue.h
@@ -41,21 +41,14 @@
#include <language/forward_decls.h>
-#include <QtScript/qscriptvalue.h>
-
-QT_BEGIN_NAMESPACE
-class QScriptContext;
-QT_END_NAMESPACE
+#include <quickjs.h>
namespace qbs {
namespace Internal {
class ScriptEngine;
-QScriptValue artifactsScriptValueForProduct(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedProduct *product);
-
-QScriptValue artifactsScriptValueForModule(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedModule *module);
+JSValue artifactsScriptValueForProduct(JSContext *ctx, JSValueConst this_val, int, JSValueConst *);
+JSValue artifactsScriptValueForModule(JSContext *ctx, JSValueConst this_val, int, JSValueConst *);
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/artifactvisitor.cpp b/src/lib/corelib/buildgraph/artifactvisitor.cpp
index c28f07424..885292f06 100644
--- a/src/lib/corelib/buildgraph/artifactvisitor.cpp
+++ b/src/lib/corelib/buildgraph/artifactvisitor.cpp
@@ -55,13 +55,13 @@ void ArtifactVisitor::visitProduct(const ResolvedProductConstPtr &product)
{
if (!product->buildData)
return;
- for (BuildGraphNode *node : qAsConst(product->buildData->allNodes()))
+ for (BuildGraphNode *node : std::as_const(product->buildData->allNodes()))
node->accept(this);
}
void ArtifactVisitor::visitProject(const ResolvedProjectConstPtr &project)
{
- for (const ResolvedProductConstPtr &product : project->allProducts())
+ for (const auto &product : project->allProducts())
visitProduct(product);
}
diff --git a/src/lib/corelib/buildgraph/buildgraph.cpp b/src/lib/corelib/buildgraph/buildgraph.cpp
index 3726c654d..10aae5991 100644
--- a/src/lib/corelib/buildgraph/buildgraph.cpp
+++ b/src/lib/corelib/buildgraph/buildgraph.cpp
@@ -45,7 +45,6 @@
#include "projectbuilddata.h"
#include "productbuilddata.h"
#include "rulenode.h"
-#include "scriptclasspropertyiterator.h"
#include "transformer.h"
#include <jsextensions/jsextensions.h>
@@ -62,14 +61,14 @@
#include <logging/translator.h>
#include <tools/error.h>
#include <tools/fileinfo.h>
-#include <tools/scripttools.h>
#include <tools/qbsassert.h>
#include <tools/qttools.h>
+#include <tools/scripttools.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
-#include <QtScript/qscriptclass.h>
#include <algorithm>
#include <iterator>
@@ -82,146 +81,187 @@ static QString childItemsProperty() { return QStringLiteral("childItems"); }
static QString exportsProperty() { return QStringLiteral("exports"); }
// TODO: Introduce productscriptvalue.{h,cpp}.
-static QScriptValue getDataForProductScriptValue(QScriptEngine *engine,
- const ResolvedProduct *product)
+
+static JSValue getDataObject(JSContext *ctx, JSValue obj)
{
- QScriptValue data = engine->newObject();
- QVariant v;
- v.setValue<quintptr>(reinterpret_cast<quintptr>(product));
- data.setProperty(ProductPtrKey, engine->newVariant(v));
- return data;
+ return getJsProperty(ctx, obj, StringConstants::dataPropertyInternal());
}
-class ProductPropertyScriptClass : public QScriptClass
+static JSValue getValueFromData(JSContext *ctx, JSValue data,
+ ModulePropertiesScriptValueCommonPropertyKeys key)
{
-public:
- ProductPropertyScriptClass(QScriptEngine *engine) : QScriptClass(engine) { }
-
-private:
- QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags,
- uint *) override
- {
- if (name == StringConstants::parametersProperty()) {
- m_result = object.data().property(DependencyParametersKey);
- return HandlesReadAccess;
- }
- if (name == StringConstants::moduleNameProperty()) {
- m_result = object.data().property(ModuleNameKey);
- return HandlesReadAccess;
- }
- if (name == StringConstants::dependenciesProperty()
- || name == StringConstants::artifactsProperty()
- || name == exportsProperty()) {
- // The prototype is not backed by a QScriptClass.
- m_result = object.prototype().property(name);
- return HandlesReadAccess;
- }
-
- getProduct(object);
- QBS_ASSERT(m_product, return {});
-
- const auto it = m_product->productProperties.find(name);
+ return JS_GetPropertyUint32(ctx, data, key);
+}
- // It is important that we reject unknown property names. Otherwise QtScript will forward
- // *everything* to us, including built-in stuff like the hasOwnProperty function.
- if (it == m_product->productProperties.cend())
- return {};
+static JSValue getValueFromObject(JSContext *ctx, JSValue obj,
+ ModulePropertiesScriptValueCommonPropertyKeys key)
+{
+ const ScopedJsValue data(ctx, getDataObject(ctx, obj));
+ return getValueFromData(ctx, data, key);
+}
- qbsEngine()->addPropertyRequestedInScript(Property(m_product->uniqueName(), QString(), name,
- it.value(), Property::PropertyInProduct));
- m_result = qbsEngine()->toScriptValue(it.value());
- return HandlesReadAccess;
+void getPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ const QVariantMap &properties, const QStringList &extraPropertyNames,
+ JSValue extraObject)
+{
+ JSPropertyEnum *basePTab = nullptr;
+ uint32_t basePlen = 0;
+ JS_GetOwnPropertyNames(ctx, &basePTab, &basePlen, extraObject,
+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY);
+ *plen = uint32_t(extraPropertyNames.size() + properties.size() + basePlen);
+ *ptab = reinterpret_cast<JSPropertyEnum *>(js_malloc(ctx, *plen * sizeof **ptab));
+ JSPropertyEnum *entry = *ptab;
+ for (auto it = properties.begin(); it != properties.end(); ++it, ++entry) {
+ entry->atom = JS_NewAtom(ctx, it.key().toUtf8().constData());
+ entry->is_enumerable = 1;
}
-
- QScriptValue property(const QScriptValue &, const QScriptString &, uint) override
- {
- return m_result;
+ for (const QString &prop : extraPropertyNames) {
+ entry->atom = JS_NewAtom(ctx, prop.toUtf8().constData());
+ entry->is_enumerable = 1;
+ ++entry;
+ }
+ for (uint32_t i = 0; i < basePlen; ++i, ++entry) {
+ entry->atom = basePTab[i].atom;
+ entry->is_enumerable = 1;
+ }
+ js_free(ctx, basePTab);
+}
+
+static int getProductPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj)
+{
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const ScopedJsValue data(ctx, getDataObject(engine->context(), obj));
+ const auto product = attachedPointer<ResolvedProduct>(
+ obj, engine->productPropertyScriptClass());
+ QBS_ASSERT(product, return -1);
+
+ // The "moduleName" convenience property is only available for the "main product" in a rule,
+ // and the "parameters" property exists only for elements of the "dependencies" array for
+ // which dependency parameters are present.
+ const auto hasModuleName = [&] {
+ const ScopedJsValue v(ctx, getValueFromData(ctx, data, ModuleNameKey));
+ return JS_IsString(v);
+ };
+ const auto hasDependencyParams = [&] {
+ const ScopedJsValue v(ctx, getValueFromData(ctx, data, DependencyParametersKey));
+ return JS_IsObject(v);
+ };
+ QStringList additionalProperties;
+ if (hasModuleName())
+ additionalProperties.push_back(StringConstants::moduleNameProperty());
+ else if (hasDependencyParams())
+ additionalProperties.push_back(StringConstants::parametersProperty());
+ getPropertyNames(ctx, ptab, plen, product->productProperties, additionalProperties,
+ engine->baseProductScriptValue(product));
+ return 0;
+}
+
+static int getProductProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValueConst obj,
+ JSAtom prop)
+{
+ if (desc) {
+ desc->getter = desc->setter = desc->value = JS_UNDEFINED;
+ desc->flags = JS_PROP_ENUMERABLE;
+ }
+ const QString name = getJsString(ctx, prop);
+ if (name == StringConstants::parametersProperty()) {
+ if (desc)
+ desc->value = getValueFromObject(ctx, obj, DependencyParametersKey);
+ return 1;
+ }
+ if (name == StringConstants::moduleNameProperty()) {
+ if (desc)
+ desc->value = getValueFromObject(ctx, obj, ModuleNameKey);
+ return 1;
}
- QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override
- {
- getProduct(object);
- QBS_ASSERT(m_product, return nullptr);
-
- // These two are in the prototype and are thus common to all product script values.
- std::vector<QString> additionalProperties({StringConstants::artifactsProperty(),
- StringConstants::dependenciesProperty(),
- exportsProperty()});
-
- // The "moduleName" convenience property is only available for the "main product" in a rule,
- // and the "parameters" property exists only for elements of the "dependencies" array for
- // which dependency parameters are present.
- if (object.data().property(ModuleNameKey).isValid())
- additionalProperties.push_back(StringConstants::moduleNameProperty());
- else if (object.data().property(DependencyParametersKey).isValid())
- additionalProperties.push_back(StringConstants::parametersProperty());
- return new ScriptClassPropertyIterator(object, m_product->productProperties,
- additionalProperties);
- }
-
- void getProduct(const QScriptValue &object)
- {
- if (m_lastObjectId != object.objectId()) {
- m_lastObjectId = object.objectId();
- m_product = reinterpret_cast<const ResolvedProduct *>(
- object.data().property(ProductPtrKey).toVariant().value<quintptr>());
- }
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto product = attachedPointer<ResolvedProduct>(
+ obj, engine->productPropertyScriptClass());
+ QBS_ASSERT(product, return -1);
+
+ const JSValue baseProductValue = engine->baseProductScriptValue(product);
+ if (name == exportsProperty()) {
+ if (desc)
+ desc->value = getJsProperty(ctx, baseProductValue, name);
+ engine->addRequestedExport(product);
+ return 1;
}
- ScriptEngine *qbsEngine() const { return static_cast<ScriptEngine *>(engine()); }
+ const auto it = product->productProperties.constFind(name);
+ if (it == product->productProperties.cend()) {
+ ScopedJsValue v(ctx, JS_GetProperty(ctx, baseProductValue, prop));
+ const int ret = JS_IsUndefined(v) ? 0 : 1;
+ if (desc)
+ desc->value = v.release();
+ return ret;
+ }
- qint64 m_lastObjectId = 0;
- const ResolvedProduct *m_product = nullptr;
- QScriptValue m_result;
-};
+ engine->addPropertyRequestedInScript(Property(product->uniqueName(), QString(), name,
+ it.value(), Property::PropertyInProduct));
+ if (desc)
+ desc->value = engine->toScriptValue(it.value());
+ return 1;
+}
-static QScriptValue setupProjectScriptValue(ScriptEngine *engine,
- const ResolvedProjectConstPtr &project)
+static JSValue setupProjectScriptValue(ScriptEngine *engine, const ResolvedProjectConstPtr &project)
{
- QScriptValue &obj = engine->projectScriptValue(project.get());
- if (obj.isValid())
- return obj;
+ JSValue &obj = engine->projectScriptValue(project.get());
+ if (JS_IsObject(obj))
+ return JS_DupValue(engine->context(), obj);
obj = engine->newObject();
- obj.setProperty(StringConstants::filePathProperty(), project->location.filePath());
- obj.setProperty(StringConstants::pathProperty(), FileInfo::path(project->location.filePath()));
+ setJsProperty(engine->context(), obj, StringConstants::filePathProperty(),
+ project->location.filePath());
+ setJsProperty(engine->context(), obj, StringConstants::pathProperty(),
+ 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()));
+ const ScopedJsValue val(engine->context(), engine->toScriptValue(it.value()));
+ engine->setObservedProperty(obj, it.key(), val);
}
- engine->observer()->addProjectObjectId(obj.objectId(), project->name);
- return obj;
+ engine->observer()->addProjectObjectId(jsObjectId(obj), project->name);
+ return JS_DupValue(engine->context(), obj);
}
-static QScriptValue setupProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product);
+static void setupBaseProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product);
class DependenciesFunction
{
public:
- DependenciesFunction(ScriptEngine *engine)
- : m_engine(engine)
- {
- }
+ DependenciesFunction(ScriptEngine *engine) : m_engine(engine) { }
- void init(QScriptValue &productScriptValue, QScriptValue &exportsScriptValue,
- const ResolvedProduct *product)
+ void init(JSValue &productScriptValue, JSValue exportsScriptValue)
{
- QScriptValue depfunc = m_engine->newFunction(&js_internalProductDependencies, product);
- productScriptValue.setProperty(StringConstants::dependenciesProperty(), depfunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
- depfunc = m_engine->newFunction(&js_exportedProductDependencies, product);
- exportsScriptValue.setProperty(StringConstants::dependenciesProperty(), depfunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
+ const QByteArray name = StringConstants::dependenciesProperty().toUtf8();
+ const ScopedJsValue depfunc(
+ m_engine->context(),
+ JS_NewCFunction(m_engine->context(), &js_internalProductDependencies,
+ name.constData(), 0));
+ const ScopedJsAtom depAtom(m_engine->context(), name);
+ JS_DefineProperty(m_engine->context(), productScriptValue, depAtom, JS_UNDEFINED, depfunc,
+ JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
+ const ScopedJsValue exportedDepfunc(
+ m_engine->context(),
+ JS_NewCFunction(m_engine->context(), &js_exportedProductDependencies,
+ name.constData(), 0));
+ JS_DefineProperty(m_engine->context(), exportsScriptValue, depAtom, JS_UNDEFINED,
+ exportedDepfunc, JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
}
private:
enum class DependencyType { Internal, Exported };
- static QScriptValue js_productDependencies(QScriptContext *, ScriptEngine *engine,
- const ResolvedProduct *product, DependencyType depType)
+ static JSValue js_productDependencies(const ResolvedProduct *product,
+ JSContext *ctx, JSValueConst this_val, int argc,
+ JSValueConst *argv, DependencyType depType)
{
- QScriptValue result = engine->newArray();
+ Q_UNUSED(this_val)
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ JSValue result = JS_NewArray(ctx);
quint32 idx = 0;
const bool exportCase = depType == DependencyType::Exported;
std::vector<ResolvedProductPtr> productDeps;
@@ -243,202 +283,234 @@ private:
} else {
productDeps = product->dependencies;
}
- for (const ResolvedProductPtr &dependency : qAsConst(productDeps)) {
- QScriptValue obj = engine->newObject(engine->productPropertyScriptClass());
- obj.setPrototype(setupProductScriptValue(static_cast<ScriptEngine *>(engine),
- dependency.get()));
+ for (const ResolvedProductPtr &dependency : std::as_const(productDeps)) {
+ setupBaseProductScriptValue(engine, dependency.get());
+ JSValue obj = JS_NewObjectClass(engine->context(),
+ engine->productPropertyScriptClass());
+ attachPointerTo(obj, dependency.get());
const QVariantMap &params
= (exportCase ? product->exportedModule.dependencyParameters.value(dependency)
: product->dependencyParameters.value(dependency));
- QScriptValue data = getDataForProductScriptValue(engine, dependency.get());
- data.setProperty(DependencyParametersKey, dependencyParametersValue(
- product->uniqueName(), dependency->name, params, engine));
- obj.setData(data);
- result.setProperty(idx++, obj);
+ JSValue data = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
+ JS_SetPropertyUint32(ctx, data, DependencyParametersKey, dependencyParametersValue(
+ product->uniqueName(), dependency->name, params, engine));
+ defineJsProperty(ctx, obj, StringConstants::dataPropertyInternal(), data);
+ JS_SetPropertyUint32(ctx, result, idx++, obj);
}
if (exportCase) {
for (const ExportedModuleDependency &m : product->exportedModule.moduleDependencies) {
- QScriptValue obj = engine->newObject();
- obj.setProperty(StringConstants::nameProperty(), m.name);
- QScriptValue exportsValue = engine->newObject();
- obj.setProperty(exportsProperty(), exportsValue);
- exportsValue.setProperty(StringConstants::dependenciesProperty(),
- engine->newArray());
+ JSValue obj = engine->newObject();
+ setJsProperty(ctx, obj, StringConstants::nameProperty(), m.name);
+ JSValue exportsValue = engine->newObject();
+ setJsProperty(ctx, obj, exportsProperty(), exportsValue);
+ setJsProperty(ctx, exportsValue, StringConstants::dependenciesProperty(),
+ JS_NewArray(ctx));
for (auto modIt = m.moduleProperties.begin(); modIt != m.moduleProperties.end();
++modIt) {
const QVariantMap entries = modIt.value().toMap();
if (entries.empty())
continue;
- QScriptValue moduleObj = engine->newObject();
- ModuleProperties::setModuleScriptValue(exportsValue, moduleObj, modIt.key());
- for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt)
- moduleObj.setProperty(valIt.key(), engine->toScriptValue(valIt.value()));
+ JSValue moduleObj = engine->newObject();
+ ModuleProperties::setModuleScriptValue(engine, exportsValue, moduleObj,
+ modIt.key());
+ for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt) {
+ setJsProperty(ctx, moduleObj, valIt.key(),
+ engine->toScriptValue(valIt.value()));
+ }
}
- result.setProperty(idx++, obj);
+ JS_SetPropertyUint32(ctx, result, idx++, obj);
}
return result;
}
- for (const ResolvedModuleConstPtr &dependency : product->modules) {
+ for (const auto &dependency : product->modules) {
if (dependency->isProduct)
continue;
- QScriptValue obj = engine->newObject(engine->modulePropertyScriptClass());
- obj.setPrototype(engine->moduleScriptValuePrototype(dependency.get()));
-
- // The prototype must exist already, because we set it up for all modules
- // of the product in ModuleProperties::init().
- QBS_ASSERT(obj.prototype().isValid(), ;);
-
+ JSValue obj = JS_NewObjectClass(ctx, engine->modulePropertyScriptClass());
+ attachPointerTo(obj, dependency.get());
const QVariantMap &params = product->moduleParameters.value(dependency);
- QScriptValue data = getDataForModuleScriptValue(engine, product, nullptr,
- dependency.get());
- data.setProperty(DependencyParametersKey, dependencyParametersValue(
- product->uniqueName(), dependency->name, params, engine));
- obj.setData(data);
- result.setProperty(idx++, obj);
+ JSValue data = createDataForModuleScriptValue(engine, nullptr);
+ JS_SetPropertyUint32(ctx, data, DependencyParametersKey, dependencyParametersValue(
+ product->uniqueName(), dependency->name, params, engine));
+ defineJsProperty(ctx, obj, StringConstants::dataPropertyInternal(), data);
+ JS_SetPropertyUint32(ctx, result, idx++, obj);
}
return result;
}
- static QScriptValue js_internalProductDependencies(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedProduct * const product)
+ static JSValue js_internalProductDependencies(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto product = attachedPointer<ResolvedProduct>(this_val, engine->dataWithPtrClass());
engine->addDependenciesArrayRequested(product);
- return js_productDependencies(ctx, engine, product, DependencyType::Internal);
+ return js_productDependencies(product, ctx, this_val, argc, argv,
+ DependencyType::Internal);
}
- static QScriptValue js_exportedProductDependencies(QScriptContext *ctx, ScriptEngine *engine,
- const ResolvedProduct * const product)
+ static JSValue js_exportedProductDependencies(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
{
- return js_productDependencies(ctx, engine, product, DependencyType::Exported);
+ const auto product = attachedPointer<ResolvedProduct>(
+ this_val, ScriptEngine::engineForContext(ctx)->dataWithPtrClass());
+ return js_productDependencies(product, ctx, this_val, argc, argv, DependencyType::Exported);
}
ScriptEngine *m_engine;
};
-static QScriptValue setupExportedPropertyScriptValue(const ExportedProperty &property,
+static JSValue setupExportedPropertyScriptValue(const ExportedProperty &property,
ScriptEngine *engine)
{
- QScriptValue propertyScriptValue = engine->newObject();
- propertyScriptValue.setProperty(StringConstants::nameProperty(), property.fullName);
- propertyScriptValue.setProperty(StringConstants::typeProperty(),
- PropertyDeclaration::typeString(property.type));
- propertyScriptValue.setProperty(StringConstants::sourceCodeProperty(), property.sourceCode);
- propertyScriptValue.setProperty(QStringLiteral("isBuiltin"), property.isBuiltin);
+ JSValue propertyScriptValue = engine->newObject();
+ setJsProperty(engine->context(), propertyScriptValue, StringConstants::nameProperty(),
+ property.fullName);
+ setJsProperty(engine->context(), propertyScriptValue, StringConstants::typeProperty(),
+ PropertyDeclaration::typeString(property.type));
+ setJsProperty(engine->context(), propertyScriptValue, StringConstants::sourceCodeProperty(),
+ property.sourceCode);
+ JS_SetPropertyStr(engine->context(), propertyScriptValue, "isBuiltin",
+ JS_NewBool(engine->context(), property.isBuiltin));
return propertyScriptValue;
}
-static void setupExportedPropertiesScriptValue(QScriptValue &parentObject,
+static void setupExportedPropertiesScriptValue(JSValue &parentObject,
const std::vector<ExportedProperty> &properties,
ScriptEngine *engine)
{
- QScriptValue propertiesScriptValue = engine->newArray(static_cast<uint>(properties.size()));
- parentObject.setProperty(QStringLiteral("properties"), propertiesScriptValue);
+ JSValue propertiesScriptValue = JS_NewArray(engine->context());
quint32 arrayIndex = 0;
for (const ExportedProperty &p : properties) {
- propertiesScriptValue.setProperty(arrayIndex++,
- setupExportedPropertyScriptValue(p, engine));
+ JS_SetPropertyUint32(engine->context(), propertiesScriptValue, arrayIndex++,
+ setupExportedPropertyScriptValue(p, engine));
}
+ JS_SetPropertyStr(engine->context(), parentObject, "properties", propertiesScriptValue);
}
-static QScriptValue setupExportedItemScriptValue(const ExportedItem *item, ScriptEngine *engine)
+static JSValue setupExportedItemScriptValue(const ExportedItem *item, ScriptEngine *engine)
{
- QScriptValue itemScriptValue = engine->newObject();
- itemScriptValue.setProperty(StringConstants::nameProperty(), item->name);
+ JSValue itemScriptValue = engine->newObject();
+ setJsProperty(engine->context(), itemScriptValue, StringConstants::nameProperty(), item->name);
setupExportedPropertiesScriptValue(itemScriptValue, item->properties, engine);
- QScriptValue childrenScriptValue = engine->newArray(static_cast<uint>(item->children.size()));
- itemScriptValue.setProperty(childItemsProperty(), childrenScriptValue);
+ JSValue childrenScriptValue = JS_NewArray(engine->context());
quint32 arrayIndex = 0;
for (const auto &childItem : item->children) {
- childrenScriptValue.setProperty(arrayIndex++,
- setupExportedItemScriptValue(childItem.get(), engine));
+ JS_SetPropertyUint32(engine->context(), childrenScriptValue, arrayIndex++,
+ setupExportedItemScriptValue(childItem.get(), engine));
}
+ setJsProperty(engine->context(), itemScriptValue, childItemsProperty(), childrenScriptValue);
return itemScriptValue;
}
-static QScriptValue setupExportsScriptValue(const ExportedModule &module, ScriptEngine *engine)
+static JSValue setupExportsScriptValue(const ResolvedProduct *product, ScriptEngine *engine)
{
- QScriptValue exportsScriptValue = engine->newObject();
- for (auto it = module.propertyValues.cbegin(); it != module.propertyValues.cend(); ++it)
- exportsScriptValue.setProperty(it.key(), engine->toScriptValue(it.value()));
+ const ExportedModule &module = product->exportedModule;
+ JSValue exportsScriptValue = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
+ attachPointerTo(exportsScriptValue, product);
+ for (auto it = module.propertyValues.cbegin(); it != module.propertyValues.cend(); ++it) {
+ setJsProperty(engine->context(), exportsScriptValue, it.key(),
+ engine->toScriptValue(it.value()));
+ }
setupExportedPropertiesScriptValue(exportsScriptValue, module.m_properties, engine);
- QScriptValue childrenScriptValue = engine->newArray(static_cast<uint>(module.children.size()));
- exportsScriptValue.setProperty(childItemsProperty(), childrenScriptValue);
+ JSValue childrenScriptValue = JS_NewArray(engine->context());
quint32 arrayIndex = 0;
for (const auto &exportedItem : module.children) {
- childrenScriptValue.setProperty(arrayIndex++,
- setupExportedItemScriptValue(exportedItem.get(), engine));
+ JS_SetPropertyUint32(engine->context(), childrenScriptValue, arrayIndex++,
+ setupExportedItemScriptValue(exportedItem.get(), engine));
}
- QScriptValue importsScriptValue = engine->newArray(module.importStatements.size());
- exportsScriptValue.setProperty(StringConstants::importsProperty(), importsScriptValue);
+ setJsProperty(engine->context(), exportsScriptValue, childItemsProperty(), childrenScriptValue);
+ JSValue importsScriptValue = JS_NewArray(engine->context());
arrayIndex = 0;
- for (const QString &importStatement : module.importStatements)
- importsScriptValue.setProperty(arrayIndex++, importStatement);
+ for (const QString &importStatement : module.importStatements) {
+ JS_SetPropertyUint32(engine->context(), importsScriptValue, arrayIndex++,
+ makeJsString(engine->context(), importStatement));
+ }
+ setJsProperty(engine->context(), exportsScriptValue, StringConstants::importsProperty(),
+ importsScriptValue);
for (auto it = module.modulePropertyValues.cbegin(); it != module.modulePropertyValues.cend();
++it) {
const QVariantMap entries = it.value().toMap();
if (entries.empty())
continue;
- QScriptValue moduleObject = engine->newObject();
- ModuleProperties::setModuleScriptValue(exportsScriptValue, moduleObject, it.key());
- for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt)
- moduleObject.setProperty(valIt.key(), engine->toScriptValue(valIt.value()));
+ JSValue moduleObject = engine->newObject();
+ ModuleProperties::setModuleScriptValue(engine, exportsScriptValue, moduleObject, it.key());
+ for (auto valIt = entries.begin(); valIt != entries.end(); ++valIt) {
+ setJsProperty(engine->context(), moduleObject, valIt.key(),
+ engine->toScriptValue(valIt.value()));
+ }
}
return exportsScriptValue;
}
-static QScriptValue setupProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product)
+static void setupBaseProductScriptValue(ScriptEngine *engine, const ResolvedProduct *product)
{
- QScriptValue &productScriptValue = engine->productScriptValuePrototype(product);
- if (productScriptValue.isValid())
- return productScriptValue;
- productScriptValue = engine->newObject();
- ModuleProperties::init(productScriptValue, product);
-
- QScriptValue artifactsFunc = engine->newFunction(&artifactsScriptValueForProduct, product);
- productScriptValue.setProperty(StringConstants::artifactsProperty(), artifactsFunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
-
- QScriptValue exportsScriptValue = setupExportsScriptValue(product->exportedModule, engine);
- DependenciesFunction(engine).init(productScriptValue, exportsScriptValue, product);
+ JSValue &productScriptValue = engine->baseProductScriptValue(product);
+ if (JS_IsObject(productScriptValue))
+ return;
+ const ScopedJsValue proto(engine->context(), JS_NewObject(engine->context()));
+ productScriptValue = JS_NewObjectProtoClass(engine->context(), proto,
+ engine->dataWithPtrClass());
+ attachPointerTo(productScriptValue, product);
+ ModuleProperties::init(engine, productScriptValue, product);
+
+ const QByteArray funcName = StringConstants::artifactsProperty().toUtf8();
+ const ScopedJsValue artifactsFunc(
+ engine->context(),
+ JS_NewCFunction(engine->context(), &artifactsScriptValueForProduct,
+ funcName.constData(), 0));
+ const ScopedJsAtom artifactsAtom(engine->context(), funcName);
+ JS_DefineProperty(engine->context(), productScriptValue, artifactsAtom,
+ JS_UNDEFINED, artifactsFunc, JS_UNDEFINED,
+ JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
+
+ // FIXME: Proper un-observe rather than ref count decrease here.
+ ScopedJsValue exportsScriptValue(engine->context(), setupExportsScriptValue(product, engine));
+ DependenciesFunction(engine).init(productScriptValue, exportsScriptValue);
+
+ // TODO: Why are these necessary? We catch accesses to product.exports in getProductProperty().
+ // But the exportsQbs() and exportsPkgConfig() tests fail without them.
engine->setObservedProperty(productScriptValue, exportsProperty(), exportsScriptValue);
- engine->observer()->addExportsObjectId(exportsScriptValue.objectId(), product);
- return productScriptValue;
+ engine->observer()->addExportsObjectId(jsObjectId(exportsScriptValue), product);
}
void setupScriptEngineForFile(ScriptEngine *engine, const FileContextBaseConstPtr &fileContext,
- QScriptValue targetObject, const ObserveMode &observeMode)
+ JSValue targetObject, const ObserveMode &observeMode)
{
engine->import(fileContext, targetObject, observeMode);
- JsExtensions::setupExtensions(fileContext->jsExtensions(), targetObject);
+ JsExtensions::setupExtensions(engine, fileContext->jsExtensions(), targetObject);
}
void setupScriptEngineForProduct(ScriptEngine *engine, ResolvedProduct *product,
- const ResolvedModule *module, QScriptValue targetObject,
+ const ResolvedModule *module, JSValue targetObject,
bool setBuildEnvironment)
{
- QScriptValue projectScriptValue = setupProjectScriptValue(engine, product->project.lock());
- targetObject.setProperty(StringConstants::projectVar(), projectScriptValue);
-
+ JSValue projectScriptValue = setupProjectScriptValue(engine, product->project.lock());
+ setJsProperty(engine->context(), targetObject, StringConstants::projectVar(),
+ projectScriptValue);
if (setBuildEnvironment) {
QVariant v;
v.setValue<void*>(&product->buildEnvironment);
engine->setProperty(StringConstants::qbsProcEnvVarInternal(), v);
}
- QScriptClass *scriptClass = engine->productPropertyScriptClass();
- if (!scriptClass) {
- scriptClass = new ProductPropertyScriptClass(engine);
- engine->setProductPropertyScriptClass(scriptClass);
+ JSClassID scriptClass = engine->productPropertyScriptClass();
+ if (scriptClass == 0) {
+ engine->setProductPropertyScriptClass(engine->registerClass("ProductProperties", nullptr,
+ nullptr, JS_UNDEFINED, &getProductPropertyNames, &getProductProperty));
+ scriptClass = engine->productPropertyScriptClass();
}
- QScriptValue productScriptValue = engine->newObject(scriptClass);
- productScriptValue.setPrototype(setupProductScriptValue(engine, product));
- targetObject.setProperty(StringConstants::productVar(), productScriptValue);
+ setupBaseProductScriptValue(engine, product);
+ JSValue productScriptValue = JS_NewObjectClass(engine->context(), scriptClass);
+ attachPointerTo(productScriptValue, product);
+ setJsProperty(engine->context(), targetObject, StringConstants::productVar(),
+ productScriptValue);
- QScriptValue data = getDataForProductScriptValue(engine, product);
+ JSValue data = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
// If the Rule is in a Module, set up the 'moduleName' property
- if (!module->name.isEmpty())
- data.setProperty(ModuleNameKey, module->name);
- productScriptValue.setData(data);
+ if (!module->name.isEmpty()) {
+ JS_SetPropertyUint32(engine->context(), data, ModuleNameKey,
+ makeJsString(engine->context(), module->name));
+ }
+ defineJsProperty(engine->context(), productScriptValue,
+ StringConstants::dataPropertyInternal(), data);
}
bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList<BuildGraphNode *> &path)
@@ -448,7 +520,7 @@ bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList<BuildGraphNode *> &pat
return true;
}
- for (BuildGraphNode * const childNode : qAsConst(u->children)) {
+ for (BuildGraphNode * const childNode : std::as_const(u->children)) {
if (findPath(childNode, v, path)) {
path.prepend(u);
return true;
@@ -493,12 +565,9 @@ static bool existsPath_impl(BuildGraphNode *u, BuildGraphNode *v, NodeSet *seen)
if (!seen->insert(u).second)
return false;
- for (BuildGraphNode * const childNode : qAsConst(u->children)) {
- if (existsPath_impl(childNode, v, seen))
- return true;
- }
-
- return false;
+ return Internal::any_of(u->children, [v, seen](const auto &child) {
+ return existsPath_impl(child, v, seen);
+ });
}
static bool existsPath(BuildGraphNode *u, BuildGraphNode *v)
@@ -641,7 +710,7 @@ void updateArtifactFromSourceArtifact(const ResolvedProductPtr &product,
const QVariantMap oldModuleProperties = artifact->properties->value();
setArtifactData(artifact, sourceArtifact);
if (oldFileTags != artifact->fileTags()
- || oldModuleProperties != artifact->properties->value()) {
+ || !qVariantMapsEqual(oldModuleProperties, artifact->properties->value())) {
invalidateArtifactAsRuleInputIfNecessary(artifact);
}
}
@@ -661,7 +730,7 @@ void provideFullFileTagsAndProperties(Artifact *artifact)
artifact->properties = artifact->product->moduleProperties;
FileTags allTags = artifact->pureFileTags.empty()
? artifact->product->fileTagsForFileName(artifact->fileName()) : artifact->pureFileTags;
- for (const ArtifactPropertiesConstPtr &props : artifact->product->artifactProperties) {
+ for (const auto &props : artifact->product->artifactProperties) {
if (allTags.intersects(props->fileTagsFilter())) {
artifact->properties = props->propertyMap();
allTags += props->extraFileTags();
@@ -697,7 +766,7 @@ void updateGeneratedArtifacts(ResolvedProduct *product)
provideFullFileTagsAndProperties(artifact);
applyPerArtifactProperties(artifact);
if (oldFileTags != artifact->fileTags()
- || oldModuleProperties != artifact->properties->value()) {
+ || !qVariantMapsEqual(oldModuleProperties, artifact->properties->value())) {
invalidateArtifactAsRuleInputIfNecessary(artifact);
}
}
@@ -733,24 +802,24 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product,
CycleDetector cycleDetector(logger);
cycleDetector.visitProduct(product);
const ProductBuildData * const buildData = product->buildData.get();
- for (const ResolvedModuleConstPtr &m : product->modules)
+ for (const auto &m : product->modules)
QBS_CHECK(m->product == product.get());
qCDebug(lcBuildGraph) << "enabled:" << product->enabled << "build data:" << buildData;
if (product->enabled)
QBS_CHECK(buildData);
if (!product->buildData)
return;
- for (BuildGraphNode * const node : qAsConst(buildData->rootNodes())) {
+ for (BuildGraphNode * const node : std::as_const(buildData->rootNodes())) {
qCDebug(lcBuildGraph).noquote() << "Checking root node" << node->toString();
QBS_CHECK(buildData->allNodes().contains(node));
}
Set<QString> filePaths;
- for (BuildGraphNode * const node : qAsConst(buildData->allNodes())) {
+ for (BuildGraphNode * const node : std::as_const(buildData->allNodes())) {
qCDebug(lcBuildGraph).noquote() << "Sanity checking node" << node->toString();
QBS_CHECK(node->product == product);
- for (const BuildGraphNode * const parent : qAsConst(node->parents))
+ for (const BuildGraphNode * const parent : std::as_const(node->parents))
QBS_CHECK(parent->children.contains(node));
- for (BuildGraphNode * const child : qAsConst(node->children)) {
+ for (BuildGraphNode * const child : std::as_const(node->children)) {
QBS_CHECK(child->parents.contains(node));
QBS_CHECK(!child->product.expired());
QBS_CHECK(child->product->buildData);
@@ -777,7 +846,7 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product,
!filePaths.contains(artifact->filePath()));
filePaths << artifact->filePath();
- for (Artifact * const child : qAsConst(artifact->childrenAddedByScanner))
+ for (Artifact * const child : std::as_const(artifact->childrenAddedByScanner))
QBS_CHECK(artifact->children.contains(child));
const TransformerConstPtr transformer = artifact->transformer;
if (artifact->artifactType == Artifact::SourceFile)
@@ -796,7 +865,7 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product,
qCDebug(lcBuildGraph)
<< "The transformer has" << transformer->outputs.size() << "outputs.";
ArtifactSet transformerOutputChildren;
- for (const Artifact * const output : qAsConst(transformer->outputs)) {
+ for (const Artifact * const output : std::as_const(transformer->outputs)) {
QBS_CHECK(output->transformer == transformer);
transformerOutputChildren.unite(ArtifactSet::filtered(output->children));
for (const Artifact *a : filterByType<Artifact>(output->children)) {
@@ -814,14 +883,14 @@ static void doSanityChecksForProduct(const ResolvedProductConstPtr &product,
}
if (lcBuildGraph().isDebugEnabled()) {
qCDebug(lcBuildGraph) << "The transformer output children are:";
- for (const Artifact * const a : qAsConst(transformerOutputChildren))
+ for (const Artifact * const a : std::as_const(transformerOutputChildren))
qCDebug(lcBuildGraph) << "\t" << a->fileName();
qCDebug(lcBuildGraph) << "The transformer inputs are:";
- for (const Artifact * const a : qAsConst(transformer->inputs))
+ for (const Artifact * const a : std::as_const(transformer->inputs))
qCDebug(lcBuildGraph) << "\t" << a->fileName();
}
QBS_CHECK(transformer->inputs.size() <= transformerOutputChildren.size());
- for (Artifact * const transformerInput : qAsConst(transformer->inputs))
+ for (Artifact * const transformerInput : std::as_const(transformer->inputs))
QBS_CHECK(transformerOutputChildren.contains(transformerInput));
transformer->artifactsMapRequestedInPrepareScript.doSanityChecks();
transformer->artifactsMapRequestedInCommands.doSanityChecks();
@@ -833,10 +902,10 @@ static void doSanityChecks(const ResolvedProjectPtr &project,
const Logger &logger)
{
logger.qbsDebug() << "Sanity checking project '" << project->name << "'";
- for (const ResolvedProjectPtr &subProject : qAsConst(project->subProjects))
+ for (const ResolvedProjectPtr &subProject : std::as_const(project->subProjects))
doSanityChecks(subProject, allProducts, productNames, logger);
- for (const ResolvedProductConstPtr &product : project->products) {
+ for (const auto &product : project->products) {
QBS_CHECK(product->project == project);
QBS_CHECK(product->topLevelProject() == project->topLevelProject());
doSanityChecksForProduct(product, allProducts, logger);
@@ -850,8 +919,7 @@ void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger)
if (qEnvironmentVariableIsEmpty("QBS_SANITY_CHECKS"))
return;
Set<QString> productNames;
- const Set<ResolvedProductPtr> allProducts
- = Set<ResolvedProductPtr>::fromStdVector(project->allProducts());
+ const auto allProducts = rangeTo<Set<ResolvedProductPtr>>(project->allProducts());
doSanityChecks(project, allProducts, productNames, logger);
}
diff --git a/src/lib/corelib/buildgraph/buildgraph.h b/src/lib/corelib/buildgraph/buildgraph.h
index 2909b06bb..1eadb2e20 100644
--- a/src/lib/corelib/buildgraph/buildgraph.h
+++ b/src/lib/corelib/buildgraph/buildgraph.h
@@ -43,9 +43,10 @@
#include <language/forward_decls.h>
#include <tools/qbs_export.h>
-#include <QtCore/qstringlist.h>
+#include <quickjs.h>
-#include <QtScript/qscriptvalue.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/QVariantMap>
namespace qbs {
namespace Internal {
@@ -88,14 +89,18 @@ void removeGeneratedArtifactFromDisk(const QString &filePath, const Logger &logg
void disconnect(BuildGraphNode *u, BuildGraphNode *v);
void setupScriptEngineForFile(ScriptEngine *engine, const FileContextBaseConstPtr &fileContext,
- QScriptValue targetObject, const ObserveMode &observeMode);
+ JSValue targetObject, const ObserveMode &observeMode);
void setupScriptEngineForProduct(ScriptEngine *engine, ResolvedProduct *product,
- const ResolvedModule *module, QScriptValue targetObject,
+ const ResolvedModule *module, JSValue targetObject,
bool setBuildEnvironment);
QString relativeArtifactFileName(const Artifact *artifact); // Debugging helpers
void doSanityChecks(const ResolvedProjectPtr &project, const Logger &logger);
+void getPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ const QVariantMap &properties, const QStringList &extraPropertyNames,
+ JSValueConst extraObject);
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/buildgraph.pri b/src/lib/corelib/buildgraph/buildgraph.pri
deleted file mode 100644
index 2ed6be4fd..000000000
--- a/src/lib/corelib/buildgraph/buildgraph.pri
+++ /dev/null
@@ -1,89 +0,0 @@
-include(../../../install_prefix.pri)
-
-SOURCES += \
- $$PWD/abstractcommandexecutor.cpp \
- $$PWD/artifact.cpp \
- $$PWD/artifactcleaner.cpp \
- $$PWD/artifactsscriptvalue.cpp \
- $$PWD/artifactvisitor.cpp \
- $$PWD/buildgraph.cpp \
- $$PWD/buildgraphloader.cpp \
- $$PWD/buildgraphnode.cpp \
- $$PWD/cycledetector.cpp \
- $$PWD/dependencyparametersscriptvalue.cpp \
- $$PWD/depscanner.cpp \
- $$PWD/emptydirectoriesremover.cpp \
- $$PWD/environmentscriptrunner.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/rawscanneddependency.cpp \
- $$PWD/rawscanresults.cpp \
- $$PWD/requestedartifacts.cpp \
- $$PWD/requesteddependencies.cpp \
- $$PWD/rulecommands.cpp \
- $$PWD/rulegraph.cpp \
- $$PWD/rulenode.cpp \
- $$PWD/rulesapplicator.cpp \
- $$PWD/rulesevaluationcontext.cpp \
- $$PWD/timestampsupdater.cpp \
- $$PWD/transformerchangetracking.cpp \
- $$PWD/transformer.cpp
-
-HEADERS += \
- $$PWD/abstractcommandexecutor.h \
- $$PWD/artifact.h \
- $$PWD/artifactcleaner.h \
- $$PWD/artifactsscriptvalue.h \
- $$PWD/artifactvisitor.h \
- $$PWD/buildgraph.h \
- $$PWD/buildgraphloader.h \
- $$PWD/buildgraphnode.h \
- $$PWD/buildgraphvisitor.h \
- $$PWD/cycledetector.h \
- $$PWD/dependencyparametersscriptvalue.h \
- $$PWD/depscanner.h \
- $$PWD/emptydirectoriesremover.h \
- $$PWD/environmentscriptrunner.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/rawscanneddependency.h \
- $$PWD/rawscanresults.h \
- $$PWD/requestedartifacts.h \
- $$PWD/requesteddependencies.h \
- $$PWD/rescuableartifactdata.h \
- $$PWD/rulecommands.h \
- $$PWD/rulegraph.h \
- $$PWD/rulenode.h \
- $$PWD/rulesapplicator.h \
- $$PWD/rulesevaluationcontext.h \
- $$PWD/scriptclasspropertyiterator.h \
- $$PWD/timestampsupdater.h \
- $$PWD/transformerchangetracking.h \
- $$PWD/transformer.h
-
-!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
index cad236db7..ffae52ab2 100644
--- a/src/lib/corelib/buildgraph/buildgraphloader.cpp
+++ b/src/lib/corelib/buildgraph/buildgraphloader.cpp
@@ -39,31 +39,32 @@
#include "buildgraphloader.h"
#include "buildgraph.h"
-#include "cycledetector.h"
#include "emptydirectoriesremover.h"
#include "productbuilddata.h"
#include "projectbuilddata.h"
#include "rulenode.h"
#include "rulecommands.h"
-#include "rulesevaluationcontext.h"
#include "transformer.h"
+#include <buildgraph/rulesevaluationcontext.h>
#include <language/artifactproperties.h>
#include <language/language.h>
-#include <language/loader.h>
#include <language/propertymapinternal.h>
#include <language/qualifiedid.h>
#include <language/resolvedfilecontext.h>
+#include <loader/projectresolver.h>
#include <logging/categories.h>
#include <logging/translator.h>
#include <tools/buildgraphlocker.h>
#include <tools/fileinfo.h>
+#include <tools/jsliterals.h>
#include <tools/persistence.h>
#include <tools/profile.h>
#include <tools/profiling.h>
#include <tools/qbsassert.h>
#include <tools/qttools.h>
#include <tools/settings.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
#include <QtCore/qdir.h>
@@ -93,7 +94,7 @@ static void restoreBackPointers(const ResolvedProjectPtr &project)
product->project = project;
if (!product->buildData)
continue;
- for (BuildGraphNode * const n : qAsConst(product->buildData->allNodes())) {
+ for (BuildGraphNode * const n : std::as_const(product->buildData->allNodes())) {
if (n->type() == BuildGraphNode::ArtifactNodeType) {
project->topLevelProject()->buildData
->insertIntoLookupTable(static_cast<Artifact *>(n));
@@ -101,7 +102,7 @@ static void restoreBackPointers(const ResolvedProjectPtr &project)
}
}
- for (const ResolvedProjectPtr &subProject : qAsConst(project->subProjects)) {
+ for (const ResolvedProjectPtr &subProject : std::as_const(project->subProjects)) {
subProject->parentProject = project;
restoreBackPointers(subProject);
}
@@ -127,7 +128,7 @@ BuildGraphLoadResult BuildGraphLoader::load(const TopLevelProjectPtr &existingPr
if (!m_result.loadedProject)
return m_result;
if (parameters.restoreBehavior() == SetupProjectParameters::RestoreOnly) {
- for (const ErrorInfo &e : qAsConst(m_result.loadedProject->warningsEncountered))
+ for (const ErrorInfo &e : std::as_const(m_result.loadedProject->warningsEncountered))
m_logger.printWarning(e);
return m_result;
}
@@ -218,7 +219,7 @@ bool BuildGraphLoader::checkBuildGraphCompatibility(const TopLevelProjectConstPt
if (m_parameters.projectFilePath().isEmpty())
m_parameters.setProjectFilePath(project->location.filePath());
else
- Loader::setupProjectFilePath(m_parameters);
+ m_parameters.finalizeProjectFilePath();
if (QFileInfo(project->location.filePath()) == QFileInfo(m_parameters.projectFilePath()))
return true;
QString message = Tr::tr("Stored build graph at '%1' is for project file '%2', but "
@@ -242,7 +243,7 @@ static bool checkProductForChangedDependency(std::vector<ResolvedProductPtr> &ch
return false;
if (contains(changedProducts, product))
return true;
- for (const ResolvedProductPtr &dep : qAsConst(product->dependencies)) {
+ for (const ResolvedProductPtr &dep : std::as_const(product->dependencies)) {
if (checkProductForChangedDependency(changedProducts, seenProducts, dep)) {
changedProducts << product;
return true;
@@ -267,14 +268,14 @@ static void makeChangedProductsListComplete(std::vector<ResolvedProductPtr> &cha
static void updateProductAndRulePointers(const ResolvedProductPtr &newProduct)
{
std::unordered_map<RuleConstPtr, RuleConstPtr> ruleMap;
- for (BuildGraphNode *node : qAsConst(newProduct->buildData->allNodes())) {
+ for (BuildGraphNode *node : std::as_const(newProduct->buildData->allNodes())) {
node->product = newProduct;
const auto findNewRule = [&ruleMap, &newProduct]
(const RuleConstPtr &oldRule) -> RuleConstPtr {
const auto it = ruleMap.find(oldRule);
if (it != ruleMap.cend())
return it->second;
- for (const RuleConstPtr &r : qAsConst(newProduct->rules)) {
+ for (const auto &r : std::as_const(newProduct->rules)) {
if (*r == *oldRule) {
ruleMap.insert(std::make_pair(oldRule, r));
return r;
@@ -306,8 +307,10 @@ void BuildGraphLoader::trackProjectChanges()
std::vector<ResolvedProductPtr> allRestoredProducts = restoredProject->allProducts();
std::vector<ResolvedProductPtr> changedProducts;
bool reResolvingNecessary = false;
- if (!checkConfigCompatibility())
+ if (!checkConfigCompatibility()) {
+ m_logger.qbsInfo() << Tr::tr("One or more properties have changed.");
reResolvingNecessary = true;
+ }
if (hasProductFileChanged(allRestoredProducts, restoredProject->lastStartResolveTime,
buildSystemFiles, changedProducts)) {
reResolvingNecessary = true;
@@ -328,33 +331,41 @@ void BuildGraphLoader::trackProjectChanges()
}
if (!reResolvingNecessary) {
- for (const ErrorInfo &e : qAsConst(restoredProject->warningsEncountered))
+ for (const ErrorInfo &e : std::as_const(restoredProject->warningsEncountered))
m_logger.printWarning(e);
return;
}
+ for (const QString &file : m_changedProjectFiles) {
+ m_logger.qbsInfo() << Tr::tr("Project file '%1' has changed.")
+ .arg(QDir::toNativeSeparators(file));
+ }
+ for (const QString &file : m_removedProjectFiles) {
+ m_logger.qbsInfo() << Tr::tr("Project file '%1' was removed.")
+ .arg(QDir::toNativeSeparators(file));
+ }
+
restoredProject->buildData->setDirty();
markTransformersForChangeTracking(allRestoredProducts);
if (!m_parameters.overrideBuildGraphData())
m_parameters.setEnvironment(restoredProject->environment);
- Loader ldr(m_evalContext->engine(), m_logger);
- ldr.setSearchPaths(m_parameters.searchPaths());
- ldr.setProgressObserver(m_evalContext->observer());
- ldr.setOldProjectProbes(restoredProject->probes);
+ ProjectResolver resolver(m_parameters, m_evalContext->engine(), m_logger);
+ resolver.setProgressObserver(m_evalContext->observer());
+ resolver.setOldProjectProbes(restoredProject->probes);
if (!m_parameters.forceProbeExecution())
- ldr.setStoredModuleProviderInfo(restoredProject->moduleProviderInfo);
- ldr.setLastResolveTime(restoredProject->lastStartResolveTime);
+ resolver.setStoredModuleProviderInfo(restoredProject->moduleProviderInfo);
+ resolver.setLastResolveTime(restoredProject->lastStartResolveTime);
QHash<QString, std::vector<ProbeConstPtr>> restoredProbes;
- for (const auto &restoredProduct : qAsConst(allRestoredProducts))
+ for (const auto &restoredProduct : std::as_const(allRestoredProducts))
restoredProbes.insert(restoredProduct->uniqueName(), restoredProduct->probes);
- ldr.setOldProductProbes(restoredProbes);
+ resolver.setOldProductProbes(restoredProbes);
if (!m_parameters.overrideBuildGraphData())
- ldr.setStoredProfiles(restoredProject->profileConfigs);
- m_result.newlyResolvedProject = ldr.loadProject(m_parameters);
+ resolver.setStoredProfiles(restoredProject->profileConfigs);
+ m_result.newlyResolvedProject = resolver.resolve();
std::vector<ResolvedProductPtr> allNewlyResolvedProducts
= m_result.newlyResolvedProject->allProducts();
- for (const ResolvedProductPtr &cp : qAsConst(allNewlyResolvedProducts))
+ for (const ResolvedProductPtr &cp : std::as_const(allNewlyResolvedProducts))
m_freshProductsByName.insert(cp->uniqueName(), cp);
checkAllProductsForChanges(allRestoredProducts, changedProducts);
@@ -363,7 +374,7 @@ void BuildGraphLoader::trackProjectChanges()
ChildListHash childLists;
if (!changedProducts.empty()) {
oldBuildData = std::make_shared<ProjectBuildData>(restoredProject->buildData.get());
- for (const ResolvedProductConstPtr &product : qAsConst(allRestoredProducts)) {
+ for (const auto &product : std::as_const(allRestoredProducts)) {
if (!product->buildData)
continue;
@@ -382,7 +393,7 @@ void BuildGraphLoader::trackProjectChanges()
// 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<QString, AllRescuableArtifactData> rescuableArtifactData;
- for (const ResolvedProductPtr &product : qAsConst(changedProducts)) {
+ for (const ResolvedProductPtr &product : std::as_const(changedProducts)) {
const QString name = product->uniqueName();
m_changedSourcesByProduct.erase(name);
m_productsWhoseArtifactsNeedUpdate.remove(name);
@@ -426,7 +437,7 @@ void BuildGraphLoader::trackProjectChanges()
}
// Products still left in the list do not exist anymore.
- for (const ResolvedProductPtr &removedProduct : qAsConst(allRestoredProducts)) {
+ for (const ResolvedProductPtr &removedProduct : std::as_const(allRestoredProducts)) {
removeOne(changedProducts, removedProduct);
onProductRemoved(removedProduct, m_result.newlyResolvedProject->buildData.get());
}
@@ -452,7 +463,7 @@ void BuildGraphLoader::trackProjectChanges()
updateGeneratedArtifacts(product.get());
}
- for (const ResolvedProductConstPtr &changedProduct : qAsConst(changedProducts)) {
+ for (const auto &changedProduct : std::as_const(changedProducts)) {
rescueOldBuildData(changedProduct,
m_freshProductsByName.value(changedProduct->uniqueName()),
childLists, rescuableArtifactData.value(changedProduct->uniqueName()));
@@ -461,7 +472,7 @@ void BuildGraphLoader::trackProjectChanges()
EmptyDirectoriesRemover(m_result.newlyResolvedProject.get(), m_logger)
.removeEmptyParentDirectories(m_artifactsRemovedFromDisk);
- for (FileResourceBase * const f : qAsConst(m_objectsToDelete)) {
+ for (FileResourceBase * const f : std::as_const(m_objectsToDelete)) {
if (f->fileType() == FileResourceBase::FileTypeArtifact)
static_cast<Artifact *>(f)->product.reset(); // To help with the sanity checks.
}
@@ -478,12 +489,9 @@ bool BuildGraphLoader::probeExecutionForced(
if (!restoredProject->probes.empty())
return true;
- for (const auto &p : qAsConst(restoredProducts)) {
- if (!p->probes.empty())
- return true;
- }
-
- return false;
+ return Internal::any_of(restoredProducts, [](const auto &p) {
+ return !p->probes.empty();
+ });
}
bool BuildGraphLoader::hasEnvironmentChanged(const TopLevelProjectConstPtr &restoredProject) const
@@ -500,9 +508,11 @@ bool BuildGraphLoader::hasEnvironmentChanged(const TopLevelProjectConstPtr &rest
newEnv.remove(ldPreloadEnvVar);
if (oldEnv != newEnv) {
- qCDebug(lcBuildGraph) << "Set of environment variables changed. Must re-resolve project."
- << "\nold:" << restoredProject->environment.toStringList()
- << "\nnew:" << m_parameters.adjustedEnvironment().toStringList();
+ m_logger.qbsInfo() << Tr::tr("Environment changed.");
+ m_logger.qbsDebug() << Tr::tr("old: %1").arg(
+ restoredProject->environment.toStringList().join(QLatin1Char('\n')));
+ m_logger.qbsDebug() << Tr::tr("new: %2").arg(
+ m_parameters.adjustedEnvironment().toStringList().join(QLatin1Char('\n')));
return true;
}
return false;
@@ -513,8 +523,8 @@ bool BuildGraphLoader::hasCanonicalFilePathResultChanged(const TopLevelProjectCo
for (auto it = restoredProject->canonicalFilePathResults.constBegin();
it != restoredProject->canonicalFilePathResults.constEnd(); ++it) {
if (QFileInfo(it.key()).canonicalFilePath() != it.value()) {
- qCDebug(lcBuildGraph) << "Canonical file path for file" << it.key()
- << "changed, must re-resolve project.";
+ m_logger.qbsInfo() << Tr::tr("Canonical file path for file '%1' changed.")
+ .arg(QDir::toNativeSeparators(it.key()));
return true;
}
}
@@ -527,8 +537,8 @@ bool BuildGraphLoader::hasFileExistsResultChanged(const TopLevelProjectConstPtr
for (QHash<QString, bool>::ConstIterator it = restoredProject->fileExistsResults.constBegin();
it != restoredProject->fileExistsResults.constEnd(); ++it) {
if (FileInfo(it.key()).exists() != it.value()) {
- qCDebug(lcBuildGraph) << "Existence check for file" << it.key()
- << "changed, must re-resolve project.";
+ m_logger.qbsInfo() << Tr::tr("Existence check for file '%1' changed.")
+ .arg(QDir::toNativeSeparators(it.key()));
return true;
}
}
@@ -542,9 +552,8 @@ bool BuildGraphLoader::hasDirectoryEntriesResultChanged(const TopLevelProjectCon
it != restoredProject->directoryEntriesResults.constEnd(); ++it) {
if (QDir(it.key().first).entryList(static_cast<QDir::Filters>(it.key().second), QDir::Name)
!= it.value()) {
- qCDebug(lcBuildGraph) << "Entry list for directory" << it.key().first
- << static_cast<QDir::Filters>(it.key().second)
- << "changed, must re-resolve project.";
+ m_logger.qbsInfo() << Tr::tr("Entry list for directory '%1' changed.")
+ .arg(QDir::toNativeSeparators(it.key().first));
return true;
}
}
@@ -558,8 +567,8 @@ bool BuildGraphLoader::hasFileLastModifiedResultChanged(const TopLevelProjectCon
= restoredProject->fileLastModifiedResults.constBegin();
it != restoredProject->fileLastModifiedResults.constEnd(); ++it) {
if (FileInfo(it.key()).lastModified() != it.value()) {
- qCDebug(lcBuildGraph) << "Timestamp for file" << it.key()
- << "changed, must re-resolve project.";
+ m_logger.qbsInfo() << Tr::tr("Timestamp for file '%1' has changed.")
+ .arg(QDir::toNativeSeparators(it.key()));
return true;
}
}
@@ -577,17 +586,18 @@ bool BuildGraphLoader::hasProductFileChanged(const std::vector<ResolvedProductPt
const FileInfo pfi(filePath);
remainingBuildSystemFiles.remove(filePath);
if (!pfi.exists()) {
- qCDebug(lcBuildGraph) << "A product was removed, must re-resolve project";
+ m_removedProjectFiles << filePath;
hasChanged = true;
} else if (referenceTime < pfi.lastModified()) {
- qCDebug(lcBuildGraph) << "A product was changed, must re-resolve project";
+ m_changedProjectFiles << filePath;
hasChanged = true;
} else if (!contains(changedProducts, product)) {
bool foundMissingSourceFile = false;
- for (const QString &file : qAsConst(product->missingSourceFiles)) {
+ for (const QString &file : std::as_const(product->missingSourceFiles)) {
if (FileInfo(file).exists()) {
- qCDebug(lcBuildGraph) << "Formerly missing file" << file << "in product"
- << product->name << "exists now, must re-resolve project";
+ m_logger.qbsInfo()
+ << Tr::tr("Formerly missing file '%1' in product '%2' exists now.")
+ .arg(QDir::toNativeSeparators(filePath), product->fullDisplayName());
foundMissingSourceFile = true;
break;
}
@@ -601,27 +611,14 @@ bool BuildGraphLoader::hasProductFileChanged(const std::vector<ResolvedProductPt
AccumulatingTimer wildcardTimer(m_parameters.logElapsedTime()
? &m_wildcardExpansionEffort : nullptr);
for (const GroupPtr &group : product->groups) {
- if (!group->wildcards)
- continue;
- const bool reExpansionRequired = std::any_of(
- group->wildcards->dirTimeStamps.cbegin(),
- group->wildcards->dirTimeStamps.cend(),
- [](const std::pair<QString, FileTime> &pair) {
- return FileInfo(pair.first).lastModified() > pair.second;
- });
- if (!reExpansionRequired)
- continue;
- const Set<QString> files = group->wildcards->expandPatterns(group,
- FileInfo::path(group->location.filePath()),
- product->topLevelProject()->buildDirectory);
- Set<QString> wcFiles;
- for (const SourceArtifactConstPtr &sourceArtifact : group->wildcards->files)
- wcFiles += sourceArtifact->absoluteFilePath;
- if (files == wcFiles)
- continue;
- hasChanged = true;
- changedProducts.push_back(product);
- break;
+ if (group->wildcards && group->wildcards->hasChangedSinceExpansion()) {
+ m_logger.qbsInfo()
+ << Tr::tr("Must re-expand wildcards for group '%1' in product '%2'.")
+ .arg(group->name, product->fullDisplayName());
+ hasChanged = true;
+ changedProducts.push_back(product);
+ break;
+ }
}
}
}
@@ -635,19 +632,18 @@ bool BuildGraphLoader::hasBuildSystemFileChanged(const Set<QString> &buildSystem
for (const QString &file : buildSystemFiles) {
const FileInfo fi(file);
if (!fi.exists()) {
- qCDebug(lcBuildGraph) << "Project file" << file
- << "no longer exists, must re-resolve project.";
+ m_removedProjectFiles << file;
return true;
}
const auto generatedChecker = [&file, restoredProject](const ModuleProviderInfo &mpi) {
return file.startsWith(mpi.outputDirPath(restoredProject->buildDirectory));
};
- const bool fileWasCreatedByModuleProvider = any_of(restoredProject->moduleProviderInfo,
- generatedChecker);
+ const bool fileWasCreatedByModuleProvider =
+ any_of(restoredProject->moduleProviderInfo.providers, generatedChecker);
const FileTime referenceTime = fileWasCreatedByModuleProvider
? restoredProject->lastEndResolveTime : restoredProject->lastStartResolveTime;
if (referenceTime < fi.lastModified()) {
- qCDebug(lcBuildGraph) << "Project file" << file << "changed, must re-resolve project.";
+ m_changedProjectFiles << file;
return true;
}
}
@@ -742,9 +738,9 @@ static bool dependenciesAreEqual(const ResolvedProductConstPtr &p1,
return false;
Set<QString> names1;
Set<QString> names2;
- for (const ResolvedProductConstPtr &dep : qAsConst(p1->dependencies))
+ for (const auto &dep : std::as_const(p1->dependencies))
names1 << dep->uniqueName();
- for (const ResolvedProductConstPtr &dep : qAsConst(p2->dependencies))
+ for (const auto &dep : std::as_const(p2->dependencies))
names2 << dep->uniqueName();
return names1 == names2;
}
@@ -774,10 +770,11 @@ bool BuildGraphLoader::checkProductForInstallInfoChanges(const ResolvedProductPt
<< StringConstants::installDirProperty() << StringConstants::installPrefixProperty()
<< StringConstants::installRootProperty();
for (const QString &key : specialProperties) {
- if (restoredProduct->moduleProperties->qbsPropertyValue(key)
- != newlyResolvedProduct->moduleProperties->qbsPropertyValue(key)) {
+ if (!qVariantsEqual(
+ restoredProduct->moduleProperties->qbsPropertyValue(key),
+ newlyResolvedProduct->moduleProperties->qbsPropertyValue(key))) {
qCDebug(lcBuildGraph).noquote().nospace()
- << "Product property 'qbs." << key << "' changed.";
+ << "Product property 'qbs." << key << "' changed.";
return true;
}
}
@@ -823,7 +820,7 @@ void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product,
removeOne(product->project->products, product);
if (product->buildData) {
- for (BuildGraphNode * const node : qAsConst(product->buildData->allNodes())) {
+ for (BuildGraphNode * const node : std::as_const(product->buildData->allNodes())) {
if (node->type() == BuildGraphNode::ArtifactNodeType) {
const auto artifact = static_cast<Artifact *>(node);
projectBuildData->removeArtifact(artifact, m_logger, removeArtifactsFromDisk,
@@ -831,10 +828,10 @@ void BuildGraphLoader::onProductRemoved(const ResolvedProductPtr &product,
if (removeArtifactsFromDisk && artifact->artifactType == Artifact::Generated)
m_artifactsRemovedFromDisk << artifact->filePath();
} else {
- for (BuildGraphNode * const parent : qAsConst(node->parents))
+ for (BuildGraphNode * const parent : std::as_const(node->parents))
parent->children.remove(node);
node->parents.clear();
- for (BuildGraphNode * const child : qAsConst(node->children))
+ for (BuildGraphNode * const child : std::as_const(node->children))
child->parents.remove(node);
node->children.clear();
}
@@ -863,15 +860,32 @@ void BuildGraphLoader::replaceFileDependencyWithArtifact(const ResolvedProductPt
bool BuildGraphLoader::checkConfigCompatibility()
{
const TopLevelProjectConstPtr restoredProject = m_result.loadedProject;
- if (m_parameters.topLevelProfile().isEmpty())
+ if (m_parameters.topLevelProfile().isEmpty()) {
m_parameters.setTopLevelProfile(restoredProject->profile());
+ m_parameters.expandBuildConfiguration();
+ }
if (!m_parameters.overrideBuildGraphData()) {
if (!m_parameters.overriddenValues().empty()
- && m_parameters.overriddenValues() != restoredProject->overriddenValues) {
+ && !qVariantMapsEqual(
+ m_parameters.overriddenValues(), restoredProject->overriddenValues)) {
+ const auto toUserOutput = [](const QVariantMap &propMap) {
+ QString o;
+ for (auto it = propMap.begin(); it != propMap.end(); ++it) {
+ if (!o.isEmpty())
+ o += QLatin1Char(' ');
+ o.append(it.key()).append(QLatin1Char(':')).append(toJSLiteral(it.value()));
+ }
+ return o;
+ };
throw ErrorInfo(Tr::tr("Property values set on the command line differ from the "
- "ones used for the previous build. Use the 'resolve' command if "
- "you really want to rebuild with the new properties."));
- }
+ "ones used for the previous build.\n"
+ "Old property values: %1\n"
+ "New property values: %2\n"
+ "Use the 'resolve' command if "
+ "you really want to rebuild with the new properties.")
+ .arg(toUserOutput(restoredProject->overriddenValues),
+ toUserOutput(m_parameters.overriddenValues())));
+ }
m_parameters.setOverriddenValues(restoredProject->overriddenValues);
if (m_parameters.topLevelProfile() != restoredProject->profile()) {
throw ErrorInfo(Tr::tr("The current profile is '%1', but profile '%2' was used "
@@ -886,11 +900,12 @@ bool BuildGraphLoader::checkConfigCompatibility()
}
if (!m_parameters.overrideBuildGraphData())
return true;
- if (m_parameters.finalBuildConfigurationTree() != restoredProject->buildConfiguration())
+ if (!qVariantMapsEqual(
+ m_parameters.finalBuildConfigurationTree(), restoredProject->buildConfiguration()))
return false;
Settings settings(m_parameters.settingsDirectory());
- for (QVariantMap::ConstIterator it = restoredProject->profileConfigs.constBegin();
- it != restoredProject->profileConfigs.constEnd(); ++it) {
+ const QVariantMap profileConfigsTree = restoredProject->fullProfileConfigsTree();
+ for (auto it = profileConfigsTree.begin(); it != profileConfigsTree.end(); ++it) {
const Profile profile(it.key(), &settings);
const QVariantMap buildConfig = SetupProjectParameters::expandedBuildConfiguration(
profile, m_parameters.configurationName());
@@ -956,14 +971,12 @@ void BuildGraphLoader::rescueOldBuildData(const ResolvedProductConstPtr &restore
rad.lastPrepareScriptExecutionTime
= oldArtifact->transformer->lastPrepareScriptExecutionTime;
const ChildrenInfo &childrenInfo = childLists.value(oldArtifact);
- for (Artifact * const child : qAsConst(childrenInfo.children)) {
+ for (Artifact * const child : std::as_const(childrenInfo.children)) {
rad.children.emplace_back(child->product->name,
child->product->multiplexConfigurationId, child->filePath(),
childrenInfo.childrenAddedByScanner.contains(child));
- std::transform(oldArtifact->fileDependencies.cbegin(),
- oldArtifact->fileDependencies.cend(),
- std::back_inserter(rad.fileDependencies),
- std::mem_fn(&FileDependency::filePath));
+ transform(oldArtifact->fileDependencies, rad.fileDependencies,
+ std::mem_fn(&FileDependency::filePath));
}
newlyResolvedProduct->buildData->addRescuableArtifactData(oldArtifact->filePath(), rad);
}
diff --git a/src/lib/corelib/buildgraph/buildgraphloader.h b/src/lib/corelib/buildgraph/buildgraphloader.h
index a89272958..02b00e29d 100644
--- a/src/lib/corelib/buildgraph/buildgraphloader.h
+++ b/src/lib/corelib/buildgraph/buildgraphloader.h
@@ -46,6 +46,7 @@
#include <language/forward_decls.h>
#include <logging/logger.h>
+#include <tools/set.h>
#include <tools/setupprojectparameters.h>
#include <QtCore/qprocess.h>
@@ -122,8 +123,8 @@ private:
struct ChildrenInfo {
ChildrenInfo() = default;
- ChildrenInfo(const ArtifactSet &c1, const ArtifactSet &c2)
- : children(c1), childrenAddedByScanner(c2) {}
+ ChildrenInfo(ArtifactSet c1, ArtifactSet c2)
+ : children(std::move(c1)), childrenAddedByScanner(std::move(c2)) {}
ArtifactSet children;
ArtifactSet childrenAddedByScanner;
};
@@ -140,6 +141,8 @@ private:
Logger m_logger;
QStringList m_artifactsRemovedFromDisk;
std::unordered_map<QString, std::vector<SourceArtifactConstPtr>> m_changedSourcesByProduct;
+ Set<QString> m_changedProjectFiles;
+ Set<QString> m_removedProjectFiles;
Set<QString> m_productsWhoseArtifactsNeedUpdate;
qint64 m_wildcardExpansionEffort = 0;
qint64 m_propertyComparisonEffort = 0;
diff --git a/src/lib/corelib/buildgraph/buildgraphnode.cpp b/src/lib/corelib/buildgraph/buildgraphnode.cpp
index 7d011d50c..cb19ee95c 100644
--- a/src/lib/corelib/buildgraph/buildgraphnode.cpp
+++ b/src/lib/corelib/buildgraph/buildgraphnode.cpp
@@ -56,9 +56,9 @@ BuildGraphNode::BuildGraphNode() : buildState(Untouched)
BuildGraphNode::~BuildGraphNode()
{
- for (BuildGraphNode *p : qAsConst(parents))
+ for (BuildGraphNode *p : std::as_const(parents))
p->children.remove(this);
- for (BuildGraphNode *c : qAsConst(children))
+ for (BuildGraphNode *c : std::as_const(children))
c->parents.remove(this);
}
@@ -69,7 +69,7 @@ void BuildGraphNode::onChildDisconnected(BuildGraphNode *child)
void BuildGraphNode::acceptChildren(BuildGraphVisitor *visitor)
{
- for (BuildGraphNode *child : qAsConst(children))
+ for (BuildGraphNode *child : std::as_const(children))
child->accept(visitor);
}
diff --git a/src/lib/corelib/buildgraph/cycledetector.cpp b/src/lib/corelib/buildgraph/cycledetector.cpp
index 5daed55fd..3a1c43cd1 100644
--- a/src/lib/corelib/buildgraph/cycledetector.cpp
+++ b/src/lib/corelib/buildgraph/cycledetector.cpp
@@ -91,7 +91,7 @@ bool CycleDetector::visitNode(BuildGraphNode *node)
m_nodesInCurrentPath += node;
m_parent = node;
- for (BuildGraphNode * const child : qAsConst(node->children))
+ for (BuildGraphNode * const child : std::as_const(node->children))
child->accept(this);
m_nodesInCurrentPath -= node;
m_allNodes += node;
diff --git a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp
index f1bf8db13..7b333d32f 100644
--- a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp
+++ b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.cpp
@@ -46,37 +46,39 @@
namespace qbs {
namespace Internal {
-static QScriptValue toScriptValue(ScriptEngine *engine, const QString &productName,
- const QVariantMap &v, const QString &depName,
- const QualifiedId &moduleName)
+static JSValue toScriptValue(ScriptEngine *engine, const QString &productName,
+ const QVariantMap &v, const QString &depName,
+ const QualifiedId &moduleName)
{
- QScriptValue obj = engine->newObject();
+ JSValue obj = engine->newObject();
bool objIdAddedToObserver = false;
for (auto it = v.begin(); it != v.end(); ++it) {
- if (it.value().type() == QVariant::Map) {
- obj.setProperty(it.key(), toScriptValue(engine, productName, it.value().toMap(),
- depName, QualifiedId(moduleName) << it.key()));
+ if (it.value().userType() == QMetaType::QVariantMap) {
+ setJsProperty(engine->context(), obj, it.key(),
+ toScriptValue(engine, productName, it.value().toMap(),
+ depName, QualifiedId(moduleName) << it.key()));
} else {
if (!objIdAddedToObserver) {
objIdAddedToObserver = true;
- engine->observer()->addParameterObjectId(obj.objectId(), productName, depName,
+ engine->observer()->addParameterObjectId(jsObjectId(obj), productName, depName,
moduleName);
}
- engine->setObservedProperty(obj, it.key(), engine->toScriptValue(it.value()));
+ const ScopedJsValue val(engine->context(), engine->toScriptValue(it.value()));
+ engine->setObservedProperty(obj, it.key(), val);
}
}
return obj;
}
-static QScriptValue toScriptValue(ScriptEngine *scriptEngine, const QString &productName,
- const QVariantMap &v, const QString &depName)
+static JSValue toScriptValue(ScriptEngine *scriptEngine, const QString &productName,
+ const QVariantMap &v, const QString &depName)
{
return toScriptValue(scriptEngine, productName, v, depName, {});
}
-QScriptValue dependencyParametersValue(const QString &productName, const QString &dependencyName,
- const QVariantMap &parametersMap, ScriptEngine *engine)
+JSValue dependencyParametersValue(const QString &productName, const QString &dependencyName,
+ const QVariantMap &parametersMap, ScriptEngine *engine)
{
return toScriptValue(engine, productName, parametersMap, dependencyName);
}
diff --git a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h
index 7e4287be6..2fb4634e8 100644
--- a/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h
+++ b/src/lib/corelib/buildgraph/dependencyparametersscriptvalue.h
@@ -39,15 +39,16 @@
#ifndef QBS_DEPENDENCYPARAMETERSSCRIPTVALUE_H
#define QBS_DEPENDENCYPARAMETERSSCRIPTVALUE_H
+#include <quickjs.h>
+
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptvalue.h>
namespace qbs {
namespace Internal {
class ScriptEngine;
-QScriptValue dependencyParametersValue(const QString &productName, const QString &dependencyName,
- const QVariantMap &parametersMap, ScriptEngine *engine);
+JSValue dependencyParametersValue(const QString &productName, const QString &dependencyName,
+ const QVariantMap &parametersMap, ScriptEngine *engine);
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/depscanner.cpp b/src/lib/corelib/buildgraph/depscanner.cpp
index 0bf644286..01e781a54 100644
--- a/src/lib/corelib/buildgraph/depscanner.cpp
+++ b/src/lib/corelib/buildgraph/depscanner.cpp
@@ -56,7 +56,7 @@
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptcontext.h>
+#include <vector>
namespace qbs {
namespace Internal {
@@ -110,7 +110,7 @@ QStringList PluginDependencyScanner::collectDependencies(Artifact *artifact, Fil
void *scannerHandle = m_plugin->open(filepath.utf16(), fileTags, ScanForDependenciesFlag);
if (!scannerHandle)
return {};
- forever {
+ for (;;) {
int flags = 0;
int length = 0;
const char *szOutFilePath = m_plugin->next(scannerHandle, &length, &flags);
@@ -127,7 +127,7 @@ QStringList PluginDependencyScanner::collectDependencies(Artifact *artifact, Fil
result += outFilePath;
}
m_plugin->close(scannerHandle);
- return result.toList();
+ return rangeTo<QStringList>(result);
}
bool PluginDependencyScanner::recursive() const
@@ -158,10 +158,9 @@ UserDependencyScanner::UserDependencyScanner(ResolvedScannerConstPtr scanner,
ScriptEngine *engine)
: m_scanner(std::move(scanner)),
m_engine(engine),
+ m_global(engine->context(), JS_NewObjectProto(engine->context(), m_engine->globalObject())),
m_product(nullptr)
{
- m_global = m_engine->newObject();
- m_global.setPrototype(m_engine->globalObject());
setupScriptEngineForFile(m_engine, m_scanner->scanScript.fileContext(), m_global,
ObserveMode::Disabled); // TODO: QBS-1092
}
@@ -218,7 +217,7 @@ public:
}
};
-QStringList UserDependencyScanner::evaluate(const Artifact *artifact,
+QStringList UserDependencyScanner::evaluate(Artifact *artifact,
const FileResourceBase *fileToScan, const PrivateScriptFunction &script)
{
ScriptEngineActiveFlagGuard guard(m_engine);
@@ -229,38 +228,37 @@ QStringList UserDependencyScanner::evaluate(const Artifact *artifact,
m_scanner->module.get(), m_global, true);
}
- QScriptValueList args;
+ JSValueList args;
args.reserve(fileToScan ? 4 : 3);
- args.push_back(m_global.property(StringConstants::projectVar()));
- args.push_back(m_global.property(StringConstants::productVar()));
+ args.push_back(getJsProperty(m_engine->context(), m_global, StringConstants::projectVar()));
+ args.push_back(getJsProperty(m_engine->context(), m_global, StringConstants::productVar()));
args.push_back(Transformer::translateFileConfig(m_engine, artifact, m_scanner->module->name));
if (fileToScan)
- args.push_back(fileToScan->filePath());
+ args.push_back(makeJsString(m_engine->context(), fileToScan->filePath()));
+ const ScopedJsValueList argsMgr(m_engine->context(), args);
- 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());
+ const TemporaryGlobalObjectSetter gos(m_engine, m_global);
+ const JSValue function = script.getFunction(m_engine, Tr::tr("Invalid scan script."));
+ const ScopedJsValue result(
+ m_engine->context(),
+ JS_Call(m_engine->context(), function, m_engine->globalObject(),
+ int(args.size()), args.data()));
m_engine->clearRequestedProperties();
- if (Q_UNLIKELY(m_engine->hasErrorOrException(result))) {
- QString msg = Tr::tr("evaluating scan script: ") + m_engine->lastErrorString(result);
- const CodeLocation loc = m_engine->lastErrorLocation(result, script.location());
- m_engine->clearExceptions();
- throw ErrorInfo(msg, loc);
+ if (JsException ex = m_engine->checkAndClearException(script.location())) {
+ ErrorInfo err = ex.toErrorInfo();
+ err.prepend(Tr::tr("Error evaluating scan script"));
+ throw err;
}
QStringList list;
- if (result.isArray()) {
- const int count = result.property(StringConstants::lengthProperty()).toInt32();
+ if (JS_IsArray(m_engine->context(), result)) {
+ const int count = getJsIntProperty(m_engine->context(), result,
+ StringConstants::lengthProperty());
list.reserve(count);
for (qint32 i = 0; i < count; ++i) {
- QScriptValue item = result.property(i);
- if (item.isValid() && !item.isUndefined())
- list.push_back(item.toString());
+ JSValue item = JS_GetPropertyUint32(m_engine->context(), result, i);
+ if (!JS_IsUninitialized(item) && !JS_IsUndefined(item))
+ list.push_back(getJsString(m_engine->context(), item));
+ JS_FreeValue(m_engine->context(), item);
}
}
return list;
diff --git a/src/lib/corelib/buildgraph/depscanner.h b/src/lib/corelib/buildgraph/depscanner.h
index 6b18004f9..51816dbd7 100644
--- a/src/lib/corelib/buildgraph/depscanner.h
+++ b/src/lib/corelib/buildgraph/depscanner.h
@@ -43,11 +43,10 @@
#include <language/forward_decls.h>
#include <language/filetags.h>
#include <language/preparescriptobserver.h>
+#include <tools/scripttools.h>
#include <QtCore/qstringlist.h>
-#include <QtScript/qscriptvalue.h>
-
class ScannerPlugin;
namespace qbs {
@@ -115,11 +114,11 @@ private:
const PropertyMapConstPtr &m2) const override;
bool cacheIsPerFile() const override { return true; }
- QStringList evaluate(const Artifact *artifact, const FileResourceBase *fileToScan, const PrivateScriptFunction &script);
+ QStringList evaluate(Artifact *artifact, const FileResourceBase *fileToScan, const PrivateScriptFunction &script);
ResolvedScannerConstPtr m_scanner;
ScriptEngine *m_engine;
- QScriptValue m_global;
+ ScopedJsValue m_global;
ResolvedProduct *m_product;
};
diff --git a/src/lib/corelib/buildgraph/environmentscriptrunner.cpp b/src/lib/corelib/buildgraph/environmentscriptrunner.cpp
index 7d82efb41..0fbb3ab19 100644
--- a/src/lib/corelib/buildgraph/environmentscriptrunner.cpp
+++ b/src/lib/corelib/buildgraph/environmentscriptrunner.cpp
@@ -45,11 +45,14 @@
#include <language/propertymapinternal.h>
#include <language/resolvedfilecontext.h>
#include <language/scriptengine.h>
+#include <logging/translator.h>
#include <tools/qbsassert.h>
#include <tools/qttools.h>
-#include <logging/translator.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
+#include <quickjs.h>
+
#include <QtCore/qhash.h>
#include <QtCore/qvariant.h>
@@ -123,19 +126,18 @@ void EnvironmentScriptRunner::setupEnvironment()
const auto hasScript = [this](const ResolvedModuleConstPtr &m) {
return !getScript(m.get()).sourceCode().isEmpty();
};
- const bool hasAnyScripts = std::any_of(m_product->modules.cbegin(), m_product->modules.cend(),
- hasScript);
+ const bool hasAnyScripts = Internal::any_of(m_product->modules, hasScript);
if (!hasAnyScripts)
return;
QMap<QString, const ResolvedModule *> moduleMap;
- for (const ResolvedModuleConstPtr &module : m_product->modules)
+ for (const auto &module : m_product->modules)
moduleMap.insert(module->name, module.get());
QHash<const ResolvedModule*, QList<const ResolvedModule*> > moduleParents;
QHash<const ResolvedModule*, QList<const ResolvedModule*> > moduleChildren;
- for (const ResolvedModuleConstPtr &module : m_product->modules) {
- for (const QString &moduleName : qAsConst(module->moduleDependencies)) {
+ for (const auto &module : m_product->modules) {
+ for (const QString &moduleName : std::as_const(module->moduleDependencies)) {
const ResolvedModule * const depmod = moduleMap.value(moduleName);
QBS_ASSERT(depmod, return);
moduleParents[depmod].push_back(module.get());
@@ -144,7 +146,7 @@ void EnvironmentScriptRunner::setupEnvironment()
}
QList<const ResolvedModule *> rootModules;
- for (const ResolvedModuleConstPtr &module : m_product->modules) {
+ for (const auto &module : m_product->modules) {
if (moduleParents.value(module.get()).isEmpty()) {
QBS_ASSERT(module, return);
rootModules.push_back(module.get());
@@ -165,38 +167,40 @@ void EnvironmentScriptRunner::setupEnvironment()
continue;
RulesEvaluationContext::Scope s(m_evalContext);
- QScriptValue envScriptContext = engine()->newObject();
- envScriptContext.setPrototype(engine()->globalObject());
+ JSContext * const ctx = engine()->context();
+ ScopedJsValue envScriptContext(ctx, JS_NewObjectProto(ctx, engine()->globalObject()));
setupScriptEngineForProduct(engine(), m_product, module, envScriptContext, false);
const QString &productKey = StringConstants::productVar();
const QString &projectKey = StringConstants::projectVar();
- m_evalContext->scope().setProperty(productKey, envScriptContext.property(productKey));
- m_evalContext->scope().setProperty(projectKey, envScriptContext.property(projectKey));
+ setJsProperty(ctx, m_evalContext->scope(), productKey,
+ getJsProperty(ctx, envScriptContext, productKey));
+ setJsProperty(ctx, m_evalContext->scope(), projectKey,
+ getJsProperty(ctx, envScriptContext, projectKey));
if (m_envType == RunEnv) {
- QScriptValue configArray = engine()->newArray(m_runEnvConfig.size());
+ JSValue configArray = JS_NewArray(ctx);
for (int i = 0; i < m_runEnvConfig.size(); ++i)
- configArray.setProperty(i, QScriptValue(m_runEnvConfig.at(i)));
- m_evalContext->scope().setProperty(QStringLiteral("config"), configArray);
+ JS_SetPropertyUint32(ctx, configArray, i, makeJsString(ctx, m_runEnvConfig.at(i)));
+ JS_SetPropertyStr(ctx, m_evalContext->scope(), "config", configArray);
}
setupScriptEngineForFile(engine(), setupScript.fileContext(), m_evalContext->scope(),
ObserveMode::Disabled);
// TODO: Cache evaluate result
- QScriptValue fun = engine()->evaluate(setupScript.sourceCode(),
- setupScript.location().filePath(),
- setupScript.location().line());
- QBS_CHECK(fun.isFunction());
- const QScriptValueList svArgs = ScriptEngine::argumentList(scriptFunctionArgs,
- m_evalContext->scope());
- const QScriptValue res = fun.call(QScriptValue(), svArgs);
- engine()->releaseResourcesOfScriptObjects();
- if (Q_UNLIKELY(engine()->hasErrorOrException(res))) {
+ ScopedJsValue fun(ctx, engine()->evaluate(JsValueOwner::Caller, setupScript.sourceCode(),
+ setupScript.location().filePath(),
+ setupScript.location().line()));
+ QBS_CHECK(JS_IsFunction(ctx, fun));
+ const ScopedJsValueList svArgs = engine()->argumentList(scriptFunctionArgs,
+ m_evalContext->scope());
+ JSValueList argsForFun = svArgs;
+ JS_Call(ctx, fun, engine()->globalObject(), int(argsForFun.size()), argsForFun.data());
+ if (const JsException ex = engine()->checkAndClearException(setupScript.location())) {
const QString scriptName = m_envType == BuildEnv
? StringConstants::setupBuildEnvironmentProperty()
: StringConstants::setupRunEnvironmentProperty();
- throw ErrorInfo(Tr::tr("Error running %1 script for product '%2': %3")
- .arg(scriptName, m_product->fullDisplayName(),
- engine()->lastErrorString(res)),
- engine()->lastErrorLocation(res, setupScript.location()));
+ ErrorInfo err = ex.toErrorInfo();
+ err.prepend(Tr::tr("Error running %1 script for product '%2'")
+ .arg(scriptName, m_product->fullDisplayName()));
+ throw err;
}
}
diff --git a/src/lib/corelib/buildgraph/executor.cpp b/src/lib/corelib/buildgraph/executor.cpp
index de81ada20..2b8232a0a 100644
--- a/src/lib/corelib/buildgraph/executor.cpp
+++ b/src/lib/corelib/buildgraph/executor.cpp
@@ -67,6 +67,7 @@
#include <tools/qbsassert.h>
#include <tools/qttools.h>
#include <tools/settings.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
#include <QtCore/qdir.h>
@@ -102,12 +103,8 @@ Executor::Executor(Logger logger, QObject *parent)
Executor::~Executor()
{
- // jobs must be destroyed before deleting the shared scan result cache
- for (ExecutorJob *job : qAsConst(m_availableJobs))
- delete job;
- const auto processingJobs = m_processingJobs.keys();
- for (ExecutorJob *job : processingJobs)
- delete job;
+ // jobs must be destroyed before deleting the m_inputArtifactScanContext
+ m_allJobs.clear();
delete m_inputArtifactScanContext;
delete m_productInstaller;
}
@@ -138,11 +135,9 @@ void Executor::retrieveSourceFileTimestamp(Artifact *artifact) const
{
QBS_CHECK(artifact->artifactType == Artifact::SourceFile);
- if (m_buildOptions.changedFiles().empty())
- artifact->setTimestamp(recursiveFileTime(artifact->filePath()));
- else if (m_buildOptions.changedFiles().contains(artifact->filePath()))
+ if (m_buildOptions.changedFiles().contains(artifact->filePath()))
artifact->setTimestamp(FileTime::currentTime());
- else if (!artifact->timestamp().isValid())
+ else if (m_buildOptions.changedFiles().empty() || !artifact->timestamp().isValid())
artifact->setTimestamp(recursiveFileTime(artifact->filePath()));
artifact->timestampRetrieved = true;
@@ -153,7 +148,7 @@ void Executor::retrieveSourceFileTimestamp(Artifact *artifact) const
void Executor::build()
{
try {
- m_partialBuild = m_productsToBuild.size() != m_allProducts.size();
+ m_partialBuild = size_t(m_productsToBuild.size()) != m_allProducts.size();
doBuild();
} catch (const ErrorInfo &e) {
handleError(e);
@@ -170,7 +165,7 @@ void Executor::setProject(const TopLevelProjectPtr &project)
m_projectsByName.insert(std::make_pair(p->name, p.get()));
}
-void Executor::setProducts(const std::vector<ResolvedProductPtr> &productsToBuild)
+void Executor::setProducts(const QVector<ResolvedProductPtr> &productsToBuild)
{
m_productsToBuild = productsToBuild;
m_productsByName.clear();
@@ -197,7 +192,7 @@ public:
allDependencies += dep;
}
const Set<ResolvedProductPtr> rootProducts
- = Set<ResolvedProductPtr>::fromStdVector(m_allProducts) - allDependencies;
+ = rangeTo<Set<ResolvedProductPtr>>(m_allProducts) - allDependencies;
m_priority = UINT_MAX;
m_seenProducts.clear();
for (const ResolvedProductPtr &rootProduct : rootProducts)
@@ -209,7 +204,7 @@ private:
{
if (!m_seenProducts.insert(product).second)
return;
- for (const ResolvedProductPtr &dependency : qAsConst(product->dependencies))
+ for (const ResolvedProductPtr &dependency : std::as_const(product->dependencies))
traverse(dependency);
if (!product->buildData)
return;
@@ -266,9 +261,9 @@ void Executor::doBuild()
doSanityChecks();
QBS_CHECK(!m_project->buildData->evaluationContext);
- m_project->buildData->evaluationContext
- = RulesEvaluationContextPtr(new RulesEvaluationContext(m_logger));
+ m_project->buildData->evaluationContext = std::make_shared<RulesEvaluationContext>(m_logger);
m_evalContext = m_project->buildData->evaluationContext;
+ m_progressObserver->addScriptEngine(m_evalContext->engine());
m_elapsedTimeRules = m_elapsedTimeScanners = m_elapsedTimeInstalling = 0;
m_evalContext->engine()->enableProfiling(m_buildOptions.logElapsedTime());
@@ -334,7 +329,7 @@ void Executor::updateLeaves(BuildGraphNode *node, NodeSet &seenNodes)
}
bool isLeaf = true;
- for (BuildGraphNode *child : qAsConst(node->children)) {
+ for (BuildGraphNode *child : std::as_const(node->children)) {
if (child->buildState != BuildGraphNode::Built) {
isLeaf = false;
updateLeaves(child, seenNodes);
@@ -402,8 +397,10 @@ bool Executor::schedulingBlockedByJobLimit(const BuildGraphNode *node)
if (currentJobCount == 0)
continue;
const auto jobLimitIsExceeded = [currentJobCount, jobPool, this](const Transformer *t) {
- const int maxJobCount = m_jobLimitsPerProduct.at(t->product().get())
- .getLimit(jobPool);
+ const auto it = m_jobLimitsPerProduct.find(t->product().get());
+ if (it == m_jobLimitsPerProduct.cend())
+ return false; // See checkNodeProduct() for why this is possible
+ const int maxJobCount = it->second.getLimit(jobPool);
return maxJobCount > 0 && currentJobCount >= maxJobCount;
};
@@ -455,7 +452,7 @@ bool Executor::isUpToDate(Artifact *artifact) const
return false;
}
- for (FileDependency *fileDependency : qAsConst(artifact->fileDependencies)) {
+ for (FileDependency *fileDependency : std::as_const(artifact->fileDependencies)) {
if (!fileDependency->timestamp().isValid()) {
qCDebug(lcUpToDateCheck) << "file dependency doesn't exist"
<< fileDependency->filePath();
@@ -482,7 +479,7 @@ bool Executor::mustExecuteTransformer(const TransformerPtr &transformer) const
bool hasAlwaysUpdatedArtifacts = false;
bool hasUpToDateNotAlwaysUpdatedArtifacts = false;
- for (Artifact *artifact : qAsConst(transformer->outputs)) {
+ for (Artifact *artifact : std::as_const(transformer->outputs)) {
if (isUpToDate(artifact)) {
if (artifact->alwaysUpdated)
hasAlwaysUpdatedArtifacts = true;
@@ -571,7 +568,7 @@ void Executor::finishJob(ExecutorJob *job, bool success)
updateJobCounts(transformer.get(), -1);
if (success) {
m_project->buildData->setDirty();
- for (Artifact * const artifact : qAsConst(transformer->outputs)) {
+ for (Artifact * const artifact : std::as_const(transformer->outputs)) {
if (artifact->alwaysUpdated) {
artifact->setTimestamp(FileTime::currentTime());
for (Artifact * const parent : artifact->parentArtifacts())
@@ -624,14 +621,13 @@ void Executor::finishJob(ExecutorJob *job, bool success)
static bool allChildrenBuilt(BuildGraphNode *node)
{
- return std::all_of(node->children.cbegin(), node->children.cend(),
- std::mem_fn(&BuildGraphNode::isBuilt));
+ return Internal::all_of(node->children, std::mem_fn(&BuildGraphNode::isBuilt));
}
void Executor::finishNode(BuildGraphNode *leaf)
{
leaf->buildState = BuildGraphNode::Built;
- for (BuildGraphNode * const parent : qAsConst(leaf->parents)) {
+ for (BuildGraphNode * const parent : std::as_const(leaf->parents)) {
if (parent->buildState != BuildGraphNode::Buildable) {
qCDebug(lcExec).noquote() << "parent" << parent->toString()
<< "build state:" << toString(parent->buildState);
@@ -666,8 +662,8 @@ bool Executor::transformerHasMatchingOutputTags(const TransformerConstPtr &trans
if (m_activeFileTags.empty())
return true; // No filtering requested.
- return std::any_of(transformer->outputs.cbegin(), transformer->outputs.cend(),
- [this](const Artifact *a) { return artifactHasMatchingOutputTags(a); });
+ return Internal::any_of(transformer->outputs, [this](const Artifact *a) {
+ return artifactHasMatchingOutputTags(a); });
}
bool Executor::artifactHasMatchingOutputTags(const Artifact *artifact) const
@@ -684,7 +680,7 @@ bool Executor::transformerHasMatchingInputFiles(const TransformerConstPtr &trans
return false;
if (transformer->inputs.empty())
return true;
- for (const Artifact * const input : qAsConst(transformer->inputs)) {
+ for (const Artifact * const input : std::as_const(transformer->inputs)) {
const auto files = m_buildOptions.filesToConsider();
for (const QString &filePath : files) {
if (input->filePath() == filePath
@@ -700,7 +696,7 @@ bool Executor::transformerHasMatchingInputFiles(const TransformerConstPtr &trans
void Executor::setupJobLimits()
{
Settings settings(m_buildOptions.settingsDirectory());
- for (const ResolvedProductConstPtr &p : m_productsToBuild) {
+ for (const auto &p : std::as_const(m_productsToBuild)) {
const Preferences prefs(&settings, p->profile());
const JobLimits &jobLimitsFromSettings = prefs.jobLimits();
JobLimits effectiveJobLimits;
@@ -737,7 +733,7 @@ void Executor::setupProgressObserver()
if (!m_progressObserver)
return;
int totalEffort = 1; // For the effort after the last rule application;
- for (const ResolvedProductConstPtr &product : qAsConst(m_productsToBuild)) {
+ for (const auto &product : std::as_const(m_productsToBuild)) {
QBS_CHECK(product->buildData);
const auto filtered = filterByType<RuleNode>(product->buildData->allNodes());
totalEffort += std::distance(filtered.begin(), filtered.end());
@@ -749,7 +745,7 @@ void Executor::doSanityChecks()
{
QBS_CHECK(m_project);
QBS_CHECK(!m_productsToBuild.empty());
- for (const ResolvedProductConstPtr &product : qAsConst(m_productsToBuild)) {
+ for (const auto &product : std::as_const(m_productsToBuild)) {
QBS_CHECK(product->buildData);
QBS_CHECK(product->topLevelProject() == m_project.get());
}
@@ -768,10 +764,13 @@ void Executor::handleError(const ErrorInfo &error)
void Executor::addExecutorJobs()
{
- qCDebug(lcExec) << "preparing executor for" << m_buildOptions.maxJobCount()
- << "jobs in parallel";
- for (int i = 1; i <= m_buildOptions.maxJobCount(); i++) {
- const auto job = new ExecutorJob(m_logger, this);
+ const int count = m_buildOptions.maxJobCount();
+ qCDebug(lcExec) << "preparing executor for" << count << "jobs in parallel";
+ m_allJobs.reserve(count);
+ m_availableJobs.reserve(count);
+ for (int i = 1; i <= count; i++) {
+ m_allJobs.push_back(std::make_unique<ExecutorJob>(m_logger));
+ const auto job = m_allJobs.back().get();
job->setMainThreadScriptEngine(m_evalContext->engine());
job->setObjectName(QStringLiteral("J%1").arg(i));
job->setDryRun(m_buildOptions.dryRun());
@@ -917,7 +916,7 @@ bool Executor::checkForUnbuiltDependencies(Artifact *artifact)
{
bool buildingDependenciesFound = false;
NodeSet unbuiltDependencies;
- for (BuildGraphNode * const dependency : qAsConst(artifact->children)) {
+ for (BuildGraphNode * const dependency : std::as_const(artifact->children)) {
switch (dependency->buildState) {
case BuildGraphNode::Untouched:
case BuildGraphNode::Buildable:
@@ -948,7 +947,7 @@ bool Executor::checkForUnbuiltDependencies(Artifact *artifact)
void Executor::potentiallyRunTransformer(const TransformerPtr &transformer)
{
- for (Artifact * const output : qAsConst(transformer->outputs)) {
+ for (Artifact * const output : std::as_const(transformer->outputs)) {
// Rescuing build data can introduce new dependencies, potentially delaying execution of
// this transformer.
bool childrenAddedDueToRescue;
@@ -971,7 +970,7 @@ void Executor::potentiallyRunTransformer(const TransformerPtr &transformer)
const bool mustExecute = mustExecuteTransformer(transformer);
if (mustExecute || m_buildOptions.forceTimestampCheck()) {
- for (Artifact * const output : qAsConst(transformer->outputs)) {
+ for (Artifact * const output : std::as_const(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);
@@ -1002,7 +1001,7 @@ void Executor::runTransformer(const TransformerPtr &transformer)
// create the output directories
if (!m_buildOptions.dryRun()) {
- for (Artifact * const output : qAsConst(transformer->outputs)) {
+ for (Artifact * const output : std::as_const(transformer->outputs)) {
QDir outDir = QFileInfo(output->filePath()).absoluteDir();
if (!outDir.exists() && !outDir.mkpath(StringConstants::dot())) {
throw ErrorInfo(tr("Failed to create directory '%1'.")
@@ -1013,7 +1012,7 @@ void Executor::runTransformer(const TransformerPtr &transformer)
QBS_CHECK(!m_availableJobs.empty());
ExecutorJob *job = m_availableJobs.takeFirst();
- for (Artifact * const artifact : qAsConst(transformer->outputs))
+ for (Artifact * const artifact : std::as_const(transformer->outputs))
artifact->buildState = BuildGraphNode::Building;
m_processingJobs.insert(job, transformer);
updateJobCounts(transformer.get(), 1);
@@ -1023,7 +1022,7 @@ void Executor::runTransformer(const TransformerPtr &transformer)
void Executor::finishTransformer(const TransformerPtr &transformer)
{
transformer->markedForRerun = false;
- for (Artifact * const artifact : qAsConst(transformer->outputs)) {
+ for (Artifact * const artifact : std::as_const(transformer->outputs)) {
possiblyInstallArtifact(artifact);
finishArtifact(artifact);
}
@@ -1058,7 +1057,7 @@ void Executor::onJobFinished(const qbs::ErrorInfo &err)
if (m_evalContext->engine()->isActive()) {
qCDebug(lcExec) << "Executor job finished while rule execution is pausing. "
"Delaying slot execution.";
- QTimer::singleShot(0, job, [job, err] { job->finished(err); });
+ QTimer::singleShot(0, job, [job, err] { emit job->finished(err); });
return;
}
@@ -1084,9 +1083,9 @@ void Executor::checkForUnbuiltProducts()
if (m_buildOptions.executeRulesOnly())
return;
std::vector<ResolvedProductPtr> unbuiltProducts;
- for (const ResolvedProductPtr &product : m_productsToBuild) {
+ for (const ResolvedProductPtr &product : std::as_const(m_productsToBuild)) {
bool productBuilt = true;
- for (BuildGraphNode *rootNode : qAsConst(product->buildData->rootNodes())) {
+ for (BuildGraphNode *rootNode : std::as_const(product->buildData->rootNodes())) {
if (rootNode->buildState != BuildGraphNode::Built) {
productBuilt = false;
unbuiltProducts.push_back(product);
@@ -1109,10 +1108,8 @@ void Executor::checkForUnbuiltProducts()
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()));
- QStringList productNames;
- std::transform(unbuiltProducts.cbegin(), unbuiltProducts.cend(),
- std::back_inserter(productNames),
- [](const ResolvedProductConstPtr &p) { return p->fullDisplayName(); });
+ auto productNames = transformed<QStringList>(unbuiltProducts, [](const auto &p) {
+ return p->fullDisplayName(); });
std::sort(productNames.begin(), productNames.end());
m_error.append(productNames.join(QLatin1String(", ")));
}
@@ -1202,11 +1199,11 @@ void Executor::prepareAllNodes()
for (const ResolvedProductPtr &product : m_allProducts) {
if (product->enabled) {
QBS_CHECK(product->buildData);
- for (BuildGraphNode * const node : qAsConst(product->buildData->allNodes()))
+ for (BuildGraphNode * const node : std::as_const(product->buildData->allNodes()))
node->buildState = BuildGraphNode::Untouched;
}
}
- for (const ResolvedProductPtr &product : m_productsToBuild) {
+ for (const ResolvedProductPtr &product : std::as_const(m_productsToBuild)) {
QBS_CHECK(product->buildData);
for (Artifact * const artifact : filterByType<Artifact>(product->buildData->allNodes()))
prepareArtifact(artifact);
@@ -1228,12 +1225,12 @@ void Executor::syncFileDependencies()
"removing from lookup table";
m_project->buildData->removeFromLookupTable(dep);
bool isReferencedByArtifact = false;
- for (const ResolvedProductConstPtr &product : m_allProducts) {
+ for (const auto &product : m_allProducts) {
if (!product->buildData)
continue;
const auto artifactList = filterByType<Artifact>(product->buildData->allNodes());
- isReferencedByArtifact = std::any_of(artifactList.begin(), artifactList.end(),
- [dep](const Artifact *a) { return a->fileDependencies.contains(dep); });
+ isReferencedByArtifact = Internal::any_of(artifactList, [dep](const Artifact *a) {
+ return a->fileDependencies.contains(dep); });
// TODO: Would it be safe to mark the artifact as "not up to date" here and clear
// its list of file dependencies, rather than doing the check again in
// isUpToDate()?
@@ -1290,7 +1287,7 @@ void Executor::setupForBuildingSelectedFiles(const BuildGraphNode *node)
*/
void Executor::prepareReachableNodes()
{
- for (BuildGraphNode * const root : qAsConst(m_roots))
+ for (BuildGraphNode * const root : std::as_const(m_roots))
prepareReachableNodes_impl(root);
}
@@ -1302,7 +1299,7 @@ void Executor::prepareReachableNodes_impl(BuildGraphNode *node)
return;
node->buildState = BuildGraphNode::Buildable;
- for (BuildGraphNode *child : qAsConst(node->children))
+ for (BuildGraphNode *child : std::as_const(node->children))
prepareReachableNodes_impl(child);
}
@@ -1310,7 +1307,7 @@ void Executor::prepareProducts()
{
ProductPrioritySetter prioritySetter(m_allProducts);
prioritySetter.apply();
- for (const ResolvedProductPtr &product : m_productsToBuild) {
+ for (const ResolvedProductPtr &product : std::as_const(m_productsToBuild)) {
EnvironmentScriptRunner(product.get(), m_evalContext.get(), m_project->environment)
.setupForBuild();
}
@@ -1319,7 +1316,7 @@ void Executor::prepareProducts()
void Executor::setupRootNodes()
{
m_roots.clear();
- for (const ResolvedProductPtr &product : m_productsToBuild)
+ for (const ResolvedProductPtr &product : std::as_const(m_productsToBuild))
m_roots += product->buildData->rootNodes();
}
diff --git a/src/lib/corelib/buildgraph/executor.h b/src/lib/corelib/buildgraph/executor.h
index 1fd591176..cc879e125 100644
--- a/src/lib/corelib/buildgraph/executor.h
+++ b/src/lib/corelib/buildgraph/executor.h
@@ -81,7 +81,7 @@ public:
~Executor() override;
void setProject(const TopLevelProjectPtr &project);
- void setProducts(const std::vector<ResolvedProductPtr> &productsToBuild);
+ void setProducts(const QVector<ResolvedProductPtr> &productsToBuild);
void setBuildOptions(const BuildOptions &buildOptions);
void setProgressObserver(ProgressObserver *observer) { m_progressObserver = observer; }
@@ -166,10 +166,11 @@ private:
BuildOptions m_buildOptions;
Logger m_logger;
ProgressObserver *m_progressObserver;
+ std::vector<std::unique_ptr<ExecutorJob>> m_allJobs;
QList<ExecutorJob*> m_availableJobs;
ExecutorState m_state;
TopLevelProjectPtr m_project;
- std::vector<ResolvedProductPtr> m_productsToBuild;
+ QVector<ResolvedProductPtr> m_productsToBuild;
std::vector<ResolvedProductPtr> m_allProducts;
std::unordered_map<QString, const ResolvedProduct *> m_productsByName;
std::unordered_map<QString, const ResolvedProject *> m_projectsByName;
diff --git a/src/lib/corelib/buildgraph/executorjob.h b/src/lib/corelib/buildgraph/executorjob.h
index bc8954072..1f8f0cd73 100644
--- a/src/lib/corelib/buildgraph/executorjob.h
+++ b/src/lib/corelib/buildgraph/executorjob.h
@@ -65,7 +65,7 @@ class ExecutorJob : public QObject
{
Q_OBJECT
public:
- ExecutorJob(const Logger &logger, QObject *parent);
+ explicit ExecutorJob(const Logger &logger, QObject *parent = nullptr);
~ExecutorJob() override;
void setMainThreadScriptEngine(ScriptEngine *engine);
diff --git a/src/lib/corelib/buildgraph/filedependency.h b/src/lib/corelib/buildgraph/filedependency.h
index 802654e9f..93bfc5d05 100644
--- a/src/lib/corelib/buildgraph/filedependency.h
+++ b/src/lib/corelib/buildgraph/filedependency.h
@@ -77,8 +77,8 @@ private:
FileTime m_timestamp;
QString m_filePath;
- QStringRef m_dirPath;
- QStringRef m_fileName;
+ QStringView m_dirPath;
+ QStringView m_fileName;
};
class FileDependency : public FileResourceBase
diff --git a/src/lib/corelib/buildgraph/inputartifactscanner.cpp b/src/lib/corelib/buildgraph/inputartifactscanner.cpp
index 14f39bb2a..0c73f599f 100644
--- a/src/lib/corelib/buildgraph/inputartifactscanner.cpp
+++ b/src/lib/corelib/buildgraph/inputartifactscanner.cpp
@@ -49,11 +49,12 @@
#include <language/language.h>
#include <logging/categories.h>
+#include <tools/error.h>
#include <tools/fileinfo.h>
-#include <tools/scannerpluginmanager.h>
#include <tools/qbsassert.h>
-#include <tools/error.h>
#include <tools/qttools.h>
+#include <tools/scannerpluginmanager.h>
+#include <tools/stlutils.h>
#include <QtCore/qdir.h>
#include <QtCore/qstringlist.h>
@@ -77,6 +78,7 @@ static void resolveDepencency(const RawScannedDependency &dependency,
FileDependency *fileDependencyArtifact = nullptr;
Artifact *dependencyInProduct = nullptr;
Artifact *dependencyInOtherProduct = nullptr;
+ bool productOfDependencyIsDependency = false;
const auto files = project->topLevelProject()
->buildData->lookupFiles(absDirPath, dependency.fileName());
for (FileResourceBase *lookupResult : files) {
@@ -86,10 +88,13 @@ static void resolveDepencency(const RawScannedDependency &dependency,
break;
case FileResourceBase::FileTypeArtifact: {
auto const foundArtifact = static_cast<Artifact *>(lookupResult);
- if (foundArtifact->product == product)
+ if (foundArtifact->product == product) {
dependencyInProduct = foundArtifact;
- else
+ } else if (!productOfDependencyIsDependency) {
dependencyInOtherProduct = foundArtifact;
+ productOfDependencyIsDependency
+ = contains(product->dependencies, dependencyInOtherProduct->product.lock());
+ }
break;
}
}
@@ -98,10 +103,23 @@ static void resolveDepencency(const RawScannedDependency &dependency,
}
// prioritize found artifacts
- if ((result->file = dependencyInProduct)
- || (result->file = dependencyInOtherProduct)
- || (result->file = fileDependencyArtifact)) {
+ if (dependencyInProduct)
+ result->file = dependencyInProduct;
+ else if (dependencyInOtherProduct)
+ result->file = dependencyInOtherProduct;
+ else
+ result->file = fileDependencyArtifact;
+
+ if (result->file) {
result->filePath = result->file->filePath();
+
+ if (result->file == dependencyInOtherProduct && !productOfDependencyIsDependency) {
+ qCDebug(lcDepScan) << "product" << dependencyInOtherProduct->product->fullDisplayName()
+ << "of scanned dependency" << result->filePath
+ << "is not a dependency of product" << product->fullDisplayName()
+ << ". The file dependency might get lost during change tracking.";
+ }
+
return;
}
@@ -145,7 +163,7 @@ void InputArtifactScanner::scan()
for (Artifact * const dependency : childrenAddedByScanner)
disconnect(m_artifact, dependency);
- for (Artifact * const inputArtifact : qAsConst(m_artifact->transformer->inputs))
+ for (Artifact * const inputArtifact : std::as_const(m_artifact->transformer->inputs))
scanForFileDependencies(inputArtifact);
}
@@ -200,19 +218,18 @@ Set<DependencyScanner *> InputArtifactScanner::scannersForArtifact(const Artifac
InputArtifactScannerContext::DependencyScannerCacheItem &cache = scannerCache[fileTag];
if (!cache.valid) {
cache.valid = true;
- for (ScannerPlugin *scanner : ScannerPluginManager::scannersForFileTag(fileTag)) {
- const auto pluginScanner = new PluginDependencyScanner(scanner);
- cache.scanners.push_back(DependencyScannerPtr(pluginScanner));
- }
+ const auto scanners = ScannerPluginManager::scannersForFileTag(fileTag);
+ transform(scanners, cache.scanners, [](const auto &scanner) {
+ return std::make_shared<PluginDependencyScanner>(scanner); });
for (const ResolvedScannerConstPtr &scanner : product->scanners) {
if (scanner->inputs.contains(fileTag)) {
- cache.scanners.push_back(DependencyScannerPtr(
- new UserDependencyScanner(scanner, engine)));
+ cache.scanners.push_back(
+ std::make_shared<UserDependencyScanner>(scanner, engine));
break;
}
}
}
- for (const DependencyScannerPtr &scanner : qAsConst(cache.scanners))
+ for (const DependencyScannerPtr &scanner : std::as_const(cache.scanners))
scanners += scanner.get();
}
return scanners;
@@ -231,21 +248,16 @@ void InputArtifactScanner::scanForScannerFileDependencies(DependencyScanner *sca
cache.searchPaths = scanner->collectSearchPaths(inputArtifact);
}
qCDebug(lcDepScan) << "include paths (cache" << (cacheHit ? "hit)" : "miss)");
- for (const QString &s : qAsConst(cache.searchPaths))
+ for (const QString &s : std::as_const(cache.searchPaths))
qCDebug(lcDepScan) << " " << s;
const QString &filePathToBeScanned = fileToBeScanned->filePath();
RawScanResults::ScanData &scanData = m_rawScanResults.findScanData(fileToBeScanned, scanner,
m_artifact->properties);
if (scanData.lastScanTime < fileToBeScanned->timestamp()) {
- try {
- qCDebug(lcDepScan) << "scanning" << FileInfo::fileName(filePathToBeScanned);
- scanWithScannerPlugin(scanner, inputArtifact, fileToBeScanned, &scanData.rawScanResult);
- scanData.lastScanTime = FileTime::currentTime();
- } catch (const ErrorInfo &error) {
- m_logger.printWarning(error);
- return;
- }
+ qCDebug(lcDepScan) << "scanning" << FileInfo::fileName(filePathToBeScanned);
+ scanWithScannerPlugin(scanner, inputArtifact, fileToBeScanned, &scanData.rawScanResult);
+ scanData.lastScanTime = FileTime::currentTime();
}
resolveScanResultDependencies(inputArtifact, scanData.rawScanResult, filesToScan, cache);
@@ -255,38 +267,45 @@ void InputArtifactScanner::resolveScanResultDependencies(const Artifact *inputAr
const RawScanResult &scanResult, QList<FileResourceBase *> *artifactsToScan,
InputArtifactScannerContext::ScannerResolvedDependenciesCache &cache)
{
- for (const RawScannedDependency &dependency : scanResult.deps) {
+ auto getResolvedDependency = [inputArtifact, &cache](const RawScannedDependency &dependency)
+ -> ResolvedDependency*
+ {
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;
+ return nullptr;
+ return &resolvedDependency;
}
cachedResolvedDependencyItem.valid = true;
if (FileInfo::isAbsolute(dependencyFilePath)) {
resolveDepencency(dependency, inputArtifact->product.get(), &resolvedDependency);
if (resolvedDependency.filePath.isEmpty())
- goto unresolved;
- goto resolved;
+ return nullptr;
+ return &resolvedDependency;
}
// try include paths
- for (const QString &includePath : qAsConst(cache.searchPaths)) {
+ for (const QString &includePath : std::as_const(cache.searchPaths)) {
resolveDepencency(dependency, inputArtifact->product.get(),
&resolvedDependency, includePath);
if (resolvedDependency.isValid())
- goto resolved;
+ return &resolvedDependency;
}
+ return nullptr;
+ };
-unresolved:
- qCWarning(lcDepScan) << "unresolved dependency " << dependencyFilePath;
- continue;
+ for (const RawScannedDependency &dependency : scanResult.deps) {
+ const auto maybeResolvedDependency = getResolvedDependency(dependency);
+ if (!maybeResolvedDependency) {
+ qCWarning(lcDepScan) << "unresolved dependency " << dependency.filePath();
+ continue;
+ }
+ auto &resolvedDependency = *maybeResolvedDependency;
-resolved:
handleDependency(resolvedDependency);
if (artifactsToScan && resolvedDependency.file) {
if (resolvedDependency.file->fileType() == FileResourceBase::FileTypeArtifact) {
@@ -349,6 +368,8 @@ void InputArtifactScanner::handleDependency(ResolvedDependency &dependency)
if (m_artifact == dependency.file)
return;
+ if (artifactDependency && artifactDependency->transformer == m_artifact->transformer)
+ return;
if (fileDependency) {
m_artifact->fileDependencies << fileDependency;
diff --git a/src/lib/corelib/buildgraph/jscommandexecutor.cpp b/src/lib/corelib/buildgraph/jscommandexecutor.cpp
index aa72a7b5c..4d92bf7c3 100644
--- a/src/lib/corelib/buildgraph/jscommandexecutor.cpp
+++ b/src/lib/corelib/buildgraph/jscommandexecutor.cpp
@@ -40,7 +40,6 @@
#include "jscommandexecutor.h"
-#include "artifact.h"
#include "buildgraph.h"
#include "rulecommands.h"
#include "transformer.h"
@@ -53,8 +52,12 @@
#include <tools/codelocation.h>
#include <tools/error.h>
#include <tools/qbsassert.h>
+#include <tools/qttools.h>
+
+#include <quickjs.h>
#include <QtCore/qeventloop.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qpointer.h>
#include <QtCore/qthread.h>
#include <QtCore/qtimer.h>
@@ -86,10 +89,11 @@ public:
void cancel(const qbs::ErrorInfo &reason)
{
+ QMutexLocker locker(&m_resultMutex);
m_result.success = !reason.hasError();
m_result.errorMessage = reason.toString();
if (m_scriptEngine)
- m_scriptEngine->abortEvaluation();
+ m_scriptEngine->cancel();
m_cancelled = true;
}
@@ -121,40 +125,34 @@ private:
m_result.success = true;
m_result.errorMessage.clear();
ScriptEngine * const scriptEngine = provideScriptEngine();
- QScriptValue scope = scriptEngine->newObject();
- scope.setPrototype(scriptEngine->globalObject());
- m_scriptEngine->clearRequestedProperties();
+ JSContext * const ctx = scriptEngine->context();
+ const ScopedJsValue scope(ctx, JS_NewObject(scriptEngine->context()));
setupScriptEngineForFile(scriptEngine,
transformer->rule->prepareScript.fileContext(), scope,
ObserveMode::Enabled);
-
- QScriptValue importScopeForSourceCode;
+ ScopedJsValue importScopeForSourceCode(ctx, JS_UNDEFINED);
if (!cmd->scopeName().isEmpty())
- importScopeForSourceCode = scope.property(cmd->scopeName());
+ importScopeForSourceCode.setValue(getJsProperty(ctx, scope, cmd->scopeName()));
setupScriptEngineForProduct(scriptEngine, transformer->product().get(),
transformer->rule->module.get(), scope, true);
- transformer->setupInputs(scope);
- transformer->setupOutputs(scope);
- transformer->setupExplicitlyDependsOn(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);
- if (importScopeForSourceCode.isObject())
- scriptEngine->currentContext()->pushScope(importScopeForSourceCode);
- scriptEngine->evaluate(cmd->sourceCode());
- scriptEngine->releaseResourcesOfScriptObjects();
- if (importScopeForSourceCode.isObject())
- scriptEngine->currentContext()->popScope();
- scriptEngine->setGlobalObject(scope.prototype());
+ transformer->setupInputs(scriptEngine, scope);
+ transformer->setupOutputs(scriptEngine, scope);
+ transformer->setupExplicitlyDependsOn(scriptEngine, scope);
+
+ for (auto it = cmd->properties().constBegin(); it != cmd->properties().constEnd(); ++it)
+ setJsProperty(ctx, scope, it.key(), scriptEngine->toScriptValue(it.value()));
+
+ const TemporaryGlobalObjectSetter gos(scriptEngine, scope);
+ JSValueList scopeChain;
+ if (JS_IsObject(importScopeForSourceCode))
+ scopeChain << importScopeForSourceCode;
+ const ScopedJsValue res(ctx, scriptEngine->evaluate(JsValueOwner::Caller, cmd->sourceCode(),
+ {}, 1, scopeChain));
transformer->propertiesRequestedInCommands
+= scriptEngine->propertiesRequestedInScript();
- transformer->propertiesRequestedFromArtifactInCommands
- .unite(scriptEngine->propertiesRequestedFromArtifact());
+ unite(transformer->propertiesRequestedFromArtifactInCommands,
+ scriptEngine->propertiesRequestedFromArtifact());
const std::vector<QString> &importFilesUsedInCommand
= scriptEngine->importedFilesUsedInScript();
transformer->importedFilesUsedInCommands.insert(
@@ -167,14 +165,17 @@ private:
std::make_pair(p->uniqueName(), p->exportedModule));
}
scriptEngine->clearRequestedProperties();
- if (scriptEngine->hasUncaughtException()) {
+ if (const JsException exception = scriptEngine->checkAndClearException(cmd->codeLocation())) {
// ### We don't know the line number of the command's sourceCode property assignment.
- setError(scriptEngine->uncaughtException().toString(), cmd->codeLocation());
+ setError(exception.message(), cmd->codeLocation());
}
}
void setError(const QString &errorMessage, const CodeLocation &codeLocation)
{
+ QMutexLocker locker(&m_resultMutex);
+ if (m_cancelled)
+ return;
m_result.success = false;
m_result.errorMessage = errorMessage;
m_result.errorLocation = codeLocation;
@@ -183,13 +184,16 @@ private:
ScriptEngine *provideScriptEngine()
{
if (!m_scriptEngine)
- m_scriptEngine = ScriptEngine::create(m_logger, EvalContext::JsCommand, this);
- return m_scriptEngine;
+ m_scriptEngine = ScriptEngine::create(m_logger, EvalContext::JsCommand);
+ else
+ m_scriptEngine->reset();
+ return m_scriptEngine.get();
}
Logger m_logger;
- ScriptEngine *m_scriptEngine;
+ std::unique_ptr<ScriptEngine> m_scriptEngine;
JavaScriptCommandResult m_result;
+ QMutex m_resultMutex;
bool m_running = false;
bool m_cancelled = false;
};
@@ -201,6 +205,9 @@ JsCommandExecutor::JsCommandExecutor(const Logger &logger, QObject *parent)
, m_objectInThread(new JsCommandExecutorThreadObject(logger))
, m_running(false)
{
+ qRegisterMetaType<Transformer *>("Transformer *");
+ qRegisterMetaType<const JavaScriptCommand *>("const JavaScriptCommand *");
+
m_objectInThread->moveToThread(m_thread);
connect(m_objectInThread, &JsCommandExecutorThreadObject::finished,
this, &JsCommandExecutor::onJavaScriptCommandFinished);
@@ -254,11 +261,8 @@ bool JsCommandExecutor::doStart()
void JsCommandExecutor::cancel(const qbs::ErrorInfo &reason)
{
- if (m_running && (!dryRun() || command()->ignoreDryRun()))
- QTimer::singleShot(0, m_objectInThread, [objectInThread = QPointer<JsCommandExecutorThreadObject>{m_objectInThread}, reason] {
- if (objectInThread)
- objectInThread->cancel(reason);
- });
+ if (m_running && (!dryRun() || command()->ignoreDryRun()) && m_objectInThread)
+ m_objectInThread->cancel(reason);
}
void JsCommandExecutor::onJavaScriptCommandFinished()
diff --git a/src/lib/corelib/buildgraph/nodeset.h b/src/lib/corelib/buildgraph/nodeset.h
index 961a4bcc3..469988b19 100644
--- a/src/lib/corelib/buildgraph/nodeset.h
+++ b/src/lib/corelib/buildgraph/nodeset.h
@@ -71,11 +71,17 @@ public:
{
}
- class const_iterator : public std::iterator<std::forward_iterator_tag, T *>
+ class const_iterator
{
const NodeSet &m_nodes;
NodeSet::const_iterator m_it;
public:
+ using value_type = T*;
+ using difference_type = ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+ using iterator_category = std::forward_iterator_tag;
+
const_iterator(const NodeSet &nodes, const NodeSet::const_iterator &it)
: m_nodes(nodes), m_it(it)
{
diff --git a/src/lib/corelib/buildgraph/nodetreedumper.cpp b/src/lib/corelib/buildgraph/nodetreedumper.cpp
index 89975baf2..6ad597c70 100644
--- a/src/lib/corelib/buildgraph/nodetreedumper.cpp
+++ b/src/lib/corelib/buildgraph/nodetreedumper.cpp
@@ -51,13 +51,13 @@
namespace qbs {
namespace Internal {
-static unsigned int indentWidth() { return 4; }
+static int indentWidth() { return 4; }
NodeTreeDumper::NodeTreeDumper(QIODevice &outDevice) : m_outDevice(outDevice)
{
}
-void NodeTreeDumper::start(const QList<ResolvedProductPtr> &products)
+void NodeTreeDumper::start(const QVector<ResolvedProductPtr> &products)
{
m_indentation = 0;
for (const ResolvedProductPtr &p : products) {
@@ -120,7 +120,7 @@ bool NodeTreeDumper::doVisit(BuildGraphNode *node, const QString &nodeRepr)
QByteArray NodeTreeDumper::indentation() const
{
- return QByteArray(m_indentation, ' ');
+ return {m_indentation, ' '};
}
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/nodetreedumper.h b/src/lib/corelib/buildgraph/nodetreedumper.h
index 4175ce727..38ccd6dae 100644
--- a/src/lib/corelib/buildgraph/nodetreedumper.h
+++ b/src/lib/corelib/buildgraph/nodetreedumper.h
@@ -57,7 +57,7 @@ class NodeTreeDumper : public BuildGraphVisitor
public:
NodeTreeDumper(QIODevice &outDevice);
- void start(const QList<ResolvedProductPtr> &products);
+ void start(const QVector<ResolvedProductPtr> &products);
private:
bool visit(Artifact *artifact) override;
@@ -74,7 +74,7 @@ private:
QIODevice &m_outDevice;
ResolvedProductPtr m_currentProduct;
NodeSet m_visited;
- unsigned int m_indentation = 0;
+ int m_indentation = 0;
};
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/processcommandexecutor.cpp b/src/lib/corelib/buildgraph/processcommandexecutor.cpp
index 79edda320..52a8fe75d 100644
--- a/src/lib/corelib/buildgraph/processcommandexecutor.cpp
+++ b/src/lib/corelib/buildgraph/processcommandexecutor.cpp
@@ -61,19 +61,19 @@
#include <tools/shellutils.h>
#include <tools/stringconstants.h>
+#include <quickjs.h>
+
#include <QtCore/qdir.h>
#include <QtCore/qtemporaryfile.h>
#include <QtCore/qtimer.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
ProcessCommandExecutor::ProcessCommandExecutor(const Logger &logger, QObject *parent)
: AbstractCommandExecutor(logger, parent)
{
- connect(&m_process, static_cast<void (QbsProcess::*)(QProcess::ProcessError)>(&QbsProcess::error),
+ connect(&m_process, &QbsProcess::errorOccurred,
this, &ProcessCommandExecutor::onProcessError);
connect(&m_process, static_cast<void (QbsProcess::*)(int)>(&QbsProcess::finished),
this, &ProcessCommandExecutor::onProcessFinished);
@@ -166,6 +166,7 @@ bool ProcessCommandExecutor::doStart()
.arg(responseFile.fileName())));
return false;
}
+ const auto separator = cmd->responseFileSeparator().toUtf8();
for (int i = cmd->responseFileArgumentIndex(); i < cmd->arguments().size(); ++i) {
const QString arg = cmd->arguments().at(i);
if (arg.startsWith(cmd->responseFileUsagePrefix())) {
@@ -179,12 +180,12 @@ bool ProcessCommandExecutor::doStart()
} else {
responseFile.write(qbs::Internal::shellQuote(arg).toLocal8Bit());
}
- responseFile.write("\n");
+ responseFile.write(separator);
}
responseFile.close();
m_responseFileName = responseFile.fileName();
- arguments = arguments.mid(0,
- std::min(cmd->responseFileArgumentIndex(), arguments.size()));
+ arguments = arguments.mid(0, std::min<int>(cmd->responseFileArgumentIndex(),
+ arguments.size()));
arguments += QDir::toNativeSeparators(cmd->responseFileUsagePrefix()
+ responseFile.fileName());
}
@@ -210,38 +211,43 @@ void ProcessCommandExecutor::cancel(const qbs::ErrorInfo &reason)
QString ProcessCommandExecutor::filterProcessOutput(const QByteArray &_output,
const QString &filterFunctionSource)
{
- const QString output = QString::fromLocal8Bit(_output);
+ 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()));
+ JSContext * const ctx = scriptEngine()->context();
+ const ScopedJsValue scope(ctx, JS_NewObjectProto(ctx, scriptEngine()->globalObject()));
+ for (auto it = command()->properties().constBegin();
+ it != command()->properties().constEnd(); ++it) {
+ setJsProperty(ctx, scope, it.key(), scriptEngine()->toScriptValue(it.value()));
}
- TemporaryGlobalObjectSetter tgos(scope);
- QScriptValue filterFunction = scriptEngine()->evaluate(QLatin1String("var f = ")
- + filterFunctionSource
- + QLatin1String("; f"));
- if (!filterFunction.isFunction()) {
+ TemporaryGlobalObjectSetter tgos(scriptEngine(), scope);
+ const ScopedJsValue filterFunction(
+ ctx,
+ scriptEngine()->evaluate(JsValueOwner::Caller,
+ QLatin1String("var f = ")
+ + filterFunctionSource
+ + QLatin1String("; f")));
+ if (!JS_IsFunction(scriptEngine()->context(), filterFunction)) {
logger().printWarning(ErrorInfo(Tr::tr("Error in filter function: %1.\n%2")
- .arg(filterFunctionSource, filterFunction.toString())));
+ .arg(filterFunctionSource,
+ getJsString(scriptEngine()->context(), filterFunction))));
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)),
- scriptEngine()->lastErrorLocation(filteredOutput)));
+ const ScopedJsValue outputArg(ctx, scriptEngine()->toScriptValue(output));
+ JSValue outputArgForCall = outputArg;
+ const ScopedJsValue filteredOutput(
+ ctx, JS_Call(ctx, filterFunction, JS_UNDEFINED, 1, &outputArgForCall));
+ if (const JsException ex = scriptEngine()->checkAndClearException({})) {
+ ErrorInfo err = ex.toErrorInfo();
+ err.prepend(Tr::tr("Error when calling output filter function"));
+ logger().printWarning(err);
return output;
}
- return filteredOutput.toString();
+ return getJsString(ctx, filteredOutput);
}
static QProcess::ProcessError saveToFile(const QString &filePath, const QByteArray &content)
@@ -285,7 +291,7 @@ void ProcessCommandExecutor::getProcessOutput(bool stdOut, ProcessResult &result
} else {
if (!contentString.isEmpty() && contentString.endsWith(QLatin1Char('\n')))
contentString.chop(1);
- *target = contentString.split(QLatin1Char('\n'), QString::SkipEmptyParts);
+ *target = contentString.split(QLatin1Char('\n'), Qt::SkipEmptyParts);
}
}
@@ -392,7 +398,7 @@ void ProcessCommandExecutor::doReportCommandDescription(const QString &productNa
if (m_echoMode == CommandEchoModeCommandLineWithEnvironment) {
QStringList keys = m_commandEnvironment.keys();
keys.sort();
- for (const QString &key : keys)
+ for (const QString &key : std::as_const(keys))
fullInvocation += environmentVariableString(key, m_commandEnvironment.value(key));
}
fullInvocation += m_shellInvocation;
diff --git a/src/lib/corelib/buildgraph/productbuilddata.cpp b/src/lib/corelib/buildgraph/productbuilddata.cpp
index db51b2b9f..6ec01a1ce 100644
--- a/src/lib/corelib/buildgraph/productbuilddata.cpp
+++ b/src/lib/corelib/buildgraph/productbuilddata.cpp
@@ -102,7 +102,6 @@ void ProductBuildData::addFileTagToArtifact(Artifact *artifact, const FileTag &t
ArtifactSetByFileTag ProductBuildData::artifactsByFileTag() const
{
- std::lock_guard<std::mutex> l(m_artifactsMapMutex);
return m_artifactsByFileTag;
}
@@ -124,7 +123,6 @@ void ProductBuildData::addRescuableArtifactData(const QString &filePath,
bool ProductBuildData::checkAndSetJsArtifactsMapUpToDateFlag()
{
- std::lock_guard<std::mutex> l(m_artifactsMapMutex);
if (!m_jsArtifactsMapUpToDate) {
m_jsArtifactsMapUpToDate = true;
return false;
diff --git a/src/lib/corelib/buildgraph/productbuilddata.h b/src/lib/corelib/buildgraph/productbuilddata.h
index a7660af27..f6c531713 100644
--- a/src/lib/corelib/buildgraph/productbuilddata.h
+++ b/src/lib/corelib/buildgraph/productbuilddata.h
@@ -86,6 +86,9 @@ public:
void setBuildPriority(unsigned int prio) { m_buildPriority = prio; }
bool checkAndSetJsArtifactsMapUpToDateFlag();
+ std::unique_lock<std::mutex> getArtifactsMapLock() {
+ return std::unique_lock(m_artifactsMapMutex);
+ }
template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool)
{
diff --git a/src/lib/corelib/buildgraph/productinstaller.cpp b/src/lib/corelib/buildgraph/productinstaller.cpp
index d4acc9ace..f757c8a85 100644
--- a/src/lib/corelib/buildgraph/productinstaller.cpp
+++ b/src/lib/corelib/buildgraph/productinstaller.cpp
@@ -60,7 +60,7 @@ namespace qbs {
namespace Internal {
ProductInstaller::ProductInstaller(TopLevelProjectPtr project,
- std::vector<ResolvedProductPtr> products, InstallOptions options,
+ QVector<ResolvedProductPtr> products, InstallOptions options,
ProgressObserver *observer, Logger logger)
: m_project(std::move(project)),
m_products(std::move(products)),
@@ -96,7 +96,7 @@ void ProductInstaller::install()
removeInstallRoot();
QList<const Artifact *> artifactsToInstall;
- for (const ResolvedProductConstPtr &product : qAsConst(m_products)) {
+ for (const auto &product : std::as_const(m_products)) {
QBS_CHECK(product->buildData);
for (const Artifact *artifact : filterByType<Artifact>(product->buildData->allNodes())) {
if (artifact->properties->qbsPropertyValue(StringConstants::installProperty()).toBool())
@@ -105,7 +105,7 @@ void ProductInstaller::install()
}
m_observer->initialize(Tr::tr("Installing"), artifactsToInstall.size());
- for (const Artifact * const a : qAsConst(artifactsToInstall)) {
+ for (const Artifact * const a : std::as_const(artifactsToInstall)) {
copyFile(a);
m_observer->incrementProgressValue();
}
@@ -250,5 +250,5 @@ void ProductInstaller::handleError(const QString &message)
m_logger.qbsWarning() << message;
}
-} // namespace Intern
+} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/buildgraph/productinstaller.h b/src/lib/corelib/buildgraph/productinstaller.h
index 09828cfe9..c07e0b7cf 100644
--- a/src/lib/corelib/buildgraph/productinstaller.h
+++ b/src/lib/corelib/buildgraph/productinstaller.h
@@ -56,7 +56,7 @@ class ProductInstaller
{
public:
ProductInstaller(TopLevelProjectPtr project,
- std::vector<ResolvedProductPtr> products,
+ QVector<ResolvedProductPtr> products,
InstallOptions options, ProgressObserver *observer, Logger logger);
void install();
@@ -72,7 +72,7 @@ private:
void handleError(const QString &message);
const TopLevelProjectConstPtr m_project;
- const std::vector<ResolvedProductPtr> m_products;
+ const QVector<ResolvedProductPtr> m_products;
InstallOptions m_options;
ProgressObserver * const m_observer;
Logger m_logger;
diff --git a/src/lib/corelib/buildgraph/projectbuilddata.cpp b/src/lib/corelib/buildgraph/projectbuilddata.cpp
index 0c7f7bfdc..36ac75331 100644
--- a/src/lib/corelib/buildgraph/projectbuilddata.cpp
+++ b/src/lib/corelib/buildgraph/projectbuilddata.cpp
@@ -157,7 +157,7 @@ void ProjectBuildData::insertFileDependency(FileDependency *dependency)
static void disconnectArtifactChildren(Artifact *artifact)
{
qCDebug(lcBuildGraph) << "disconnect children of" << relativeArtifactFileName(artifact);
- for (BuildGraphNode * const child : qAsConst(artifact->children))
+ for (BuildGraphNode * const child : std::as_const(artifact->children))
child->parents.remove(artifact);
artifact->children.clear();
artifact->childrenAddedByScanner.clear();
@@ -166,7 +166,7 @@ static void disconnectArtifactChildren(Artifact *artifact)
static void disconnectArtifactParents(Artifact *artifact)
{
qCDebug(lcBuildGraph) << "disconnect parents of" << relativeArtifactFileName(artifact);
- for (BuildGraphNode * const parent : qAsConst(artifact->parents)) {
+ for (BuildGraphNode * const parent : std::as_const(artifact->parents)) {
parent->children.remove(artifact);
if (parent->type() != BuildGraphNode::ArtifactNodeType)
continue;
@@ -240,7 +240,6 @@ void ProjectBuildData::removeArtifact(Artifact *artifact,
artifact->transformer->outputs.remove(artifact);
if (removeFromProduct)
artifact->product->buildData->removeArtifact(artifact);
- m_isDirty = false;
}
void ProjectBuildData::setDirty()
@@ -258,7 +257,7 @@ void ProjectBuildData::setClean()
void ProjectBuildData::load(PersistentPool &pool)
{
serializationOp<PersistentPool::Load>(pool);
- for (FileDependency * const dep : qAsConst(fileDependencies))
+ for (FileDependency * const dep : std::as_const(fileDependencies))
insertIntoLookupTable(dep);
m_isDirty = false;
}
@@ -338,7 +337,7 @@ private:
{
if (!m_rulesOnPath.insert(rule.get()).second) {
QString pathstr;
- for (const Rule *r : qAsConst(m_rulePath)) {
+ for (const Rule *r : std::as_const(m_rulePath)) {
pathstr += QLatin1Char('\n') + r->toString() + QLatin1Char('\t')
+ r->prepareScript.location().toString();
}
@@ -395,7 +394,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
product->buildData = std::make_unique<ProductBuildData>();
ArtifactSetByFileTag artifactsPerFileTag;
- for (const auto &dependency : qAsConst(product->dependencies)) {
+ for (const auto &dependency : std::as_const(product->dependencies)) {
QBS_CHECK(dependency->enabled);
resolveProductBuildData(dependency);
}
@@ -413,7 +412,7 @@ void BuildDataResolver::resolveProductBuildData(const ResolvedProductPtr &produc
artifactsPerFileTag["qbs"].insert(qbsFileArtifact);
// read sources
- for (const SourceArtifactConstPtr &sourceArtifact : product->allEnabledFiles()) {
+ for (const auto &sourceArtifact : product->allEnabledFiles()) {
QString filePath = sourceArtifact->absoluteFilePath;
if (lookupArtifact(product, filePath))
continue; // ignore duplicate artifacts
@@ -445,7 +444,7 @@ void BuildDataResolver::connectRulesToDependencies(const ResolvedProductPtr &pro
std::vector<RuleNode *> ruleNodes;
for (RuleNode *ruleNode : filterByType<RuleNode>(product->buildData->allNodes()))
ruleNodes.push_back(ruleNode);
- for (const ResolvedProductConstPtr &dep : dependencies) {
+ for (const auto &dep : dependencies) {
if (!dep->buildData)
continue;
for (RuleNode *depRuleNode : filterByType<RuleNode>(dep->buildData->allNodes())) {
@@ -474,7 +473,7 @@ ScriptEngine *BuildDataResolver::engine() const
return evalContext()->engine();
}
-QScriptValue BuildDataResolver::scope() const
+JSValue BuildDataResolver::scope() const
{
return evalContext()->scope();
}
diff --git a/src/lib/corelib/buildgraph/projectbuilddata.h b/src/lib/corelib/buildgraph/projectbuilddata.h
index 930344435..416651912 100644
--- a/src/lib/corelib/buildgraph/projectbuilddata.h
+++ b/src/lib/corelib/buildgraph/projectbuilddata.h
@@ -47,11 +47,11 @@
#include <tools/set.h>
#include <tools/qttools.h>
+#include <quickjs.h>
+
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
-#include <QtScript/qscriptvalue.h>
-
#include <unordered_map>
namespace qbs {
@@ -126,7 +126,7 @@ private:
RulesEvaluationContextPtr evalContext() const;
ScriptEngine *engine() const;
- QScriptValue scope() const;
+ JSValue scope() const;
TopLevelProjectPtr m_project;
Logger m_logger;
diff --git a/src/lib/corelib/buildgraph/qtmocscanner.cpp b/src/lib/corelib/buildgraph/qtmocscanner.cpp
index 4e054a636..7df84e52c 100644
--- a/src/lib/corelib/buildgraph/qtmocscanner.cpp
+++ b/src/lib/corelib/buildgraph/qtmocscanner.cpp
@@ -54,9 +54,6 @@
#include <QtCore/qdebug.h>
-#include <QtScript/qscriptcontext.h>
-#include <QtScript/qscriptengine.h>
-
namespace qbs {
namespace Internal {
@@ -102,21 +99,23 @@ private:
static QString qtMocScannerJsName() { return QStringLiteral("QtMocScanner"); }
-QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue)
- : m_tags(*commonFileTags())
+QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, ScriptEngine *engine, JSValue targetScriptValue)
+ : m_engine(engine)
+ , m_tags(*commonFileTags())
, m_product(product)
- , m_targetScriptValue(targetScriptValue)
+ , m_targetScriptValue(JS_DupValue(engine->context(), targetScriptValue))
{
- const auto engine = static_cast<ScriptEngine *>(targetScriptValue.engine());
- QScriptValue scannerObj = engine->newObject();
- targetScriptValue.setProperty(qtMocScannerJsName(), scannerObj);
- QScriptValue applyFunction = engine->newFunction(&js_apply, this);
- scannerObj.setProperty(QStringLiteral("apply"), applyFunction);
+ JSValue scannerObj = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
+ attachPointerTo(scannerObj, this);
+ setJsProperty(engine->context(), targetScriptValue, qtMocScannerJsName(), scannerObj);
+ JSValue applyFunction = JS_NewCFunction(engine->context(), &js_apply, "QtMocScanner", 1);
+ setJsProperty(engine->context(), scannerObj, QStringLiteral("apply"), applyFunction);
}
QtMocScanner::~QtMocScanner()
{
- m_targetScriptValue.setProperty(qtMocScannerJsName(), QScriptValue());
+ setJsProperty(m_engine->context(), m_targetScriptValue, qtMocScannerJsName(), JS_UNDEFINED);
+ JS_FreeValue(m_engine->context(), m_targetScriptValue);
}
static RawScanResult runScanner(ScannerPlugin *scanner, const Artifact *artifact)
@@ -153,7 +152,7 @@ static RawScanResult runScanner(ScannerPlugin *scanner, const Artifact *artifact
}
QString baseDirOfInFilePath = artifact->dirPath();
- forever {
+ for (;;) {
int flags = 0;
const char *szOutFilePath = scanner->next(opaq, &length, &flags);
if (szOutFilePath == nullptr)
@@ -199,22 +198,24 @@ void QtMocScanner::findIncludedMocCppFiles()
}
}
-QScriptValue QtMocScanner::js_apply(QScriptContext *ctx, QScriptEngine *engine,
- QtMocScanner *that)
+JSValue QtMocScanner::js_apply(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
{
- QScriptValue input = ctx->argument(0);
- return that->apply(engine, attachedPointer<Artifact>(input));
+ if (argc < 1)
+ return throwError(ctx, Tr::tr("QtMocScanner.apply() requires an argument."));
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto scanner = attachedPointer<QtMocScanner>(this_val, engine->dataWithPtrClass());
+ return scanner->apply(engine, attachedPointer<Artifact>(argv[0], engine->dataWithPtrClass()));
}
-static QScriptValue scannerCountError(QScriptEngine *engine, size_t scannerCount,
- const QString &fileTag)
+static JSValue scannerCountError(ScriptEngine *engine, size_t 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));
+ return throwError(engine->context(),
+ 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)
+JSValue QtMocScanner::apply(ScriptEngine *engine, const Artifact *artifact)
{
if (!m_cppScanner) {
auto scanners = ScannerPluginManager::scannersForFileTag(m_tags.cpp);
@@ -266,11 +267,13 @@ QScriptValue QtMocScanner::apply(QScriptEngine *engine, const Artifact *artifact
<< "mustCompile:" << mustCompile
<< "hasPluginMetaDataMacro:" << hasPluginMetaDataMacro;
- QScriptValue obj = engine->newObject();
- obj.setProperty(QStringLiteral("hasQObjectMacro"), hasQObjectMacro);
- obj.setProperty(QStringLiteral("mustCompile"), mustCompile);
- obj.setProperty(QStringLiteral("hasPluginMetaDataMacro"), hasPluginMetaDataMacro);
- static_cast<ScriptEngine *>(engine)->setUsesIo();
+ JSValue obj = engine->newObject();
+ JSContext * const ctx = m_engine->context();
+ setJsProperty(ctx, obj, QStringLiteral("hasQObjectMacro"), JS_NewBool(ctx, hasQObjectMacro));
+ setJsProperty(ctx, obj, QStringLiteral("mustCompile"), JS_NewBool(ctx, mustCompile));
+ setJsProperty(ctx, obj, QStringLiteral("hasPluginMetaDataMacro"),
+ JS_NewBool(ctx, hasPluginMetaDataMacro));
+ engine->setUsesIo();
return obj;
}
diff --git a/src/lib/corelib/buildgraph/qtmocscanner.h b/src/lib/corelib/buildgraph/qtmocscanner.h
index 6455383f3..2e2a00007 100644
--- a/src/lib/corelib/buildgraph/qtmocscanner.h
+++ b/src/lib/corelib/buildgraph/qtmocscanner.h
@@ -41,12 +41,11 @@
#define QBS_QTMOCSCANNER_H
#include <language/language.h>
+#include <quickjs.h>
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
-#include <QtScript/qscriptvalue.h>
-
QT_BEGIN_NAMESPACE
class QScriptContext;
QT_END_NAMESPACE
@@ -55,6 +54,7 @@ class ScannerPlugin;
namespace qbs {
namespace Internal {
+class ScriptEngine;
class Artifact;
struct CommonFileTags;
@@ -62,17 +62,19 @@ struct CommonFileTags;
class QtMocScanner
{
public:
- explicit QtMocScanner(const ResolvedProductPtr &product, QScriptValue targetScriptValue);
+ explicit QtMocScanner(const ResolvedProductPtr &product, ScriptEngine *engine,
+ JSValue targetScriptValue);
~QtMocScanner();
private:
void findIncludedMocCppFiles();
- static QScriptValue js_apply(QScriptContext *ctx, QScriptEngine *engine, QtMocScanner *that);
- QScriptValue apply(QScriptEngine *engine, const Artifact *artifact);
+ static JSValue js_apply(JSContext *ctx, JSValue this_val, int argc, JSValue *argv);
+ JSValue apply(ScriptEngine *engine, const Artifact *artifact);
+ ScriptEngine * const m_engine;
const CommonFileTags &m_tags;
const ResolvedProductPtr &m_product;
- QScriptValue m_targetScriptValue;
+ JSValue m_targetScriptValue;
QHash<QString, QString> m_includedMocCppFiles;
ScannerPlugin *m_cppScanner = nullptr;
};
diff --git a/src/lib/corelib/buildgraph/requesteddependencies.cpp b/src/lib/corelib/buildgraph/requesteddependencies.cpp
index f993b2518..b95c8db94 100644
--- a/src/lib/corelib/buildgraph/requesteddependencies.cpp
+++ b/src/lib/corelib/buildgraph/requesteddependencies.cpp
@@ -48,9 +48,9 @@ namespace Internal {
static Set<QString> depNamesForProduct(const ResolvedProduct *p)
{
Set<QString> names;
- for (const ResolvedProductConstPtr &dep : p->dependencies)
+ for (const auto &dep : p->dependencies)
names.insert(dep->uniqueName());
- for (const ResolvedModuleConstPtr &m : p->modules) {
+ for (const auto &m : p->modules) {
if (!m->isProduct)
names.insert(m->name);
}
@@ -73,7 +73,7 @@ bool RequestedDependencies::isUpToDate(const TopLevelProject *project) const
{
if (m_depsPerProduct.empty())
return true;
- for (const ResolvedProductConstPtr &product : project->allProducts()) {
+ for (const auto &product : project->allProducts()) {
const auto it = m_depsPerProduct.find(product->uniqueName());
if (it == m_depsPerProduct.cend())
continue;
diff --git a/src/lib/corelib/buildgraph/rescuableartifactdata.h b/src/lib/corelib/buildgraph/rescuableartifactdata.h
index 3e4d6e25f..6dd10f76c 100644
--- a/src/lib/corelib/buildgraph/rescuableartifactdata.h
+++ b/src/lib/corelib/buildgraph/rescuableartifactdata.h
@@ -84,9 +84,12 @@ public:
struct ChildData
{
- ChildData(const QString &n = QString(), const QString &m = QString(),
- const QString &c = QString(), bool byScanner = false)
- : productName(n), productMultiplexId(m), childFilePath(c), addedByScanner(byScanner)
+ ChildData(QString n = QString(), QString m = QString(),
+ QString c = QString(), bool byScanner = false)
+ : productName(std::move(n))
+ , productMultiplexId(std::move(m))
+ , childFilePath(std::move(c))
+ , addedByScanner(byScanner)
{}
template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool)
diff --git a/src/lib/corelib/buildgraph/rulecommands.cpp b/src/lib/corelib/buildgraph/rulecommands.cpp
index c09c31c2c..73d05eaca 100644
--- a/src/lib/corelib/buildgraph/rulecommands.cpp
+++ b/src/lib/corelib/buildgraph/rulecommands.cpp
@@ -39,6 +39,8 @@
****************************************************************************/
#include "rulecommands.h"
+
+#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/error.h>
#include <tools/fileinfo.h>
@@ -48,9 +50,6 @@
#include <QtCore/qfile.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalueiterator.h>
-
namespace qbs {
namespace Internal {
@@ -70,6 +69,7 @@ static QString responseFileUsagePrefixProperty()
{
return QStringLiteral("responseFileUsagePrefix");
}
+static QString responseFileSeparatorProperty() { return QStringLiteral("responseFileSeparator"); }
static QString silentProperty() { return QStringLiteral("silent"); }
static QString stderrFilePathProperty() { return QStringLiteral("stderrFilePath"); }
static QString stderrFilterFunctionProperty() { return QStringLiteral("stderrFilterFunction"); }
@@ -78,10 +78,11 @@ static QString stdoutFilterFunctionProperty() { return QStringLiteral("stdoutFil
static QString timeoutProperty() { return QStringLiteral("timeout"); }
static QString workingDirProperty() { return QStringLiteral("workingDirectory"); }
-static QString invokedSourceCode(const QScriptValue codeOrFunction)
+static QString invokedSourceCode(JSContext *ctx, const JSValue &codeOrFunction)
{
- const QString &code = codeOrFunction.toString();
- return codeOrFunction.isFunction() ? QStringLiteral("(") + code + QStringLiteral(")()") : code;
+ const QString &code = getJsString(ctx, codeOrFunction);
+ return JS_IsFunction(ctx, codeOrFunction)
+ ? QStringLiteral("(") + code + QStringLiteral(")()") : code;
}
AbstractCommand::AbstractCommand()
@@ -98,28 +99,25 @@ AbstractCommand::~AbstractCommand() = default;
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_jobPool == other->m_jobPool
- && m_timeout == other->m_timeout
- && m_properties == other->m_properties;
-}
-
-void AbstractCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation)
-{
- m_description = scriptValue->property(StringConstants::descriptionProperty()).toString();
- m_extendedDescription = scriptValue->property(extendedDescriptionProperty()).toString();
- m_highlight = scriptValue->property(highlightProperty()).toString();
- m_ignoreDryRun = scriptValue->property(ignoreDryRunProperty()).toBool();
- m_silent = scriptValue->property(silentProperty()).toBool();
- m_jobPool = scriptValue->property(StringConstants::jobPoolProperty()).toString();
- const auto timeoutScriptValue = scriptValue->property(timeoutProperty());
- if (!timeoutScriptValue.isUndefined() && !timeoutScriptValue.isNull())
- m_timeout = timeoutScriptValue.toInt32();
+ 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_jobPool == other->m_jobPool
+ && m_timeout == other->m_timeout && qVariantMapsEqual(m_properties, other->m_properties);
+}
+
+void AbstractCommand::fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue,
+ const CodeLocation &codeLocation)
+{
+ m_description = getJsStringProperty(ctx, *scriptValue, StringConstants::descriptionProperty());
+ m_extendedDescription = getJsStringProperty(ctx, *scriptValue, extendedDescriptionProperty());
+ m_highlight = getJsStringProperty(ctx, *scriptValue, highlightProperty());
+ m_ignoreDryRun = getJsBoolProperty(ctx, *scriptValue, ignoreDryRunProperty());
+ m_silent = getJsBoolProperty(ctx, *scriptValue, silentProperty());
+ m_jobPool = getJsStringProperty(ctx, *scriptValue, StringConstants::jobPoolProperty());
+ const auto timeoutScriptValue = getJsProperty(ctx, *scriptValue, timeoutProperty());
+ if (!JS_IsUndefined(timeoutScriptValue) && !JS_IsNull(timeoutScriptValue))
+ m_timeout = JS_VALUE_GET_INT(timeoutScriptValue);
m_codeLocation = codeLocation;
m_predefinedProperties
@@ -147,98 +145,102 @@ void AbstractCommand::store(PersistentPool &pool)
serializationOp<PersistentPool::Store>(pool);
}
-void AbstractCommand::applyCommandProperties(const QScriptValue *scriptValue)
+void AbstractCommand::applyCommandProperties(JSContext *ctx, const JSValue *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
- || it.value().scriptClass()
- || it.value().data().isValid()) {
+ handleJsProperties(ctx, *scriptValue, [this, ctx](const JSAtom &prop, const JSPropertyDescriptor &desc) {
+ const QString name = getJsString(ctx, prop);
+ if (m_predefinedProperties.contains(name))
+ return;
+ // TODO: Use script class for command objects, don't allow setting random properties
+ if (!isSimpleValue(desc.value)) {
throw ErrorInfo(Tr::tr("Property '%1' has a type unsuitable for storing in a command "
- "object.").arg(it.name()), m_codeLocation);
+ "object.").arg(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(StringConstants::descriptionProperty(),
- engine->toScriptValue(AbstractCommand::defaultDescription()));
- cmd.setProperty(extendedDescriptionProperty(),
- engine->toScriptValue(AbstractCommand::defaultExtendedDescription()));
- cmd.setProperty(highlightProperty(),
- engine->toScriptValue(AbstractCommand::defaultHighLight()));
- cmd.setProperty(ignoreDryRunProperty(),
- engine->toScriptValue(AbstractCommand::defaultIgnoreDryRun()));
- cmd.setProperty(silentProperty(),
- engine->toScriptValue(AbstractCommand::defaultIsSilent()));
- cmd.setProperty(timeoutProperty(),
- engine->toScriptValue(AbstractCommand::defaultTimeout()));
+ m_properties.insert(name, getJsVariant(ctx, desc.value));
+ });
+}
+
+static JSValue js_CommandBase(JSContext *ctx)
+{
+ const JSValue cmd = JS_NewObject(ctx);
+ setJsProperty(ctx, cmd, StringConstants::descriptionProperty(),
+ makeJsString(ctx, AbstractCommand::defaultDescription()));
+ setJsProperty(ctx, cmd, extendedDescriptionProperty(),
+ makeJsString(ctx, AbstractCommand::defaultExtendedDescription()));
+ setJsProperty(ctx, cmd, highlightProperty(),
+ makeJsString(ctx, AbstractCommand::defaultHighLight()));
+ setJsProperty(ctx, cmd, ignoreDryRunProperty(),
+ JS_NewBool(ctx, AbstractCommand::defaultIgnoreDryRun()));
+ setJsProperty(ctx, cmd, silentProperty(),
+ JS_NewBool(ctx, AbstractCommand::defaultIsSilent()));
+ setJsProperty(ctx, cmd, timeoutProperty(),
+ JS_NewInt32(ctx, AbstractCommand::defaultTimeout()));
return cmd;
}
-static QScriptValue js_Command(QScriptContext *context, QScriptEngine *engine)
+static JSValue js_Command(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int)
{
- 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(StringConstants::classNameProperty(),
- engine->toScriptValue(StringConstants::commandType()));
- cmd.setProperty(programProperty(), program);
- cmd.setProperty(argumentsProperty(), arguments);
- cmd.setProperty(workingDirProperty(),
- engine->toScriptValue(commandPrototype->workingDir()));
- cmd.setProperty(maxExitCodeProperty(),
- engine->toScriptValue(commandPrototype->maxExitCode()));
- cmd.setProperty(stdoutFilterFunctionProperty(),
- engine->toScriptValue(commandPrototype->stdoutFilterFunction()));
- cmd.setProperty(stderrFilterFunctionProperty(),
- engine->toScriptValue(commandPrototype->stderrFilterFunction()));
- cmd.setProperty(responseFileThresholdProperty(),
- engine->toScriptValue(commandPrototype->responseFileThreshold()));
- cmd.setProperty(responseFileArgumentIndexProperty(),
- engine->toScriptValue(commandPrototype->responseFileArgumentIndex()));
- cmd.setProperty(responseFileUsagePrefixProperty(),
- engine->toScriptValue(commandPrototype->responseFileUsagePrefix()));
- cmd.setProperty(stdoutFilePathProperty(),
- engine->toScriptValue(commandPrototype->stdoutFilePath()));
- cmd.setProperty(stderrFilePathProperty(),
- engine->toScriptValue(commandPrototype->stderrFilePath()));
- cmd.setProperty(environmentProperty(),
- engine->toScriptValue(commandPrototype->environment().toStringList()));
- cmd.setProperty(ignoreDryRunProperty(),
- engine->toScriptValue(commandPrototype->ignoreDryRun()));
+ JSValue program = JS_UNDEFINED;
+ if (argc > 0)
+ program = JS_DupValue(ctx, argv[0]);
+ if (JS_IsUndefined(program))
+ program = makeJsString(ctx, commandPrototype->program());
+ JSValue arguments = JS_UNDEFINED;
+ if (argc > 1)
+ arguments = JS_DupValue(ctx, argv[1]);
+ if (JS_IsUndefined(arguments))
+ arguments = makeJsStringList(ctx, commandPrototype->arguments());
+ if (JS_IsString(arguments)) {
+ JSValue args = JS_NewArray(ctx);
+ JS_SetPropertyUint32(ctx, args, 0, arguments);
+ arguments = args;
+ }
+ JSValue cmd = js_CommandBase(ctx);
+ setJsProperty(ctx, cmd, StringConstants::classNameProperty(),
+ makeJsString(ctx, StringConstants::commandType()));
+ setJsProperty(ctx, cmd, programProperty(), program);
+ setJsProperty(ctx, cmd, argumentsProperty(), arguments);
+ setJsProperty(ctx, cmd, workingDirProperty(),
+ makeJsString(ctx, commandPrototype->workingDir()));
+ setJsProperty(ctx, cmd, maxExitCodeProperty(),
+ JS_NewInt32(ctx, commandPrototype->maxExitCode()));
+ setJsProperty(ctx, cmd, stdoutFilterFunctionProperty(),
+ makeJsString(ctx, commandPrototype->stdoutFilterFunction()));
+ setJsProperty(ctx, cmd, stderrFilterFunctionProperty(),
+ makeJsString(ctx, commandPrototype->stderrFilterFunction()));
+ setJsProperty(ctx, cmd, responseFileThresholdProperty(),
+ JS_NewInt32(ctx, commandPrototype->responseFileThreshold()));
+ setJsProperty(ctx, cmd, responseFileArgumentIndexProperty(),
+ JS_NewInt32(ctx, commandPrototype->responseFileArgumentIndex()));
+ setJsProperty(ctx, cmd, responseFileUsagePrefixProperty(),
+ makeJsString(ctx, commandPrototype->responseFileUsagePrefix()));
+ setJsProperty(ctx, cmd, responseFileSeparatorProperty(),
+ makeJsString(ctx, commandPrototype->responseFileSeparator()));
+ setJsProperty(ctx, cmd, stdoutFilePathProperty(),
+ makeJsString(ctx, commandPrototype->stdoutFilePath()));
+ setJsProperty(ctx, cmd, stderrFilePathProperty(),
+ makeJsString(ctx, commandPrototype->stderrFilePath()));
+ setJsProperty(ctx, cmd, environmentProperty(),
+ makeJsStringList(ctx, commandPrototype->environment().toStringList()));
+ setJsProperty(ctx, cmd, ignoreDryRunProperty(),
+ JS_NewBool(ctx, commandPrototype->ignoreDryRun()));
return cmd;
}
-void ProcessCommand::setupForJavaScript(QScriptValue targetObject)
+void ProcessCommand::setupForJavaScript(ScriptEngine *engine, JSValue targetObject)
{
- QBS_CHECK(targetObject.isObject());
- QScriptValue ctor = targetObject.engine()->newFunction(js_Command, 2);
- targetObject.setProperty(StringConstants::commandType(), ctor);
+ engine->registerClass(StringConstants::commandType().toUtf8().constData(), js_Command, nullptr,
+ targetObject);
}
ProcessCommand::ProcessCommand()
: m_maxExitCode(0)
, m_responseFileThreshold(defaultResponseFileThreshold())
, m_responseFileArgumentIndex(0)
+ , m_responseFileSeparator(QStringLiteral("\n"))
{
}
@@ -277,6 +279,7 @@ bool ProcessCommand::equals(const AbstractCommand *otherAbstractCommand) const
&& m_responseFileThreshold == other->m_responseFileThreshold
&& m_responseFileArgumentIndex == other->m_responseFileArgumentIndex
&& m_responseFileUsagePrefix == other->m_responseFileUsagePrefix
+ && m_responseFileSeparator == other->m_responseFileSeparator
&& m_stdoutFilePath == other->m_stdoutFilePath
&& m_stderrFilePath == other->m_stderrFilePath
&& m_relevantEnvVars == other->m_relevantEnvVars
@@ -284,38 +287,37 @@ bool ProcessCommand::equals(const AbstractCommand *otherAbstractCommand) const
&& m_environment == other->m_environment;
}
-void ProcessCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation)
-{
- AbstractCommand::fillFromScriptValue(scriptValue, codeLocation);
- m_program = scriptValue->property(programProperty()).toString();
- m_arguments = scriptValue->property(argumentsProperty()).toVariant().toStringList();
- m_workingDir = scriptValue->property(workingDirProperty()).toString();
- m_maxExitCode = scriptValue->property(maxExitCodeProperty()).toInt32();
-
- // toString() is required, presumably due to QtScript bug that manifests itself on Windows
- const QScriptValue stdoutFilterFunction
- = scriptValue->property(stdoutFilterFunctionProperty()).toString();
-
- m_stdoutFilterFunction = invokedSourceCode(stdoutFilterFunction);
-
- // toString() is required, presumably due to QtScript bug that manifests itself on Windows
- const QScriptValue stderrFilterFunction
- = scriptValue->property(stderrFilterFunctionProperty()).toString();
-
- m_stderrFilterFunction = invokedSourceCode(stderrFilterFunction);
- m_relevantEnvVars = scriptValue->property(QStringLiteral("relevantEnvironmentVariables"))
- .toVariant().toStringList();
- m_responseFileThreshold = scriptValue->property(responseFileThresholdProperty())
- .toInt32();
- m_responseFileArgumentIndex = scriptValue->property(responseFileArgumentIndexProperty())
- .toInt32();
- m_responseFileUsagePrefix = scriptValue->property(responseFileUsagePrefixProperty())
- .toString();
- QStringList envList = scriptValue->property(environmentProperty()).toVariant()
- .toStringList();
+void ProcessCommand::fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue, const CodeLocation &codeLocation)
+{
+ AbstractCommand::fillFromScriptValue(ctx, scriptValue, codeLocation);
+ m_program = getJsStringProperty(ctx, *scriptValue, programProperty());
+ m_arguments = getJsStringListProperty(ctx, *scriptValue, argumentsProperty());
+ m_workingDir = getJsStringProperty(ctx, *scriptValue, workingDirProperty());
+ m_maxExitCode = getJsIntProperty(ctx, *scriptValue, maxExitCodeProperty());
+
+ const ScopedJsValue stdoutFilterFunction(ctx, getJsProperty(ctx, *scriptValue,
+ stdoutFilterFunctionProperty()));
+ if (JS_IsFunction(ctx, stdoutFilterFunction))
+ m_stdoutFilterFunction = QLatin1Char('(') + getJsString(ctx, stdoutFilterFunction) + QLatin1Char(')');
+
+ const ScopedJsValue stderrFilterFunction(ctx, getJsProperty(ctx, *scriptValue,
+ stderrFilterFunctionProperty()));
+ if (JS_IsFunction(ctx, stderrFilterFunction))
+ m_stderrFilterFunction = QLatin1Char('(') + getJsString(ctx, stderrFilterFunction) + QLatin1Char(')');
+
+ m_relevantEnvVars = getJsStringListProperty(ctx, *scriptValue,
+ QStringLiteral("relevantEnvironmentVariables"));
+ m_responseFileThreshold = getJsIntProperty(ctx, *scriptValue, responseFileThresholdProperty());
+ m_responseFileArgumentIndex = getJsIntProperty(ctx, *scriptValue,
+ responseFileArgumentIndexProperty());
+ m_responseFileUsagePrefix = getJsStringProperty(ctx, *scriptValue,
+ responseFileUsagePrefixProperty());
+ m_responseFileSeparator = getJsStringProperty(ctx, *scriptValue,
+ responseFileSeparatorProperty());
+ QStringList envList = getJsStringListProperty(ctx, *scriptValue, environmentProperty());
getEnvironmentFromList(envList);
- m_stdoutFilePath = scriptValue->property(stdoutFilePathProperty()).toString();
- m_stderrFilePath = scriptValue->property(stderrFilePathProperty()).toString();
+ m_stdoutFilePath = getJsStringProperty(ctx, *scriptValue, stdoutFilePathProperty());
+ m_stderrFilePath = getJsStringProperty(ctx, *scriptValue, stderrFilePathProperty());
m_predefinedProperties
<< programProperty()
@@ -330,7 +332,7 @@ void ProcessCommand::fillFromScriptValue(const QScriptValue *scriptValue, const
<< environmentProperty()
<< stdoutFilePathProperty()
<< stderrFilePathProperty();
- applyCommandProperties(scriptValue);
+ applyCommandProperties(ctx, scriptValue);
}
QStringList ProcessCommand::relevantEnvVars() const
@@ -358,43 +360,42 @@ void ProcessCommand::store(PersistentPool &pool)
serializationOp<PersistentPool::Store>(pool);
}
-static QString currentImportScopeName(QScriptContext *context)
+static QString currentImportScopeName(JSContext *ctx)
{
- for (; context; context = context->parentContext()) {
- QScriptValue v = context->thisObject()
- .property(StringConstants::importScopeNamePropertyInternal());
- if (v.isString())
- return v.toString();
+ const ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const JSValueList &contextStack = engine->contextStack();
+ for (auto it = contextStack.rbegin(); it != contextStack.rend(); ++it) {
+ if (!JS_IsObject(*it))
+ continue;
+ const ScopedJsValue v(ctx, getJsProperty(ctx, *it,
+ StringConstants::importScopeNamePropertyInternal()));
+ if (JS_IsString(v))
+ return getJsString(ctx, v);
}
return {};
}
-static QScriptValue js_JavaScriptCommand(QScriptContext *context, QScriptEngine *engine)
+static JSValue js_JavaScriptCommand(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *, int)
{
- 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,
- Tr::tr("JavaScriptCommand c'tor doesn't take arguments."));
- }
+ if (argc > 0)
+ return throwError(ctx, Tr::tr("JavaScriptCommand c'tor doesn't take arguments."));
static JavaScriptCommandPtr commandPrototype = JavaScriptCommand::create();
- QScriptValue cmd = js_CommandBase(context, engine);
- cmd.setProperty(StringConstants::classNameProperty(),
- engine->toScriptValue(StringConstants::javaScriptCommandType()));
- cmd.setProperty(StringConstants::sourceCodeProperty(),
- engine->toScriptValue(commandPrototype->sourceCode()));
- cmd.setProperty(StringConstants::importScopeNamePropertyInternal(),
- engine->toScriptValue(currentImportScopeName(context)));
-
+ JSValue cmd = js_CommandBase(ctx);
+ setJsProperty(ctx, cmd, StringConstants::classNameProperty(),
+ makeJsString(ctx, StringConstants::javaScriptCommandType()));
+ setJsProperty(ctx, cmd, StringConstants::sourceCodeProperty(),
+ makeJsString(ctx, commandPrototype->sourceCode()));
+ setJsProperty(ctx, cmd, StringConstants::importScopeNamePropertyInternal(),
+ makeJsString(ctx, currentImportScopeName(ctx)));
return cmd;
}
-void JavaScriptCommand::setupForJavaScript(QScriptValue targetObject)
+void JavaScriptCommand::setupForJavaScript(ScriptEngine *engine, JSValue targetObject)
{
- QBS_CHECK(targetObject.isObject());
- QScriptValue ctor = targetObject.engine()->newFunction(js_JavaScriptCommand, 0);
- targetObject.setProperty(StringConstants::javaScriptCommandType(), ctor);
+ engine->registerClass(StringConstants::javaScriptCommandType().toUtf8().constData(),
+ js_JavaScriptCommand, nullptr, targetObject);
}
JavaScriptCommand::JavaScriptCommand() = default;
@@ -407,22 +408,24 @@ bool JavaScriptCommand::equals(const AbstractCommand *otherAbstractCommand) cons
return m_sourceCode == other->m_sourceCode;
}
-void JavaScriptCommand::fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation)
+void JavaScriptCommand::fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue,
+ const CodeLocation &codeLocation)
{
- AbstractCommand::fillFromScriptValue(scriptValue, codeLocation);
+ AbstractCommand::fillFromScriptValue(ctx, scriptValue, codeLocation);
- const QScriptValue importScope = scriptValue->property(
- StringConstants::importScopeNamePropertyInternal());
- if (importScope.isString())
- m_scopeName = importScope.toString();
+ const ScopedJsValue importScope(ctx, getJsProperty(ctx, *scriptValue,
+ StringConstants::importScopeNamePropertyInternal()));
+ if (JS_IsString(importScope))
+ m_scopeName = getJsString(ctx, importScope);
- const QScriptValue sourceCode = scriptValue->property(StringConstants::sourceCodeProperty());
- m_sourceCode = invokedSourceCode(sourceCode);
+ const ScopedJsValue sourceCode(ctx, getJsProperty(ctx, *scriptValue,
+ StringConstants::sourceCodeProperty()));
+ m_sourceCode = invokedSourceCode(ctx, sourceCode);
m_predefinedProperties << StringConstants::classNameProperty()
<< StringConstants::sourceCodeProperty()
<< StringConstants::importScopeNamePropertyInternal();
- applyCommandProperties(scriptValue);
+ applyCommandProperties(ctx, scriptValue);
}
void JavaScriptCommand::load(PersistentPool &pool)
@@ -461,7 +464,7 @@ void CommandList::load(PersistentPool &pool)
void CommandList::store(PersistentPool &pool) const
{
- pool.store(m_commands.size());
+ pool.store(int(m_commands.size()));
for (const AbstractCommandPtr &cmd : m_commands) {
pool.store(static_cast<quint8>(cmd->type()));
pool.store(cmd);
diff --git a/src/lib/corelib/buildgraph/rulecommands.h b/src/lib/corelib/buildgraph/rulecommands.h
index d4d70d591..7b08d1015 100644
--- a/src/lib/corelib/buildgraph/rulecommands.h
+++ b/src/lib/corelib/buildgraph/rulecommands.h
@@ -47,14 +47,15 @@
#include <tools/persistence.h>
#include <tools/set.h>
+#include <quickjs.h>
+
#include <QtCore/qprocess.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
+class ScriptEngine;
class AbstractCommand
{
@@ -75,7 +76,8 @@ public:
virtual CommandType type() const = 0;
virtual bool equals(const AbstractCommand *other) const;
- virtual void fillFromScriptValue(const QScriptValue *scriptValue, const CodeLocation &codeLocation);
+ virtual void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue,
+ const CodeLocation &codeLocation);
QString fullDescription(const QString &productName) const;
const QString description() const { return m_description; }
@@ -94,7 +96,7 @@ public:
protected:
AbstractCommand();
- void applyCommandProperties(const QScriptValue *scriptValue);
+ void applyCommandProperties(JSContext *ctx, const JSValue *scriptValue);
Set<QString> m_predefinedProperties;
@@ -121,11 +123,11 @@ class ProcessCommand : public AbstractCommand
{
public:
static ProcessCommandPtr create() { return ProcessCommandPtr(new ProcessCommand); }
- static void setupForJavaScript(QScriptValue targetObject);
+ static void setupForJavaScript(ScriptEngine *engine, JSValue targetObject);
CommandType type() const override { return ProcessCommandType; }
bool equals(const AbstractCommand *otherAbstractCommand) const override;
- void fillFromScriptValue(const QScriptValue *scriptValue,
+ void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue,
const CodeLocation &codeLocation) override;
const QString program() const { return m_program; }
const QStringList arguments() const { return m_arguments; }
@@ -136,6 +138,7 @@ public:
int responseFileThreshold() const { return m_responseFileThreshold; }
int responseFileArgumentIndex() const { return m_responseFileArgumentIndex; }
QString responseFileUsagePrefix() const { return m_responseFileUsagePrefix; }
+ QString responseFileSeparator() const { return m_responseFileSeparator; }
QProcessEnvironment environment() const { return m_environment; }
QStringList relevantEnvVars() const;
void clearRelevantEnvValues() { m_relevantEnvValues.clear(); }
@@ -158,10 +161,10 @@ private:
{
pool.serializationOp<opType>(m_program, m_arguments, m_environment, m_workingDir,
m_stdoutFilterFunction, m_stderrFilterFunction,
- m_responseFileUsagePrefix, m_maxExitCode,
- m_responseFileThreshold, m_responseFileArgumentIndex,
- m_relevantEnvVars, m_relevantEnvValues, m_stdoutFilePath,
- m_stderrFilePath);
+ m_responseFileUsagePrefix, m_responseFileSeparator,
+ m_maxExitCode, m_responseFileThreshold,
+ m_responseFileArgumentIndex, m_relevantEnvVars,
+ m_relevantEnvValues, m_stdoutFilePath, m_stderrFilePath);
}
QString m_program;
@@ -173,6 +176,7 @@ private:
int m_responseFileThreshold; // When to use response files? In bytes of (program name + arguments).
int m_responseFileArgumentIndex;
QString m_responseFileUsagePrefix;
+ QString m_responseFileSeparator;
QProcessEnvironment m_environment;
QStringList m_relevantEnvVars;
QProcessEnvironment m_relevantEnvValues;
@@ -184,16 +188,15 @@ class JavaScriptCommand : public AbstractCommand
{
public:
static JavaScriptCommandPtr create() { return JavaScriptCommandPtr(new JavaScriptCommand); }
- static void setupForJavaScript(QScriptValue targetObject);
+ static void setupForJavaScript(ScriptEngine *engine, JSValue targetObject);
CommandType type() const override { return JavaScriptCommandType; }
bool equals(const AbstractCommand *otherAbstractCommand) const override;
- void fillFromScriptValue(const QScriptValue *scriptValue,
+ void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue,
const CodeLocation &codeLocation) override;
const QString &scopeName() const { return m_scopeName; }
const QString &sourceCode() const { return m_sourceCode; }
- void setSourceCode(const QString &str) { m_sourceCode = str; }
void load(PersistentPool &pool) override;
void store(PersistentPool &pool) override;
diff --git a/src/lib/corelib/buildgraph/rulegraph.cpp b/src/lib/corelib/buildgraph/rulegraph.cpp
index e01a8bda8..2acc2a97e 100644
--- a/src/lib/corelib/buildgraph/rulegraph.cpp
+++ b/src/lib/corelib/buildgraph/rulegraph.cpp
@@ -61,11 +61,11 @@ void RuleGraph::build(const std::vector<RulePtr> &rules, const FileTags &product
m_parents.resize(rules.size());
m_children.resize(rules.size());
- for (const RuleConstPtr &rule : qAsConst(m_rules)) {
+ for (const auto &rule : std::as_const(m_rules)) {
FileTags inFileTags = rule->inputs;
inFileTags += rule->auxiliaryInputs;
inFileTags += rule->explicitlyDependsOn;
- for (const FileTag &fileTag : qAsConst(inFileTags)) {
+ for (const FileTag &fileTag : std::as_const(inFileTags)) {
inputFileTagToRule[fileTag].push_back(rule.get());
for (const Rule * const producingRule : m_outputFileTagToRule.value(fileTag)) {
if (!producingRule->collectedOutputFileTags().intersects(
@@ -82,38 +82,38 @@ void RuleGraph::build(const std::vector<RulePtr> &rules, const FileTags &product
productRules << rules;
//### check: the rule graph must be a in valid shape!
}
- for (const Rule *r : qAsConst(productRules))
+ for (const Rule *r : std::as_const(productRules))
m_rootRules += r->ruleGraphId;
}
void RuleGraph::accept(RuleGraphVisitor *visitor) const
{
const RuleConstPtr nullParent;
- for (int rootIndex : qAsConst(m_rootRules))
+ for (int rootIndex : std::as_const(m_rootRules))
traverse(visitor, nullParent, m_rules.at(rootIndex));
}
void RuleGraph::dump() const
{
QByteArray indent;
- printf("---rule graph dump:\n");
+ std::printf("---rule graph dump:\n");
Set<int> rootRules;
- for (const RuleConstPtr &rule : qAsConst(m_rules))
+ for (const auto &rule : std::as_const(m_rules))
if (m_parents[rule->ruleGraphId].empty())
rootRules += rule->ruleGraphId;
- for (int idx : qAsConst(rootRules))
+ for (int idx : std::as_const(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");
+ std::printf("%s", indent.constData());
+ std::printf("%s", qPrintable(r->toString()));
+ std::printf("\n");
indent.append(" ");
- for (int childIndex : qAsConst(m_children[rootIndex]))
+ for (int childIndex : std::as_const(m_children[rootIndex]))
dump_impl(indent, childIndex);
indent.chop(2);
}
diff --git a/src/lib/corelib/buildgraph/rulenode.cpp b/src/lib/corelib/buildgraph/rulenode.cpp
index 48d17934f..8568e4098 100644
--- a/src/lib/corelib/buildgraph/rulenode.cpp
+++ b/src/lib/corelib/buildgraph/rulenode.cpp
@@ -229,7 +229,7 @@ int RuleNode::transformerCount() const
ArtifactSet RuleNode::currentInputArtifacts() const
{
ArtifactSet s;
- for (const FileTag &t : qAsConst(m_rule->inputs)) {
+ for (const FileTag &t : std::as_const(m_rule->inputs)) {
for (Artifact *artifact : product->lookupArtifactsByFileTag(t)) {
if (artifact->isTargetOfModule())
continue;
@@ -246,7 +246,7 @@ ArtifactSet RuleNode::currentInputArtifacts() const
if (m_rule->inputsFromDependencies.empty())
return s;
- for (const FileTag &t : qAsConst(m_rule->inputsFromDependencies)) {
+ for (const FileTag &t : std::as_const(m_rule->inputsFromDependencies)) {
for (Artifact *artifact : product->lookupArtifactsByFileTag(t)) {
if (!artifact->isTargetOfModule())
continue;
@@ -258,7 +258,7 @@ ArtifactSet RuleNode::currentInputArtifacts() const
}
}
- for (const ResolvedProductConstPtr &dep : qAsConst(product->dependencies)) {
+ for (const auto &dep : std::as_const(product->dependencies)) {
if (!dep->buildData)
continue;
for (Artifact * const a : filterByType<Artifact>(dep->buildData->allNodes())) {
diff --git a/src/lib/corelib/buildgraph/rulesapplicator.cpp b/src/lib/corelib/buildgraph/rulesapplicator.cpp
index 437e3f4da..e07abd74a 100644
--- a/src/lib/corelib/buildgraph/rulesapplicator.cpp
+++ b/src/lib/corelib/buildgraph/rulesapplicator.cpp
@@ -1,5 +1,3 @@
-#include <utility>
-
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
@@ -41,7 +39,6 @@
#include "rulesapplicator.h"
#include "buildgraph.h"
-#include "productbuilddata.h"
#include "projectbuilddata.h"
#include "qtmocscanner.h"
#include "rulecommands.h"
@@ -57,7 +54,6 @@
#include <language/preparescriptobserver.h>
#include <language/propertymapinternal.h>
#include <language/resolvedfilecontext.h>
-#include <language/scriptengine.h>
#include <logging/categories.h>
#include <logging/translator.h>
#include <tools/error.h>
@@ -69,7 +65,6 @@
#include <QtCore/qcryptographichash.h>
#include <QtCore/qdir.h>
-#include <QtScript/qscriptvalueiterator.h>
#include <memory>
#include <vector>
@@ -79,12 +74,13 @@ namespace Internal {
RulesApplicator::RulesApplicator(
ResolvedProductPtr product,
- std::unordered_map<QString, const ResolvedProduct *> productsByName,
- std::unordered_map<QString, const ResolvedProject *> projectsByName,
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName,
Logger logger)
: m_product(std::move(product))
- , m_productsByName(std::move(productsByName))
- , m_projectsByName(std::move(projectsByName))
+ // m_productsByName and m_projectsByName are references, cannot move-construct
+ , m_productsByName(productsByName)
+ , m_projectsByName(projectsByName)
, m_mocScanner(nullptr)
, m_logger(std::move(logger))
{
@@ -112,10 +108,10 @@ void RulesApplicator::applyRule(RuleNode *ruleNode, const ArtifactSet &inputArti
m_completeInputSet = inputArtifacts;
if (m_rule->name.startsWith(QLatin1String("QtCoreMocRule"))) {
delete m_mocScanner;
- m_mocScanner = new QtMocScanner(m_product, scope());
+ m_mocScanner = new QtMocScanner(m_product, engine(), scope());
}
- QScriptValue prepareScriptContext = engine()->newObject();
- prepareScriptContext.setPrototype(engine()->globalObject());
+ ScopedJsValue prepareScriptContext(jsContext(), engine()->newObject());
+ JS_SetPrototype(jsContext(), prepareScriptContext, engine()->globalObject());
setupScriptEngineForFile(engine(), m_rule->prepareScript.fileContext(), scope(),
ObserveMode::Enabled);
setupScriptEngineForProduct(engine(), m_product.get(), m_rule->module.get(),
@@ -133,6 +129,7 @@ void RulesApplicator::applyRule(RuleNode *ruleNode, const ArtifactSet &inputArti
}
if (engine()->usesIo())
m_ruleUsesIo = true;
+ engine()->releaseInputArtifactScriptValues(ruleNode);
}
void RulesApplicator::handleRemovedRuleOutputs(const ArtifactSet &inputArtifacts,
@@ -149,7 +146,7 @@ void RulesApplicator::handleRemovedRuleOutputs(const ArtifactSet &inputArtifacts
project->buildData->removeArtifactAndExclusiveDependents(removedArtifact, logger, true,
&artifactsToRemove);
}
- for (Artifact * const artifact : qAsConst(artifactsToRemove)) {
+ for (Artifact * const artifact : std::as_const(artifactsToRemove)) {
QBS_CHECK(!inputArtifacts.contains(artifact));
removedArtifacts << artifact->filePath();
delete artifact;
@@ -163,9 +160,9 @@ ArtifactSet RulesApplicator::collectAuxiliaryInputs(const Rule *rule,
CurrentProduct | Dependencies);
}
-static void copyProperty(const QString &name, const QScriptValue &src, QScriptValue dst)
+static void copyProperty(JSContext *ctx, const QString &name, const JSValue &src, JSValue dst)
{
- dst.setProperty(name, src.property(name));
+ setJsProperty(ctx, dst, name, getJsProperty(ctx, src, name));
}
static QStringList toStringList(const ArtifactSet &artifacts)
@@ -179,7 +176,7 @@ static QStringList toStringList(const ArtifactSet &artifacts)
return lst;
}
-void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext)
+void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, JSValue prepareScriptContext)
{
evalContext()->checkForCancelation();
for (const Artifact *inputArtifact : inputArtifacts)
@@ -201,20 +198,22 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p
engine()->clearRequestedProperties();
// create the output artifacts from the set of input artifacts
- m_transformer->setupInputs(prepareScriptContext);
- m_transformer->setupExplicitlyDependsOn(prepareScriptContext);
- copyProperty(StringConstants::inputsVar(), prepareScriptContext, scope());
- copyProperty(StringConstants::inputVar(), prepareScriptContext, scope());
- copyProperty(StringConstants::explicitlyDependsOnVar(), prepareScriptContext, scope());
- copyProperty(StringConstants::productVar(), prepareScriptContext, scope());
- copyProperty(StringConstants::projectVar(), prepareScriptContext, scope());
+ m_transformer->setupInputs(engine(), prepareScriptContext);
+ m_transformer->setupExplicitlyDependsOn(engine(), prepareScriptContext);
+ copyProperty(jsContext(), StringConstants::inputsVar(), prepareScriptContext, scope());
+ copyProperty(jsContext(), StringConstants::inputVar(), prepareScriptContext, scope());
+ copyProperty(jsContext(), StringConstants::explicitlyDependsOnVar(),
+ prepareScriptContext, scope());
+ copyProperty(jsContext(), StringConstants::productVar(), prepareScriptContext, scope());
+ copyProperty(jsContext(), StringConstants::projectVar(), prepareScriptContext, scope());
if (m_rule->isDynamic()) {
- outputArtifacts = runOutputArtifactsScript(inputArtifacts,
- ScriptEngine::argumentList(Rule::argumentNamesForOutputArtifacts(), scope()));
+ const ScopedJsValueList argList
+ = engine()->argumentList(Rule::argumentNamesForOutputArtifacts(), scope());
+ outputArtifacts = runOutputArtifactsScript(inputArtifacts, argList);
} else {
Set<QString> outputFilePaths;
- for (const RuleArtifactConstPtr &ruleArtifact : m_rule->artifacts) {
- const OutputArtifactInfo outputInfo = createOutputArtifactFromRuleArtifact(
+ for (const auto &ruleArtifact : m_rule->artifacts) {
+ const OutputArtifactInfo &outputInfo = createOutputArtifactFromRuleArtifact(
ruleArtifact, inputArtifacts, &outputFilePaths);
if (!outputInfo.artifact)
continue;
@@ -227,7 +226,7 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p
}
}
- ArtifactSet newOutputs = ArtifactSet::fromList(outputArtifacts);
+ const auto newOutputs = rangeTo<ArtifactSet>(outputArtifacts);
const ArtifactSet oldOutputs = collectOldOutputArtifacts(inputArtifacts);
handleRemovedRuleOutputs(m_completeInputSet, oldOutputs - newOutputs, m_removedArtifacts,
m_logger);
@@ -248,59 +247,64 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p
if (outputArtifacts.empty())
return;
- for (Artifact * const outputArtifact : qAsConst(outputArtifacts)) {
- for (Artifact * const dependency : qAsConst(m_transformer->explicitlyDependsOn))
+ for (Artifact * const outputArtifact : std::as_const(outputArtifacts)) {
+ for (Artifact * const dependency : std::as_const(m_transformer->explicitlyDependsOn))
connect(outputArtifact, dependency);
}
if (inputArtifacts != m_transformer->inputs)
- m_transformer->setupInputs(prepareScriptContext);
+ m_transformer->setupInputs(engine(), prepareScriptContext);
// change the transformer outputs according to the bindings in Artifact
- QScriptValue scriptValue;
- if (!ruleArtifactArtifactMap.empty())
- engine()->setGlobalObject(prepareScriptContext);
- for (auto it = ruleArtifactArtifactMap.crbegin(), end = ruleArtifactArtifactMap.crend();
- it != end; ++it) {
- const RuleArtifact *ra = it->first;
- if (ra->bindings.empty())
- continue;
-
- // expose attributes of this artifact
- const OutputArtifactInfo outputInfo = it->second;
- Artifact *outputArtifact = outputInfo.artifact;
- outputArtifact->properties = outputArtifact->properties->clone();
+ if (!ruleArtifactArtifactMap.empty()) {
+ const TemporaryGlobalObjectSetter gos(engine(), prepareScriptContext);
+ for (auto it = ruleArtifactArtifactMap.crbegin(), end = ruleArtifactArtifactMap.crend();
+ it != end; ++it) {
+ const RuleArtifact *ra = it->first;
+ if (ra->bindings.empty())
+ continue;
- scope().setProperty(StringConstants::fileNameProperty(),
- engine()->toScriptValue(outputArtifact->filePath()));
- scope().setProperty(StringConstants::fileTagsProperty(),
- toScriptValue(engine(), outputArtifact->fileTags().toStringList()));
-
- QVariantMap artifactModulesCfg = outputArtifact->properties->value();
- for (const auto &binding : ra->bindings) {
- scriptValue = engine()->evaluate(binding.code);
- if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue))) {
- QString msg = QStringLiteral("evaluating rule binding '%1': %2");
- throw ErrorInfo(msg.arg(binding.name.join(QLatin1Char('.')),
- engine()->lastErrorString(scriptValue)),
- engine()->lastErrorLocation(scriptValue, binding.location));
+ // expose attributes of this artifact
+ const OutputArtifactInfo &outputInfo = it->second;
+ Artifact *outputArtifact = outputInfo.artifact;
+ outputArtifact->properties = outputArtifact->properties->clone();
+
+ setJsProperty(jsContext(), scope(), StringConstants::fileNameProperty(),
+ engine()->toScriptValue(outputArtifact->filePath()));
+ setJsProperty(jsContext(), scope(), StringConstants::fileTagsProperty(),
+ makeJsStringList(engine()->context(),
+ outputArtifact->fileTags().toStringList()));
+
+ QVariantMap artifactModulesCfg = outputArtifact->properties->value();
+ for (const auto &binding : ra->bindings) {
+ const ScopedJsValue scriptValue(jsContext(), engine()->evaluate(
+ JsValueOwner::Caller, binding.code,
+ binding.location.filePath(),
+ binding.location.line()));
+ if (JsException ex = engine()->checkAndClearException(binding.location)) {
+ ErrorInfo err = ex.toErrorInfo();
+ err.prepend(QStringLiteral("evaluating rule binding '%1'")
+ .arg(binding.name.join(QLatin1Char('.'))));
+ throw err;
+ }
+ const QVariant value = getJsVariant(jsContext(), scriptValue);
+ setConfigProperty(artifactModulesCfg, binding.name, value);
+ outputArtifact->pureProperties.emplace_back(binding.name, value);
+ }
+ outputArtifact->properties->setValue(artifactModulesCfg);
+ if (!outputInfo.newlyCreated
+ && (outputArtifact->fileTags() != outputInfo.oldFileTags
+ || !qVariantMapsEqual(
+ outputArtifact->properties->value(), outputInfo.oldProperties))) {
+ invalidateArtifactAsRuleInputIfNecessary(outputArtifact);
}
- const QVariant value = scriptValue.toVariant();
- setConfigProperty(artifactModulesCfg, binding.name, value);
- outputArtifact->pureProperties.emplace_back(binding.name, value);
- }
- outputArtifact->properties->setValue(artifactModulesCfg);
- if (!outputInfo.newlyCreated && (outputArtifact->fileTags() != outputInfo.oldFileTags
- || outputArtifact->properties->value() != outputInfo.oldProperties)) {
- invalidateArtifactAsRuleInputIfNecessary(outputArtifact);
}
}
- if (!ruleArtifactArtifactMap.empty())
- engine()->setGlobalObject(prepareScriptContext.prototype());
- m_transformer->setupOutputs(prepareScriptContext);
- m_transformer->createCommands(engine(), m_rule->prepareScript,
- ScriptEngine::argumentList(Rule::argumentNamesForPrepare(), prepareScriptContext));
+ m_transformer->setupOutputs(engine(), prepareScriptContext);
+ const ScopedJsValueList argList = engine()->argumentList(Rule::argumentNamesForPrepare(),
+ prepareScriptContext);
+ m_transformer->createCommands(engine(), m_rule->prepareScript, argList);
if (Q_UNLIKELY(m_transformer->commands.empty()))
throw ErrorInfo(Tr::tr("There is a rule without commands: %1.")
.arg(m_rule->toString()), m_rule->prepareScript.location());
@@ -310,7 +314,7 @@ void RulesApplicator::doApply(const ArtifactSet &inputArtifacts, QScriptValue &p
|| m_oldTransformer->commands != m_transformer->commands
|| commandsNeedRerun(m_transformer.get(), m_product.get(), m_productsByName,
m_projectsByName)) {
- for (Artifact * const output : qAsConst(outputArtifacts)) {
+ for (Artifact * const output : std::as_const(outputArtifacts)) {
output->clearTimestamp();
m_invalidatedArtifacts += output;
}
@@ -354,7 +358,7 @@ ArtifactSet RulesApplicator::collectAdditionalInputs(const FileTags &tags, const
}
if (inputsSources.testFlag(Dependencies)) {
- for (const ResolvedProductConstPtr &depProduct : product->dependencies) {
+ for (const auto &depProduct : product->dependencies) {
for (Artifact * const ta : depProduct->targetArtifacts()) {
if (ta->fileTags().contains(fileTag)
&& !ta->fileTags().intersects(rule->excludedInputs)) {
@@ -385,12 +389,14 @@ RulesApplicator::OutputArtifactInfo RulesApplicator::createOutputArtifactFromRul
FileTags fileTags;
bool alwaysUpdated;
if (ruleArtifact) {
- QScriptValue scriptValue = engine()->evaluate(ruleArtifact->filePath,
- ruleArtifact->filePathLocation.filePath(),
- ruleArtifact->filePathLocation.line());
- if (Q_UNLIKELY(engine()->hasErrorOrException(scriptValue)))
- throw engine()->lastError(scriptValue, ruleArtifact->filePathLocation);
- outputPath = scriptValue.toString();
+ const ScopedJsValue scriptValue(
+ jsContext(),
+ engine()->evaluate(JsValueOwner::Caller, ruleArtifact->filePath,
+ ruleArtifact->filePathLocation.filePath(),
+ ruleArtifact->filePathLocation.line()));
+ if (JsException ex = engine()->checkAndClearException(ruleArtifact->filePathLocation))
+ throw ex.toErrorInfo();
+ outputPath = getJsString(jsContext(), scriptValue);
fileTags = ruleArtifact->fileTags;
alwaysUpdated = ruleArtifact->alwaysUpdated;
} else {
@@ -513,26 +519,31 @@ public:
};
QList<Artifact *> RulesApplicator::runOutputArtifactsScript(const ArtifactSet &inputArtifacts,
- const QScriptValueList &args)
+ const JSValueList &args)
{
QList<Artifact *> lst;
- QScriptValue fun = engine()->evaluate(m_rule->outputArtifactsScript.sourceCode(),
- m_rule->outputArtifactsScript.location().filePath(),
- m_rule->outputArtifactsScript.location().line());
- if (!fun.isFunction())
+ const ScopedJsValue fun(jsContext(),
+ engine()->evaluate(JsValueOwner::Caller,
+ m_rule->outputArtifactsScript.sourceCode(),
+ m_rule->outputArtifactsScript.location().filePath(),
+ m_rule->outputArtifactsScript.location().line()));
+ if (!JS_IsFunction(jsContext(), fun))
throw ErrorInfo(QStringLiteral("Function expected."),
m_rule->outputArtifactsScript.location());
- QScriptValue res = fun.call(QScriptValue(), args);
- engine()->releaseResourcesOfScriptObjects();
- if (engine()->hasErrorOrException(res))
- throw engine()->lastError(res, m_rule->outputArtifactsScript.location());
- if (!res.isArray())
+ JSValueList argv(args.begin(), args.end());
+ const ScopedJsValue res(
+ jsContext(),
+ JS_Call(jsContext(), fun, engine()->globalObject(), int(args.size()), argv.data()));
+ if (JsException ex = engine()->checkAndClearException(m_rule->outputArtifactsScript.location()))
+ throw ex.toErrorInfo();
+ if (!JS_IsArray(jsContext(), res))
throw ErrorInfo(Tr::tr("Rule.outputArtifacts must return an array of objects."),
m_rule->outputArtifactsScript.location());
- const quint32 c = res.property(StringConstants::lengthProperty()).toUInt32();
+ const quint32 c = getJsIntProperty(jsContext(), res, StringConstants::lengthProperty());
for (quint32 i = 0; i < c; ++i) {
try {
- lst.push_back(createOutputArtifactFromScriptValue(res.property(i), inputArtifacts));
+ ScopedJsValue val(engine()->context(), JS_GetPropertyUint32(jsContext(), res, i));
+ lst.push_back(createOutputArtifactFromScriptValue(val, inputArtifacts));
} catch (const RuleOutputArtifactsException &roae) {
ErrorInfo ei = roae;
ei.prepend(Tr::tr("Error in Rule.outputArtifacts[%1]").arg(i),
@@ -555,6 +566,8 @@ class ArtifactBindingsExtractor
QString name;
QVariant value;
};
+ ScriptEngine *m_engine = nullptr;
+ JSContext *m_ctx = nullptr;
std::vector<Entry> m_propertyValues;
static Set<QString> getArtifactItemPropertyNames()
@@ -569,35 +582,36 @@ class ArtifactBindingsExtractor
return s;
}
- void extractPropertyValues(const QScriptValue &obj, const QString &moduleName = QString())
+ void extractPropertyValues(const JSValue &obj, const QString &moduleName = QString())
{
- QScriptValueIterator svit(obj);
- while (svit.hasNext()) {
- svit.next();
- const QString name = svit.name();
+ handleJsProperties(m_ctx, obj, [&](const JSAtom &prop, const JSPropertyDescriptor &desc) {
+ const QString name = getJsString(m_ctx, prop);
if (moduleName.isEmpty()) {
// Ignore property names that are part of the Artifact item.
static const Set<QString> artifactItemPropertyNames
= getArtifactItemPropertyNames();
if (artifactItemPropertyNames.contains(name))
- continue;
+ return;
}
- const QScriptValue value = svit.value();
- if (value.isObject() && !value.isArray() && !value.isError() && !value.isRegExp()) {
+ const JSValue value = desc.value;
+ if (JS_IsObject(value) && !JS_IsArray(m_ctx, value) && !JS_IsError(m_ctx, value)
+ && !JS_IsRegExp(m_ctx, value)) {
QString newModuleName;
if (!moduleName.isEmpty())
newModuleName.append(moduleName + QLatin1Char('.'));
newModuleName.append(name);
extractPropertyValues(value, newModuleName);
} else {
- m_propertyValues.emplace_back(moduleName, name, value.toVariant());
+ m_propertyValues.emplace_back(moduleName, name, getJsVariant(m_ctx, value));
}
- }
+ });
}
public:
- void apply(Artifact *outputArtifact, const QScriptValue &obj)
+ void apply(ScriptEngine *engine, Artifact *outputArtifact, const JSValue &obj)
{
+ m_engine = engine;
+ m_ctx = m_engine->context();
extractPropertyValues(obj);
if (m_propertyValues.empty())
return;
@@ -613,24 +627,27 @@ public:
}
};
-Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValue &obj,
+Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const JSValue &obj,
const ArtifactSet &inputArtifacts)
{
- if (!obj.isObject()) {
+ if (!JS_IsObject(obj)) {
throw ErrorInfo(Tr::tr("Elements of the Rule.outputArtifacts array must be "
"of Object type."), m_rule->outputArtifactsScript.location());
}
- const QString unresolvedFilePath
- = obj.property(StringConstants::filePathProperty()).toVariant().toString();
+ QString unresolvedFilePath;
+ const ScopedJsValue jsFilePath(jsContext(), getJsProperty(jsContext(), obj,
+ StringConstants::filePathProperty()));
+ if (JS_IsString(jsFilePath))
+ unresolvedFilePath = getJsString(jsContext(), jsFilePath);
if (unresolvedFilePath.isEmpty()) {
throw RuleOutputArtifactsException(
Tr::tr("Property filePath must be a non-empty string."));
}
const QString filePath = FileInfo::resolvePath(m_product->buildDirectory(), unresolvedFilePath);
const FileTags fileTags = FileTags::fromStringList(
- obj.property(StringConstants::fileTagsProperty()).toVariant().toStringList());
- const QVariant alwaysUpdatedVar
- = obj.property(StringConstants::alwaysUpdatedProperty()).toVariant();
+ getJsStringListProperty(jsContext(), obj, StringConstants::fileTagsProperty()));
+ const QVariant alwaysUpdatedVar = getJsVariantProperty(jsContext(), obj,
+ StringConstants::alwaysUpdatedProperty());
const bool alwaysUpdated = alwaysUpdatedVar.isValid() ? alwaysUpdatedVar.toBool() : true;
OutputArtifactInfo outputInfo = createOutputArtifact(filePath, fileTags, alwaysUpdated,
inputArtifacts);
@@ -641,16 +658,17 @@ Artifact *RulesApplicator::createOutputArtifactFromScriptValue(const QScriptValu
"Alternatively, a FileTagger can be provided.")
.arg(unresolvedFilePath));
}
- const FileTags explicitlyDependsOn = FileTags::fromStringList(
- obj.property(StringConstants::explicitlyDependsOnProperty())
- .toVariant().toStringList());
+ const FileTags explicitlyDependsOn = FileTags::fromStringList(getJsStringListProperty(
+ jsContext(), obj, StringConstants::explicitlyDependsOnProperty()));
for (const FileTag &tag : explicitlyDependsOn) {
for (Artifact * const dependency : m_product->lookupArtifactsByFileTag(tag))
connect(outputInfo.artifact, dependency);
}
- ArtifactBindingsExtractor().apply(outputInfo.artifact, obj);
- if (!outputInfo.newlyCreated && (outputInfo.artifact->fileTags() != outputInfo.oldFileTags
- || outputInfo.artifact->properties->value() != outputInfo.oldProperties)) {
+ ArtifactBindingsExtractor().apply(engine(), outputInfo.artifact, obj);
+ if (!outputInfo.newlyCreated
+ && (outputInfo.artifact->fileTags() != outputInfo.oldFileTags
+ || !qVariantMapsEqual(
+ outputInfo.artifact->properties->value(), outputInfo.oldProperties))) {
invalidateArtifactAsRuleInputIfNecessary(outputInfo.artifact);
}
return outputInfo.artifact;
@@ -669,15 +687,9 @@ 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();
-}
+ScriptEngine *RulesApplicator::engine() const { return evalContext()->engine(); }
+JSContext *RulesApplicator::jsContext() const { return engine()->context(); }
+JSValue 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
index 1160f3d09..72f9aadcc 100644
--- a/src/lib/corelib/buildgraph/rulesapplicator.h
+++ b/src/lib/corelib/buildgraph/rulesapplicator.h
@@ -44,12 +44,13 @@
#include "nodeset.h"
#include <language/filetags.h>
#include <language/forward_decls.h>
+#include <language/scriptengine.h>
#include <logging/logger.h>
+#include <quickjs.h>
#include <QtCore/qflags.h>
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
-#include <QtScript/qscriptvalue.h>
#include <unordered_map>
@@ -63,8 +64,8 @@ class RulesApplicator
{
public:
RulesApplicator(ResolvedProductPtr product,
- std::unordered_map<QString, const ResolvedProduct *> productsByName,
- std::unordered_map<QString, const ResolvedProject *> projectsByName,
+ const std::unordered_map<QString, const ResolvedProduct *> &productsByName,
+ const std::unordered_map<QString, const ResolvedProject *> &projectsByName,
Logger logger);
~RulesApplicator();
@@ -85,7 +86,7 @@ public:
Q_DECLARE_FLAGS(InputsSources, InputsSourceFlag)
private:
- void doApply(const ArtifactSet &inputArtifacts, QScriptValue &prepareScriptContext);
+ void doApply(const ArtifactSet &inputArtifacts, JSValue prepareScriptContext);
ArtifactSet collectOldOutputArtifacts(const ArtifactSet &inputArtifacts) const;
struct OutputArtifactInfo {
@@ -100,13 +101,14 @@ private:
OutputArtifactInfo createOutputArtifact(const QString &filePath, const FileTags &fileTags,
bool alwaysUpdated, const ArtifactSet &inputArtifacts);
QList<Artifact *> runOutputArtifactsScript(const ArtifactSet &inputArtifacts,
- const QScriptValueList &args);
- Artifact *createOutputArtifactFromScriptValue(const QScriptValue &obj,
+ const JSValueList &args);
+ Artifact *createOutputArtifactFromScriptValue(const JSValue &obj,
const ArtifactSet &inputArtifacts);
QString resolveOutPath(const QString &path) const;
const RulesEvaluationContextPtr &evalContext() const;
ScriptEngine *engine() const;
- QScriptValue scope() const;
+ JSContext *jsContext() const;
+ JSValue scope() const;
static ArtifactSet collectAdditionalInputs(const FileTags &tags,
const Rule *rule, const ResolvedProduct *product,
diff --git a/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp
index 6ae230329..2d0f5d44d 100644
--- a/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp
+++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.cpp
@@ -38,9 +38,7 @@
****************************************************************************/
#include "rulesevaluationcontext.h"
-#include "artifact.h"
#include "rulecommands.h"
-#include "transformer.h"
#include <language/language.h>
#include <language/scriptengine.h>
#include <logging/translator.h>
@@ -57,17 +55,18 @@ RulesEvaluationContext::RulesEvaluationContext(Logger logger)
: m_logger(std::move(logger)),
m_engine(ScriptEngine::create(m_logger, EvalContext::RuleExecution)),
m_observer(nullptr),
- m_initScopeCalls(0)
+ m_initScopeCalls(0),
+ m_prepareScriptScope(m_engine->newObject())
{
- m_prepareScriptScope = m_engine->newObject();
- m_prepareScriptScope.setPrototype(m_engine->globalObject());
- ProcessCommand::setupForJavaScript(m_prepareScriptScope);
- JavaScriptCommand::setupForJavaScript(m_prepareScriptScope);
+
+ JS_SetPrototype(m_engine->context(), m_prepareScriptScope, m_engine->globalObject());
+ ProcessCommand::setupForJavaScript(m_engine.get(), m_prepareScriptScope);
+ JavaScriptCommand::setupForJavaScript(m_engine.get(), m_prepareScriptScope);
}
RulesEvaluationContext::~RulesEvaluationContext()
{
- delete m_engine;
+ JS_FreeValue(m_engine->context(), m_prepareScriptScope);
}
void RulesEvaluationContext::initializeObserver(const QString &description, int maximumProgress)
@@ -95,7 +94,7 @@ void RulesEvaluationContext::initScope()
m_engine->setActive(true);
m_scope = m_engine->newObject();
- m_scope.setPrototype(m_prepareScriptScope);
+ JS_SetPrototype(m_engine->context(), m_scope, m_prepareScriptScope);
m_engine->setGlobalObject(m_scope);
}
@@ -105,8 +104,11 @@ void RulesEvaluationContext::cleanupScope()
if (--m_initScopeCalls > 0)
return;
- m_scope = QScriptValue();
- m_engine->setGlobalObject(m_prepareScriptScope.prototype());
+ JS_FreeValue(m_engine->context(), m_scope);
+ m_scope = JS_UNDEFINED;
+ const ScopedJsValue proto(engine()->context(),
+ JS_GetPrototype(m_engine->context(), m_prepareScriptScope));
+ m_engine->setGlobalObject(proto);
m_engine->setActive(false);
}
diff --git a/src/lib/corelib/buildgraph/rulesevaluationcontext.h b/src/lib/corelib/buildgraph/rulesevaluationcontext.h
index a5d81ce61..7ff75b51f 100644
--- a/src/lib/corelib/buildgraph/rulesevaluationcontext.h
+++ b/src/lib/corelib/buildgraph/rulesevaluationcontext.h
@@ -42,12 +42,11 @@
#include <language/forward_decls.h>
#include <logging/logger.h>
+#include <quickjs.h>
+
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
-#include <QtScript/qscriptprogram.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
class ProgressObserver;
@@ -69,8 +68,8 @@ public:
RulesEvaluationContext * const m_evalContext;
};
- ScriptEngine *engine() const { return m_engine; }
- QScriptValue scope() const { return m_scope; }
+ ScriptEngine *engine() const { return m_engine.get(); }
+ JSValue scope() const { return m_scope; }
void setObserver(ProgressObserver *observer) { m_observer = observer; }
ProgressObserver *observer() const { return m_observer; }
@@ -79,17 +78,15 @@ public:
void checkForCancelation();
private:
- friend class Scope;
-
void initScope();
void cleanupScope();
Logger m_logger;
- ScriptEngine * const m_engine;
+ const std::unique_ptr<ScriptEngine> m_engine;
ProgressObserver *m_observer;
unsigned int m_initScopeCalls;
- QScriptValue m_scope;
- QScriptValue m_prepareScriptScope;
+ JSValue m_scope = JS_UNDEFINED;
+ const JSValue m_prepareScriptScope;
};
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/scriptclasspropertyiterator.h b/src/lib/corelib/buildgraph/scriptclasspropertyiterator.h
deleted file mode 100644
index f6154f993..000000000
--- a/src/lib/corelib/buildgraph/scriptclasspropertyiterator.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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_SCRIPTCLASSPROPERTYITERATOR_H
-#define QBS_SCRIPTCLASSPROPERTYITERATOR_H
-
-#include <tools/qbsassert.h>
-
-#include <QtCore/qmap.h>
-#include <QtCore/qstring.h>
-#include <QtScript/qscriptclasspropertyiterator.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptstring.h>
-
-#include <vector>
-
-namespace qbs {
-namespace Internal {
-
-class ScriptClassPropertyIterator : public QScriptClassPropertyIterator
-{
-public:
- ScriptClassPropertyIterator(const QScriptValue &object, const QVariantMap &properties,
- const std::vector<QString> &additionalProperties)
- : QScriptClassPropertyIterator(object),
- m_it(properties),
- m_additionalProperties(additionalProperties)
- {
- }
-
-private:
- bool hasNext() const override
- {
- return m_it.hasNext() || m_index < int(m_additionalProperties.size()) - 1;
- }
- bool hasPrevious() const override { return m_index > -1 || m_it.hasPrevious(); }
- void toFront() override { m_it.toFront(); m_index = -1; }
- void toBack() override { m_it.toBack(); m_index = int(m_additionalProperties.size()) - 1; }
-
- void next() override
- {
- QBS_ASSERT(hasNext(), return);
- if (m_it.hasNext())
- m_it.next();
- else
- ++m_index;
- }
-
- void previous() override
- {
- QBS_ASSERT(hasPrevious(), return);
- if (m_index >= 0)
- --m_index;
- if (m_index == -1)
- m_it.previous();
- }
-
- QScriptString name() const override
- {
- const QString theName = m_index >= 0 && m_index < int(m_additionalProperties.size())
- ? m_additionalProperties.at(m_index)
- : m_it.key();
- return object().engine()->toStringHandle(theName);
- }
-
- QMapIterator<QString, QVariant> m_it;
- const std::vector<QString> m_additionalProperties;
- int m_index = -1;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_SCRIPTCLASSPROPERTYITERATOR_H
diff --git a/src/lib/corelib/buildgraph/timestampsupdater.cpp b/src/lib/corelib/buildgraph/timestampsupdater.cpp
index d31f57445..3f5279dd2 100644
--- a/src/lib/corelib/buildgraph/timestampsupdater.cpp
+++ b/src/lib/corelib/buildgraph/timestampsupdater.cpp
@@ -83,7 +83,7 @@ private:
};
void TimestampsUpdater::updateTimestamps(const TopLevelProjectPtr &project,
- const QList<ResolvedProductPtr> &products, const Logger &logger)
+ const QVector<ResolvedProductPtr> &products, const Logger &logger)
{
TimestampsUpdateVisitor v;
for (const ResolvedProductPtr &product : products)
diff --git a/src/lib/corelib/buildgraph/timestampsupdater.h b/src/lib/corelib/buildgraph/timestampsupdater.h
index cfe20df12..8184ca708 100644
--- a/src/lib/corelib/buildgraph/timestampsupdater.h
+++ b/src/lib/corelib/buildgraph/timestampsupdater.h
@@ -51,7 +51,7 @@ class TimestampsUpdater
{
public:
void updateTimestamps(const TopLevelProjectPtr &project,
- const QList<ResolvedProductPtr> &products, const Logger &logger);
+ const QVector<ResolvedProductPtr> &products, const Logger &logger);
};
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/transformer.cpp b/src/lib/corelib/buildgraph/transformer.cpp
index 5e27c3e74..2346ad5c9 100644
--- a/src/lib/corelib/buildgraph/transformer.cpp
+++ b/src/lib/corelib/buildgraph/transformer.cpp
@@ -39,6 +39,7 @@
#include "transformer.h"
#include "artifact.h"
+#include "productbuilddata.h"
#include <jsextensions/moduleproperties.h>
#include <language/language.h>
#include <language/preparescriptobserver.h>
@@ -54,6 +55,7 @@
#include <QtCore/qdir.h>
#include <algorithm>
+#include <vector>
namespace qbs {
namespace Internal {
@@ -64,78 +66,82 @@ Transformer::Transformer() : alwaysRun(false)
Transformer::~Transformer() = default;
-static QScriptValue js_baseName(QScriptContext *ctx, QScriptEngine *engine,
- const Artifact *artifact)
+static JSValue js_baseName(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- Q_UNUSED(ctx);
- Q_UNUSED(engine);
- return {FileInfo::baseName(artifact->filePath())};
+ return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val,
+ [ctx](const Artifact *a) {
+ return makeJsString(ctx, FileInfo::baseName(a->filePath()));
+ });
}
-static QScriptValue js_completeBaseName(QScriptContext *ctx, QScriptEngine *engine,
- const Artifact *artifact)
+static JSValue js_completeBaseName(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- Q_UNUSED(ctx);
- Q_UNUSED(engine);
- return {FileInfo::completeBaseName(artifact->filePath())};
+ return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val,
+ [ctx](const Artifact *a) {
+ return makeJsString(ctx, FileInfo::completeBaseName(a->filePath()));
+ });
}
-static QScriptValue js_baseDir(QScriptContext *ctx, QScriptEngine *engine,
- const Artifact *artifact)
+static JSValue js_baseDir(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- Q_UNUSED(ctx);
- Q_UNUSED(engine);
- 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;
+ return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val,
+ [ctx](const Artifact *artifact) {
+ 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 makeJsString(ctx, basedir);
+ });
}
-static QScriptValue js_children(QScriptContext *ctx, QScriptEngine *engine, const Artifact *artifact)
+static JSValue js_children(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- Q_UNUSED(ctx);
- QScriptValue sv = engine->newArray();
- uint idx = 0;
- for (const Artifact *child : artifact->childArtifacts()) {
- sv.setProperty(idx++, Transformer::translateFileConfig(static_cast<ScriptEngine *>(engine),
- child, QString()));
- }
- return sv;
+ return ScriptEngine::engineForContext(ctx)->getArtifactProperty(this_val,
+ [ctx](const Artifact *artifact) {
+ JSValue sv = JS_NewArray(ctx);
+ uint idx = 0;
+
+ // FIXME: childArtifacts() is not guarded by any mutex ...
+ for (Artifact *child : artifact->childArtifacts()) {
+ JS_SetPropertyUint32(ctx, sv, idx++, Transformer::translateFileConfig(
+ ScriptEngine::engineForContext(ctx), child, QString()));
+ }
+ return sv;
+ });
}
-static void setArtifactProperty(QScriptValue &obj, const QString &name,
- QScriptValue (*func)(QScriptContext *, QScriptEngine *, const Artifact *),
- const Artifact *artifact)
+static void setArtifactProperty(JSContext *ctx, JSValue &obj, const QString &name,
+ JSCFunction *func)
{
- obj.setProperty(name, static_cast<ScriptEngine *>(obj.engine())->newFunction(func, artifact),
- QScriptValue::PropertyGetter);
+ const QByteArray nameBa = name.toUtf8();
+ const JSValue jsFunc = JS_NewCFunction(ctx, func, nameBa.constData(), 0);
+ const ScopedJsAtom nameAtom(ctx, nameBa);
+ JS_DefinePropertyGetSet(ctx, obj, nameAtom, jsFunc, JS_UNDEFINED, JS_PROP_HAS_GET);
}
-QScriptValue Transformer::translateFileConfig(ScriptEngine *scriptEngine, const Artifact *artifact,
- const QString &defaultModuleName)
+JSValue Transformer::translateFileConfig(ScriptEngine *engine, Artifact *artifact,
+ const QString &defaultModuleName)
{
- QScriptValue obj = scriptEngine->newObject();
- attachPointerTo(obj, artifact);
- ModuleProperties::init(obj, artifact);
- obj.setProperty(StringConstants::fileNameProperty(), artifact->fileName());
- obj.setProperty(StringConstants::filePathProperty(), artifact->filePath());
- setArtifactProperty(obj, StringConstants::baseNameProperty(), js_baseName, artifact);
- setArtifactProperty(obj, StringConstants::completeBaseNameProperty(), js_completeBaseName,
- artifact);
- setArtifactProperty(obj, QStringLiteral("baseDir"), js_baseDir, artifact);
- setArtifactProperty(obj, QStringLiteral("children"), js_children, artifact);
- const QStringList fileTags = sorted(artifact->fileTags().toStringList());
- scriptEngine->setObservedProperty(obj, StringConstants::fileTagsProperty(),
- scriptEngine->toScriptValue(fileTags));
- scriptEngine->observer()->addArtifactId(obj.objectId());
- if (!defaultModuleName.isEmpty())
- obj.setProperty(StringConstants::moduleNameProperty(), defaultModuleName);
- return obj;
+ return engine->getArtifactScriptValue(artifact, defaultModuleName, [&](JSValue obj) {
+ ModuleProperties::init(engine, obj, artifact);
+ JSContext * const ctx = engine->context();
+ setJsProperty(ctx, obj, StringConstants::fileNameProperty(), artifact->fileName());
+ setJsProperty(ctx, obj, StringConstants::filePathProperty(), artifact->filePath());
+ setArtifactProperty(ctx, obj, StringConstants::baseNameProperty(), js_baseName);
+ setArtifactProperty(ctx, obj, StringConstants::completeBaseNameProperty(), js_completeBaseName);
+ setArtifactProperty(ctx, obj, QStringLiteral("baseDir"), js_baseDir);
+ setArtifactProperty(ctx, obj, QStringLiteral("children"), js_children);
+ const QStringList fileTags = sorted(artifact->fileTags().toStringList());
+ const ScopedJsValue jsFileTags(ctx, engine->toScriptValue(fileTags));
+ engine->setObservedProperty(obj, StringConstants::fileTagsProperty(), jsFileTags);
+ engine->observer()->addArtifactId(jsObjectId(obj));
+ if (!defaultModuleName.isEmpty())
+ setJsProperty(ctx, obj, StringConstants::moduleNameProperty(), defaultModuleName);
+ });
}
static bool compareByFilePath(const Artifact *a1, const Artifact *a2)
@@ -143,9 +149,8 @@ static bool compareByFilePath(const Artifact *a1, const Artifact *a2)
return a1->filePath() < a2->filePath();
}
-QScriptValue Transformer::translateInOutputs(ScriptEngine *scriptEngine,
- const ArtifactSet &artifacts,
- const QString &defaultModuleName)
+JSValue Transformer::translateInOutputs(ScriptEngine *engine, const ArtifactSet &artifacts,
+ const QString &defaultModuleName)
{
using TagArtifactsMap = QMap<QString, QList<Artifact*>>;
TagArtifactsMap tagArtifactsMap;
@@ -155,16 +160,16 @@ QScriptValue Transformer::translateInOutputs(ScriptEngine *scriptEngine,
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) {
+ JSValue jsTagFiles = engine->newObject();
+ for (auto tag = tagArtifactsMap.constBegin(); tag != tagArtifactsMap.constEnd(); ++tag) {
const QList<Artifact*> &artifacts = tag.value();
- QScriptValue jsFileConfig = scriptEngine->newArray(artifacts.size());
+ JSValue jsFileConfig = JS_NewArray(engine->context());
int i = 0;
for (Artifact * const artifact : artifacts) {
- jsFileConfig.setProperty(i++, translateFileConfig(scriptEngine, artifact,
- defaultModuleName));
+ JS_SetPropertyUint32(engine->context(), jsFileConfig, i++,
+ translateFileConfig(engine, artifact, defaultModuleName));
}
- jsTagFiles.setProperty(tag.key(), jsFileConfig);
+ setJsProperty(engine->context(), jsTagFiles, tag.key(), jsFileConfig);
}
return jsTagFiles;
@@ -177,66 +182,70 @@ ResolvedProductPtr Transformer::product() const
return (*outputs.cbegin())->product.lock();
}
-void Transformer::setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs,
- const QString &defaultModuleName)
+void Transformer::setupInputs(ScriptEngine *engine, JSValue targetScriptValue,
+ const ArtifactSet &inputs, const QString &defaultModuleName)
{
- const auto scriptEngine = static_cast<ScriptEngine *>(targetScriptValue.engine());
- QScriptValue scriptValue = translateInOutputs(scriptEngine, inputs, defaultModuleName);
- targetScriptValue.setProperty(StringConstants::inputsVar(), scriptValue);
- QScriptValue inputScriptValue;
+ JSValue scriptValue = translateInOutputs(engine, inputs, defaultModuleName);
+ setJsProperty(engine->context(), targetScriptValue, StringConstants::inputsVar(), scriptValue);
+ JSValue inputScriptValue = JS_UNDEFINED;
if (inputs.size() == 1) {
Artifact *input = *inputs.cbegin();
const FileTags &fileTags = input->fileTags();
QBS_ASSERT(!fileTags.empty(), return);
- QScriptValue inputsForFileTag = scriptValue.property(fileTags.cbegin()->toString());
- inputScriptValue = inputsForFileTag.property(0);
+ const ScopedJsValue inputsForFileTag(
+ engine->context(),
+ getJsProperty(engine->context(), scriptValue, fileTags.cbegin()->toString()));
+ inputScriptValue = JS_GetPropertyUint32(engine->context(), inputsForFileTag, 0);
}
- targetScriptValue.setProperty(StringConstants::inputVar(), inputScriptValue);
+ setJsProperty(engine->context(), targetScriptValue, StringConstants::inputVar(),
+ inputScriptValue);
}
-void Transformer::setupInputs(QScriptValue targetScriptValue)
+void Transformer::setupInputs(ScriptEngine *engine, const JSValue &targetScriptValue)
{
- setupInputs(targetScriptValue, inputs, rule->module->name);
+ setupInputs(engine, targetScriptValue, inputs, rule->module->name);
}
-void Transformer::setupOutputs(QScriptValue targetScriptValue)
+void Transformer::setupOutputs(ScriptEngine *engine, JSValue targetScriptValue)
{
- const auto scriptEngine = static_cast<ScriptEngine *>(targetScriptValue.engine());
const QString &defaultModuleName = rule->module->name;
- QScriptValue scriptValue = translateInOutputs(scriptEngine, outputs, defaultModuleName);
- targetScriptValue.setProperty(StringConstants::outputsVar(), scriptValue);
- QScriptValue outputScriptValue;
+ JSValue scriptValue = translateInOutputs(engine, outputs, defaultModuleName);
+ setJsProperty(engine->context(), targetScriptValue, StringConstants::outputsVar(), scriptValue);
+ JSValue outputScriptValue = JS_UNDEFINED;
if (outputs.size() == 1) {
Artifact *output = *outputs.cbegin();
const FileTags &fileTags = output->fileTags();
QBS_ASSERT(!fileTags.empty(), return);
- QScriptValue outputsForFileTag = scriptValue.property(fileTags.cbegin()->toString());
- outputScriptValue = outputsForFileTag.property(0);
+ const ScopedJsValue outputsForFileTag(
+ engine->context(),
+ getJsProperty(engine->context(), scriptValue, fileTags.cbegin()->toString()));
+ outputScriptValue = JS_GetPropertyUint32(engine->context(), outputsForFileTag, 0);
}
- targetScriptValue.setProperty(StringConstants::outputVar(), outputScriptValue);
+ setJsProperty(engine->context(), targetScriptValue, StringConstants::outputVar(),
+ outputScriptValue);
}
-void Transformer::setupExplicitlyDependsOn(QScriptValue targetScriptValue)
+void Transformer::setupExplicitlyDependsOn(ScriptEngine *engine, JSValue targetScriptValue)
{
- const auto scriptEngine = static_cast<ScriptEngine *>(targetScriptValue.engine());
- QScriptValue scriptValue = translateInOutputs(scriptEngine, explicitlyDependsOn,
- rule->module->name);
- targetScriptValue.setProperty(StringConstants::explicitlyDependsOnVar(), scriptValue);
+ JSValue scriptValue = translateInOutputs(engine, explicitlyDependsOn, rule->module->name);
+ setJsProperty(engine->context(), targetScriptValue, StringConstants::explicitlyDependsOnVar(),
+ scriptValue);
}
-AbstractCommandPtr Transformer::createCommandFromScriptValue(const QScriptValue &scriptValue,
- const CodeLocation &codeLocation)
+AbstractCommandPtr Transformer::createCommandFromScriptValue(
+ ScriptEngine *engine, const JSValue &scriptValue, const CodeLocation &codeLocation)
{
AbstractCommandPtr cmdBase;
- if (scriptValue.isUndefined() || !scriptValue.isValid())
+ if (JS_IsUndefined(scriptValue) || JS_IsUninitialized(scriptValue))
return cmdBase;
- QString className = scriptValue.property(StringConstants::classNameProperty()).toString();
+ QString className = getJsStringProperty(engine->context(), scriptValue,
+ StringConstants::classNameProperty());
if (className == StringConstants::commandType())
cmdBase = ProcessCommand::create();
else if (className == StringConstants::javaScriptCommandType())
cmdBase = JavaScriptCommand::create();
if (cmdBase)
- cmdBase->fillFromScriptValue(&scriptValue, codeLocation);
+ cmdBase->fillFromScriptValue(engine->context(), &scriptValue, codeLocation);
if (className == StringConstants::commandType()) {
auto procCmd = static_cast<ProcessCommand *>(cmdBase.get());
procCmd->clearRelevantEnvValues();
@@ -248,18 +257,14 @@ AbstractCommandPtr Transformer::createCommandFromScriptValue(const QScriptValue
}
void Transformer::createCommands(ScriptEngine *engine, const PrivateScriptFunction &script,
- const QScriptValueList &args)
+ const JSValueList &args)
{
- 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);
- engine->releaseResourcesOfScriptObjects();
+ JSValueList argv(args.cbegin(), args.cend());
+ const JSValue function = script.getFunction(engine, Tr::tr("Invalid prepare script."));
+ const ScopedJsValue scriptValue(
+ engine->context(),
+ JS_Call(engine->context(), function, engine->globalObject(),
+ int(argv.size()), argv.data()));
propertiesRequestedInPrepareScript = engine->propertiesRequestedInScript();
propertiesRequestedFromArtifactInPrepareScript = engine->propertiesRequestedFromArtifact();
importedFilesUsedInPrepareScript = engine->importedFilesUsedInScript();
@@ -271,22 +276,24 @@ void Transformer::createCommands(ScriptEngine *engine, const PrivateScriptFuncti
p->exportedModule));
}
engine->clearRequestedProperties();
- if (Q_UNLIKELY(engine->hasErrorOrException(scriptValue)))
- throw engine->lastError(scriptValue, script.location());
+ if (JsException ex = engine->checkAndClearException(script.location()))
+ throw ex.toErrorInfo();
commands.clear();
- if (scriptValue.isArray()) {
- const int count = scriptValue.property(StringConstants::lengthProperty()).toInt32();
+ if (JS_IsArray(engine->context(), scriptValue)) {
+ const int count = JS_VALUE_GET_INT(getJsProperty(engine->context(), scriptValue,
+ StringConstants::lengthProperty()));
for (qint32 i = 0; i < count; ++i) {
- QScriptValue item = scriptValue.property(i);
- if (item.isValid() && !item.isUndefined()) {
- const AbstractCommandPtr cmd
- = createCommandFromScriptValue(item, script.location());
+ ScopedJsValue item(engine->context(),
+ JS_GetPropertyUint32(engine->context(), scriptValue, i));
+ if (!JS_IsUninitialized(item) && !JS_IsUndefined(item)) {
+ const AbstractCommandPtr cmd = createCommandFromScriptValue(engine, item,
+ script.location());
if (cmd)
commands.addCommand(cmd);
}
}
} else {
- const AbstractCommandPtr cmd = createCommandFromScriptValue(scriptValue,
+ const AbstractCommandPtr cmd = createCommandFromScriptValue(engine, scriptValue,
script.location());
if (cmd)
commands.addCommand(cmd);
diff --git a/src/lib/corelib/buildgraph/transformer.h b/src/lib/corelib/buildgraph/transformer.h
index 2f6a8e56d..927572310 100644
--- a/src/lib/corelib/buildgraph/transformer.h
+++ b/src/lib/corelib/buildgraph/transformer.h
@@ -52,6 +52,8 @@
#include <tools/filetime.h>
#include <tools/persistence.h>
+#include <quickjs.h>
+
#include <QtCore/qhash.h>
namespace qbs {
@@ -91,15 +93,14 @@ public:
bool commandsNeedChangeTracking = false;
bool markedForRerun = false;
- static QScriptValue translateFileConfig(ScriptEngine *scriptEngine,
- const Artifact *artifact,
- const QString &defaultModuleName);
+ static JSValue translateFileConfig(ScriptEngine *engine, Artifact *artifact,
+ const QString &defaultModuleName);
ResolvedProductPtr product() const;
- void setupInputs(QScriptValue targetScriptValue);
- void setupOutputs(QScriptValue targetScriptValue);
- void setupExplicitlyDependsOn(QScriptValue targetScriptValue);
+ void setupInputs(ScriptEngine *engine, const JSValue &targetScriptValue);
+ void setupOutputs(ScriptEngine *engine, JSValue targetScriptValue);
+ void setupExplicitlyDependsOn(ScriptEngine *engine, JSValue targetScriptValue);
void createCommands(ScriptEngine *engine, const PrivateScriptFunction &script,
- const QScriptValueList &args);
+ const JSValueList &args);
void rescueChangeTrackingData(const TransformerConstPtr &other);
Set<QString> jobPools() const;
@@ -124,14 +125,13 @@ public:
private:
Transformer();
- AbstractCommandPtr createCommandFromScriptValue(const QScriptValue &scriptValue,
+ AbstractCommandPtr createCommandFromScriptValue(ScriptEngine *engine, const JSValue &scriptValue,
const CodeLocation &codeLocation);
- static void setupInputs(QScriptValue targetScriptValue, const ArtifactSet &inputs,
- const QString &defaultModuleName);
- static QScriptValue translateInOutputs(ScriptEngine *scriptEngine,
- const ArtifactSet &artifacts,
- const QString &defaultModuleName);
+ static void setupInputs(ScriptEngine *engine, JSValue targetScriptValue,
+ const ArtifactSet &inputs, const QString &defaultModuleName);
+ static JSValue translateInOutputs(ScriptEngine *engine, const ArtifactSet &artifacts,
+ const QString &defaultModuleName);
};
} // namespace Internal
diff --git a/src/lib/corelib/buildgraph/transformerchangetracking.cpp b/src/lib/corelib/buildgraph/transformerchangetracking.cpp
index 505f0cbba..ae43e8219 100644
--- a/src/lib/corelib/buildgraph/transformerchangetracking.cpp
+++ b/src/lib/corelib/buildgraph/transformerchangetracking.cpp
@@ -39,6 +39,7 @@
#include <tools/fileinfo.h>
#include <tools/qbsassert.h>
#include <tools/qttools.h>
+#include <tools/stlutils.h>
#include <QtCore/qvariant.h>
@@ -71,7 +72,7 @@ private:
const char *context) const;
bool isExportedModuleUpToDate(const QString &productName, const ExportedModule &module) const;
bool areExportedModulesUpToDate(
- const std::unordered_map<QString, ExportedModule> exportedModules) const;
+ const std::unordered_map<QString, ExportedModule> &exportedModules) const;
const Artifact *getArtifact(const QString &filePath, const QString &productName) const;
const ResolvedProduct *getProduct(const QString &name) const;
@@ -157,7 +158,7 @@ bool TrafoChangeTracker::checkForPropertyChange(const Property &restoredProperty
case Property::PropertyInArtifact:
QBS_CHECK(false);
}
- if (restoredProperty.value != v) {
+ if (!qVariantsEqual(restoredProperty.value, v)) {
qCDebug(lcBuildGraph).noquote().nospace()
<< "Value for property '" << restoredProperty.moduleName << "."
<< restoredProperty.propertyName << "' has changed.\n"
@@ -207,13 +208,11 @@ bool TrafoChangeTracker::isExportedModuleUpToDate(const QString &productName,
}
bool TrafoChangeTracker::areExportedModulesUpToDate(
- const std::unordered_map<QString, ExportedModule> exportedModules) const
+ const std::unordered_map<QString, ExportedModule> &exportedModules) const
{
- for (const auto &kv : exportedModules) {
- if (!isExportedModuleUpToDate(kv.first, kv.second))
- return false;
- }
- return true;
+ return Internal::all_of(exportedModules, [this](const auto &kv) {
+ return isExportedModuleUpToDate(kv.first, kv.second);
+ });
}
const Artifact *TrafoChangeTracker::getArtifact(const QString &filePath,
@@ -258,7 +257,7 @@ const ResolvedProduct *TrafoChangeTracker::getProduct(const QString &name) const
bool TrafoChangeTracker::prepareScriptNeedsRerun() const
{
- for (const Property &property : qAsConst(m_transformer->propertiesRequestedInPrepareScript)) {
+ for (const Property &property : std::as_const(m_transformer->propertiesRequestedInPrepareScript)) {
if (checkForPropertyChange(property, propertyMapByKind(property)))
return true;
}
@@ -270,7 +269,7 @@ bool TrafoChangeTracker::prepareScriptNeedsRerun() const
for (auto it = m_transformer->propertiesRequestedFromArtifactInPrepareScript.constBegin();
it != m_transformer->propertiesRequestedFromArtifactInPrepareScript.constEnd(); ++it) {
- for (const Property &property : qAsConst(it.value())) {
+ for (const Property &property : std::as_const(it.value())) {
const Artifact * const artifact = getArtifact(it.key(), property.productName);
if (!artifact)
return true;
@@ -298,15 +297,14 @@ bool TrafoChangeTracker::prepareScriptNeedsRerun() const
bool TrafoChangeTracker::commandsNeedRerun() const
{
- for (const Property &property : qAsConst(m_transformer->propertiesRequestedInCommands)) {
+ for (const Property &property : std::as_const(m_transformer->propertiesRequestedInCommands)) {
if (checkForPropertyChange(property, propertyMapByKind(property)))
return true;
}
- QMap<QString, SourceArtifactConstPtr> artifactMap;
for (auto it = m_transformer->propertiesRequestedFromArtifactInCommands.cbegin();
it != m_transformer->propertiesRequestedFromArtifactInCommands.cend(); ++it) {
- for (const Property &property : qAsConst(it.value())) {
+ for (const Property &property : std::as_const(it.value())) {
const Artifact * const artifact = getArtifact(it.key(), property.productName);
if (!artifact)
return true;
@@ -333,7 +331,7 @@ bool TrafoChangeTracker::commandsNeedRerun() const
return true;
// TODO: Also track env access in JS commands and prepare scripts
- for (const AbstractCommandPtr &c : qAsConst(m_transformer->commands.commands())) {
+ for (const AbstractCommandPtr &c : std::as_const(m_transformer->commands.commands())) {
if (c->type() != AbstractCommand::ProcessCommandType)
continue;
const ProcessCommandPtr &processCmd = std::static_pointer_cast<ProcessCommand>(c);
diff --git a/src/lib/corelib/corelib.pro b/src/lib/corelib/corelib.pro
deleted file mode 100644
index 002e36683..000000000
--- a/src/lib/corelib/corelib.pro
+++ /dev/null
@@ -1,42 +0,0 @@
-TARGET = qbscore
-include(../library.pri)
-include(../bundledlibs.pri)
-
-qbs_use_bundled_qtscript {
- include(../scriptengine/use_scriptengine.pri)
-} else {
- QT += script
-}
-
-isEmpty(QBS_RELATIVE_LIBEXEC_PATH) {
- win32:QBS_RELATIVE_LIBEXEC_PATH=../bin
- else:QBS_RELATIVE_LIBEXEC_PATH=../libexec/qbs
-}
-DEFINES += QBS_RELATIVE_LIBEXEC_PATH=\\\"$${QBS_RELATIVE_LIBEXEC_PATH}\\\"
-
-QT += core-private network
-qbs_enable_project_file_updates: QT += gui
-
-INCLUDEPATH += $$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
index bd44ac3ed..1a7890166 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -1,37 +1,32 @@
-import qbs 1.0
import qbs.Utilities
QbsLibrary {
Depends { name: "cpp" }
Depends { name: "Qt"; submodules: ["core-private", "network", "xml"] }
Depends {
- name: "Qt.script"
- condition: !qbsbuildconfig.useBundledQtScript
- required: false
+ name: "Qt.core5compat";
+ condition: Utilities.versionCompare(Qt.core.version, "6.0.0") >= 0
}
- Depends {
- name: "qbsscriptengine"
- condition: qbsbuildconfig.useBundledQtScript || !Qt.script.present
- }
- Depends { condition: qbsbuildconfig.enableProjectFileUpdates; name: "Qt.gui" }
+ Depends { name: "quickjs" }
+ Depends { name: "qbspkgconfig" }
name: "qbscore"
- property stringList bundledQtScriptIncludes: qbsbuildconfig.useBundledQtScript
- || !Qt.script.present ? qbsscriptengine.includePaths : []
- cpp.includePaths: base.concat(bundledQtScriptIncludes).concat([
+ cpp.includePaths: base.concat([
".",
"../.." // for the plugin headers
])
- property stringList projectFileUpdateDefines:
- qbsbuildconfig.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : []
- property stringList enableUnitTestsDefines:
- qbsbuildconfig.enableUnitTests ? ["QBS_ENABLE_UNIT_TESTS"] : []
- property stringList systemSettingsDirDefines: qbsbuildconfig.systemSettingsDir
- ? ['QBS_SYSTEM_SETTINGS_DIR="' + qbsbuildconfig.systemSettingsDir + '"'] : []
- cpp.defines: base.concat([
- "QBS_RELATIVE_LIBEXEC_PATH=" + Utilities.cStringQuote(qbsbuildconfig.relativeLibexecPath),
- "QBS_VERSION=" + Utilities.cStringQuote(version),
- ]).concat(projectFileUpdateDefines).concat(enableUnitTestsDefines)
- .concat(systemSettingsDirDefines)
+ cpp.defines: {
+ var defines = base.concat([
+ "QBS_RELATIVE_LIBEXEC_PATH=" + Utilities.cStringQuote(qbsbuildconfig.relativeLibexecPath),
+ "QBS_VERSION=" + Utilities.cStringQuote(version),
+ ]);
+ if (project.withTests)
+ defines.push("QBS_WITH_TESTS");
+ if (qbsbuildconfig.enableUnitTests)
+ defines.push("QBS_ENABLE_UNIT_TESTS");
+ if (qbsbuildconfig.systemSettingsDir)
+ defines.push('QBS_SYSTEM_SETTINGS_DIR="' + qbsbuildconfig.systemSettingsDir + '"');
+ return defines;
+ }
Properties {
condition: qbs.targetOS.contains("windows")
@@ -52,7 +47,6 @@ QbsLibrary {
}
Group {
name: "project file updating"
- condition: qbsbuildconfig.enableProjectFileUpdates
prefix: "api/"
files: [
"changeset.cpp",
@@ -173,7 +167,6 @@ QbsLibrary {
"rulesapplicator.h",
"rulesevaluationcontext.cpp",
"rulesevaluationcontext.h",
- "scriptclasspropertyiterator.h",
"timestampsupdater.cpp",
"timestampsupdater.h",
"transformer.cpp",
@@ -233,10 +226,14 @@ QbsLibrary {
"environmentextension.cpp",
"file.cpp",
"fileinfoextension.cpp",
+ "host.cpp",
+ "jsextension.h",
"jsextensions.cpp",
"jsextensions.h",
"moduleproperties.cpp",
"moduleproperties.h",
+ "pkgconfigjs.cpp",
+ "pkgconfigjs.h",
"process.cpp",
"temporarydir.cpp",
"textfile.cpp",
@@ -258,7 +255,7 @@ QbsLibrary {
prefix: "jsextensions/"
condition: qbs.targetOS.contains("darwin")
files: [
- "propertylist.mm",
+ "propertylist_darwin.mm",
"propertylistutils.h",
"propertylistutils.mm",
]
@@ -269,20 +266,13 @@ QbsLibrary {
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",
@@ -298,27 +288,13 @@ QbsLibrary {
"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",
"moduleproviderinfo.h",
"preparescriptobserver.cpp",
"preparescriptobserver.h",
- "projectresolver.cpp",
- "projectresolver.h",
"property.cpp",
"property.h",
"propertydeclaration.cpp",
@@ -346,6 +322,50 @@ QbsLibrary {
files: "language/forward_decls.h"
}
Group {
+ name: "loader"
+ prefix: name + '/'
+ files: [
+ "astimportshandler.cpp",
+ "astimportshandler.h",
+ "astpropertiesitemhandler.cpp",
+ "astpropertiesitemhandler.h",
+ "dependenciesresolver.cpp",
+ "dependenciesresolver.h",
+ "groupshandler.cpp",
+ "groupshandler.h",
+ "itemreader.cpp",
+ "itemreader.h",
+ "itemreaderastvisitor.cpp",
+ "itemreaderastvisitor.h",
+ "itemreadervisitorstate.cpp",
+ "itemreadervisitorstate.h",
+ "loaderutils.cpp",
+ "loaderutils.h",
+ "localprofiles.cpp",
+ "localprofiles.h",
+ "moduleinstantiator.cpp",
+ "moduleinstantiator.h",
+ "moduleloader.cpp",
+ "moduleloader.h",
+ "modulepropertymerger.cpp",
+ "modulepropertymerger.h",
+ "moduleproviderloader.cpp",
+ "moduleproviderloader.h",
+ "probesresolver.cpp",
+ "probesresolver.h",
+ "productitemmultiplexer.cpp",
+ "productitemmultiplexer.h",
+ "productresolver.cpp",
+ "productresolver.h",
+ "productscollector.cpp",
+ "productscollector.h",
+ "productsresolver.cpp",
+ "productsresolver.h",
+ "projectresolver.cpp",
+ "projectresolver.h",
+ ]
+ }
+ Group {
name: "logging"
prefix: name + '/'
files: [
@@ -369,6 +389,7 @@ QbsLibrary {
files: [
"qmlerror.cpp",
"qmlerror.h",
+ "qmljs.g",
"qmljsast.cpp",
"qmljsast_p.h",
"qmljsastfwd_p.h",
@@ -395,9 +416,12 @@ QbsLibrary {
"buildgraphlocker.cpp",
"buildgraphlocker.h",
"buildoptions.cpp",
+ "clangclinfo.cpp",
+ "clangclinfo.h",
"cleanoptions.cpp",
"codelocation.cpp",
"commandechomode.cpp",
+ "deprecationwarningmode.cpp",
"dynamictypecheck.h",
"error.cpp",
"executablefinder.cpp",
@@ -427,8 +451,10 @@ QbsLibrary {
"msvcinfo.cpp",
"msvcinfo.h",
"pathutils.h",
+ "pimpl.h",
"persistence.cpp",
"persistence.h",
+ "porting.h",
"preferences.cpp",
"processresult.cpp",
"processresult_p.h",
@@ -440,6 +466,7 @@ QbsLibrary {
"progressobserver.cpp",
"progressobserver.h",
"projectgeneratormanager.cpp",
+ "propagate_const.h",
"qbsassert.cpp",
"qbsassert.h",
"qbspluginmanager.cpp",
@@ -482,10 +509,12 @@ QbsLibrary {
"cleanoptions.h",
"codelocation.h",
"commandechomode.h",
+ "deprecationwarningmode.h",
"error.h",
"generateoptions.h",
"installoptions.h",
"joblimits.h",
+ "mutexdata.h",
"preferences.h",
"processresult.h",
"profile.h",
@@ -510,17 +539,4 @@ QbsLibrary {
"applecodesignutils.h"
]
}
- Group {
- name: "use_installed.pri"
- files: [
- "use_installed_corelib.pri",
- "../../../qbs_version.pri"
- ]
- qbs.install: qbsbuildconfig.installApiHeaders
- qbs.installDir: headerInstallPrefix
- }
- Export {
- Depends { name: "cpp" }
- cpp.defines: base.concat(product.projectFileUpdateDefines)
- }
}
diff --git a/src/lib/corelib/generators/generator.cpp b/src/lib/corelib/generators/generator.cpp
index 90bebdcaa..8fdd3555e 100644
--- a/src/lib/corelib/generators/generator.cpp
+++ b/src/lib/corelib/generators/generator.cpp
@@ -58,14 +58,11 @@ public:
};
ProjectGenerator::ProjectGenerator()
- : d(new ProjectGeneratorPrivate)
+ : d(std::make_unique<ProjectGeneratorPrivate>())
{
}
-ProjectGenerator::~ProjectGenerator()
-{
- delete d;
-}
+ProjectGenerator::~ProjectGenerator() = default;
static QString _configurationName(const Project &project)
{
@@ -85,14 +82,11 @@ ErrorInfo ProjectGenerator::generate(const QList<Project> &projects,
const QString &qbsSettingsDir,
const Internal::Logger &logger)
{
- 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->projects = Internal::sorted(projects, [](const Project &lhs, const Project &rhs) {
+ return _configurationName(lhs) < _configurationName(rhs); });
+ d->buildConfigurations = Internal::sorted(
+ buildConfigurations, [](const QVariantMap &lhs, const QVariantMap &rhs) {
+ return _configurationName(lhs) < _configurationName(rhs); });
d->installOptions = installOptions;
d->qbsSettingsDir = qbsSettingsDir;
d->logger = logger;
@@ -213,7 +207,7 @@ const GeneratableProject ProjectGenerator::project() const
{
QMap<QString, ProjectData> rootProjects;
GeneratableProject proj;
- for (const auto &project : qAsConst(d->projects)) {
+ for (const auto &project : std::as_const(d->projects)) {
const QString configurationName = _configurationName(project);
rootProjects.insert(configurationName, project.projectData());
proj.projects.insert(configurationName, project);
diff --git a/src/lib/corelib/generators/generator.h b/src/lib/corelib/generators/generator.h
index 775469f18..a7cc56b51 100644
--- a/src/lib/corelib/generators/generator.h
+++ b/src/lib/corelib/generators/generator.h
@@ -45,6 +45,8 @@
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
+#include <memory>
+
namespace qbs {
class ProjectGeneratorPrivate;
@@ -89,7 +91,7 @@ private:
QVariantMap buildConfiguration(const Project &project) const;
QStringList buildConfigurationCommandLine(const Project &project) const;
- ProjectGeneratorPrivate *d;
+ const std::unique_ptr<ProjectGeneratorPrivate> d;
};
} // namespace qbs
diff --git a/src/lib/corelib/generators/generatordata.cpp b/src/lib/corelib/generators/generatordata.cpp
index 7c6573484..25afae23d 100644
--- a/src/lib/corelib/generators/generatordata.cpp
+++ b/src/lib/corelib/generators/generatordata.cpp
@@ -133,7 +133,7 @@ QFileInfo GeneratableProject::filePath() const
filePath.insert(it.value().location().filePath());
}
Q_ASSERT(filePath.size() == 1);
- return *filePath.begin();
+ return QFileInfo(*filePath.begin());
}
bool GeneratableProject::hasMultipleConfigurations() const
diff --git a/src/lib/corelib/generators/generators.pri b/src/lib/corelib/generators/generators.pri
deleted file mode 100644
index e9730d895..000000000
--- a/src/lib/corelib/generators/generators.pri
+++ /dev/null
@@ -1,38 +0,0 @@
-include(../../../install_prefix.pri)
-
-SOURCES += \
- $$PWD/generatableprojectiterator.cpp \
- $$PWD/generator.cpp \
- $$PWD/generatordata.cpp \
- $$PWD/generatorutils.cpp \
- $$PWD/generatorversioninfo.cpp \
- $$PWD/xmlproject.cpp \
- $$PWD/xmlprojectwriter.cpp\
- $$PWD/xmlproperty.cpp \
- $$PWD/xmlpropertygroup.cpp \
- $$PWD/xmlworkspace.cpp \
- $$PWD/xmlworkspacewriter.cpp
-
-HEADERS += \
- $$PWD/generatableprojectiterator.h \
- $$PWD/generator.h \
- $$PWD/generatordata.h \
- $$PWD/generatorutils.h \
- $$PWD/generatorversioninfo.h \
- $$PWD/igeneratableprojectvisitor.h \
- $$PWD/ixmlnodevisitor.h \
- $$PWD/ixmlnodevisitor.h \
- $$PWD/xmlproject.h \
- $$PWD/xmlprojectwriter.h \
- $$PWD/xmlproperty.h \
- $$PWD/xmlpropertygroup.h \
- $$PWD/xmlworkspace.h \
- $$PWD/xmlworkspacewriter.h
-
-!qbs_no_dev_install {
- generators_headers.files = \
- $$PWD/generator.h \
- $$PWD/generatordata.h
- generators_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/generators
- INSTALLS += generators_headers
-}
diff --git a/src/lib/corelib/generators/generatorutils.cpp b/src/lib/corelib/generators/generatorutils.cpp
index 9c00eef05..d2dcbba25 100644
--- a/src/lib/corelib/generators/generatorutils.cpp
+++ b/src/lib/corelib/generators/generatorutils.cpp
@@ -30,6 +30,8 @@
#include "generatorutils.h"
+#include <tools/stlutils.h>
+
namespace qbs {
namespace gen {
namespace utils {
@@ -124,8 +126,8 @@ std::vector<ProductData> dependenciesOf(const ProductData &qbsProduct,
const QString &configurationName)
{
std::vector<ProductData> result;
- const auto depsNames = qbsProduct.dependencies();
- for (const auto &product : qAsConst(genProject.products)) {
+ const auto &depsNames = qbsProduct.dependencies();
+ for (const auto &product : std::as_const(genProject.products)) {
const auto pt = product.type();
if (!pt.contains(QLatin1String("staticlibrary")))
continue;
@@ -139,12 +141,12 @@ std::vector<ProductData> dependenciesOf(const ProductData &qbsProduct,
QString targetBinary(const ProductData &qbsProduct)
{
- const auto type = qbsProduct.type();
+ const auto &type = qbsProduct.type();
if (type.contains(QLatin1String("application"))) {
return QFileInfo(qbsProduct.targetExecutable()).fileName();
- } else if (type.contains(QLatin1String("staticlibrary"))) {
- const auto artifacts = qbsProduct.targetArtifacts();
- for (const auto &artifact : artifacts) {
+ }
+ if (type.contains(QLatin1String("staticlibrary"))) {
+ for (const auto &artifact : qbsProduct.targetArtifacts()) {
if (artifact.fileTags().contains(QLatin1String("staticlibrary")))
return QFileInfo(artifact.filePath()).fileName();
}
@@ -189,11 +191,10 @@ QStringList cppStringModuleProperties(const PropertyMap &qbsProps,
{
QStringList properties;
for (const auto &propertyName : propertyNames) {
- const auto entries = qbsProps.getModuleProperty(
- Internal::StringConstants::cppModule(),
- propertyName).toStringList();
- for (const auto &entry : entries)
- properties.push_back(entry.trimmed());
+ const auto entries = qbsProps.getModuleProperty(Internal::StringConstants::cppModule(),
+ propertyName).toStringList();
+ Internal::transform(entries, properties, [](const auto &entry) {
+ return entry.trimmed(); });
}
return properties;
}
@@ -221,7 +222,7 @@ static QString parseFlagValue(const QString &flagKey,
return parts.at(1).trimmed();
} else if (flagKey < *flagIt) {
// In this case an option is in form of 'flagKey<flagValue>'.
- return flagIt->mid(flagKey.count()).trimmed();
+ return flagIt->mid(flagKey.size()).trimmed();
} else {
// In this case an option is in form of 'flagKey <flagValue>'.
++flagIt;
diff --git a/src/lib/corelib/generators/generatorutils.h b/src/lib/corelib/generators/generatorutils.h
index 9348ab18c..58e59cbf8 100644
--- a/src/lib/corelib/generators/generatorutils.h
+++ b/src/lib/corelib/generators/generatorutils.h
@@ -41,14 +41,17 @@ namespace gen {
namespace utils {
enum class Architecture {
- Arm,
- Avr,
- Mcs51,
- Stm8,
- Msp430,
- Unknown
+ Unknown = 0,
+ Arm = 1 << 1,
+ Avr = 1 << 2,
+ Mcs51 = 1 << 3,
+ Stm8 = 1 << 4,
+ Msp430 = 1 << 5
};
+Q_DECLARE_FLAGS(ArchitectureFlags, Architecture)
+Q_DECLARE_OPERATORS_FOR_FLAGS(ArchitectureFlags)
+
QBS_EXPORT QString architectureName(Architecture arch);
QBS_EXPORT Architecture architecture(const Project &qbsProject);
QBS_EXPORT QString buildConfigurationName(const Project &qbsProject);
diff --git a/src/lib/corelib/generators/generatorversioninfo.cpp b/src/lib/corelib/generators/generatorversioninfo.cpp
index 3e2106b57..c5c8db03f 100644
--- a/src/lib/corelib/generators/generatorversioninfo.cpp
+++ b/src/lib/corelib/generators/generatorversioninfo.cpp
@@ -42,42 +42,10 @@
namespace qbs {
namespace gen {
-VersionInfo::VersionInfo(const Version &version,
- const std::set<utils::Architecture> &archs)
- : m_version(version), m_archs(archs)
-{
-}
-
-bool VersionInfo::operator<(const VersionInfo &other) const
-{
- return m_version < other.m_version;
-}
-
-bool VersionInfo::operator==(const VersionInfo &other) const
-{
- return m_version == other.m_version
- && m_archs == other.m_archs;
-}
-
-Version VersionInfo::version() const
-{
- return m_version;
-}
-
-bool VersionInfo::containsArchitecture(utils::Architecture arch) const
-{
- return m_archs.find(arch) != m_archs.cend();
-}
-
int VersionInfo::marketingVersion() const
{
return m_version.majorVersion();
}
-quint32 qHash(const VersionInfo &info)
-{
- return qHash(info.version().toString());
-}
-
} // namespace gen
} // namespace qbs
diff --git a/src/lib/corelib/generators/generatorversioninfo.h b/src/lib/corelib/generators/generatorversioninfo.h
index 65bfcf685..5c867326f 100644
--- a/src/lib/corelib/generators/generatorversioninfo.h
+++ b/src/lib/corelib/generators/generatorversioninfo.h
@@ -45,7 +45,7 @@
#include <tools/qbs_export.h>
#include <tools/version.h>
-#include <set>
+#include <QFlags>
namespace qbs {
namespace gen {
@@ -53,24 +53,31 @@ namespace gen {
class QBS_EXPORT VersionInfo
{
public:
- VersionInfo(const Version &version,
- const std::set<utils::Architecture> &archs);
- virtual ~VersionInfo() = default;
+ constexpr VersionInfo(const Version &version, utils::ArchitectureFlags archs)
+ : m_version(version), m_archs(archs)
+ {
+ }
- bool operator<(const VersionInfo &other) const;
- bool operator==(const VersionInfo &other) const;
+ constexpr bool operator<(const VersionInfo &other) const { return m_version < other.m_version; }
+ constexpr bool operator==(const VersionInfo &other) const
+ {
+ return m_version == other.m_version && m_archs == other.m_archs;
+ }
- Version version() const;
- bool containsArchitecture(utils::Architecture arch) const;
+ constexpr Version version() const { return m_version; }
+ constexpr bool containsArchitecture(utils::Architecture arch) const { return m_archs & arch; }
- virtual int marketingVersion() const;
+ int marketingVersion() const;
private:
Version m_version;
- std::set<utils::Architecture> m_archs;
+ utils::ArchitectureFlags m_archs;
};
-quint32 qHash(const VersionInfo &info);
+inline auto qHash(const VersionInfo &info)
+{
+ return qHash(info.version().toString());
+}
} // namespace gen
} // namespace qbs
diff --git a/src/lib/corelib/generators/xmlpropertygroup.cpp b/src/lib/corelib/generators/xmlpropertygroup.cpp
index 1dc92e553..c9e6a97c1 100644
--- a/src/lib/corelib/generators/xmlpropertygroup.cpp
+++ b/src/lib/corelib/generators/xmlpropertygroup.cpp
@@ -45,8 +45,7 @@ void PropertyGroup::appendProperty(QByteArray name, QVariant value)
appendChild<Property>(std::move(name), std::move(value));
}
-void PropertyGroup::appendMultiLineProperty(
- QByteArray key, QStringList values, QChar sep)
+void PropertyGroup::appendMultiLineProperty(QByteArray key, const QStringList &values, QChar sep)
{
const auto line = values.join(sep);
appendProperty(std::move(key), QVariant::fromValue(line));
diff --git a/src/lib/corelib/generators/xmlpropertygroup.h b/src/lib/corelib/generators/xmlpropertygroup.h
index e63b515fc..e7e051a87 100644
--- a/src/lib/corelib/generators/xmlpropertygroup.h
+++ b/src/lib/corelib/generators/xmlpropertygroup.h
@@ -52,7 +52,7 @@ public:
explicit PropertyGroup(QByteArray name);
void appendProperty(QByteArray name, QVariant value);
- void appendMultiLineProperty(QByteArray key, QStringList values,
+ void appendMultiLineProperty(QByteArray key, const QStringList &values,
QChar sep = QLatin1Char(','));
void accept(INodeVisitor *visitor) const final;
diff --git a/src/lib/corelib/jsextensions/binaryfile.cpp b/src/lib/corelib/jsextensions/binaryfile.cpp
index 5f28f689b..e4d2583dd 100644
--- a/src/lib/corelib/jsextensions/binaryfile.cpp
+++ b/src/lib/corelib/jsextensions/binaryfile.cpp
@@ -39,26 +39,22 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/hostosinfo.h>
+#include <tools/stlutils.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qvariant.h>
-
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
namespace qbs {
namespace Internal {
-class BinaryFile : public QObject, public QScriptable, public ResourceAcquiringScriptObject
+class BinaryFile : public JsExtension<BinaryFile>
{
- Q_OBJECT
- Q_ENUMS(OpenMode)
+ friend class JsExtension<BinaryFile>;
public:
enum OpenMode {
ReadOnly = 1,
@@ -66,69 +62,85 @@ public:
ReadWrite = ReadOnly | WriteOnly
};
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
- ~BinaryFile() override;
-
- Q_INVOKABLE void close();
- Q_INVOKABLE QString filePath();
- Q_INVOKABLE bool atEof() const;
- Q_INVOKABLE qint64 size() const;
- Q_INVOKABLE void resize(qint64 size);
- Q_INVOKABLE qint64 pos() const;
- Q_INVOKABLE void seek(qint64 pos);
- Q_INVOKABLE QVariantList read(qint64 size);
- Q_INVOKABLE void write(const QVariantList &data);
+ static const char *name() { return "BinaryFile"; }
+ static void declareEnums(JSContext *ctx, JSValue classObj);
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int);
private:
- explicit BinaryFile(QScriptContext *context, const QString &filePath, OpenMode mode = ReadOnly);
-
- bool checkForClosed() const;
-
- // ResourceAcquiringScriptObject implementation
- void releaseResources() override;
-
- QFile *m_file = nullptr;
+ static void setupMethods(JSContext *ctx, JSValue obj);
+
+ DEFINE_JS_FORWARDER(jsClose, &BinaryFile::close, "BinaryFile.close")
+ DEFINE_JS_FORWARDER(jsFilePath, &BinaryFile::filePath, "BinaryFile.filePath")
+ DEFINE_JS_FORWARDER(jsAtEof, &BinaryFile::atEof, "BinaryFile.atEof")
+ DEFINE_JS_FORWARDER(jsSize, &BinaryFile::size, "BinaryFile.size")
+ DEFINE_JS_FORWARDER(jsResize, &BinaryFile::resize, "BinaryFile.resize")
+ DEFINE_JS_FORWARDER(jsPos, &BinaryFile::pos, "BinaryFile.pos")
+ DEFINE_JS_FORWARDER(jsSeek, &BinaryFile::seek, "BinaryFile.seek")
+ DEFINE_JS_FORWARDER(jsRead, &BinaryFile::read, "BinaryFile.read")
+ DEFINE_JS_FORWARDER(jsWrite, &BinaryFile::write, "BinaryFile.write")
+
+ void close();
+ QString filePath();
+ bool atEof() const;
+ qint64 size() const;
+ void resize(qint64 size);
+ qint64 pos() const;
+ void seek(qint64 pos);
+ QByteArray read(qint64 size);
+ void write(const QByteArray &data);
+
+ explicit BinaryFile(JSContext *, const QString &filePath, OpenMode mode);
+
+ void checkForClosed() const;
+
+ std::unique_ptr<QFile> m_file;
};
-QScriptValue BinaryFile::ctor(QScriptContext *context, QScriptEngine *engine)
+void BinaryFile::declareEnums(JSContext *ctx, JSValue classObj)
{
- BinaryFile *t = nullptr;
- switch (context->argumentCount()) {
- case 0:
- return context->throwError(Tr::tr("BinaryFile constructor needs "
- "path of file to be opened."));
- case 1:
- t = new BinaryFile(context, context->argument(0).toString());
- break;
- case 2:
- t = new BinaryFile(context,
- context->argument(0).toString(),
- static_cast<OpenMode>(context->argument(1).toInt32()));
- break;
- default:
- return context->throwError(Tr::tr("BinaryFile constructor takes at most two parameters."));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- se->addResourceAcquiringScriptObject(t);
- const DubiousContextList dubiousContexts {
- DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- };
- se->checkContext(QStringLiteral("qbs.BinaryFile"), dubiousContexts);
- se->setUsesIo();
+ DECLARE_ENUM(ctx, classObj, ReadOnly);
+ DECLARE_ENUM(ctx, classObj, WriteOnly);
+ DECLARE_ENUM(ctx, classObj, ReadWrite);
+}
- return engine->newQObject(t, QScriptEngine::QtOwnership);
+void BinaryFile::setupMethods(JSContext *ctx, JSValue obj)
+{
+ setupMethod(ctx, obj, "close", &jsClose, 0);
+ setupMethod(ctx, obj, "filePath", &jsFilePath, 0);
+ setupMethod(ctx, obj, "atEof", &jsAtEof, 0);
+ setupMethod(ctx, obj, "size", &jsSize, 0);
+ setupMethod(ctx, obj, "resize", &jsResize, 0);
+ setupMethod(ctx, obj, "pos", &jsPos, 0);
+ setupMethod(ctx, obj, "seek", &jsSeek, 0);
+ setupMethod(ctx, obj, "read", &jsRead, 0);
+ setupMethod(ctx, obj, "write", &jsWrite, 0);
}
-BinaryFile::~BinaryFile()
-{
- delete m_file;
+JSValue BinaryFile::ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int)
+{
+ try {
+ const auto filePath = getArgument<QString>(ctx, "BinaryFile constructor", argc, argv);
+ OpenMode mode = ReadOnly;
+ if (argc > 1) {
+ mode = static_cast<OpenMode>
+ (fromArg<qint32>(ctx, "BinaryFile constructor", 2, argv[1]));
+ }
+ const JSValue obj = createObject(ctx, filePath, mode);
+
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts {
+ DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
+ };
+ se->checkContext(QStringLiteral("qbs.BinaryFile"), dubiousContexts);
+ se->setUsesIo();
+ return obj;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-BinaryFile::BinaryFile(QScriptContext *context, const QString &filePath, OpenMode mode)
+BinaryFile::BinaryFile(JSContext *, const QString &filePath, OpenMode mode)
{
- Q_ASSERT(thisObject().engine() == engine());
-
QIODevice::OpenMode m = QIODevice::NotOpen;
switch (mode) {
case ReadWrite:
@@ -141,136 +153,91 @@ BinaryFile::BinaryFile(QScriptContext *context, const QString &filePath, OpenMod
m = QIODevice::WriteOnly;
break;
default:
- context->throwError(Tr::tr("Unable to open file '%1': Undefined mode '%2'")
- .arg(filePath, mode));
- return;
+ throw Tr::tr("Unable to open file '%1': Undefined mode '%2'").arg(filePath).arg(mode);
}
- m_file = new QFile(filePath);
- 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 = nullptr;
- }
+ auto file = std::make_unique<QFile>(filePath);
+ if (Q_UNLIKELY(!file->open(m)))
+ throw Tr::tr("Unable to open file '%1': %2").arg(filePath, file->errorString());
+ m_file = std::move(file);
}
void BinaryFile::close()
{
- if (checkForClosed())
- return;
+ checkForClosed();
m_file->close();
- delete m_file;
- m_file = nullptr;
+ m_file.reset();
}
QString BinaryFile::filePath()
{
- if (checkForClosed())
- return {};
+ checkForClosed();
return QFileInfo(*m_file).absoluteFilePath();
}
bool BinaryFile::atEof() const
{
- if (checkForClosed())
- return true;
+ checkForClosed();
return m_file->atEnd();
}
qint64 BinaryFile::size() const
{
- if (checkForClosed())
- return -1;
+ checkForClosed();
return m_file->size();
}
void BinaryFile::resize(qint64 size)
{
- if (checkForClosed())
- return;
- if (Q_UNLIKELY(!m_file->resize(size))) {
- context()->throwError(Tr::tr("Could not resize '%1': %2")
- .arg(m_file->fileName(), m_file->errorString()));
- }
+ checkForClosed();
+ if (Q_UNLIKELY(!m_file->resize(size)))
+ throw Tr::tr("Could not resize '%1': %2").arg(m_file->fileName(), m_file->errorString());
}
qint64 BinaryFile::pos() const
{
- if (checkForClosed())
- return -1;
+ checkForClosed();
return m_file->pos();
}
void BinaryFile::seek(qint64 pos)
{
- if (checkForClosed())
- return;
- if (Q_UNLIKELY(!m_file->seek(pos))) {
- context()->throwError(Tr::tr("Could not seek '%1': %2")
- .arg(m_file->fileName(), m_file->errorString()));
- }
+ checkForClosed();
+ if (Q_UNLIKELY(!m_file->seek(pos)))
+ throw Tr::tr("Could not seek '%1': %2").arg(m_file->fileName(), m_file->errorString());
}
-QVariantList BinaryFile::read(qint64 size)
+QByteArray BinaryFile::read(qint64 size)
{
- if (checkForClosed())
- return {};
- const QByteArray bytes = m_file->read(size);
+ checkForClosed();
+ QByteArray bytes = m_file->read(size);
if (Q_UNLIKELY(bytes.size() == 0 && m_file->error() != QFile::NoError)) {
- context()->throwError(Tr::tr("Could not read from '%1': %2")
- .arg(m_file->fileName(), m_file->errorString()));
+ throw (Tr::tr("Could not read from '%1': %2")
+ .arg(m_file->fileName(), m_file->errorString()));
}
-
- QVariantList data;
- std::for_each(bytes.constBegin(), bytes.constEnd(), [&data](const char &c) {
- data.append(c); });
- return data;
+ return bytes;
}
-void BinaryFile::write(const QVariantList &data)
+void BinaryFile::write(const QByteArray &data)
{
- if (checkForClosed())
- return;
-
- QByteArray bytes;
- std::for_each(data.constBegin(), data.constEnd(), [&bytes](const QVariant &v) {
- bytes.append(v.toUInt() & 0xFF); });
-
- const qint64 size = m_file->write(bytes);
+ checkForClosed();
+ const qint64 size = m_file->write(data);
if (Q_UNLIKELY(size == -1)) {
- context()->throwError(Tr::tr("Could not write to '%1': %2")
- .arg(m_file->fileName(), m_file->errorString()));
+ throw Tr::tr("Could not write to '%1': %2")
+ .arg(m_file->fileName(), m_file->errorString());
}
}
-bool BinaryFile::checkForClosed() const
-{
- if (m_file)
- return false;
- if (QScriptContext *ctx = context())
- ctx->throwError(Tr::tr("Access to BinaryFile object that was already closed."));
- return true;
-}
-
-void BinaryFile::releaseResources()
+void BinaryFile::checkForClosed() const
{
- close();
- deleteLater();
+ if (!m_file)
+ throw Tr::tr("Access to BinaryFile object that was already closed.");
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionBinaryFile(QScriptValue extensionObject)
+void initializeJsExtensionBinaryFile(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- const QScriptValue obj = engine->newQMetaObject(&BinaryFile::staticMetaObject,
- engine->newFunction(&BinaryFile::ctor));
- extensionObject.setProperty(QStringLiteral("BinaryFile"), obj);
+ qbs::Internal::BinaryFile::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::BinaryFile *)
-
-#include "binaryfile.moc"
diff --git a/src/lib/corelib/jsextensions/domxml.cpp b/src/lib/corelib/jsextensions/domxml.cpp
index 118e8d5e1..35cff186b 100644
--- a/src/lib/corelib/jsextensions/domxml.cpp
+++ b/src/lib/corelib/jsextensions/domxml.cpp
@@ -38,429 +38,516 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <QtCore/qfile.h>
-#include <QtCore/qobject.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
-#include <QtScript/qscriptable.h>
-
#include <QtXml/qdom.h>
namespace qbs {
namespace Internal {
-class XmlDomDocument;
-
-class XmlDomNode: public QObject, public QScriptable
+template<class C> class XmlDomNode : public JsExtension<XmlDomNode<C>>
{
- 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;
-};
+ static const char *name();
+ XmlDomNode(const C &value) : m_value(value) {}
+ XmlDomNode(JSContext *, const QString &name);
+ XmlDomNode(JSContext *) {}
-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);
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int);
+ static void setupMethods(JSContext *ctx, JSValue obj);
- 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());
+ C value() const { return m_value; }
private:
- QDomDocument m_domDocument;
-};
-
-QScriptValue XmlDomDocument::ctor(QScriptContext *context, QScriptEngine *engine)
-{
- XmlDomDocument *xml = nullptr;
- 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(QStringLiteral("DomXml(QString file = QLatin1String(\"\"))"));
- }
- QScriptValue obj = engine->newQObject(xml, QScriptEngine::ScriptOwnership);
- static_cast<ScriptEngine *>(engine)->setUsesIo();
- 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(QStringLiteral("unable to open '%1'")
- .arg(filePath));
- return;
+#define XML_JS_FWD(name, func, text) DEFINE_JS_FORWARDER_QUAL(XmlDomNode, name, func, text)
+
+ XML_JS_FWD(jsIsElement, &XmlDomNode::isElement, "DomNode.isElement");
+ XML_JS_FWD(jsIsCDATASection, &XmlDomNode::isCDATASection, "DomNode.isCDATASection");
+ XML_JS_FWD(jsIsText, &XmlDomNode::isText, "DomNode.isText");
+ XML_JS_FWD(jsHasAttribute, &XmlDomNode::hasAttribute, "DomNode.hasAttribute");
+ XML_JS_FWD(jsTagName, &XmlDomNode::tagName, "DomNode.tagName");
+ XML_JS_FWD(jsSetTagName, &XmlDomNode::setTagName, "DomNode.setTagName");
+ XML_JS_FWD(jsText, &XmlDomNode::text, "DomNode.text");
+ XML_JS_FWD(jsData, &XmlDomNode::data, "DomNode.data");
+ XML_JS_FWD(jsSetData, &XmlDomNode::setData, "DomNode.setData");
+ XML_JS_FWD(jsClear, &XmlDomNode::clear, "DomNode.clear");
+ XML_JS_FWD(jsHasAttributes, &XmlDomNode::hasAttributes, "DomNode.hasAttributes");
+ XML_JS_FWD(jsHasChildNodes, &XmlDomNode::hasChildNodes, "DomNode.hasChildNodes");
+ XML_JS_FWD(jsSetAttribute, &XmlDomNode::setAttribute, "DomNode.setAttribute");
+ XML_JS_FWD(jsAppendChild, &XmlDomNode::appendChild, "DomNode.appendChild");
+ XML_JS_FWD(jsInsertBefore, &XmlDomNode::insertBefore, "DomNode.insertBefore");
+ XML_JS_FWD(jsInsertAfter, &XmlDomNode::insertAfter, "DomNode.insertAfter");
+ XML_JS_FWD(jsReplaceChild, &XmlDomNode::replaceChild, "DomNode.replaceChild");
+ XML_JS_FWD(jsRemoveChild, &XmlDomNode::removeChild, "DomNode.removeChild");
+
+ static JSValue jsAttribute(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
+ {
+ try {
+ const auto name = JsExtension<XmlDomNode>::template getArgument<QString>
+ (ctx, "DomNode.attribute", argc, argv);
+ QString defaultValue;
+ if (argc > 1)
+ defaultValue = JsExtension<XmlDomNode>::template fromArg<QString>
+ (ctx, "DomNode.attribute", 2, argv[1]);
+ return makeJsString(ctx, JsExtension<XmlDomNode>::fromJsObject
+ (ctx, this_val)->attribute(name, defaultValue));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-
- QByteArray buff(m_domDocument.toByteArray(indent));
- if (buff.size() != f.write(buff))
+ static JSValue jsParentNode(JSContext *ctx, JSValueConst this_val, int, JSValueConst *)
{
- context()->throwError(f.errorString());
- f.close();
- return;
+ return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect(
+ ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->parentNode());
}
-
- 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(QStringLiteral("unable to open '%1'")
- .arg(filePath));
- return;
+ static JSValue jsFirstChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
+ {
+ try {
+ QString tagName;
+ if (argc > 0)
+ tagName = JsExtension<XmlDomNode>::template getArgument<QString>
+ (ctx, "DomNode.firstChild", argc, argv);
+ return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect
+ (ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->firstChild(tagName));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
+ }
+ static JSValue jsLastChild(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
+ {
+ try {
+ QString tagName;
+ if (argc > 0)
+ tagName = JsExtension<XmlDomNode>::template getArgument<QString>
+ (ctx, "DomNode.lastChild", argc, argv);
+ return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect
+ (ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->lastChild(tagName));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
+ }
+ static JSValue jsPreviousSibling(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+ try {
+ QString tagName;
+ if (argc > 0)
+ tagName = JsExtension<XmlDomNode>::template getArgument<QString>
+ (ctx, "DomNode.previousSibling", argc, argv);
+ return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect(ctx, JsExtension<XmlDomNode>::fromJsObject
+ (ctx, this_val)->previousSibling(tagName));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
+ }
+ static JSValue jsNextSibling(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+ try {
+ QString tagName;
+ if (argc > 0)
+ tagName = JsExtension<C>::template getArgument<QString>
+ (ctx, "Xml.DomElement.nextSibling", argc, argv);
+ return JsExtension<XmlDomNode<QDomNode>>::createObjectDirect
+ (ctx, JsExtension<XmlDomNode>::fromJsObject(ctx, this_val)->nextSibling(tagName));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
- QString errorMsg;
- if (!m_domDocument.setContent(&f, &errorMsg)) {
- context()->throwError(errorMsg);
- return;
+ // Implemented for QDomDocument only.
+ static JSValue jsDocumentElement(JSContext *ctx, JSValueConst this_val, int, JSValueConst *);
+ static JSValue jsCreateElement(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+ static JSValue jsCreateCDATASection(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+ static JSValue jsCreateTextNode(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+ static JSValue jsSetContent(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
+ static JSValue jsToString(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
+ static JSValue jsSave(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
+ static JSValue jsLoad(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
+
+ bool isElement() const { return m_value.isElement(); }
+ bool isCDATASection() const { return m_value.isCDATASection(); }
+ bool isText() const { return m_value.isText(); }
+ QString attribute(const QString &name, const QString &defaultValue)
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ return el.attribute(name, defaultValue);
+ }
+ void setAttribute(const QString &name, const QString &value)
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ el.setAttribute(name, value);
+ }
+ bool hasAttribute(const QString &name) const
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ return el.hasAttribute(name);
+ }
+ QString tagName() const
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ return el.tagName();
+ }
+ void setTagName(const QString &name)
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ el.setTagName(name);
}
-}
-XmlDomDocument::XmlDomDocument(QScriptContext *context, const QString &name):m_domDocument(name)
-{
- Q_UNUSED(context)
- m_domNode = m_domDocument;
-}
+ QString text() const
+ {
+ QDomElement el = m_value.toElement();
+ if (el.isNull())
+ throw QStringLiteral("Node '%1' is not an element node").arg(m_value.nodeName());
+ return el.text();
+ }
-QScriptValue XmlDomNode::ctor(QScriptContext *context, QScriptEngine *engine)
-{
- Q_UNUSED(context)
- return engine->newQObject(new XmlDomNode(), QScriptEngine::ScriptOwnership);
-}
+ QString data() const
+ {
+ if (m_value.isText())
+ return m_value.toText().data();
+ if (m_value.isCDATASection())
+ return m_value.toCDATASection().data();
+ if (m_value.isCharacterData())
+ return m_value.toCharacterData().data();
+ throw QStringLiteral("Node '%1' is not a character data node").arg(m_value.nodeName());
+ }
+ void setData(const QString &v) const
+ {
+ if (m_value.isText())
+ return m_value.toText().setData(v);
+ if (m_value.isCDATASection())
+ return m_value.toCDATASection().setData(v);
+ if (m_value.isCharacterData())
+ return m_value.toCharacterData().setData(v);
+ throw QStringLiteral("Node '%1' is not a character data node").arg(m_value.nodeName());
+ }
+ void clear() { m_value.clear(); }
+ bool hasAttributes() const { return m_value.hasAttributes(); }
+ bool hasChildNodes() const { return m_value.hasChildNodes(); }
-bool XmlDomNode::isElement() const
-{
- return m_domNode.isElement();
-}
+ XmlDomNode<QDomNode> *parentNode() const
+ {
+ return new XmlDomNode<QDomNode>(m_value.parentNode());
+ }
+ XmlDomNode<QDomNode> *firstChild(const QString &tagName)
+ {
+ if (tagName.isEmpty())
+ return new XmlDomNode<QDomNode>(m_value.firstChild());
+ return new XmlDomNode<QDomNode>(m_value.firstChildElement(tagName));
+ }
+ XmlDomNode<QDomNode> *lastChild(const QString &tagName) const
+ {
+ if (tagName.isEmpty())
+ return new XmlDomNode<QDomNode>(m_value.lastChild());
+ return new XmlDomNode<QDomNode>(m_value.lastChildElement(tagName));
+ }
+ XmlDomNode<QDomNode> *previousSibling(const QString &tagName) const
+ {
+ if (tagName.isEmpty())
+ return new XmlDomNode<QDomNode>(m_value.previousSibling());
+ return new XmlDomNode<QDomNode>(m_value.previousSiblingElement(tagName));
+ }
+ XmlDomNode<QDomNode> *nextSibling(const QString &tagName) const
+ {
+ if (tagName.isEmpty())
+ return new XmlDomNode<QDomNode>(m_value.nextSibling());
+ return new XmlDomNode<QDomNode>(m_value.nextSiblingElement(tagName));
+ }
+ void appendChild(const XmlDomNode<QDomNode> *newChild)
+ {
+ m_value.appendChild(newChild->value());
+ }
+ void insertBefore(const XmlDomNode<QDomNode> *newChild, const XmlDomNode<QDomNode> *refChild)
+ {
+ m_value.insertBefore(newChild->value(), refChild->value());
+ }
+ void insertAfter(const XmlDomNode<QDomNode> *newChild, const XmlDomNode<QDomNode> *refChild)
+ {
+ m_value.insertAfter(newChild->value(), refChild->value());
+ }
+ void replaceChild(const XmlDomNode<QDomNode> *newChild, const XmlDomNode<QDomNode> *oldChild)
+ {
+ m_value.replaceChild(newChild->value(), oldChild->value());
+ }
+ void removeChild(const XmlDomNode<QDomNode> *oldChild)
+ {
+ m_value.removeChild(oldChild->value());
+ }
-bool XmlDomNode::isCDATASection() const
-{
- return m_domNode.isCDATASection();
-}
+ // Implemented for QDomDocument only.
+ XmlDomNode<QDomNode> *documentElement();
+ XmlDomNode<QDomNode> *createElement(const QString &tagName);
+ XmlDomNode<QDomNode> *createCDATASection(const QString &value);
+ XmlDomNode<QDomNode> *createTextNode(const QString &value);
-bool XmlDomNode::isText() const
-{
- return m_domNode.isText();
-}
+ bool setContent(const QString &content);
+ QString toString(int indent = 1);
-QString XmlDomNode::attribute(const QString &name, const QString &defValue)
-{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return defValue;
- }
- return el.attribute(name, defValue);
-}
+ void save(const QString &filePath, int indent = 1);
+ void load(const QString &filePath);
-void XmlDomNode::setAttribute(const QString &name, const QString &value)
-{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return;
- }
- el.setAttribute(name, value);
-}
+ C m_value;
+};
-bool XmlDomNode::hasAttribute(const QString &name) const
-{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return false;
- }
- return el.hasAttribute(name);
-}
+template<> const char *XmlDomNode<QDomNode>::name() { return "DomNode"; }
+template<> const char *XmlDomNode<QDomDocument>::name() { return "DomDocument"; }
-QString XmlDomNode::tagName() const
+template<> JSValue XmlDomNode<QDomNode>::ctor(JSContext *ctx, JSValue, JSValue, int , JSValue *,
+ int)
{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return {};
- }
- return el.tagName();
+ const JSValue obj = createObject(ctx);
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{
+ DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
+ };
+ se->checkContext(QStringLiteral("qbs.Xml.DomNode"), dubiousContexts);
+ se->setUsesIo();
+ return obj;
}
-void XmlDomNode::setTagName(const QString &name)
+template<> JSValue XmlDomNode<QDomDocument>::ctor(JSContext *ctx, JSValue, JSValue,
+ int argc, JSValue *argv, int)
{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return;
- }
- el.setTagName(name);
+ try {
+ JSValue obj;
+ if (argc == 0) {
+ obj = createObject(ctx);
+ } else {
+ const auto name = getArgument<QString>(ctx, "XmlDomDocument constructor",
+ argc, argv);
+ obj = createObject(ctx, name);
+ }
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{
+ DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
+ };
+ se->checkContext(QStringLiteral("qbs.Xml.DomDocument"), dubiousContexts);
+ se->setUsesIo();
+ return obj;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QString XmlDomNode::text() const
+template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::documentElement()
{
- QDomElement el = m_domNode.toElement();
- if (el.isNull()) {
- context()->throwError(QStringLiteral("Node '%1' is not an element node").arg(m_domNode.nodeName()));
- return {};
- }
- return el.text();
+ return new XmlDomNode<QDomNode>(m_value.documentElement());
}
-QString XmlDomNode::data() const
+template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::createElement(const QString &tagName)
{
- 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(QStringLiteral("Node '%1' is not a character data node").arg(m_domNode.nodeName()));
- return {};
+ return new XmlDomNode<QDomNode>(m_value.createElement(tagName));
}
-void XmlDomNode::setData(const QString &v) const
+template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::createCDATASection(const QString &value)
{
- 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(QStringLiteral("Node '%1' is not a character data node").arg(m_domNode.nodeName()));
- return;
+ return new XmlDomNode<QDomNode>(m_value.createCDATASection(value));
}
-void XmlDomNode::clear()
+template<> XmlDomNode<QDomNode> *XmlDomNode<QDomDocument>::createTextNode(const QString &value)
{
- m_domNode.clear();
+ return new XmlDomNode<QDomNode>(m_value.createTextNode(value));
}
-bool XmlDomNode::hasAttributes() const
+template<> bool XmlDomNode<QDomDocument>::setContent(const QString &content)
{
- return m_domNode.hasAttributes();
+ return static_cast<bool>(m_value.setContent(content));
}
-bool XmlDomNode::hasChildNodes() const
+template<> QString XmlDomNode<QDomDocument>::toString(int indent)
{
- return m_domNode.hasChildNodes();
+ return m_value.toString(indent);
}
-QScriptValue XmlDomNode::parentNode() const
+template<> JSValue XmlDomNode<QDomDocument>::jsDocumentElement(JSContext *ctx, JSValue this_val,
+ int, JSValue *)
{
- return engine()->newQObject(new XmlDomNode(m_domNode.parentNode()), QScriptEngine::ScriptOwnership);
+ return XmlDomNode<QDomNode>::createObjectDirect(
+ ctx, fromJsObject(ctx, this_val)->documentElement());
}
-QScriptValue XmlDomNode::firstChild(const QString &tagName)
+template<> JSValue XmlDomNode<QDomDocument>::jsCreateElement(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- if (tagName.isEmpty())
- return engine()->newQObject(new XmlDomNode(m_domNode.firstChild()), QScriptEngine::ScriptOwnership);
- return engine()->newQObject(new XmlDomNode(m_domNode.firstChildElement(tagName)), QScriptEngine::ScriptOwnership);
+ try {
+ const auto tagName = getArgument<QString>(ctx, "DomDocument.createElement", argc, argv);
+ const JSValue obj = XmlDomNode<QDomNode>::createObjectDirect(
+ ctx, fromJsObject(ctx, this_val)->createElement(tagName));
+ return obj;
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue XmlDomNode::lastChild(const QString &tagName) const
+template<> JSValue XmlDomNode<QDomDocument>::jsCreateCDATASection(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- if (tagName.isEmpty())
- return engine()->newQObject(new XmlDomNode(m_domNode.lastChild()), QScriptEngine::ScriptOwnership);
- return engine()->newQObject(new XmlDomNode(m_domNode.lastChildElement(tagName)), QScriptEngine::ScriptOwnership);
+ try {
+ const auto value = getArgument<QString>(ctx, "DomDocument.createCDATASection", argc, argv);
+ return XmlDomNode<QDomNode>::createObjectDirect(
+ ctx, fromJsObject(ctx, this_val)->createCDATASection(value));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue XmlDomNode::previousSibling(const QString &tagName) const
+template<> JSValue XmlDomNode<QDomDocument>::jsCreateTextNode(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- if (tagName.isEmpty())
- return engine()->newQObject(new XmlDomNode(m_domNode.previousSibling()), QScriptEngine::ScriptOwnership);
- return engine()->newQObject(new XmlDomNode(m_domNode.previousSiblingElement(tagName)), QScriptEngine::ScriptOwnership);
+ try {
+ const auto value = getArgument<QString>(ctx, "DomDocument.createTextNode", argc, argv);
+ return XmlDomNode<QDomNode>::createObjectDirect(
+ ctx, fromJsObject(ctx, this_val)->createTextNode(value));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue XmlDomNode::nextSibling(const QString &tagName) const
+template<> JSValue XmlDomNode<QDomDocument>::jsSetContent(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- if (tagName.isEmpty())
- return engine()->newQObject(new XmlDomNode(m_domNode.nextSibling()), QScriptEngine::ScriptOwnership);
- return engine()->newQObject(new XmlDomNode(m_domNode.nextSiblingElement(tagName)), QScriptEngine::ScriptOwnership);
+ try {
+ const auto content = getArgument<QString>(ctx, "DomDocument.setContent", argc, argv);
+ return JS_NewBool(ctx, fromJsObject(ctx, this_val)->setContent(content));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue XmlDomNode::appendChild(QScriptValue newChild)
+template<> JSValue XmlDomNode<QDomDocument>::jsToString(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
- if (!newNode) {
- context()->throwError(QStringLiteral("First argument is not a XmlDomNode object"));
- return {};
+ try {
+ qint32 indent = 1;
+ if (argc > 0)
+ indent = getArgument<qint32>(ctx, "DomDocument.toString", argc, argv);
+ return makeJsString(ctx, fromJsObject(ctx, this_val)->toString(indent));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
- return engine()->newQObject(new XmlDomNode(m_domNode.appendChild(newNode->m_domNode)), QScriptEngine::ScriptOwnership);
}
-QScriptValue XmlDomNode::insertBefore(const QScriptValue &newChild, const QScriptValue &refChild)
+template<> void XmlDomNode<QDomDocument>::save(const QString &filePath, int indent)
{
- auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
- if (!newNode) {
- context()->throwError(QStringLiteral("First argument is not a XmlDomNode object"));
- return {};
- }
+ QFile f(filePath);
+ if (!f.open(QIODevice::WriteOnly))
+ throw QStringLiteral("unable to open '%1'").arg(filePath);
- auto refNode = qobject_cast<XmlDomNode*>(refChild.toQObject());
- if (!refNode) {
- context()->throwError(QStringLiteral("Second argument is not a XmlDomNode object"));
- return {};
- }
+ QByteArray buff(m_value.toByteArray(indent));
+ if (buff.size() != f.write(buff))
+ throw f.errorString();
- return engine()->newQObject(new XmlDomNode(m_domNode.insertBefore(newNode->m_domNode, refNode->m_domNode)), QScriptEngine::ScriptOwnership);
+ f.close();
+ if (f.error() != QFile::NoError)
+ throw f.errorString();
}
-QScriptValue XmlDomNode::insertAfter(const QScriptValue &newChild, const QScriptValue &refChild)
+template<> JSValue XmlDomNode<QDomDocument>::jsSave(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
- if (!newNode) {
- context()->throwError(QStringLiteral("First argument is not a XmlDomNode object"));
- return {};
- }
-
- auto refNode = qobject_cast<XmlDomNode*>(refChild.toQObject());
- if (!refNode) {
- context()->throwError(QStringLiteral("Second argument is not a XmlDomNode object"));
- return {};
+ try {
+ const auto filePath = getArgument<QString>(ctx, "DomDocument.save", argc, argv);
+ qint32 indent = 1;
+ if (argc > 1)
+ indent = fromArg<qint32>(ctx, "toString", 2, argv[1]);
+ fromJsObject(ctx, this_val)->save(filePath, indent);
+ return JS_UNDEFINED;
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
-
- 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)
+template<> void XmlDomNode<QDomDocument>::load(const QString &filePath)
{
- auto newNode = qobject_cast<XmlDomNode*>(newChild.toQObject());
- if (!newNode) {
- context()->throwError(QStringLiteral("First argument is not a XmlDomNode object"));
- return {};
- }
-
- auto oldNode = qobject_cast<XmlDomNode*>(oldChild.toQObject());
- if (!oldNode) {
- context()->throwError(QStringLiteral("Second argument is not a XmlDomNode object"));
- return {};
- }
+ QFile f(filePath);
+ if (!f.open(QIODevice::ReadOnly))
+ throw QStringLiteral("unable to open '%1'").arg(filePath);
- return engine()->newQObject(new XmlDomNode(m_domNode.replaceChild(newNode->m_domNode, oldNode->m_domNode)), QScriptEngine::ScriptOwnership);
+ QString errorMsg;
+ if (!m_value.setContent(&f, &errorMsg))
+ throw errorMsg;
}
-QScriptValue XmlDomNode::removeChild(const QScriptValue &oldChild)
+template<> JSValue XmlDomNode<QDomDocument>::jsLoad(JSContext *ctx, JSValue this_val,
+ int argc, JSValue *argv)
{
- auto oldNode = qobject_cast<XmlDomNode*>(oldChild.toQObject());
- if (!oldNode) {
- context()->throwError(QStringLiteral("First argument is not a XmlDomNode object"));
- return {};
+ try {
+ const auto filePath = getArgument<QString>(ctx, "DomDocument.load", argc, argv);
+ fromJsObject(ctx, this_val)->load(filePath);
+ return JS_UNDEFINED;
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
-
- return engine()->newQObject(new XmlDomNode(m_domNode.removeChild(oldNode->m_domNode)), QScriptEngine::ScriptOwnership);
}
-XmlDomNode::XmlDomNode(const QDomNode &other)
-{
- m_domNode = other;
+template<> XmlDomNode<QDomDocument>::XmlDomNode(JSContext *, const QString &name) : m_value(name) {}
+
+template<class C>
+void XmlDomNode<C>::setupMethods(JSContext *ctx, JSValue obj)
+{
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "appendChild", &jsAppendChild, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "attribute", &jsAttribute, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "clear", &jsClear, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "data", &jsData, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "firstChild", &jsFirstChild, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "hasAttribute", &jsHasAttribute, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "hasAttributes", &jsHasAttributes, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "hasChildNodes", &jsHasChildNodes, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "insertAfter", &jsInsertAfter, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "insertBefore", &jsInsertBefore, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "isCDATASection", &jsIsCDATASection, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "isElement", &jsIsElement, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "isText", &jsIsText, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "lastChild", &jsLastChild, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "nextSibling", &jsNextSibling, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "parentNode", &jsParentNode, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "previousSibling", &jsPreviousSibling, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "removeChild", &jsRemoveChild, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "replaceChild", &jsReplaceChild, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setAttribute", &jsSetAttribute, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setData", &jsSetData, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setTagName", &jsSetTagName, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "tagName", &jsTagName, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "text", &jsText, 0);
+ if constexpr (std::is_same_v<C, QDomDocument>) {
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "documentElement", &jsDocumentElement, 0);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "createElement", &jsCreateElement, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "createCDATASection",
+ &jsCreateCDATASection, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "createTextNode", &jsCreateTextNode, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "setContent", &jsSetContent, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "toString", &jsToString, 1);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "save", &jsSave, 2);
+ JsExtension<XmlDomNode>::setupMethod(ctx, obj, "load", &jsLoad, 1);
+ }
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionXml(QScriptValue extensionObject)
+void initializeJsExtensionXml(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue docObj = engine->newQMetaObject(&XmlDomDocument::staticMetaObject,
- engine->newFunction(&XmlDomDocument::ctor));
- QScriptValue nodeObj = engine->newQMetaObject(&XmlDomNode::staticMetaObject,
- engine->newFunction(&XmlDomNode::ctor));
- QScriptValue contextObject = engine->newObject();
- contextObject.setProperty(QStringLiteral("DomDocument"), docObj);
- contextObject.setProperty(QStringLiteral("DomElement"), nodeObj);
-
- extensionObject.setProperty(QStringLiteral("Xml"), contextObject);
+ JSValue contextObject = engine->newObject();
+ XmlDomNode<QDomNode>::registerClass(engine, contextObject);
+ XmlDomNode<QDomDocument>::registerClass(engine, contextObject);
+ setJsProperty(engine->context(), extensionObject, QLatin1String("Xml"), contextObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::XmlDomDocument *)
-Q_DECLARE_METATYPE(qbs::Internal::XmlDomNode *)
-
-#include "domxml.moc"
diff --git a/src/lib/corelib/jsextensions/environmentextension.cpp b/src/lib/corelib/jsextensions/environmentextension.cpp
index cf17c938b..e973072d0 100644
--- a/src/lib/corelib/jsextensions/environmentextension.cpp
+++ b/src/lib/corelib/jsextensions/environmentextension.cpp
@@ -37,6 +37,8 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/hostosinfo.h>
@@ -44,93 +46,89 @@
#include <QtCore/qdir.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-
namespace qbs {
namespace Internal {
-class EnvironmentExtension : public QObject, QScriptable
+class EnvironmentExtension : public JsExtension<EnvironmentExtension>
{
- 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);
+ static const char *name() { return "Environment"; }
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static JSValue jsGetEnv(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsPutEnv(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsUnsetEnv(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsCurrentEnv(JSContext *ctx, JSValueConst, int, JSValueConst *);
};
-QScriptValue EnvironmentExtension::js_ctor(QScriptContext *context, QScriptEngine *engine)
+void EnvironmentExtension::setupStaticMethods(JSContext *ctx, JSValue classObj)
{
- Q_UNUSED(engine);
- return context->throwError(Tr::tr("'Environment' cannot be instantiated."));
+ setupMethod(ctx, classObj, "getEnv", &EnvironmentExtension::jsGetEnv, 1);
+ setupMethod(ctx, classObj, "putEnv", &EnvironmentExtension::jsPutEnv, 2);
+ setupMethod(ctx, classObj, "unsetEnv", &EnvironmentExtension::jsUnsetEnv, 1);
+ setupMethod(ctx, classObj, "currentEnv", &EnvironmentExtension::jsCurrentEnv, 0);
}
-static QProcessEnvironment *getProcessEnvironment(QScriptContext *context, QScriptEngine *engine,
- const QString &func, bool doThrow = true)
+static QProcessEnvironment *getProcessEnvironment(ScriptEngine *engine, const QString &func,
+ bool doThrow = true)
{
QVariant v = engine->property(StringConstants::qbsProcEnvVarInternal());
auto procenv = reinterpret_cast<QProcessEnvironment *>(v.value<void *>());
- if (!procenv && doThrow)
- throw context->throwError(QScriptContext::UnknownError,
- QStringLiteral("%1 can only be called from ").arg(func) +
- QStringLiteral("Module.setupBuildEnvironment and ") +
- QStringLiteral("Module.setupRunEnvironment"));
+ if (!procenv && doThrow) {
+ throw QStringLiteral("%1 can only be called from "
+ "Module.setupBuildEnvironment and "
+ "Module.setupRunEnvironment").arg(func);
+ }
return procenv;
}
-QScriptValue EnvironmentExtension::js_getEnv(QScriptContext *context, QScriptEngine *engine)
+JSValue EnvironmentExtension::jsGetEnv(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("getEnv expects 1 argument"));
- const QProcessEnvironment env = static_cast<ScriptEngine *>(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);
- return value.isNull() ? engine->undefinedValue() : value;
+ try {
+ const auto name = getArgument<QString>(ctx, "Environment.getEnv", argc, argv);
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const QProcessEnvironment env = engine->environment();
+ const QProcessEnvironment *procenv = getProcessEnvironment(engine, QStringLiteral("getEnv"),
+ false);
+ if (!procenv)
+ procenv = &env;
+ const QString value = procenv->value(name);
+ return value.isNull() ? engine->undefinedValue() : makeJsString(ctx, value);
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue EnvironmentExtension::js_putEnv(QScriptContext *context, QScriptEngine *engine)
+JSValue EnvironmentExtension::jsPutEnv(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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();
+ try {
+ const auto args = getArguments<QString, QString>(ctx, "Environment.putEnv", argc, argv);
+ getProcessEnvironment(ScriptEngine::engineForContext(ctx), QStringLiteral("putEnv"))
+ ->insert(std::get<0>(args), std::get<1>(args));
+ return JS_UNDEFINED;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue EnvironmentExtension::js_unsetEnv(QScriptContext *context, QScriptEngine *engine)
+JSValue EnvironmentExtension::jsUnsetEnv(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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();
+ try {
+ const auto name = getArgument<QString>(ctx, "Environment.unsetEnv", argc, argv);
+ getProcessEnvironment(ScriptEngine::engineForContext(ctx), QStringLiteral("unsetEnv"))
+ ->remove(name);
+ return JS_UNDEFINED;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue EnvironmentExtension::js_currentEnv(QScriptContext *context, QScriptEngine *engine)
+JSValue EnvironmentExtension::jsCurrentEnv(JSContext *ctx, JSValue, int, JSValue *)
{
- Q_UNUSED(context);
- const QProcessEnvironment env = static_cast<ScriptEngine *>(engine)->environment();
- const QProcessEnvironment *procenv = getProcessEnvironment(context, engine,
- QStringLiteral("currentEnv"), false);
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const QProcessEnvironment env = engine->environment();
+ const QProcessEnvironment *procenv = getProcessEnvironment(engine, QStringLiteral("currentEnv"),
+ false);
if (!procenv)
procenv = &env;
- QScriptValue envObject = engine->newObject();
+ JSValue envObject = engine->newObject();
const auto keys = procenv->keys();
for (const QString &key : keys) {
const QString keyName = HostOsInfo::isWindowsHost() ? key.toUpper() : key;
- envObject.setProperty(keyName, QScriptValue(procenv->value(key)));
+ setJsProperty(ctx, envObject, keyName, procenv->value(key));
}
return envObject;
}
@@ -138,23 +136,7 @@ QScriptValue EnvironmentExtension::js_currentEnv(QScriptContext *context, QScrip
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionEnvironment(QScriptValue extensionObject)
+void initializeJsExtensionEnvironment(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- 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);
+ qbs::Internal::EnvironmentExtension::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::EnvironmentExtension *)
-
-#include "environmentextension.moc"
diff --git a/src/lib/corelib/jsextensions/file.cpp b/src/lib/corelib/jsextensions/file.cpp
index 7cd2aadb8..6c24635a0 100644
--- a/src/lib/corelib/jsextensions/file.cpp
+++ b/src/lib/corelib/jsextensions/file.cpp
@@ -37,6 +37,8 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/fileinfo.h>
@@ -44,15 +46,11 @@
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-
namespace qbs {
namespace Internal {
-class File : public QObject, QScriptable
+class File : public JsExtension<File>
{
- Q_OBJECT
public:
enum Filter {
Dirs = 0x001,
@@ -82,209 +80,198 @@ public:
NoFilter = -1
};
Q_DECLARE_FLAGS(Filters, Filter)
- Q_ENUMS(Filter)
- 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);
+ static const char *name() { return "File"; }
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static void declareEnums(JSContext *ctx, JSValue classObj);
+ static JSValue jsCopy(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsExists(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsDirectoryEntries(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsLastModified(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsMakePath(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsMove(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsRemove(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsCanonicalFilePath(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
};
-QScriptValue File::js_ctor(QScriptContext *context, QScriptEngine *engine)
+void File::setupStaticMethods(JSContext *ctx, JSValue classObj)
{
- Q_UNUSED(engine);
- return context->throwError(Tr::tr("'File' cannot be instantiated."));
+ setupMethod(ctx, classObj, "copy", &File::jsCopy, 2);
+ setupMethod(ctx, classObj, "exists", &File::jsExists, 1);
+ setupMethod(ctx, classObj, "directoryEntries", File::jsDirectoryEntries, 2);
+ setupMethod(ctx, classObj, "lastModified", File::jsLastModified, 1);
+ setupMethod(ctx, classObj, "makePath", &File::jsMakePath, 1);
+ setupMethod(ctx, classObj, "move", &File::jsMove, 3);
+ setupMethod(ctx, classObj, "remove", &File::jsRemove, 1);
+ setupMethod(ctx, classObj, "canonicalFilePath", &File::jsCanonicalFilePath, 1);
}
-QScriptValue File::js_copy(QScriptContext *context, QScriptEngine *engine)
+void File::declareEnums(JSContext *ctx, JSValue classObj)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("copy expects 2 arguments"));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({
- DubiousContext(EvalContext::PropertyEvaluation),
- DubiousContext(EvalContext::RuleExecution, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("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;
+ DECLARE_ENUM(ctx, classObj, Dirs);
+ DECLARE_ENUM(ctx, classObj, Files);
+ DECLARE_ENUM(ctx, classObj, Drives);
+ DECLARE_ENUM(ctx, classObj, NoSymLinks);
+ DECLARE_ENUM(ctx, classObj, AllEntries);
+ DECLARE_ENUM(ctx, classObj, TypeMask);
+ DECLARE_ENUM(ctx, classObj, Readable);
+ DECLARE_ENUM(ctx, classObj, Writable);
+ DECLARE_ENUM(ctx, classObj, Executable);
+ DECLARE_ENUM(ctx, classObj, PermissionMask);
+ DECLARE_ENUM(ctx, classObj, Modified);
+ DECLARE_ENUM(ctx, classObj, Hidden);
+ DECLARE_ENUM(ctx, classObj, System);
+ DECLARE_ENUM(ctx, classObj, AccessMask);
+ DECLARE_ENUM(ctx, classObj, AllDirs);
+ DECLARE_ENUM(ctx, classObj, CaseSensitive);
+ DECLARE_ENUM(ctx, classObj, NoDot);
+ DECLARE_ENUM(ctx, classObj, NoDotDot);
+ DECLARE_ENUM(ctx, classObj, NoDotAndDotDot);
+ DECLARE_ENUM(ctx, classObj, NoFilter);
}
-QScriptValue File::js_exists(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsCopy(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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);
- const auto se = static_cast<ScriptEngine *>(engine);
- se->addFileExistsResult(filePath, exists);
- return exists;
+ try {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts({
+ DubiousContext(EvalContext::PropertyEvaluation),
+ DubiousContext(EvalContext::RuleExecution, DubiousContext::SuggestMoving)
+ });
+ se->checkContext(QStringLiteral("File.copy()"), dubiousContexts);
+
+ const auto args = getArguments<QString, QString>(ctx, "File.copy", argc, argv);
+ QString errorMessage;
+ if (Q_UNLIKELY(!copyFileRecursion(std::get<0>(args), std::get<1>(args), true, true,
+ &errorMessage))) {
+ throw errorMessage;
+ }
+ return JS_TRUE;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_directoryEntries(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsExists(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("directoryEntries expects 2 arguments"));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({
- DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("File.directoryEntries()"), dubiousContexts);
-
- const QString path = context->argument(0).toString();
- const auto filters = static_cast<QDir::Filters>(context->argument(1).toUInt32());
- QDir dir(path);
- const QStringList entries = dir.entryList(filters, QDir::Name);
- se->addDirectoryEntriesResult(path, filters, entries);
- return qScriptValueFromSequence(engine, entries);
+ try {
+ const auto filePath = getArgument<QString>(ctx, "File.exists", argc, argv);
+ const bool exists = FileInfo::exists(filePath);
+ ScriptEngine::engineForContext(ctx)->addFileExistsResult(filePath, exists);
+ return JS_NewBool(ctx, exists);
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_remove(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsDirectoryEntries(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("remove expects 1 argument"));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({ DubiousContext(EvalContext::PropertyEvaluation) });
- se->checkContext(QStringLiteral("File.remove()"), dubiousContexts);
-
- QString fileName = context->argument(0).toString();
-
- QString errorMessage;
- if (Q_UNLIKELY(!removeFileRecursion(QFileInfo(fileName), &errorMessage)))
- return context->throwError(errorMessage);
- return true;
+ try {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts({
+ DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
+ });
+ se->checkContext(QStringLiteral("File.directoryEntries()"), dubiousContexts);
+
+ const auto args = getArguments<QString, qint32>(ctx, "Environment.directoryEntries",
+ argc, argv);
+ const QString path = std::get<0>(args);
+ const QDir dir(path);
+ const auto filters = static_cast<QDir::Filters>(std::get<1>(args));
+ const QStringList entries = dir.entryList(filters, QDir::Name);
+ se->addDirectoryEntriesResult(path, filters, entries);
+ return makeJsStringList(ctx, entries);
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_lastModified(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsRemove(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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();
- const auto se = static_cast<ScriptEngine *>(engine);
- se->addFileLastModifiedResult(filePath, timestamp);
- return timestamp.asDouble();
+ try {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{DubiousContext(EvalContext::PropertyEvaluation)};
+ se->checkContext(QStringLiteral("File.remove()"), dubiousContexts);
+ const auto fileName = getArgument<QString>(ctx, "Environment.remove", argc, argv);
+ QString errorMessage;
+ if (Q_UNLIKELY(!removeFileRecursion(QFileInfo(fileName), &errorMessage)))
+ throw errorMessage;
+ return JS_TRUE;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_makePath(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsLastModified(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("makePath expects 1 argument"));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({ DubiousContext(EvalContext::PropertyEvaluation) });
- se->checkContext(QStringLiteral("File.makePath()"), dubiousContexts);
-
- return QDir::root().mkpath(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "Environment.lastModified", argc, argv);
+ const FileTime timestamp = FileInfo(filePath).lastModified();
+ const auto se = ScriptEngine::engineForContext(ctx);
+ se->addFileLastModifiedResult(filePath, timestamp);
+ return JS_NewFloat64(ctx, timestamp.asDouble());
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_move(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsMakePath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("move expects 2 arguments"));
- }
-
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({ DubiousContext(EvalContext::PropertyEvaluation) });
- se->checkContext(QStringLiteral("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(QStringLiteral("Could not move '%1' to '%2': "
- "Source file path is a directory.")
- .arg(sourceFile, targetFile));
-
- if (Q_UNLIKELY(QFileInfo(targetFile).isDir())) {
- return context->throwError(QStringLiteral("Could not move '%1' to '%2': "
- "Destination file path is a directory.")
- .arg(sourceFile, targetFile));
- }
-
- QFile f(targetFile);
- if (overwrite && f.exists() && !f.remove())
- return context->throwError(QStringLiteral("Could not move '%1' to '%2': %3")
- .arg(sourceFile, targetFile, f.errorString()));
-
- if (QFile::exists(targetFile))
- return context->throwError(QStringLiteral("Could not move '%1' to '%2': "
- "Destination file exists.")
- .arg(sourceFile, targetFile));
+ try {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{DubiousContext(EvalContext::PropertyEvaluation)};
+ se->checkContext(QStringLiteral("File.makePath()"), dubiousContexts);
+ const auto path = getArgument<QString>(ctx, "File.makePath", argc, argv);
+ return JS_NewBool(ctx, QDir::root().mkpath(path));
+ } catch (const QString &error) { return throwError(ctx, error); }
+}
- QFile f2(sourceFile);
- if (Q_UNLIKELY(!f2.rename(targetFile)))
- return context->throwError(QStringLiteral("Could not move '%1' to '%2': %3")
- .arg(sourceFile, targetFile, f2.errorString()));
- return true;
+JSValue File::jsMove(JSContext *ctx, JSValue, int argc, JSValue *argv)
+{
+ try {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{DubiousContext(EvalContext::PropertyEvaluation)};
+ se->checkContext(QStringLiteral("File.move()"), dubiousContexts);
+
+ const auto args = getArguments<QString, QString>(ctx, "File.move", argc, argv);
+ const QString sourceFile = std::get<0>(args);
+ const QString targetFile = std::get<1>(args);
+ const auto overwrite = argc > 2 ? fromArg<bool>(ctx, "File.move", 3, argv[2]) : true;
+
+ if (Q_UNLIKELY(QFileInfo(sourceFile).isDir())) {
+ throw QStringLiteral("Could not move '%1' to '%2': "
+ "Source file path is a directory.").arg(sourceFile, targetFile);
+ }
+ if (Q_UNLIKELY(QFileInfo(targetFile).isDir())) {
+ throw QStringLiteral("Could not move '%1' to '%2': "
+ "Destination file path is a directory.")
+ .arg(sourceFile, targetFile);
+ }
+
+ QFile f(targetFile);
+ if (overwrite && f.exists() && !f.remove()) {
+ throw QStringLiteral("Could not move '%1' to '%2': %3")
+ .arg(sourceFile, targetFile, f.errorString());
+ }
+ if (QFile::exists(targetFile)) {
+ throw QStringLiteral("Could not move '%1' to '%2': "
+ "Destination file exists.").arg(sourceFile, targetFile);
+ }
+
+ QFile f2(sourceFile);
+ if (Q_UNLIKELY(!f2.rename(targetFile))) {
+ throw QStringLiteral("Could not move '%1' to '%2': %3")
+ .arg(sourceFile, targetFile, f2.errorString());
+ }
+ return JS_TRUE;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue File::js_canonicalFilePath(QScriptContext *context, QScriptEngine *engine)
+JSValue File::jsCanonicalFilePath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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();
+ try {
+ const auto path = getArgument<QString>(ctx, "File.canonicalFilePath", argc, argv);
+ return makeJsString(ctx, QFileInfo(path).canonicalFilePath());
+ } catch (const QString &error) { return throwError(ctx, error); }
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionFile(QScriptValue extensionObject)
+void initializeJsExtensionFile(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue fileObj = engine->newQMetaObject(&File::staticMetaObject,
- engine->newFunction(&File::js_ctor));
- fileObj.setProperty(QStringLiteral("copy"), engine->newFunction(File::js_copy));
- fileObj.setProperty(QStringLiteral("exists"), engine->newFunction(File::js_exists));
- fileObj.setProperty(QStringLiteral("directoryEntries"),
- engine->newFunction(File::js_directoryEntries));
- fileObj.setProperty(QStringLiteral("lastModified"), engine->newFunction(File::js_lastModified));
- fileObj.setProperty(QStringLiteral("makePath"), engine->newFunction(File::js_makePath));
- fileObj.setProperty(QStringLiteral("move"), engine->newFunction(File::js_move));
- fileObj.setProperty(QStringLiteral("remove"), engine->newFunction(File::js_remove));
- fileObj.setProperty(QStringLiteral("canonicalFilePath"),
- engine->newFunction(File::js_canonicalFilePath));
- extensionObject.setProperty(QStringLiteral("File"), fileObj);
+ qbs::Internal::File::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::File *)
-
-#include "file.moc"
diff --git a/src/lib/corelib/jsextensions/fileinfoextension.cpp b/src/lib/corelib/jsextensions/fileinfoextension.cpp
index 0f2570ed0..cfc45ca40 100644
--- a/src/lib/corelib/jsextensions/fileinfoextension.cpp
+++ b/src/lib/corelib/jsextensions/fileinfoextension.cpp
@@ -37,6 +37,8 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/fileinfo.h>
@@ -45,278 +47,268 @@
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-
#include <regex>
namespace qbs {
namespace Internal {
-class FileInfoExtension : public QObject, QScriptable
+// removes duplicate separators from the path
+static QString uniqueSeparators(QString path)
+{
+ const auto it = std::unique(path.begin(), path.end(), [](QChar c1, QChar c2) {
+ return c1 == c2 && c1 == QLatin1Char('/');
+ });
+ path.resize(int(it - path.begin()));
+ return path;
+}
+
+class FileInfoExtension : public JsExtension<FileInfoExtension>
{
- Q_OBJECT
public:
- 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_suffix(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_completeSuffix(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_canonicalPath(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_cleanPath(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);
+ static const char *name() { return "FileInfo"; }
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static JSValue jsPath(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsFileName(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsBaseName(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsSuffix(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsCompleteSuffix(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsCanonicalPath(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsCleanPath(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsCompleteBaseName(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsRelativePath(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsResolvePath(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsIsAbsolutePath(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsToWindowsSeparators(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsFromWindowsSeparators(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsToNativeSeparators(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsFromNativeSeparators(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv);
+ static JSValue jsJoinPaths(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue jsPathListSeparator(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsPathSeparator(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsExecutableSuffix(JSContext *ctx, JSValueConst, int, JSValueConst *);
};
-QScriptValue FileInfoExtension::js_ctor(QScriptContext *context, QScriptEngine *engine)
+void FileInfoExtension::setupStaticMethods(JSContext *ctx, JSValue classObj)
{
- Q_UNUSED(engine);
- return context->throwError(Tr::tr("'FileInfo' cannot be instantiated."));
+ setupMethod(ctx, classObj, StringConstants::fileInfoPath(), &FileInfoExtension::jsPath, 1);
+ setupMethod(ctx, classObj, StringConstants::fileInfoFileName(),
+ &FileInfoExtension::jsFileName, 1);
+ setupMethod(ctx, classObj, StringConstants::baseNameProperty(),
+ &FileInfoExtension::jsBaseName, 1);
+ setupMethod(ctx, classObj, "suffix", &FileInfoExtension::jsSuffix, 1);
+ setupMethod(ctx, classObj, "completeSuffix", &FileInfoExtension::jsCompleteSuffix, 1);
+ setupMethod(ctx, classObj, "canonicalPath", &FileInfoExtension::jsCanonicalPath, 1);
+ setupMethod(ctx, classObj, "cleanPath", &FileInfoExtension::jsCleanPath, 1);
+ setupMethod(ctx, classObj, StringConstants::completeBaseNameProperty(),
+ &FileInfoExtension::jsCompleteBaseName, 1);
+ setupMethod(ctx, classObj, "relativePath", &FileInfoExtension::jsRelativePath, 1);
+ setupMethod(ctx, classObj, "resolvePath", &FileInfoExtension::jsResolvePath, 1);
+ setupMethod(ctx, classObj, "isAbsolutePath", &FileInfoExtension::jsIsAbsolutePath, 1);
+ setupMethod(ctx, classObj, "toWindowsSeparators",
+ &FileInfoExtension::jsToWindowsSeparators, 1);
+ setupMethod(ctx, classObj, "fromWindowsSeparators",
+ &FileInfoExtension::jsFromWindowsSeparators, 1);
+ setupMethod(ctx, classObj, "toNativeSeparators",
+ &FileInfoExtension::jsToNativeSeparators, 1);
+ setupMethod(ctx, classObj, "fromNativeSeparators",
+ &FileInfoExtension::jsFromNativeSeparators, 1);
+ setupMethod(ctx, classObj, "joinPaths", &FileInfoExtension::jsJoinPaths, 0);
+ setupMethod(ctx, classObj, "pathListSeparator",
+ &FileInfoExtension::jsPathListSeparator, 0);
+ setupMethod(ctx, classObj, "pathSeparator", &FileInfoExtension::jsPathSeparator, 0);
+ setupMethod(ctx, classObj, "executableSuffix", &FileInfoExtension::jsExecutableSuffix, 0);
}
-QScriptValue FileInfoExtension::js_path(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsPath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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);
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.path", argc, argv);
+ HostOsInfo::HostOs hostOs = HostOsInfo::hostOs();
+ if (argc > 1) {
+ const auto osList = fromArg<QStringList>(ctx, "FileInfo.path", 2, argv[1]);
+ hostOs = osList.contains(QLatin1String("windows"))
+ ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix;
+ }
+ return makeJsString(ctx, FileInfo::path(filePath, hostOs));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_fileName(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsFileName(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.fileName", argc, argv);
+ return makeJsString(ctx, FileInfo::fileName(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_baseName(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsBaseName(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.baseName", argc, argv);
+ return makeJsString(ctx, FileInfo::baseName(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_suffix(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsSuffix(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("suffix expects 1 argument"));
- }
- return FileInfo::suffix(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.suffix", argc, argv);
+ return makeJsString(ctx, FileInfo::suffix(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_completeSuffix(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsCompleteSuffix(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("completeSuffix expects 1 argument"));
- }
- return FileInfo::completeSuffix(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.completeSuffix", argc, argv);
+ return makeJsString(ctx, FileInfo::completeSuffix(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_canonicalPath(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsCanonicalPath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("canonicalPath expects 1 argument"));
- }
- return QFileInfo(context->argument(0).toString()).canonicalFilePath();
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.canonicalPath", argc, argv);
+ return makeJsString(ctx, QFileInfo(filePath).canonicalFilePath());
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_cleanPath(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsCleanPath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("cleanPath expects 1 argument"));
- }
- return QDir::cleanPath(context->argument(0).toString());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.cleanPath", argc, argv);
+ return makeJsString(ctx, QDir::cleanPath(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_completeBaseName(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsCompleteBaseName(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.completeBaseName", argc, argv);
+ return makeJsString(ctx, FileInfo::completeBaseName(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_relativePath(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsRelativePath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- Q_UNUSED(engine);
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("relativePath expects 2 arguments"));
- }
- const QString baseDir = context->argument(0).toString();
- const QString filePath = context->argument(1).toString();
- if (!FileInfo::isAbsolute(baseDir)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("FileInfo.relativePath() expects an absolute path as "
- "its first argument, but it is '%1'.").arg(baseDir));
- }
- if (!FileInfo::isAbsolute(filePath)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("FileInfo.relativePath() expects an absolute path as "
- "its second argument, but it is '%1'.").arg(filePath));
- }
- return QDir(baseDir).relativeFilePath(filePath);
+ try {
+ const auto args = getArguments<QString, QString>(ctx, "File.relativePath", argc, argv);
+ const QString baseDir = std::get<0>(args);
+ const QString filePath = std::get<1>(args);
+ if (!FileInfo::isAbsolute(baseDir)) {
+ throw Tr::tr("FileInfo.relativePath() expects an absolute path as "
+ "its first argument, but it is '%1'.").arg(baseDir);
+ }
+ if (!FileInfo::isAbsolute(filePath)) {
+ throw Tr::tr("FileInfo.relativePath() expects an absolute path as "
+ "its second argument, but it is '%1'.").arg(filePath);
+ }
+ return makeJsString(ctx, QDir(baseDir).relativeFilePath(filePath));
+
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_resolvePath(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsResolvePath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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);
+ try {
+ const auto args = getArguments<QString, QString>(ctx, "File.resolvePath", argc, argv);
+ const QString base = std::get<0>(args);
+ const QString rel = std::get<1>(args);
+ return makeJsString(ctx, FileInfo::resolvePath(base, rel));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_isAbsolutePath(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsIsAbsolutePath(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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);
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.isAbsolutePath", argc, argv);
+ HostOsInfo::HostOs hostOs = HostOsInfo::hostOs();
+ if (argc > 1) {
+ const auto osList = fromArg<QStringList>(ctx, "FileInfo.isAbsolutePath", 2, argv[1]);
+ hostOs = osList.contains(QLatin1String("windows"))
+ ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix;
+ }
+ return JS_NewBool(ctx, FileInfo::isAbsolute(filePath, hostOs));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_toWindowsSeparators(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsToWindowsSeparators(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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('\\'));
+ try {
+ auto filePath = getArgument<QString>(ctx, "FileInfo.toWindowsSeparators", argc, argv);
+ return makeJsString(ctx, filePath.replace(QLatin1Char('/'), QLatin1Char('\\')));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_fromWindowsSeparators(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsFromWindowsSeparators(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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('/'));
+ try {
+ auto filePath = getArgument<QString>(ctx, "FileInfo.fromWindowsSeparators", argc, argv);
+ return makeJsString(ctx, filePath.replace(QLatin1Char('\\'), QLatin1Char('/')));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_toNativeSeparators(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsToNativeSeparators(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.toWindowsSeparators", argc, argv);
+ return makeJsString(ctx, QDir::toNativeSeparators(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_fromNativeSeparators(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsFromNativeSeparators(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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());
+ try {
+ const auto filePath = getArgument<QString>(ctx, "FileInfo.fromWindowsSeparators",
+ argc, argv);
+ return makeJsString(ctx, QDir::fromNativeSeparators(filePath));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue FileInfoExtension::js_joinPaths(QScriptContext *context, QScriptEngine *engine)
+JSValue FileInfoExtension::jsJoinPaths(JSContext *ctx, JSValue, int argc, JSValue *argv)
{
- 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.push_back(arg);
+ try {
+ QStringList paths;
+ for (int i = 0; i < argc; ++i) {
+ const auto value = fromArg<QString>(ctx, "FileInfo.joinPaths", i + 1, argv[i]);
+ if (!value.isEmpty())
+ paths.push_back(value);
}
- }
- return engine->toScriptValue(QString::fromStdString(
- std::regex_replace(paths.join(QLatin1Char('/')).toStdString(),
- std::regex("/{2,}"), std::string("/"))));
+ return makeJsString(ctx, uniqueSeparators(paths.join(QLatin1Char('/'))));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-} // namespace Internal
-} // namespace qbs
+JSValue FileInfoExtension::jsPathListSeparator(JSContext *ctx, JSValue, int, JSValue *)
+{
+ return makeJsString(ctx, QString(HostOsInfo::pathListSeparator()));
+}
+
+JSValue FileInfoExtension::jsPathSeparator(JSContext *ctx, JSValue, int, JSValue *)
+{
+ return makeJsString(ctx, QString(HostOsInfo::pathSeparator()));
+}
-void initializeJsExtensionFileInfo(QScriptValue extensionObject)
+JSValue FileInfoExtension::jsExecutableSuffix(JSContext *ctx, JSValue, int, JSValue *)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue fileInfoObj = engine->newQMetaObject(&FileInfoExtension::staticMetaObject,
- engine->newFunction(&FileInfoExtension::js_ctor));
- fileInfoObj.setProperty(StringConstants::fileInfoPath(),
- engine->newFunction(FileInfoExtension::js_path));
- fileInfoObj.setProperty(StringConstants::fileInfoFileName(),
- engine->newFunction(FileInfoExtension::js_fileName));
- fileInfoObj.setProperty(StringConstants::baseNameProperty(),
- engine->newFunction(FileInfoExtension::js_baseName));
- fileInfoObj.setProperty(QStringLiteral("suffix"),
- engine->newFunction(FileInfoExtension::js_suffix));
- fileInfoObj.setProperty(QStringLiteral("completeSuffix"),
- engine->newFunction(FileInfoExtension::js_completeSuffix));
- fileInfoObj.setProperty(QStringLiteral("canonicalPath"),
- engine->newFunction(FileInfoExtension::js_canonicalPath));
- fileInfoObj.setProperty(QStringLiteral("cleanPath"),
- engine->newFunction(FileInfoExtension::js_cleanPath));
- fileInfoObj.setProperty(StringConstants::completeBaseNameProperty(),
- engine->newFunction(FileInfoExtension::js_completeBaseName));
- fileInfoObj.setProperty(QStringLiteral("relativePath"),
- engine->newFunction(FileInfoExtension::js_relativePath));
- fileInfoObj.setProperty(QStringLiteral("resolvePath"),
- engine->newFunction(FileInfoExtension::js_resolvePath));
- fileInfoObj.setProperty(QStringLiteral("isAbsolutePath"),
- engine->newFunction(FileInfoExtension::js_isAbsolutePath));
- fileInfoObj.setProperty(QStringLiteral("toWindowsSeparators"),
- engine->newFunction(FileInfoExtension::js_toWindowsSeparators));
- fileInfoObj.setProperty(QStringLiteral("fromWindowsSeparators"),
- engine->newFunction(FileInfoExtension::js_fromWindowsSeparators));
- fileInfoObj.setProperty(QStringLiteral("toNativeSeparators"),
- engine->newFunction(FileInfoExtension::js_toNativeSeparators));
- fileInfoObj.setProperty(QStringLiteral("fromNativeSeparators"),
- engine->newFunction(FileInfoExtension::js_fromNativeSeparators));
- fileInfoObj.setProperty(QStringLiteral("joinPaths"),
- engine->newFunction(FileInfoExtension::js_joinPaths));
- extensionObject.setProperty(QStringLiteral("FileInfo"), fileInfoObj);
+ static QString executableSuffix = HostOsInfo::isWindowsHost() ?
+ QLatin1String(QBS_HOST_EXE_SUFFIX) : QString();
+ return makeJsString(ctx, executableSuffix);
}
-Q_DECLARE_METATYPE(qbs::Internal::FileInfoExtension *)
+} // namespace Internal
+} // namespace qbs
-#include "fileinfoextension.moc"
+void initializeJsExtensionFileInfo(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
+{
+ qbs::Internal::FileInfoExtension::registerClass(engine, extensionObject);
+}
diff --git a/src/lib/corelib/jsextensions/host.cpp b/src/lib/corelib/jsextensions/host.cpp
new file mode 100644
index 000000000..24d40fe6a
--- /dev/null
+++ b/src/lib/corelib/jsextensions/host.cpp
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 Raphaël Cotty <raphael.cotty@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "jsextension.h"
+
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+#include <tools/fileinfo.h>
+
+#include <QtCore/qdir.h>
+#include <QtCore/qfileinfo.h>
+
+namespace qbs {
+namespace Internal {
+
+class Host : public JsExtension<Host>
+{
+public:
+ static const char *name() { return "Host"; }
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static JSValue jsArchitecture(JSContext *ctx, JSValueConst, int, JSValueConst *) {
+ return makeJsString(ctx, HostOsInfo::hostOSArchitecture());
+ }
+ static JSValue jsOs(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsPlatform(JSContext *ctx, JSValueConst, int, JSValueConst *) {
+ return makeJsString(ctx, HostOsInfo::hostOSIdentifier());
+ }
+ static JSValue jsOsVersion(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsOsBuildVersion(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsOsVersionParts(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsOsVersionMajor(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsOsVersionMinor(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsOsVersionPatch(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue jsNullDevice(JSContext *ctx, JSValueConst, int, JSValueConst *);
+};
+
+static QStringList osList()
+{
+ QStringList list;
+ for (const auto &s : HostOsInfo::canonicalOSIdentifiers(HostOsInfo::hostOSIdentifier()))
+ list.push_back(s);
+ return list;
+}
+
+void Host::setupStaticMethods(JSContext *ctx, JSValue classObj)
+{
+ setupMethod(ctx, classObj, "architecture", &Host::jsArchitecture, 0);
+ setupMethod(ctx, classObj, "os", &Host::jsOs, 0);
+ setupMethod(ctx, classObj, "platform", &Host::jsPlatform, 0);
+ setupMethod(ctx, classObj, "osVersion", &Host::jsOsVersion, 0);
+ setupMethod(ctx, classObj, "osBuildVersion", &Host::jsOsBuildVersion, 0);
+ setupMethod(ctx, classObj, "osVersionParts", &Host::jsOsVersionParts, 0);
+ setupMethod(ctx, classObj, "osVersionMajor", &Host::jsOsVersionMajor, 0);
+ setupMethod(ctx, classObj, "osVersionMinor", &Host::jsOsVersionMinor, 0);
+ setupMethod(ctx, classObj, "osVersionPatch", &Host::jsOsVersionPatch, 0);
+ setupMethod(ctx, classObj, "nullDevice", &Host::jsNullDevice, 0);
+}
+
+JSValue Host::jsOs(JSContext *ctx, JSValue, int, JSValue *)
+{
+ static QStringList host = osList();
+ return makeJsStringList(ctx, host);
+}
+
+JSValue Host::jsOsVersion(JSContext *ctx, JSValue, int, JSValue *)
+{
+ static QString osVersion = HostOsInfo::hostOsVersion().toString();
+ return osVersion.isNull() ? JS_UNDEFINED : makeJsString(ctx, osVersion);
+}
+
+JSValue Host::jsOsBuildVersion(JSContext *ctx, JSValue, int, JSValue *)
+{
+ static QString osBuildVersion = HostOsInfo::hostOsBuildVersion();
+ return osBuildVersion.isNull() ? JS_UNDEFINED : makeJsString(ctx, osBuildVersion);
+}
+
+JSValue Host::jsOsVersionParts(JSContext *ctx, JSValue, int, JSValue *)
+{
+ static QStringList osVersionParts = HostOsInfo::hostOsVersion().toString().split(
+ QStringLiteral("."));
+ return makeJsStringList(ctx, osVersionParts);
+}
+
+JSValue Host::jsOsVersionMajor(JSContext *ctx, JSValue, int, JSValue *)
+{
+ static int osVersionMajor = HostOsInfo::hostOsVersion().majorVersion();
+ return JS_NewInt32(ctx, osVersionMajor);
+}
+
+JSValue Host::jsOsVersionMinor(JSContext *ctx, JSValue, int, JSValue *)
+{
+ static int osVersionMinor = HostOsInfo::hostOsVersion().minorVersion();
+ return JS_NewInt32(ctx, osVersionMinor);
+}
+
+JSValue Host::jsOsVersionPatch(JSContext *ctx, JSValue, int, JSValue *)
+{
+ static int osVersionPatch = HostOsInfo::hostOsVersion().patchLevel();
+ return JS_NewInt32(ctx, osVersionPatch);
+}
+
+JSValue Host::jsNullDevice(JSContext *ctx, JSValue, int, JSValue *)
+{
+ static QString nullDevice = HostOsInfo::isWindowsHost() ? QStringLiteral("NUL") :
+ QStringLiteral("/dev/null");
+ return makeJsString(ctx, nullDevice);
+}
+
+} // namespace Internal
+} // namespace qbs
+
+void initializeJsExtensionHost(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
+{
+ qbs::Internal::Host::registerClass(engine, extensionObject);
+}
diff --git a/src/lib/corelib/jsextensions/jsextension.h b/src/lib/corelib/jsextensions/jsextension.h
new file mode 100644
index 000000000..d34fb2b83
--- /dev/null
+++ b/src/lib/corelib/jsextensions/jsextension.h
@@ -0,0 +1,339 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+
+#include <optional>
+#include <tuple>
+#include <utility>
+
+namespace qbs::Internal {
+
+template<typename ...T> struct PackHelper {};
+template<typename T> struct FromArgHelper;
+template <typename T> struct FunctionTrait;
+
+#define DECLARE_ENUM(ctx, obj, enumVal) \
+ JS_SetPropertyStr((ctx), (obj), #enumVal, JS_NewInt32((ctx), (enumVal)))
+
+template<class Derived> class JsExtension
+{
+#define DEFINE_JS_FORWARDER(name, func, text) \
+ static JSValue name(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) \
+ { \
+ return jsForward(func, text, ctx, this_val, argc, argv); \
+ }
+
+#define DEFINE_JS_FORWARDER_QUAL(klass, name, func, text) \
+ static JSValue name(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) \
+ { \
+ return klass::jsForward(func, text, ctx, this_val, argc, argv); \
+ }
+
+public:
+ virtual ~JsExtension() = default;
+
+ template<typename ...Args > static JSValue createObject(JSContext *ctx, Args... args)
+ {
+ ScopedJsValue obj(ctx, JS_NewObjectClass(ctx, classId(ctx)));
+ JS_SetOpaque(obj, new Derived(ctx, args...));
+ Derived::setupMethods(ctx, obj);
+ return obj.release();
+ }
+
+ static JSValue createObjectDirect(JSContext *ctx, void *obj)
+ {
+ JSValue jsObj = JS_NewObjectClass(ctx, classId(ctx));
+ attachPointerTo(jsObj, obj);
+ Derived::setupMethods(ctx, jsObj);
+ return jsObj;
+ }
+
+ static Derived *fromJsObject(JSContext *ctx, JSValue obj)
+ {
+ return attachedPointer<Derived>(obj, classId(ctx));
+ }
+
+ static void registerClass(ScriptEngine *engine, JSValue extensionObject)
+ {
+ if (const JSValue cachedValue = engine->getInternalExtension(Derived::name());
+ !JS_IsUndefined(cachedValue)) {
+ JS_SetPropertyStr(engine->context(), extensionObject, Derived::name(), cachedValue);
+ return;
+ }
+ engine->registerClass(Derived::name(), &Derived::ctor, &finalizer, extensionObject);
+ const ScopedJsValue classObj(
+ engine->context(),
+ JS_GetPropertyStr(engine->context(), extensionObject, Derived::name()));
+ Derived::setupStaticMethods(engine->context(), classObj);
+ Derived::declareEnums(engine->context(), classObj);
+ }
+
+ // obj is either class (for "static" methods) or class instance
+ template<typename Func> static void setupMethod(JSContext *ctx, JSValue obj, const char *name,
+ Func func, int argc)
+ {
+ JS_DefinePropertyValueStr(ctx, obj, name, JS_NewCFunction(ctx, func, name, argc), 0);
+ }
+
+ template<typename Func> static void setupMethod(JSContext *ctx, JSValue obj,
+ const QString &name, Func func, int argc)
+ {
+ setupMethod(ctx, obj, name.toUtf8().constData(), func, argc);
+ }
+
+ template<typename T> static T fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v)
+ {
+ return FromArgHelper<T>::fromArg(ctx, funcName, pos, v);
+ }
+
+ template <typename Tuple, std::size_t... I>
+ static void assignToTuple(Tuple &tuple, std::index_sequence<I...>, JSContext *ctx, const char *funcName, JSValueConst *argv) {
+ Q_UNUSED(ctx)
+ Q_UNUSED(funcName)
+ Q_UNUSED(argv)
+ ((std::get<I>(tuple) = fromArg<std::tuple_element_t<I, Tuple>>(ctx, funcName, I + 1, argv[I])), ...);
+ }
+
+ template<typename ...Args> static std::tuple<Args...> getArguments(JSContext *ctx,
+ const char *funcName, int argc, JSValueConst *argv)
+ {
+ constexpr int expectedArgc = sizeof ...(Args);
+ if (argc < expectedArgc) {
+ throw Tr::tr("%1 requires %d arguments.")
+ .arg(QLatin1String(funcName)).arg(expectedArgc);
+ }
+ std::tuple<std::remove_const_t<std::remove_reference_t<Args>>...> values;
+ assignToTuple(values, std::make_index_sequence<std::tuple_size<decltype(values)>::value>(),
+ ctx, funcName, argv);
+ return values;
+ }
+ template<typename ...Args> static std::tuple<Args...> getArgumentsHelper(PackHelper<Args...>, JSContext *ctx,
+ const char *funcName, int argc, JSValueConst *argv)
+ {
+ return getArguments<Args...>(ctx, funcName, argc, argv);
+ }
+
+ template<typename T> static T getArgument(JSContext *ctx, const char *funcName,
+ int argc, JSValueConst *argv)
+ {
+ return std::get<0>(getArguments<T>(ctx, funcName, argc, argv));
+ }
+
+ template <typename Func, typename Tuple, std::size_t... I>
+ static void jsForwardHelperVoid(Derived *obj, Func func, const Tuple &tuple, std::index_sequence<I...>) {
+ (obj->*func)(std::get<I>(tuple)...);
+ }
+ template <typename Func, typename Ret, typename Tuple, std::size_t... I>
+ static Ret jsForwardHelperRet(Derived *obj, Func &func, Tuple const& tuple, std::index_sequence<I...>) {
+ return (obj->*func)(std::get<I>(tuple)...);
+ }
+
+ static JSValue toJsValue(JSContext *ctx, const QString &s) { return makeJsString(ctx, s); }
+ static JSValue toJsValue(JSContext *ctx, bool v) { return JS_NewBool(ctx, v); }
+ static JSValue toJsValue(JSContext *ctx, qint32 v) { return JS_NewInt32(ctx, v); }
+ static JSValue toJsValue(JSContext *ctx, qint64 v) { return JS_NewInt64(ctx, v); }
+ static JSValue toJsValue(JSContext *ctx, const QVariant &v) { return makeJsVariant(ctx, v); }
+ static JSValue toJsValue(JSContext *ctx, const QByteArray &data)
+ {
+ const JSValue array = JS_NewArray(ctx);
+ for (int i = 0; i < data.size(); ++i) {
+ JS_SetPropertyUint32(ctx, array, i,
+ JS_NewUint32(ctx, static_cast<unsigned char>(data.at(i))));
+ }
+ return array;
+ }
+ static JSValue toJsValue(JSContext *ctx, const QVariantMap &m)
+ {
+ return makeJsVariantMap(ctx, m);
+ }
+ template<typename T> static JSValue toJsValue(JSContext *ctx, const std::optional<T> &v)
+ {
+ if (!v)
+ return JS_UNDEFINED;
+ return makeJsString(ctx, *v);
+ }
+
+ template<typename Func>
+ static JSValue jsForward(Func func, const char *name, JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+ try {
+ using Ret = typename FunctionTrait<Func>::Ret;
+ using Args = typename FunctionTrait<Func>::Args;
+ const auto args = getArgumentsHelper(Args(), ctx, name, argc, argv);
+ const auto obj = fromJsObject(ctx, this_val);
+ if constexpr (std::is_same_v<Ret, void>) {
+ jsForwardHelperVoid(obj, func, args, std::make_index_sequence<std::tuple_size<decltype(args)>::value>());
+ return JS_UNDEFINED;
+ } else {
+ return toJsValue(ctx, jsForwardHelperRet<Func, Ret>(obj, func, args, std::make_index_sequence<std::tuple_size_v<decltype(args)>>()));
+ }
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
+ }
+
+ static void setupMethods(JSContext *, JSValue) {}
+ static void setupStaticMethods(JSContext *, JSValue) {}
+ static void declareEnums(JSContext *, JSValue) {}
+
+private:
+ static void finalizer(JSRuntime *rt, JSValue val)
+ {
+ delete attachedPointer<Derived>(val, classId(rt));
+ }
+
+ static JSClassID classId(JSContext *ctx)
+ {
+ return ScriptEngine::engineForContext(ctx)->getClassId(Derived::name());
+ }
+
+ static JSClassID classId(JSRuntime *rt)
+ {
+ return ScriptEngine::engineForRuntime(rt)->getClassId(Derived::name());
+ }
+
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int)
+ {
+ return throwError(ctx, Tr::tr("'%1' cannot be instantiated.")
+ .arg(QLatin1String(Derived::name())));
+ }
+};
+
+template<> struct FromArgHelper<bool> {
+ static bool fromArg(JSContext *ctx, const char *, int, JSValue v) {
+ return JS_ToBool(ctx, v);
+ }
+};
+template<> struct FromArgHelper<qint32> {
+ static qint32 fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ int32_t val;
+ if (JS_ToInt32(ctx, &val, v) < 0) {
+ throw Tr::tr("%1 requires an integer as argument %2")
+ .arg(QLatin1String(funcName)).arg(pos);
+ }
+ return val;
+ }
+};
+template<> struct FromArgHelper<qint64> {
+ static qint64 fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ int64_t val;
+ if (JS_ToInt64(ctx, &val, v) < 0) {
+ throw Tr::tr("%1 requires an integer as argument %2")
+ .arg(QLatin1String(funcName)).arg(pos);
+ }
+ return val;
+ }
+};
+template<> struct FromArgHelper<QByteArray> {
+ static QByteArray fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ const auto throwError = [&] {
+ throw Tr::tr("%1 requires an array of bytes as argument %2")
+ .arg(QLatin1String(funcName)).arg(pos);
+ };
+ if (!JS_IsArray(ctx, v))
+ throwError();
+ QByteArray data;
+ data.resize(getJsIntProperty(ctx, v, QLatin1String("length")));
+ for (int i = 0; i < data.size(); ++i) {
+ const JSValue jsNumber = JS_GetPropertyUint32(ctx, v, i);
+ if (JS_VALUE_GET_TAG(jsNumber) != JS_TAG_INT)
+ throwError();
+ uint32_t n;
+ JS_ToUint32(ctx, &n, jsNumber);
+ if (n > 0xff)
+ throwError();
+ data[i] = n;
+ }
+ return data;
+ }
+};
+template<> struct FromArgHelper<QString> {
+ static QString fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ if (JS_IsUndefined(v))
+ return {};
+ const ScopedJsValue stringified(ctx, JS_ToString(ctx, v));
+ if (!JS_IsString(stringified)) {
+ throw Tr::tr("%1 requires a string as argument %2")
+ .arg(QLatin1String(funcName)).arg(pos);
+ }
+ return getJsString(ctx, stringified);
+ }
+};
+template<> struct FromArgHelper<QStringList> {
+ static QStringList fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ if (!JS_IsArray(ctx, v)) {
+ throw Tr::tr("%1 requires an array of strings as argument %2")
+ .arg(QLatin1String(funcName)).arg(pos);
+ }
+ return getJsStringList(ctx, v);
+ }
+};
+template<> struct FromArgHelper<QVariant> {
+ static QVariant fromArg(JSContext *ctx, const char *, int, JSValue v) {
+ return getJsVariant(ctx, v);
+ }
+};
+template<class C> struct FromArgHelper<C *> {
+ static C *fromArg(JSContext *ctx, const char *funcName, int pos, JSValue v) {
+ C * const obj = JsExtension<C>::fromJsObject(ctx, v);
+ if (!obj) {
+ throw Tr::tr("Argument %2 has wrong type in call to %1")
+ .arg(QLatin1String(funcName)).arg(pos);
+ }
+ return obj;
+ }
+};
+
+template <typename R, typename C, typename... A>
+struct FunctionTrait<R(C::*)(A... )> {
+ using Ret = R;
+ using Args = PackHelper<std::remove_reference_t<A>...>;
+};
+template <typename R, typename C, typename... A>
+struct FunctionTrait<R(C::*)(A... ) const> {
+ using Ret = R;
+ using Args = PackHelper<std::remove_reference_t<A>...>;
+};
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/jsextensions/jsextensions.cpp b/src/lib/corelib/jsextensions/jsextensions.cpp
index e5fbd3de8..66d7ebbd6 100644
--- a/src/lib/corelib/jsextensions/jsextensions.cpp
+++ b/src/lib/corelib/jsextensions/jsextensions.cpp
@@ -39,17 +39,19 @@
#include "jsextensions.h"
+#include <language/scriptengine.h>
+#include <tools/scripttools.h>
+
#include <QtCore/qmap.h>
-#include <QtScript/qscriptengine.h>
#include <utility>
-using InitializerMap = QMap<QString, void (*)(QScriptValue)>;
+using InitializerMap = QMap<QString, void (*)(qbs::Internal::ScriptEngine *, JSValue)>;
static InitializerMap setupMap()
{
#define INITIALIZER_NAME(name) initializeJsExtension##name
#define ADD_JS_EXTENSION(name) \
- void INITIALIZER_NAME(name)(QScriptValue); \
+ void INITIALIZER_NAME(name)(qbs::Internal::ScriptEngine *, JSValue); \
map.insert(QStringLiteral(#name), &INITIALIZER_NAME(name))
InitializerMap map;
@@ -57,6 +59,8 @@ static InitializerMap setupMap()
ADD_JS_EXTENSION(Environment);
ADD_JS_EXTENSION(File);
ADD_JS_EXTENSION(FileInfo);
+ ADD_JS_EXTENSION(Host);
+ ADD_JS_EXTENSION(PkgConfig);
ADD_JS_EXTENSION(Process);
ADD_JS_EXTENSION(PropertyList);
ADD_JS_EXTENSION(TemporaryDir);
@@ -75,20 +79,21 @@ static InitializerMap &initializers()
return theMap;
}
-void JsExtensions::setupExtensions(const QStringList &names, QScriptValue scope)
+void JsExtensions::setupExtensions(ScriptEngine *engine, const QStringList &names,
+ const JSValue &scope)
{
for (const QString &name : names)
- initializers().value(name)(scope);
+ initializers().value(name)(engine, scope);
}
-QScriptValue JsExtensions::loadExtension(QScriptEngine *engine, const QString &name)
+JSValue JsExtensions::loadExtension(ScriptEngine *engine, const QString &name)
{
if (!hasExtension(name))
return {};
- QScriptValue extensionObj = engine->newObject();
- initializers().value(name)(extensionObj);
- return extensionObj.property(name);
+ ScopedJsValue extensionObj(engine->context(), engine->newObject());
+ initializers().value(name)(engine, extensionObj);
+ return getJsProperty(engine->context(), extensionObj, name);
}
bool JsExtensions::hasExtension(const QString &name)
diff --git a/src/lib/corelib/jsextensions/jsextensions.h b/src/lib/corelib/jsextensions/jsextensions.h
index 8977dbe8a..2602679b5 100644
--- a/src/lib/corelib/jsextensions/jsextensions.h
+++ b/src/lib/corelib/jsextensions/jsextensions.h
@@ -40,22 +40,20 @@
#ifndef QBS_JSEXTENSIONS_H
#define QBS_JSEXTENSIONS_H
-#include <QtCore/qhash.h>
-#include <QtCore/qstringlist.h>
+#include <quickjs.h>
-QT_BEGIN_NAMESPACE
-class QScriptEngine;
-class QScriptValue;
-QT_END_NAMESPACE
+#include <QtCore/qstringlist.h>
namespace qbs {
namespace Internal {
+class ScriptEngine;
class JsExtensions
{
public:
- static void setupExtensions(const QStringList &names, QScriptValue scope);
- static QScriptValue loadExtension(QScriptEngine *engine, const QString &name);
+ static void setupExtensions(ScriptEngine *engine, const QStringList &names,
+ const JSValue &scope);
+ static JSValue loadExtension(ScriptEngine *engine, const QString &name);
static bool hasExtension(const QString &name);
static QStringList extensionNames();
};
diff --git a/src/lib/corelib/jsextensions/jsextensions.pri b/src/lib/corelib/jsextensions/jsextensions.pri
deleted file mode 100644
index 2bffc9914..000000000
--- a/src/lib/corelib/jsextensions/jsextensions.pri
+++ /dev/null
@@ -1,26 +0,0 @@
-QT += xml
-
-HEADERS += \
- $$PWD/moduleproperties.h \
- $$PWD/jsextensions.h
-
-SOURCES += \
- $$PWD/environmentextension.cpp \
- $$PWD/file.cpp \
- $$PWD/fileinfoextension.cpp \
- $$PWD/temporarydir.cpp \
- $$PWD/textfile.cpp \
- $$PWD/binaryfile.cpp \
- $$PWD/process.cpp \
- $$PWD/moduleproperties.cpp \
- $$PWD/domxml.cpp \
- $$PWD/jsextensions.cpp \
- $$PWD/utilitiesextension.cpp
-
-darwin {
- HEADERS += $$PWD/propertylistutils.h
- SOURCES += $$PWD/propertylist.mm $$PWD/propertylistutils.mm
- LIBS += -framework Foundation
-} else {
- SOURCES += $$PWD/propertylist.cpp
-}
diff --git a/src/lib/corelib/jsextensions/moduleproperties.cpp b/src/lib/corelib/jsextensions/moduleproperties.cpp
index c0fd8ca84..2c73f2c53 100644
--- a/src/lib/corelib/jsextensions/moduleproperties.cpp
+++ b/src/lib/corelib/jsextensions/moduleproperties.cpp
@@ -41,8 +41,8 @@
#include <buildgraph/artifact.h>
#include <buildgraph/artifactsscriptvalue.h>
+#include <buildgraph/buildgraph.h>
#include <buildgraph/dependencyparametersscriptvalue.h>
-#include <buildgraph/scriptclasspropertyiterator.h>
#include <language/language.h>
#include <language/propertymapinternal.h>
#include <language/qualifiedid.h>
@@ -53,28 +53,21 @@
#include <tools/qttools.h>
#include <tools/stlutils.h>
#include <tools/stringconstants.h>
-
-#include <QtScript/qscriptclass.h>
+#include <utility>
namespace qbs {
namespace Internal {
-QScriptValue getDataForModuleScriptValue(QScriptEngine *engine, const ResolvedProduct *product,
- const Artifact *artifact, const ResolvedModule *module)
+JSValue createDataForModuleScriptValue(ScriptEngine *engine, const Artifact *artifact)
{
- QScriptValue data = engine->newObject();
- data.setProperty(ModuleNameKey, module->name);
- QVariant v;
- v.setValue<quintptr>(reinterpret_cast<quintptr>(product));
- data.setProperty(ProductPtrKey, engine->newVariant(v));
- v.setValue<quintptr>(reinterpret_cast<quintptr>(artifact));
- data.setProperty(ArtifactPtrKey, engine->newVariant(v));
+ JSValue data = JS_NewObjectClass(engine->context(), engine->dataWithPtrClass());
+ attachPointerTo(data, artifact);
return data;
}
-static QScriptValue getModuleProperty(const ResolvedProduct *product, const Artifact *artifact,
- ScriptEngine *engine, const QString &moduleName,
- const QString &propertyName, bool *isPresent = nullptr)
+static JSValue getModuleProperty(const ResolvedProduct *product, const Artifact *artifact,
+ ScriptEngine *engine, const QString &moduleName,
+ const QString &propertyName, bool *isPresent = nullptr)
{
const PropertyMapConstPtr &properties = artifact ? artifact->properties
: product->moduleProperties;
@@ -84,7 +77,7 @@ static QScriptValue getModuleProperty(const ResolvedProduct *product, const Arti
if (!value.isValid()) {
value = properties->moduleProperty(moduleName, propertyName, isPresent);
- // Cache the variant value. We must not cache the QScriptValue here, because it's a
+ // Cache the variant value. We must not cache the script value here, because it's a
// reference and the user might change the actual object.
if (engine->isPropertyCacheEnabled())
engine->addToPropertyCache(moduleName, propertyName, properties, value);
@@ -102,111 +95,101 @@ static QScriptValue getModuleProperty(const ResolvedProduct *product, const Arti
return engine->toScriptValue(value);
}
-class ModulePropertyScriptClass : public QScriptClass
-{
-public:
- ModulePropertyScriptClass(QScriptEngine *engine)
- : QScriptClass(engine)
- {
- }
-
-private:
- QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name,
- QueryFlags flags, uint *id) override
- {
- Q_UNUSED(flags);
- Q_UNUSED(id);
-
- if (name == StringConstants::dependenciesProperty()
- || name == StringConstants::artifactsProperty()) {
- // The prototype is not backed by a QScriptClass.
- m_result = object.prototype().property(name);
- return HandlesReadAccess;
- }
-
- if (name == StringConstants::parametersProperty()) {
- m_result = object.data().property(DependencyParametersKey);
- return HandlesReadAccess;
- }
+struct ModuleData {
+ JSValue dependencyParameters = JS_UNDEFINED;
+ const Artifact *artifact = nullptr;
+};
- setup(object);
- QBS_ASSERT(m_product, return {});
- bool isPresent;
- m_result = getModuleProperty(m_product, m_artifact, static_cast<ScriptEngine *>(engine()),
- m_moduleName, name, &isPresent);
+ModuleData getModuleData(JSContext *ctx, JSValue obj)
+{
+ const ScopedJsValue jsData(ctx, getJsProperty(ctx, obj,
+ StringConstants::dataPropertyInternal()));
+ ModuleData data;
+ data.dependencyParameters = JS_GetPropertyUint32(ctx, jsData, DependencyParametersKey);
+ data.artifact = attachedPointer<Artifact>(
+ jsData, ScriptEngine::engineForContext(ctx)->dataWithPtrClass());
+ return data;
+}
- // It is important that we reject unknown property names. Otherwise QtScript will forward
- // *everything* to us, including built-in stuff like the hasOwnProperty function.
- return isPresent ? HandlesReadAccess : QueryFlags();
+static int getModulePropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj)
+{
+ const auto engine = ScriptEngine::engineForContext(ctx);
+ const ModuleData data = getModuleData(ctx, obj);
+ const auto module = attachedPointer<ResolvedModule>(obj, engine->modulePropertyScriptClass());
+ QBS_ASSERT(module, return -1);
+
+ const PropertyMapInternal *propertyMap;
+ QStringList additionalProperties;
+ if (data.artifact) {
+ propertyMap = data.artifact->properties.get();
+ } else {
+ propertyMap = module->product->moduleProperties.get();
+ if (JS_IsObject(data.dependencyParameters))
+ additionalProperties.push_back(StringConstants::parametersProperty());
}
+ JS_FreeValue(ctx, data.dependencyParameters);
+ getPropertyNames(ctx, ptab, plen, propertyMap->value().value(module->name).toMap(),
+ additionalProperties, engine->baseModuleScriptValue(module));
+ return 0;
+}
- QScriptValue property(const QScriptValue &, const QScriptString &, uint) override
- {
- return m_result;
+static int getModuleProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValueConst obj,
+ JSAtom prop)
+{
+ if (desc) {
+ desc->getter = desc->setter = desc->value = JS_UNDEFINED;
+ desc->flags = JS_PROP_ENUMERABLE;
}
-
- QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override
- {
- setup(object);
- QBS_ASSERT(m_artifact || m_product, return nullptr);
- const PropertyMapInternal *propertyMap;
- std::vector<QString> additionalProperties({StringConstants::artifactsProperty(),
- StringConstants::dependenciesProperty()});
- if (m_artifact) {
- propertyMap = m_artifact->properties.get();
- } else {
- propertyMap = m_product->moduleProperties.get();
- if (object.data().property(DependencyParametersKey).isValid())
- additionalProperties.push_back(StringConstants::parametersProperty());
- }
- return new ScriptClassPropertyIterator(object,
- propertyMap->value().value(m_moduleName).toMap(),
- additionalProperties);
+ const auto engine = ScriptEngine::engineForContext(ctx);
+ const QString name = getJsString(ctx, prop);
+ const ModuleData data = getModuleData(ctx, obj);
+ const auto module = attachedPointer<ResolvedModule>(obj, engine->modulePropertyScriptClass());
+ QBS_ASSERT(module, return -1);
+
+ ScopedJsValue parametersMgr(ctx, data.dependencyParameters);
+ if (name == StringConstants::parametersProperty()) {
+ if (desc)
+ desc->value = parametersMgr.release();
+ return 1;
}
- void setup(const QScriptValue &object)
- {
- if (m_lastObjectId != object.objectId()) {
- m_lastObjectId = object.objectId();
- const QScriptValue data = object.data();
- QBS_ASSERT(data.isValid(), return);
- m_moduleName = data.property(ModuleNameKey).toString();
- m_product = reinterpret_cast<const ResolvedProduct *>(
- data.property(ProductPtrKey).toVariant().value<quintptr>());
- m_artifact = reinterpret_cast<const Artifact *>(
- data.property(ArtifactPtrKey).toVariant().value<quintptr>());
- }
+ bool isPresent;
+ JSValue value = getModuleProperty(
+ module->product, data.artifact, ScriptEngine::engineForContext(ctx),
+ module->name, name, &isPresent);
+ if (isPresent) {
+ if (desc)
+ desc->value = value;
+ return 1;
}
- qint64 m_lastObjectId = 0;
- QString m_moduleName;
- const ResolvedProduct *m_product = nullptr;
- const Artifact *m_artifact = nullptr;
- QScriptValue m_result;
-};
-
-static QString ptrKey() { return QStringLiteral("__internalPtr"); }
-static QString typeKey() { return QStringLiteral("__type"); }
-static QString artifactType() { return QStringLiteral("artifact"); }
+ ScopedJsValue v(ctx, JS_GetProperty(ctx, engine->baseModuleScriptValue(module), prop));
+ const int ret = JS_IsUndefined(v) ? 0 : 1;
+ if (desc)
+ desc->value = v.release();
+ return ret;
+}
-static QScriptValue js_moduleDependencies(QScriptContext *, ScriptEngine *engine,
- const ResolvedModule *module)
+static JSValue js_moduleDependencies(JSContext *ctx, JSValueConst this_val, int , JSValueConst *)
{
- QScriptValue result = engine->newArray();
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const auto module = attachedPointer<ResolvedModule>(this_val, engine->dataWithPtrClass());
+ JSValue result = JS_NewArray(engine->context());
quint32 idx = 0;
- for (const QString &depName : qAsConst(module->moduleDependencies)) {
- for (const ResolvedModuleConstPtr &dep : module->product->modules) {
+ for (const QString &depName : std::as_const(module->moduleDependencies)) {
+ for (const auto &dep : module->product->modules) {
if (dep->name != depName)
continue;
- QScriptValue obj = engine->newObject(engine->modulePropertyScriptClass());
- obj.setPrototype(engine->moduleScriptValuePrototype(dep.get()));
- QScriptValue data = getDataForModuleScriptValue(engine, module->product, nullptr,
- dep.get());
+ JSValue obj = JS_NewObjectClass(ctx, engine->modulePropertyScriptClass());
+ attachPointerTo(obj, dep.get());
+ JSValue data = createDataForModuleScriptValue(engine, nullptr);
const QVariantMap &params = module->product->moduleParameters.value(dep);
- data.setProperty(DependencyParametersKey, dependencyParametersValue(
- module->product->uniqueName(), dep->name, params, engine));
- obj.setData(data);
- result.setProperty(idx++, obj);
+ JS_SetPropertyUint32(ctx, data, DependencyParametersKey,
+ dependencyParametersValue(module->product->uniqueName(),
+ dep->name, params, engine));
+ defineJsProperty(ctx, obj, StringConstants::dataPropertyInternal(), data);
+ JS_SetPropertyUint32(ctx, result, idx++, obj);
break;
}
}
@@ -214,35 +197,84 @@ static QScriptValue js_moduleDependencies(QScriptContext *, ScriptEngine *engine
return result;
}
-static QScriptValue setupModuleScriptValue(ScriptEngine *engine,
- const ResolvedModule *module)
+template<class ProductOrArtifact>
+static JSValue moduleProperty(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
+{
+ if (Q_UNLIKELY(argc < 2))
+ return throwError(ctx, Tr::tr("Function moduleProperty() expects 2 arguments"));
+
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const ResolvedProduct *product = nullptr;
+ const Artifact *artifact = nullptr;
+ if constexpr (std::is_same_v<ProductOrArtifact, ResolvedProduct>) {
+ product = attachedPointer<ResolvedProduct>(this_val, engine->productPropertyScriptClass());
+ } else {
+ artifact = attachedPointer<Artifact>(this_val, engine->dataWithPtrClass());
+ product = artifact->product;
+ }
+
+ const QString moduleName = getJsString(ctx, argv[0]);
+ const QString propertyName = getJsString(ctx, argv[1]);
+ return getModuleProperty(product, artifact, engine, moduleName, propertyName);
+}
+
+
+template<class ProductOrArtifact>
+static JSValue js_moduleProperty(JSContext *ctx, JSValue this_val, int argc, JSValue *argv)
+{
+ try {
+ return moduleProperty<ProductOrArtifact>(ctx, this_val, argc, argv);
+ } catch (const ErrorInfo &e) {
+ return throwError(ctx, e.toString());
+ }
+}
+
+template<class ProductOrArtifact>
+static void initModuleProperties(ScriptEngine *engine, JSValue objectWithProperties)
+{
+ JSContext * const ctx = engine->context();
+ JSValue func = JS_NewCFunction(ctx, js_moduleProperty<ProductOrArtifact>, "moduleProperty", 2);
+ setJsProperty(ctx, objectWithProperties, QStringLiteral("moduleProperty"), func);
+}
+
+static JSValue setupBaseModuleScriptValue(ScriptEngine *engine, const ResolvedModule *module)
{
- QScriptValue &moduleScriptValue = engine->moduleScriptValuePrototype(module);
- if (moduleScriptValue.isValid())
+ JSValue &moduleScriptValue = engine->baseModuleScriptValue(module);
+ if (JS_IsObject(moduleScriptValue))
return moduleScriptValue;
- moduleScriptValue = engine->newObject();
- QScriptValue depfunc = engine->newFunction<const ResolvedModule *>(&js_moduleDependencies,
- module);
- moduleScriptValue.setProperty(StringConstants::dependenciesProperty(), depfunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
- QScriptValue artifactsFunc = engine->newFunction(&artifactsScriptValueForModule, module);
- moduleScriptValue.setProperty(StringConstants::artifactsProperty(), artifactsFunc,
- QScriptValue::ReadOnly | QScriptValue::Undeletable
- | QScriptValue::PropertyGetter);
+ const ScopedJsValue proto(engine->context(), JS_NewObject(engine->context()));
+ moduleScriptValue = JS_NewObjectProtoClass(engine->context(), proto,
+ engine->dataWithPtrClass());
+ attachPointerTo(moduleScriptValue, module);
+ QByteArray name = StringConstants::dependenciesProperty().toUtf8();
+ const ScopedJsValue depfunc(
+ engine->context(),
+ JS_NewCFunction(engine->context(), js_moduleDependencies, name.constData(), 0));
+ const ScopedJsAtom depAtom(engine->context(), name);
+ JS_DefineProperty(engine->context(), moduleScriptValue, depAtom,
+ JS_UNDEFINED, depfunc, JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
+ name = StringConstants::artifactsProperty().toUtf8();
+ const ScopedJsValue artifactsFunc(
+ engine->context(),
+ JS_NewCFunction(engine->context(), &artifactsScriptValueForModule,
+ name.constData(), 0));
+ const ScopedJsAtom artifactsAtom(engine->context(), name);
+ JS_DefineProperty(engine->context(), moduleScriptValue, artifactsAtom,
+ JS_UNDEFINED, artifactsFunc, JS_UNDEFINED,
+ JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
return moduleScriptValue;
}
-void ModuleProperties::init(QScriptValue productObject,
+void ModuleProperties::init(ScriptEngine *engine, JSValue productObject,
const ResolvedProduct *product)
{
- init(productObject, product, StringConstants::productValue());
- setupModules(productObject, product, nullptr);
+ initModuleProperties<ResolvedProduct>(engine, productObject);
+ setupModules(engine, productObject, product, nullptr);
}
-void ModuleProperties::init(QScriptValue artifactObject, const Artifact *artifact)
+void ModuleProperties::init(ScriptEngine *engine, JSValue artifactObject, const Artifact *artifact)
{
- init(artifactObject, artifact, artifactType());
+ initModuleProperties<Artifact>(engine, artifactObject);
const auto product = artifact->product;
const QVariantMap productProperties {
{StringConstants::buildDirectoryProperty(), product->buildDirectory()},
@@ -252,106 +284,51 @@ void ModuleProperties::init(QScriptValue artifactObject, const Artifact *artifac
{StringConstants::targetNameProperty(), product->targetName},
{StringConstants::typeProperty(), sorted(product->fileTags.toStringList())}
};
- QScriptEngine * const engine = artifactObject.engine();
- artifactObject.setProperty(StringConstants::productVar(),
- engine->toScriptValue(productProperties));
- setupModules(artifactObject, artifact->product.get(), artifact);
+ setJsProperty(engine->context(), artifactObject, StringConstants::productVar(),
+ engine->toScriptValue(productProperties));
+ setupModules(engine, artifactObject, artifact->product.get(), artifact);
}
-void ModuleProperties::setModuleScriptValue(QScriptValue targetObject,
- const QScriptValue &moduleObject, const QString &moduleName)
+void ModuleProperties::setModuleScriptValue(ScriptEngine *engine, JSValue targetObject,
+ const JSValue &moduleObject, const QString &moduleName)
{
- auto const e = static_cast<ScriptEngine *>(targetObject.engine());
const QualifiedId name = QualifiedId::fromString(moduleName);
- QScriptValue obj = targetObject;
+ JSValue obj = targetObject;
for (int i = 0; i < name.size() - 1; ++i) {
- QScriptValue tmp = obj.property(name.at(i));
- if (!tmp.isObject())
- tmp = e->newObject();
- obj.setProperty(name.at(i), tmp);
+ JSValue tmp = getJsProperty(engine->context(), obj, name.at(i));
+ if (!JS_IsObject(tmp)) {
+ tmp = engine->newObject();
+ setJsProperty(engine->context(), obj, name.at(i), tmp);
+ } else {
+ JS_FreeValue(engine->context(), tmp);
+ }
obj = tmp;
}
- obj.setProperty(name.last(), moduleObject);
- if (moduleName.size() > 1)
- targetObject.setProperty(moduleName, moduleObject);
-}
-
-void ModuleProperties::init(QScriptValue objectWithProperties, const void *ptr,
- const QString &type)
-{
- QScriptEngine * const engine = objectWithProperties.engine();
- objectWithProperties.setProperty(QStringLiteral("moduleProperty"),
- engine->newFunction(ModuleProperties::js_moduleProperty, 2));
- objectWithProperties.setProperty(ptrKey(), engine->toScriptValue(quintptr(ptr)));
- objectWithProperties.setProperty(typeKey(), type);
+ setJsProperty(engine->context(), obj, name.last(), moduleObject);
+ if (name.size() > 1) {
+ setJsProperty(engine->context(), targetObject, moduleName,
+ JS_DupValue(engine->context(), moduleObject));
+ }
}
-void ModuleProperties::setupModules(QScriptValue &object, const ResolvedProduct *product,
- const Artifact *artifact)
+void ModuleProperties::setupModules(ScriptEngine *engine, JSValue &object,
+ const ResolvedProduct *product, const Artifact *artifact)
{
- const auto engine = static_cast<ScriptEngine *>(object.engine());
- QScriptClass *modulePropertyScriptClass = engine->modulePropertyScriptClass();
- if (!modulePropertyScriptClass) {
- modulePropertyScriptClass = new ModulePropertyScriptClass(engine);
+ JSClassID modulePropertyScriptClass = engine->modulePropertyScriptClass();
+ if (modulePropertyScriptClass == 0) {
+ modulePropertyScriptClass = engine->registerClass("ModulePropertyScriptClass", nullptr,
+ nullptr, JS_UNDEFINED, &getModulePropertyNames, &getModuleProperty);
engine->setModulePropertyScriptClass(modulePropertyScriptClass);
}
for (const auto &module : product->modules) {
- QScriptValue moduleObjectPrototype = setupModuleScriptValue(engine, module.get());
- QScriptValue moduleObject = engine->newObject(modulePropertyScriptClass);
- moduleObject.setPrototype(moduleObjectPrototype);
- moduleObject.setData(getDataForModuleScriptValue(engine, product, artifact, module.get()));
- setModuleScriptValue(object, moduleObject, module->name);
- }
-}
-
-QScriptValue ModuleProperties::js_moduleProperty(QScriptContext *context, QScriptEngine *engine)
-{
- try {
- return moduleProperty(context, engine);
- } catch (const ErrorInfo &e) {
- return context->throwError(e.toString());
+ setupBaseModuleScriptValue(engine, module.get());
+ JSValue moduleObject = JS_NewObjectClass(engine->context(), modulePropertyScriptClass);
+ attachPointerTo(moduleObject, module.get());
+ defineJsProperty(engine->context(), moduleObject, StringConstants::dataPropertyInternal(),
+ createDataForModuleScriptValue(engine, artifact));
+ setModuleScriptValue(engine, object, moduleObject, module->name);
}
}
-QScriptValue ModuleProperties::moduleProperty(QScriptContext *context, QScriptEngine *engine)
-{
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- Tr::tr("Function moduleProperty() expects 2 arguments"));
- }
-
- const QScriptValue objectWithProperties = context->thisObject();
- const QScriptValue typeScriptValue = objectWithProperties.property(typeKey());
- if (Q_UNLIKELY(!typeScriptValue.isString())) {
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("Internal error: __type not set up"));
- }
- const QScriptValue ptrScriptValue = objectWithProperties.property(ptrKey());
- if (Q_UNLIKELY(!ptrScriptValue.isNumber())) {
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("Internal error: __internalPtr not set up"));
- }
-
- const void *ptr = reinterpret_cast<const void *>(qscriptvalue_cast<quintptr>(ptrScriptValue));
- const ResolvedProduct *product = nullptr;
- const Artifact *artifact = nullptr;
- if (typeScriptValue.toString() == StringConstants::productValue()) {
- QBS_ASSERT(ptr, return {});
- product = static_cast<const ResolvedProduct *>(ptr);
- } else if (typeScriptValue.toString() == artifactType()) {
- QBS_ASSERT(ptr, return {});
- artifact = static_cast<const Artifact *>(ptr);
- product = artifact->product.get();
- } else {
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("Internal error: invalid type"));
- }
-
- const auto qbsEngine = static_cast<ScriptEngine *>(engine);
- const QString moduleName = context->argument(0).toString();
- const QString propertyName = context->argument(1).toString();
- return getModuleProperty(product, artifact, qbsEngine, moduleName, propertyName);
-}
-
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/jsextensions/moduleproperties.h b/src/lib/corelib/jsextensions/moduleproperties.h
index 3fe4fbfd0..6272ee8a2 100644
--- a/src/lib/corelib/jsextensions/moduleproperties.h
+++ b/src/lib/corelib/jsextensions/moduleproperties.h
@@ -43,41 +43,32 @@
#include <buildgraph/forward_decls.h>
#include <language/forward_decls.h>
-#include <QtScript/qscriptcontext.h>
-#include <QtScript/qscriptvalue.h>
+#include <quickjs.h>
namespace qbs {
namespace Internal {
-
class ScriptEngine;
enum ModulePropertiesScriptValueCommonPropertyKeys : quint32
{
ModuleNameKey,
- ProductPtrKey,
- ArtifactPtrKey,
DependencyParametersKey,
};
-QScriptValue getDataForModuleScriptValue(QScriptEngine *engine, const ResolvedProduct *product,
- const Artifact *artifact, const ResolvedModule *module);
+JSValue createDataForModuleScriptValue(ScriptEngine *engine,
+ const Artifact *artifact);
class ModuleProperties
{
public:
- static void init(QScriptValue productObject, const ResolvedProduct *product);
- static void init(QScriptValue artifactObject, const Artifact *artifact);
- static void setModuleScriptValue(QScriptValue targetObject, const QScriptValue &moduleObject,
- const QString &moduleName);
+ static void init(ScriptEngine *engine, JSValue productObject, const ResolvedProduct *product);
+ static void init(ScriptEngine *engine, JSValue artifactObject, const Artifact *artifact);
+ static void setModuleScriptValue(ScriptEngine *engine, JSValue targetObject,
+ const JSValue &moduleObject, const QString &moduleName);
private:
- static void init(QScriptValue objectWithProperties, const void *ptr, const QString &type);
- static void setupModules(QScriptValue &object, const ResolvedProduct *product,
+ static void setupModules(ScriptEngine *engine, JSValue &object, const ResolvedProduct *product,
const Artifact *artifact);
-
- static QScriptValue js_moduleProperty(QScriptContext *context, QScriptEngine *engine);
-
- static QScriptValue moduleProperty(QScriptContext *context, QScriptEngine *engine);
};
} // namespace Internal
diff --git a/src/lib/corelib/jsextensions/pkgconfigjs.cpp b/src/lib/corelib/jsextensions/pkgconfigjs.cpp
new file mode 100644
index 000000000..817a3dfb9
--- /dev/null
+++ b/src/lib/corelib/jsextensions/pkgconfigjs.cpp
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "pkgconfigjs.h"
+
+#include <language/scriptengine.h>
+#include <tools/version.h>
+
+#include <QtCore/QProcessEnvironment>
+
+#include <stdexcept>
+
+namespace qbs {
+namespace Internal {
+
+namespace {
+
+template<typename C, typename F> QVariantList convert(const C &c, F &&f)
+{
+ QVariantList result;
+ result.reserve(int(c.size()));
+ std::transform(c.begin(), c.end(), std::back_inserter(result), f);
+ return result;
+}
+
+QVariantMap variablesMapToMap(const PcPackage::VariablesMap &variables)
+{
+ QVariantMap result;
+ for (const auto &item: variables)
+ result.insert(QString::fromStdString(item.first), QString::fromStdString(item.second));
+ return result;
+}
+
+QVariantMap packageToMap(const PcPackage &package)
+{
+ QVariantMap result;
+ result[QStringLiteral("isBroken")] = false;
+ result[QStringLiteral("filePath")] = QString::fromStdString(package.filePath);
+ result[QStringLiteral("baseFileName")] = QString::fromStdString(package.baseFileName);
+ result[QStringLiteral("name")] = QString::fromStdString(package.name);
+ result[QStringLiteral("version")] = QString::fromStdString(package.version);
+ result[QStringLiteral("description")] = QString::fromStdString(package.description);
+ result[QStringLiteral("url")] = QString::fromStdString(package.url);
+
+ const auto flagToMap = [](const PcPackage::Flag &flag)
+ {
+ QVariantMap result;
+ const auto value = QString::fromStdString(flag.value);
+ result[QStringLiteral("type")] = QVariant::fromValue(qint32(flag.type));
+ result[QStringLiteral("value")] = value;
+ return result;
+ };
+
+ const auto requiredVersionToMap = [](const PcPackage::RequiredVersion &version)
+ {
+ using Type = PcPackage::RequiredVersion::ComparisonType;
+ QVariantMap result;
+ result[QStringLiteral("name")] = QString::fromStdString(version.name);
+ const auto versionString = QString::fromStdString(version.version);
+ const auto qbsVersion = Version::fromString(QString::fromStdString(version.version));
+ const auto nextQbsVersion = Version(
+ qbsVersion.majorVersion(),
+ qbsVersion.minorVersion(),
+ qbsVersion.patchLevel() + 1);
+ switch (version.comparison) {
+ case Type::LessThan:
+ result[QStringLiteral("versionBelow")] = versionString;
+ break;
+ case Type::GreaterThan:
+ result[QStringLiteral("versionAtLeast")] = nextQbsVersion.toString();
+ break;
+ case Type::LessThanEqual:
+ result[QStringLiteral("versionBelow")] = nextQbsVersion.toString();
+ break;
+ case Type::GreaterThanEqual:
+ result[QStringLiteral("versionAtLeast")] = versionString;
+ break;
+ case Type::Equal:
+ result[QStringLiteral("version")] = versionString;
+ break;
+ case Type::NotEqual:
+ result[QStringLiteral("versionBelow")] = versionString;
+ result[QStringLiteral("versionAtLeast")] = nextQbsVersion.toString();
+ break;
+ case Type::AlwaysMatch:
+ break;
+ }
+ return result;
+ };
+
+ result[QStringLiteral("libs")] = convert(package.libs, flagToMap);
+ result[QStringLiteral("libsPrivate")] = convert(package.libsPrivate, flagToMap);
+ result[QStringLiteral("cflags")] = convert(package.cflags, flagToMap);
+ result[QStringLiteral("requires")] = convert(package.requiresPublic, requiredVersionToMap);
+ result[QStringLiteral("requiresPrivate")] =
+ convert(package.requiresPrivate, requiredVersionToMap);
+ result[QStringLiteral("conflicts")] = convert(package.conflicts, requiredVersionToMap);
+ result[QStringLiteral("variables")] = variablesMapToMap(package.variables);
+
+ return result;
+};
+
+QVariantMap brokenPackageToMap(const PcBrokenPackage &package)
+{
+ QVariantMap result;
+ result[QStringLiteral("isBroken")] = true;
+ result[QStringLiteral("filePath")] = QString::fromStdString(package.filePath);
+ result[QStringLiteral("baseFileName")] = QString::fromStdString(package.baseFileName);
+ result[QStringLiteral("errorText")] = QString::fromStdString(package.errorText);
+ return result;
+}
+
+QVariantMap packageVariantToMap(const PcPackageVariant &package)
+{
+ return package.visit([](const auto &value) {
+ using T = std::decay_t<decltype(value)>;
+ if constexpr (std::is_same_v<T, PcPackage>)
+ return packageToMap(value);
+ else
+ return brokenPackageToMap(value);
+ });
+}
+
+PcPackage::VariablesMap envToVariablesMap(const QProcessEnvironment &env)
+{
+ PcPackage::VariablesMap result;
+ const auto keys = env.keys();
+ for (const auto &key : keys)
+ result[key.toStdString()] = env.value(key).toStdString();
+ return result;
+}
+
+PcPackage::VariablesMap variablesFromQVariantMap(const QVariantMap &map)
+{
+ PcPackage::VariablesMap result;
+ for (auto it = map.cbegin(), end = map.cend(); it != end; ++it)
+ result[it.key().toStdString()] = it.value().toString().toStdString();
+ return result;
+}
+
+std::vector<std::string> stringListToStdVector(const QStringList &list)
+{
+ return transformed<std::vector<std::string>>(list, [](const auto &s) {
+ return s.toStdString(); });
+}
+
+} // namespace
+
+void PkgConfigJs::declareEnums(JSContext *ctx, JSValue classObj)
+{
+ DECLARE_ENUM(ctx, classObj, LibraryName);
+ DECLARE_ENUM(ctx, classObj, LibraryPath);
+ DECLARE_ENUM(ctx, classObj, StaticLibraryName);
+ DECLARE_ENUM(ctx, classObj, Framework);
+ DECLARE_ENUM(ctx, classObj, FrameworkPath);
+ DECLARE_ENUM(ctx, classObj, LinkerFlag);
+ DECLARE_ENUM(ctx, classObj, IncludePath);
+ DECLARE_ENUM(ctx, classObj, SystemIncludePath);
+ DECLARE_ENUM(ctx, classObj, Define);
+ DECLARE_ENUM(ctx, classObj, CompilerFlag);
+}
+
+JSValue PkgConfigJs::ctor(JSContext *ctx, JSValue, JSValue, int argc, JSValue *argv, int)
+{
+ try {
+ QVariantMap options;
+ if (argc > 0)
+ options = fromArg<QVariant>(ctx, "PkgConfig constructor", 1, argv[0]).toMap();
+ JSValue obj = createObject(ctx, options);
+ return obj;
+ } catch (const PcException &e) {
+ return throwError(ctx, QString::fromUtf8(e.what()));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
+}
+
+PkgConfigJs::PkgConfigJs(JSContext *ctx, const QVariantMap &options) :
+ m_pkgConfig(std::make_unique<PkgConfig>(
+ convertOptions(ScriptEngine::engineForContext(ctx)->environment(), options)))
+{
+ for (const auto &package : m_pkgConfig->packages()) {
+ m_packages.insert(
+ QString::fromStdString(package.getBaseFileName()), packageVariantToMap(package));
+ }
+}
+
+PkgConfig::Options PkgConfigJs::convertOptions(const QProcessEnvironment &env, const QVariantMap &map)
+{
+ PkgConfig::Options result;
+ result.libDirs =
+ stringListToStdVector(map.value(QStringLiteral("libDirs")).toStringList());
+ result.extraPaths =
+ stringListToStdVector(map.value(QStringLiteral("extraPaths")).toStringList());
+ result.sysroot = map.value(QStringLiteral("sysroot")).toString().toStdString();
+ result.topBuildDir = map.value(QStringLiteral("topBuildDir")).toString().toStdString();
+ result.allowSystemLibraryPaths =
+ map.value(QStringLiteral("allowSystemLibraryPaths"), false).toBool();
+ const auto systemLibraryPaths = map.value(QStringLiteral("systemLibraryPaths")).toStringList();
+ result.systemLibraryPaths.reserve(systemLibraryPaths.size());
+ std::transform(
+ systemLibraryPaths.begin(),
+ systemLibraryPaths.end(),
+ std::back_inserter(result.systemLibraryPaths),
+ [](const QString &str){ return str.toStdString(); });
+ result.disableUninstalled = map.value(QStringLiteral("disableUninstalled"), true).toBool();
+ result.staticMode = map.value(QStringLiteral("staticMode"), false).toBool();
+ result.definePrefix = map.value(QStringLiteral("definePrefix"), false).toBool();
+ result.globalVariables =
+ variablesFromQVariantMap(map.value(QStringLiteral("globalVariables")).toMap());
+ result.systemVariables = envToVariablesMap(env);
+
+ return result;
+}
+
+void PkgConfigJs::setupMethods(JSContext *ctx, JSValue obj)
+{
+ setupMethod(ctx, obj, "packages", &PkgConfigJs::jsPackages, 0);
+}
+
+} // namespace Internal
+} // namespace qbs
+
+void initializeJsExtensionPkgConfig(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
+{
+ qbs::Internal::PkgConfigJs::registerClass(engine, extensionObject);
+}
diff --git a/src/lib/corelib/jsextensions/pkgconfigjs.h b/src/lib/corelib/jsextensions/pkgconfigjs.h
new file mode 100644
index 000000000..7e84545a0
--- /dev/null
+++ b/src/lib/corelib/jsextensions/pkgconfigjs.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "jsextension.h"
+
+#include "tools/qbs_export.h"
+#include <tools/stlutils.h>
+
+#include <pkgconfig.h>
+
+#include <QtCore/qvariant.h>
+
+#include <memory>
+
+class QProcessEnvironment;
+
+namespace qbs {
+namespace Internal {
+
+class QBS_AUTOTEST_EXPORT PkgConfigJs : public JsExtension<PkgConfigJs>
+{
+public:
+ enum FlagType {
+ LibraryName = toUnderlying(PcPackage::Flag::Type::LibraryName),
+ LibraryPath = toUnderlying(PcPackage::Flag::Type::LibraryPath),
+ StaticLibraryName = toUnderlying(PcPackage::Flag::Type::StaticLibraryName),
+ Framework = toUnderlying(PcPackage::Flag::Type::Framework),
+ FrameworkPath = toUnderlying(PcPackage::Flag::Type::FrameworkPath),
+ LinkerFlag = toUnderlying(PcPackage::Flag::Type::LinkerFlag),
+ IncludePath = toUnderlying(PcPackage::Flag::Type::IncludePath),
+ SystemIncludePath = toUnderlying(PcPackage::Flag::Type::SystemIncludePath),
+ Define = toUnderlying(PcPackage::Flag::Type::Define),
+ CompilerFlag = toUnderlying(PcPackage::Flag::Type::CompilerFlag),
+ };
+
+ enum class ComparisonType {
+ LessThan,
+ GreaterThan,
+ LessThanEqual,
+ GreaterThanEqual,
+ Equal,
+ NotEqual,
+ AlwaysMatch
+ };
+
+ static const char *name() { return "PkgConfig"; }
+ static void declareEnums(JSContext *ctx, JSValue classObj);
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int);
+
+ explicit PkgConfigJs(JSContext *ctx, const QVariantMap &options = {});
+
+ DEFINE_JS_FORWARDER(jsPackages, &PkgConfigJs::packages, "PkgConfig.packages")
+ QVariantMap packages() const { return m_packages; }
+
+ // also used in tests
+ static PkgConfig::Options convertOptions(const QProcessEnvironment &env, const QVariantMap &map);
+
+ static void setupMethods(JSContext *ctx, JSValue obj);
+
+private:
+ std::unique_ptr<PkgConfig> m_pkgConfig;
+ QVariantMap m_packages;
+};
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/jsextensions/process.cpp b/src/lib/corelib/jsextensions/process.cpp
index 064297a32..f0febb364 100644
--- a/src/lib/corelib/jsextensions/process.cpp
+++ b/src/lib/corelib/jsextensions/process.cpp
@@ -37,6 +37,8 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/executablefinder.h>
@@ -44,163 +46,182 @@
#include <tools/shellutils.h>
#include <tools/stringconstants.h>
-#include <QtCore/qobject.h>
#include <QtCore/qprocess.h>
-#include <QtCore/qtextcodec.h>
#include <QtCore/qtextstream.h>
#include <QtCore/qvariant.h>
-
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QtCore5Compat/qtextcodec.h>
+#else
+#include <QtCore/qtextcodec.h>
+#endif
namespace qbs {
namespace Internal {
-class Process : public QObject, public QScriptable, public ResourceAcquiringScriptObject
+class Process : public JsExtension<Process>
{
- Q_OBJECT
+ friend class JsExtension<Process>;
public:
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
- Process(QScriptContext *context);
- ~Process() override;
-
- Q_INVOKABLE QString getEnv(const QString &name);
- Q_INVOKABLE void setEnv(const QString &name, const QString &value);
- Q_INVOKABLE void setCodec(const QString &codec);
+ static const char *name() { return "Process"; }
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int);
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static void setupMethods(JSContext *ctx, JSValue obj);
+ Process(JSContext *context);
+
+ DEFINE_JS_FORWARDER(jsGetEnv, &Process::getEnv, "Process.getEnv")
+ DEFINE_JS_FORWARDER(jsSetEnv, &Process::setEnv, "Process.setEnv")
+ DEFINE_JS_FORWARDER(jsSetCodec, &Process::setCodec, "Process.setCodec")
+ DEFINE_JS_FORWARDER(jsWorkingDir, &Process::workingDirectory, "Process.workingDirectory")
+ DEFINE_JS_FORWARDER(jsSetWorkingDir, &Process::setWorkingDirectory,
+ "Process.setWorkingDirectory")
+ DEFINE_JS_FORWARDER(jsStart, &Process::start, "Process.start")
+ DEFINE_JS_FORWARDER(jsClose, &Process::close, "Process.close")
+ DEFINE_JS_FORWARDER(jsTerminate, &Process::terminate, "Process.terminate")
+ DEFINE_JS_FORWARDER(jsKill, &Process::kill, "Process.kill")
+ DEFINE_JS_FORWARDER(jsReadLine, &Process::readLine, "Process.readLine")
+ DEFINE_JS_FORWARDER(jsAtEnd, &Process::atEnd, "Process.atEnd")
+ DEFINE_JS_FORWARDER(jsReadStdOut, &Process::readStdOut, "Process.readStdOut")
+ DEFINE_JS_FORWARDER(jsReadStdErr, &Process::readStdErr, "Process.readStdErr")
+ DEFINE_JS_FORWARDER(jsCloseWriteChannel, &Process::closeWriteChannel,
+ "Process.closeWriteChannel")
+ DEFINE_JS_FORWARDER(jsWrite, &Process::write, "Process.write")
+ DEFINE_JS_FORWARDER(jsWriteLine, &Process::writeLine, "Process.writeLine")
+ DEFINE_JS_FORWARDER(jsExitCode, &Process::exitCode, "Process.exitCode")
+
+ static JSValue jsExec(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
+ {
+ try {
+ const auto args = getArguments<QString, QStringList>(ctx, "Process.exec", argc, argv);
+ bool throwOnError = false;
+ if (argc > 2)
+ throwOnError = fromArg<bool>(ctx, "Process.exec", 3, argv[2]);
+ return JS_NewInt32(ctx, fromJsObject(ctx, this_val)
+ ->exec(std::get<0>(args), std::get<1>(args), throwOnError));
+ } catch (const QString &error) { return throwError(ctx, error); }
+ }
+ static JSValue jsWaitForFinished(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+ try {
+ int msecs = 30000;
+ if (argc > 0)
+ msecs = getArgument<int>(ctx, "Process.waitForFinished", argc, argv);
+ return JS_NewBool(ctx, fromJsObject(ctx, this_val)->waitForFinished(msecs));
+ } catch (const QString &error) { return throwError(ctx, error); }
+ }
+ QString getEnv(const QString &name) const { return m_environment.value(name); }
+ void setEnv(const QString &name, const QString &value) { m_environment.insert(name, value); }
+ void setCodec(const QString &codec);
- Q_INVOKABLE QString workingDirectory();
- Q_INVOKABLE void setWorkingDirectory(const QString &dir);
+ QString workingDirectory() const { return m_workingDirectory; }
+ void setWorkingDirectory(const QString &dir) { m_workingDirectory = 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();
+ bool start(const QString &program, const QStringList &arguments);
+ int exec(const QString &program, const QStringList &arguments, bool throwOnError);
+ void close();
+ bool waitForFinished(int msecs);
+ void terminate() { m_qProcess->terminate(); }
+ void kill() { m_qProcess->kill(); }
- Q_INVOKABLE QString readLine();
- Q_INVOKABLE bool atEnd() const;
- Q_INVOKABLE QString readStdOut();
- Q_INVOKABLE QString readStdErr();
+ QString readLine();
+ bool atEnd() const { return m_qProcess->atEnd(); }
+ QString readStdOut() { return m_codec->toUnicode(m_qProcess->readAllStandardOutput()); }
+ QString readStdErr() { return m_codec->toUnicode(m_qProcess->readAllStandardError()); }
- Q_INVOKABLE void closeWriteChannel();
+ void closeWriteChannel() { m_qProcess->closeWriteChannel(); }
- Q_INVOKABLE void write(const QString &str);
- Q_INVOKABLE void writeLine(const QString &str);
+ void write(const QString &str) { m_qProcess->write(m_codec->fromUnicode(str)); }
+ void writeLine(const QString &str);
- Q_INVOKABLE int exitCode() const;
+ int exitCode() const { return m_qProcess->exitCode(); }
- static QScriptValue js_shellQuote(QScriptContext *context, QScriptEngine *engine);
+ static JSValue jsShellQuote(JSContext *ctx, JSValue, int argc, JSValue *argv);
private:
QString findExecutable(const QString &filePath) const;
- // ResourceAcquiringScriptObject implementation
- void releaseResources() override;
-
- QProcess *m_qProcess;
+ std::unique_ptr<QProcess> m_qProcess;
QProcessEnvironment m_environment;
QString m_workingDirectory;
- QTextStream *m_textStream;
+ QTextCodec *m_codec = nullptr;
};
-QScriptValue Process::ctor(QScriptContext *context, QScriptEngine *engine)
+JSValue Process::ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int)
{
- Process *t;
- switch (context->argumentCount()) {
- case 0:
- t = new Process(context);
- break;
- default:
- return context->throwError(QStringLiteral("Process()"));
- }
+ try {
+ JSValue obj = createObject(ctx);
- const auto se = static_cast<ScriptEngine *>(engine);
- se->addResourceAcquiringScriptObject(t);
- const DubiousContextList dubiousContexts ({
+ Process * const process = fromJsObject(ctx, obj);
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{
DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("qbs.Process"), dubiousContexts);
-
- QScriptValue obj = engine->newQObject(t, QScriptEngine::QtOwnership);
-
- // Get environment
- QVariant v = engine->property(StringConstants::qbsProcEnvVarInternal());
- 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<ScriptEngine *>(engine)->environment();
- } else {
- t->m_environment
- = QProcessEnvironment(*reinterpret_cast<QProcessEnvironment*>(v.value<void*>()));
- }
- se->setUsesIo();
-
- 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);
+ };
+ se->checkContext(QStringLiteral("qbs.Process"), dubiousContexts);
+
+ // Get environment
+ QVariant v = se->property(StringConstants::qbsProcEnvVarInternal());
+ 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.
+ process->m_environment = se->environment();
+ } else {
+ process->m_environment
+ = QProcessEnvironment(*reinterpret_cast<QProcessEnvironment*>(v.value<void*>()));
+ }
+ se->setUsesIo();
+ return obj;
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-void Process::setEnv(const QString &name, const QString &value)
+void Process::setupStaticMethods(JSContext *ctx, JSValue classObj)
{
- Q_ASSERT(thisObject().engine() == engine());
- m_environment.insert(name, value);
+ setupMethod(ctx, classObj, "shellQuote", &Process::jsShellQuote, 3);
}
-QString Process::workingDirectory()
+void Process::setupMethods(JSContext *ctx, JSValue obj)
{
- Q_ASSERT(thisObject().engine() == engine());
- return m_workingDirectory;
+ setupMethod(ctx, obj, "getEnv", &jsGetEnv, 1);
+ setupMethod(ctx, obj, "setEnv", &jsSetEnv, 2);
+ setupMethod(ctx, obj, "setCodec", &jsSetCodec, 1);
+ setupMethod(ctx, obj, "workingDirectory", &jsWorkingDir, 0);
+ setupMethod(ctx, obj, "setWorkingDirectory", &jsSetWorkingDir, 1);
+ setupMethod(ctx, obj, "start", &jsStart, 2);
+ setupMethod(ctx, obj, "exec", &jsExec, 3);
+ setupMethod(ctx, obj, "close", &jsClose, 0);
+ setupMethod(ctx, obj, "waitForFinished", &jsWaitForFinished, 1);
+ setupMethod(ctx, obj, "terminate", &jsTerminate, 0);
+ setupMethod(ctx, obj, "kill", &jsKill, 0);
+ setupMethod(ctx, obj, "readLine", &jsReadLine, 0);
+ setupMethod(ctx, obj, "atEnd", &jsAtEnd, 0);
+ setupMethod(ctx, obj, "readStdOut", &jsReadStdOut, 0);
+ setupMethod(ctx, obj, "readStdErr", &jsReadStdErr, 0);
+ setupMethod(ctx, obj, "closeWriteChannel", &jsCloseWriteChannel, 0);
+ setupMethod(ctx, obj, "write", &jsWrite, 1);
+ setupMethod(ctx, obj, "writeLine", &jsWriteLine, 1);
+ setupMethod(ctx, obj, "exitCode", &jsExitCode, 0);
}
-void Process::setWorkingDirectory(const QString &dir)
+Process::Process(JSContext *)
{
- Q_ASSERT(thisObject().engine() == engine());
- m_workingDirectory = dir;
+ m_qProcess = std::make_unique<QProcess>();
+ m_codec = QTextCodec::codecForName("UTF-8");
}
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);
+ m_qProcess->start(findExecutable(program), arguments, QIODevice::ReadWrite | QIODevice::Text);
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()));
- }
+ if (throwOnError)
+ throw Tr::tr("Error running '%1': %2").arg(program, m_qProcess->errorString());
return -1;
}
m_qProcess->closeWriteChannel();
@@ -208,20 +229,20 @@ int Process::exec(const QString &program, const QStringList &arguments, bool thr
if (throwOnError) {
if (m_qProcess->error() != QProcess::UnknownError
&& m_qProcess->error() != QProcess::Crashed) {
- context()->throwError(Tr::tr("Error running '%1': %2")
- .arg(program, m_qProcess->errorString()));
+ throw Tr::tr("Error running '%1': %2").arg(program, m_qProcess->errorString());
} else if (m_qProcess->exitStatus() == QProcess::CrashExit || m_qProcess->exitCode() != 0) {
QString errorMessage = m_qProcess->error() == QProcess::Crashed
? Tr::tr("Error running '%1': %2").arg(program, m_qProcess->errorString())
- : Tr::tr("Process '%1' finished with exit code %2.").arg(program).arg(
- m_qProcess->exitCode());
+ : Tr::tr("Process '%1 %2' finished with exit code %3.")
+ .arg(program, arguments.join(QStringLiteral(" ")))
+ .arg(m_qProcess->exitCode());
const QString stdOut = readStdOut();
if (!stdOut.isEmpty())
errorMessage.append(Tr::tr(" The standard output was:\n")).append(stdOut);
const QString stdErr = readStdErr();
if (!stdErr.isEmpty())
errorMessage.append(Tr::tr(" The standard error output was:\n")).append(stdErr);
- context()->throwError(errorMessage);
+ throw errorMessage;
}
}
if (m_qProcess->error() != QProcess::UnknownError)
@@ -233,67 +254,29 @@ void Process::close()
{
if (!m_qProcess)
return;
- Q_ASSERT(thisObject().engine() == engine());
- delete m_textStream;
- m_textStream = nullptr;
- delete m_qProcess;
- m_qProcess = nullptr;
+ m_qProcess.reset();
}
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));
+ const auto newCodec = QTextCodec::codecForName(qPrintable(codec));
+ if (newCodec)
+ m_codec = newCodec;
}
QString Process::readLine()
{
- return m_textStream->readLine();
-}
-
-bool Process::atEnd() const
-{
- return m_textStream->atEnd();
-}
-
-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();
+ auto result = m_codec->toUnicode(m_qProcess->readLine());
+ if (!result.isEmpty() && result.back() == QLatin1Char('\n'))
+ result.chop(1);
+ return result;
}
QString Process::findExecutable(const QString &filePath) const
@@ -302,53 +285,30 @@ QString Process::findExecutable(const QString &filePath) const
return exeFinder.findExecutable(filePath, m_workingDirectory);
}
-void Process::releaseResources()
-{
- close();
- deleteLater();
-}
-
-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';
+ m_qProcess->write(m_codec->fromUnicode(str));
+ m_qProcess->putChar('\n');
}
-QScriptValue Process::js_shellQuote(QScriptContext *context, QScriptEngine *engine)
+JSValue Process::jsShellQuote(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() < 2)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("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));
+ try {
+ const auto args = getArguments<QString, QStringList>(ctx, "Process.shellQuote", argc, argv);
+ HostOsInfo::HostOs hostOs = HostOsInfo::hostOs();
+ if (argc > 2) {
+ const auto osList = fromArg<QStringList>(ctx, "Process.shellQuote", 3, argv[2]);
+ hostOs = osList.contains(QLatin1String("windows"))
+ ? HostOsInfo::HostOsWindows : HostOsInfo::HostOsOtherUnix;
+ }
+ return makeJsString(ctx, shellQuote(std::get<0>(args), std::get<1>(args), hostOs));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionProcess(QScriptValue extensionObject)
+void initializeJsExtensionProcess(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newQMetaObject(&Process::staticMetaObject, engine->newFunction(&Process::ctor));
- extensionObject.setProperty(QStringLiteral("Process"), obj);
- obj.setProperty(QStringLiteral("shellQuote"), engine->newFunction(Process::js_shellQuote, 3));
+ qbs::Internal::Process::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::Process *)
-
-#include "process.moc"
diff --git a/src/lib/corelib/jsextensions/propertylist.cpp b/src/lib/corelib/jsextensions/propertylist.cpp
index 197d5e99e..914701203 100644
--- a/src/lib/corelib/jsextensions/propertylist.cpp
+++ b/src/lib/corelib/jsextensions/propertylist.cpp
@@ -38,11 +38,11 @@
**
****************************************************************************/
-#include <QtScript/qscriptengine.h>
+#include <language/scriptengine.h>
-void initializeJsExtensionPropertyList(QScriptValue extensionObject)
+void initializeJsExtensionPropertyList(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newObject(); // provide a fake object
- extensionObject.setProperty(QStringLiteral("PropertyList"), obj);
+ JSValue obj = engine->newObject(); // provide a fake object
+ qbs::Internal::setJsProperty(engine->context(), extensionObject,
+ QStringLiteral("PropertyList"), obj);
}
diff --git a/src/lib/corelib/jsextensions/propertylist.h b/src/lib/corelib/jsextensions/propertylist.h
deleted file mode 100644
index adb90f786..000000000
--- a/src/lib/corelib/jsextensions/propertylist.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/****************************************************************************
-**
-** 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 <QtCore/qglobal.h>
-
-// ### 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 <QtScript/qscriptengine.h>
-
-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 <QtCore/qobject.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qvariant.h>
-
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptvalue.h>
-
-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
deleted file mode 100644
index 6ac9d56c9..000000000
--- a/src/lib/corelib/jsextensions/propertylist.mm
+++ /dev/null
@@ -1,373 +0,0 @@
-/****************************************************************************
-**
-** 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 <language/scriptengine.h>
-#include <tools/hostosinfo.h>
-
-#include <QtCore/qfile.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qtextstream.h>
-#include <QtCore/qvariant.h>
-
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
-
-// 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;
-
-class PropertyList : public QObject, public QScriptable
-{
- Q_OBJECT
-public:
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
- PropertyList(QScriptContext *context);
- ~PropertyList() override;
- 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;
-};
-
-class PropertyListPrivate
-{
-public:
- PropertyListPrivate();
-
- QVariant propertyListObject;
- int propertyListFormat;
-
- void readFromData(QScriptContext *context, QByteArray data);
- QByteArray writeToData(QScriptContext *context, const QString &format);
-};
-
-QScriptValue PropertyList::ctor(QScriptContext *context, QScriptEngine *engine)
-{
- auto const se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({
- DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("qbs.PropertyList"), dubiousContexts);
-
- auto 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());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- return p->d->propertyListObject.isNull();
-}
-
-void PropertyList::clear()
-{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- p->d->propertyListObject = QVariant();
- p->d->propertyListFormat = 0;
-}
-
-void PropertyList::readFromObject(const QScriptValue &value)
-{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(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());
- auto p = qscriptvalue_cast<PropertyList*>(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());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- p->d->readFromData(p->context(), data);
-}
-
-void PropertyList::writeToFile(const QString &filePath, const QString &plistFormat)
-{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(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());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- switch (p->d->propertyListFormat)
- {
- case QPropertyListOpenStepFormat:
- return QStringLiteral("openstep");
- case QPropertyListXMLFormat_v1_0:
- return QStringLiteral("xml1");
- case QPropertyListBinaryFormat_v1_0:
- return QStringLiteral("binary1");
- case QPropertyListJSONFormat:
- return QStringLiteral("json");
- default:
- return p->engine()->undefinedValue();
- }
-}
-
-QScriptValue PropertyList::toObject() const
-{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
- return p->engine()->toScriptValue(p->d->propertyListObject);
-}
-
-QString PropertyList::toString(const QString &plistFormat) const
-{
- Q_ASSERT(thisObject().engine() == engine());
- auto p = qscriptvalue_cast<PropertyList*>(thisObject());
-
- if (plistFormat == QLatin1String("binary1")) {
- p->context()->throwError(QStringLiteral("Property list object cannot be converted to a "
- "string in the binary1 format; this format can only "
- "be written directly to a file"));
- return {};
- }
-
- if (!isEmpty())
- return QString::fromUtf8(p->d->writeToData(p->context(), plistFormat));
-
- return {};
-}
-
-QString PropertyList::toXMLString() const
-{
- return toString(QStringLiteral("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(QStringLiteral("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(QStringLiteral("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
-
-void initializeJsExtensionPropertyList(QScriptValue extensionObject)
-{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newQMetaObject(&PropertyList::staticMetaObject,
- engine->newFunction(&PropertyList::ctor));
- extensionObject.setProperty(QStringLiteral("PropertyList"), obj);
-}
-
-Q_DECLARE_METATYPE(qbs::Internal::PropertyList *)
-
-#include "propertylist.moc"
diff --git a/src/lib/corelib/jsextensions/propertylist_darwin.mm b/src/lib/corelib/jsextensions/propertylist_darwin.mm
new file mode 100644
index 000000000..97e8f6209
--- /dev/null
+++ b/src/lib/corelib/jsextensions/propertylist_darwin.mm
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** 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 "jsextension.h"
+#include "propertylistutils.h"
+
+#include <language/scriptengine.h>
+#include <logging/translator.h>
+#include <tools/hostosinfo.h>
+
+#include <QtCore/qfile.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qvariant.h>
+
+namespace qbs {
+namespace Internal {
+
+class PropertyList : public JsExtension<PropertyList>
+{
+public:
+ static const char *name() { return "PropertyList"; }
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int);
+ PropertyList(JSContext *) {}
+ static void setupMethods(JSContext *ctx, JSValue obj);
+
+ DEFINE_JS_FORWARDER(jsIsEmpty, &PropertyList::isEmpty, "PropertyList.isEmpty");
+ DEFINE_JS_FORWARDER(jsClear, &PropertyList::clear, "PropertyList.clear");
+ DEFINE_JS_FORWARDER(jsReadFromObject, &PropertyList::readFromObject,
+ "PropertyList.readFromObject");
+ DEFINE_JS_FORWARDER(jsReadFromString, &PropertyList::readFromString,
+ "PropertyList.readFromString");
+ DEFINE_JS_FORWARDER(jsReadFromFile, &PropertyList::readFromFile, "PropertyList.readFromFile");
+ DEFINE_JS_FORWARDER(jsReadFromData, &PropertyList::readFromData, "PropertyList.readFromData");
+ DEFINE_JS_FORWARDER(jsWriteToFile, &PropertyList::writeToFile, "PropertyList.writeToFile");
+ DEFINE_JS_FORWARDER(jsFormat, &PropertyList::format, "PropertyList.format");
+ DEFINE_JS_FORWARDER(jsToObject, &PropertyList::toObject, "PropertyList.toObject");
+ DEFINE_JS_FORWARDER(jsToString, &PropertyList::toString, "PropertyList.toString");
+ DEFINE_JS_FORWARDER(jsToXmlString, &PropertyList::toXMLString, "PropertyList.toXMLString");
+
+ static JSValue jsToJson(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
+ {
+ try {
+ QString style;
+ if (argc > 0)
+ style = fromArg<QString>(ctx, "Process.exec", 1, argv[0]);
+ return toJsValue(ctx, fromJsObject(ctx, this_val)->toJSON(style));
+ } catch (const QString &error) { return throwError(ctx, error); }
+ }
+
+private:
+ bool isEmpty() const { return m_propertyListObject.isNull(); }
+ void clear();
+ void readFromObject(const QVariant &value);
+ void readFromString(const QString &input);
+ void readFromFile(const QString &filePath);
+ void readFromData(const QByteArray &data);
+ void writeToFile(const QString &filePath, const QString &plistFormat);
+ std::optional<QString> format() const;
+ QVariant toObject() const {
+ return m_propertyListObject.isNull() ? QVariantMap() : m_propertyListObject;
+ }
+ QString toString(const QString &plistFormat) const;
+ QString toXMLString() const;
+ QString toJSON(const QString &style = QString()) const;
+
+ QByteArray writeToData(const QString &format) const;
+
+ QVariant m_propertyListObject;
+ int m_propertyListFormat = 0;
+};
+
+// 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 :)
+};
+
+JSValue PropertyList::ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int)
+{
+ try {
+ JSValue obj = createObject(ctx);
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{
+ DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
+ };
+ se->checkContext(QStringLiteral("qbs.PropertyList"), dubiousContexts);
+ return obj;
+ } catch (const QString &error) { return throwError(ctx, error); }
+}
+
+void PropertyList::setupMethods(JSContext *ctx, JSValue obj)
+{
+ setupMethod(ctx, obj, "isEmpty", &jsIsEmpty, 0);
+ setupMethod(ctx, obj, "clear", &jsClear, 0);
+ setupMethod(ctx, obj, "readFromObject", &jsReadFromObject, 1);
+ setupMethod(ctx, obj, "readFromString", &jsReadFromString, 1);
+ setupMethod(ctx, obj, "readFromFile", &jsReadFromFile, 1);
+ setupMethod(ctx, obj, "readFromData", &jsReadFromData, 1);
+ setupMethod(ctx, obj, "writeToFile", &jsWriteToFile, 1);
+ setupMethod(ctx, obj, "format", &jsFormat, 0);
+ setupMethod(ctx, obj, "toObject", &jsToObject, 0);
+ setupMethod(ctx, obj, "toString", &jsToString, 1);
+ setupMethod(ctx, obj, "toXMLString", &jsToXmlString, 1);
+ setupMethod(ctx, obj, "toJSON", &jsToJson, 1);
+}
+
+void PropertyList::clear()
+{
+ m_propertyListObject = QVariant();
+ m_propertyListFormat = 0;
+}
+
+void PropertyList::readFromObject(const QVariant &value)
+{
+ m_propertyListObject = value;
+ m_propertyListFormat = 0; // wasn't deserialized from any external format
+}
+
+void PropertyList::readFromString(const QString &input)
+{
+ readFromData(input.toUtf8());
+}
+
+void PropertyList::readFromFile(const QString &filePath)
+{
+ QFile file(filePath);
+ if (file.open(QIODevice::ReadOnly)) {
+ const QByteArray data = file.readAll();
+ if (file.error() == QFile::NoError) {
+ readFromData(data);
+ return;
+ }
+ }
+ throw QStringLiteral("%1: %2").arg(filePath).arg(file.errorString());
+}
+
+void PropertyList::readFromData(const 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))
+ throw QString::fromNSString(errorString);
+ QVariant obj = QPropertyListUtils::fromPropertyList(plist);
+ if (!obj.isNull()) {
+ m_propertyListObject = obj;
+ m_propertyListFormat = internalFormat;
+ } else {
+ throw Tr::tr("error converting property list");
+ }
+ }
+}
+
+void PropertyList::writeToFile(const QString &filePath, const QString &plistFormat)
+{
+ QFile file(filePath);
+ QByteArray data = writeToData(plistFormat);
+ if (Q_LIKELY(!data.isEmpty())) {
+ if (file.open(QIODevice::WriteOnly) && file.write(data) == data.size()) {
+ return;
+ }
+ }
+
+ throw QStringLiteral("%1: %2").arg(filePath).arg(file.errorString());
+}
+
+std::optional<QString> PropertyList::format() const
+{
+ switch (m_propertyListFormat)
+ {
+ case QPropertyListOpenStepFormat:
+ return QStringLiteral("openstep");
+ case QPropertyListXMLFormat_v1_0:
+ return QStringLiteral("xml1");
+ case QPropertyListBinaryFormat_v1_0:
+ return QStringLiteral("binary1");
+ case QPropertyListJSONFormat:
+ return QStringLiteral("json");
+ default:
+ return {};
+ }
+}
+
+QString PropertyList::toString(const QString &plistFormat) const
+{
+ if (plistFormat == QLatin1String("binary1")) {
+ throw Tr::tr("Property list object cannot be converted to a "
+ "string in the binary1 format; this format can only "
+ "be written directly to a file");
+ }
+
+ if (!isEmpty())
+ return QString::fromUtf8(writeToData(plistFormat));
+ return {};
+}
+
+QString PropertyList::toXMLString() const
+{
+ return toString(QStringLiteral("xml1"));
+}
+
+QString PropertyList::toJSON(const QString &style) const
+{
+ QString format = QLatin1String("json");
+ if (!style.isEmpty())
+ format += QLatin1String("-") + style;
+
+ return toString(format);
+}
+
+QByteArray PropertyList::writeToData(const QString &format) const
+{
+ @autoreleasepool {
+ NSError *error = nil;
+ NSString *errorString = nil;
+ NSData *data = nil;
+
+ id obj = QPropertyListUtils::toPropertyList(m_propertyListObject);
+ if (!obj)
+ throw Tr::tr("error converting property list");
+
+ 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))
+ throw QString::fromNSString(errorString);
+
+ return QByteArray::fromNSData(data);
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
+
+void initializeJsExtensionPropertyList(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
+{
+ qbs::Internal::PropertyList::registerClass(engine, extensionObject);
+}
diff --git a/src/lib/corelib/jsextensions/propertylistutils.mm b/src/lib/corelib/jsextensions/propertylistutils.mm
index b8ae1b8e0..704b1a8ce 100644
--- a/src/lib/corelib/jsextensions/propertylistutils.mm
+++ b/src/lib/corelib/jsextensions/propertylistutils.mm
@@ -127,33 +127,33 @@ static NSArray *toArray(const QVariantList &list);
static id toObject(const QVariant &variant)
{
- if (variant.type() == QVariant::Hash) {
+ if (variant.userType() == QMetaType::QVariantHash) {
return toDictionary(qHashToMap(variant.toHash()));
- } else if (variant.type() == QVariant::Map) {
+ } else if (variant.userType() == QMetaType::QVariantMap) {
return toDictionary(variant.toMap());
- } else if (variant.type() == QVariant::List) {
+ } else if (variant.userType() == QMetaType::QVariantList) {
return toArray(variant.toList());
- } else if (variant.type() == QVariant::String) {
+ } else if (variant.userType() == QMetaType::QString) {
return variant.toString().toNSString();
- } else if (variant.type() == QVariant::ByteArray) {
+ } else if (variant.userType() == QMetaType::QByteArray) {
return variant.toByteArray().toNSData();
- } else if (variant.type() == QVariant::Date ||
- variant.type() == QVariant::DateTime) {
+ } else if (variant.userType() == QMetaType::QDate ||
+ variant.userType() == QMetaType::QDateTime) {
return variant.toDateTime().toNSDate();
- } else if (variant.type() == QVariant::Bool) {
+ } else if (variant.userType() == QMetaType::Bool) {
return variant.toBool()
? [NSNumber numberWithBool:YES]
: [NSNumber numberWithBool:NO];
- } else if (variant.type() == QVariant::Char ||
- variant.type() == QVariant::Int) {
+ } else if (variant.userType() == QMetaType::Char ||
+ variant.userType() == QMetaType::Int) {
return [NSNumber numberWithInt:variant.toInt()];
- } else if (variant.type() == QVariant::UInt) {
+ } else if (variant.userType() == QMetaType::UInt) {
return [NSNumber numberWithUnsignedInt:variant.toUInt()];
- } else if (variant.type() == QVariant::LongLong) {
+ } else if (variant.userType() == QMetaType::LongLong) {
return [NSNumber numberWithLongLong:variant.toLongLong()];
- } else if (variant.type() == QVariant::ULongLong) {
+ } else if (variant.userType() == QMetaType::ULongLong) {
return [NSNumber numberWithUnsignedLongLong:variant.toULongLong()];
- } else if (variant.type() == QVariant::Double) {
+ } else if (variant.userType() == QMetaType::Double) {
return [NSNumber numberWithDouble:variant.toDouble()];
} else {
return [NSNull null];
diff --git a/src/lib/corelib/jsextensions/temporarydir.cpp b/src/lib/corelib/jsextensions/temporarydir.cpp
index 470d21d20..209c058f2 100644
--- a/src/lib/corelib/jsextensions/temporarydir.cpp
+++ b/src/lib/corelib/jsextensions/temporarydir.cpp
@@ -38,17 +38,14 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <QtCore/qfileinfo.h>
-#include <QtCore/qobject.h>
#include <QtCore/qtemporarydir.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
@@ -60,63 +57,49 @@ static bool tempDirIsCanonical()
return false;
}
-class TemporaryDir : public QObject, public QScriptable
+class TemporaryDir : public JsExtension<TemporaryDir>
{
- Q_OBJECT
+ friend class JsExtension<TemporaryDir>;
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();
+ static const char *name() { return "TemporaryDir"; }
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst, int, JSValueConst *, int)
+ {
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts{DubiousContext(EvalContext::PropertyEvaluation,
+ DubiousContext::SuggestMoving)};
+ se->checkContext(QStringLiteral("qbs.TemporaryDir"), dubiousContexts);
+ return createObject(ctx);
+ }
+ static void setupMethods(JSContext *ctx, JSValue obj)
+ {
+ setupMethod(ctx, obj, "isValid", &jsIsValid, 0);
+ setupMethod(ctx, obj, "path", &jsPath, 0);
+ setupMethod(ctx, obj, "remove", &jsRemove, 0);
+ }
+
private:
- QTemporaryDir dir;
-};
+ TemporaryDir(JSContext *) { m_dir.setAutoRemove(false); }
-QScriptValue TemporaryDir::ctor(QScriptContext *context, QScriptEngine *engine)
-{
- const auto se = static_cast<ScriptEngine *>(engine);
- const DubiousContextList dubiousContexts({
- DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("qbs.TemporaryDir"), dubiousContexts);
+ DEFINE_JS_FORWARDER(jsIsValid, &TemporaryDir::isValid, "TemporaryDir.isValid")
+ DEFINE_JS_FORWARDER(jsPath, &TemporaryDir::path, "TemporaryDir.path")
+ DEFINE_JS_FORWARDER(jsRemove, &TemporaryDir::remove, "TemporaryDir.remove")
- const auto t = new TemporaryDir(context);
- QScriptValue obj = engine->newQObject(t, QScriptEngine::ScriptOwnership);
- return obj;
-}
+ bool isValid() const { return m_dir.isValid(); }
-TemporaryDir::TemporaryDir(QScriptContext *context)
-{
- Q_UNUSED(context);
- dir.setAutoRemove(false);
-}
+ QString path() const
+ {
+ return tempDirIsCanonical() ? m_dir.path() : QFileInfo(m_dir.path()).canonicalFilePath();
+ }
-bool TemporaryDir::isValid() const
-{
- return dir.isValid();
-}
-
-QString TemporaryDir::path() const
-{
- return tempDirIsCanonical() ? dir.path() : QFileInfo(dir.path()).canonicalFilePath();
-}
+ bool remove() { return m_dir.remove(); }
-bool TemporaryDir::remove()
-{
- return dir.remove();
-}
+ QTemporaryDir m_dir;
+};
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionTemporaryDir(QScriptValue extensionObject)
+void initializeJsExtensionTemporaryDir(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newQMetaObject(&TemporaryDir::staticMetaObject,
- engine->newFunction(&TemporaryDir::ctor));
- extensionObject.setProperty(QStringLiteral("TemporaryDir"), obj);
+ qbs::Internal::TemporaryDir::registerClass(engine, extensionObject);
}
-
-#include "temporarydir.moc"
diff --git a/src/lib/corelib/jsextensions/textfile.cpp b/src/lib/corelib/jsextensions/textfile.cpp
index 7c67f9019..c9f0edec9 100644
--- a/src/lib/corelib/jsextensions/textfile.cpp
+++ b/src/lib/corelib/jsextensions/textfile.cpp
@@ -37,27 +37,29 @@
**
****************************************************************************/
+#include "jsextension.h"
+
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/hostosinfo.h>
#include <QtCore/qfile.h>
#include <QtCore/qfileinfo.h>
-#include <QtCore/qobject.h>
#include <QtCore/qtextstream.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QtCore5Compat/qtextcodec.h>
+#else
+#include <QtCore/qtextcodec.h>
+#endif
namespace qbs {
namespace Internal {
-class TextFile : public QObject, public QScriptable, public ResourceAcquiringScriptObject
+class TextFile : public JsExtension<TextFile>
{
- Q_OBJECT
- Q_ENUMS(OpenMode)
+ friend class JsExtension<TextFile>;
public:
enum OpenMode
{
@@ -67,83 +69,93 @@ public:
Append = 4
};
- static QScriptValue ctor(QScriptContext *context, QScriptEngine *engine);
- ~TextFile() override;
-
- Q_INVOKABLE void close();
- Q_INVOKABLE QString filePath();
- 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);
+ static const char *name() { return "TextFile"; }
+ static void declareEnums(JSContext *ctx, JSValue classObj);
+ static JSValue ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int);
+ static void setupMethods(JSContext *ctx, JSValue obj);
private:
- TextFile(QScriptContext *context, const QString &filePath, OpenMode mode = ReadOnly,
- const QString &codec = QLatin1String("UTF8"));
-
- bool checkForClosed() const;
-
- // ResourceAcquiringScriptObject implementation
- void releaseResources() override;
-
- QFile *m_file;
- QTextStream *m_stream;
+ DEFINE_JS_FORWARDER(jsClose, &TextFile::close, "TextFile.close")
+ DEFINE_JS_FORWARDER(jsFilePath, &TextFile::filePath, "TextFile.filePath")
+ DEFINE_JS_FORWARDER(jsSetCodec, &TextFile::setCodec, "TextFile.setCodec")
+ DEFINE_JS_FORWARDER(jsReadLine, &TextFile::readLine, "TextFile.readLine")
+ DEFINE_JS_FORWARDER(jsReadAll, &TextFile::readAll, "TextFile.readAll")
+ DEFINE_JS_FORWARDER(jsAtEof, &TextFile::atEof, "TextFile.atEof")
+ DEFINE_JS_FORWARDER(jsTruncate, &TextFile::truncate, "TextFile.truncate")
+ DEFINE_JS_FORWARDER(jsWrite, &TextFile::write, "TextFile.write")
+ DEFINE_JS_FORWARDER(jsWriteLine, &TextFile::writeLine, "TextFile.writeLine")
+
+ void close();
+ QString filePath();
+ void setCodec(const QString &codec);
+ QString readLine();
+ QString readAll();
+ bool atEof() const;
+ void truncate();
+ void write(const QString &str);
+ void writeLine(const QString &str);
+
+ TextFile(JSContext *, const QString &filePath, OpenMode mode, const QString &codec);
+
+ void checkForClosed() const;
+
+ std::unique_ptr<QFile> m_file;
+ QTextCodec *m_codec = nullptr;
};
-QScriptValue TextFile::ctor(QScriptContext *context, QScriptEngine *engine)
+void TextFile::declareEnums(JSContext *ctx, JSValue classObj)
{
- 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<OpenMode>(context->argument(1).toInt32())
- );
- break;
- case 3:
- t = new TextFile(context,
- context->argument(0).toString(),
- static_cast<OpenMode>(context->argument(1).toInt32()),
- context->argument(2).toString()
- );
- break;
- default:
- return context->throwError(Tr::tr("TextFile constructor takes at most three parameters."));
- }
+ DECLARE_ENUM(ctx, classObj, ReadOnly);
+ DECLARE_ENUM(ctx, classObj, WriteOnly);
+ DECLARE_ENUM(ctx, classObj, ReadWrite);
+ DECLARE_ENUM(ctx, classObj, Append);
+}
- const auto se = static_cast<ScriptEngine *>(engine);
- se->addResourceAcquiringScriptObject(t);
- const DubiousContextList dubiousContexts({
+JSValue TextFile::ctor(JSContext *ctx, JSValueConst, JSValueConst,
+ int argc, JSValueConst *argv, int)
+{
+ try {
+ const auto filePath = getArgument<QString>(ctx, "TextFile constructor", argc, argv);
+ OpenMode mode = ReadOnly;
+ QString codec = QLatin1String("UTF-8");
+ if (argc > 1) {
+ mode = static_cast<OpenMode>
+ (fromArg<qint32>(ctx, "TextFile constructor", 2, argv[1]));
+ }
+ if (argc > 2) {
+ codec = fromArg<QString>(ctx, "TextFile constructor", 3, argv[2]);
+ }
+ ScopedJsValue obj(ctx, createObject(ctx, filePath, mode, codec));
+
+ const auto se = ScriptEngine::engineForContext(ctx);
+ const DubiousContextList dubiousContexts {
DubiousContext(EvalContext::PropertyEvaluation, DubiousContext::SuggestMoving)
- });
- se->checkContext(QStringLiteral("qbs.TextFile"), dubiousContexts);
- se->setUsesIo();
-
- return engine->newQObject(t, QScriptEngine::QtOwnership);
+ };
+ se->checkContext(QStringLiteral("qbs.TextFile"), dubiousContexts);
+ se->setUsesIo();
+ return obj.release();
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-TextFile::~TextFile()
-{
- delete m_stream;
- delete m_file;
+void TextFile::setupMethods(JSContext *ctx, JSValue obj)
+{
+ setupMethod(ctx, obj, "close", &jsClose, 0);
+ setupMethod(ctx, obj, "filePath", &jsFilePath, 0);
+ setupMethod(ctx, obj, "atEof", &jsAtEof, 0);
+ setupMethod(ctx, obj, "setCodec", &jsSetCodec, 1);
+ setupMethod(ctx, obj, "readLine", &jsReadLine, 0);
+ setupMethod(ctx, obj, "readAll", &jsReadAll, 0);
+ setupMethod(ctx, obj, "truncate", &jsTruncate, 0);
+ setupMethod(ctx, obj, "write", &jsWrite, 1);
+ setupMethod(ctx, obj, "writeLine", &jsWriteLine, 1);
}
-TextFile::TextFile(QScriptContext *context, const QString &filePath, OpenMode mode,
- const QString &codec)
+TextFile::TextFile(JSContext *, 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);
+ auto file = std::make_unique<QFile>(filePath);
+ const auto newCodec = QTextCodec::codecForName(qPrintable(codec));
+ m_codec = newCodec ? newCodec : QTextCodec::codecForName("UTF-8");
QIODevice::OpenMode m = QIODevice::NotOpen;
if (mode & ReadOnly)
m |= QIODevice::ReadOnly;
@@ -151,113 +163,83 @@ TextFile::TextFile(QScriptContext *context, const QString &filePath, OpenMode mo
m |= QIODevice::WriteOnly;
if (mode & Append)
m |= QIODevice::Append;
- 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 = nullptr;
- }
+ m |= QIODevice::Text;
+ if (Q_UNLIKELY(!file->open(m)))
+ throw Tr::tr("Unable to open file '%1': %2").arg(filePath, file->errorString());
+ m_file = std::move(file);
}
void TextFile::close()
{
- if (checkForClosed())
- return;
- delete m_stream;
- m_stream = nullptr;
+ checkForClosed();
m_file->close();
- delete m_file;
- m_file = nullptr;
+ m_file.reset();
}
QString TextFile::filePath()
{
- if (checkForClosed())
- return {};
+ checkForClosed();
return QFileInfo(*m_file).absoluteFilePath();
}
void TextFile::setCodec(const QString &codec)
{
- if (checkForClosed())
- return;
- m_stream->setCodec(qPrintable(codec));
+ checkForClosed();
+ const auto newCodec = QTextCodec::codecForName(qPrintable(codec));
+ if (newCodec)
+ m_codec = newCodec;
}
QString TextFile::readLine()
{
- if (checkForClosed())
- return {};
- return m_stream->readLine();
+ checkForClosed();
+ auto result = m_codec->toUnicode(m_file->readLine());
+ if (!result.isEmpty() && result.back() == QLatin1Char('\n'))
+ result.chop(1);
+ return result;
}
QString TextFile::readAll()
{
- if (checkForClosed())
- return {};
- return m_stream->readAll();
+ checkForClosed();
+ return m_codec->toUnicode(m_file->readAll());
}
bool TextFile::atEof() const
{
- if (checkForClosed())
- return true;
- return m_stream->atEnd();
+ checkForClosed();
+ return m_file->atEnd();
}
void TextFile::truncate()
{
- if (checkForClosed())
- return;
+ checkForClosed();
m_file->resize(0);
- m_stream->reset();
}
void TextFile::write(const QString &str)
{
- if (checkForClosed())
- return;
- (*m_stream) << str;
+ checkForClosed();
+ m_file->write(m_codec->fromUnicode(str));
}
void TextFile::writeLine(const QString &str)
{
- if (checkForClosed())
- return;
- (*m_stream) << str;
- if (HostOsInfo::isWindowsHost())
- (*m_stream) << '\r';
- (*m_stream) << '\n';
+ checkForClosed();
+ m_file->write(m_codec->fromUnicode(str));
+ m_file->putChar('\n');
}
-bool TextFile::checkForClosed() const
+void TextFile::checkForClosed() const
{
- if (m_file)
- return false;
- QScriptContext *ctx = context();
- if (ctx)
- ctx->throwError(Tr::tr("Access to TextFile object that was already closed."));
- return true;
-}
-
-void TextFile::releaseResources()
-{
- close();
- deleteLater();
+ if (!m_file)
+ throw Tr::tr("Access to TextFile object that was already closed.");
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionTextFile(QScriptValue extensionObject)
+void initializeJsExtensionTextFile(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
{
- using namespace qbs::Internal;
- QScriptEngine *engine = extensionObject.engine();
- QScriptValue obj = engine->newQMetaObject(&TextFile::staticMetaObject,
- engine->newFunction(&TextFile::ctor));
- extensionObject.setProperty(QStringLiteral("TextFile"), obj);
+ qbs::Internal::TextFile::registerClass(engine, extensionObject);
}
-
-Q_DECLARE_METATYPE(qbs::Internal::TextFile *)
-
-#include "textfile.moc"
diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp
index b425bb4a2..cdcee59fa 100644
--- a/src/lib/corelib/jsextensions/utilitiesextension.cpp
+++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp
@@ -37,12 +37,15 @@
**
****************************************************************************/
+#include "jsextension.h"
+#include "jsextensions.h"
+
#include <api/languageinfo.h>
-#include <jsextensions/jsextensions.h>
#include <language/scriptengine.h>
#include <logging/translator.h>
#include <tools/architectures.h>
#include <tools/hostosinfo.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
#include <tools/toolchains.h>
#include <tools/version.h>
@@ -72,6 +75,7 @@ struct fat_arch_64 {
#ifdef Q_OS_WIN
+#include <tools/clangclinfo.h>
#include <tools/msvcinfo.h>
#include <tools/vsenvironmentdetector.h>
#endif
@@ -82,120 +86,152 @@ struct fat_arch_64 {
#include <QtCore/qfile.h>
#include <QtCore/qlibrary.h>
-#include <QtScript/qscriptable.h>
-#include <QtScript/qscriptengine.h>
-
namespace qbs {
namespace Internal {
-class UtilitiesExtension : public QObject, QScriptable
+class DummyLogSink : public ILogSink {
+ void doPrintMessage(LoggerLevel, const QString &, const QString &) override { }
+};
+
+class UtilitiesExtension : public JsExtension<UtilitiesExtension>
{
- Q_OBJECT
public:
- static QScriptValue js_ctor(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_canonicalArchitecture(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_canonicalPlatform(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_canonicalTargetArchitecture(QScriptContext *context,
- QScriptEngine *engine);
- static QScriptValue js_canonicalToolchain(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_cStringQuote(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_getHash(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_getNativeSetting(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_kernelVersion(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_clangClCompilerInfo(QScriptContext *context, QScriptEngine *engine);
-
- static QScriptValue js_versionCompare(QScriptContext *context, QScriptEngine *engine);
-
- static QScriptValue js_qmlTypeInfo(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_builtinExtensionNames(QScriptContext *context, QScriptEngine *engine);
- static QScriptValue js_isSharedLibrary(QScriptContext *context, QScriptEngine *engine);
-
- static QScriptValue js_getArchitecturesFromBinary(QScriptContext *context,
- QScriptEngine *engine);
+ static const char *name() { return "Utilities"; }
+ static void setupStaticMethods(JSContext *ctx, JSValue classObj);
+ static JSValue js_canonicalArchitecture(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_canonicalPlatform(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_canonicalTargetArchitecture(JSContext *ctx, JSValueConst,
+ int, JSValueConst *);
+ static JSValue js_canonicalToolchain(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_cStringQuote(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_getHash(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_getNativeSetting(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_kernelVersion(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_nativeSettingGroups(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_rfc1034identifier(JSContext *ctx, JSValueConst, int, JSValueConst *);
+
+ static JSValue js_smimeMessageContent(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_certificateInfo(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_signingIdentities(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_msvcCompilerInfo(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_clangClCompilerInfo(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+ static JSValue js_installedMSVCs(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_installedClangCls(JSContext *ctx, JSValueConst, int, JSValueConst *);
+
+ static JSValue js_versionCompare(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+
+ static JSValue js_qmlTypeInfo(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_builtinExtensionNames(JSContext *ctx, JSValueConst, int, JSValueConst *);
+ static JSValue js_isSharedLibrary(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv);
+
+ static JSValue js_getArchitecturesFromBinary(JSContext *ctx, JSValueConst, int, JSValueConst *);
};
-QScriptValue UtilitiesExtension::js_ctor(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_canonicalPlatform(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- Q_UNUSED(engine);
- return context->throwError(Tr::tr("'Utilities' cannot be instantiated."));
+ try {
+ const auto platform = getArgument<QString>(ctx, "Utilities.canonicalPlatform", argc, argv);
+ if (platform.isEmpty())
+ return makeJsStringList(ctx, {});
+ std::vector<QString> platforms = HostOsInfo::canonicalOSIdentifiers(platform);
+ return makeJsStringList(ctx, QStringList(std::move_iterator(platforms.begin()),
+ std::move_iterator(platforms.end())));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue UtilitiesExtension::js_canonicalPlatform(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_canonicalTargetArchitecture(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- const QScriptValue value = context->argument(0);
- if (value.isUndefined() || value.isNull())
- return value;
-
- if (context->argumentCount() == 1 && value.isString()) {
- return engine->toScriptValue([&value] {
- QStringList list;
- for (const auto &s : HostOsInfo::canonicalOSIdentifiers(value.toString().toStdString()))
- list.push_back(QString::fromStdString(s));
- return list;
- }());
+ try {
+ const auto arch = getArgument<QString>(ctx, "Utilities.canonicalTargetArchitecture",
+ argc, argv);
+ QString endianness, vendor, system, abi;
+ if (argc > 1)
+ endianness = fromArg<QString>(ctx, "Utilities.canonicalTargetArchitecture", 2, argv[1]);
+ if (argc > 2)
+ vendor = fromArg<QString>(ctx, "Utilities.canonicalTargetArchitecture", 3, argv[2]);
+ if (argc > 3)
+ system = fromArg<QString>(ctx, "Utilities.canonicalTargetArchitecture", 4, argv[3]);
+ if (argc > 4)
+ abi = fromArg<QString>(ctx, "Utilities.canonicalTargetArchitecture", 5, argv[4]);
+ return makeJsString(ctx,
+ canonicalTargetArchitecture(arch, endianness, vendor, system, abi));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
-
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("canonicalPlatform expects one argument of type string"));
}
-QScriptValue UtilitiesExtension::js_canonicalTargetArchitecture(QScriptContext *context,
- QScriptEngine *engine)
+void UtilitiesExtension::setupStaticMethods(JSContext *ctx, JSValue classObj)
{
- const QScriptValue arch = context->argument(0);
- if (arch.isUndefined() || arch.isNull())
- return arch;
-
- QScriptValue endianness = context->argument(1);
- if (endianness.isUndefined() || endianness.isNull())
- endianness = QString();
- const QScriptValue vendor = context->argument(2);
- const QScriptValue system = context->argument(3);
- const QScriptValue abi = context->argument(4);
-
- if (!arch.isString() || !endianness.isString()
- || !vendor.isString() || !system.isString() || !abi.isString())
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("canonicalTargetArchitecture expects 1 to 5 arguments of type string"));
-
- return engine->toScriptValue(canonicalTargetArchitecture(arch.toString(), endianness.toString(),
- vendor.toString(),
- system.toString(), abi.toString()));
+ setupMethod(ctx, classObj, "canonicalArchitecture",
+ &UtilitiesExtension::js_canonicalArchitecture, 1);
+ setupMethod(ctx, classObj, "canonicalPlatform",
+ &UtilitiesExtension::js_canonicalPlatform, 1);
+ setupMethod(ctx, classObj, "canonicalTargetArchitecture",
+ &UtilitiesExtension::js_canonicalTargetArchitecture, 1);
+ setupMethod(ctx, classObj, "canonicalToolchain",
+ &UtilitiesExtension::js_canonicalToolchain, 1);
+ setupMethod(ctx, classObj, "cStringQuote", &UtilitiesExtension::js_cStringQuote, 1);
+ setupMethod(ctx, classObj, "getHash", &UtilitiesExtension::js_getHash, 1);
+ setupMethod(ctx, classObj, "getNativeSetting",
+ &UtilitiesExtension::js_getNativeSetting, 3);
+ setupMethod(ctx, classObj, "kernelVersion", &UtilitiesExtension::js_kernelVersion, 0);
+ setupMethod(ctx, classObj, "nativeSettingGroups",
+ &UtilitiesExtension::js_nativeSettingGroups, 1);
+ setupMethod(ctx, classObj, "rfc1034Identifier",
+ &UtilitiesExtension::js_rfc1034identifier, 1);
+ setupMethod(ctx, classObj, "smimeMessageContent",
+ &UtilitiesExtension::js_smimeMessageContent, 1);
+ setupMethod(ctx, classObj, "certificateInfo", &UtilitiesExtension::js_certificateInfo, 1);
+ setupMethod(ctx, classObj, "signingIdentities",
+ &UtilitiesExtension::js_signingIdentities, 1);
+ setupMethod(ctx, classObj, "msvcCompilerInfo",
+ &UtilitiesExtension::js_msvcCompilerInfo, 1);
+ setupMethod(ctx, classObj, "clangClCompilerInfo",
+ &UtilitiesExtension::js_clangClCompilerInfo, 1);
+ setupMethod(ctx, classObj, "installedMSVCs", &UtilitiesExtension::js_installedMSVCs, 1);
+ setupMethod(ctx, classObj, "installedClangCls",
+ &UtilitiesExtension::js_installedClangCls, 1);
+ setupMethod(ctx, classObj, "versionCompare", &UtilitiesExtension::js_versionCompare, 2);
+ setupMethod(ctx, classObj, "qmlTypeInfo", &UtilitiesExtension::js_qmlTypeInfo, 0);
+ setupMethod(ctx, classObj, "builtinExtensionNames",
+ &UtilitiesExtension::js_builtinExtensionNames, 0);
+ setupMethod(ctx, classObj, "isSharedLibrary", &UtilitiesExtension::js_isSharedLibrary, 1);
+ setupMethod(ctx, classObj, "getArchitecturesFromBinary",
+ &UtilitiesExtension::js_getArchitecturesFromBinary, 1);
}
-QScriptValue UtilitiesExtension::js_canonicalArchitecture(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_canonicalArchitecture(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- 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"));
+ try {
+ const auto arch = getArgument<QString>(ctx, "Utilities.canonicalArchitecture", argc, argv);
+ if (arch.isEmpty())
+ return makeJsString(ctx, {});
+ return makeJsString(ctx, canonicalArchitecture(arch));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue UtilitiesExtension::js_canonicalToolchain(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_canonicalToolchain(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- QStringList toolchain;
- for (int i = 0; i < context->argumentCount(); ++i)
- toolchain << context->argument(i).toString();
- return engine->toScriptValue(canonicalToolchain(toolchain));
+ try {
+ QStringList toolchain;
+ for (int i = 0; i < argc; ++i)
+ toolchain << fromArg<QString>(ctx, "Utilities.canonicalToolchain", i + 1, argv[i]);
+ return makeJsStringList(ctx, canonicalToolchain(toolchain));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
// copied from src/corelib/tools/qtools_p.h
-Q_DECL_CONSTEXPR inline char toHexUpper(uint value) Q_DECL_NOTHROW
+Q_DECL_CONSTEXPR inline uchar toHexUpper(uint value) Q_DECL_NOTHROW
{
return "0123456789ABCDEF"[value & 0xF];
}
@@ -323,74 +359,85 @@ static inline QString escapedString(const Char *begin, int length, bool isUnicod
return out;
}
-QScriptValue UtilitiesExtension::js_cStringQuote(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_cStringQuote(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("cStringQuote expects 1 argument"));
+ try {
+ const auto str = getArgument<QString>(ctx, "Utilities.cStringQuote", argc, argv);
+ return makeJsString(ctx, escapedString(
+ reinterpret_cast<const ushort *>(str.constData()), str.size()));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
- QString value = context->argument(0).toString();
- return engine->toScriptValue(escapedString(reinterpret_cast<const ushort *>(value.constData()), value.size()));
}
-QScriptValue UtilitiesExtension::js_getHash(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_getHash(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() < 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("getHash expects 1 argument"));
+ try {
+ const QByteArray input = getArgument<QString>(ctx, "Utilities.getHash", argc, argv)
+ .toLatin1();
+ const QByteArray hash = QCryptographicHash::hash(input, QCryptographicHash::Sha1)
+ .toHex().left(16);
+ return makeJsString(ctx, QString::fromLatin1(hash));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
- 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)
+JSValue UtilitiesExtension::js_getNativeSetting(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() < 1 || context->argumentCount() > 3)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("getNativeSetting expects between 1 and 3 arguments"));
- }
+ try {
+ const auto path = getArgument<QString>(ctx, "Utilities.getNativeSetting", argc, argv);
- QString key = context->argumentCount() > 1 ? context->argument(1).toString() : QString();
+ QString key;
+ if (argc > 1)
+ key = fromArg<QString>(ctx, "Utilities.getNativeSetting", 2, argv[1]);
- // We'll let empty string represent the default registry value
- if (HostOsInfo::isWindowsHost() && key.isEmpty())
- key = StringConstants::dot();
+ // QSettings will ignore the default value if an empty key is passed.
+ if (key.isEmpty()) {
+ key = HostOsInfo::isWindowsHost() ? StringConstants::dot() // default registry value
+ : QLatin1String("__dummy");
+ }
- QVariant defaultValue = context->argumentCount() > 2 ? context->argument(2).toVariant() : QVariant();
+ QVariant defaultValue;
+ if (argc > 2)
+ defaultValue = fromArg<QVariant>(ctx, "Utilities.getNativeSetting", 3, argv[2]);
- QSettings settings(context->argument(0).toString(), QSettings::NativeFormat);
- QVariant value = settings.value(key, defaultValue);
- return value.isNull() ? engine->undefinedValue() : engine->toScriptValue(value);
+ const QSettings settings(path, QSettings::NativeFormat);
+ const QVariant v = settings.value(key, defaultValue);
+ return v.isNull() ? JS_UNDEFINED : makeJsVariant(ctx, v);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
-QScriptValue UtilitiesExtension::js_kernelVersion(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_kernelVersion(JSContext *ctx, JSValueConst, int, JSValueConst *)
{
- Q_UNUSED(context);
- return engine->toScriptValue(QSysInfo::kernelVersion());
+ return makeJsString(ctx, QSysInfo::kernelVersion());
}
-QScriptValue UtilitiesExtension::js_nativeSettingGroups(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_nativeSettingGroups(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() != 1)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("nativeSettingGroups expects 1 argument"));
+ try {
+ const auto path = getArgument<QString>(ctx, "Utilities.nativeSettingGroups", argc, argv);
+ QSettings settings(path, QSettings::NativeFormat);
+ return makeJsStringList(ctx, settings.childGroups());
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
-
- QSettings settings(context->argument(0).toString(), QSettings::NativeFormat);
- return engine->toScriptValue(settings.childGroups());
}
-QScriptValue UtilitiesExtension::js_rfc1034identifier(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_rfc1034identifier(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("rfc1034Identifier expects 1 argument"));
- const QString identifier = context->argument(0).toString();
- return engine->toScriptValue(HostOsInfo::rfc1034Identifier(identifier));
+ try {
+ const auto identifier = getArgument<QString>(ctx, "Utilities.rfc1034identifier", argc, argv);
+ return makeJsString(ctx, HostOsInfo::rfc1034Identifier(identifier));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
}
/**
@@ -403,67 +450,84 @@ QScriptValue UtilitiesExtension::js_rfc1034identifier(QScriptContext *context,
* \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)
+JSValue UtilitiesExtension::js_smimeMessageContent(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
#if !defined(Q_OS_MACOS) && !defined(Q_OS_OSX)
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("smimeMessageContent is not available on this platform"));
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+ return throwError(ctx, QStringLiteral("smimeMessageContent is not available on this platform"));
#else
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("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);
+ try {
+ const QString filePath = getArgument<QString>(ctx, "Utilities.smimeMessageContent",
+ argc, argv);
+ QFile file(filePath);
+ if (!file.open(QIODevice::ReadOnly))
+ return JS_UNDEFINED;
+
+ QByteArray content = smimeMessageContent(file.readAll());
+ if (content.isEmpty())
+ return JS_UNDEFINED;
+ return toJsValue(ctx, content);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
#endif
}
-QScriptValue UtilitiesExtension::js_certificateInfo(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_certificateInfo(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
#if !defined(Q_OS_MACOS) && !defined(Q_OS_OSX)
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("certificateInfo is not available on this platform"));
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+ return throwError(ctx, QStringLiteral("certificateInfo is not available on this platform"));
#else
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("certificateInfo expects 1 argument"));
- return engine->toScriptValue(certificateInfo(context->argument(0).toVariant().toByteArray()));
+ try {
+ const QVariant arg = getArgument<QVariant>(ctx, "Utilities.certificateInfo", argc, argv);
+ return toJsValue(ctx, certificateInfo(arg.toByteArray()));
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
#endif
}
// Rough command line equivalent: security find-identity -p codesigning -v
-QScriptValue UtilitiesExtension::js_signingIdentities(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_signingIdentities(JSContext *ctx, JSValueConst, int, JSValueConst *)
{
#if !defined(Q_OS_MACOS) && !defined(Q_OS_OSX)
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("signingIdentities is not available on this platform"));
+ return throwError(ctx, QStringLiteral("signingIdentities is not available on this platform"));
#else
- Q_UNUSED(context);
- return engine->toScriptValue(identitiesProperties());
+ return makeJsVariantMap(ctx, identitiesProperties());
#endif
}
#ifdef Q_OS_WIN
+// Try to detect the cross-architecture from the compiler path; prepend the host architecture
+// if it is differ than the target architecture (e.g. something like x64_x86 and so forth).
+static QString detectArchitecture(const QString &compilerFilePath, const QString &targetArch)
+{
+ const auto startIndex = compilerFilePath.lastIndexOf(QLatin1String("Host"));
+ if (startIndex == -1)
+ return targetArch;
+ const auto endIndex = compilerFilePath.indexOf(QLatin1Char('/'), startIndex);
+ if (endIndex == -1)
+ return targetArch;
+ const auto hostArch = compilerFilePath.mid(startIndex + 4, endIndex - startIndex - 4).toLower();
+ if (hostArch.isEmpty() || (hostArch == targetArch))
+ return targetArch;
+ return hostArch + QLatin1Char('_') + targetArch;
+}
+
static std::pair<QVariantMap /*result*/, QString /*error*/> msvcCompilerInfoHelper(
const QString &compilerFilePath,
MSVC::CompilerLanguage language,
const QString &vcvarsallPath,
- const QString &arch)
+ const QString &arch,
+ const QString &sdkVersion)
{
- MSVC msvc(compilerFilePath, arch);
+ QString detailedArch = detectArchitecture(compilerFilePath, arch);
+ MSVC msvc(compilerFilePath, std::move(detailedArch), sdkVersion);
VsEnvironmentDetector envdetector(vcvarsallPath);
if (!envdetector.start(&msvc))
return { {}, QStringLiteral("Detecting the MSVC build environment failed: ")
@@ -471,7 +535,8 @@ static std::pair<QVariantMap /*result*/, QString /*error*/> msvcCompilerInfoHelp
try {
QVariantMap envMap;
- for (const QString &key : msvc.environment.keys())
+ const auto keys = msvc.environment.keys();
+ for (const QString &key : keys)
envMap.insert(key, msvc.environment.value(key));
return {
@@ -487,108 +552,164 @@ static std::pair<QVariantMap /*result*/, QString /*error*/> msvcCompilerInfoHelp
}
#endif
-QScriptValue UtilitiesExtension::js_msvcCompilerInfo(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_msvcCompilerInfo(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
#ifndef Q_OS_WIN
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("msvcCompilerInfo is not available on this platform"));
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+ return throwError(ctx, QStringLiteral("msvcCompilerInfo is not available on this platform"));
#else
- if (Q_UNLIKELY(context->argumentCount() < 2))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("msvcCompilerInfo expects 2 arguments"));
+ try {
+ const auto args = getArguments<QString, QString, QString>(ctx, "Utilities.msvcCompilerInfo", argc, argv);
+ const QString compilerFilePath = std::get<0>(args);
+ const QString compilerLanguage = std::get<1>(args);
+ const QString sdkVersion = std::get<2>(args);
+ MSVC::CompilerLanguage language;
+ if (compilerLanguage == QStringLiteral("c"))
+ language = MSVC::CLanguage;
+ else if (compilerLanguage == StringConstants::cppLang())
+ language = MSVC::CPlusPlusLanguage;
+ else
+ throw Tr::tr("Utilities.msvcCompilerInfo expects \"c\" or \"cpp\" as its second argument");
+ const auto result = msvcCompilerInfoHelper(
+ compilerFilePath,
+ language,
+ {},
+ MSVC::architectureFromClPath(compilerFilePath),
+ sdkVersion);
+ if (result.first.isEmpty())
+ throw result.second;
+ return toJsValue(ctx, result.first);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
+#endif
+}
- const QString compilerFilePath = context->argument(0).toString();
- const QString compilerLanguage = context->argument(1).toString();
+JSValue UtilitiesExtension::js_clangClCompilerInfo(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
+{
+#ifndef Q_OS_WIN
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+ return throwError(ctx, QStringLiteral("clangClCompilerInfo is not available on this platform"));
+#else
+ try {
+ const auto args = getArguments<QString, QString, QString, QString, QString>(ctx, "Utilities.clangClCompilerInfo", argc, argv);
+ const QString compilerFilePath = std::get<0>(args);
+
+ // architecture cannot be empty as vcvarsall.bat requires at least 1 arg, so fallback
+ // to host architecture if none is present
+ QString arch = std::get<1>(args);
+ if (arch.isEmpty())
+ arch = HostOsInfo::hostOSArchitecture();
+
+ const QString vcvarsallPath = std::get<2>(args);
+ const QString compilerLanguage = std::get<3>(args);
+ const QString sdkVersion = std::get<4>(args);
MSVC::CompilerLanguage language;
if (compilerLanguage == QStringLiteral("c"))
language = MSVC::CLanguage;
else if (compilerLanguage == StringConstants::cppLang())
language = MSVC::CPlusPlusLanguage;
else
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("msvcCompilerInfo expects \"c\" or \"cpp\" as its second argument"));
+ throw Tr::tr("Utilities.clangClCompilerInfo expects \"c\" or \"cpp\" as its fourth argument");
const auto result = msvcCompilerInfoHelper(
- compilerFilePath, language, {}, MSVC::architectureFromClPath(compilerFilePath));
+ compilerFilePath, language, vcvarsallPath, arch, sdkVersion);
if (result.first.isEmpty())
- return context->throwError(QScriptContext::UnknownError, result.second);
- return engine->toScriptValue(result.first);
+ throw result.second;
+ return toJsValue(ctx, result.first);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
#endif
}
-QScriptValue UtilitiesExtension::js_clangClCompilerInfo(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_installedMSVCs(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
#ifndef Q_OS_WIN
- Q_UNUSED(engine);
- return context->throwError(QScriptContext::UnknownError,
- QStringLiteral("clangClCompilerInfo is not available on this platform"));
+ return throwError(ctx, QStringLiteral("Utilities.installedMSVCs is not available on this platform"));
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
#else
- if (Q_UNLIKELY(context->argumentCount() < 4))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("clangClCompilerInfo expects 4 arguments"));
-
- const QString compilerFilePath = context->argument(0).toString();
- QString arch = context->argument(1).toString();
- QString vcvarsallPath = context->argument(2).toString();
- const QString compilerLanguage = context->argumentCount() > 3
- ? context->argument(3).toString()
- : QString();
- MSVC::CompilerLanguage language;
- if (compilerLanguage == QStringLiteral("c"))
- language = MSVC::CLanguage;
- else if (compilerLanguage == StringConstants::cppLang())
- language = MSVC::CPlusPlusLanguage;
- else
- return context->throwError(QScriptContext::TypeError,
- QStringLiteral("clangClCompilerInfo expects \"c\" or \"cpp\" as its fourth argument"));
-
- const auto result = msvcCompilerInfoHelper(
- compilerFilePath, language, vcvarsallPath, arch);
- if (result.first.isEmpty())
- return context->throwError(QScriptContext::UnknownError, result.second);
- return engine->toScriptValue(result.first);
+ try {
+ const QString hostArch = HostOsInfo::hostOSArchitecture();
+ QString preferredArch = getArgument<QString>(ctx, "Utilities.installedMSVCs", argc, argv);
+ if (preferredArch.isEmpty())
+ preferredArch = hostArch;
+
+ DummyLogSink dummySink;
+ Logger dummyLogger(&dummySink);
+ auto msvcs = MSVC::installedCompilers(dummyLogger);
+
+ const auto predicate = [&preferredArch, &hostArch](const MSVC &msvc)
+ {
+ auto archPair = MSVC::getHostTargetArchPair(msvc.architecture);
+ return archPair.first != hostArch || preferredArch != archPair.second;
+ };
+ Internal::removeIf(msvcs, predicate);
+ QVariantList result;
+ for (const auto &msvc: msvcs)
+ result.append(msvc.toVariantMap());
+ return makeJsVariantList(ctx, result);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
+ }
#endif
}
-QScriptValue UtilitiesExtension::js_versionCompare(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_installedClangCls(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv)
{
- if (context->argumentCount() == 2) {
- const QScriptValue value1 = context->argument(0);
- const QScriptValue value2 = context->argument(1);
- if (value1.isString() && value2.isString()) {
- const auto a = Version::fromString(value1.toString());
- const auto b = Version::fromString(value2.toString());
- return engine->toScriptValue(compare(a, b));
- }
+#ifndef Q_OS_WIN
+ Q_UNUSED(argc)
+ Q_UNUSED(argv)
+ return throwError(ctx, QStringLiteral("Utilities.installedClangCls is not available on this platform"));
+#else
+ try {
+ const QString path = getArgument<QString>(ctx, "Utilities.installedClangCls", argc, argv);
+ DummyLogSink dummySink;
+ Logger dummyLogger(&dummySink);
+ auto compilers = ClangClInfo::installedCompilers({path}, dummyLogger);
+ QVariantList result;
+ for (const auto &compiler: compilers)
+ result.append(compiler.toVariantMap());
+ return makeJsVariantList(ctx, result);
+ } catch (const QString &error) {
+ return throwError(ctx, error);
}
+#endif
+}
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("versionCompare expects two arguments of type string"));
+JSValue UtilitiesExtension::js_versionCompare(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
+{
+ try {
+ const auto args = getArguments<QString, QString>(ctx, "Utilities.versionCompare",
+ argc, argv);
+ const auto a = Version::fromString(std::get<0>(args), true);
+ const auto b = Version::fromString(std::get<1>(args), true);
+ return JS_NewInt32(ctx, compare(a, b));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
-QScriptValue UtilitiesExtension::js_qmlTypeInfo(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_qmlTypeInfo(JSContext *ctx, JSValueConst, int, JSValueConst *)
{
- Q_UNUSED(context);
- return engine->toScriptValue(QString::fromStdString(qbs::LanguageInfo::qmlTypeInfo()));
+ return makeJsString(ctx, QString::fromStdString(qbs::LanguageInfo::qmlTypeInfo()));
}
-QScriptValue UtilitiesExtension::js_builtinExtensionNames(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_builtinExtensionNames(JSContext *ctx, JSValueConst,
+ int, JSValueConst *)
{
- Q_UNUSED(context);
- return engine->toScriptValue(JsExtensions::extensionNames());
+ return makeJsStringList(ctx, JsExtensions::extensionNames());
}
-QScriptValue UtilitiesExtension::js_isSharedLibrary(QScriptContext *context, QScriptEngine *engine)
+JSValue UtilitiesExtension::js_isSharedLibrary(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (context->argumentCount() == 1) {
- const QScriptValue value = context->argument(0);
- if (value.isString())
- return engine->toScriptValue(QLibrary::isLibrary(value.toString()));
- }
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("isSharedLibrary expects one argument of type string"));
+ try {
+ const auto fileName = getArgument<QString>(ctx, "Utilities.isSharedLibrary", argc, argv);
+ return JS_NewBool(ctx, QLibrary::isLibrary(fileName));
+ } catch (const QString &error) { return throwError(ctx, error); }
}
#ifdef __APPLE__
@@ -661,7 +782,7 @@ static QStringList detectMachOArchs(QIODevice *device)
if (strncmp(ar_header, ARMAG, SARMAG) == 0) {
while (!device->atEnd()) {
static_assert(sizeof(ar_hdr) == 60, "sizeof(ar_hdr) != 60");
- ar_hdr header;
+ ar_hdr header{};
if (device->read(reinterpret_cast<char *>(&header),
sizeof(ar_hdr)) != sizeof(ar_hdr))
return {};
@@ -711,7 +832,7 @@ static QStringList detectMachOArchs(QIODevice *device)
pos = device->pos();
- fat_header fatheader;
+ fat_header fatheader{};
fatheader.magic = readInt(device, nullptr, false);
if (fatheader.magic == FAT_MAGIC || fatheader.magic == FAT_CIGAM ||
fatheader.magic == FAT_MAGIC_64 || fatheader.magic == FAT_CIGAM_64) {
@@ -724,7 +845,7 @@ static QStringList detectMachOArchs(QIODevice *device)
QStringList archs;
for (uint32_t n = 0; n < fatheader.nfat_arch; ++n) {
- fat_arch_64 fatarch;
+ fat_arch_64 fatarch{};
static_assert(sizeof(fat_arch_64) == 32, "sizeof(fat_arch_64) != 32");
static_assert(sizeof(fat_arch) == 20, "sizeof(fat_arch) != 20");
const qint64 expectedBytes = is64bit ? sizeof(fat_arch_64) : sizeof(fat_arch);
@@ -754,7 +875,7 @@ static QStringList detectMachOArchs(QIODevice *device)
return {};
bool swap = false;
- mach_header header;
+ mach_header header{};
header.magic = readInt(device, nullptr, swap);
switch (header.magic) {
case MH_CIGAM:
@@ -786,84 +907,32 @@ static QStringList detectMachOArchs(QIODevice *device)
}
#endif
-QScriptValue UtilitiesExtension::js_getArchitecturesFromBinary(QScriptContext *context,
- QScriptEngine *engine)
+JSValue UtilitiesExtension::js_getArchitecturesFromBinary(JSContext *ctx, JSValueConst,
+ int argc, JSValueConst *argv)
{
- if (context->argumentCount() != 1) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("getArchitecturesFromBinary expects exactly one argument"));
- }
- const QScriptValue arg = context->argument(0);
- if (!arg.isString()) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("getArchitecturesFromBinary expects a string argument"));
- }
- QStringList archs;
+ try {
+ const auto path = getArgument<QString>(ctx, "Utilities.getArchitecturesFromBinary",
+ argc, argv);
+ QStringList archs;
#ifdef __APPLE__
- QFile file(arg.toString());
- if (!file.open(QIODevice::ReadOnly)) {
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("Failed to open file '%1': %2")
- .arg(file.fileName(), file.errorString()));
- }
- archs = detectMachOArchs(&file);
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly)) {
+ throw Tr::tr("In Utilities.getArchitecturesFromBinary: "
+ "Failed to open file '%1': %2")
+ .arg(file.fileName(), file.errorString());
+ }
+ archs = detectMachOArchs(&file);
+#else
+ Q_UNUSED(path)
#endif // __APPLE__
- return engine->toScriptValue(archs);
+ return makeJsStringList(ctx, archs);
+ } catch (const QString &error) { return throwError(ctx, error); }
}
} // namespace Internal
} // namespace qbs
-void initializeJsExtensionUtilities(QScriptValue extensionObject)
-{
- using namespace qbs::Internal;
- 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("canonicalPlatform"),
- engine->newFunction(UtilitiesExtension::js_canonicalPlatform, 1));
- environmentObj.setProperty(QStringLiteral("canonicalTargetArchitecture"),
- engine->newFunction(
- UtilitiesExtension::js_canonicalTargetArchitecture, 4));
- environmentObj.setProperty(QStringLiteral("canonicalToolchain"),
- engine->newFunction(UtilitiesExtension::js_canonicalToolchain));
- environmentObj.setProperty(QStringLiteral("cStringQuote"),
- engine->newFunction(UtilitiesExtension::js_cStringQuote, 1));
- environmentObj.setProperty(QStringLiteral("getHash"),
- engine->newFunction(UtilitiesExtension::js_getHash, 1));
- environmentObj.setProperty(QStringLiteral("getNativeSetting"),
- engine->newFunction(UtilitiesExtension::js_getNativeSetting, 3));
- environmentObj.setProperty(QStringLiteral("kernelVersion"),
- engine->newFunction(UtilitiesExtension::js_kernelVersion, 0));
- 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("clangClCompilerInfo"),
- engine->newFunction(UtilitiesExtension::js_clangClCompilerInfo, 1));
- environmentObj.setProperty(QStringLiteral("versionCompare"),
- engine->newFunction(UtilitiesExtension::js_versionCompare, 2));
- environmentObj.setProperty(QStringLiteral("qmlTypeInfo"),
- engine->newFunction(UtilitiesExtension::js_qmlTypeInfo, 0));
- environmentObj.setProperty(QStringLiteral("builtinExtensionNames"),
- engine->newFunction(UtilitiesExtension::js_builtinExtensionNames, 0));
- environmentObj.setProperty(QStringLiteral("isSharedLibrary"),
- engine->newFunction(UtilitiesExtension::js_isSharedLibrary, 1));
- environmentObj.setProperty(QStringLiteral("getArchitecturesFromBinary"),
- engine->newFunction(UtilitiesExtension::js_getArchitecturesFromBinary, 1));
- extensionObject.setProperty(QStringLiteral("Utilities"), environmentObj);
-}
-
-Q_DECLARE_METATYPE(qbs::Internal::UtilitiesExtension *)
-
-#include "utilitiesextension.moc"
+void initializeJsExtensionUtilities(qbs::Internal::ScriptEngine *engine, JSValue extensionObject)
+{
+ qbs::Internal::UtilitiesExtension::registerClass(engine, extensionObject);
+}
diff --git a/src/lib/corelib/language/artifactproperties.cpp b/src/lib/corelib/language/artifactproperties.cpp
index e5b11f4d7..011e58d88 100644
--- a/src/lib/corelib/language/artifactproperties.cpp
+++ b/src/lib/corelib/language/artifactproperties.cpp
@@ -64,6 +64,7 @@ bool operator==(const ArtifactProperties &ap1, const ArtifactProperties &ap2)
{
return ap1.fileTagsFilter() == ap2.fileTagsFilter()
&& ap1.extraFileTags() == ap2.extraFileTags()
+ && !ap1.propertyMap() == !ap2.propertyMap()
&& *ap1.propertyMap() == *ap2.propertyMap();
}
diff --git a/src/lib/corelib/language/astimportshandler.cpp b/src/lib/corelib/language/astimportshandler.cpp
deleted file mode 100644
index d634af7e4..000000000
--- a/src/lib/corelib/language/astimportshandler.cpp
+++ /dev/null
@@ -1,305 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <logging/logger.h>
-#include <logging/translator.h>
-#include <parser/qmljsast_p.h>
-#include <tools/error.h>
-#include <tools/fileinfo.h>
-#include <tools/qttools.h>
-#include <tools/stringconstants.h>
-#include <tools/version.h>
-
-#include <QtCore/qdiriterator.h>
-
-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)
-{
- const auto searchPaths = m_file->searchPaths();
- for (const QString &searchPath : searchPaths)
- collectPrototypes(searchPath + QStringLiteral("/imports"), QString());
-
- // files in the same directory are available as prototypes
- collectPrototypes(m_directory, QString());
-
- bool baseImported = false;
- for (const auto *it = uiImportList; it; it = it->next)
- handleImport(it->import, &baseImported);
- if (!baseImported) {
- QStringRef qbsref(&StringConstants::qbsModule());
- QbsQmlJS::AST::UiQualifiedId qbsURI(qbsref);
- qbsURI.finish();
- QbsQmlJS::AST::UiImport imp(&qbsURI);
- handleImport(&imp, &baseImported);
- }
-
- for (auto it = m_jsImports.constBegin(); it != m_jsImports.constEnd(); ++it)
- m_file->addJsImport(it.value());
-}
-
-void ASTImportsHandler::handleImport(const QbsQmlJS::AST::UiImport *import, bool *baseImported)
-{
- QStringList importUri;
- bool isBase = false;
- if (import->importUri) {
- importUri = toStringList(import->importUri);
- isBase = (importUri.size() == 1 && importUri.front() == StringConstants::qbsModule())
- || (importUri.size() == 2 && importUri.front() == StringConstants::qbsModule()
- && importUri.last() == StringConstants::baseVar());
- if (isBase) {
- *baseImported = true;
- 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 <Name>'"),
- toCodeLocation(m_file->filePath(), import->importIdToken));
- }
- } else {
- if (importUri.size() == 2 && importUri.front() == StringConstants::qbsModule()) {
- 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 <Name>'"),
- toCodeLocation(m_file->filePath(), import->importToken));
- }
- if (importUri.empty()) {
- throw ErrorInfo(Tr::tr("Invalid import URI."),
- toCodeLocation(m_file->filePath(), import->importToken));
- }
- as = importUri.last();
- } else {
- as = import->importId.toString();
- }
-
- 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));
- if (Q_UNLIKELY(!m_importAsNames.insert(as).second)) {
- throw ErrorInfo(Tr::tr("Cannot import into the same name more than once."),
- toCodeLocation(m_file->filePath(), import->importIdToken));
- }
- }
-
- 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(QStringLiteral(".js"), Qt::CaseInsensitive)) {
- JsImport &jsImport = m_jsImports[as];
- jsImport.scopeName = as;
- jsImport.filePaths.push_back(filePath);
- jsImport.location
- = toCodeLocation(m_file->filePath(), import->firstSourceLocation());
- } else if (filePath.endsWith(QStringLiteral(".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.empty()) {
- const QString importPath = isBase
- ? QStringLiteral("qbs/base") : importUri.join(QDir::separator());
- bool found = m_typeNameToFile.contains(importUri);
- if (!found) {
- const auto searchPaths = m_file->searchPaths();
- for (const QString &searchPath : searchPaths) {
- const QFileInfo fi(FileInfo::resolvePath(
- FileInfo::resolvePath(searchPath,
- StringConstants::importsDir()),
- importPath));
- if (fi.isDir()) {
- // ### versioning, qbsdir file, etc.
- const QString &resultPath = fi.absoluteFilePath();
- collectPrototypesAndJsCollections(resultPath, as,
- toCodeLocation(m_file->filePath(), import->fileNameToken));
- 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.push_back(as);
- prototypeName.push_back(componentName);
- if (!m_typeNameToFile.contains(prototypeName))
- 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)) {
- for (const QString &fileName : qAsConst(fileNames))
- addPrototype(fileName, path + QLatin1Char('/') + fileName, as, false);
- return;
- }
-
- QDirIterator dirIter(path, StringConstants::qbsFileWildcards());
- 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, StringConstants::jsFileWildcards());
- while (dirIter.hasNext()) {
- dirIter.next();
- JsImport &jsImport = m_jsImports[as];
- if (jsImport.scopeName.isNull()) {
- jsImport.scopeName = as;
- jsImport.location = location;
- }
- jsImport.filePaths.push_back(dirIter.filePath());
- }
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/astimportshandler.h b/src/lib/corelib/language/astimportshandler.h
deleted file mode 100644
index e9c2b6c27..000000000
--- a/src/lib/corelib/language/astimportshandler.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <parser/qmljsastfwd_p.h>
-#include <tools/set.h>
-
-#include <QtCore/qhash.h>
-#include <QtCore/qstringlist.h>
-
-namespace qbs {
-class CodeLocation;
-class Version;
-
-namespace Internal {
-class ItemReaderVisitorState;
-class JsImport;
-class Logger;
-
-class ASTImportsHandler
-{
-public:
- ASTImportsHandler(ItemReaderVisitorState &visitorState, Logger &logger,
- const FileContextPtr &file);
-
- void handleImports(const QbsQmlJS::AST::UiImportList *uiImportList);
-
- QHash<QStringList, QString> 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, bool *baseImported);
-
- ItemReaderVisitorState &m_visitorState;
- Logger &m_logger;
- const FileContextPtr &m_file;
- const QString m_directory;
- QHash<QStringList, QString> m_typeNameToFile;
- Set<QString> m_importAsNames;
-
- using JsImportsHash = QHash<QString, JsImport>;
- 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
deleted file mode 100644
index 1ea78bf79..000000000
--- a/src/lib/corelib/language/astpropertiesitemhandler.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <logging/translator.h>
-#include <tools/error.h>
-#include <tools/qbsassert.h>
-#include <tools/stringconstants.h>
-
-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;
- bool remove = false;
- if (child->type() == ItemType::Properties) {
- handlePropertiesBlock(child);
- remove = m_parentItem->type() != ItemType::Export;
- }
- if (remove)
- it = m_parentItem->m_children.erase(it);
- else
- ++it;
- }
-}
-
-class PropertiesBlockConverter
-{
-public:
- PropertiesBlockConverter(const JSSourceValue::AltProperty &condition,
- const JSSourceValue::AltProperty &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() == StringConstants::conditionProperty()
- || it.key() == StringConstants::overrideListPropertiesProperty())) {
- continue;
- }
- if (it.value()->type() == Value::ItemValueType) {
- Item * const innerVal = std::static_pointer_cast<ItemValue>(it.value())->item();
- ItemValuePtr outerVal = outer->itemProperty(it.key());
- if (!outerVal) {
- outerVal = ItemValue::create(Item::create(outer->pool(), innerVal->type()),
- true);
- outer->setProperty(it.key(), outerVal);
- }
- doApply(outerVal->item(), innerVal);
- } 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, std::static_pointer_cast<JSSourceValue>(outerVal),
- std::static_pointer_cast<JSSourceValue>(it.value()));
- } else {
- QBS_CHECK(!"Unexpected value type in conditional value.");
- }
- }
- }
-
- void doApply(const QString &propertyName, Item *item, JSSourceValuePtr value,
- const JSSourceValuePtr &conditionalValue)
- {
- if (!value) {
- value = JSSourceValue::create(true);
- value->setFile(conditionalValue->file());
- item->setProperty(propertyName, value);
- value->setSourceCode(QStringRef(&StringConstants::baseVar()));
- value->setSourceUsesBaseFlag();
- }
- m_alternative.value = conditionalValue;
- value->addAlternative(m_alternative);
- }
-};
-
-static JSSourceValue::AltProperty getPropertyData(const Item *propertiesItem, const QString &name)
-{
- const ValuePtr value = propertiesItem->property(name);
- if (!value) {
- if (name == StringConstants::conditionProperty()) {
- throw ErrorInfo(Tr::tr("Properties.condition must be provided."),
- propertiesItem->location());
- }
- return JSSourceValue::AltProperty(StringConstants::falseValue(),
- propertiesItem->location());
- }
- if (Q_UNLIKELY(value->type() != Value::JSSourceValueType)) {
- throw ErrorInfo(Tr::tr("Properties.%1 must be a value binding.").arg(name),
- propertiesItem->location());
- }
- if (name == StringConstants::overrideListPropertiesProperty()) {
- 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 = std::static_pointer_cast<JSSourceValue>(value);
- return JSSourceValue::AltProperty(srcval->sourceCodeForEvaluation(), srcval->location());
-}
-
-void ASTPropertiesItemHandler::handlePropertiesBlock(const Item *propertiesItem)
-{
- const auto condition = getPropertyData(propertiesItem, StringConstants::conditionProperty());
- const auto overrideListProperties = getPropertyData(propertiesItem,
- StringConstants::overrideListPropertiesProperty());
- 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
deleted file mode 100644
index 413512ee5..000000000
--- a/src/lib/corelib/language/astpropertiesitemhandler.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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
index 617c8b95b..0d0c234c4 100644
--- a/src/lib/corelib/language/asttools.cpp
+++ b/src/lib/corelib/language/asttools.cpp
@@ -61,13 +61,13 @@ QString textOf(const QString &source, QbsQmlJS::AST::Node *node)
if (!node)
return {};
return source.mid(node->firstSourceLocation().begin(),
- node->lastSourceLocation().end() - node->firstSourceLocation().begin());
+ int(node->lastSourceLocation().end() - node->firstSourceLocation().begin()));
}
-QStringRef textRefOf(const QString &source, QbsQmlJS::AST::Node *node)
+QStringView textViewOf(const QString &source, QbsQmlJS::AST::Node *node)
{
const quint32 firstBegin = node->firstSourceLocation().begin();
- return source.midRef(firstBegin, node->lastSourceLocation().end() - firstBegin);
+ return QStringView(source).mid(firstBegin, int(node->lastSourceLocation().end() - firstBegin));
}
} // namespace Internal
diff --git a/src/lib/corelib/language/asttools.h b/src/lib/corelib/language/asttools.h
index b4f5c4d98..086d59290 100644
--- a/src/lib/corelib/language/asttools.h
+++ b/src/lib/corelib/language/asttools.h
@@ -50,7 +50,7 @@ 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);
+QStringView textViewOf(const QString &source, QbsQmlJS::AST::Node *node);
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/builtindeclarations.cpp b/src/lib/corelib/language/builtindeclarations.cpp
index 68355df51..44dc8a326 100644
--- a/src/lib/corelib/language/builtindeclarations.cpp
+++ b/src/lib/corelib/language/builtindeclarations.cpp
@@ -125,7 +125,7 @@ ItemDeclaration BuiltinDeclarations::declarationsForType(ItemType type) const
}
ItemType BuiltinDeclarations::typeForName(const QString &typeName,
- const CodeLocation location) const
+ const CodeLocation &location) const
{
const auto it = m_typeMap.constFind(typeName);
if (it == m_typeMap.constEnd())
@@ -166,25 +166,28 @@ void BuiltinDeclarations::insert(const ItemDeclaration &decl)
static PropertyDeclaration conditionProperty()
{
- return PropertyDeclaration(StringConstants::conditionProperty(), PropertyDeclaration::Boolean,
- StringConstants::trueValue());
+ return {
+ StringConstants::conditionProperty(),
+ PropertyDeclaration::Boolean,
+ StringConstants::trueValue()};
}
static PropertyDeclaration alwaysRunProperty()
{
- return PropertyDeclaration(StringConstants::alwaysRunProperty(), PropertyDeclaration::Boolean,
- StringConstants::falseValue());
+ return {
+ StringConstants::alwaysRunProperty(),
+ PropertyDeclaration::Boolean,
+ StringConstants::falseValue()};
}
static PropertyDeclaration nameProperty()
{
- return PropertyDeclaration(StringConstants::nameProperty(), PropertyDeclaration::String);
+ return {StringConstants::nameProperty(), PropertyDeclaration::String};
}
static PropertyDeclaration buildDirProperty()
{
- return PropertyDeclaration(StringConstants::buildDirectoryProperty(),
- PropertyDeclaration::Path);
+ return {StringConstants::buildDirectoryProperty(), PropertyDeclaration::Path};
}
static PropertyDeclaration prepareScriptProperty()
@@ -244,11 +247,11 @@ void BuiltinDeclarations::addDependsItem()
PropertyDeclaration::StringList);
item << PropertyDeclaration(StringConstants::limitToSubProjectProperty(),
PropertyDeclaration::Boolean, StringConstants::falseValue());
- item << PropertyDeclaration(StringConstants::multiplexConfigurationIdsProperty(),
- PropertyDeclaration::StringList, QString(),
- PropertyDeclaration::ReadOnlyFlag);
- item << PropertyDeclaration(StringConstants::enableFallbackProperty(),
- PropertyDeclaration::Boolean, StringConstants::trueValue());
+ item << PropertyDeclaration(
+ StringConstants::multiplexConfigurationIdsProperty(),
+ PropertyDeclaration::StringList,
+ QString(),
+ PropertyDeclaration::ReadOnlyFlag);
insert(item);
}
@@ -258,7 +261,6 @@ void BuiltinDeclarations::addExportItem()
item << PropertyDeclaration(StringConstants::prefixMappingProperty(),
PropertyDeclaration::Variant);
auto allowedChildTypes = item.allowedChildTypes();
- allowedChildTypes.insert(ItemType::Parameters);
allowedChildTypes.insert(ItemType::Properties);
item.setAllowedChildTypes(allowedChildTypes);
insert(item);
@@ -326,25 +328,28 @@ void BuiltinDeclarations::addModuleProviderItem()
ItemDeclaration item(ItemType::ModuleProvider);
item << nameProperty()
<< PropertyDeclaration(QStringLiteral("outputBaseDir"), PropertyDeclaration::String)
+ << PropertyDeclaration(StringConstants::isEagerProperty(),
+ PropertyDeclaration::Boolean,
+ StringConstants::trueValue())
+ << PropertyDeclaration(StringConstants::moduleNameProperty(), PropertyDeclaration::String)
<< PropertyDeclaration(QStringLiteral("relativeSearchPaths"),
PropertyDeclaration::StringList);
+ item.setAllowedChildTypes({ItemType::PropertyOptions, ItemType::Probe});
insert(item);
}
ItemDeclaration BuiltinDeclarations::moduleLikeItem(ItemType type)
{
ItemDeclaration item(type);
- item.setAllowedChildTypes(ItemDeclaration::TypeNames()
- << ItemType::Group
- << ItemType::Depends
- << ItemType::FileTagger
- << ItemType::JobLimit
- << ItemType::Rule
- << ItemType::Parameter
- << ItemType::Probe
- << ItemType::PropertyOptions
- << ItemType::Scanner);
- item << nameProperty();
+ item.setAllowedChildTypes({ItemType::Depends, ItemType::FileTagger, ItemType::Group,
+ ItemType::JobLimit, ItemType::Parameter, ItemType::Parameters,
+ ItemType::Probe, ItemType::PropertyOptions,
+ ItemType::Rule, ItemType::Scanner});
+ PropertyDeclaration nameDecl = nameProperty();
+ PropertyDeclaration::Flags nameFlags = nameDecl.flags();
+ nameFlags |= PropertyDeclaration::ReadOnlyFlag;
+ nameDecl.setFlags(nameFlags);
+ item << nameDecl;
item << conditionProperty();
PropertyDeclaration setupBuildEnvDecl(StringConstants::setupBuildEnvironmentProperty(),
PropertyDeclaration::Variant, QString(),
@@ -433,6 +438,8 @@ void BuiltinDeclarations::addProductItem()
item << PropertyDeclaration(StringConstants::multiplexConfigurationIdProperty(),
PropertyDeclaration::String, QString(),
PropertyDeclaration::ReadOnlyFlag);
+ item << PropertyDeclaration(StringConstants::qbsModuleProviders(),
+ PropertyDeclaration::StringList);
insert(item);
}
@@ -473,6 +480,8 @@ void BuiltinDeclarations::addProjectItem()
item << PropertyDeclaration(StringConstants::qbsSearchPathsProperty(),
PropertyDeclaration::StringList,
QString(), PropertyDeclaration::PropertyNotAvailableInConfig);
+ item << PropertyDeclaration(StringConstants::qbsModuleProviders(),
+ PropertyDeclaration::StringList);
insert(item);
}
diff --git a/src/lib/corelib/language/builtindeclarations.h b/src/lib/corelib/language/builtindeclarations.h
index 988f9ab81..9d7aee982 100644
--- a/src/lib/corelib/language/builtindeclarations.h
+++ b/src/lib/corelib/language/builtindeclarations.h
@@ -62,7 +62,7 @@ public:
QStringList allTypeNames() const;
ItemDeclaration declarationsForType(ItemType type) const;
ItemType typeForName(const QString &typeName,
- const CodeLocation location = CodeLocation()) const;
+ const CodeLocation &location = CodeLocation()) const;
QString nameForType(ItemType itemType) const;
QStringList argumentNamesForScriptFunction(ItemType itemType, const QString &scriptName) const;
diff --git a/src/lib/corelib/language/deprecationinfo.h b/src/lib/corelib/language/deprecationinfo.h
index 220f99ad3..2f9bfc103 100644
--- a/src/lib/corelib/language/deprecationinfo.h
+++ b/src/lib/corelib/language/deprecationinfo.h
@@ -39,6 +39,11 @@
#ifndef QBS_DEPRECATIONINFO_H
#define QBS_DEPRECATIONINFO_H
+#include <api/languageinfo.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/deprecationwarningmode.h>
+#include <tools/error.h>
#include <tools/version.h>
#include <QtCore/qstring.h>
@@ -50,15 +55,54 @@ class DeprecationInfo
{
public:
explicit DeprecationInfo(const Version &removalVersion,
- const QString &additionalUserInfo = QString())
+ QString additionalUserInfo = QString())
: m_removalVersion(removalVersion)
- , m_additionalUserInfo(additionalUserInfo)
+ , m_additionalUserInfo(std::move(additionalUserInfo))
{}
DeprecationInfo() = default;
bool isValid() const { return m_removalVersion.isValid(); }
Version removalVersion() const { return m_removalVersion; }
- QString additionalUserInfo() const { return m_additionalUserInfo; }
+
+ ErrorInfo checkForDeprecation(DeprecationWarningMode mode, const QString &name,
+ const CodeLocation &loc, bool isItem, Logger &logger) const
+ {
+ if (!isValid())
+ return {};
+ const Version qbsVersion = LanguageInfo::qbsVersion();
+ if (removalVersion() <= qbsVersion) {
+ const QString msgTemplate = isItem
+ ? Tr::tr("The item '%1' can no longer be used. It was removed in Qbs %2.")
+ : Tr::tr("The property '%1' can no longer be used. It was removed in Qbs %2.");
+ ErrorInfo error(msgTemplate.arg(name, removalVersion().toString()), loc);
+ if (!m_additionalUserInfo.isEmpty())
+ error.append(m_additionalUserInfo);
+ return error;
+ }
+ const QString msgTemplate = isItem
+ ? Tr::tr("The item '%1' is deprecated and will be removed in Qbs %2.")
+ : Tr::tr("The property '%1' is deprecated and will be removed in Qbs %2.");
+ ErrorInfo error(msgTemplate.arg(name, removalVersion().toString()), loc);
+ if (!m_additionalUserInfo.isEmpty())
+ error.append(m_additionalUserInfo);
+ switch (mode) {
+ case DeprecationWarningMode::Error:
+ return error;
+ case DeprecationWarningMode::On:
+ logger.printWarning(error);
+ break;
+ case DeprecationWarningMode::BeforeRemoval: {
+ const Version next(qbsVersion.majorVersion(), qbsVersion.minorVersion() + 1);
+ if (removalVersion() == next || removalVersion().minorVersion() == 0)
+ logger.printWarning(error);
+ break;
+ }
+ case DeprecationWarningMode::Off:
+ break;
+ }
+
+ return {};
+ }
private:
Version m_removalVersion;
diff --git a/src/lib/corelib/language/evaluationdata.h b/src/lib/corelib/language/evaluationdata.h
deleted file mode 100644
index 791b2f234..000000000
--- a/src/lib/corelib/language/evaluationdata.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <QtCore/qhash.h>
-#include <QtCore/qvariant.h>
-
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
-
-namespace qbs {
-namespace Internal {
-
-class Evaluator;
-class Item;
-
-class EvaluationData
-{
-public:
- Evaluator *evaluator = nullptr;
- const Item *item = nullptr;
- mutable QHash<QScriptString, QScriptValue> 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
index 5ea506d6a..084f2f4a9 100644
--- a/src/lib/corelib/language/evaluator.cpp
+++ b/src/lib/corelib/language/evaluator.cpp
@@ -39,18 +39,19 @@
#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 <quickjs.h>
+
#include <buildgraph/buildgraph.h>
#include <jsextensions/jsextensions.h>
#include <logging/translator.h>
#include <tools/error.h>
+#include <tools/fileinfo.h>
#include <tools/scripttools.h>
#include <tools/qbsassert.h>
#include <tools/qttools.h>
@@ -61,27 +62,63 @@
namespace qbs {
namespace Internal {
+class EvaluationData
+{
+public:
+ Evaluator *evaluator = nullptr;
+ const Item *item = nullptr;
+ mutable QHash<QString, JSValue> valueCache;
+};
+
+static void convertToPropertyType_impl(
+ ScriptEngine *engine, const QString &pathPropertiesBaseDir, const Item *item,
+ const PropertyDeclaration& decl, const Value *value, const CodeLocation &location, JSValue &v);
+static int getEvalPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj);
+static int getEvalProperty(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop);
+static int getEvalPropertySafe(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop);
+
+static bool debugProperties = false;
+
Evaluator::Evaluator(ScriptEngine *scriptEngine)
: m_scriptEngine(scriptEngine)
- , m_scriptClass(new EvaluatorScriptClass(scriptEngine))
+ , m_scriptClass(scriptEngine->registerClass("Evaluator", nullptr, nullptr, JS_UNDEFINED,
+ getEvalPropertyNames, getEvalPropertySafe))
{
+ scriptEngine->registerEvaluator(this);
}
Evaluator::~Evaluator()
{
- for (const auto &data : qAsConst(m_scriptValueMap))
- delete attachedPointer<EvaluationData>(data);
- delete m_scriptClass;
+ Set<JSValue> valuesToFree;
+ for (const auto &data : std::as_const(m_scriptValueMap)) {
+ const auto evalData = attachedPointer<EvaluationData>(data, m_scriptClass);
+ valuesToFree << data;
+ for (const JSValue cachedValue : evalData->valueCache)
+ JS_FreeValue(m_scriptEngine->context(), cachedValue);
+ evalData->item->removeObserver(this);
+ delete evalData;
+ }
+ for (const auto &scopes : std::as_const(m_fileContextScopesMap)) {
+ valuesToFree << scopes.fileScope;
+ valuesToFree << scopes.importScope;
+ }
+ for (const JSValue v : std::as_const(valuesToFree)) {
+ JS_FreeValue(m_scriptEngine->context(), v);
+ }
+ m_scriptEngine->unregisterEvaluator(this);
}
-QScriptValue Evaluator::property(const Item *item, const QString &name)
+JSValue Evaluator::property(const Item *item, const QString &name)
{
- return scriptValue(item).property(name);
+ return getJsProperty(m_scriptEngine->context(), scriptValue(item), name);
}
-QScriptValue Evaluator::value(const Item *item, const QString &name, bool *propertyWasSet)
+JSValue Evaluator::value(const Item *item, const QString &name, bool *propertyWasSet)
{
- QScriptValue v;
+ JSValue v;
evaluateProperty(&v, item, name, propertyWasSet);
return v;
}
@@ -89,16 +126,19 @@ QScriptValue Evaluator::value(const Item *item, const QString &name, bool *prope
bool Evaluator::boolValue(const Item *item, const QString &name,
bool *propertyWasSet)
{
- return value(item, name, propertyWasSet).toBool();
+ const ScopedJsValue sv(m_scriptEngine->context(), value(item, name, propertyWasSet));
+ return JS_ToBool(m_scriptEngine->context(), sv);
}
int Evaluator::intValue(const Item *item, const QString &name, int defaultValue,
bool *propertyWasSet)
{
- QScriptValue v;
+ JSValue v;
if (!evaluateProperty(&v, item, name, propertyWasSet))
return defaultValue;
- return v.toInt32();
+ qint32 n;
+ JS_ToInt32(m_scriptEngine->context(), &n, v);
+ return n;
}
FileTags Evaluator::fileTagsValue(const Item *item, const QString &name, bool *propertySet)
@@ -109,24 +149,27 @@ FileTags Evaluator::fileTagsValue(const Item *item, const QString &name, bool *p
QString Evaluator::stringValue(const Item *item, const QString &name,
const QString &defaultValue, bool *propertyWasSet)
{
- QScriptValue v;
+ JSValue v;
if (!evaluateProperty(&v, item, name, propertyWasSet))
return defaultValue;
- return v.toString();
+ QString str = getJsString(m_scriptEngine->context(), v);
+ JS_FreeValue(m_scriptEngine->context(), v);
+ return str;
}
-static QStringList toStringList(const QScriptValue &scriptValue)
+static QStringList toStringList(ScriptEngine *engine, const JSValue &scriptValue)
{
- if (scriptValue.isString()) {
- return {scriptValue.toString()};
- } else if (scriptValue.isArray()) {
+ if (JS_IsString(scriptValue))
+ return {getJsString(engine->context(), scriptValue)};
+ if (JS_IsArray(engine->context(), scriptValue)) {
QStringList lst;
int i = 0;
- forever {
- QScriptValue elem = scriptValue.property(i++);
- if (!elem.isValid())
+ for (;;) {
+ JSValue elem = JS_GetPropertyUint32(engine->context(), scriptValue, i++);
+ if (JS_IsUndefined(elem))
break;
- lst.push_back(elem.toString());
+ lst.push_back(getJsString(engine->context(), elem));
+ JS_FreeValue(engine->context(), elem);
}
return lst;
}
@@ -135,11 +178,29 @@ static QStringList toStringList(const QScriptValue &scriptValue)
QStringList Evaluator::stringListValue(const Item *item, const QString &name, bool *propertyWasSet)
{
- QScriptValue v = property(item, name);
- handleEvaluationError(item, name, v);
+ const auto result = optionalStringListValue(item, name, propertyWasSet);
+ return result ? *result : QStringList();
+}
+
+std::optional<QStringList> Evaluator::optionalStringListValue(
+ const Item *item, const QString &name, bool *propertyWasSet)
+{
+ const ScopedJsValue v(m_scriptEngine->context(), property(item, name));
+ handleEvaluationError(item, name);
if (propertyWasSet)
*propertyWasSet = isNonDefaultValue(item, name);
- return toStringList(v);
+ if (JS_IsUndefined(v))
+ return std::nullopt;
+ return toStringList(m_scriptEngine, v);
+}
+
+QVariant Evaluator::variantValue(const Item *item, const QString &name, bool *propertySet)
+{
+ const ScopedJsValue jsValue(m_scriptEngine->context(), property(item, name));
+ handleEvaluationError(item, name);
+ if (propertySet)
+ *propertySet = isNonDefaultValue(item, name);
+ return getJsVariant(m_scriptEngine->context(), jsValue);
}
bool Evaluator::isNonDefaultValue(const Item *item, const QString &name) const
@@ -150,15 +211,15 @@ bool Evaluator::isNonDefaultValue(const Item *item, const QString &name) const
}
void Evaluator::convertToPropertyType(const PropertyDeclaration &decl, const CodeLocation &loc,
- QScriptValue &v)
+ JSValue &v)
{
- m_scriptClass->convertToPropertyType(decl, loc, v);
+ convertToPropertyType_impl(engine(), QString(), nullptr, decl, nullptr, loc, v);
}
-QScriptValue Evaluator::scriptValue(const Item *item)
+JSValue Evaluator::scriptValue(const Item *item)
{
- QScriptValue &scriptValue = m_scriptValueMap[item];
- if (scriptValue.isObject()) {
+ JSValue &scriptValue = m_scriptValueMap[item];
+ if (JS_IsObject(scriptValue)) {
// already initialized
return scriptValue;
}
@@ -166,111 +227,783 @@ QScriptValue Evaluator::scriptValue(const Item *item)
const auto edata = new EvaluationData;
edata->evaluator = this;
edata->item = item;
- edata->item->setObserver(this);
+ edata->item->addObserver(this);
- scriptValue = m_scriptEngine->newObject(m_scriptClass);
+ scriptValue = JS_NewObjectClass(m_scriptEngine->context(), m_scriptClass);
attachPointerTo(scriptValue, edata);
return scriptValue;
}
-void Evaluator::onItemPropertyChanged(Item *item)
-{
- auto data = attachedPointer<EvaluationData>(m_scriptValueMap.value(item));
- if (data)
- data->valueCache.clear();
-}
-
-void Evaluator::handleEvaluationError(const Item *item, const QString &name,
- const QScriptValue &scriptValue)
+void Evaluator::handleEvaluationError(const Item *item, const QString &name)
{
- throwOnEvaluationError(m_scriptEngine, scriptValue, [&item, &name] () {
+ throwOnEvaluationError(m_scriptEngine, [&item, &name] () {
const ValueConstPtr &value = item->property(name);
return value ? value->location() : CodeLocation();
});
}
-void Evaluator::setPathPropertiesBaseDir(const QString &dirPath)
-{
- m_scriptClass->setPathPropertiesBaseDir(dirPath);
-}
-
-void Evaluator::clearPathPropertiesBaseDir()
-{
- m_scriptClass->clearPathPropertiesBaseDir();
-}
-
-bool Evaluator::evaluateProperty(QScriptValue *result, const Item *item, const QString &name,
+bool Evaluator::evaluateProperty(JSValue *result, const Item *item, const QString &name,
bool *propertyWasSet)
{
*result = property(item, name);
- handleEvaluationError(item, name, *result);
+ ScopedJsValue valMgr(m_scriptEngine->context(), *result);
+ handleEvaluationError(item, name);
+ valMgr.release();
if (propertyWasSet)
*propertyWasSet = isNonDefaultValue(item, name);
- return result->isValid() && !result->isUndefined();
+ return !JS_IsUndefined(*result);
}
Evaluator::FileContextScopes Evaluator::fileContextScopes(const FileContextConstPtr &file)
{
FileContextScopes &result = m_fileContextScopesMap[file];
- if (!result.fileScope.isObject()) {
+ if (!JS_IsObject(result.fileScope)) {
if (file->idScope())
result.fileScope = scriptValue(file->idScope());
else
result.fileScope = m_scriptEngine->newObject();
- result.fileScope.setProperty(StringConstants::filePathGlobalVar(), file->filePath());
- result.fileScope.setProperty(StringConstants::pathGlobalVar(), file->dirPath());
+ setJsProperty(m_scriptEngine->context(), result.fileScope,
+ StringConstants::filePathGlobalVar(), file->filePath());
+ setJsProperty(m_scriptEngine->context(), result.fileScope,
+ StringConstants::pathGlobalVar(), file->dirPath());
}
- if (!result.importScope.isObject()) {
+ if (!JS_IsObject(result.importScope)) {
try {
- result.importScope = m_scriptEngine->newObject();
- setupScriptEngineForFile(m_scriptEngine, file, result.importScope,
- ObserveMode::Enabled);
+ ScopedJsValue importScope(m_scriptEngine->context(), m_scriptEngine->newObject());
+ setupScriptEngineForFile(m_scriptEngine, file, importScope, ObserveMode::Enabled);
+ result.importScope = importScope.release();
} catch (const ErrorInfo &e) {
- result.importScope = m_scriptEngine->currentContext()->throwError(e.toString());
+ result.importScope = throwError(m_scriptEngine->context(), e.toString());
}
}
return result;
}
-void Evaluator::setCachingEnabled(bool enabled)
+// This is the only function in this class that can be called from a thread that is not
+// the evaluating one. For this reason, we do not clear the cache here, as that would
+// incur enourmous synchronization overhead. Instead, we mark the item's cache as invalidated
+// and do the actual clearing only at the very few places where the cache is actually accessed.
+void Evaluator::invalidateCache(const Item *item)
+{
+ std::lock_guard lock(m_cacheInvalidationMutex);
+ m_invalidatedCaches << item;
+}
+
+void Evaluator::clearCache(const Item *item)
{
- m_scriptClass->setValueCacheEnabled(enabled);
+ std::lock_guard lock(m_cacheInvalidationMutex);
+ if (const auto data = attachedPointer<EvaluationData>(m_scriptValueMap.value(item),
+ m_scriptEngine->dataWithPtrClass())) {
+ clearCache(*data);
+ m_invalidatedCaches.remove(data->item);
+ }
}
-PropertyDependencies Evaluator::propertyDependencies() const
+void Evaluator::clearCacheIfInvalidated(EvaluationData &edata)
{
- return m_scriptClass->propertyDependencies();
+ std::lock_guard lock(m_cacheInvalidationMutex);
+ if (const auto it = m_invalidatedCaches.find(edata.item); it != m_invalidatedCaches.end()) {
+ clearCache(edata);
+ m_invalidatedCaches.erase(it);
+ }
}
-void Evaluator::clearPropertyDependencies()
+void Evaluator::clearCache(EvaluationData &edata)
{
- m_scriptClass->clearPropertyDependencies();
+ for (const auto value : std::as_const(edata.valueCache))
+ JS_FreeValue(m_scriptEngine->context(), value);
+ edata.valueCache.clear();
}
-void throwOnEvaluationError(ScriptEngine *engine, const QScriptValue &scriptValue,
+void throwOnEvaluationError(ScriptEngine *engine,
const std::function<CodeLocation()> &provideFallbackCodeLocation)
{
- if (Q_LIKELY(!engine->hasErrorOrException(scriptValue)))
+ if (JsException ex = engine->checkAndClearException(provideFallbackCodeLocation()))
+ throw ex.toErrorInfo();
+}
+
+static void makeTypeError(ScriptEngine *engine, const ErrorInfo &error, JSValue &v)
+{
+ v = throwError(engine->context(), error.toString());
+}
+
+static void makeTypeError(ScriptEngine *engine, const PropertyDeclaration &decl,
+ const CodeLocation &location, JSValue &v)
+{
+ const ErrorInfo error(Tr::tr("Value assigned to property '%1' does not have type '%2'.")
+ .arg(decl.name(), decl.typeString()), location);
+ makeTypeError(engine, error, v);
+}
+
+static QString overriddenSourceDirectory(const Item *item, const QString &defaultValue)
+{
+ const VariantValuePtr v = item->variantProperty
+ (StringConstants::qbsSourceDirPropertyInternal());
+ return v ? v->value().toString() : defaultValue;
+}
+
+static void convertToPropertyType_impl(ScriptEngine *engine,
+ const QString &pathPropertiesBaseDir, const Item *item,
+ const PropertyDeclaration& decl,
+ const Value *value, const CodeLocation &location, JSValue &v)
+{
+ JSContext * const ctx = engine->context();
+ if (JS_IsUndefined(v) || JS_IsError(ctx, v) || JS_IsException(v))
return;
- QString message;
- QString filePath;
- int line = -1;
- const QScriptValue value = scriptValue.isError() ? scriptValue
- : engine->uncaughtException();
- if (value.isError()) {
- QScriptValue v = value.property(QStringLiteral("message"));
- if (v.isString())
- message = v.toString();
- v = value.property(StringConstants::fileNameProperty());
- if (v.isString())
- filePath = v.toString();
- v = value.property(QStringLiteral("lineNumber"));
- if (v.isNumber())
- line = v.toInt32();
- throw ErrorInfo(message, CodeLocation(filePath, line, -1, false));
+ QString srcDir;
+ QString actualBaseDir;
+ const Item * const srcDirItem = value && value->scope() ? value->scope() : item;
+ if (item && !pathPropertiesBaseDir.isEmpty()) {
+ const VariantValueConstPtr itemSourceDir
+ = item->variantProperty(QStringLiteral("sourceDirectory"));
+ actualBaseDir = itemSourceDir ? itemSourceDir->value().toString() : pathPropertiesBaseDir;
+ }
+ switch (decl.type()) {
+ case PropertyDeclaration::UnknownType:
+ case PropertyDeclaration::Variant:
+ break;
+ case PropertyDeclaration::Boolean:
+ if (!JS_IsBool(v))
+ v = JS_NewBool(ctx, JS_ToBool(ctx, v));
+ break;
+ case PropertyDeclaration::Integer:
+ if (!JS_IsNumber(v))
+ makeTypeError(engine, decl, location, v);
+ break;
+ case PropertyDeclaration::Path:
+ {
+ if (!JS_IsString(v)) {
+ makeTypeError(engine, decl, location, v);
+ break;
+ }
+ const QString srcDir = srcDirItem ? overriddenSourceDirectory(srcDirItem, actualBaseDir)
+ : pathPropertiesBaseDir;
+ if (!srcDir.isEmpty()) {
+ v = engine->toScriptValue(QDir::cleanPath(FileInfo::resolvePath(srcDir,
+ getJsString(ctx, v))));
+ JS_FreeValue(ctx, v);
+ }
+ break;
+ }
+ case PropertyDeclaration::String:
+ if (!JS_IsString(v))
+ makeTypeError(engine, decl, location, v);
+ break;
+ case PropertyDeclaration::PathList:
+ srcDir = srcDirItem ? overriddenSourceDirectory(srcDirItem, actualBaseDir)
+ : pathPropertiesBaseDir;
+ // Fall-through.
+ case PropertyDeclaration::StringList:
+ {
+ if (!JS_IsArray(ctx, v)) {
+ JSValue x = engine->newArray(1, JsValueOwner::ScriptEngine);
+ JS_SetPropertyUint32(ctx, x, 0, JS_DupValue(ctx, v));
+ v = x;
+ }
+ const quint32 c = getJsIntProperty(ctx, v, StringConstants::lengthProperty());
+ for (quint32 i = 0; i < c; ++i) {
+ const ScopedJsValue elem(ctx, JS_GetPropertyUint32(ctx, v, i));
+ if (JS_IsUndefined(elem)) {
+ ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' is undefined. "
+ "String expected.").arg(i).arg(decl.name()), location);
+ makeTypeError(engine, error, v);
+ break;
+ }
+ if (JS_IsNull(elem)) {
+ ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' is null. "
+ "String expected.").arg(i).arg(decl.name()), location);
+ makeTypeError(engine, error, v);
+ break;
+ }
+ if (!JS_IsString(elem)) {
+ ErrorInfo error(Tr::tr("Element at index %1 of list property '%2' does not have "
+ "string type.").arg(i).arg(decl.name()), location);
+ makeTypeError(engine, error, v);
+ break;
+ }
+ if (srcDir.isEmpty())
+ continue;
+ const JSValue newElem = engine->toScriptValue(
+ QDir::cleanPath(FileInfo::resolvePath(srcDir, getJsString(ctx, elem))));
+ JS_SetPropertyUint32(ctx, v, i, newElem);
+ }
+ break;
+ }
+ case PropertyDeclaration::VariantList:
+ if (!JS_IsArray(ctx, v)) {
+ JSValue x = engine->newArray(1, JsValueOwner::ScriptEngine);
+ JS_SetPropertyUint32(ctx, x, 0, JS_DupValue(ctx, v));
+ v = x;
+ }
+ break;
+ }
+}
+
+static int getEvalPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, JSValue obj)
+{
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ const Evaluator * const evaluator = engine->evaluator();
+ const auto data = attachedPointer<EvaluationData>(obj, evaluator->classId());
+ if (!data)
+ return -1;
+ const Item::PropertyMap &map = data->item->properties();
+ *plen = map.size();
+ if (!map.isEmpty()) {
+ *ptab = reinterpret_cast<JSPropertyEnum *>(js_malloc(ctx, *plen * sizeof **ptab));
+ JSPropertyEnum *entry = *ptab;
+ for (auto it = map.cbegin(); it != map.cend(); ++it, ++entry) {
+ entry->atom = JS_NewAtom(ctx, it.key().toUtf8().constData());
+ entry->is_enumerable = 1;
+ }
} else {
- message = value.toString();
- throw ErrorInfo(message, provideFallbackCodeLocation());
+ *ptab = nullptr;
+ }
+ return 0;
+}
+
+class PropertyStackManager
+{
+public:
+ PropertyStackManager(const Item *itemOfProperty, const QString &name, const Value *value,
+ std::stack<QualifiedId> &requestedProperties,
+ PropertyDependencies &propertyDependencies)
+ : m_requestedProperties(requestedProperties)
+ {
+ if (value->type() == Value::JSSourceValueType
+ && (itemOfProperty->type() == ItemType::ModuleInstance
+ || itemOfProperty->type() == ItemType::Module
+ || itemOfProperty->type() == ItemType::Export)) {
+ const VariantValueConstPtr varValue
+ = itemOfProperty->variantProperty(StringConstants::nameProperty());
+ if (!varValue)
+ return;
+ m_stackUpdate = true;
+ const QualifiedId fullPropName
+ = QualifiedId::fromString(varValue->value().toString()) << name;
+ if (!requestedProperties.empty())
+ propertyDependencies[fullPropName].insert(requestedProperties.top());
+ m_requestedProperties.push(fullPropName);
+ }
+ }
+
+ ~PropertyStackManager()
+ {
+ if (m_stackUpdate)
+ m_requestedProperties.pop();
+ }
+
+private:
+ std::stack<QualifiedId> &m_requestedProperties;
+ bool m_stackUpdate = false;
+};
+
+class SVConverter : ValueHandler
+{
+ ScriptEngine * const engine;
+ const JSValue * const object;
+ Value * const valuePtr;
+ const Item * const itemOfProperty;
+ const QString * const propertyName;
+ const EvaluationData * const data;
+ JSValue * const result;
+ JSValueList scopeChain;
+ char pushedScopesCount;
+
+public:
+
+ SVConverter(ScriptEngine *engine, const JSValue *obj, const ValuePtr &v,
+ const Item *_itemOfProperty, const QString *propertyName,
+ const EvaluationData *data, JSValue *result)
+ : engine(engine)
+ , object(obj)
+ , valuePtr(v.get())
+ , itemOfProperty(_itemOfProperty)
+ , propertyName(propertyName)
+ , data(data)
+ , result(result)
+ , pushedScopesCount(0)
+ {
+ }
+
+ void start()
+ {
+ valuePtr->apply(this);
+ }
+
+private:
+ friend class AutoScopePopper;
+
+ class AutoScopePopper
+ {
+ public:
+ AutoScopePopper(SVConverter *converter)
+ : m_converter(converter)
+ {
+ }
+
+ ~AutoScopePopper()
+ {
+ m_converter->popScopes();
+ }
+
+ private:
+ SVConverter *m_converter;
+ };
+
+ void setupConvenienceProperty(const QString &conveniencePropertyName, JSValue *extraScope,
+ const JSValue &scriptValue)
+ {
+ if (!JS_IsObject(*extraScope))
+ *extraScope = engine->newObject();
+ const PropertyDeclaration::Type type
+ = itemOfProperty->propertyDeclaration(*propertyName).type();
+ const bool isArray = type == PropertyDeclaration::StringList
+ || type == PropertyDeclaration::PathList
+ || type == PropertyDeclaration::Variant // TODO: Why?
+ || type == PropertyDeclaration::VariantList;
+ JSValue valueToSet = JS_DupValue(engine->context(), scriptValue);
+ if (isArray && JS_IsUndefined(valueToSet))
+ valueToSet = engine->newArray(0, JsValueOwner::Caller);
+ setJsProperty(engine->context(), *extraScope, conveniencePropertyName, valueToSet);
+ }
+
+ std::pair<JSValue, bool> createExtraScope(const JSSourceValue *value, Item *outerItem,
+ JSValue *outerScriptValue)
+ {
+ std::pair<JSValue, bool> result;
+ auto &extraScope = result.first;
+ result.second = true;
+ if (value->sourceUsesBase()) {
+ JSValue baseValue = JS_UNDEFINED;
+ if (value->baseValue()) {
+ SVConverter converter(engine, object, value->baseValue(), itemOfProperty,
+ propertyName, data, &baseValue);
+ converter.start();
+ }
+ setupConvenienceProperty(StringConstants::baseVar(), &extraScope, baseValue);
+ }
+ if (value->sourceUsesOuter()) {
+ JSValue v = JS_UNDEFINED;
+ bool doSetup = false;
+ if (outerItem) {
+ v = data->evaluator->property(outerItem, *propertyName);
+ if (JsException ex = engine->checkAndClearException({})) {
+ extraScope = engine->throwError(ex.toErrorInfo().toString());
+ result.second = false;
+ return result;
+ }
+ doSetup = true;
+ JS_FreeValue(engine->context(), v);
+ } else if (outerScriptValue) {
+ doSetup = true;
+ v = *outerScriptValue;
+ }
+ if (doSetup)
+ setupConvenienceProperty(StringConstants::outerVar(), &extraScope, v);
+ }
+ if (value->sourceUsesOriginal()) {
+ JSValue originalJs = JS_UNDEFINED;
+ ScopedJsValue originalMgr(engine->context(), JS_UNDEFINED);
+ if (data->item->propertyDeclaration(*propertyName).isScalar()) {
+ const Item *item = itemOfProperty;
+
+ if (item->type() != ItemType::ModuleInstance
+ && item->type() != ItemType::ModuleInstancePlaceholder) {
+ const QString errorMessage = Tr::tr("The special value 'original' can only "
+ "be used with module properties.");
+ extraScope = throwError(engine->context(), errorMessage);
+ result.second = false;
+ return result;
+ }
+
+ if (!value->scope()) {
+ const QString errorMessage = Tr::tr("The special value 'original' cannot "
+ "be used on the right-hand side of a property declaration.");
+ extraScope = throwError(engine->context(), errorMessage);
+ result.second = false;
+ return result;
+ }
+
+ ValuePtr original;
+ for (const ValuePtr &v : value->candidates()) {
+ if (!v->scope()) {
+ original = v;
+ break;
+ }
+ }
+
+ // This can happen when resolving shadow products. The error will be ignored
+ // in that case.
+ if (!original) {
+ const QString errorMessage = Tr::tr("Error setting up 'original'.");
+ extraScope = throwError(engine->context(), errorMessage);
+ result.second = false;
+ return result;
+ }
+
+ SVConverter converter(engine, object, original, item, propertyName, data,
+ &originalJs);
+ converter.start();
+ } else {
+ originalJs = engine->newArray(0, JsValueOwner::Caller);
+ originalMgr.setValue(originalJs);
+ }
+ setupConvenienceProperty(StringConstants::originalVar(), &extraScope, originalJs);
+ }
+ return result;
+ }
+
+ void pushScope(const JSValue &scope)
+ {
+ if (JS_IsObject(scope)) {
+ scopeChain << scope;
+ ++pushedScopesCount;
+ }
+ }
+
+ void pushScopeRecursively(const Item *scope)
+ {
+ if (scope) {
+ pushScopeRecursively(scope->scope());
+ pushScope(data->evaluator->scriptValue(scope));
+ }
+ }
+ void pushItemScopes(const Item *item)
+ {
+ const Item *scope = item->scope();
+ if (scope) {
+ pushItemScopes(scope);
+ pushScope(data->evaluator->scriptValue(scope));
+ }
+ }
+
+ void popScopes()
+ {
+ for (; pushedScopesCount; --pushedScopesCount)
+ scopeChain.pop_back();
+ }
+
+ void handle(JSSourceValue *value) override
+ {
+ JSValue outerScriptValue = JS_UNDEFINED;
+ for (const JSSourceValue::Alternative &alternative : value->alternatives()) {
+ if (alternative.value->sourceUsesOuter()
+ && !data->item->outerItem()
+ && JS_IsUndefined(outerScriptValue)) {
+ JSSourceValueEvaluationResult sver = evaluateJSSourceValue(value, nullptr);
+ if (sver.hasError) {
+ *result = sver.scriptValue;
+ return;
+ }
+ outerScriptValue = sver.scriptValue;
+ }
+ JSSourceValueEvaluationResult sver = evaluateJSSourceValue(alternative.value.get(),
+ data->item->outerItem(),
+ &alternative,
+ value, &outerScriptValue);
+ if (!sver.tryNextAlternative || sver.hasError) {
+ *result = sver.scriptValue;
+ return;
+ }
+ }
+ *result = evaluateJSSourceValue(value, data->item->outerItem()).scriptValue;
+ }
+
+ struct JSSourceValueEvaluationResult
+ {
+ JSValue scriptValue = JS_UNDEFINED;
+ bool tryNextAlternative = true;
+ bool hasError = false;
+ };
+
+ JSSourceValueEvaluationResult evaluateJSSourceValue(const JSSourceValue *value, Item *outerItem,
+ const JSSourceValue::Alternative *alternative = nullptr,
+ JSSourceValue *elseCaseValue = nullptr, JSValue *outerScriptValue = nullptr)
+ {
+ JSSourceValueEvaluationResult result;
+ QBS_ASSERT(!alternative || value == alternative->value.get(), return result);
+ AutoScopePopper autoScopePopper(this);
+ auto maybeExtraScope = createExtraScope(value, outerItem, outerScriptValue);
+ if (!maybeExtraScope.second) {
+ result.scriptValue = maybeExtraScope.first;
+ result.hasError = true;
+ return result;
+ }
+ const ScopedJsValue extraScopeMgr(engine->context(), maybeExtraScope.first);
+ const Evaluator::FileContextScopes fileCtxScopes
+ = data->evaluator->fileContextScopes(value->file());
+ if (JsException ex = engine->checkAndClearException({})) {
+ result.scriptValue = engine->throwError(ex.toErrorInfo().toString());
+ result.hasError = true;
+ return result;
+ }
+ pushScope(fileCtxScopes.fileScope);
+ pushItemScopes(data->item);
+ if ((itemOfProperty->type() != ItemType::ModuleInstance
+ && itemOfProperty->type() != ItemType::ModuleInstancePlaceholder) || !value->scope()) {
+ pushScope(*object);
+ }
+ pushScopeRecursively(value->scope());
+ pushScope(maybeExtraScope.first);
+ pushScope(fileCtxScopes.importScope);
+ if (alternative) {
+ ScopedJsValue sv(engine->context(), engine->evaluate(JsValueOwner::Caller,
+ alternative->condition.value, {}, 1, scopeChain));
+ if (JsException ex = engine->checkAndClearException(alternative->condition.location)) {
+
+ // This handles cases like the following:
+ // Depends { name: "cpp" }
+ // Properties {
+ // condition: qbs.targetOS.contains("darwin")
+ // bundle.isBundle: false
+ // }
+ // On non-Darwin platforms, bundle never gets instantiated, and thus bundle.isBundle
+ // has no scope, so the qbs item in the condition is not found.
+ // TODO: Ideally, we would never evaluate values in placeholder items, but
+ // there are currently several contexts where we do that, e.g. Export
+ // and Group items. Perhaps change that, or try to collect all such
+ // exceptions and don't try to evaluate other cases.
+ if (itemOfProperty->type() == ItemType::ModuleInstancePlaceholder) {
+ result.scriptValue = JS_UNDEFINED;
+ result.tryNextAlternative = false;
+ return result;
+ }
+
+ result.scriptValue = engine->throwError(ex.toErrorInfo().toString());
+ //result.scriptValue = JS_Throw(engine->context(), ex.takeValue());
+ //result.scriptValue = ex.takeValue();
+ result.hasError = true;
+ return result;
+ }
+ if (JS_ToBool(engine->context(), sv)) {
+ // The condition is true. Continue evaluating the value.
+ result.tryNextAlternative = false;
+ } else {
+ // The condition is false. Try the next alternative or the else value.
+ result.tryNextAlternative = true;
+ return result;
+ }
+ sv.reset(engine->evaluate(JsValueOwner::Caller,
+ alternative->overrideListProperties.value, {}, 1, scopeChain));
+ if (JsException ex = engine->checkAndClearException(
+ alternative->overrideListProperties.location)) {
+ result.scriptValue = engine->throwError(ex.toErrorInfo().toString());
+ //result.scriptValue = JS_Throw(engine->context(), ex.takeValue());
+ result.hasError = true;
+ return result;
+ }
+ if (JS_ToBool(engine->context(), sv))
+ elseCaseValue->setIsExclusiveListValue();
+ }
+ result.scriptValue = engine->evaluate(JsValueOwner::ScriptEngine,
+ value->sourceCodeForEvaluation(), value->file()->filePath(), value->line(),
+ scopeChain);
+ return result;
+ }
+
+ void handle(ItemValue *value) override
+ {
+ *result = data->evaluator->scriptValue(value->item());
+ if (JS_IsUninitialized(*result))
+ qDebug() << "SVConverter returned invalid script value.";
+ }
+
+ void handle(VariantValue *variantValue) override
+ {
+ *result = engine->toScriptValue(variantValue->value(), variantValue->id());
+ engine->takeOwnership(*result);
+ }
+};
+
+static void convertToPropertyType(ScriptEngine *engine, const Item *item,
+ const PropertyDeclaration& decl, const Value *value, JSValue &v)
+{
+ if (value->type() == Value::VariantValueType && JS_IsUndefined(v) && !decl.isScalar()) {
+ v = engine->newArray(0, JsValueOwner::ScriptEngine); // QTBUG-51237
+ return;
+ }
+ convertToPropertyType_impl(engine, engine->evaluator()->pathPropertiesBaseDir(), item, decl,
+ value, value->location(), v);
+}
+
+static QString resultToString(JSContext *ctx, const JSValue &scriptValue)
+{
+ if (JS_IsUndefined(scriptValue))
+ return QLatin1String("undefined");
+ if (JS_IsArray(ctx, scriptValue))
+ return getJsStringList(ctx, scriptValue).join(QLatin1Char(','));
+ if (JS_IsObject(scriptValue)) {
+ return QStringLiteral("[Object: ")
+ + QString::number(jsObjectId(scriptValue)) + QLatin1Char(']');
+ }
+ return getJsVariant(ctx, scriptValue).toString();
+}
+
+static void collectValuesFromNextChain(
+ ScriptEngine *engine, JSValue obj, const ValuePtr &value, const Item *itemOfProperty, const QString &name,
+ const EvaluationData *data, JSValue *result)
+{
+ JSValueList lst;
+ for (ValuePtr next = value; next; next = next->next()) {
+ JSValue v = JS_UNDEFINED;
+ SVConverter svc(engine, &obj, next, itemOfProperty, &name, data, &v);
+ svc.start();
+ if (JsException ex = engine->checkAndClearException({})) {
+ const ScopedJsValueList l(engine->context(), lst);
+ *result = engine->throwError(ex.toErrorInfo().toString());
+ return;
+ }
+ if (JS_IsUndefined(v))
+ continue;
+ const PropertyDeclaration decl = data->item->propertyDeclaration(name);
+ convertToPropertyType(engine, data->item, decl, next.get(), v);
+ lst.push_back(JS_DupValue(engine->context(), v));
+ if (next->type() == Value::JSSourceValueType
+ && std::static_pointer_cast<JSSourceValue>(next)->isExclusiveListValue()) {
+ // TODO: Why on earth do we keep the last _2_ elements?
+ auto keepIt = lst.rbegin();
+ for (int i = 0; i < 2 && keepIt != lst.rend(); ++i)
+ ++keepIt;
+ for (auto it = lst.begin(); it < keepIt.base(); ++it)
+ JS_FreeValue(engine->context(), *it);
+ lst.erase(lst.begin(), keepIt.base());
+ break;
+ }
+ }
+
+ *result = engine->newArray(int(lst.size()), JsValueOwner::ScriptEngine);
+ quint32 k = 0;
+ JSContext * const ctx = engine->context();
+ for (const JSValue &v : std::as_const(lst)) {
+ QBS_ASSERT(!JS_IsError(ctx, v), continue);
+ if (JS_IsArray(ctx, v)) {
+ const quint32 vlen = getJsIntProperty(ctx, v, StringConstants::lengthProperty());
+ for (quint32 j = 0; j < vlen; ++j)
+ JS_SetPropertyUint32(ctx, *result, k++, JS_GetPropertyUint32(ctx, v, j));
+ JS_FreeValue(ctx, v);
+ } else {
+ JS_SetPropertyUint32(ctx, *result, k++, v);
+ }
+ }
+ setJsProperty(ctx, *result, StringConstants::lengthProperty(), JS_NewInt32(ctx, k));
+}
+
+struct EvalResult { JSValue v = JS_UNDEFINED; bool found = false; };
+static EvalResult getEvalProperty(ScriptEngine *engine, JSValue obj, const Item *item,
+ const QString &name, EvaluationData *data)
+{
+ Evaluator * const evaluator = data->evaluator;
+ const bool isModuleInstance = item->type() == ItemType::ModuleInstance
+ || item->type() == ItemType::ModuleInstancePlaceholder;
+ for (; item; item = item->prototype()) {
+ if (isModuleInstance
+ && (item->type() == ItemType::Module || item->type() == ItemType::Export)) {
+ break; // There's no property in a prototype that's not also in the instance.
+ }
+ const ValuePtr value = item->ownProperty(name);
+ if (!value)
+ continue;
+ const Item * const itemOfProperty = item; // The item that owns the property.
+ PropertyStackManager propStackmanager(itemOfProperty, name, value.get(),
+ evaluator->requestedProperties(),
+ evaluator->propertyDependencies());
+ JSValue result;
+ if (evaluator->cachingEnabled()) {
+ data->evaluator->clearCacheIfInvalidated(*data);
+ const auto result = data->valueCache.constFind(name);
+ if (result != data->valueCache.constEnd()) {
+ if (debugProperties)
+ qDebug() << "[SC] cache hit " << name << ": "
+ << resultToString(engine->context(), *result);
+ return {*result, true};
+ }
+ }
+
+ if (value->next()) {
+ collectValuesFromNextChain(engine, obj, value, itemOfProperty, name, data, &result);
+ } else {
+ SVConverter converter(engine, &obj, value, itemOfProperty, &name, data, &result);
+ converter.start();
+ const PropertyDeclaration decl = data->item->propertyDeclaration(name);
+ convertToPropertyType(engine, data->item, decl, value.get(), result);
+ }
+
+ if (debugProperties)
+ qDebug() << "[SC] cache miss " << name << ": "
+ << resultToString(engine->context(), result);
+ if (evaluator->cachingEnabled()) {
+ data->evaluator->clearCacheIfInvalidated(*data);
+ const auto it = data->valueCache.find(name);
+ if (it != data->valueCache.end()) {
+ JS_FreeValue(engine->context(), it.value());
+ it.value() = JS_DupValue(engine->context(), result);
+ } else {
+ data->valueCache.insert(name, JS_DupValue(engine->context(), result));
+ }
+ }
+ return {result, true};
+ }
+ return {JS_UNDEFINED, false};
+}
+
+static int getEvalProperty(JSContext *ctx, JSPropertyDescriptor *desc, JSValue obj, JSAtom prop)
+{
+ if (desc) {
+ desc->getter = desc->setter = desc->value = JS_UNDEFINED;
+ desc->flags = JS_PROP_ENUMERABLE;
+ }
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ Evaluator * const evaluator = engine->evaluator();
+ const auto data = attachedPointer<EvaluationData>(obj, evaluator->classId());
+ const QString name = getJsString(ctx, prop);
+ if (debugProperties)
+ qDebug() << "[SC] queryProperty " << jsObjectId(obj) << " " << name;
+
+ if (name == QStringLiteral("parent")) {
+ if (desc) {
+ Item * const parent = data->item->parent();
+ desc->value = parent
+ ? JS_DupValue(ctx, data->evaluator->scriptValue(data->item->parent()))
+ : JS_UNDEFINED;
+ }
+ return 1;
+ }
+
+ if (!data) {
+ if (debugProperties)
+ qDebug() << "[SC] queryProperty: no data attached";
+ engine->setLastLookupStatus(false);
+ return -1;
+ }
+
+ EvalResult result = getEvalProperty(engine, obj, data->item, name, data);
+ if (!result.found && data->item->parent()) {
+ if (debugProperties)
+ qDebug() << "[SC] queryProperty: query parent";
+ const Item * const parentItem = data->item->parent();
+ result = getEvalProperty(engine, evaluator->scriptValue(parentItem), parentItem,
+ name, data);
+ }
+ if (result.found) {
+ if (desc)
+ desc->value = JS_DupValue(ctx, result.v);
+ engine->setLastLookupStatus(true);
+ return 1;
+ }
+
+ if (debugProperties)
+ qDebug() << "[SC] queryProperty: no such property";
+ engine->setLastLookupStatus(false);
+ return 0;
+}
+
+static int getEvalPropertySafe(JSContext *ctx, JSPropertyDescriptor *desc, JSValue obj, JSAtom prop)
+{
+ try {
+ return getEvalProperty(ctx, desc, obj, prop);
+ } catch (const ErrorInfo &e) {
+ ScopedJsValue error(ctx, throwError(ctx, e.toString()));
+ return -1;
}
}
diff --git a/src/lib/corelib/language/evaluator.h b/src/lib/corelib/language/evaluator.h
index f8535d0d7..d86e08eb1 100644
--- a/src/lib/corelib/language/evaluator.h
+++ b/src/lib/corelib/language/evaluator.h
@@ -44,15 +44,18 @@
#include "itemobserver.h"
#include "qualifiedid.h"
-#include <QtCore/qhash.h>
+#include <quickjs.h>
-#include <QtScript/qscriptvalue.h>
+#include <QtCore/qhash.h>
#include <functional>
+#include <mutex>
+#include <optional>
+#include <stack>
namespace qbs {
namespace Internal {
-class EvaluatorScriptClass;
+class EvaluationData;
class FileTags;
class Logger;
class PropertyDeclaration;
@@ -67,9 +70,10 @@ public:
~Evaluator() override;
ScriptEngine *engine() const { return m_scriptEngine; }
- QScriptValue property(const Item *item, const QString &name);
+ JSClassID classId() const { return m_scriptClass; }
+ JSValue property(const Item *item, const QString &name);
- QScriptValue value(const Item *item, const QString &name, bool *propertySet = nullptr);
+ JSValue value(const Item *item, const QString &name, bool *propertySet = nullptr);
bool boolValue(const Item *item, const QString &name, bool *propertyWasSet = nullptr);
int intValue(const Item *item, const QString &name, int defaultValue = 0,
bool *propertyWasSet = nullptr);
@@ -78,55 +82,79 @@ public:
const QString &defaultValue = QString(), bool *propertyWasSet = nullptr);
QStringList stringListValue(const Item *item, const QString &name,
bool *propertyWasSet = nullptr);
+ std::optional<QStringList> optionalStringListValue(const Item *item, const QString &name,
+ bool *propertyWasSet = nullptr);
+
+ QVariant variantValue(const Item *item, const QString &name, bool *propertySet = nullptr);
void convertToPropertyType(const PropertyDeclaration& decl, const CodeLocation &loc,
- QScriptValue &v);
+ JSValue &v);
- QScriptValue scriptValue(const Item *item);
+ JSValue scriptValue(const Item *item);
struct FileContextScopes
{
- QScriptValue fileScope;
- QScriptValue importScope;
+ JSValue fileScope = JS_UNDEFINED;
+ JSValue importScope = JS_UNDEFINED;
};
FileContextScopes fileContextScopes(const FileContextConstPtr &file);
- void setCachingEnabled(bool enabled);
+ void setCachingEnabled(bool enabled) { m_valueCacheEnabled = enabled; }
+ bool cachingEnabled() const { return m_valueCacheEnabled; }
+ void clearCache(const Item *item);
+ void invalidateCache(const Item *item);
+ void clearCacheIfInvalidated(EvaluationData &edata);
- PropertyDependencies propertyDependencies() const;
- void clearPropertyDependencies();
+ PropertyDependencies &propertyDependencies() { return m_propertyDependencies; }
+ void clearPropertyDependencies() { m_propertyDependencies.clear(); }
- void handleEvaluationError(const Item *item, const QString &name,
- const QScriptValue &scriptValue);
+ std::stack<QualifiedId> &requestedProperties() { return m_requestedProperties; }
- void setPathPropertiesBaseDir(const QString &dirPath);
- void clearPathPropertiesBaseDir();
+ void handleEvaluationError(const Item *item, const QString &name);
+
+ QString pathPropertiesBaseDir() const { return m_pathPropertiesBaseDir; }
+ void setPathPropertiesBaseDir(const QString &dirPath) { m_pathPropertiesBaseDir = dirPath; }
+ void clearPathPropertiesBaseDir() { m_pathPropertiesBaseDir.clear(); }
bool isNonDefaultValue(const Item *item, const QString &name) const;
private:
- void onItemPropertyChanged(Item *item) override;
- bool evaluateProperty(QScriptValue *result, const Item *item, const QString &name,
+ void onItemPropertyChanged(Item *item) override { invalidateCache(item); }
+ bool evaluateProperty(JSValue *result, const Item *item, const QString &name,
bool *propertyWasSet);
+ void clearCache(EvaluationData &edata);
- ScriptEngine *m_scriptEngine;
- EvaluatorScriptClass *m_scriptClass;
- mutable QHash<const Item *, QScriptValue> m_scriptValueMap;
+ ScriptEngine * const m_scriptEngine;
+ const JSClassID m_scriptClass;
+ mutable QHash<const Item *, JSValue> m_scriptValueMap;
mutable QHash<FileContextConstPtr, FileContextScopes> m_fileContextScopesMap;
+ QString m_pathPropertiesBaseDir;
+ PropertyDependencies m_propertyDependencies;
+ std::stack<QualifiedId> m_requestedProperties;
+ std::mutex m_cacheInvalidationMutex;
+ Set<const Item *> m_invalidatedCaches;
+ bool m_valueCacheEnabled = false;
};
-void throwOnEvaluationError(ScriptEngine *engine, const QScriptValue &scriptValue,
+void throwOnEvaluationError(ScriptEngine *engine,
const std::function<CodeLocation()> &provideFallbackCodeLocation);
class EvalCacheEnabler
{
public:
- EvalCacheEnabler(Evaluator *evaluator) : m_evaluator(evaluator)
+ EvalCacheEnabler(Evaluator *evaluator, const QString &baseDir) : m_evaluator(evaluator)
{
m_evaluator->setCachingEnabled(true);
+ m_evaluator->setPathPropertiesBaseDir(baseDir);
}
- ~EvalCacheEnabler() { m_evaluator->setCachingEnabled(false); }
+ ~EvalCacheEnabler() { reset(); }
+
+ void reset()
+ {
+ m_evaluator->setCachingEnabled(false);
+ m_evaluator->clearPathPropertiesBaseDir();
+ }
private:
Evaluator * const m_evaluator;
diff --git a/src/lib/corelib/language/evaluatorscriptclass.cpp b/src/lib/corelib/language/evaluatorscriptclass.cpp
deleted file mode 100644
index 375954133..000000000
--- a/src/lib/corelib/language/evaluatorscriptclass.cpp
+++ /dev/null
@@ -1,776 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <logging/translator.h>
-#include <tools/fileinfo.h>
-#include <tools/qbsassert.h>
-#include <tools/qttools.h>
-#include <tools/scripttools.h>
-#include <tools/stringconstants.h>
-
-#include <QtCore/qbytearray.h>
-#include <QtCore/qdebug.h>
-#include <QtCore/qsettings.h>
-
-#include <QtScript/qscriptclasspropertyiterator.h>
-#include <QtScript/qscriptstring.h>
-#include <QtScript/qscriptvalue.h>
-
-#include <utility>
-
-namespace qbs {
-namespace Internal {
-
-class SVConverter : ValueHandler
-{
- EvaluatorScriptClass * const scriptClass;
- ScriptEngine * const engine;
- QScriptContext * const scriptContext;
- const QScriptValue * const object;
- Value * const valuePtr;
- const Item * const itemOfProperty;
- const QScriptString * const propertyName;
- const EvaluationData * const data;
- QScriptValue * const result;
- char pushedScopesCount;
-
-public:
-
- SVConverter(EvaluatorScriptClass *esc, const QScriptValue *obj, const ValuePtr &v,
- const Item *_itemOfProperty, const QScriptString *propertyName, const EvaluationData *data,
- QScriptValue *result)
- : scriptClass(esc)
- , engine(static_cast<ScriptEngine *>(esc->engine()))
- , scriptContext(esc->engine()->currentContext())
- , object(obj)
- , valuePtr(v.get())
- , itemOfProperty(_itemOfProperty)
- , propertyName(propertyName)
- , data(data)
- , result(result)
- , pushedScopesCount(0)
- {
- }
-
- void start()
- {
- valuePtr->apply(this);
- }
-
-private:
- friend class AutoScopePopper;
-
- class AutoScopePopper
- {
- public:
- AutoScopePopper(SVConverter *converter)
- : m_converter(converter)
- {
- }
-
- ~AutoScopePopper()
- {
- m_converter->popScopes();
- }
-
- private:
- SVConverter *m_converter;
- };
-
- void setupConvenienceProperty(const QString &conveniencePropertyName, QScriptValue *extraScope,
- const QScriptValue &scriptValue)
- {
- if (!extraScope->isObject())
- *extraScope = engine->newObject();
- const PropertyDeclaration::Type type
- = itemOfProperty->propertyDeclaration(propertyName->toString()).type();
- const bool isArray = type == PropertyDeclaration::StringList
- || type == PropertyDeclaration::PathList
- || type == PropertyDeclaration::Variant // TODO: Why?
- || type == PropertyDeclaration::VariantList;
- QScriptValue valueToSet = scriptValue;
- if (isArray) {
- if (!valueToSet.isValid() || valueToSet.isUndefined())
- valueToSet = engine->newArray();
- } else if (!valueToSet.isValid()) {
- valueToSet = engine->undefinedValue();
- }
- extraScope->setProperty(conveniencePropertyName, valueToSet);
- }
-
- std::pair<QScriptValue, bool> createExtraScope(const JSSourceValue *value, Item *outerItem,
- QScriptValue *outerScriptValue)
- {
- std::pair<QScriptValue, bool> result;
- auto &extraScope = result.first;
- result.second = true;
- if (value->sourceUsesBase()) {
- QScriptValue baseValue;
- if (value->baseValue()) {
- SVConverter converter(scriptClass, object, value->baseValue(), itemOfProperty,
- propertyName, data, &baseValue);
- converter.start();
- }
- setupConvenienceProperty(StringConstants::baseVar(), &extraScope, baseValue);
- }
- if (value->sourceUsesOuter()) {
- QScriptValue v;
- if (outerItem) {
- v = data->evaluator->property(outerItem, *propertyName);
- if (engine->hasErrorOrException(v)) {
- extraScope = engine->lastErrorValue(v);
- result.second = false;
- return result;
- }
- } else if (outerScriptValue) {
- v = *outerScriptValue;
- }
- if (v.isValid())
- setupConvenienceProperty(StringConstants::outerVar(), &extraScope, v);
- }
- if (value->sourceUsesOriginal()) {
- QScriptValue originalValue;
- if (data->item->propertyDeclaration(propertyName->toString()).isScalar()) {
- const Item *item = itemOfProperty;
- if (item->type() == ItemType::Module || item->type() == ItemType::Export) {
- const QString errorMessage = Tr::tr("The special value 'original' cannot "
- "be used on the right-hand side of a property declaration.");
- extraScope = engine->currentContext()->throwError(errorMessage);
- result.second = false;
- return result;
- }
-
- // TODO: Provide a dedicated item type for not-yet-instantiated things that
- // look like module instances in the AST visitor.
- if (item->type() == ItemType::ModuleInstance
- && !item->hasProperty(StringConstants::presentProperty())) {
- const QString errorMessage = Tr::tr("Trying to assign property '%1' "
- "on something that is not a module.").arg(propertyName->toString());
- extraScope = engine->currentContext()->throwError(errorMessage);
- result.second = false;
- return result;
- }
-
- while (item->type() == ItemType::ModuleInstance)
- item = item->prototype();
- if (item->type() != ItemType::Module && item->type() != ItemType::Export) {
- const QString errorMessage = Tr::tr("The special value 'original' can only "
- "be used with module properties.");
- extraScope = engine->currentContext()->throwError(errorMessage);
- result.second = false;
- return result;
- }
- const ValuePtr v = item->property(*propertyName);
-
- // This can happen when resolving shadow products. The error will be ignored
- // in that case.
- if (!v) {
- const QString errorMessage = Tr::tr("Error setting up 'original'.");
- extraScope = engine->currentContext()->throwError(errorMessage);
- result.second = false;
- return result;
- }
-
- SVConverter converter(scriptClass, object, v, item,
- propertyName, data, &originalValue);
- converter.start();
- } else {
- originalValue = engine->newArray(0);
- }
- setupConvenienceProperty(StringConstants::originalVar(), &extraScope, originalValue);
- }
- return result;
- }
-
- 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) override
- {
- QScriptValue outerScriptValue;
- for (const JSSourceValue::Alternative &alternative : value->alternatives()) {
- if (alternative.value->sourceUsesOuter()
- && !data->item->outerItem()
- && !outerScriptValue.isValid()) {
- JSSourceValueEvaluationResult sver = evaluateJSSourceValue(value, nullptr);
- if (sver.hasError) {
- *result = sver.scriptValue;
- return;
- }
- outerScriptValue = sver.scriptValue;
- }
- JSSourceValueEvaluationResult sver = evaluateJSSourceValue(alternative.value.get(),
- data->item->outerItem(),
- &alternative,
- value, &outerScriptValue);
- if (!sver.tryNextAlternative || sver.hasError) {
- *result = sver.scriptValue;
- return;
- }
- }
- *result = evaluateJSSourceValue(value, data->item->outerItem()).scriptValue;
- }
-
- struct JSSourceValueEvaluationResult
- {
- QScriptValue scriptValue;
- bool tryNextAlternative = true;
- bool hasError = false;
- };
-
- void injectErrorLocation(QScriptValue &sv, const CodeLocation &loc)
- {
- if (sv.isError() && !engine->lastErrorLocation(sv).isValid())
- sv = engine->currentContext()->throwError(engine->lastError(sv, loc).toString());
- }
-
- JSSourceValueEvaluationResult evaluateJSSourceValue(const JSSourceValue *value, Item *outerItem,
- const JSSourceValue::Alternative *alternative = nullptr,
- JSSourceValue *elseCaseValue = nullptr, QScriptValue *outerScriptValue = nullptr)
- {
- JSSourceValueEvaluationResult result;
- QBS_ASSERT(!alternative || value == alternative->value.get(), return result);
- AutoScopePopper autoScopePopper(this);
- auto maybeExtraScope = createExtraScope(value, outerItem, outerScriptValue);
- if (!maybeExtraScope.second) {
- result.scriptValue = maybeExtraScope.first;
- result.hasError = true;
- return result;
- }
- const Evaluator::FileContextScopes fileCtxScopes
- = data->evaluator->fileContextScopes(value->file());
- if (fileCtxScopes.importScope.isError()) {
- result.scriptValue = fileCtxScopes.importScope;
- result.hasError = true;
- return result;
- }
- pushScope(fileCtxScopes.fileScope);
- pushItemScopes(data->item);
- if (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(maybeExtraScope.first);
- pushScope(fileCtxScopes.importScope);
- if (alternative) {
- QScriptValue sv = engine->evaluate(alternative->condition.value);
- if (engine->hasErrorOrException(sv)) {
- result.scriptValue = sv;
- result.hasError = true;
- injectErrorLocation(result.scriptValue, alternative->condition.location);
- return result;
- }
- if (sv.toBool()) {
- // The condition is true. Continue evaluating the value.
- result.tryNextAlternative = false;
- } else {
- // The condition is false. Try the next alternative or the else value.
- result.tryNextAlternative = true;
- return result;
- }
- sv = engine->evaluate(alternative->overrideListProperties.value);
- if (engine->hasErrorOrException(sv)) {
- result.scriptValue = sv;
- result.hasError = true;
- injectErrorLocation(result.scriptValue,
- alternative->overrideListProperties.location);
- return result;
- }
- if (sv.toBool())
- elseCaseValue->setIsExclusiveListValue();
- }
- result.scriptValue = engine->evaluate(value->sourceCodeForEvaluation(),
- value->file()->filePath(), value->line());
- return result;
- }
-
- void handle(ItemValue *value) override
- {
- *result = data->evaluator->scriptValue(value->item());
- if (!result->isValid())
- qDebug() << "SVConverter returned invalid script value.";
- }
-
- void handle(VariantValue *variantValue) override
- {
- *result = engine->toScriptValue(variantValue->value());
- }
-};
-
-bool debugProperties = false;
-
-enum QueryPropertyType
-{
- QPTDefault,
- QPTParentProperty
-};
-
-EvaluatorScriptClass::EvaluatorScriptClass(ScriptEngine *scriptEngine)
- : QScriptClass(scriptEngine)
- , m_valueCacheEnabled(false)
-{
-}
-
-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 {});
-
- if (debugProperties)
- qDebug() << "[SC] queryProperty " << object.objectId() << " " << name;
-
- auto const data = attachedPointer<EvaluationData>(object);
- const QString nameString = name.toString();
- if (nameString == QStringLiteral("parent")) {
- *id = QPTParentProperty;
- m_queryResult.data = data;
- return QScriptClass::HandlesReadAccess;
- }
-
- *id = QPTDefault;
- if (!data) {
- if (debugProperties)
- qDebug() << "[SC] queryProperty: no data attached";
- return {};
- }
-
- 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->ownProperty(name);
- if (m_queryResult.value) {
- 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.foundInParent = true;
- m_queryResult.data = data;
- return qf;
- }
- }
-
- if (debugProperties)
- qDebug() << "[SC] queryProperty: no such property";
- return {};
-}
-
-QString EvaluatorScriptClass::resultToString(const QScriptValue &scriptValue)
-{
- return (scriptValue.isObject()
- ? QStringLiteral("[Object: ")
- + QString::number(scriptValue.objectId()) + QLatin1Char(']')
- : scriptValue.toVariant().toString());
-}
-
-void EvaluatorScriptClass::collectValuesFromNextChain(const EvaluationData *data, QScriptValue *result,
- const QString &propertyName, const ValuePtr &value)
-{
- QScriptValueList lst;
- Set<Value *> oldNextChain = m_currentNextChain;
- for (ValuePtr next = value; next; next = next->next())
- m_currentNextChain.insert(next.get());
-
- for (ValuePtr next = value; next; next = next->next()) {
- QScriptValue v = data->evaluator->property(next->definingItem(), propertyName);
- const auto se = static_cast<const ScriptEngine *>(engine());
- if (se->hasErrorOrException(v)) {
- *result = se->lastErrorValue(v);
- return;
- }
- if (v.isUndefined())
- continue;
- lst << v;
- if (next->type() == Value::JSSourceValueType
- && std::static_pointer_cast<JSSourceValue>(next)->isExclusiveListValue()) {
- lst = lst.mid(lst.length() - 2);
- break;
- }
- }
- m_currentNextChain = oldNextChain;
-
- *result = engine()->newArray();
- quint32 k = 0;
- for (const QScriptValue &v : qAsConst(lst)) {
- QBS_ASSERT(!v.isError(), continue);
- if (v.isArray()) {
- const quint32 vlen = v.property(StringConstants::lengthProperty()).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 QString &defaultValue)
-{
- const VariantValuePtr v = item->variantProperty
- (StringConstants::qbsSourceDirPropertyInternal());
- return v ? v->value().toString() : defaultValue;
-}
-
-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_impl(const QString &pathPropertiesBaseDir, const Item *item,
- const PropertyDeclaration& decl,
- const CodeLocation &location, QScriptValue &v)
-{
- if (v.isUndefined() || v.isError())
- return;
- QString srcDir;
- QString actualBaseDir;
- if (item && !pathPropertiesBaseDir.isEmpty()) {
- const VariantValueConstPtr itemSourceDir
- = item->variantProperty(QStringLiteral("sourceDirectory"));
- actualBaseDir = itemSourceDir ? itemSourceDir->value().toString() : pathPropertiesBaseDir;
- }
- 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 = item ? overriddenSourceDirectory(item, actualBaseDir)
- : pathPropertiesBaseDir;
- if (!srcDir.isEmpty())
- v = v.engine()->toScriptValue(QDir::cleanPath(
- FileInfo::resolvePath(srcDir, v.toString())));
- break;
- }
- case PropertyDeclaration::String:
- if (!v.isString())
- makeTypeError(decl, location, v);
- break;
- case PropertyDeclaration::PathList:
- srcDir = item ? overriddenSourceDirectory(item, actualBaseDir)
- : pathPropertiesBaseDir;
- // 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(StringConstants::lengthProperty()).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(
- QDir::cleanPath(FileInfo::resolvePath(srcDir, elem.toString())));
- v.setProperty(i, elem);
- }
- break;
- }
- case PropertyDeclaration::VariantList:
- if (!v.isArray()) {
- QScriptValue x = v.engine()->newArray(1);
- x.setProperty(0, v);
- v = x;
- }
- break;
- }
-}
-
-void EvaluatorScriptClass::convertToPropertyType(const PropertyDeclaration &decl,
- const CodeLocation &loc, QScriptValue &v)
-{
- convertToPropertyType_impl(QString(), nullptr, decl, loc, v);
-}
-
-void EvaluatorScriptClass::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;
- }
- convertToPropertyType_impl(m_pathPropertiesBaseDir, item, decl, value->location(), v);
-}
-
-class PropertyStackManager
-{
-public:
- PropertyStackManager(const Item *itemOfProperty, const QScriptString &name, const Value *value,
- std::stack<QualifiedId> &requestedProperties,
- PropertyDependencies &propertyDependencies)
- : m_requestedProperties(requestedProperties)
- {
- if (value->type() == Value::JSSourceValueType
- && (itemOfProperty->type() == ItemType::ModuleInstance
- || itemOfProperty->type() == ItemType::Module
- || itemOfProperty->type() == ItemType::Export)) {
- const VariantValueConstPtr varValue
- = itemOfProperty->variantProperty(StringConstants::nameProperty());
- if (!varValue)
- return;
- m_stackUpdate = true;
- const QualifiedId fullPropName
- = QualifiedId::fromString(varValue->value().toString()) << name.toString();
- if (!requestedProperties.empty())
- propertyDependencies[fullPropName].insert(requestedProperties.top());
- m_requestedProperties.push(fullPropName);
- }
- }
-
- ~PropertyStackManager()
- {
- if (m_stackUpdate)
- m_requestedProperties.pop();
- }
-
-private:
- std::stack<QualifiedId> &m_requestedProperties;
- bool m_stackUpdate = false;
-};
-
-QScriptValue EvaluatorScriptClass::property(const QScriptValue &object, const QScriptString &name,
- uint id)
-{
- const bool foundInParent = m_queryResult.foundInParent;
- const EvaluationData *data = m_queryResult.data;
- const Item * const itemOfProperty = m_queryResult.itemOfProperty;
- m_queryResult.foundInParent = false;
- m_queryResult.data = nullptr;
- m_queryResult.itemOfProperty = nullptr;
- QBS_ASSERT(data, {});
-
- const auto qpt = static_cast<QueryPropertyType>(id);
- if (qpt == QPTParentProperty) {
- return data->item->parent()
- ? data->evaluator->scriptValue(data->item->parent())
- : engine()->undefinedValue();
- }
-
- ValuePtr value;
- m_queryResult.value.swap(value);
- QBS_ASSERT(value, return {});
- QBS_ASSERT(m_queryResult.isNull(), return {});
-
- if (debugProperties)
- qDebug() << "[SC] property " << name;
-
- PropertyStackManager propStackmanager(itemOfProperty, name, value.get(),
- 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.get())) {
- collectValuesFromNextChain(data, &result, name.toString(), value);
- } else {
- QScriptValue parentObject;
- if (foundInParent)
- parentObject = data->evaluator->scriptValue(data->item->parent());
- SVConverter converter(this, foundInParent ? &parentObject : &object, value, itemOfProperty,
- &name, data, &result);
- converter.start();
-
- const PropertyDeclaration decl = data->item->propertyDeclaration(name.toString());
- convertToPropertyType(data->item, decl, value.get(), result);
- }
-
- if (debugProperties)
- qDebug() << "[SC] cache miss " << name << ": " << resultToString(result);
- if (m_valueCacheEnabled)
- data->valueCache.insert(name, result);
- return result;
-}
-
-class EvaluatorScriptClassPropertyIterator : public QScriptClassPropertyIterator
-{
-public:
- EvaluatorScriptClassPropertyIterator(const QScriptValue &object, EvaluationData *data)
- : QScriptClassPropertyIterator(object), m_it(data->item->properties())
- {
- }
-
- bool hasNext() const override
- {
- return m_it.hasNext();
- }
-
- void next() override
- {
- m_it.next();
- }
-
- bool hasPrevious() const override
- {
- return m_it.hasPrevious();
- }
-
- void previous() override
- {
- m_it.previous();
- }
-
- void toFront() override
- {
- m_it.toFront();
- }
-
- void toBack() override
- {
- m_it.toBack();
- }
-
- QScriptString name() const override
- {
- return object().engine()->toStringHandle(m_it.key());
- }
-
-private:
- QMapIterator<QString, ValuePtr> m_it;
-};
-
-QScriptClassPropertyIterator *EvaluatorScriptClass::newIterator(const QScriptValue &object)
-{
- auto const data = attachedPointer<EvaluationData>(object);
- return data ? new EvaluatorScriptClassPropertyIterator(object, data) : nullptr;
-}
-
-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
deleted file mode 100755
index c234c17fa..000000000
--- a/src/lib/corelib/language/evaluatorscriptclass.h
+++ /dev/null
@@ -1,133 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <tools/set.h>
-
-#include <QtScript/qscriptclass.h>
-
-#include <stack>
-
-QT_BEGIN_NAMESPACE
-class QScriptContext;
-QT_END_NAMESPACE
-
-namespace qbs {
-namespace Internal {
-class EvaluationData;
-class Item;
-class PropertyDeclaration;
-class ScriptEngine;
-
-class EvaluatorScriptClass : public QScriptClass
-{
-public:
- EvaluatorScriptClass(ScriptEngine *scriptEngine);
-
- QueryFlags queryProperty(const QScriptValue &object,
- const QScriptString &name,
- QueryFlags flags, uint *id) override;
- QScriptValue property(const QScriptValue &object,
- const QScriptString &name, uint id) override;
- QScriptClassPropertyIterator *newIterator(const QScriptValue &object) override;
-
- void setValueCacheEnabled(bool enabled);
-
- void convertToPropertyType(const PropertyDeclaration& decl, const CodeLocation &loc,
- QScriptValue &v);
-
- PropertyDependencies propertyDependencies() const { return m_propertyDependencies; }
- void clearPropertyDependencies() { m_propertyDependencies.clear(); }
-
- void setPathPropertiesBaseDir(const QString &dirPath) { m_pathPropertiesBaseDir = dirPath; }
- void clearPathPropertiesBaseDir() { m_pathPropertiesBaseDir.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);
-
- void convertToPropertyType(const Item *item,
- const PropertyDeclaration& decl, const Value *value,
- QScriptValue &v);
-
- struct QueryResult
- {
- QueryResult()
- : data(nullptr), itemOfProperty(nullptr)
- {}
-
- bool isNull() const
- {
- static const QueryResult pristine;
- return *this == pristine;
- }
-
- bool operator==(const QueryResult &rhs) const
- {
- return foundInParent == rhs.foundInParent
- && data == rhs.data
- && itemOfProperty == rhs.itemOfProperty
- && value == rhs.value;
- }
-
- bool foundInParent = false;
- const EvaluationData *data;
- const Item *itemOfProperty; // The item that owns the property.
- ValuePtr value;
- };
- QueryResult m_queryResult;
- bool m_valueCacheEnabled;
- Set<Value *> m_currentNextChain;
- PropertyDependencies m_propertyDependencies;
- std::stack<QualifiedId> m_requestedProperties;
- QString m_pathPropertiesBaseDir;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_EVALUATORSCRIPTCLASS_H
diff --git a/src/lib/corelib/language/forward_decls.h b/src/lib/corelib/language/forward_decls.h
index 6697ac8c9..6ee22a5a6 100644
--- a/src/lib/corelib/language/forward_decls.h
+++ b/src/lib/corelib/language/forward_decls.h
@@ -142,12 +142,14 @@ class PersistentPool;
} // namespace qbs
#ifdef QT_CORE_LIB
+#include "../tools/porting.h"
+
#include <QtCore/qhash.h>
namespace qbs {
namespace Internal {
-template <typename T> inline static uint qHash(const std::shared_ptr<T> &p, uint seed = 0)
+template <typename T> inline static QHashValueType qHash(const std::shared_ptr<T> &p, QHashValueType seed = 0)
{
return ::qHash(p.get(), seed);
}
diff --git a/src/lib/corelib/language/item.cpp b/src/lib/corelib/language/item.cpp
index 9f754bdd7..e5de8f195 100644
--- a/src/lib/corelib/language/item.cpp
+++ b/src/lib/corelib/language/item.cpp
@@ -40,13 +40,14 @@
#include "item.h"
#include "builtindeclarations.h"
-#include "deprecationinfo.h"
#include "filecontext.h"
#include "itemobserver.h"
#include "itempool.h"
#include "value.h"
#include <api/languageinfo.h>
+#include <loader/loaderutils.h>
+#include <logging/categories.h>
#include <logging/logger.h>
#include <logging/translator.h>
#include <tools/error.h>
@@ -59,27 +60,19 @@
namespace qbs {
namespace Internal {
-Item::Item(ItemPool *pool, ItemType type)
- : m_pool(pool)
- , m_observer(nullptr)
- , m_prototype(nullptr)
- , m_scope(nullptr)
- , m_outerItem(nullptr)
- , m_parent(nullptr)
- , m_type(type)
-{
-}
-
Item *Item::create(ItemPool *pool, ItemType type)
{
return pool->allocateItem(type);
}
-Item *Item::clone() const
+Item *Item::clone(ItemPool &pool) const
{
- Item *dup = create(pool(), type());
+ assertModuleLocked();
+
+ Item *dup = create(&pool, type());
dup->m_id = m_id;
dup->m_location = m_location;
+ dup->m_endPosition = m_endPosition;
dup->m_prototype = m_prototype;
dup->m_scope = m_scope;
dup->m_outerItem = m_outerItem;
@@ -89,25 +82,34 @@ Item *Item::clone() const
dup->m_modules = m_modules;
dup->m_children.reserve(m_children.size());
- for (const Item * const child : qAsConst(m_children)) {
- Item *clonedChild = child->clone();
+ for (const Item * const child : std::as_const(m_children)) {
+ Item *clonedChild = child->clone(pool);
clonedChild->m_parent = dup;
dup->m_children.push_back(clonedChild);
}
for (PropertyMap::const_iterator it = m_properties.constBegin(); it != m_properties.constEnd();
++it) {
- dup->m_properties.insert(it.key(), it.value()->clone());
+ dup->m_properties.insert(it.key(), it.value()->clone(pool));
}
return dup;
}
+Item *Item::rootPrototype()
+{
+ Item *proto = this;
+ while (proto->prototype())
+ proto = proto->prototype();
+ return proto;
+}
+
QString Item::typeName() const
{
switch (type()) {
case ItemType::IdScope: return QStringLiteral("[IdScope]");
case ItemType::ModuleInstance: return QStringLiteral("[ModuleInstance]");
+ case ItemType::ModuleInstancePlaceholder: return QStringLiteral("[ModuleInstancePlaceholder]");
case ItemType::ModuleParameters: return QStringLiteral("[ModuleParametersInstance]");
case ItemType::ModulePrefix: return QStringLiteral("[ModulePrefix]");
case ItemType::Outer: return QStringLiteral("[Outer]");
@@ -118,6 +120,7 @@ QString Item::typeName() const
bool Item::hasProperty(const QString &name) const
{
+ assertModuleLocked();
const Item *item = this;
do {
if (item->m_properties.contains(name))
@@ -129,15 +132,18 @@ bool Item::hasProperty(const QString &name) const
bool Item::hasOwnProperty(const QString &name) const
{
+ assertModuleLocked();
return m_properties.contains(name);
}
ValuePtr Item::property(const QString &name) const
{
+ assertModuleLocked();
ValuePtr value;
const Item *item = this;
do {
- if ((value = item->m_properties.value(name)))
+ value = item->m_properties.value(name);
+ if (value)
break;
item = item->m_prototype;
} while (item);
@@ -146,30 +152,31 @@ ValuePtr Item::property(const QString &name) const
ValuePtr Item::ownProperty(const QString &name) const
{
+ assertModuleLocked();
return m_properties.value(name);
}
-ItemValuePtr Item::itemProperty(const QString &name, const Item *itemTemplate)
+ItemValuePtr Item::itemProperty(const QString &name, ItemPool &pool, const Item *itemTemplate)
{
- return itemProperty(name, itemTemplate, ItemValueConstPtr());
+ return itemProperty(name, itemTemplate, ItemValueConstPtr(), pool);
}
-ItemValuePtr Item::itemProperty(const QString &name, const ItemValueConstPtr &value)
+ItemValuePtr Item::itemProperty(const QString &name, const ItemValueConstPtr &value, ItemPool &pool)
{
- return itemProperty(name, value->item(), value);
+ return itemProperty(name, value->item(), value, pool);
}
ItemValuePtr Item::itemProperty(const QString &name, const Item *itemTemplate,
- const ItemValueConstPtr &itemValue)
+ const ItemValueConstPtr &itemValue, ItemPool &pool)
{
const ValuePtr v = property(name);
if (v && v->type() == Value::ItemValueType)
return std::static_pointer_cast<ItemValue>(v);
if (!itemTemplate)
- return ItemValuePtr();
+ return {};
const bool createdByPropertiesBlock = itemValue && itemValue->createdByPropertiesBlock();
- const ItemValuePtr result = ItemValue::create(Item::create(m_pool, itemTemplate->type()),
- createdByPropertiesBlock);
+ ItemValuePtr result = ItemValue::create(Item::create(&pool, itemTemplate->type()),
+ createdByPropertiesBlock);
setProperty(name, result);
return result;
}
@@ -178,7 +185,7 @@ JSSourceValuePtr Item::sourceProperty(const QString &name) const
{
ValuePtr v = property(name);
if (!v || v->type() != Value::JSSourceValueType)
- return JSSourceValuePtr();
+ return {};
return std::static_pointer_cast<JSSourceValue>(v);
}
@@ -186,7 +193,7 @@ VariantValuePtr Item::variantProperty(const QString &name) const
{
ValuePtr v = property(name);
if (!v || v->type() != Value::VariantValueType)
- return VariantValuePtr();
+ return {};
return std::static_pointer_cast<VariantValue>(v);
}
@@ -201,37 +208,69 @@ bool Item::isOfTypeOrhasParentOfType(ItemType type) const
return false;
}
+void Item::addObserver(ItemObserver *observer) const
+{
+ // Cached Module properties never change.
+ if (m_type == ItemType::Module)
+ return;
+
+ std::lock_guard lock(m_observersMutex);
+ if (!qEnvironmentVariableIsEmpty("QBS_SANITY_CHECKS"))
+ QBS_CHECK(!contains(m_observers, observer));
+ m_observers << observer;
+}
+
+void Item::removeObserver(ItemObserver *observer) const
+{
+ if (m_type == ItemType::Module)
+ return;
+ std::lock_guard lock(m_observersMutex);
+ const auto it = std::find(m_observers.begin(), m_observers.end(), observer);
+ QBS_CHECK(it != m_observers.end());
+ m_observers.erase(it);
+}
+
PropertyDeclaration Item::propertyDeclaration(const QString &name, bool allowExpired) const
{
- PropertyDeclaration decl = m_propertyDeclarations.value(name);
- if (decl.isValid())
- return decl;
+ auto it = m_propertyDeclarations.find(name);
+ if (it != m_propertyDeclarations.end())
+ return it.value();
if (allowExpired) {
- decl = m_expiredPropertyDeclarations.value(name);
- if (decl.isValid())
- return decl;
+ it = m_expiredPropertyDeclarations.find(name);
+ if (it != m_expiredPropertyDeclarations.end())
+ return it.value();
}
- return m_prototype ? m_prototype->propertyDeclaration(name) : decl;
+ return m_prototype ? m_prototype->propertyDeclaration(name) : PropertyDeclaration();
}
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);
-}
+ if (!qEnvironmentVariableIsEmpty("QBS_SANITY_CHECKS")) {
+ QBS_CHECK(none_of(m_modules, [&](const Module &m) {
+ if (m.name != module.name)
+ return false;
+ if (!!module.product != !!m.product)
+ return true;
+ if (!module.product)
+ return true;
+ if (module.product->multiplexConfigurationId == m.product->multiplexConfigurationId
+ && module.product->profileName == m.product->profileName) {
+ return true;
+ }
+ return false;
+ }));
+ }
-void Item::setObserver(ItemObserver *observer) const
-{
- QBS_ASSERT(!observer || !m_observer, return); // warn if accidentally overwritten
- m_observer = observer;
+ m_modules.push_back(module);
}
void Item::setProperty(const QString &name, const ValuePtr &value)
{
+ assertModuleLocked();
m_properties.insert(name, value);
- if (m_observer)
- m_observer->onItemPropertyChanged(this);
+ std::lock_guard lock(m_observersMutex);
+ for (ItemObserver * const observer : m_observers)
+ observer->onItemPropertyChanged(this);
}
void Item::dump() const
@@ -246,8 +285,14 @@ bool Item::isPresentModule() const
return v && v->type() == Value::JSSourceValueType;
}
-void Item::setupForBuiltinType(Logger &logger)
+bool Item::isFallbackModule() const
{
+ return hasProperty(QLatin1String("__fallback"));
+}
+
+void Item::setupForBuiltinType(DeprecationWarningMode deprecationMode, Logger &logger)
+{
+ assertModuleLocked();
const BuiltinDeclarations &builtins = BuiltinDeclarations::instance();
const auto properties = builtins.declarationsForType(type()).properties();
for (const PropertyDeclaration &pd : properties) {
@@ -260,26 +305,12 @@ void Item::setupForBuiltinType(Logger &logger)
sourceValue->setIsBuiltinDefaultValue();
sourceValue->setFile(file());
sourceValue->setSourceCode(pd.initialValueSource().isEmpty()
- ? QStringRef(&StringConstants::undefinedValue())
- : QStringRef(&pd.initialValueSource()));
+ ? StringConstants::undefinedValue()
+ : pd.initialValueSource());
m_properties.insert(pd.name(), sourceValue);
- } else if (pd.isDeprecated()) {
- const DeprecationInfo &di = pd.deprecationInfo();
- if (di.removalVersion() <= LanguageInfo::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);
+ } else if (ErrorInfo error = pd.checkForDeprecation(deprecationMode, value->location(),
+ logger); error.hasError()) {
+ throw error;
}
}
}
@@ -289,6 +320,15 @@ void Item::copyProperty(const QString &propertyName, Item *target) const
target->setProperty(propertyName, property(propertyName));
}
+void Item::overrideProperties(const QVariantMap &config, const QString &key,
+ const SetupProjectParameters &parameters, Logger &logger)
+{
+ const QVariant configMap = config.value(key);
+ if (configMap.isNull())
+ return;
+ overrideProperties(configMap.toMap(), QualifiedId(key), parameters, logger);
+}
+
static const char *valueType(const Value *v)
{
switch (v->type()) {
@@ -326,7 +366,7 @@ void Item::dump(int indentation) const
}
if (!m_children.empty())
qDebug("%schildren:", indent.constData());
- for (const Item * const child : qAsConst(m_children))
+ for (const Item * const child : std::as_const(m_children))
child->dump(indentation + 4);
if (prototype()) {
qDebug("%sprototype:", indent.constData());
@@ -334,8 +374,39 @@ void Item::dump(int indentation) const
}
}
+void Item::lockModule() const
+{
+ QBS_CHECK(m_type == ItemType::Module);
+ m_moduleMutex.lock();
+#ifndef NDEBUG
+ QBS_CHECK(!m_moduleLocked);
+ m_moduleLocked = true;
+#endif
+}
+
+void Item::unlockModule() const
+{
+ QBS_CHECK(m_type == ItemType::Module);
+#ifndef NDEBUG
+ QBS_CHECK(m_moduleLocked);
+ m_moduleLocked = false;
+#endif
+ m_moduleMutex.unlock();
+}
+
+// This safeguard verifies that all contexts which access Module properties have really
+// acquired the lock via ModuleItemLocker, as they must.
+void Item::assertModuleLocked() const
+{
+#ifndef NDEBUG
+ if (m_type == ItemType::Module)
+ QBS_CHECK(m_moduleLocked);
+#endif
+}
+
void Item::removeProperty(const QString &name)
{
+ assertModuleLocked();
m_properties.remove(name);
}
@@ -387,5 +458,61 @@ void Item::setPropertyDeclarations(const Item::PropertyDeclarationMap &decls)
m_propertyDeclarations = decls;
}
+void Item::overrideProperties(
+ const QVariantMap &overridden,
+ const QualifiedId &namePrefix,
+ const SetupProjectParameters &parameters,
+ Logger &logger)
+{
+ if (overridden.isEmpty())
+ return;
+ for (QVariantMap::const_iterator it = overridden.constBegin(); it != overridden.constEnd();
+ ++it) {
+ const PropertyDeclaration decl = propertyDeclaration(it.key());
+ if (!decl.isValid()) {
+ ErrorInfo error(Tr::tr("Unknown property: %1.%2").
+ arg(namePrefix.toString(), it.key()));
+ handlePropertyError(error, parameters, logger);
+ continue;
+ }
+ const auto overridenValue = VariantValue::create(PropertyDeclaration::convertToPropertyType(
+ it.value(), decl.type(), namePrefix, it.key()));
+ overridenValue->markAsSetByCommandLine();
+ setProperty(it.key(), overridenValue);
+ }
+}
+
+void Item::setModules(const Modules &modules)
+{
+ m_modules = modules;
+}
+
+Item *createNonPresentModule(ItemPool &pool, const QString &name, const QString &reason, Item *module)
+{
+ qCDebug(lcModuleLoader) << "Non-required module '" << name << "' not loaded (" << reason << ")."
+ << "Creating dummy module for presence check.";
+ if (!module) {
+ module = Item::create(&pool, ItemType::ModuleInstance);
+ module->setFile(FileContext::create());
+ module->setProperty(StringConstants::nameProperty(), VariantValue::create(name));
+ }
+ module->setType(ItemType::ModuleInstance);
+ module->setProperty(StringConstants::presentProperty(), VariantValue::falseValue());
+ return module;
+}
+
+void setScopeForDescendants(Item *item, Item *scope)
+{
+ for (Item * const child : item->children()) {
+ child->setScope(scope);
+ setScopeForDescendants(child, scope);
+ }
+}
+
+CodeRange Item::codeRange() const
+{
+ return {{m_location.line(), m_location.column()}, m_endPosition};
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/item.h b/src/lib/corelib/language/item.h
index c5d8ef980..d0dde98c4 100644
--- a/src/lib/corelib/language/item.h
+++ b/src/lib/corelib/language/item.h
@@ -46,66 +46,107 @@
#include "qualifiedid.h"
#include <parser/qmljsmemorypool_p.h>
#include <tools/codelocation.h>
+#include <tools/deprecationwarningmode.h>
#include <tools/error.h>
#include <tools/version.h>
#include <QtCore/qlist.h>
#include <QtCore/qmap.h>
+#include <atomic>
+#include <mutex>
+#include <utility>
#include <vector>
namespace qbs {
+
+class SetupProjectParameters;
+
namespace Internal {
class ItemObserver;
class ItemPool;
class Logger;
+class ModuleItemLocker;
+class ProductContext;
class QBS_AUTOTEST_EXPORT Item : public QbsQmlJS::Managed
{
friend class ASTPropertiesItemHandler;
friend class ItemPool;
friend class ItemReaderASTVisitor;
+ friend class ModuleItemLocker;
Q_DISABLE_COPY(Item)
- Item(ItemPool *pool, ItemType type);
+ Item(ItemType type) : m_type(type) {}
public:
struct Module
{
- Module()
- : item(nullptr), isProduct(false), required(true)
- {}
-
QualifiedId name;
- Item *item;
- bool isProduct;
- bool required;
+ Item *item = nullptr;
+ ProductContext *product = nullptr; // Set if and only if the dep is a product.
+
+ // All the sites that declared an explicit dependency on this module. Can contain any
+ // number of module instances and at most one product.
+ using ParametersWithPriority = std::pair<QVariantMap, int>;
+ struct LoadContext {
+ LoadContext(Item *dependsItem,
+ const ParametersWithPriority &parameters)
+ : dependsItem(dependsItem), parameters(parameters) {}
+ LoadContext(Item *dependsItem, ParametersWithPriority &&parameters)
+ : dependsItem(dependsItem), parameters(std::move(parameters)) {}
+
+ LoadContext(const LoadContext &) = default;
+ LoadContext(LoadContext &&) = default;
+ LoadContext &operator=(const LoadContext &) = default;
+ LoadContext &operator=(LoadContext &&) = default;
+
+ Item *loadingItem() const { return dependsItem ? dependsItem->parent() : nullptr; }
+ Item *dependsItem;
+ ParametersWithPriority parameters;
+ };
+ std::vector<LoadContext> loadContexts;
+
QVariantMap parameters;
VersionRange versionRange;
+
+ // The shorter this value, the "closer to the product" we consider the module,
+ // and the higher its precedence is when merging property values.
+ int maxDependsChainLength = 0;
+
+ bool required = true;
};
using Modules = std::vector<Module>;
using PropertyDeclarationMap = QMap<QString, PropertyDeclaration>;
using PropertyMap = QMap<QString, ValuePtr>;
static Item *create(ItemPool *pool, ItemType type);
- Item *clone() const;
- ItemPool *pool() const { return m_pool; }
+ Item *clone(ItemPool &pool) const;
const QString &id() const { return m_id; }
const CodeLocation &location() const { return m_location; }
+ CodeRange codeRange() const;
Item *prototype() const { return m_prototype; }
+ Item *rootPrototype();
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; }
const QList<Item *> &children() const { return m_children; }
+ QList<Item *> &children() { return m_children; }
Item *child(ItemType type, bool checkForMultiple = true) const;
- const PropertyMap &properties() const { return m_properties; }
+ const PropertyMap &properties() const { assertModuleLocked(); return m_properties; }
+ PropertyMap &properties() { assertModuleLocked(); return m_properties; }
const PropertyDeclarationMap &propertyDeclarations() const { return m_propertyDeclarations; }
PropertyDeclaration propertyDeclaration(const QString &name, bool allowExpired = true) const;
+
+ // The list of modules is ordered such that dependencies appear before the modules
+ // depending on them.
const Modules &modules() const { return m_modules; }
+ Modules &modules() { return m_modules; }
+
void addModule(const Module &module);
void removeModules() { m_modules.clear(); }
- void setModules(const Modules &modules) { m_modules = modules; }
+ void setModules(const Modules &modules);
ItemType type() const { return m_type; }
void setType(ItemType type) { m_type = type; }
@@ -115,18 +156,20 @@ public:
bool hasOwnProperty(const QString &name) const;
ValuePtr property(const QString &name) const;
ValuePtr ownProperty(const QString &name) const;
- ItemValuePtr itemProperty(const QString &name, const Item *itemTemplate = nullptr);
- ItemValuePtr itemProperty(const QString &name, const ItemValueConstPtr &value);
+ ItemValuePtr itemProperty(const QString &name, ItemPool &pool, const Item *itemTemplate = nullptr);
+ ItemValuePtr itemProperty(const QString &name, const ItemValueConstPtr &value, ItemPool &pool);
JSSourceValuePtr sourceProperty(const QString &name) const;
VariantValuePtr variantProperty(const QString &name) const;
bool isOfTypeOrhasParentOfType(ItemType type) const;
- void setObserver(ItemObserver *observer) const;
+ void addObserver(ItemObserver *observer) const;
+ void removeObserver(ItemObserver *observer) const;
void setProperty(const QString &name, const ValuePtr &value);
- void setProperties(const PropertyMap &props) { m_properties = props; }
+ void setProperties(const PropertyMap &props) { assertModuleLocked(); m_properties = props; }
void removeProperty(const QString &name);
void setPropertyDeclaration(const QString &name, const PropertyDeclaration &declaration);
void setPropertyDeclarations(const PropertyDeclarationMap &decls);
void setLocation(const CodeLocation &location) { m_location = location; }
+ void setEndPosition(const CodePosition &position) { m_endPosition = position; }
void setPrototype(Item *prototype) { m_prototype = prototype; }
void setFile(const FileContextPtr &file) { m_file = file; }
void setId(const QString &id) { m_id = id; }
@@ -139,23 +182,39 @@ public:
static void removeChild(Item *parent, Item *child);
void dump() const;
bool isPresentModule() const;
- void setupForBuiltinType(Logger &logger);
+ bool isFallbackModule() const;
+ void setupForBuiltinType(DeprecationWarningMode deprecationMode, Logger &logger);
void copyProperty(const QString &propertyName, Item *target) const;
+ void overrideProperties(
+ const QVariantMap &config,
+ const QString &key,
+ const SetupProjectParameters &parameters,
+ Logger &logger);
+ void overrideProperties(
+ const QVariantMap &config,
+ const QualifiedId &namePrefix,
+ const SetupProjectParameters &parameters,
+ Logger &logger);
private:
ItemValuePtr itemProperty(const QString &name, const Item *itemTemplate,
- const ItemValueConstPtr &itemValue);
+ const ItemValueConstPtr &itemValue, ItemPool &pool);
void dump(int indentation) const;
- ItemPool *m_pool;
- mutable ItemObserver *m_observer;
+ void lockModule() const;
+ void unlockModule() const;
+ void assertModuleLocked() const;
+
+ mutable std::vector<ItemObserver *> m_observers;
+ mutable std::mutex m_observersMutex;
QString m_id;
CodeLocation m_location;
- Item *m_prototype;
- Item *m_scope;
- Item *m_outerItem;
- Item *m_parent;
+ CodePosition m_endPosition;
+ Item *m_prototype = nullptr;
+ Item *m_scope = nullptr;
+ Item *m_outerItem = nullptr;
+ Item *m_parent = nullptr;
QList<Item *> m_children;
FileContextPtr m_file;
PropertyMap m_properties;
@@ -163,10 +222,34 @@ private:
PropertyDeclarationMap m_expiredPropertyDeclarations;
Modules m_modules;
ItemType m_type;
+ mutable std::mutex m_moduleMutex;
+#ifndef NDEBUG
+ mutable std::atomic_bool m_moduleLocked = false;
+#endif
};
inline bool operator<(const Item::Module &m1, const Item::Module &m2) { return m1.name < m2.name; }
+Item *createNonPresentModule(ItemPool &pool, const QString &name, const QString &reason,
+ Item *module);
+void setScopeForDescendants(Item *item, Item *scope);
+
+// This mechanism is needed because Module items are shared between products (not doing so
+// would be prohibitively expensive).
+// The competing accesses are between
+// - Attaching a temporary qbs module for evaluating the Module condition.
+// - Cloning the module when creating an instance.
+// - Directly accessing Module properties, which happens rarely (as opposed to properties of
+// an instance).
+class ModuleItemLocker
+{
+public:
+ ModuleItemLocker(const Item &item) : m_item(item) { item.lockModule(); }
+ ~ModuleItemLocker() { m_item.unlockModule(); }
+private:
+ const Item &m_item;
+};
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/itemdeclaration.cpp b/src/lib/corelib/language/itemdeclaration.cpp
index d7230e9d6..eb9fd84a6 100644
--- a/src/lib/corelib/language/itemdeclaration.cpp
+++ b/src/lib/corelib/language/itemdeclaration.cpp
@@ -60,5 +60,11 @@ bool ItemDeclaration::isChildTypeAllowed(ItemType type) const
return m_allowedChildTypes.contains(type);
}
+ErrorInfo ItemDeclaration::checkForDeprecation(DeprecationWarningMode mode, const QString &name,
+ const CodeLocation &loc, Logger &logger) const
+{
+ return deprecationInfo().checkForDeprecation(mode, name, loc, true, logger);
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/itemdeclaration.h b/src/lib/corelib/language/itemdeclaration.h
index 6da699d28..1fbd7e456 100644
--- a/src/lib/corelib/language/itemdeclaration.h
+++ b/src/lib/corelib/language/itemdeclaration.h
@@ -71,6 +71,9 @@ public:
const TypeNames &allowedChildTypes() const { return m_allowedChildTypes; }
bool isChildTypeAllowed(ItemType type) const;
+ ErrorInfo checkForDeprecation(DeprecationWarningMode mode, const QString &name,
+ const CodeLocation &loc, Logger &logger) const;
+
private:
ItemType m_type;
Properties m_properties;
diff --git a/src/lib/corelib/language/itempool.cpp b/src/lib/corelib/language/itempool.cpp
index ccd22fe2e..6552f92ef 100644
--- a/src/lib/corelib/language/itempool.cpp
+++ b/src/lib/corelib/language/itempool.cpp
@@ -53,7 +53,7 @@ ItemPool::~ItemPool()
Item *ItemPool::allocateItem(const ItemType &type)
{
- const auto item = new (&m_pool) Item(this, type);
+ const auto item = new (&m_pool) Item(type);
m_items.push_back(item);
return item;
}
diff --git a/src/lib/corelib/language/itemreader.cpp b/src/lib/corelib/language/itemreader.cpp
deleted file mode 100644
index 578f194bc..000000000
--- a/src/lib/corelib/language/itemreader.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <tools/profiling.h>
-
-#include <QtCore/qfileinfo.h>
-
-#include <algorithm>
-
-namespace qbs {
-namespace Internal {
-
-static void makePathsCanonical(QStringList &paths)
-{
- auto it = std::remove_if(paths.begin(), paths.end(), [](QString &p) {
- p = QFileInfo(p).canonicalFilePath();
- return p.isEmpty();
- });
- auto e = paths.end();
- if (it != e)
- paths.erase(it, e);
-}
-
-ItemReader::ItemReader(Logger &logger) : m_visitorState(new ItemReaderVisitorState(logger))
-{
-}
-
-ItemReader::~ItemReader()
-{
- delete m_visitorState;
-}
-
-void ItemReader::setSearchPaths(const QStringList &searchPaths)
-{
- m_searchPaths = searchPaths;
- makePathsCanonical(m_searchPaths);
- m_allSearchPaths.clear();
-}
-
-void ItemReader::pushExtraSearchPaths(const QStringList &extraSearchPaths)
-{
- m_extraSearchPaths.push_back(extraSearchPaths);
- makePathsCanonical(m_extraSearchPaths.back());
- m_allSearchPaths.clear();
-}
-
-void ItemReader::popExtraSearchPaths()
-{
- m_extraSearchPaths.pop_back();
- m_allSearchPaths.clear();
-}
-
-std::vector<QStringList> ItemReader::extraSearchPathsStack() const
-{
- return m_extraSearchPaths;
-}
-
-void ItemReader::setExtraSearchPathsStack(const std::vector<QStringList> &s)
-{
- m_extraSearchPaths = s;
- m_allSearchPaths.clear();
-}
-
-void ItemReader::clearExtraSearchPathsStack()
-{
- m_extraSearchPaths.clear();
- m_allSearchPaths.clear();
-}
-
-const QStringList &ItemReader::allSearchPaths() const
-{
- if (m_allSearchPaths.empty()) {
- std::for_each(m_extraSearchPaths.crbegin(), m_extraSearchPaths.crend(),
- [this] (const QStringList &paths) {
- m_allSearchPaths += paths;
- });
- m_allSearchPaths += m_searchPaths;
- m_allSearchPaths.removeDuplicates();
- }
- return m_allSearchPaths;
-}
-
-Item *ItemReader::readFile(const QString &filePath)
-{
- AccumulatingTimer readFileTimer(m_elapsedTime != -1 ? &m_elapsedTime : nullptr);
- return m_visitorState->readFile(filePath, allSearchPaths(), m_pool);
-}
-
-Set<QString> 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
deleted file mode 100644
index 6ec99fcb5..000000000
--- a/src/lib/corelib/language/itemreader.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <logging/logger.h>
-#include <tools/set.h>
-
-#include <QtCore/qstringlist.h>
-
-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();
- std::vector<QStringList> extraSearchPathsStack() const;
- void setExtraSearchPathsStack(const std::vector<QStringList> &s);
- void clearExtraSearchPathsStack();
- const QStringList &allSearchPaths() const;
-
- Item *readFile(const QString &filePath);
-
- Set<QString> filesRead() const;
-
- void setEnableTiming(bool on);
- qint64 elapsedTime() const { return m_elapsedTime; }
-
-private:
- ItemPool *m_pool = nullptr;
- QStringList m_searchPaths;
- std::vector<QStringList> m_extraSearchPaths;
- mutable QStringList m_allSearchPaths;
- 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
deleted file mode 100644
index 901772d16..000000000
--- a/src/lib/corelib/language/itemreaderastvisitor.cpp
+++ /dev/null
@@ -1,405 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <api/languageinfo.h>
-#include <jsextensions/jsextensions.h>
-#include <parser/qmljsast_p.h>
-#include <tools/codelocation.h>
-#include <tools/error.h>
-#include <tools/qbsassert.h>
-#include <tools/qttools.h>
-#include <tools/stringconstants.h>
-#include <logging/translator.h>
-
-#include <algorithm>
-
-using namespace QbsQmlJS;
-
-namespace qbs {
-namespace Internal {
-
-ItemReaderASTVisitor::ItemReaderASTVisitor(ItemReaderVisitorState &visitorState,
- FileContextPtr file, ItemPool *itemPool, Logger &logger)
- : m_visitorState(visitorState)
- , m_file(std::move(file))
- , m_itemPool(itemPool)
- , m_logger(logger)
-{
-}
-
-bool ItemReaderASTVisitor::visit(AST::UiProgram *uiProgram)
-{
- ASTImportsHandler importsHandler(m_visitorState, m_logger, m_file);
- importsHandler.handleImports(uiProgram->imports);
- m_typeNameToFile = importsHandler.typeNameFileMap();
- return true;
-}
-
-static ItemValuePtr findItemProperty(const Item *container, const Item *item)
-{
- ItemValuePtr itemValue;
- const auto &srcprops = container->properties();
- auto it = std::find_if(srcprops.begin(), srcprops.end(), [item] (const ValuePtr &v) {
- return v->type() == Value::ItemValueType
- && std::static_pointer_cast<ItemValue>(v)->item() == item;
- });
- if (it != srcprops.end())
- itemValue = std::static_pointer_cast<ItemValue>(it.value());
- return itemValue;
-}
-
-bool ItemReaderASTVisitor::visit(AST::UiObjectDefinition *ast)
-{
- const QString typeName = ast->qualifiedTypeNameId->name.toString();
- const CodeLocation itemLocation = toCodeLocation(ast->qualifiedTypeNameId->identifierToken);
- const Item *baseItem = nullptr;
- Item *mostDerivingItem = nullptr;
-
- Item *item = Item::create(m_itemPool, ItemType::Unknown);
- item->setFile(m_file);
- item->setLocation(itemLocation);
-
- // 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);
- ItemType itemType;
- if (!baseTypeFileName.isEmpty()) {
- const bool isMostDerivingItem = (m_visitorState.mostDerivingItem() == nullptr);
- if (isMostDerivingItem)
- m_visitorState.setMostDerivingItem(item);
- mostDerivingItem = m_visitorState.mostDerivingItem();
- baseItem = m_visitorState.readFile(baseTypeFileName, m_file->searchPaths(), m_itemPool);
- if (isMostDerivingItem)
- m_visitorState.setMostDerivingItem(nullptr);
- QBS_CHECK(baseItem->type() <= ItemType::LastActualItem);
- itemType = baseItem->type();
- } else {
- if (fullTypeName.size() > 1) {
- throw ErrorInfo(Tr::tr("Invalid item '%1'. Did you mean to set a module property?")
- .arg(fullTypeName.join(QLatin1Char('.'))), itemLocation);
- }
- itemType = BuiltinDeclarations::instance().typeForName(typeName, itemLocation);
- checkDeprecationStatus(itemType, typeName, itemLocation);
- if (itemType == ItemType::Properties && m_item && m_item->type() == ItemType::SubProject)
- itemType = ItemType::PropertiesInSubProject;
- }
-
- item->m_type = itemType;
-
- 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.
-
- if (ast->initializer) {
- Item *mdi = m_visitorState.mostDerivingItem();
- m_visitorState.setMostDerivingItem(nullptr);
- qSwap(m_item, item);
- const ItemType oldInstanceItemType = m_instanceItemType;
- if (itemType == ItemType::Parameters || itemType == ItemType::Depends)
- m_instanceItemType = ItemType::ModuleParameters;
- ast->initializer->accept(this);
- m_instanceItemType = oldInstanceItemType;
- qSwap(m_item, item);
- m_visitorState.setMostDerivingItem(mdi);
- }
-
- ASTPropertiesItemHandler(item).handlePropertiesItems();
-
- // Inheritance resolving, part 2 (depends on alternatives having been set up).
- if (baseItem) {
- inheritItem(item, baseItem);
- if (baseItem->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);
- baseItem->file()->idScope()->setPrototype(item->file()->idScope());
-
- // Replace the base item with the most deriving item.
- ItemValuePtr baseItemIdValue = findItemProperty(baseItem->file()->idScope(), baseItem);
- if (baseItemIdValue)
- baseItemIdValue->setItem(mostDerivingItem);
- }
- } 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->hasOwnProperty(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 (Q_UNLIKELY(!ast->typeModifier.isEmpty())) {
- throw ErrorInfo(Tr::tr("public member with type modifier '%1' not supported").arg(
- ast->typeModifier.toString()));
- }
- if (ast->isReadonlyMember)
- p.setFlags(PropertyDeclaration::ReadOnlyFlag);
-
- 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.front() == QStringLiteral("id")) {
- const auto * const expStmt = AST::cast<AST::ExpressionStatement *>(ast->statement);
- if (Q_UNLIKELY(!expStmt))
- throw ErrorInfo(Tr::tr("id: must be followed by identifier"));
- const auto * const idExp = AST::cast<AST::IdentifierExpression *>(expStmt->expression);
- if (Q_UNLIKELY(!idExp || idExp->name.isEmpty()))
- throw ErrorInfo(Tr::tr("id: must be followed by identifier"));
- m_item->m_id = idExp->name.toString();
- m_file->ensureIdScope(m_itemPool);
- 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::handleBindingRhs(AST::Statement *statement,
- const JSSourceValuePtr &value)
-{
- QBS_CHECK(statement);
- QBS_CHECK(value);
-
- if (AST::cast<AST::Block *>(statement))
- value->m_flags |= JSSourceValue::HasFunctionForm;
-
- value->setFile(m_file);
- value->setSourceCode(textRefOf(m_file->content(), statement));
- value->setLocation(statement->firstSourceLocation().startLine,
- statement->firstSourceLocation().startColumn);
-
- bool usesBase, usesOuter, usesOriginal;
- IdentifierSearch idsearch;
- idsearch.add(StringConstants::baseVar(), &usesBase);
- idsearch.add(StringConstants::outerVar(), &usesOuter);
- idsearch.add(StringConstants::originalVar(), &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.size() - 1;
- for (int i = 0; i < c; ++i) {
- ValuePtr v = targetItem->ownProperty(bindingName.at(i));
- if (!v) {
- const ItemType itemType = i < c - 1 ? ItemType::ModulePrefix : m_instanceItemType;
- Item *newItem = Item::create(m_itemPool, itemType);
- newItem->setLocation(value->location());
- 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 = std::static_pointer_cast<ItemValue>(v)->item();
- }
- return targetItem;
-}
-
-void ItemReaderASTVisitor::inheritItem(Item *dst, const Item *src)
-{
- int insertPos = 0;
- for (Item *child : qAsConst(src->m_children)) {
- dst->m_children.insert(insertPos++, child);
- child->m_parent = dst;
- }
-
- for (const PropertyDeclaration &pd : src->propertyDeclarations()) {
- if (pd.flags().testFlag(PropertyDeclaration::ReadOnlyFlag)
- && dst->hasOwnProperty(pd.name())) {
- throw ErrorInfo(Tr::tr("Cannot set read-only property '%1'.").arg(pd.name()),
- dst->property(pd.name())->location());
- }
- dst->setPropertyDeclaration(pd.name(), pd);
- }
-
- 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 = std::static_pointer_cast<JSSourceValue>(v);
- QBS_CHECK(!sv->baseValue());
- const JSSourceValuePtr baseValue = std::static_pointer_cast<JSSourceValue>(it.value());
- sv->setBaseValue(baseValue);
- for (const JSSourceValue::Alternative &alt : sv->m_alternatives)
- alt.value->setBaseValue(baseValue);
- break;
- }
- case Value::ItemValueType:
- inheritItem(std::static_pointer_cast<ItemValue>(v)->item(),
- std::static_pointer_cast<const ItemValue>(it.value())->item());
- break;
- default:
- QBS_CHECK(!"unexpected value type");
- }
- }
-}
-
-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() <= LanguageInfo::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());
- for (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
deleted file mode 100644
index 963b78471..000000000
--- a/src/lib/corelib/language/itemreaderastvisitor.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <logging/logger.h>
-#include <parser/qmljsastvisitor_p.h>
-
-#include <QtCore/qhash.h>
-#include <QtCore/qstringlist.h>
-
-namespace qbs {
-class CodeLocation;
-
-namespace Internal {
-class Item;
-class ItemPool;
-class ItemReaderVisitorState;
-
-class ItemReaderASTVisitor : public QbsQmlJS::AST::Visitor
-{
-public:
- ItemReaderASTVisitor(ItemReaderVisitorState &visitorState, FileContextPtr file,
- ItemPool *itemPool, Logger &logger);
- void checkItemTypes() { doCheckItemTypes(rootItem()); }
-
- Item *rootItem() const { return m_item; }
-
-private:
- bool visit(QbsQmlJS::AST::UiProgram *uiProgram) override;
- bool visit(QbsQmlJS::AST::UiObjectDefinition *ast) override;
- bool visit(QbsQmlJS::AST::UiPublicMember *ast) override;
- bool visit(QbsQmlJS::AST::UiScriptBinding *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<QStringList, QString> m_typeNameToFile;
- Item *m_item = nullptr;
- ItemType m_instanceItemType = ItemType::ModuleInstance;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_ITEMREADERASTVISITOR_H
diff --git a/src/lib/corelib/language/itemreadervisitorstate.cpp b/src/lib/corelib/language/itemreadervisitorstate.cpp
deleted file mode 100644
index 20ddb5cfb..000000000
--- a/src/lib/corelib/language/itemreadervisitorstate.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <logging/translator.h>
-#include <parser/qmljsengine_p.h>
-#include <parser/qmljslexer_p.h>
-#include <parser/qmljsparser_p.h>
-#include <tools/error.h>
-
-#include <QtCore/qshareddata.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qfileinfo.h>
-#include <QtCore/qshareddata.h>
-#include <QtCore/qtextstream.h>
-
-namespace qbs {
-namespace Internal {
-
-class ASTCacheValueData : public QSharedData
-{
- Q_DISABLE_COPY(ASTCacheValueData)
-public:
- ASTCacheValueData()
- : ast(nullptr)
- , processing(false)
- {
- }
-
- QString code;
- QbsQmlJS::Engine engine;
- QbsQmlJS::AST::UiProgram *ast;
- bool processing;
-};
-
-class ASTCacheValue
-{
-public:
- ASTCacheValue()
- : d(new ASTCacheValueData)
- {
- }
-
- ASTCacheValue(const ASTCacheValue &other) = default;
-
- void setProcessingFlag(bool b) { d->processing = b; }
- bool isProcessing() const { return d->processing; }
-
- void setCode(const QString &code) { d->code = code; }
- QString code() const { return d->code; }
-
- QbsQmlJS::Engine *engine() const { return &d->engine; }
-
- void setAst(QbsQmlJS::AST::UiProgram *ast) { d->ast = ast; }
- QbsQmlJS::AST::UiProgram *ast() const { return d->ast; }
- bool isValid() const { return d->ast; }
-
-private:
- QExplicitlySharedDataPointer<ASTCacheValueData> d;
-};
-
-class ItemReaderVisitorState::ASTCache : public QHash<QString, ASTCacheValue> {};
-
-
-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()) {
- const QList<QbsQmlJS::DiagnosticMessage> &parserMessages = parser.diagnosticMessages();
- if (Q_UNLIKELY(!parserMessages.empty())) {
- ErrorInfo err;
- for (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);
- {
- class ProcessingFlagManager {
- public:
- ProcessingFlagManager(ASTCacheValue &v) : m_cacheValue(v) { v.setProcessingFlag(true); }
- ~ProcessingFlagManager() { m_cacheValue.setProcessingFlag(false); }
- private:
- ASTCacheValue &m_cacheValue;
- } processingFlagManager(cacheValue);
- cacheValue.ast()->accept(&astVisitor);
- }
- 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;
-}
-
-Item *ItemReaderVisitorState::mostDerivingItem() const
-{
- return m_mostDerivingItem;
-}
-
-void ItemReaderVisitorState::setMostDerivingItem(Item *item)
-{
- m_mostDerivingItem = item;
-}
-
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/itemreadervisitorstate.h b/src/lib/corelib/language/itemreadervisitorstate.h
deleted file mode 100644
index 6db9d3613..000000000
--- a/src/lib/corelib/language/itemreadervisitorstate.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <logging/logger.h>
-#include <tools/set.h>
-
-#include <QtCore/qstringlist.h>
-
-namespace qbs {
-namespace Internal {
-class Item;
-class ItemPool;
-
-class ItemReaderVisitorState
-{
-public:
- ItemReaderVisitorState(Logger &logger);
- ~ItemReaderVisitorState();
-
- Set<QString> 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;
-
- Item *mostDerivingItem() const;
- void setMostDerivingItem(Item *item);
-
-private:
- Logger &m_logger;
- Set<QString> m_filesRead;
- QHash<QString, QStringList> m_directoryEntries;
- Item *m_mostDerivingItem = nullptr;
-
- 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
index 724666cb4..7e9900a5b 100644
--- a/src/lib/corelib/language/itemtype.h
+++ b/src/lib/corelib/language/itemtype.h
@@ -74,6 +74,7 @@ enum class ItemType {
// Internal items created mainly by the module loader.
IdScope,
ModuleInstance,
+ ModuleInstancePlaceholder,
ModuleParameters,
ModulePrefix,
Outer,
@@ -82,7 +83,7 @@ enum class ItemType {
Unknown
};
-inline uint qHash(ItemType t) { return QT_PREPEND_NAMESPACE(qHash)(uint(t)); }
+inline auto qHash(ItemType t) { return QT_PREPEND_NAMESPACE(qHash)(uint(t)); }
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/jsimports.h b/src/lib/corelib/language/jsimports.h
index a892e0ec0..a4a045f2b 100644
--- a/src/lib/corelib/language/jsimports.h
+++ b/src/lib/corelib/language/jsimports.h
@@ -71,7 +71,7 @@ public:
pool.serializationOp<opType>(scopeName, filePaths, location);
}
};
-inline uint qHash(const JsImport &jsi) { return qHash(jsi.scopeName); }
+inline auto qHash(const JsImport &jsi) { return qHash(jsi.scopeName); }
inline bool operator<(const JsImport &lhs, const JsImport &rhs)
{
diff --git a/src/lib/corelib/language/language.cpp b/src/lib/corelib/language/language.cpp
index 3a08c089b..b3f4b2a64 100644
--- a/src/lib/corelib/language/language.cpp
+++ b/src/lib/corelib/language/language.cpp
@@ -51,15 +51,19 @@
#include <buildgraph/rulegraph.h> // TODO: Move to language?
#include <buildgraph/transformer.h>
#include <jsextensions/jsextensions.h>
+#include <language/value.h>
+#include <loader/loaderutils.h>
#include <logging/categories.h>
#include <logging/translator.h>
#include <tools/buildgraphlocker.h>
#include <tools/hostosinfo.h>
#include <tools/error.h>
#include <tools/fileinfo.h>
-#include <tools/scripttools.h>
#include <tools/qbsassert.h>
#include <tools/qttools.h>
+#include <tools/scripttools.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
#include <QtCore/qcryptographichash.h>
@@ -67,8 +71,6 @@
#include <QtCore/qdiriterator.h>
#include <QtCore/qmap.h>
-#include <QtScript/qscriptvalue.h>
-
#include <algorithm>
#include <memory>
#include <mutex>
@@ -102,7 +104,7 @@ void FileTagger::setPatterns(const QStringList &patterns)
m_patterns.clear();
for (const QString &pattern : patterns) {
QBS_CHECK(!pattern.isEmpty());
- m_patterns << QRegExp(pattern, Qt::CaseSensitive, QRegExp::Wildcard);
+ m_patterns << QRegularExpression(QRegularExpression::wildcardToRegularExpression(pattern));
}
}
@@ -113,9 +115,15 @@ bool Probe::needsReconfigure(const FileTime &referenceTime) const
FileInfo fi(filePath);
return !fi.exists() || fi.lastModified() > referenceTime;
};
- return std::any_of(m_importedFilesUsed.cbegin(), m_importedFilesUsed.cend(), criterion);
+ return Internal::any_of(m_importedFilesUsed, criterion);
}
+void Probe::restoreValues()
+{
+ for (auto it = m_properties.begin(), end = m_properties.end(); it != end; ++it) {
+ m_values[it.key()] = VariantValue::createStored(it.value());
+ }
+}
/*!
* \class SourceArtifact
@@ -136,41 +144,16 @@ bool Probe::needsReconfigure(const FileTime &referenceTime) const
/*!
* \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.
+ * \brief Represents the wildcard patterns 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.
- */
-std::vector<SourceArtifactPtr> ResolvedGroup::allFiles() const
-{
- std::vector<SourceArtifactPtr> lst = files;
- if (wildcards)
- lst << wildcards->files;
- return lst;
-}
-
-void ResolvedGroup::load(PersistentPool &pool)
-{
- serializationOp<PersistentPool::Load>(pool);
- if (wildcards)
- wildcards->group = this;
-}
-
-void ResolvedGroup::store(PersistentPool &pool)
-{
- serializationOp<PersistentPool::Store>(pool);
-}
-
-/*!
* \class RuleArtifact
* \brief The \c RuleArtifact class represents an Artifact item encountered in the context
* of a Rule item.
@@ -276,7 +259,7 @@ QString Rule::toString() const
FileTags Rule::staticOutputFileTags() const
{
FileTags result;
- for (const RuleArtifactConstPtr &artifact : artifacts)
+ for (const auto &artifact : artifacts)
result.unite(artifact->fileTags);
return result;
}
@@ -312,7 +295,7 @@ void ResolvedProduct::accept(BuildGraphVisitor *visitor) const
{
if (!buildData)
return;
- for (BuildGraphNode * const node : qAsConst(buildData->rootNodes()))
+ for (BuildGraphNode * const node : std::as_const(buildData->rootNodes()))
node->accept(visitor);
}
@@ -323,8 +306,8 @@ void ResolvedProduct::accept(BuildGraphVisitor *visitor) const
std::vector<SourceArtifactPtr> ResolvedProduct::allFiles() const
{
std::vector<SourceArtifactPtr> lst;
- for (const GroupConstPtr &group : groups)
- lst << group->allFiles();
+ for (const auto &group : groups)
+ lst << group->files;
return lst;
}
@@ -335,9 +318,9 @@ std::vector<SourceArtifactPtr> ResolvedProduct::allFiles() const
std::vector<SourceArtifactPtr> ResolvedProduct::allEnabledFiles() const
{
std::vector<SourceArtifactPtr> lst;
- for (const GroupConstPtr &group : groups) {
+ for (const auto &group : groups) {
if (group->enabled)
- lst << group->allFiles();
+ lst << group->files;
}
return lst;
}
@@ -346,9 +329,9 @@ FileTags ResolvedProduct::fileTagsForFileName(const QString &fileName) const
{
FileTags result;
std::unique_ptr<int> priority;
- for (const FileTaggerConstPtr &tagger : qAsConst(fileTaggers)) {
- for (const QRegExp &pattern : tagger->patterns()) {
- if (FileInfo::globMatches(pattern, fileName)) {
+ for (const FileTaggerConstPtr &tagger : std::as_const(fileTaggers)) {
+ for (const QRegularExpression &pattern : tagger->patterns()) {
+ if (pattern.match(fileName).hasMatch()) {
if (priority) {
if (*priority != tagger->priority()) {
// The taggers are expected to be sorted by priority.
@@ -373,6 +356,8 @@ void ResolvedProduct::load(PersistentPool &pool)
rule->product = this;
for (const ResolvedModulePtr &module : modules)
module->product = this;
+ for (const auto &group: groups)
+ group->restoreWildcards(buildDirectory());
}
void ResolvedProduct::store(PersistentPool &pool)
@@ -424,18 +409,9 @@ QString ResolvedProduct::uniqueName() const
return uniqueName(name, multiplexConfigurationId);
}
-QString ResolvedProduct::fullDisplayName(const QString &name,
- const QString &multiplexConfigurationId)
-{
- QString result = name;
- if (!multiplexConfigurationId.isEmpty())
- result.append(QLatin1Char(' ')).append(multiplexIdToString(multiplexConfigurationId));
- return result;
-}
-
QString ResolvedProduct::fullDisplayName() const
{
- return fullDisplayName(name, multiplexConfigurationId);
+ return fullProductDisplayName(name, multiplexConfigurationId);
}
QString ResolvedProduct::profile() const
@@ -511,6 +487,19 @@ QString ResolvedProduct::cachedExecutablePath(const QString &origFilePath) const
return m_executablePathCache.value(origFilePath);
}
+void ResolvedGroup::restoreWildcards(const QString &buildDir)
+{
+ if (wildcards) {
+ wildcards->buildDir = buildDir;
+ wildcards->prefix = prefix;
+ wildcards->baseDir = FileInfo::path(location.filePath());
+ for (const auto &sourceArtifact : files) {
+ if (sourceArtifact->fromWildcard)
+ wildcards->expandedFiles += sourceArtifact->absoluteFilePath;
+ }
+ }
+}
+
ResolvedProject::ResolvedProject() : enabled(true), m_topLevelProject(nullptr)
{
@@ -522,7 +511,7 @@ void ResolvedProject::accept(BuildGraphVisitor *visitor) const
{
for (const ResolvedProductPtr &product : products)
product->accept(visitor);
- for (const ResolvedProjectPtr &subProject : qAsConst(subProjects))
+ for (const ResolvedProjectPtr &subProject : std::as_const(subProjects))
subProject->accept(visitor);
}
@@ -541,7 +530,7 @@ TopLevelProject *ResolvedProject::topLevelProject()
std::vector<ResolvedProjectPtr> ResolvedProject::allSubProjects() const
{
std::vector<ResolvedProjectPtr> projectList = subProjects;
- for (const ResolvedProjectConstPtr &subProject : subProjects)
+ for (const auto &subProject : subProjects)
projectList << subProject->allSubProjects();
return projectList;
}
@@ -549,7 +538,7 @@ std::vector<ResolvedProjectPtr> ResolvedProject::allSubProjects() const
std::vector<ResolvedProductPtr> ResolvedProject::allProducts() const
{
std::vector<ResolvedProductPtr> productList = products;
- for (const ResolvedProjectConstPtr &subProject : qAsConst(subProjects))
+ for (const auto &subProject : std::as_const(subProjects))
productList << subProject->allProducts();
return productList;
}
@@ -561,11 +550,11 @@ void ResolvedProject::load(PersistentPool &pool)
[](const ResolvedProductPtr &p) {
if (!p->buildData)
return;
- for (BuildGraphNode * const node : qAsConst(p->buildData->allNodes())) {
+ for (BuildGraphNode * const node : std::as_const(p->buildData->allNodes())) {
node->product = p;
// restore parent links
- for (BuildGraphNode * const child : qAsConst(node->children))
+ for (BuildGraphNode * const child : std::as_const(node->children))
child->parents.insert(node);
}
});
@@ -591,7 +580,7 @@ TopLevelProject::~TopLevelProject()
QString TopLevelProject::deriveId(const QVariantMap &config)
{
const QVariantMap qbsProperties = config.value(StringConstants::qbsModule()).toMap();
- const QString configurationName = qbsProperties.value(
+ QString configurationName = qbsProperties.value(
StringConstants::configurationNameProperty()).toString();
return configurationName;
}
@@ -614,10 +603,20 @@ QString TopLevelProject::profile() const
void TopLevelProject::makeModuleProvidersNonTransient()
{
- for (ModuleProviderInfo &m : moduleProviderInfo)
+ for (ModuleProviderInfo &m : moduleProviderInfo.providers)
m.transientOutput = false;
}
+QVariantMap TopLevelProject::fullProfileConfigsTree() const
+{
+ QVariantMap tree;
+ for (auto it = profileConfigs.cbegin(); it != profileConfigs.cend(); ++it) {
+ tree.insert(it.key(), SetupProjectParameters::finalBuildConfigurationTree(
+ it.value().toMap(), overriddenValues));
+ }
+ return tree;
+}
+
QString TopLevelProject::buildGraphFilePath() const
{
return ProjectBuildData::deriveBuildGraphFilePath(buildDirectory, id());
@@ -664,7 +663,7 @@ void TopLevelProject::store(PersistentPool &pool)
void TopLevelProject::cleanupModuleProviderOutput()
{
QString error;
- for (const ModuleProviderInfo &m : moduleProviderInfo) {
+ for (const ModuleProviderInfo &m : std::as_const(moduleProviderInfo.providers)) {
if (m.transientOutput) {
if (!removeDirectoryWithContents(m.outputDirPath(buildDirectory), &error))
qCWarning(lcBuildGraph) << "Error removing module provider output:" << error;
@@ -708,25 +707,22 @@ void TopLevelProject::cleanupModuleProviderOutput()
* \brief The \c SourceArtifacts resulting from the expanded list of matching files.
*/
-Set<QString> SourceWildCards::expandPatterns(const GroupConstPtr &group,
- const QString &baseDir, const QString &buildDir)
+void SourceWildCards::expandPatterns()
{
- Set<QString> files = expandPatterns(group, patterns, baseDir, buildDir);
- files -= expandPatterns(group, excludePatterns, baseDir, buildDir);
- return files;
+ dirTimeStamps.clear();
+ expandedFiles = expandPatterns(patterns) - expandPatterns(excludePatterns);
}
-Set<QString> SourceWildCards::expandPatterns(const GroupConstPtr &group,
- const QStringList &patterns, const QString &baseDir, const QString &buildDir)
+Set<QString> SourceWildCards::expandPatterns(const QStringList &patterns)
{
Set<QString> files;
- QString expandedPrefix = group->prefix;
+ QString expandedPrefix = prefix;
if (expandedPrefix.startsWith(StringConstants::tildeSlash()))
expandedPrefix.replace(0, 1, QDir::homePath());
for (QString pattern : patterns) {
pattern.prepend(expandedPrefix);
pattern.replace(QLatin1Char('\\'), QLatin1Char('/'));
- QStringList parts = pattern.split(QLatin1Char('/'), QString::SkipEmptyParts);
+ QStringList parts = pattern.split(QLatin1Char('/'), Qt::SkipEmptyParts);
if (FileInfo::isAbsolute(pattern)) {
QString rootDir;
if (HostOsInfo::isWindowsHost() && pattern.at(0) != QLatin1Char('/')) {
@@ -736,18 +732,17 @@ Set<QString> SourceWildCards::expandPatterns(const GroupConstPtr &group,
} else {
rootDir = QLatin1Char('/');
}
- expandPatterns(files, group, parts, rootDir, buildDir);
+ expandPatterns(files, parts, rootDir);
} else {
- expandPatterns(files, group, parts, baseDir, buildDir);
+ expandPatterns(files, parts, baseDir);
}
}
return files;
}
-void SourceWildCards::expandPatterns(Set<QString> &result, const GroupConstPtr &group,
- const QStringList &parts,
- const QString &baseDir, const QString &buildDir)
+void SourceWildCards::expandPatterns(Set<QString> &result, const QStringList &parts,
+ const QString &baseDir)
{
// 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
@@ -755,8 +750,6 @@ void SourceWildCards::expandPatterns(Set<QString> &result, const GroupConstPtr &
if (baseDir.startsWith(buildDir))
return;
- dirTimeStamps.emplace_back(baseDir, FileInfo(baseDir).lastModified());
-
QStringList changed_parts = parts;
bool recursive = false;
QString part = changed_parts.takeFirst();
@@ -783,8 +776,12 @@ void SourceWildCards::expandPatterns(Set<QString> &result, const GroupConstPtr &
: QDir::Files | QDir::System
| QDir::Dirs; // This one is needed to get symbolic links to directories
- if (isDir && !FileInfo::isPattern(filePattern))
+ if (FileInfo::isPattern(filePattern)) {
+ if (!recursive)
+ dirTimeStamps.emplace_back(baseDir, FileInfo(baseDir).lastModified());
+ } else if (isDir) {
itFilters |= QDir::Hidden;
+ }
if (filePattern != StringConstants::dotDot() && filePattern != StringConstants::dot())
itFilters |= QDir::NoDotAndDotDot;
@@ -796,16 +793,28 @@ void SourceWildCards::expandPatterns(Set<QString> &result, const GroupConstPtr &
continue; // See above.
if (!isDir && it.fileInfo().isDir() && !it.fileInfo().isSymLink())
continue;
- if (isDir) {
- expandPatterns(result, group, changed_parts, filePath, buildDir);
- } else {
- if (parentDir != baseDir)
- dirTimeStamps.emplace_back(parentDir, FileInfo(baseDir).lastModified());
+ if (isDir)
+ expandPatterns(result, changed_parts, filePath);
+ else
result += QDir::cleanPath(filePath);
- }
}
}
+bool SourceWildCards::hasChangedSinceExpansion() const
+{
+ const bool reExpansionRequired =
+ Internal::any_of(dirTimeStamps,
+ [](const std::pair<QString, FileTime> &pair) {
+ return FileInfo(pair.first).lastModified() > pair.second;
+ });
+ if (reExpansionRequired)
+ return true;
+
+ auto wc = *this;
+ wc.expandPatterns();
+ return this->expandedFiles != wc.expandedFiles;
+}
+
template<typename L>
QMap<QString, typename L::value_type> listToMap(const L &list)
{
@@ -824,14 +833,13 @@ bool listsAreEqual(const L &l1, const L &l2)
using V = typename L::value_type;
const QMap<QString, V> map1 = listToMap(l1);
const QMap<QString, V> map2 = listToMap(l2);
- for (const QString &key : map1.keys()) {
+ const auto keys = map1.keys();
+ return Internal::all_of(keys, [&map1, &map2](const auto &key) {
const V &value2 = map2.value(key);
if (!value2)
return false;
- if (!equals(map1.value(key).get(), value2.get()))
- return false;
- }
- return true;
+ return equals(map1.value(key).get(), value2.get());
+ });
}
QString keyFromElem(const SourceArtifactPtr &sa) { return sa->absoluteFilePath; }
@@ -857,6 +865,7 @@ bool operator==(const SourceArtifactInternal &sa1, const SourceArtifactInternal
&& sa1.fileTags == sa2.fileTags
&& sa1.overrideFileTags == sa2.overrideFileTags
&& sa1.targetOfModule == sa2.targetOfModule
+ && !sa1.properties == !sa2.properties
&& *sa1.properties == *sa2.properties;
}
@@ -894,8 +903,8 @@ bool operator==(const RuleArtifact &a1, const RuleArtifact &a2)
return a1.filePath == a2.filePath
&& a1.fileTags == a2.fileTags
&& a1.alwaysUpdated == a2.alwaysUpdated
- && Set<RuleArtifact::Binding>::fromStdVector(a1.bindings) ==
- Set<RuleArtifact::Binding>::fromStdVector(a2.bindings);
+ && rangeTo<Set<RuleArtifact::Binding>>(a1.bindings) ==
+ rangeTo<Set<RuleArtifact::Binding>>(a2.bindings);
}
bool operator==(const RuleArtifact::Binding &b1, const RuleArtifact::Binding &b2)
@@ -903,7 +912,7 @@ 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)
+QHashValueType qHash(const RuleArtifact::Binding &b)
{
return qHash(std::make_pair(b.code, b.name.join(QLatin1Char(','))));
}
@@ -914,11 +923,6 @@ bool artifactPropertyListsAreEqual(const std::vector<ArtifactPropertiesPtr> &l1,
return listsAreEqual(l1, l2);
}
-QString multiplexIdToString(const QString &id)
-{
- return QString::fromUtf8(QByteArray::fromBase64(id.toUtf8()));
-}
-
bool operator==(const PrivateScriptFunction &a, const PrivateScriptFunction &b)
{
return equals(a.m_sharedData.get(), b.m_sharedData.get());
@@ -934,7 +938,7 @@ bool operator==(const ExportedProperty &p1, const ExportedProperty &p2)
bool operator==(const ExportedModuleDependency &d1, const ExportedModuleDependency &d2)
{
- return d1.name == d2.name && d1.moduleProperties == d2.moduleProperties;
+ return d1.name == d2.name && qVariantMapsEqual(d1.moduleProperties, d2.moduleProperties);
}
bool equals(const std::vector<ExportedItemPtr> &l1, const std::vector<ExportedItemPtr> &l2)
@@ -961,20 +965,35 @@ bool operator==(const ExportedModule &m1, const ExportedModule &m2)
for (auto it1 = m1.cbegin(), it2 = m2.cbegin(); it1 != m1.cend(); ++it1, ++it2) {
if (it1.key()->name != it2.key()->name)
return false;
- if (it1.value() != it2.value())
+ if (!qVariantMapsEqual(it1.value(), it2.value()))
return false;
}
return true;
};
- return m1.propertyValues == m2.propertyValues
- && m1.modulePropertyValues == m2.modulePropertyValues
- && equals(m1.children, m2.children)
- && m1.m_properties == m2.m_properties
- && m1.importStatements == m2.importStatements
- && m1.productDependencies.size() == m2.productDependencies.size()
- && m1.productDependencies == m2.productDependencies
- && depMapsEqual(m1.dependencyParameters, m2.dependencyParameters);
+ return qVariantMapsEqual(m1.propertyValues, m2.propertyValues)
+ && qVariantMapsEqual(m1.modulePropertyValues, m2.modulePropertyValues)
+ && equals(m1.children, m2.children) && m1.m_properties == m2.m_properties
+ && m1.importStatements == m2.importStatements
+ && m1.productDependencies.size() == m2.productDependencies.size()
+ && m1.productDependencies == m2.productDependencies
+ && depMapsEqual(m1.dependencyParameters, m2.dependencyParameters);
+}
+
+JSValue PrivateScriptFunction::getFunction(ScriptEngine *engine, const QString &errorMessage) const
+{
+ if (JS_IsUndefined(scriptFunction)) {
+ ScopedJsValue val(engine->context(),
+ engine->evaluate(JsValueOwner::Caller, sourceCode(),
+ location().filePath(), location().line()));
+ if (Q_UNLIKELY(!JS_IsFunction(engine->context(), val)))
+ throw ErrorInfo(errorMessage, location());
+ scriptFunction = val.release();
+ engine->addExternallyCachedValue(&scriptFunction);
+ } else {
+ QBS_CHECK(JS_IsFunction(engine->context(), scriptFunction));
+ }
+ return scriptFunction;
}
} // namespace Internal
diff --git a/src/lib/corelib/language/language.h b/src/lib/corelib/language/language.h
index 2d934edc2..774d703d0 100644
--- a/src/lib/corelib/language/language.h
+++ b/src/lib/corelib/language/language.h
@@ -49,35 +49,34 @@
#include <buildgraph/forward_decls.h>
#include <tools/codelocation.h>
+#include <tools/fileinfo.h>
#include <tools/filetime.h>
#include <tools/joblimits.h>
#include <tools/persistence.h>
+#include <tools/porting.h>
#include <tools/set.h>
#include <tools/weakpointer.h>
#include <QtCore/qdatastream.h>
#include <QtCore/qhash.h>
#include <QtCore/qprocess.h>
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptvalue.h>
+#include <quickjs.h>
#include <memory>
#include <mutex>
#include <vector>
-QT_BEGIN_NAMESPACE
-class QScriptEngine;
-QT_END_NAMESPACE
-
namespace qbs {
namespace Internal {
class BuildGraphLocker;
class BuildGraphLoader;
class BuildGraphVisitor;
+class ScriptEngine;
class FileTagger
{
@@ -88,7 +87,7 @@ public:
return FileTaggerPtr(new FileTagger(patterns, fileTags, priority));
}
- const QList<QRegExp> &patterns() const { return m_patterns; }
+ const QList<QRegularExpression> &patterns() const { return m_patterns; }
const FileTags &fileTags() const { return m_fileTags; }
int priority() const { return m_priority; }
@@ -103,7 +102,7 @@ private:
void setPatterns(const QStringList &patterns);
- QList<QRegExp> m_patterns;
+ QList<QRegularExpression> m_patterns;
FileTags m_fileTags;
int m_priority = 0;
};
@@ -118,17 +117,20 @@ public:
const QString &configureScript,
const QVariantMap &properties,
const QVariantMap &initialProperties,
+ const QMap<QString, VariantValuePtr> &values,
const std::vector<QString> &importedFilesUsed)
{
return ProbeConstPtr(new Probe(globalId, location, condition, configureScript, properties,
- initialProperties, importedFilesUsed));
+ initialProperties, values, importedFilesUsed));
}
const QString &globalId() const { return m_globalId; }
bool condition() const { return m_condition; }
+ const CodeLocation &location() const { return m_location; }
const QString &configureScript() const { return m_configureScript; }
const QVariantMap &properties() const { return m_properties; }
const QVariantMap &initialProperties() const { return m_initialProperties; }
+ const QMap<QString, VariantValuePtr> &values() const { return m_values; }
const std::vector<QString> &importedFilesUsed() const { return m_importedFilesUsed; }
bool needsReconfigure(const FileTime &referenceTime) const;
@@ -136,31 +138,39 @@ public:
{
pool.serializationOp<opType>(m_globalId, m_location, m_condition, m_configureScript,
m_properties, m_initialProperties, m_importedFilesUsed);
+ if constexpr (opType == PersistentPool::OpType::Load)
+ restoreValues();
}
private:
Probe() = default;
- Probe(const QString &globalId,
+ Probe(QString globalId,
const CodeLocation &location,
bool condition,
- const QString &configureScript,
- const QVariantMap &properties,
- const QVariantMap &initialProperties,
- const std::vector<QString> &importedFilesUsed)
- : m_globalId(globalId)
+ QString configureScript,
+ QVariantMap properties,
+ QVariantMap initialProperties,
+ QMap<QString, VariantValuePtr> values,
+ std::vector<QString> importedFilesUsed)
+ : m_globalId(std::move(globalId))
, m_location(location)
- , m_configureScript(configureScript)
- , m_properties(properties)
- , m_initialProperties(initialProperties)
- , m_importedFilesUsed(importedFilesUsed)
+ , m_configureScript(std::move(configureScript))
+ , m_properties(std::move(properties))
+ , m_initialProperties(std::move(initialProperties))
+ , m_values(std::move(values))
+ , m_importedFilesUsed(std::move(importedFilesUsed))
, m_condition(condition)
- {}
+ {
+ }
+
+ void restoreValues();
QString m_globalId;
CodeLocation m_location;
QString m_configureScript;
QVariantMap m_properties;
QVariantMap m_initialProperties;
+ QMap<QString, VariantValuePtr> m_values;
std::vector<QString> m_importedFilesUsed;
bool m_condition = false;
};
@@ -212,7 +222,7 @@ private:
: alwaysUpdated(true)
{}
};
-uint qHash(const RuleArtifact::Binding &b);
+QHashValueType 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);
@@ -232,11 +242,12 @@ public:
bool overrideFileTags;
QString targetOfModule;
PropertyMapPtr properties;
+ bool fromWildcard;
template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool)
{
pool.serializationOp<opType>(absoluteFilePath, fileTags, overrideFileTags, properties,
- targetOfModule);
+ targetOfModule, fromWildcard);
}
private:
@@ -250,26 +261,28 @@ inline bool operator!=(const SourceArtifactInternal &sa1, const SourceArtifactIn
class SourceWildCards
{
public:
- Set<QString> expandPatterns(const GroupConstPtr &group, const QString &baseDir,
- const QString &buildDir);
+ void expandPatterns();
+ bool hasChangedSinceExpansion() const;
+
+ // to be restored by the owning class
+ QString prefix;
+ QString baseDir;
+ QString buildDir;
+ Set<QString> expandedFiles;
- const ResolvedGroup *group = nullptr; // The owning group.
+ // stored
QStringList patterns;
QStringList excludePatterns;
std::vector<std::pair<QString, FileTime>> dirTimeStamps;
- std::vector<SourceArtifactPtr> files;
template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool)
{
- pool.serializationOp<opType>(patterns, excludePatterns, dirTimeStamps, files);
+ pool.serializationOp<opType>(patterns, excludePatterns, dirTimeStamps);
}
private:
- Set<QString> expandPatterns(const GroupConstPtr &group, const QStringList &patterns,
- const QString &baseDir, const QString &buildDir);
- void expandPatterns(Set<QString> &result, const GroupConstPtr &group,
- const QStringList &parts, const QString &baseDir,
- const QString &buildDir);
+ Set<QString> expandPatterns(const QStringList &patterns);
+ void expandPatterns(Set<QString> &result, const QStringList &parts, const QString &baseDir);
};
class QBS_AUTOTEST_EXPORT ResolvedGroup
@@ -289,13 +302,9 @@ public:
QString targetOfModule;
bool overrideTags = false;
- std::vector<SourceArtifactPtr> allFiles() const;
-
- void load(PersistentPool &pool);
- void store(PersistentPool &pool);
+ void restoreWildcards(const QString &buildDir);
-private:
- template<PersistentPool::OpType opType> void serializationOp(PersistentPool &pool)
+ template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool)
{
pool.serializationOp<opType>(name, enabled, location, prefix, files, wildcards, properties,
fileTags, targetOfModule, overrideTags);
@@ -334,8 +343,8 @@ class PrivateScriptFunction
friend bool operator==(const PrivateScriptFunction &a, const PrivateScriptFunction &b);
public:
void initialize(const ScriptFunctionPtr &sharedData) { m_sharedData = sharedData; }
- mutable QScriptValue scriptFunction; // not stored
+ JSValue getFunction(ScriptEngine *engine, const QString &errorMessage) const;
QString &sourceCode() const { return m_sharedData->sourceCode; }
CodeLocation &location() const { return m_sharedData->location; }
ResolvedFileContextConstPtr &fileContext() const { return m_sharedData->fileContext; }
@@ -348,6 +357,7 @@ public:
private:
ScriptFunctionPtr m_sharedData;
+ mutable JSValue scriptFunction = JS_UNDEFINED; // not stored
};
bool operator==(const PrivateScriptFunction &a, const PrivateScriptFunction &b);
@@ -362,7 +372,7 @@ public:
static ResolvedModulePtr create() { return ResolvedModulePtr(new ResolvedModule); }
QString name;
- QStringList moduleDependencies;
+ QStringList moduleDependencies; // TODO: Still needed?
PrivateScriptFunction setupBuildEnvironmentScript;
PrivateScriptFunction setupRunEnvironmentScript;
ResolvedProduct *product = nullptr;
@@ -598,7 +608,6 @@ public:
static QString uniqueName(const QString &name,
const QString &multiplexConfigurationId);
QString uniqueName() const;
- static QString fullDisplayName(const QString &name, const QString &multiplexConfigurationId);
QString fullDisplayName() const;
QString profile() const;
@@ -688,12 +697,13 @@ public:
QString buildDirectory; // Not saved
QProcessEnvironment environment;
std::vector<ProbeConstPtr> probes;
- ModuleProviderInfoList moduleProviderInfo;
+ StoredModuleProviderInfo moduleProviderInfo;
QHash<QString, QString> canonicalFilePathResults; // Results of calls to "File.canonicalFilePath()."
QHash<QString, bool> fileExistsResults; // Results of calls to "File.exists()".
QHash<std::pair<QString, quint32>, QStringList> directoryEntriesResults; // Results of calls to "File.directoryEntries()".
QHash<QString, FileTime> fileLastModifiedResults; // Results of calls to "File.lastModified()".
+ CodeLinks codeLinks;
std::unique_ptr<ProjectBuildData> buildData;
BuildGraphLocker *bgLocker; // This holds the system-wide build graph file lock.
bool locked; // This is the API-level lock for the project instance.
@@ -708,6 +718,7 @@ public:
QString id() const { return m_id; }
QString profile() const;
void makeModuleProvidersNonTransient();
+ QVariantMap fullProfileConfigsTree() const; // Tree-ified + overridden values
QVariantMap profileConfigs;
QVariantMap overriddenValues;
@@ -724,7 +735,7 @@ private:
directoryEntriesResults, fileLastModifiedResults, environment,
probes, profileConfigs, overriddenValues, buildSystemFiles,
lastStartResolveTime, lastEndResolveTime, warningsEncountered,
- buildData, moduleProviderInfo);
+ buildData, moduleProviderInfo, codeLinks);
}
void load(PersistentPool &pool) override;
void store(PersistentPool &pool) override;
@@ -738,8 +749,6 @@ private:
bool artifactPropertyListsAreEqual(const std::vector<ArtifactPropertiesPtr> &l1,
const std::vector<ArtifactPropertiesPtr> &l2);
-QString multiplexIdToString(const QString &id);
-
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/language.pri b/src/lib/corelib/language/language.pri
deleted file mode 100644
index e07a671b9..000000000
--- a/src/lib/corelib/language/language.pri
+++ /dev/null
@@ -1,82 +0,0 @@
-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/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/moduleproviderinfo.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/scriptpropertyobserver.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_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
deleted file mode 100644
index f248fbb1a..000000000
--- a/src/lib/corelib/language/loader.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "evaluator.h"
-#include "language.h"
-#include "moduleloader.h"
-#include "projectresolver.h"
-#include "scriptengine.h"
-
-#include <logging/translator.h>
-#include <tools/fileinfo.h>
-#include <tools/profile.h>
-#include <tools/progressobserver.h>
-#include <tools/qbsassert.h>
-#include <tools/settings.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stringconstants.h>
-
-#include <QtCore/qdir.h>
-#include <QtCore/qobject.h>
-#include <QtCore/qtimer.h>
-
-namespace qbs {
-namespace Internal {
-
-Loader::Loader(ScriptEngine *engine, Logger logger)
- : m_logger(std::move(logger))
- , m_progressObserver(nullptr)
- , m_engine(engine)
-{
- m_logger.storeWarnings();
-}
-
-void Loader::setProgressObserver(ProgressObserver *observer)
-{
- m_progressObserver = observer;
-}
-
-void Loader::setSearchPaths(const QStringList &_searchPaths)
-{
- QStringList searchPaths;
- for (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::setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes)
-{
- m_oldProjectProbes = oldProbes;
-}
-
-void Loader::setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes)
-{
- m_oldProductProbes = oldProbes;
-}
-
-void Loader::setStoredProfiles(const QVariantMap &profiles)
-{
- m_storedProfiles = profiles;
-}
-
-void Loader::setStoredModuleProviderInfo(const ModuleProviderInfoList &providerInfo)
-{
- m_storedModuleProviderInfo = providerInfo;
-}
-
-TopLevelProjectPtr Loader::loadProject(const SetupProjectParameters &_parameters)
-{
- SetupProjectParameters parameters = _parameters;
-
- if (parameters.topLevelProfile().isEmpty()) {
- Settings settings(parameters.settingsDirectory());
- QString profileName = settings.defaultProfile();
- if (profileName.isEmpty()) {
- m_logger.qbsDebug() << Tr::tr("No profile specified and no default profile exists. "
- "Using default property values.");
- profileName = Profile::fallbackName();
- }
- parameters.setTopLevelProfile(profileName);
- parameters.expandBuildConfiguration();
- }
-
- setupProjectFilePath(parameters);
- QBS_CHECK(QFileInfo(parameters.projectFilePath()).isAbsolute());
- m_logger.qbsDebug() << "Using project file '"
- << QDir::toNativeSeparators(parameters.projectFilePath()) << "'.";
-
- 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();
- Evaluator evaluator(m_engine);
- ModuleLoader moduleLoader(&evaluator, m_logger);
- moduleLoader.setProgressObserver(m_progressObserver);
- moduleLoader.setSearchPaths(m_searchPaths);
- moduleLoader.setOldProjectProbes(m_oldProjectProbes);
- moduleLoader.setOldProductProbes(m_oldProductProbes);
- moduleLoader.setLastResolveTime(m_lastResolveTime);
- moduleLoader.setStoredProfiles(m_storedProfiles);
- moduleLoader.setStoredModuleProviderInfo(m_storedModuleProviderInfo);
- const ModuleLoaderResult loadResult = moduleLoader.load(parameters);
- ProjectResolver resolver(&evaluator, loadResult, std::move(parameters), m_logger);
- resolver.setProgressObserver(m_progressObserver);
- const TopLevelProjectPtr project = resolver.resolve();
- project->lastStartResolveTime = resolveTime;
- project->lastEndResolveTime = FileTime::currentTime();
-
- // E.g. if the top-level project is disabled.
- if (m_progressObserver)
- m_progressObserver->setFinished();
-
- return project;
-}
-
-void Loader::setupProjectFilePath(SetupProjectParameters &parameters)
-{
- QString projectFilePath = parameters.projectFilePath();
- if (projectFilePath.isEmpty())
- 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()) {
- parameters.setProjectFilePath(projectFilePath);
- return;
- }
- if (!projectFileInfo.isDir())
- throw ErrorInfo(Tr::tr("Project file '%1' has invalid type.").arg(projectFilePath));
-
- const QStringList &actualFileNames
- = QDir(projectFilePath).entryList(StringConstants::qbsFileWildcards(), QDir::Files);
- if (actualFileNames.empty()) {
- QString error;
- if (parameters.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.size() > 1) {
- throw ErrorInfo(Tr::tr("More than one project file found in directory '%1'.")
- .arg(projectFilePath));
- }
- projectFilePath.append(QLatin1Char('/')).append(actualFileNames.front());
-
- projectFilePath = QDir::current().filePath(projectFilePath);
- projectFilePath = QDir::cleanPath(projectFilePath);
- parameters.setProjectFilePath(projectFilePath);
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/loader.h b/src/lib/corelib/language/loader.h
deleted file mode 100644
index d172a74ed..000000000
--- a/src/lib/corelib/language/loader.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "moduleproviderinfo.h"
-#include <logging/logger.h>
-#include <tools/filetime.h>
-
-#include <QtCore/qstringlist.h>
-
-namespace qbs {
-class Settings;
-class SetupProjectParameters;
-namespace Internal {
-class Logger;
-class ProgressObserver;
-class ScriptEngine;
-
-class QBS_AUTOTEST_EXPORT Loader
-{
-public:
- Loader(ScriptEngine *engine, Logger logger);
-
- void setProgressObserver(ProgressObserver *observer);
- void setSearchPaths(const QStringList &searchPaths);
- void setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes);
- void setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes);
- void setLastResolveTime(const FileTime &time) { m_lastResolveTime = time; }
- void setStoredProfiles(const QVariantMap &profiles);
- void setStoredModuleProviderInfo(const ModuleProviderInfoList &providerInfo);
- TopLevelProjectPtr loadProject(const SetupProjectParameters &parameters);
-
- static void setupProjectFilePath(SetupProjectParameters &parameters);
-
-private:
- Logger m_logger;
- ProgressObserver *m_progressObserver;
- ScriptEngine * const m_engine;
- QStringList m_searchPaths;
- std::vector<ProbeConstPtr> m_oldProjectProbes;
- QHash<QString, std::vector<ProbeConstPtr>> m_oldProductProbes;
- ModuleProviderInfoList m_storedModuleProviderInfo;
- QVariantMap m_storedProfiles;
- FileTime m_lastResolveTime;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_LOADER_H
diff --git a/src/lib/corelib/language/moduleloader.cpp b/src/lib/corelib/language/moduleloader.cpp
deleted file mode 100644
index ec7c38197..000000000
--- a/src/lib/corelib/language/moduleloader.cpp
+++ /dev/null
@@ -1,4142 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <api/languageinfo.h>
-#include <language/language.h>
-#include <logging/categories.h>
-#include <logging/logger.h>
-#include <logging/translator.h>
-#include <tools/error.h>
-#include <tools/fileinfo.h>
-#include <tools/jsliterals.h>
-#include <tools/preferences.h>
-#include <tools/profile.h>
-#include <tools/profiling.h>
-#include <tools/progressobserver.h>
-#include <tools/qbsassert.h>
-#include <tools/qttools.h>
-#include <tools/scripttools.h>
-#include <tools/settings.h>
-#include <tools/stlutils.h>
-#include <tools/stringconstants.h>
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qdir.h>
-#include <QtCore/qdiriterator.h>
-#include <QtCore/qjsondocument.h>
-#include <QtCore/qjsonobject.h>
-#include <QtCore/qtemporaryfile.h>
-#include <QtCore/qtextstream.h>
-#include <QtScript/qscriptvalueiterator.h>
-
-#include <algorithm>
-#include <memory>
-#include <utility>
-
-namespace qbs {
-namespace Internal {
-
-static QString shadowProductPrefix() { return QStringLiteral("__shadow__"); }
-
-static void handlePropertyError(const ErrorInfo &error, const SetupProjectParameters &params,
- Logger &logger)
-{
- if (params.propertyCheckingMode() == ErrorHandlingMode::Strict)
- throw error;
- logger.printWarning(error);
-}
-
-class ModuleLoader::ItemModuleList : public QList<Item::Module> {};
-
-static QString probeGlobalId(Item *probe)
-{
- QString id;
-
- for (Item *obj = probe; obj; obj = obj->prototype()) {
- if (!obj->id().isEmpty()) {
- id = obj->id();
- break;
- }
- }
-
- if (id.isEmpty())
- return {};
-
- QBS_CHECK(probe->file());
- return id + QLatin1Char('_') + probe->file()->filePath();
-}
-
-class ModuleLoader::ProductSortByDependencies
-{
-public:
- ProductSortByDependencies(TopLevelProjectContext &tlp) : m_tlp(tlp)
- {
- }
-
- void apply()
- {
- QHash<QString, std::vector<ProductContext *>> productsMap;
- QList<ProductContext *> allProducts;
- for (ProjectContext * const projectContext : qAsConst(m_tlp.projects)) {
- for (auto &product : projectContext->products) {
- allProducts.push_back(&product);
- productsMap[product.name].push_back(&product);
- }
- }
- Set<ProductContext *> allDependencies;
- for (auto productContext : qAsConst(allProducts)) {
- auto &productDependencies = m_dependencyMap[productContext];
- for (const auto &dep : qAsConst(productContext->info.usedProducts)) {
- QBS_CHECK(!dep.name.isEmpty());
- const auto &deps = productsMap.value(dep.name);
- if (dep.profile == StringConstants::star()) {
- QBS_CHECK(!deps.empty());
- for (ProductContext *depProduct : deps) {
- if (depProduct == productContext)
- continue;
- productDependencies.push_back(depProduct);
- allDependencies << depProduct;
- }
- } else {
- auto it = std::find_if(deps.begin(), deps.end(), [&dep] (ProductContext *p) {
- return p->multiplexConfigurationId == dep.multiplexConfigurationId;
- });
- if (it == deps.end()) {
- QBS_CHECK(!productContext->multiplexConfigurationId.isEmpty());
- const QString productName = ResolvedProduct::fullDisplayName(
- productContext->name, productContext->multiplexConfigurationId);
- const QString depName = ResolvedProduct::fullDisplayName(
- dep.name, dep.multiplexConfigurationId);
- throw ErrorInfo(Tr::tr("Dependency from product '%1' to product '%2' not "
- "fulfilled.").arg(productName, depName),
- productContext->item->location());
- }
- productDependencies.push_back(*it);
- allDependencies << *it;
- }
- }
- }
- const Set<ProductContext *> rootProducts
- = Set<ProductContext *>::fromList(allProducts) - allDependencies;
- for (ProductContext * const rootProduct : rootProducts)
- traverse(rootProduct);
- if (m_sortedProducts.size() < allProducts.size()) {
- for (auto const product : qAsConst(allProducts)) {
- QList<ModuleLoader::ProductContext *> path;
- findCycle(product, path);
- }
- }
- QBS_CHECK(m_sortedProducts.size() == allProducts.size());
- }
-
- // No product at position i has dependencies to a product at position j > i.
- const QList<ProductContext *> &sortedProducts() const
- {
- return m_sortedProducts;
- }
-
-private:
- void traverse(ModuleLoader::ProductContext *product)
- {
- if (!m_seenProducts.insert(product).second)
- return;
- for (const auto &dependency : m_dependencyMap.value(product))
- traverse(dependency);
- m_sortedProducts << product;
- }
-
- void findCycle(ModuleLoader::ProductContext *product,
- QList<ModuleLoader::ProductContext *> &path)
- {
- if (path.contains(product)) {
- ErrorInfo error(Tr::tr("Cyclic dependencies detected."));
- for (const auto * const p : path)
- error.append(p->name, p->item->location());
- error.append(product->name, product->item->location());
- throw error;
- }
- path << product;
- for (auto const dep : m_dependencyMap.value(product))
- findCycle(dep, path);
- path.removeLast();
- }
-
- TopLevelProjectContext &m_tlp;
- QHash<ProductContext *, std::vector<ProductContext *>> m_dependencyMap;
- Set<ProductContext *> m_seenProducts;
- QList<ProductContext *> 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(Evaluator *evaluator, Logger &logger)
- : m_pool(nullptr)
- , m_logger(logger)
- , m_progressObserver(nullptr)
- , m_reader(new ItemReader(logger))
- , m_evaluator(evaluator)
-{
-}
-
-ModuleLoader::~ModuleLoader()
-{
- delete m_reader;
-}
-
-void ModuleLoader::setProgressObserver(ProgressObserver *progressObserver)
-{
- m_progressObserver = progressObserver;
-}
-
-void ModuleLoader::setSearchPaths(const QStringList &searchPaths)
-{
- m_reader->setSearchPaths(searchPaths);
- qCDebug(lcModuleLoader) << "initial search paths:" << searchPaths;
-}
-
-void ModuleLoader::setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes)
-{
- m_oldProjectProbes.clear();
- for (const ProbeConstPtr& probe : oldProbes)
- m_oldProjectProbes[probe->globalId()] << probe;
-}
-
-void ModuleLoader::setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes)
-{
- m_oldProductProbes = oldProbes;
-}
-
-void ModuleLoader::setStoredProfiles(const QVariantMap &profiles)
-{
- m_storedProfiles = profiles;
-}
-
-void ModuleLoader::setStoredModuleProviderInfo(const ModuleProviderInfoList &moduleProviderInfo)
-{
- m_moduleProviderInfo = moduleProviderInfo;
-}
-
-ModuleLoaderResult ModuleLoader::load(const SetupProjectParameters &parameters)
-{
- TimedActivityLogger moduleLoaderTimer(m_logger, Tr::tr("ModuleLoader"),
- parameters.logElapsedTime());
- qCDebug(lcModuleLoader) << "load" << parameters.projectFilePath();
- m_parameters = parameters;
- m_modulePrototypes.clear();
- m_modulePrototypeEnabledInfo.clear();
- m_parameterDeclarations.clear();
- m_disabledItems.clear();
- m_reader->clearExtraSearchPathsStack();
- m_reader->setEnableTiming(parameters.logElapsedTime());
- m_elapsedTimeProbes = m_elapsedTimePrepareProducts = m_elapsedTimeHandleProducts
- = m_elapsedTimeProductDependencies = m_elapsedTimeTransitiveDependencies
- = m_elapsedTimePropertyChecking = 0;
- m_elapsedTimeProbes = 0;
- m_probesEncountered = m_probesRun = m_probesCachedCurrent = m_probesCachedOld = 0;
- m_settings = std::make_unique<Settings>(parameters.settingsDirectory());
-
- const auto keys = m_parameters.overriddenValues().keys();
- for (const QString &key : keys) {
- static const QStringList prefixes({ StringConstants::projectPrefix(),
- QStringLiteral("projects"),
- QStringLiteral("products"), QStringLiteral("modules"),
- StringConstants::moduleProviders(),
- StringConstants::qbsModule()});
- bool ok = false;
- for (const auto &prefix : prefixes) {
- if (key.startsWith(prefix + QLatin1Char('.'))) {
- ok = true;
- break;
- }
- }
- if (ok) {
- collectNameFromOverride(key);
- continue;
- }
- ErrorInfo e(Tr::tr("Property override key '%1' not understood.").arg(key));
- e.append(Tr::tr("Please use one of the following:"));
- e.append(QLatin1Char('\t') + Tr::tr("projects.<project-name>.<property-name>:value"));
- e.append(QLatin1Char('\t') + Tr::tr("products.<product-name>.<property-name>:value"));
- e.append(QLatin1Char('\t') + Tr::tr("modules.<module-name>.<property-name>:value"));
- e.append(QLatin1Char('\t') + Tr::tr("products.<product-name>.<module-name>."
- "<property-name>:value"));
- e.append(QLatin1Char('\t') + Tr::tr("moduleProviders.<provider-name>."
- "<property-name>:value"));
- handlePropertyError(e, m_parameters, m_logger);
- }
-
- ModuleLoaderResult result;
- result.profileConfigs = m_storedProfiles;
- m_pool = result.itemPool.get();
- m_reader->setPool(m_pool);
-
- const QStringList topLevelSearchPaths = parameters.finalBuildConfigurationTree()
- .value(StringConstants::projectPrefix()).toMap()
- .value(StringConstants::qbsSearchPathsProperty()).toStringList();
- Item *root;
- {
- SearchPathsManager searchPathsManager(m_reader, topLevelSearchPaths);
- root = loadItemFromFile(parameters.projectFilePath(), CodeLocation());
- if (!root)
- return ModuleLoaderResult();
- }
-
- switch (root->type()) {
- case ItemType::Product:
- root = wrapInProjectIfNecessary(root);
- break;
- case ItemType::Project:
- break;
- default:
- throw ErrorInfo(Tr::tr("The top-level item must be of type 'Project' or 'Product', but it"
- " is of type '%1'.").arg(root->typeName()), root->location());
- }
-
- const QString buildDirectory = TopLevelProject::deriveBuildDirectory(parameters.buildRoot(),
- TopLevelProject::deriveId(parameters.finalBuildConfigurationTree()));
- root->setProperty(StringConstants::sourceDirectoryProperty(),
- VariantValue::create(QFileInfo(root->file()->filePath()).absolutePath()));
- root->setProperty(StringConstants::buildDirectoryProperty(),
- VariantValue::create(buildDirectory));
- root->setProperty(StringConstants::profileProperty(),
- VariantValue::create(m_parameters.topLevelProfile()));
- handleTopLevelProject(&result, root, buildDirectory,
- Set<QString>() << QDir::cleanPath(parameters.projectFilePath()));
- result.root = root;
- result.qbsFiles = m_reader->filesRead() - m_tempQbsFiles;
- for (auto it = m_localProfiles.cbegin(); it != m_localProfiles.cend(); ++it)
- result.profileConfigs.remove(it.key());
- printProfilingInfo();
- return result;
-}
-
-class PropertyDeclarationCheck : public ValueHandler
-{
- const Set<Item *> &m_disabledItems;
- Set<Item *> m_handledItems;
- std::vector<Item *> m_parentItems;
- Item *m_currentModuleInstance = nullptr;
- QualifiedId m_currentModuleName;
- QString m_currentName;
- SetupProjectParameters m_params;
- Logger &m_logger;
-public:
- PropertyDeclarationCheck(const Set<Item *> &disabledItems,
- const SetupProjectParameters &params, Logger &logger)
- : m_disabledItems(disabledItems)
- , m_params(params)
- , m_logger(logger)
- {
- }
-
- void operator()(Item *item)
- {
- handleItem(item);
- }
-
-private:
- void handle(JSSourceValue *value) override
- {
- 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) override
- {
- if (checkItemValue(value))
- handleItem(value->item());
- }
-
- bool checkItemValue(ItemValue *value)
- {
- // TODO: Remove once QBS-1030 is fixed.
- if (parentItem()->type() == ItemType::Artifact)
- return false;
-
- if (parentItem()->type() == ItemType::Properties)
- return false;
-
- if (parentItem()->isOfTypeOrhasParentOfType(ItemType::Export)) {
- // Export item prototypes do not have instantiated modules.
- // The module instances are where the Export is used.
- QBS_ASSERT(m_currentModuleInstance, return false);
- auto hasCurrentModuleName = [this](const Item::Module &m) {
- return m.name == m_currentModuleName;
- };
- if (any_of(m_currentModuleInstance->modules(), hasCurrentModuleName))
- return true;
- }
-
- // TODO: We really should have a dedicated item type for "pre-instantiated" item values
- // and only use ModuleInstance for actual module instances.
- const bool itemIsModuleInstance = value->item()->type() == ItemType::ModuleInstance
- && value->item()->hasProperty(StringConstants::presentProperty());
-
- if (!itemIsModuleInstance
- && value->item()->type() != ItemType::ModulePrefix
- && (!parentItem()->file() || !parentItem()->file()->idScope()
- || !parentItem()->file()->idScope()->hasProperty(m_currentName))
- && !value->createdByPropertiesBlock()) {
- CodeLocation location = value->location();
- for (int i = int(m_parentItems.size() - 1); !location.isValid() && i >= 0; --i)
- location = m_parentItems.at(i)->location();
- const ErrorInfo error(Tr::tr("Item '%1' is not declared. "
- "Did you forget to add a Depends item?")
- .arg(m_currentModuleName.toString()), location);
- handlePropertyError(error, m_params, m_logger);
- return false;
- }
-
- return true;
- }
-
- void handleItem(Item *item)
- {
- if (!m_handledItems.insert(item).second)
- return;
- if (m_disabledItems.contains(item)
- || (item->type() == ItemType::ModuleInstance && !item->isPresentModule())
- || item->type() == ItemType::Properties
-
- // The Properties child of a SubProject item is not a regular item.
- || item->type() == ItemType::PropertiesInSubProject) {
- return;
- }
-
- // If a module was found but its validate script failed, only the canonical
- // module instance will have the "non-present" flag set, so we need to locate it.
- if (item->type() == ItemType::ModuleInstance) {
- const Item *productItem = nullptr;
- for (auto it = m_parentItems.rbegin(); it != m_parentItems.rend(); ++it) {
- if ((*it)->type() == ItemType::Product) {
- productItem = *it;
- break;
- }
- }
- if (productItem) {
- for (const Item::Module &m : productItem->modules()) {
- if (m.name == m_currentModuleName) {
- if (!m.item->isPresentModule())
- return;
- break;
- }
- }
- }
- }
-
- m_parentItems.push_back(item);
- for (Item::PropertyMap::const_iterator it = item->properties().constBegin();
- it != item->properties().constEnd(); ++it) {
- if (item->type() == ItemType::Product && it.key() == StringConstants::moduleProviders()
- && it.value()->type() == Value::ItemValueType)
- continue;
- const PropertyDeclaration decl = item->propertyDeclaration(it.key());
- if (decl.isValid()) {
- if (!decl.isDeprecated())
- continue;
- const DeprecationInfo &di = decl.deprecationInfo();
- QString message;
- bool warningOnly;
- if (decl.isExpired()) {
- 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();
- const QualifiedId oldModuleName = m_currentModuleName;
- if (parentItem()->type() != ItemType::ModulePrefix)
- m_currentModuleName.clear();
- m_currentModuleName.push_back(m_currentName);
- it.value()->apply(this);
- m_currentModuleName = oldModuleName;
- }
- m_parentItems.pop_back();
- for (Item * const child : item->children()) {
- switch (child->type()) {
- case ItemType::Export:
- case ItemType::Depends:
- case ItemType::Parameter:
- case ItemType::Parameters:
- break;
- case ItemType::Group:
- if (item->type() == ItemType::Module || item->type() == ItemType::ModuleInstance)
- break;
- Q_FALLTHROUGH();
- default:
- 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()) {
- Item *oldInstance = m_currentModuleInstance;
- m_currentModuleInstance = item;
- handleItem(item->prototype());
- m_currentModuleInstance = oldInstance;
- }
- }
-
- void handle(VariantValue *) override { /* only created internally - no need to check */ }
-
- Item *parentItem() const { return m_parentItems.back(); }
-};
-
-void ModuleLoader::handleTopLevelProject(ModuleLoaderResult *loadResult, Item *projectItem,
- const QString &buildDirectory, const Set<QString> &referencedFilePaths)
-{
- TopLevelProjectContext tlp;
- tlp.buildDirectory = buildDirectory;
- handleProject(loadResult, &tlp, projectItem, referencedFilePaths);
- checkProjectNamesInOverrides(tlp);
- collectProductsByName(tlp);
- checkProductNamesInOverrides();
-
- adjustDependenciesForMultiplexing(tlp);
-
- m_dependencyResolvingPass = 1;
- for (ProjectContext * const projectContext : qAsConst(tlp.projects)) {
- m_reader->setExtraSearchPathsStack(projectContext->searchPathsStack);
- for (ProductContext &productContext : projectContext->products) {
- try {
- setupProductDependencies(&productContext, Set<DeferredDependsContext>());
- } catch (const ErrorInfo &err) {
- if (productContext.name.isEmpty())
- throw err;
- handleProductError(err, &productContext);
- }
- for (std::size_t i = 0; i < productContext.newlyAddedModuleProviderSearchPaths.size(); ++i)
- m_reader->popExtraSearchPaths();
- productContext.newlyAddedModuleProviderSearchPaths.clear();
- }
- }
- if (!m_productsWithDeferredDependsItems.empty() || !m_exportsWithDeferredDependsItems.empty()) {
- collectProductsByType(tlp);
- m_dependencyResolvingPass = 2;
-
- // Doing the normalization for the Export items themselves (as opposed to doing it only
- // for the corresponding module instances) serves two purposes:
- // (1) It makes recursive use of Depends.productTypes via Export items work; otherwise,
- // we'd need an additional dependency resolving pass for every export level.
- // (2) The "expanded" Depends items are available to the Exporter.qbs module.
- for (Item * const exportItem : m_exportsWithDeferredDependsItems)
- normalizeDependencies(nullptr, DeferredDependsContext(nullptr, exportItem));
-
- for (const auto &deferredDependsData : m_productsWithDeferredDependsItems) {
- ProductContext * const productContext = deferredDependsData.first;
- m_reader->setExtraSearchPathsStack(productContext->project->searchPathsStack);
- try {
- setupProductDependencies(productContext, deferredDependsData.second);
- } catch (const ErrorInfo &err) {
- handleProductError(err, productContext);
- }
- }
- }
-
- ProductSortByDependencies productSorter(tlp);
- productSorter.apply();
- for (ProductContext * const p : productSorter.sortedProducts()) {
- try {
- handleProduct(p);
- if (p->name.startsWith(shadowProductPrefix()))
- tlp.probes << p->info.probes;
- } catch (const ErrorInfo &err) {
- handleProductError(err, p);
- }
- }
-
- loadResult->projectProbes = tlp.probes;
- loadResult->moduleProviderInfo = m_moduleProviderInfo;
-
- m_reader->clearExtraSearchPathsStack();
- AccumulatingTimer timer(m_parameters.logElapsedTime()
- ? &m_elapsedTimePropertyChecking : nullptr);
- PropertyDeclarationCheck check(m_disabledItems, m_parameters, m_logger);
- check(projectItem);
-}
-
-void ModuleLoader::handleProject(ModuleLoaderResult *loadResult,
- TopLevelProjectContext *topLevelProjectContext, Item *projectItem,
- const Set<QString> &referencedFilePaths)
-{
- QScopedPointer<ProjectContext> p(new ProjectContext);
- auto &projectContext = *p;
- projectContext.topLevelProject = topLevelProjectContext;
- projectContext.result = loadResult;
- ItemValuePtr itemValue = ItemValue::create(projectItem);
- projectContext.scope = Item::create(m_pool, ItemType::Scope);
- projectContext.scope->setFile(projectItem->file());
- projectContext.scope->setProperty(StringConstants::projectVar(), itemValue);
- ProductContext dummyProductContext;
- dummyProductContext.project = &projectContext;
- dummyProductContext.moduleProperties = m_parameters.finalBuildConfigurationTree();
- projectItem->addModule(loadBaseModule(&dummyProductContext, projectItem));
- overrideItemProperties(projectItem, StringConstants::projectPrefix(),
- m_parameters.overriddenValuesTree());
- projectContext.name = m_evaluator->stringValue(projectItem,
- StringConstants::nameProperty());
- if (projectContext.name.isEmpty()) {
- projectContext.name = FileInfo::baseName(projectItem->location().filePath());
- projectItem->setProperty(StringConstants::nameProperty(),
- VariantValue::create(projectContext.name));
- }
- overrideItemProperties(projectItem,
- StringConstants::projectsOverridePrefix() + projectContext.name,
- m_parameters.overriddenValuesTree());
- if (!checkItemCondition(projectItem)) {
- m_disabledProjects.insert(projectContext.name);
- return;
- }
- p.take();
- topLevelProjectContext->projects.push_back(&projectContext);
- m_reader->pushExtraSearchPaths(readExtraSearchPaths(projectItem)
- << projectItem->file()->dirPath());
- projectContext.searchPathsStack = m_reader->extraSearchPathsStack();
- projectContext.item = projectItem;
-
- const QString minVersionStr
- = m_evaluator->stringValue(projectItem, StringConstants::minimumQbsVersionProperty(),
- QStringLiteral("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()));
- }
-
- resolveProbes(&dummyProductContext, projectItem);
- projectContext.topLevelProject->probes << dummyProductContext.info.probes;
-
- handleProfileItems(projectItem, &projectContext);
-
- QList<Item *> multiplexedProducts;
- for (Item * const child : projectItem->children()) {
- child->setScope(projectContext.scope);
- if (child->type() == ItemType::Product)
- multiplexedProducts << multiplexProductItem(&dummyProductContext, child);
- }
- for (Item * const additionalProductItem : qAsConst(multiplexedProducts))
- Item::addChild(projectItem, additionalProductItem);
-
- const QList<Item *> originalChildren = projectItem->children();
- for (Item * const child : originalChildren) {
- 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, StringConstants::referencesProperty());
- const CodeLocation referencingLocation
- = projectItem->property(StringConstants::referencesProperty())->location();
- QList<Item *> additionalProjectChildren;
- for (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);
- }
- }
- for (Item * const subItem : qAsConst(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,
- Set<QString>(referencedFilePaths) << subItem->file()->filePath());
- break;
- default:
- break;
- }
- }
- m_reader->popExtraSearchPaths();
-}
-
-QString ModuleLoader::MultiplexInfo::toIdString(size_t row) const
-{
- const auto &mprow = table.at(row);
- QVariantMap multiplexConfiguration;
- for (size_t column = 0; column < mprow.size(); ++column) {
- const QString &propertyName = properties.at(column);
- const VariantValuePtr &mpvalue = mprow.at(column);
- multiplexConfiguration.insert(propertyName, mpvalue->value());
- }
- return QString::fromUtf8(QJsonDocument::fromVariant(multiplexConfiguration)
- .toJson(QJsonDocument::Compact)
- .toBase64());
-}
-
-void qbs::Internal::ModuleLoader::ModuleLoader::dump(const ModuleLoader::MultiplexInfo &mpi)
-{
- QStringList header;
- for (const auto &str : mpi.properties)
- header << str;
- qDebug() << header;
-
- for (const auto &row : mpi.table) {
- QVariantList values;
- for (const auto &elem : row) {
- values << elem->value();
- }
- qDebug() << values;
- }
-}
-
-ModuleLoader::MultiplexTable ModuleLoader::combine(const MultiplexTable &table,
- const MultiplexRow &values)
-{
- MultiplexTable result;
- if (table.empty()) {
- result.resize(values.size());
- for (size_t i = 0; i < values.size(); ++i) {
- MultiplexRow row;
- row.resize(1);
- row[0] = values.at(i);
- result[i] = row;
- }
- } else {
- for (const auto &row : table) {
- for (const auto &value : values) {
- MultiplexRow newRow = row;
- newRow.push_back(value);
- result.push_back(newRow);
- }
- }
- }
- return result;
-}
-
-ModuleLoader::MultiplexInfo ModuleLoader::extractMultiplexInfo(Item *productItem,
- Item *qbsModuleItem)
-{
- static const QString mpmKey = QStringLiteral("multiplexMap");
-
- const QScriptValue multiplexMap = m_evaluator->value(qbsModuleItem, mpmKey);
- QStringList multiplexByQbsProperties = m_evaluator->stringListValue(
- productItem, StringConstants::multiplexByQbsPropertiesProperty());
-
- MultiplexInfo multiplexInfo;
- multiplexInfo.aggregate = m_evaluator->boolValue(
- productItem, StringConstants::aggregateProperty());
-
- const QString multiplexedType = m_evaluator->stringValue(
- productItem, StringConstants::multiplexedTypeProperty());
- if (!multiplexedType.isEmpty())
- multiplexInfo.multiplexedType = VariantValue::create(multiplexedType);
-
- Set<QString> uniqueMultiplexByQbsProperties;
- for (const QString &key : multiplexByQbsProperties) {
- const QString mappedKey = multiplexMap.property(key).toString();
- if (mappedKey.isEmpty())
- throw ErrorInfo(Tr::tr("There is no entry for '%1' in 'qbs.multiplexMap'.").arg(key));
-
- if (!uniqueMultiplexByQbsProperties.insert(mappedKey).second) {
- throw ErrorInfo(Tr::tr("Duplicate entry '%1' in Product.%2.")
- .arg(mappedKey, StringConstants::multiplexByQbsPropertiesProperty()),
- productItem->location());
- }
-
- const QScriptValue arr = m_evaluator->value(qbsModuleItem, key);
- if (arr.isUndefined())
- continue;
- if (!arr.isArray())
- throw ErrorInfo(Tr::tr("Property '%1' must be an array.").arg(key));
-
- const quint32 arrlen = arr.property(StringConstants::lengthProperty()).toUInt32();
- if (arrlen == 0)
- continue;
-
- MultiplexRow mprow;
- mprow.resize(arrlen);
- Set<QVariant> entriesForKey;
- for (quint32 i = 0; i < arrlen; ++i) {
- const QVariant value = arr.property(i).toVariant();
- if (!entriesForKey.insert(value).second) {
- throw ErrorInfo(Tr::tr("Duplicate entry '%1' in qbs.%2.")
- .arg(value.toString(), key), productItem->location());
- }
- mprow[i] = VariantValue::create(value);
- }
- multiplexInfo.table = combine(multiplexInfo.table, mprow);
- multiplexInfo.properties.push_back(mappedKey);
- }
- return multiplexInfo;
-}
-
-template <typename T, typename F>
-T ModuleLoader::callWithTemporaryBaseModule(ProductContext *productContext, const F &func)
-{
- // Temporarily attach the qbs module here, in case we need to access one of its properties
- // to evaluate properties.
- const QString &qbsKey = StringConstants::qbsModule();
- Item *productItem = productContext->item;
- ValuePtr qbsValue = productItem->property(qbsKey); // Retrieve now to restore later.
- if (qbsValue)
- qbsValue = qbsValue->clone();
- const Item::Module qbsModule = loadBaseModule(productContext, productItem);
- productItem->addModule(qbsModule);
-
- auto &&result = func(qbsModule);
-
- // "Unload" the qbs module again.
- if (qbsValue)
- productItem->setProperty(qbsKey, qbsValue);
- else
- productItem->removeProperty(qbsKey);
- productItem->removeModules();
-
- return std::forward<T>(result);
-}
-
-QList<Item *> ModuleLoader::multiplexProductItem(ProductContext *dummyContext, Item *productItem)
-{
- QString productName;
- dummyContext->item = productItem;
- auto extractMultiplexInfoFromProduct
- = [this, productItem, &productName](const Item::Module &qbsModule) {
- // Overriding the product item properties must be done here already, because multiplexing
- // properties might depend on product properties.
- const QString &nameKey = StringConstants::nameProperty();
- productName = m_evaluator->stringValue(productItem, nameKey);
- if (productName.isEmpty()) {
- productName = FileInfo::completeBaseName(productItem->file()->filePath());
- productItem->setProperty(nameKey, VariantValue::create(productName));
- }
- overrideItemProperties(productItem, StringConstants::productsOverridePrefix() + productName,
- m_parameters.overriddenValuesTree());
-
- return extractMultiplexInfo(productItem, qbsModule.item);
- };
- const MultiplexInfo multiplexInfo
- = callWithTemporaryBaseModule<const MultiplexInfo>(dummyContext,
- extractMultiplexInfoFromProduct);
-
- if (multiplexInfo.table.size() > 1)
- productItem->setProperty(StringConstants::multiplexedProperty(), VariantValue::trueValue());
-
- VariantValuePtr productNameValue = VariantValue::create(productName);
-
- Item *aggregator = multiplexInfo.aggregate ? productItem->clone() : nullptr;
- QList<Item *> additionalProductItems;
- std::vector<VariantValuePtr> multiplexConfigurationIdValues;
- for (size_t row = 0; row < multiplexInfo.table.size(); ++row) {
- Item *item = productItem;
- const auto &mprow = multiplexInfo.table.at(row);
- QBS_CHECK(mprow.size() == multiplexInfo.properties.size());
- if (row > 0) {
- item = productItem->clone();
- additionalProductItems.push_back(item);
- }
- const QString multiplexConfigurationId = multiplexInfo.toIdString(row);
- const VariantValuePtr multiplexConfigurationIdValue
- = VariantValue::create(multiplexConfigurationId);
- if (multiplexInfo.table.size() > 1 || aggregator) {
- multiplexConfigurationIdValues.push_back(multiplexConfigurationIdValue);
- item->setProperty(StringConstants::multiplexConfigurationIdProperty(),
- multiplexConfigurationIdValue);
- }
- if (multiplexInfo.multiplexedType)
- item->setProperty(StringConstants::typeProperty(), multiplexInfo.multiplexedType);
- for (size_t column = 0; column < mprow.size(); ++column) {
- Item *qbsItem = moduleInstanceItem(item, StringConstants::qbsModule());
- const QString &propertyName = multiplexInfo.properties.at(column);
- const VariantValuePtr &mpvalue = mprow.at(column);
- qbsItem->setProperty(propertyName, mpvalue);
- }
- }
-
- if (aggregator) {
- additionalProductItems << aggregator;
-
- // Add dependencies to all multiplexed instances.
- for (const auto &v : multiplexConfigurationIdValues) {
- Item *dependsItem = Item::create(aggregator->pool(), ItemType::Depends);
- dependsItem->setProperty(StringConstants::nameProperty(), productNameValue);
- dependsItem->setProperty(StringConstants::multiplexConfigurationIdProperty(), v);
- dependsItem->setProperty(StringConstants::profilesProperty(),
- VariantValue::create(QStringList()));
- Item::addChild(aggregator, dependsItem);
- }
- }
-
- return additionalProductItems;
-}
-
-void ModuleLoader::normalizeDependencies(ProductContext *product,
- const DeferredDependsContext &dependsContext)
-{
- std::vector<Item *> dependsItemsToAdd;
- std::vector<Item *> dependsItemsToRemove;
- std::vector<Item *> deferredDependsItems;
- for (Item *dependsItem : dependsContext.parentItem->children()) {
- if (dependsItem->type() != ItemType::Depends)
- continue;
- bool productTypesIsSet;
- const FileTags productTypes = m_evaluator->fileTagsValue(dependsItem,
- StringConstants::productTypesProperty(), &productTypesIsSet);
- if (productTypesIsSet) {
- bool nameIsSet;
- m_evaluator->stringValue(dependsItem, StringConstants::nameProperty(), QString(),
- &nameIsSet);
-
- // The second condition is for the case where the dependency comes from an Export item
- // that has itself been normalized in the mean time.
- if (nameIsSet && !dependsItem->variantProperty(StringConstants::nameProperty())) {
- throw ErrorInfo(Tr::tr("The 'productTypes' and 'name' properties are mutually "
- "exclusive."), dependsItem->location());
- }
-
- bool submodulesPropertySet;
- m_evaluator->stringListValue( dependsItem, StringConstants::submodulesProperty(),
- &submodulesPropertySet);
- if (submodulesPropertySet) {
- throw ErrorInfo(Tr::tr("The 'productTypes' and 'subModules' properties are "
- "mutually exclusive."), dependsItem->location());
- }
-
- // We ignore the "limitToSubProject" property for dependencies from Export items,
- // because we cannot make it work consistently, as the importing product is not
- // yet known when normalizing via an Export item.
- const bool limitToSubProject = dependsContext.parentItem->type() == ItemType::Product
- && m_evaluator->boolValue(dependsItem,
- StringConstants::limitToSubProjectProperty());
- static const auto hasSameSubProject
- = [](const ProductContext &product, const ProductContext &other) {
- for (const Item *otherParent = other.item->parent(); otherParent;
- otherParent = otherParent->parent()) {
- if (otherParent == product.item->parent())
- return true;
- }
- return false;
- };
- std::vector<const ProductContext *> matchingProducts;
- for (const FileTag &typeTag : productTypes) {
- const auto range = m_productsByType.equal_range(typeTag);
- for (auto it = range.first; it != range.second; ++it) {
- if (it->second != product
- && (!product || it->second->name != product->name)
- && (!limitToSubProject || hasSameSubProject(*product, *it->second))) {
- matchingProducts.push_back(it->second);
- }
- }
- }
- if (matchingProducts.empty()) {
- qCDebug(lcModuleLoader) << "Depends.productTypes does not match anything."
- << dependsItem->location();
- dependsItemsToRemove.push_back(dependsItem);
- continue;
- }
- if (dependsContext.parentItem->type() != ItemType::Export)
- deferredDependsItems.push_back(dependsItem);
- for (std::size_t i = 1; i < matchingProducts.size(); ++i) {
- Item * const dependsClone = dependsItem->clone();
- dependsClone->setProperty(StringConstants::nameProperty(),
- VariantValue::create(matchingProducts.at(i)->name));
- dependsItemsToAdd.push_back(dependsClone);
- if (dependsContext.parentItem->type() != ItemType::Export)
- deferredDependsItems.push_back(dependsClone);
-
- }
- dependsItem->setProperty(StringConstants::nameProperty(),
- VariantValue::create(matchingProducts.front()->name));
- }
- }
- for (Item * const newDependsItem : dependsItemsToAdd)
- Item::addChild(dependsContext.parentItem, newDependsItem);
- for (Item * const dependsItem : dependsItemsToRemove)
- Item::removeChild(dependsContext.parentItem, dependsItem);
- if (!deferredDependsItems.empty()) {
- auto &allDeferredDependsItems
- = product->deferredDependsItems[dependsContext.exportingProductItem];
- allDeferredDependsItems.insert(allDeferredDependsItems.end(), deferredDependsItems.cbegin(),
- deferredDependsItems.cend());
- }
-}
-
-void ModuleLoader::adjustDependenciesForMultiplexing(const TopLevelProjectContext &tlp)
-{
- for (const ProjectContext * const project : tlp.projects) {
- for (const ProductContext &product : project->products)
- adjustDependenciesForMultiplexing(product);
- }
-}
-
-void ModuleLoader::adjustDependenciesForMultiplexing(const ModuleLoader::ProductContext &product)
-{
- for (Item *dependsItem : product.item->children()) {
- if (dependsItem->type() == ItemType::Depends)
- adjustDependenciesForMultiplexing(product, dependsItem);
- }
-}
-
-void ModuleLoader::adjustDependenciesForMultiplexing(const ProductContext &product,
- Item *dependsItem)
-{
- const QString name = m_evaluator->stringValue(dependsItem, StringConstants::nameProperty());
- const bool productIsMultiplexed = !product.multiplexConfigurationId.isEmpty();
- if (name == product.name) {
- QBS_CHECK(!productIsMultiplexed); // This product must be an aggregator.
- return;
- }
-
- bool profilesPropertyIsSet;
- const QStringList profiles = m_evaluator->stringListValue(dependsItem,
- StringConstants::profilesProperty(), &profilesPropertyIsSet);
-
- const auto productRange = m_productsByName.equal_range(name);
- std::vector<const ProductContext *> dependencies;
- bool hasNonMultiplexedDependency = false;
- for (auto it = productRange.first; it != productRange.second; ++it) {
- if (!it->second->multiplexConfigurationId.isEmpty()) {
- dependencies.push_back(it->second);
- if (productIsMultiplexed && !profilesPropertyIsSet)
- break;
- } else {
- hasNonMultiplexedDependency = true;
- break;
- }
- }
-
- // These are the allowed cases:
- // (1) Normal dependency with no multiplexing whatsoever.
- // (2) Both product and dependency are multiplexed.
- // (3) The product is not multiplexed, but the dependency is.
- // (3a) The dependency has an aggregator. We want to depend on the aggregator.
- // (3b) The dependency does not have an aggregator. We want to depend on all the
- // multiplexed variants.
- // (4) The product is multiplexed, but the dependency is not. This case is implicitly
- // handled, because we don't have to adapt any Depends items.
- // (5) The product is a "shadow product". In that case, we know which product
- // it should have a dependency on, and we make sure we depend on that.
-
- // (1) and (3a)
- if (!productIsMultiplexed && hasNonMultiplexedDependency)
- return;
-
- QStringList multiplexIds;
- const ShadowProductInfo shadowProductInfo = getShadowProductInfo(product);
- const bool isShadowProduct = shadowProductInfo.first && shadowProductInfo.second == name;
- for (const ProductContext *dependency : dependencies) {
- const bool depMatchesShadowProduct = isShadowProduct
- && dependency->item == product.item->parent();
- const QString depMultiplexId = dependency->multiplexConfigurationId;
- if (depMatchesShadowProduct) { // (5)
- dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
- VariantValue::create(depMultiplexId));
- multiplexIds.clear();
- break;
- }
- if (productIsMultiplexed && !profilesPropertyIsSet) { // (2)
- const ValuePtr &multiplexId = product.item->property(
- StringConstants::multiplexConfigurationIdProperty());
- dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
- multiplexId);
- break;
- }
-
- // (3b) (or (2) if Depends.profiles is set).
- const bool profileMatch = !profilesPropertyIsSet || profiles.empty()
- || profiles.contains(dependency->profileName);
- if (profileMatch)
- multiplexIds << depMultiplexId;
- }
- if (!multiplexIds.empty()) {
- dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
- VariantValue::create(multiplexIds));
- }
-}
-
-void ModuleLoader::prepareProduct(ProjectContext *projectContext, Item *productItem)
-{
- AccumulatingTimer timer(m_parameters.logElapsedTime()
- ? &m_elapsedTimePrepareProducts : nullptr);
- checkCancelation();
- qCDebug(lcModuleLoader) << "prepareProduct" << productItem->file()->filePath();
-
- ProductContext productContext;
- productContext.item = productItem;
- productContext.project = projectContext;
- productContext.name = m_evaluator->stringValue(productItem, StringConstants::nameProperty());
- QBS_CHECK(!productContext.name.isEmpty());
- const ItemValueConstPtr qbsItemValue = productItem->itemProperty(StringConstants::qbsModule());
- if (!!qbsItemValue && qbsItemValue->item()->hasProperty(StringConstants::profileProperty())) {
- qbsItemValue->item()->setProperty(StringConstants::nameProperty(),
- VariantValue::create(StringConstants::nameProperty()));
- auto evaluateQbsProfileProperty = [this](const Item::Module &qbsModule) {
- return m_evaluator->stringValue(qbsModule.item,
- StringConstants::profileProperty(), QString());
- };
- productContext.profileName
- = callWithTemporaryBaseModule<QString>(&productContext,
- evaluateQbsProfileProperty);
- } else {
- productContext.profileName = m_parameters.topLevelProfile();
- }
- productContext.multiplexConfigurationId = m_evaluator->stringValue(
- productItem, StringConstants::multiplexConfigurationIdProperty());
- QBS_CHECK(!productContext.profileName.isEmpty());
- const auto it = projectContext->result->profileConfigs.constFind(productContext.profileName);
- if (it == projectContext->result->profileConfigs.constEnd()) {
- const Profile profile(productContext.profileName, m_settings.get(), m_localProfiles);
- if (!profile.exists()) {
- ErrorInfo error(Tr::tr("Profile '%1' does not exist.").arg(profile.name()),
- productItem->location());
- handleProductError(error, &productContext);
- return;
- }
- const QVariantMap buildConfig = SetupProjectParameters::expandedBuildConfiguration(
- profile, m_parameters.configurationName());
- productContext.moduleProperties = SetupProjectParameters::finalBuildConfigurationTree(
- buildConfig, m_parameters.overriddenValues());
- projectContext->result->profileConfigs.insert(productContext.profileName,
- productContext.moduleProperties);
- } else {
- productContext.moduleProperties = it.value().toMap();
- }
- initProductProperties(productContext);
-
- ItemValuePtr itemValue = ItemValue::create(productItem);
- productContext.scope = Item::create(m_pool, ItemType::Scope);
- productContext.scope->setProperty(StringConstants::productVar(), itemValue);
- productContext.scope->setFile(productItem->file());
- productContext.scope->setScope(productContext.project->scope);
-
- const bool hasExportItems = mergeExportItems(productContext);
-
- setScopeForDescendants(productItem, productContext.scope);
-
- projectContext->products.push_back(productContext);
-
- if (!hasExportItems || getShadowProductInfo(productContext).first)
- return;
-
- // This "shadow product" exists only to pull in a dependency on the actual product
- // and nothing else, thus providing us with the pure environment that we need to
- // evaluate the product's exported properties in isolation in the project resolver.
- Item * const importer = Item::create(productItem->pool(), ItemType::Product);
- importer->setProperty(QStringLiteral("name"),
- VariantValue::create(shadowProductPrefix() + productContext.name));
- importer->setFile(productItem->file());
- importer->setLocation(productItem->location());
- importer->setScope(projectContext->scope);
- importer->setupForBuiltinType(m_logger);
- Item * const dependsItem = Item::create(productItem->pool(), ItemType::Depends);
- dependsItem->setProperty(QStringLiteral("name"), VariantValue::create(productContext.name));
- dependsItem->setProperty(QStringLiteral("required"), VariantValue::create(false));
- dependsItem->setFile(importer->file());
- dependsItem->setLocation(importer->location());
- dependsItem->setupForBuiltinType(m_logger);
- Item::addChild(importer, dependsItem);
- Item::addChild(productItem, importer);
- prepareProduct(projectContext, importer);
-}
-
-void ModuleLoader::setupProductDependencies(ProductContext *productContext,
- const Set<DeferredDependsContext> &deferredDependsContext)
-{
- if (m_dependencyResolvingPass == 2) {
- for (const DeferredDependsContext &ctx : deferredDependsContext)
- normalizeDependencies(productContext, ctx);
- for (const auto &deferralData : productContext->deferredDependsItems) {
- for (Item * const deferredDependsItem : deferralData.second) {
-
- // Dependencies from Export items are handled in addProductModuleDependencies().
- if (deferredDependsItem->parent() == productContext->item)
- adjustDependenciesForMultiplexing(*productContext, deferredDependsItem);
- }
- }
- }
- AccumulatingTimer timer(m_parameters.logElapsedTime()
- ? &m_elapsedTimeProductDependencies : nullptr);
- checkCancelation();
- Item *item = productContext->item;
- qCDebug(lcModuleLoader) << "setupProductDependencies" << productContext->name
- << productContext->item->location();
-
- if (m_dependencyResolvingPass == 1)
- setSearchPathsForProduct(productContext);
- SearchPathsManager searchPathsManager(m_reader, productContext->searchPaths);
-
- DependsContext dependsContext;
- dependsContext.product = productContext;
- dependsContext.productDependencies = &productContext->info.usedProducts;
- resolveDependencies(&dependsContext, item, productContext);
- if (m_dependencyResolvingPass == 2
- || !containsKey(m_productsWithDeferredDependsItems, productContext)) {
- addProductModuleDependencies(productContext);
- }
- productContext->project->result->productInfos.insert(item, productContext->info);
-}
-
-// Leaf modules first.
-// TODO: Can this be merged with addTransitiveDependencies? Looks suspiciously similar.
-void ModuleLoader::createSortedModuleList(const Item::Module &parentModule, Item::Modules &modules)
-{
- if (std::find_if(modules.cbegin(), modules.cend(),
- [parentModule](const Item::Module &m) { return m.name == parentModule.name;})
- != modules.cend()) {
- return;
- }
- for (const Item::Module &dep : parentModule.item->modules())
- createSortedModuleList(dep, modules);
- modules.push_back(parentModule);
- return;
-}
-
-Item::Modules ModuleLoader::modulesSortedByDependency(const Item *productItem)
-{
- QBS_CHECK(productItem->type() == ItemType::Product);
- Item::Modules sortedModules;
- const Item::Modules &unsortedModules = productItem->modules();
- for (const Item::Module &module : unsortedModules)
- createSortedModuleList(module, sortedModules);
- QBS_CHECK(sortedModules.size() == unsortedModules.size());
-
- // Make sure the top-level items stay the same.
- for (Item::Module &s : sortedModules) {
- for (const Item::Module &u : unsortedModules) {
- if (s.name == u.name) {
- s.item = u.item;
- break;
- }
- }
- }
- return sortedModules;
-}
-
-
-template<typename T> bool insertIntoSet(Set<T> &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;
- for (const Item::Module &m : module.item->modules()) {
- deps[m.name].insert(module.name);
- setupReverseModuleDependencies(m, deps, seenModules);
- }
-}
-
-ModuleLoader::ModuleDependencies ModuleLoader::setupReverseModuleDependencies(const Item *product)
-{
- ModuleDependencies deps;
- QualifiedIdSet seenModules;
- for (const Item::Module &m : product->modules())
- setupReverseModuleDependencies(m, deps, seenModules);
- return deps;
-}
-
-void ModuleLoader::handleProduct(ModuleLoader::ProductContext *productContext)
-{
- AccumulatingTimer timer(m_parameters.logElapsedTime() ? &m_elapsedTimeHandleProducts : nullptr);
- if (productContext->info.delayedError.hasError())
- return;
-
- Item * const item = productContext->item;
-
- m_reader->setExtraSearchPathsStack(productContext->project->searchPathsStack);
- SearchPathsManager searchPathsManager(m_reader, productContext->searchPaths);
- addTransitiveDependencies(productContext);
-
- // It is important that dependent modules are merged after their dependency, because
- // the dependent module's merger potentially needs to replace module items that were
- // set by the dependency module's merger (namely, scopes of defining items; see
- // ModuleMerger::replaceItemInScopes()).
- Item::Modules topSortedModules = modulesSortedByDependency(item);
- for (Item::Module &module : topSortedModules)
- ModuleMerger(m_logger, item, module).start();
-
- // Re-sort the modules by name. This is more stable; see QBS-818.
- // The list of modules in the product now has the same order as before,
- // only the items have been replaced by their merged counterparts.
- Item::Modules lexicographicallySortedModules = topSortedModules;
- std::sort(lexicographicallySortedModules.begin(), lexicographicallySortedModules.end());
- item->setModules(lexicographicallySortedModules);
-
- for (const Item::Module &module : topSortedModules) {
- 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,
- StringConstants::versionProperty()));
- 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);
- if (productContext->info.delayedError.hasError())
- return;
- }
- }
-
- resolveProbes(productContext, item);
-
- // Module validation must happen in an extra pass, after all Probes have been resolved.
- EvalCacheEnabler cacheEnabler(m_evaluator);
- for (const Item::Module &module : topSortedModules) {
- if (!module.item->isPresentModule())
- continue;
- try {
- m_evaluator->boolValue(module.item, StringConstants::validateProperty());
- } catch (const ErrorInfo &error) {
- handleModuleSetupError(productContext, module, error);
- if (productContext->info.delayedError.hasError())
- return;
- }
- }
-
- if (!checkItemCondition(item)) {
- const auto &exportsData = productContext->project->topLevelProject->productModules;
- for (auto it = exportsData.find(productContext->name);
- it != exportsData.end() && it.key() == productContext->name; ++it) {
- if (it.value().multiplexId == productContext->multiplexConfigurationId) {
- createNonPresentModule(productContext->name, QStringLiteral("disabled"),
- it.value().exportItem);
- break;
- }
- }
- }
-
- checkDependencyParameterDeclarations(productContext);
- copyGroupsFromModulesToProduct(*productContext);
-
- ModuleDependencies reverseModuleDeps;
- for (Item * const child : item->children()) {
- if (child->type() == ItemType::Group) {
- if (reverseModuleDeps.empty())
- reverseModuleDeps = setupReverseModuleDependencies(item);
- handleGroup(productContext, child, reverseModuleDeps);
- }
- }
- productContext->project->result->productInfos.insert(item, productContext->info);
-}
-
-static Item *rootPrototype(Item *item)
-{
- Item *modulePrototype = item;
- while (modulePrototype->prototype())
- modulePrototype = modulePrototype->prototype();
- return modulePrototype;
-}
-
-class DependencyParameterDeclarationCheck
-{
-public:
- DependencyParameterDeclarationCheck(const QString &productName, const Item *productItem,
- const QHash<const Item *, Item::PropertyDeclarationMap> &decls)
- : m_productName(productName), m_productItem(productItem), m_parameterDeclarations(decls)
- {
- }
-
- void operator()(const QVariantMap &parameters) const
- {
- check(parameters, QualifiedId());
- }
-
-private:
- void check(const QVariantMap &parameters, const QualifiedId &moduleName) const
- {
- for (auto it = parameters.begin(); it != parameters.end(); ++it) {
- if (it.value().type() == QVariant::Map) {
- check(it.value().toMap(), QualifiedId(moduleName) << it.key());
- } else {
- const auto &deps = m_productItem->modules();
- auto m = std::find_if(deps.begin(), deps.end(),
- [&moduleName] (const Item::Module &module) {
- return module.name == moduleName;
- });
-
- if (m == deps.end()) {
- const QualifiedId fullName = QualifiedId(moduleName) << it.key();
- throw ErrorInfo(Tr::tr("Cannot set parameter '%1', "
- "because '%2' does not have a dependency on '%3'.")
- .arg(fullName.toString(), m_productName, moduleName.toString()),
- m_productItem->location());
- }
-
- auto decls = m_parameterDeclarations.value(rootPrototype(m->item));
-
- if (!decls.contains(it.key())) {
- const QualifiedId fullName = QualifiedId(moduleName) << it.key();
- throw ErrorInfo(Tr::tr("Parameter '%1' is not declared.")
- .arg(fullName.toString()), m_productItem->location());
- }
- }
- }
- }
-
- bool moduleExists(const QualifiedId &name) const
- {
- const auto &deps = m_productItem->modules();
- return any_of(deps, [&name](const Item::Module &module) {
- return module.name == name;
- });
- }
-
- const QString &m_productName;
- const Item *m_productItem;
- const QHash<const Item *, Item::PropertyDeclarationMap> &m_parameterDeclarations;
-};
-
-void ModuleLoader::checkDependencyParameterDeclarations(const ProductContext *productContext) const
-{
- DependencyParameterDeclarationCheck dpdc(productContext->name, productContext->item,
- m_parameterDeclarations);
- for (const Item::Module &dep : productContext->item->modules()) {
- if (!dep.parameters.empty())
- dpdc(dep.parameters);
- }
-}
-
-void ModuleLoader::handleModuleSetupError(ModuleLoader::ProductContext *productContext,
- const Item::Module &module, const ErrorInfo &error)
-{
- if (module.required) {
- handleProductError(error, productContext);
- } else {
- qCDebug(lcModuleLoader()) << "non-required module" << module.name.toString()
- << "found, but not usable in product" << productContext->name
- << error.toString();
- createNonPresentModule(module.name.toString(), QStringLiteral("failed validation"),
- module.item);
- }
-}
-
-void ModuleLoader::initProductProperties(const ProductContext &product)
-{
- QString buildDir = ResolvedProduct::deriveBuildDirectoryName(product.name,
- product.multiplexConfigurationId);
- buildDir = FileInfo::resolvePath(product.project->topLevelProject->buildDirectory, buildDir);
- product.item->setProperty(StringConstants::buildDirectoryProperty(),
- VariantValue::create(buildDir));
- const QString sourceDir = QFileInfo(product.item->file()->filePath()).absolutePath();
- product.item->setProperty(StringConstants::sourceDirectoryProperty(),
- VariantValue::create(sourceDir));
-}
-
-void ModuleLoader::handleSubProject(ModuleLoader::ProjectContext *projectContext, Item *projectItem,
- const Set<QString> &referencedFilePaths)
-{
- qCDebug(lcModuleLoader) << "handleSubProject" << projectItem->file()->filePath();
-
- Item * const propertiesItem = projectItem->child(ItemType::PropertiesInSubProject);
- if (!checkItemCondition(projectItem))
- return;
- if (propertiesItem) {
- propertiesItem->setScope(projectItem);
- if (!checkItemCondition(propertiesItem))
- return;
- }
-
- Item *loadedItem;
- QString subProjectFilePath;
- try {
- const QString projectFileDirPath = FileInfo::path(projectItem->file()->filePath());
- const QString relativeFilePath
- = m_evaluator->stringValue(projectItem, StringConstants::filePathProperty());
- 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 = loadItemFromFile(subProjectFilePath, projectItem->location());
- } 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, StringConstants::inheritPropertiesProperty());
-
- 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,
- Set<QString>(referencedFilePaths) << subProjectFilePath);
-}
-
-QList<Item *> ModuleLoader::loadReferencedFile(const QString &relativePath,
- const CodeLocation &referencingLocation,
- const Set<QString> &referencedFilePaths,
- ModuleLoader::ProductContext &dummyContext)
-{
- QString absReferencePath = FileInfo::resolvePath(FileInfo::path(referencingLocation.filePath()),
- relativePath);
- if (FileInfo(absReferencePath).isDir()) {
- QString qbsFilePath;
-
- QDirIterator dit(absReferencePath, StringConstants::qbsFileWildcards());
- 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 = loadItemFromFile(absReferencePath, referencingLocation);
- 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<Item *> loadedItems;
- loadedItems << subItem;
- if (subItem->type() == ItemType::Product) {
- handleProfileItems(subItem, dummyContext.project);
- loadedItems << multiplexProductItem(&dummyContext, subItem);
- }
- return loadedItems;
-}
-
-void ModuleLoader::handleGroup(ProductContext *productContext, Item *groupItem,
- const ModuleDependencies &reverseDepencencies)
-{
- checkCancelation();
- propagateModulesFromParent(productContext, groupItem, reverseDepencencies);
- checkItemCondition(groupItem);
- for (Item * const child : groupItem->children()) {
- if (child->type() == ItemType::Group)
- handleGroup(productContext, child, reverseDepencencies);
- }
-}
-
-void ModuleLoader::handleAllPropertyOptionsItems(Item *item)
-{
- QList<Item *> childItems = item->children();
- auto childIt = childItems.begin();
- while (childIt != childItems.end()) {
- Item * const child = *childIt;
- if (child->type() == ItemType::PropertyOptions) {
- handlePropertyOptions(child);
- childIt = childItems.erase(childIt);
- } else {
- handleAllPropertyOptionsItems(child);
- ++childIt;
- }
- }
- item->setChildren(childItems);
-}
-
-void ModuleLoader::handlePropertyOptions(Item *optionsItem)
-{
- const QString name = m_evaluator->stringValue(optionsItem, StringConstants::nameProperty());
- if (name.isEmpty()) {
- throw ErrorInfo(Tr::tr("PropertyOptions item needs a name property"),
- optionsItem->location());
- }
- const QString description = m_evaluator->stringValue(
- optionsItem, StringConstants::descriptionProperty());
- const auto removalVersion = Version::fromString(m_evaluator->stringValue(optionsItem,
- StringConstants::removalVersionProperty()));
- 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);
- }
- const ValuePtr property = optionsItem->parent()->property(name);
- if (!property && !decl.isExpired()) {
- throw ErrorInfo(Tr::tr("PropertyOptions item refers to non-existing property '%1'")
- .arg(name), optionsItem->location());
- }
- if (property && decl.isExpired()) {
- ErrorInfo e(Tr::tr("Property '%1' was scheduled for removal in version %2, but "
- "is still present.")
- .arg(name).arg(removalVersion.toString()),
- property->location());
- e.append(Tr::tr("Removal version for '%1' specified here.").arg(name),
- optionsItem->location());
- m_logger.printWarning(e);
- }
- optionsItem->parent()->setPropertyDeclaration(name, decl);
-}
-
-static void mergeProperty(Item *dst, const QString &name, const ValuePtr &value)
-{
- if (value->type() == Value::ItemValueType) {
- const ItemValueConstPtr itemValue = std::static_pointer_cast<ItemValue>(value);
- const Item * const valueItem = itemValue->item();
- Item * const subItem = dst->itemProperty(name, itemValue)->item();
- for (QMap<QString, ValuePtr>::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 auto jsValue = static_cast<JSSourceValue *>(value.get());
- if (jsValue->isBuiltinDefaultValue())
- return;
- const ValuePtr baseValue = dst->property(name);
- if (baseValue) {
- QBS_CHECK(baseValue->type() == Value::JSSourceValueType);
- const JSSourceValuePtr jsBaseValue = std::static_pointer_cast<JSSourceValue>(
- baseValue->clone());
- jsValue->setBaseValue(jsBaseValue);
- std::vector<JSSourceValue::Alternative> alternatives = jsValue->alternatives();
- jsValue->clearAlternatives();
- for (JSSourceValue::Alternative &a : alternatives) {
- a.value->setBaseValue(jsBaseValue);
- jsValue->addAlternative(a);
- }
- }
- }
- 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(), ItemType::Scope);
- Item * const scope = *cachedScopeItem;
- QBS_CHECK(productContext.item->file());
- scope->setFile(productContext.item->file());
- scope->setScope(productContext.item);
- productContext.project->scope->copyProperty(StringConstants::projectVar(), scope);
- productContext.scope->copyProperty(StringConstants::productVar(), 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::findOldProjectProbe(
- const QString &globalId,
- bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const
-{
- if (m_parameters.forceProbeExecution())
- return {};
-
- for (const ProbeConstPtr &oldProbe : m_oldProjectProbes.value(globalId)) {
- if (probeMatches(oldProbe, condition, initialProperties, sourceCode, CompareScript::Yes))
- return oldProbe;
- }
-
- return {};
-}
-
-ProbeConstPtr ModuleLoader::findOldProductProbe(
- const QString &productName,
- bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const
-{
- if (m_parameters.forceProbeExecution())
- return {};
-
- for (const ProbeConstPtr &oldProbe : m_oldProductProbes.value(productName)) {
- if (probeMatches(oldProbe, condition, initialProperties, sourceCode, CompareScript::Yes))
- return oldProbe;
- }
-
- return {};
-}
-
-ProbeConstPtr ModuleLoader::findCurrentProbe(
- const CodeLocation &location,
- bool condition,
- const QVariantMap &initialProperties) const
-{
- const QList<ProbeConstPtr> &cachedProbes = m_currentProbes.value(location);
- for (const ProbeConstPtr &probe : cachedProbes) {
- if (probeMatches(probe, condition, initialProperties, QString(), CompareScript::No))
- return probe;
- }
- return {};
-}
-
-bool ModuleLoader::probeMatches(const ProbeConstPtr &probe, bool condition,
- const QVariantMap &initialProperties, const QString &configureScript,
- CompareScript compareScript) const
-{
- return probe->condition() == condition
- && probe->initialProperties() == initialProperties
- && (compareScript == CompareScript::No
- || (probe->configureScript() == configureScript
- && !probe->needsReconfigure(m_lastResolveTime)));
-}
-
-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("Preparing products took %1.")
- .arg(elapsedTimeString(m_elapsedTimePrepareProducts));
- m_logger.qbsLog(LoggerInfo, true) << "\t"
- << Tr::tr("Setting up product dependencies took %1.")
- .arg(elapsedTimeString(m_elapsedTimeProductDependencies));
- m_logger.qbsLog(LoggerInfo, true) << "\t\t"
- << Tr::tr("Setting up transitive product dependencies took %1.")
- .arg(elapsedTimeString(m_elapsedTimeTransitiveDependencies));
- m_logger.qbsLog(LoggerInfo, true) << "\t"
- << Tr::tr("Handling products took %1.")
- .arg(elapsedTimeString(m_elapsedTimeHandleProducts));
- m_logger.qbsLog(LoggerInfo, true) << "\t\t"
- << Tr::tr("Running Probes took %1.")
- .arg(elapsedTimeString(m_elapsedTimeProbes));
- m_logger.qbsLog(LoggerInfo, true) << "\t\t"
- << Tr::tr("%1 probes encountered, %2 configure scripts executed, "
- "%3 re-used from current run, %4 re-used from earlier run.")
- .arg(m_probesEncountered).arg(m_probesRun).arg(m_probesCachedCurrent)
- .arg(m_probesCachedOld);
- m_logger.qbsLog(LoggerInfo, true) << "\t"
- << Tr::tr("Property checking took %1.")
- .arg(elapsedTimeString(m_elapsedTimePropertyChecking));
-}
-
-static void mergeParameters(QVariantMap &dst, const QVariantMap &src)
-{
- for (auto it = src.begin(); it != src.end(); ++it) {
- if (it.value().type() == QVariant::Map) {
- QVariant &vdst = dst[it.key()];
- QVariantMap mdst = vdst.toMap();
- mergeParameters(mdst, it.value().toMap());
- vdst = mdst;
- } else {
- dst[it.key()] = it.value();
- }
- }
-}
-
-static void adjustParametersScopes(Item *item, Item *scope)
-{
- if (item->type() == ItemType::ModuleParameters) {
- item->setScope(scope);
- return;
- }
-
- for (const auto &value : item->properties()) {
- if (value->type() != Value::ItemValueType)
- continue;
- adjustParametersScopes(std::static_pointer_cast<ItemValue>(value)->item(), scope);
- }
-}
-
-bool ModuleLoader::mergeExportItems(const ProductContext &productContext)
-{
- std::vector<Item *> exportItems;
- QList<Item *> children = productContext.item->children();
- for (int i = 0; i < children.size();) {
- Item * const child = children.at(i);
- if (child->type() == ItemType::Export) {
- exportItems.push_back(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.empty())
- productContext.item->setChildren(children);
-
- Item *merged = Item::create(productContext.item->pool(), ItemType::Export);
- const QString &nameKey = StringConstants::nameProperty();
- const ValuePtr nameValue = VariantValue::create(productContext.name);
- merged->setProperty(nameKey, nameValue);
- Set<FileContextConstPtr> filesWithExportItem;
- ProductModuleInfo pmi;
- bool hasDependenciesOnProductType = false;
- for (Item * const exportItem : qAsConst(exportItems)) {
- checkCancelation();
- if (Q_UNLIKELY(filesWithExportItem.contains(exportItem->file())))
- throw ErrorInfo(Tr::tr("Multiple Export items in one product are prohibited."),
- exportItem->location());
- exportItem->setProperty(nameKey, nameValue);
- if (!checkExportItemCondition(exportItem, productContext))
- continue;
- filesWithExportItem += exportItem->file();
- for (Item * const child : exportItem->children()) {
- if (child->type() == ItemType::Parameters) {
- adjustParametersScopes(child, child);
- mergeParameters(pmi.defaultParameters,
- m_evaluator->scriptValue(child).toVariant().toMap());
- } else {
- if (child->type() == ItemType::Depends) {
- bool productTypesIsSet;
- m_evaluator->stringValue(child, StringConstants::productTypesProperty(),
- QString(), &productTypesIsSet);
- if (productTypesIsSet)
- hasDependenciesOnProductType = true;
- }
- 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<QString, ValuePtr>::const_iterator it = exportItem->properties().constBegin();
- it != exportItem->properties().constEnd(); ++it) {
- mergeProperty(merged, it.key(), it.value());
- }
- }
- merged->setFile(exportItems.empty()
- ? productContext.item->file() : exportItems.back()->file());
- merged->setLocation(exportItems.empty()
- ? productContext.item->location() : exportItems.back()->location());
- Item::addChild(productContext.item, merged);
- merged->setupForBuiltinType(m_logger);
- pmi.exportItem = merged;
- pmi.multiplexId = productContext.multiplexConfigurationId;
- productContext.project->topLevelProject->productModules.insert(productContext.name, pmi);
- if (hasDependenciesOnProductType)
- m_exportsWithDeferredDependsItems.insert(merged);
- return !exportItems.empty();
-}
-
-Item *ModuleLoader::loadItemFromFile(const QString &filePath,
- const CodeLocation &referencingLocation)
-{
- Item *item;
- try {
- item = m_reader->readFile(filePath);
- } catch (const ErrorInfo &e) {
- if (e.hasLocation())
- throw;
- throw ErrorInfo(e.toString(), referencingLocation);
- }
- handleAllPropertyOptionsItems(item);
- return item;
-}
-
-void ModuleLoader::handleProfileItems(Item *item, ProjectContext *projectContext)
-{
- const std::vector<Item *> profileItems = collectProfileItems(item, projectContext);
- for (Item * const profileItem : profileItems) {
- try {
- handleProfile(profileItem);
- } catch (const ErrorInfo &e) {
- handlePropertyError(e, m_parameters, m_logger);
- }
- }
-}
-
-std::vector<Item *> ModuleLoader::collectProfileItems(Item *item, ProjectContext *projectContext)
-{
- QList<Item *> childItems = item->children();
- std::vector<Item *> profileItems;
- Item * scope = item->type() == ItemType::Project ? projectContext->scope : nullptr;
- for (auto it = childItems.begin(); it != childItems.end();) {
- Item * const childItem = *it;
- if (childItem->type() == ItemType::Profile) {
- if (!scope) {
- const ItemValuePtr itemValue = ItemValue::create(item);
- scope = Item::create(m_pool, ItemType::Scope);
- scope->setProperty(StringConstants::productVar(), itemValue);
- scope->setFile(item->file());
- scope->setScope(projectContext->scope);
- }
- childItem->setScope(scope);
- profileItems.push_back(childItem);
- it = childItems.erase(it);
- } else {
- if (childItem->type() == ItemType::Product) {
- for (Item * const profileItem : collectProfileItems(childItem, projectContext))
- profileItems.push_back(profileItem);
- }
- ++it;
- }
- }
- if (!profileItems.empty())
- item->setChildren(childItems);
- return profileItems;
-}
-
-void ModuleLoader::evaluateProfileValues(const QualifiedId &namePrefix, Item *item,
- Item *profileItem, QVariantMap &values)
-{
- const Item::PropertyMap &props = item->properties();
- for (auto it = props.begin(); it != props.end(); ++it) {
- QualifiedId name = namePrefix;
- name << it.key();
- switch (it.value()->type()) {
- case Value::ItemValueType:
- evaluateProfileValues(name, std::static_pointer_cast<ItemValue>(it.value())->item(),
- profileItem, values);
- break;
- case Value::VariantValueType:
- values.insert(name.join(QLatin1Char('.')),
- std::static_pointer_cast<VariantValue>(it.value())->value());
- break;
- case Value::JSSourceValueType:
- item->setType(ItemType::ModulePrefix); // TODO: Introduce new item type
- if (item != profileItem)
- item->setScope(profileItem);
- values.insert(name.join(QLatin1Char('.')),
- m_evaluator->value(item, it.key()).toVariant());
- break;
- }
- }
-}
-
-void ModuleLoader::handleProfile(Item *profileItem)
-{
- QVariantMap values;
- evaluateProfileValues(QualifiedId(), profileItem, profileItem, values);
- const bool condition = values.take(StringConstants::conditionProperty()).toBool();
- if (!condition)
- return;
- const QString profileName = values.take(StringConstants::nameProperty()).toString();
- if (profileName.isEmpty())
- throw ErrorInfo(Tr::tr("Every Profile item must have a name"), profileItem->location());
- if (profileName == Profile::fallbackName()) {
- throw ErrorInfo(Tr::tr("Reserved name '%1' cannot be used for an actual profile.")
- .arg(profileName), profileItem->location());
- }
- if (m_localProfiles.contains(profileName)) {
- throw ErrorInfo(Tr::tr("Local profile '%1' redefined.").arg(profileName),
- profileItem->location());
- }
- m_localProfiles.insert(profileName, values);
-}
-
-void ModuleLoader::collectNameFromOverride(const QString &overrideString)
-{
- static const auto extract = [](const QString &prefix, const QString &overrideString) {
- if (!overrideString.startsWith(prefix))
- return QString();
- const int startPos = prefix.length();
- const int endPos = overrideString.lastIndexOf(StringConstants::dot());
- if (endPos == -1)
- return QString();
- return overrideString.mid(startPos, endPos - startPos);
- };
- const QString &projectName = extract(StringConstants::projectsOverridePrefix(), overrideString);
- if (!projectName.isEmpty()) {
- m_projectNamesUsedInOverrides.insert(projectName);
- return;
- }
- const QString &productName = extract(StringConstants::productsOverridePrefix(), overrideString);
- if (!productName.isEmpty()) {
- m_productNamesUsedInOverrides.insert(productName.left(
- productName.indexOf(StringConstants::dot())));
- return;
- }
-}
-
-void ModuleLoader::checkProjectNamesInOverrides(const ModuleLoader::TopLevelProjectContext &tlp)
-{
- for (const QString &projectNameInOverride : m_projectNamesUsedInOverrides) {
- if (m_disabledProjects.contains(projectNameInOverride))
- continue;
- bool found = false;
- for (const ProjectContext * const p : tlp.projects) {
- if (p->name == projectNameInOverride) {
- found = true;
- break;
- }
- }
- if (!found) {
- handlePropertyError(Tr::tr("Unknown project '%1' in property override.")
- .arg(projectNameInOverride), m_parameters, m_logger);
- }
- }
-}
-
-void ModuleLoader::checkProductNamesInOverrides()
-{
- for (const QString &productNameInOverride : m_productNamesUsedInOverrides) {
- if (m_erroneousProducts.contains(productNameInOverride))
- continue;
- bool found = false;
- for (const auto &kv : m_productsByName) {
- // In an override string such as "a.b.c:d, we cannot tell whether we have a product
- // "a" and a module "b.c" or a product "a.b" and a module "c", so we need to take
- // care not to emit false positives here.
- if (kv.first == productNameInOverride
- || kv.first.startsWith(productNameInOverride + StringConstants::dot())) {
- found = true;
- break;
- }
- }
- if (!found) {
- handlePropertyError(Tr::tr("Unknown product '%1' in property override.")
- .arg(productNameInOverride), m_parameters, m_logger);
- }
- }
-}
-
-void ModuleLoader::setSearchPathsForProduct(ModuleLoader::ProductContext *product)
-{
- product->searchPaths = readExtraSearchPaths(product->item);
- Settings settings(m_parameters.settingsDirectory());
- const QVariantMap profileContents = product->project->result->profileConfigs
- .value(product->profileName).toMap();
- const QStringList prefsSearchPaths = Preferences(&settings, profileContents).searchPaths();
- const QStringList &currentSearchPaths = m_reader->allSearchPaths();
- for (const QString &p : prefsSearchPaths) {
- if (!currentSearchPaths.contains(p) && FileInfo(p).exists())
- product->searchPaths << p;
- }
-
- // Existing module provider search paths are re-used if and only if the provider configuration
- // at setup time was the same as the current one for the respective module provider.
- if (!m_moduleProviderInfo.empty()) {
- const QVariantMap configForProduct = moduleProviderConfig(*product);
- for (const ModuleProviderInfo &c : m_moduleProviderInfo) {
- if (configForProduct.value(c.name.toString()).toMap() == c.config) {
- qCDebug(lcModuleLoader) << "re-using search paths" << c.searchPaths
- << "from module provider" << c.name
- << "for product" << product->name;
- product->knownModuleProviders.insert(c.name);
- product->searchPaths << c.searchPaths;
- }
- }
- }
-}
-
-ModuleLoader::ShadowProductInfo ModuleLoader::getShadowProductInfo(
- const ModuleLoader::ProductContext &product) const
-{
- const bool isShadowProduct = product.name.startsWith(shadowProductPrefix());
- return std::make_pair(isShadowProduct, isShadowProduct
- ? product.name.mid(shadowProductPrefix().size()) : QString());
-}
-
-void ModuleLoader::collectProductsByName(const TopLevelProjectContext &topLevelProject)
-{
- for (ProjectContext * const project : topLevelProject.projects) {
- for (ProductContext &product : project->products)
- m_productsByName.insert({ product.name, &product });
- }
-}
-
-void ModuleLoader::collectProductsByType(const ModuleLoader::TopLevelProjectContext &topLevelProject)
-{
- for (ProjectContext * const project : topLevelProject.projects) {
- for (ProductContext &product : project->products) {
- try {
- const FileTags productTags
- = m_evaluator->fileTagsValue(product.item, StringConstants::typeProperty());
- for (const FileTag &tag : productTags)
- m_productsByType.insert({ tag, &product});
- } catch (const ErrorInfo &) {
- qCDebug(lcModuleLoader) << "product" << product.name << "has complex type "
- " and won't get an entry in the type map";
- }
- }
- }
-}
-
-void ModuleLoader::propagateModulesFromParent(ProductContext *productContext, Item *groupItem,
- const ModuleDependencies &reverseDepencencies)
-{
- QBS_CHECK(groupItem->type() == ItemType::Group);
- QHash<QualifiedId, Item *> moduleInstancesForGroup;
-
- // Step 1: Instantiate the product's modules for the group.
- for (Item::Module m : groupItem->parent()->modules()) {
- Item *targetItem = moduleInstanceItem(groupItem, m.name);
- targetItem->setPrototype(m.item);
-
- 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 (const Item::Module &module : groupItem->modules()) {
- Item::Modules adaptedModules;
- const Item::Modules &oldModules = module.item->prototype()->modules();
- for (Item::Module depMod : oldModules) {
- depMod.item = moduleInstancesForGroup.value(depMod.name);
- adaptedModules << depMod;
- if (depMod.name.front() == module.name.front())
- continue;
- const ItemValuePtr &modulePrefix = groupItem->itemProperty(depMod.name.front());
- QBS_CHECK(modulePrefix);
- module.item->setProperty(depMod.name.front(), modulePrefix);
- }
- module.item->setModules(adaptedModules);
- }
-
- const QualifiedIdSet &propsSetInGroup = gatherModulePropertiesSetInGroup(groupItem);
- if (propsSetInGroup.empty())
- return;
- productContext->info.modulePropertiesSetInGroups
- .insert(std::make_pair(groupItem, propsSetInGroup));
-
- // Step 3: Adapt defining items in values. This is potentially necessary if module properties
- // get assigned on the group level.
- for (const Item::Module &module : groupItem->modules()) {
- const QualifiedIdSet &dependents = reverseDepencencies.value(module.name);
- Item::Modules dependentModules;
- dependentModules.reserve(int(dependents.size()));
- for (const QualifiedId &depName : dependents) {
- Item * const itemOfDependent = moduleInstancesForGroup.value(depName);
- QBS_CHECK(itemOfDependent);
- Item::Module depMod;
- depMod.name = depName;
- depMod.item = itemOfDependent;
- dependentModules << depMod;
- }
- adjustDefiningItemsInGroupModuleInstances(module, dependentModules);
- }
-}
-
-static Item *createReplacementForDefiningItem(const Item *definingItem, ItemType type)
-{
- Item *replacement = Item::create(definingItem->pool(), type);
- replacement->setLocation(definingItem->location());
- definingItem->copyProperty(StringConstants::nameProperty(), replacement);
- return replacement;
-}
-
-void ModuleLoader::adjustDefiningItemsInGroupModuleInstances(const Item::Module &module,
- const Item::Modules &dependentModules)
-{
- if (!module.item->isPresentModule())
- return;
-
- // 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<Item *, Item *> definingItemReplacements;
-
- Item *modulePrototype = rootPrototype(module.item->prototype());
- QBS_CHECK(modulePrototype->type() == ItemType::Module
- || modulePrototype->type() == ItemType::Export);
-
- 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->ownProperty(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->ownProperty(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)
- 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 = QStringLiteral("__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 = createReplacementForDefiningItem(v->definingItem(),
- v->definingItem()->type());
- 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::invalidValue());
- } 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 = createReplacementForDefiningItem(v->definingItem(),
- ItemType::Module);
- replacement->setScope(module.item);
- }
- QBS_CHECK(!replacement->hasOwnProperty(caseA));
- qCDebug(lcModuleLoader).noquote().nospace()
- << "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();
- if (v->type() == Value::JSSourceValueType) {
- qCDebug(lcModuleLoader) << "value source code is"
- << std::static_pointer_cast<JSSourceValue>(v)->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 (const Item::Module &depMod : dependentModules) {
- 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 = createReplacementForDefiningItem(v->definingItem(),
- v->definingItem()->type());
- replacement->setProperties(v->definingItem()->properties());
- for (const auto &decl : v->definingItem()->propertyDeclarations())
- replacement->setPropertyDeclaration(decl.name(), decl);
- replacement->setPrototype(v->definingItem()->prototype());
- replacement->setScope(Item::create(v->definingItem()->pool(),
- ItemType::Scope));
- replacement->scope()->setScope(depMod.item);
- }
- QBS_CHECK(!replacement->hasOwnProperty(caseA));
- qCDebug(lcModuleLoader) << "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,
- ProductContext *productContext)
-{
- QBS_CHECK(m_dependencyResolvingPass == 1 || m_dependencyResolvingPass == 2);
-
- if (!productContext || m_dependencyResolvingPass == 1) {
- const Item::Module baseModule = loadBaseModule(dependsContext->product, item);
- item->addModule(baseModule);
- }
-
- // Resolve all Depends items.
- ItemModuleList loadedModules;
- QList<Item *> dependsItemPerLoadedModule;
- ProductDependencies productDependencies;
- const auto handleDependsItem = [&](Item *child) {
- if (child->type() != ItemType::Depends)
- return;
-
- int lastModulesCount = loadedModules.size();
- try {
- resolveDependsItem(dependsContext, child->parent(), child, &loadedModules,
- &productDependencies);
- } catch (const ErrorInfo &e) {
- if (!productContext)
- throw;
- handleProductError(e, productContext);
- }
- for (int i = lastModulesCount; i < loadedModules.size(); ++i)
- dependsItemPerLoadedModule.push_back(child);
-
- };
- if (productContext && m_dependencyResolvingPass == 2) {
- for (const auto &deferData : productContext->deferredDependsItems) {
- dependsContext->exportingProductItem = deferData.first;
- for (Item * const dependsItem : deferData.second)
- handleDependsItem(dependsItem);
- }
- } else {
- for (Item * const child : item->children())
- handleDependsItem(child);
- }
- QBS_CHECK(loadedModules.size() == dependsItemPerLoadedModule.size());
-
- Item *lastDependsItem = nullptr;
- for (Item * const dependsItem : dependsItemPerLoadedModule) {
- if (dependsItem == lastDependsItem)
- continue;
- adjustParametersScopes(dependsItem, dependsItem);
- forwardParameterDeclarations(dependsItem, loadedModules);
- lastDependsItem = dependsItem;
- }
-
- for (int i = 0; i < loadedModules.size(); ++i) {
- Item::Module &module = loadedModules[i];
- mergeParameters(module.parameters, extractParameters(dependsItemPerLoadedModule.at(i)));
- item->addModule(module);
-
- const QString moduleName = module.name.toString();
- std::for_each(productDependencies.begin(), productDependencies.end(),
- [&module, &moduleName] (ModuleLoaderResult::ProductInfo::Dependency &dep) {
- if (dep.name == moduleName)
- dep.parameters = module.parameters;
- });
- }
-
- dependsContext->productDependencies->insert(
- dependsContext->productDependencies->end(),
- productDependencies.cbegin(), productDependencies.cend());
-}
-
-class RequiredChainManager
-{
-public:
- RequiredChainManager(std::vector<bool> &requiredChain, bool required)
- : m_requiredChain(requiredChain)
- {
- m_requiredChain.push_back(required);
- }
-
- ~RequiredChainManager() { m_requiredChain.pop_back(); }
-
-private:
- std::vector<bool> &m_requiredChain;
-};
-
-void ModuleLoader::resolveDependsItem(DependsContext *dependsContext, Item *parentItem,
- Item *dependsItem, ItemModuleList *moduleResults,
- ProductDependencies *productResults)
-{
- checkCancelation();
- if (!checkItemCondition(dependsItem)) {
- qCDebug(lcModuleLoader) << "Depends item disabled, ignoring.";
- return;
- }
- bool nameIsSet;
- const QString name = m_evaluator->stringValue(dependsItem, StringConstants::nameProperty(),
- QString(), &nameIsSet);
- bool submodulesPropertySet;
- const QStringList submodules = m_evaluator->stringListValue(
- dependsItem, StringConstants::submodulesProperty(), &submodulesPropertySet);
- if (submodules.empty() && submodulesPropertySet) {
- qCDebug(lcModuleLoader) << "Ignoring Depends item with empty submodules list.";
- return;
- }
- if (Q_UNLIKELY(submodules.size() > 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());
- }
- const FallbackMode fallbackMode = m_parameters.fallbackProviderEnabled()
- && m_evaluator->boolValue(dependsItem, StringConstants::enableFallbackProperty())
- ? FallbackMode::Enabled : FallbackMode::Disabled;
-
- QList<QualifiedId> moduleNames;
- const QualifiedId nameParts = QualifiedId::fromString(name);
- if (submodules.empty()) {
- // Ignore explicit dependencies on the base module, which has already been loaded.
- if (name == StringConstants::qbsModule())
- return;
-
- moduleNames << nameParts;
- } else {
- for (const QString &submodule : submodules)
- moduleNames << nameParts + QualifiedId::fromString(submodule);
- }
-
- Item::Module result;
- bool productTypesIsSet;
- m_evaluator->stringValue(dependsItem, StringConstants::productTypesProperty(),
- QString(), &productTypesIsSet);
- if (m_dependencyResolvingPass == 1 && productTypesIsSet) {
- qCDebug(lcModuleLoader) << "queuing product" << dependsContext->product->name
- << "for a second dependencies resolving pass";
- m_productsWithDeferredDependsItems[dependsContext->product].insert(
- DeferredDependsContext(dependsContext->exportingProductItem, parentItem));
- return;
- }
-
- const bool isRequired = !productTypesIsSet
- && m_evaluator->boolValue(dependsItem, StringConstants::requiredProperty())
- && !contains(m_requiredChain, false);
- const Version minVersion = Version::fromString(
- m_evaluator->stringValue(dependsItem,
- StringConstants::versionAtLeastProperty()));
- const Version maxVersion = Version::fromString(
- m_evaluator->stringValue(dependsItem, StringConstants::versionBelowProperty()));
- const VersionRange versionRange(minVersion, maxVersion);
- QStringList multiplexConfigurationIds = m_evaluator->stringListValue(
- dependsItem,
- StringConstants::multiplexConfigurationIdsProperty());
- if (multiplexConfigurationIds.empty())
- multiplexConfigurationIds << QString();
-
- for (const QualifiedId &moduleName : qAsConst(moduleNames)) {
- // 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;
- }
-
- QVariantMap defaultParameters;
- Item *moduleItem = loadModule(dependsContext->product, dependsContext->exportingProductItem,
- parentItem, dependsItem->location(), dependsItem->id(),
- moduleName, multiplexConfigurationIds.first(), fallbackMode,
- isRequired, &result.isProduct, &defaultParameters);
- if (!moduleItem) {
- const QString productName = ResolvedProduct::fullDisplayName(
- dependsContext->product->name,
- dependsContext->product->multiplexConfigurationId);
- if (!multiplexConfigurationIds.first().isEmpty()) {
- const QString depName = ResolvedProduct::fullDisplayName(
- moduleName.toString(), multiplexConfigurationIds.first());
- throw ErrorInfo(Tr::tr("Dependency from product '%1' to product '%2' not "
- "fulfilled.").arg(productName, depName));
- }
- ErrorInfo e(Tr::tr("Dependency '%1' not found for product '%2'.")
- .arg(moduleName.toString(), productName), dependsItem->location());
- throw e;
- }
- if (result.isProduct && !m_dependsChain.empty() && !m_dependsChain.back().isProduct) {
- throw ErrorInfo(Tr::tr("Invalid dependency on product '%1': Modules cannot depend on "
- "products. You may want to turn your module into a product and "
- "add the dependency in that product's Export item.")
- .arg(moduleName.toString()), dependsItem->location());
- }
- qCDebug(lcModuleLoader) << "module loaded:" << moduleName.toString();
- result.name = moduleName;
- result.item = moduleItem;
- result.required = isRequired;
- result.parameters = defaultParameters;
- result.versionRange = versionRange;
- moduleResults->push_back(result);
- if (result.isProduct) {
- qCDebug(lcModuleLoader) << "product dependency loaded:" << moduleName.toString();
- bool profilesPropertyWasSet = false;
- QStringList profiles = m_evaluator->stringListValue(dependsItem,
- StringConstants::profilesProperty(),
- &profilesPropertyWasSet);
- if (profiles.empty()) {
- if (profilesPropertyWasSet)
- profiles.push_back(StringConstants::star());
- else
- profiles.push_back(QString());
- }
- for (const QString &profile : qAsConst(profiles)) {
- for (const QString &multiplexId : qAsConst(multiplexConfigurationIds)) {
- ModuleLoaderResult::ProductInfo::Dependency dependency;
- dependency.name = moduleName.toString();
- dependency.profile = profile;
- dependency.multiplexConfigurationId = multiplexId;
- dependency.isRequired = isRequired;
- productResults->push_back(dependency);
- }
- }
- }
- }
-}
-
-void ModuleLoader::forwardParameterDeclarations(const Item *dependsItem,
- const ItemModuleList &modules)
-{
- for (auto it = dependsItem->properties().begin(); it != dependsItem->properties().end(); ++it) {
- if (it.value()->type() != Value::ItemValueType)
- continue;
- forwardParameterDeclarations(it.key(),
- std::static_pointer_cast<ItemValue>(it.value())->item(),
- modules);
- }
-}
-
-void ModuleLoader::forwardParameterDeclarations(const QualifiedId &moduleName, Item *item,
- const ItemModuleList &modules)
-{
- auto it = std::find_if(modules.begin(), modules.end(), [&moduleName] (const Item::Module &m) {
- return m.name == moduleName;
- });
- if (it != modules.end()) {
- item->setPropertyDeclarations(m_parameterDeclarations.value(rootPrototype(it->item)));
- } else {
- for (auto it = item->properties().begin(); it != item->properties().end(); ++it) {
- if (it.value()->type() != Value::ItemValueType)
- continue;
- forwardParameterDeclarations(QualifiedId(moduleName) << it.key(),
- std::static_pointer_cast<ItemValue>(it.value())->item(),
- modules);
- }
- }
-}
-
-void ModuleLoader::resolveParameterDeclarations(const Item *module)
-{
- Item::PropertyDeclarationMap decls;
- const auto &moduleChildren = module->children();
- for (Item *param : moduleChildren) {
- if (param->type() != ItemType::Parameter)
- continue;
- const auto paramDecls = param->propertyDeclarations();
- for (auto it = paramDecls.begin(); it != paramDecls.end(); ++it)
- decls.insert(it.key(), it.value());
- }
- m_parameterDeclarations.insert(module, decls);
-}
-
-static bool isItemValue(const ValuePtr &v)
-{
- return v->type() == Value::ItemValueType;
-}
-
-static Item::PropertyMap filterItemProperties(const Item::PropertyMap &properties)
-{
- Item::PropertyMap result;
- auto itEnd = properties.end();
- for (auto it = properties.begin(); it != itEnd; ++it) {
- if (isItemValue(it.value()))
- result.insert(it.key(), it.value());
- }
- return result;
-}
-
-static QVariantMap safeToVariant(const QScriptValue &v)
-{
- QVariantMap result;
- QScriptValueIterator it(v);
- while (it.hasNext()) {
- it.next();
- QScriptValue u = it.value();
- if (u.isError())
- throw ErrorInfo(u.toString());
- result[it.name()] = (u.isObject() && !u.isArray() && !u.isRegExp())
- ? safeToVariant(u) : it.value().toVariant();
- }
- return result;
-}
-
-QVariantMap ModuleLoader::extractParameters(Item *dependsItem) const
-{
- QVariantMap result;
- const Item::PropertyMap &itemProperties = filterItemProperties(
- rootPrototype(dependsItem)->properties());
- if (itemProperties.empty())
- return result;
-
- auto origProperties = dependsItem->properties();
- dependsItem->setProperties(itemProperties);
- QScriptValue sv = m_evaluator->scriptValue(dependsItem);
- try {
- result = safeToVariant(sv);
- } catch (const ErrorInfo &exception) {
- auto ei = exception;
- ei.prepend(Tr::tr("Error in dependency parameter."), dependsItem->location());
- throw ei;
- }
- dependsItem->setProperties(origProperties);
- return result;
-}
-
-[[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.empty());
- Item *instance = containerItem;
- for (int i = 0; i < moduleName.size(); ++i) {
- const QString &moduleNameSegment = moduleName.at(i);
- const ValuePtr v = instance->ownProperty(moduleName.at(i));
- if (v && v->type() == Value::ItemValueType) {
- instance = std::static_pointer_cast<ItemValue>(v)->item();
- } else {
- const ItemType itemType = i < moduleName.size() - 1 ? ItemType::ModulePrefix
- : ItemType::ModuleInstance;
- auto newItem = Item::create(m_pool, itemType);
- instance->setProperty(moduleNameSegment, ItemValue::create(newItem));
- instance = newItem;
- }
- if (i < moduleName.size() - 1) {
- if (instance->type() == ItemType::ModuleInstance) {
- QualifiedId conflictingName = QStringList(moduleName.mid(0, i + 1));
- throwModuleNamePrefixError(conflictingName, moduleName, CodeLocation());
- }
- QBS_CHECK(instance->type() == ItemType::ModulePrefix);
- }
- }
- QBS_CHECK(instance != containerItem);
- return instance;
-}
-
-ModuleLoader::ProductModuleInfo *ModuleLoader::productModule(ProductContext *productContext,
- const QString &name, const QString &multiplexId, bool &productNameMatch)
-{
- auto &exportsData = productContext->project->topLevelProject->productModules;
- const auto firstIt = exportsData.find(name);
- productNameMatch = firstIt != exportsData.end();
- for (auto it = firstIt; it != exportsData.end() && it.key() == name; ++it) {
- if (it.value().multiplexId == multiplexId)
- return &it.value();
- }
- if (multiplexId.isEmpty() && firstIt != exportsData.end())
- return &firstIt.value();
- return nullptr;
-}
-
-ModuleLoader::ProductContext *ModuleLoader::product(ProjectContext *projectContext,
- const QString &name)
-{
- auto itEnd = projectContext->products.end();
- auto it = std::find_if(projectContext->products.begin(), itEnd,
- [&name] (const ProductContext &ctx) {
- return ctx.name == name;
- });
- return it == itEnd ? nullptr : &*it;
-}
-
-ModuleLoader::ProductContext *ModuleLoader::product(TopLevelProjectContext *tlpContext,
- const QString &name)
-{
- ProductContext *result = nullptr;
- for (auto prj : tlpContext->projects) {
- result = product(prj, name);
- if (result)
- break;
- }
- return result;
-}
-
-class ModuleLoader::DependsChainManager
-{
-public:
- DependsChainManager(std::vector<DependsChainEntry> &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.name == module;
- });
- if (alreadyInChain) {
- ErrorInfo error;
- error.append(Tr::tr("Cyclic dependencies detected:"));
- for (const DependsChainEntry &e : qAsConst(m_dependsChain))
- error.append(e.name.toString(), e.location);
- error.append(module.toString(), dependsLocation);
- throw error;
- }
- m_dependsChain.emplace_back(module, dependsLocation);
- }
-
- ~DependsChainManager() { m_dependsChain.pop_back(); }
-
-private:
- std::vector<DependsChainEntry> &m_dependsChain;
-};
-
-static bool isBaseModule(const QualifiedId &moduleName)
-{
- return moduleName.size() == 1 && moduleName.front() == StringConstants::qbsModule();
-}
-
-class DelayedPropertyChanger
-{
-public:
- ~DelayedPropertyChanger()
- {
- applyNow();
- }
-
- void setLater(Item *item, const QString &name, const ValuePtr &value)
- {
- QBS_CHECK(m_item == nullptr);
- m_item = item;
- m_name = name;
- m_value = value;
- }
-
- void removeLater(Item *item, const QString &name)
- {
- QBS_CHECK(m_item == nullptr);
- m_item = item;
- m_name = name;
- }
-
- void applyNow()
- {
- if (!m_item || m_name.isEmpty())
- return;
- if (m_value)
- m_item->setProperty(m_name, m_value);
- else
- m_item->removeProperty(m_name);
- m_item = nullptr;
- m_name.clear();
- m_value.reset();
- }
-
-private:
- Item *m_item = nullptr;
- QString m_name;
- ValuePtr m_value;
-};
-
-Item *ModuleLoader::loadModule(ProductContext *productContext, Item *exportingProductItem,
- Item *item, const CodeLocation &dependsItemLocation,
- const QString &moduleId, const QualifiedId &moduleName,
- const QString &multiplexId, FallbackMode fallbackMode,
- bool isRequired, bool *isProductDependency,
- QVariantMap *defaultParameters)
-{
- qCDebug(lcModuleLoader) << "loadModule name:" << moduleName.toString() << "id:" << moduleId;
-
- RequiredChainManager requiredChainManager(m_requiredChain, isRequired);
- DependsChainManager dependsChainManager(m_dependsChain, moduleName, dependsItemLocation);
-
- Item *moduleInstance = moduleId.isEmpty()
- ? moduleInstanceItem(item, moduleName)
- : moduleInstanceItem(item, QStringList(moduleId));
- if (moduleInstance->scope())
- return moduleInstance; // already handled
-
- if (Q_UNLIKELY(moduleInstance->type() == ItemType::ModulePrefix)) {
- for (const Item::Module &m : item->modules()) {
- if (m.name.front() == moduleName.front())
- throwModuleNamePrefixError(moduleName, m.name, dependsItemLocation);
- }
- }
- QBS_CHECK(moduleInstance->type() == ItemType::ModuleInstance);
-
- // Prepare module instance for evaluating Module.condition.
- DelayedPropertyChanger delayedPropertyChanger;
- const QString &qbsModuleName = StringConstants::qbsModule();
- if (!isBaseModule(moduleName)) {
- ItemValuePtr qbsProp = productContext->item->itemProperty(qbsModuleName);
- if (qbsProp) {
- ValuePtr qbsModuleValue = moduleInstance->ownProperty(qbsModuleName);
- if (qbsModuleValue)
- delayedPropertyChanger.setLater(moduleInstance, qbsModuleName, qbsModuleValue);
- else
- delayedPropertyChanger.removeLater(moduleInstance, qbsModuleName);
- moduleInstance->setProperty(qbsModuleName, qbsProp);
- }
- }
-
- Item *modulePrototype = nullptr;
- ProductModuleInfo * const pmi = productModule(productContext, moduleName.toString(),
- multiplexId, *isProductDependency);
- if (pmi) {
- m_dependsChain.back().isProduct = true;
- modulePrototype = pmi->exportItem;
- if (defaultParameters)
- *defaultParameters = pmi->defaultParameters;
- } else if (!*isProductDependency) {
- modulePrototype = searchAndLoadModuleFile(productContext, dependsItemLocation,
- moduleName, fallbackMode, isRequired, moduleInstance);
- }
- delayedPropertyChanger.applyNow();
- if (!modulePrototype)
- return nullptr;
-
- instantiateModule(productContext, exportingProductItem, item, moduleInstance, modulePrototype,
- moduleName, pmi);
- return moduleInstance;
-}
-
-struct PrioritizedItem
-{
- PrioritizedItem(Item *item, int priority, int searchPathIndex)
- : item(item), priority(priority), searchPathIndex(searchPathIndex)
- {
- }
-
- Item *item = nullptr;
- int priority = 0;
- int searchPathIndex = 0;
-};
-
-static Item *chooseModuleCandidate(const std::vector<PrioritizedItem> &candidates,
- const QString &moduleName)
-{
- auto maxIt = std::max_element(candidates.begin(), candidates.end(),
- [] (const PrioritizedItem &a, const PrioritizedItem &b) {
- if (a.priority < b.priority)
- return true;
- if (a.priority > b.priority)
- return false;
- return a.searchPathIndex > b.searchPathIndex;
- });
-
- size_t nmax = std::count_if(candidates.begin(), candidates.end(),
- [maxIt] (const PrioritizedItem &i) {
- return i.priority == maxIt->priority && i.searchPathIndex == maxIt->searchPathIndex;
- });
-
- if (nmax > 1) {
- ErrorInfo e(Tr::tr("There is more than one equally prioritized candidate for module '%1'.")
- .arg(moduleName));
- for (size_t i = 0; i < candidates.size(); ++i) {
- const auto candidate = candidates.at(i);
- if (candidate.priority == maxIt->priority) {
- //: The %1 denotes the number of the candidate.
- e.append(Tr::tr("candidate %1").arg(i + 1), candidates.at(i).item->location());
- }
- }
- throw e;
- }
-
- return maxIt->item;
-}
-
-Item *ModuleLoader::searchAndLoadModuleFile(ProductContext *productContext,
- const CodeLocation &dependsItemLocation, const QualifiedId &moduleName,
- FallbackMode fallbackMode, bool isRequired, Item *moduleInstance)
-{
- bool triedToLoadModule = false;
- const QString fullName = moduleName.toString();
- std::vector<PrioritizedItem> candidates;
- const QStringList &searchPaths = m_reader->allSearchPaths();
- bool matchingDirectoryFound = false;
- for (int i = 0; i < searchPaths.size(); ++i) {
- const QString &path = searchPaths.at(i);
- const QString dirPath = findExistingModulePath(path, moduleName);
- if (dirPath.isEmpty())
- continue;
- matchingDirectoryFound = true;
- QStringList moduleFileNames = m_moduleDirListCache.value(dirPath);
- if (moduleFileNames.empty()) {
- QDirIterator dirIter(dirPath, StringConstants::qbsFileWildcards());
- while (dirIter.hasNext())
- moduleFileNames += dirIter.next();
-
- m_moduleDirListCache.insert(dirPath, moduleFileNames);
- }
- for (const QString &filePath : qAsConst(moduleFileNames)) {
- triedToLoadModule = true;
- Item *module = loadModuleFile(productContext, fullName, isBaseModule(moduleName),
- filePath, &triedToLoadModule, moduleInstance);
- if (module)
- candidates.emplace_back(module, 0, i);
- if (!triedToLoadModule)
- m_moduleDirListCache[dirPath].removeOne(filePath);
- }
- }
-
- if (candidates.empty()) {
- if (!matchingDirectoryFound) {
- bool moduleAlreadyKnown = false;
- ModuleProviderResult result;
- for (QualifiedId providerName = moduleName; !providerName.empty();
- providerName.pop_back()) {
- if (!productContext->knownModuleProviders.insert(providerName).second) {
- moduleAlreadyKnown = true;
- break;
- }
- qCDebug(lcModuleLoader) << "Module" << moduleName.toString()
- << "not found, checking for module providers";
- result = findModuleProvider(providerName, *productContext,
- ModuleProviderLookup::Regular, dependsItemLocation);
- if (result.providerFound)
- break;
- }
- if (fallbackMode == FallbackMode::Enabled && !result.providerFound
- && !moduleAlreadyKnown) {
- qCDebug(lcModuleLoader) << "Specific module provider not found for"
- << moduleName.toString() << ", setting up fallback.";
- result = findModuleProvider(moduleName, *productContext,
- ModuleProviderLookup::Fallback, dependsItemLocation);
- }
- if (result.providerAddedSearchPaths) {
- qCDebug(lcModuleLoader) << "Re-checking for module" << moduleName.toString()
- << "with newly added search paths from module provider";
- return searchAndLoadModuleFile(productContext, dependsItemLocation, moduleName,
- fallbackMode, isRequired, moduleInstance);
- }
- }
- if (!isRequired)
- return createNonPresentModule(fullName, QStringLiteral("not found"), nullptr);
- if (Q_UNLIKELY(triedToLoadModule))
- throw ErrorInfo(Tr::tr("Module %1 could not be loaded.").arg(fullName),
- dependsItemLocation);
- return nullptr;
- }
-
- Item *moduleItem;
- if (candidates.size() == 1) {
- moduleItem = candidates.at(0).item;
- } else {
- for (auto &candidate : candidates) {
- candidate.priority = m_evaluator->intValue(candidate.item,
- StringConstants::priorityProperty(),
- candidate.priority);
- }
- moduleItem = chooseModuleCandidate(candidates, fullName);
- }
-
- const auto it = productContext->unknownProfilePropertyErrors.find(moduleItem);
- if (it != productContext->unknownProfilePropertyErrors.cend()) {
- const QString fullProductName = ResolvedProduct::fullDisplayName
- (productContext->name, productContext->multiplexConfigurationId);
- ErrorInfo error(Tr::tr("Loading module '%1' for product '%2' failed due to invalid values "
- "in profile '%3':").arg(fullName, fullProductName,
- productContext->profileName));
- for (const ErrorInfo &e : it->second)
- error.append(e.toString());
- handlePropertyError(error, m_parameters, m_logger);
- }
- return moduleItem;
-}
-
-// 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::VariantList:
- return QVariant::List;
- 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;
-}
-
-static Item *findDeepestModuleInstance(Item *instance)
-{
- while (instance->prototype() && instance->prototype()->type() == ItemType::ModuleInstance)
- instance = instance->prototype();
- return instance;
-}
-
-Item *ModuleLoader::loadModuleFile(ProductContext *productContext, const QString &fullModuleName,
- bool isBaseModule, const QString &filePath, bool *triedToLoad, Item *moduleInstance)
-{
- checkCancelation();
-
- qCDebug(lcModuleLoader) << "loadModuleFile" << fullModuleName << "from" << filePath;
-
- Item * const module = getModulePrototype(productContext, fullModuleName, filePath, triedToLoad);
- if (!module)
- return nullptr;
-
- const auto key = std::make_pair(module, productContext);
- const auto it = m_modulePrototypeEnabledInfo.find(key);
- if (it != m_modulePrototypeEnabledInfo.end()) {
- qCDebug(lcModuleLoader) << "prototype cache hit (level 2)";
- return it.value() ? module : nullptr;
- }
-
- // Set the name before evaluating any properties. EvaluatorScriptClass reads the module name.
- module->setProperty(StringConstants::nameProperty(), VariantValue::create(fullModuleName));
-
- Item *deepestModuleInstance = findDeepestModuleInstance(moduleInstance);
- Item *origDeepestModuleInstancePrototype = deepestModuleInstance->prototype();
- deepestModuleInstance->setPrototype(module);
- bool enabled = checkItemCondition(moduleInstance, module);
- deepestModuleInstance->setPrototype(origDeepestModuleInstancePrototype);
- if (!enabled) {
- qCDebug(lcModuleLoader) << "condition of module" << fullModuleName << "is false";
- m_modulePrototypeEnabledInfo.insert(key, false);
- return nullptr;
- }
-
- if (isBaseModule)
- setupBaseModulePrototype(module);
- else
- resolveParameterDeclarations(module);
-
- m_modulePrototypeEnabledInfo.insert(key, true);
- return module;
-}
-
-Item *ModuleLoader::getModulePrototype(ProductContext *productContext,
- const QString &fullModuleName, const QString &filePath, bool *triedToLoad)
-{
- auto &prototypeList = m_modulePrototypes[filePath];
- for (const auto &prototype : prototypeList) {
- if (prototype.second == productContext->profileName) {
- qCDebug(lcModuleLoader) << "prototype cache hit (level 1)";
- return prototype.first;
- }
- }
- Item * const module = loadItemFromFile(filePath, CodeLocation());
- prototypeList.emplace_back(module, productContext->profileName);
- if (module->type() != ItemType::Module) {
- qCDebug(lcModuleLoader).nospace()
- << "Alleged module " << fullModuleName << " has type '"
- << module->typeName() << "', so it's not a module after all.";
- *triedToLoad = false;
- return nullptr;
- }
-
- // Module properties that are defined in the profile are used as default values.
- // This is the reason we need to have different items per profile.
- const QVariantMap profileModuleProperties
- = productContext->moduleProperties.value(fullModuleName).toMap();
- for (QVariantMap::const_iterator vmit = profileModuleProperties.begin();
- vmit != profileModuleProperties.end(); ++vmit)
- {
- if (Q_UNLIKELY(!module->hasProperty(vmit.key()))) {
- productContext->unknownProfilePropertyErrors[module].emplace_back
- (Tr::tr("Unknown property: %1.%2").arg(fullModuleName, vmit.key()));
- 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);
- }
-
- return module;
-}
-
-Item::Module ModuleLoader::loadBaseModule(ProductContext *productContext, Item *item)
-{
- const QualifiedId baseModuleName(StringConstants::qbsModule());
- Item::Module baseModuleDesc;
- baseModuleDesc.name = baseModuleName;
- baseModuleDesc.item = loadModule(productContext, nullptr, item, CodeLocation(), QString(),
- baseModuleName, QString(), FallbackMode::Disabled, true,
- &baseModuleDesc.isProduct, nullptr);
- if (productContext->item) {
- const Item * const qbsInstanceItem
- = moduleInstanceItem(productContext->item, baseModuleName);
- const Item::PropertyMap &props = qbsInstanceItem->properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- if (it.value()->type() == Value::VariantValueType)
- baseModuleDesc.item->setProperty(it.key(), it.value());
- }
- }
- QBS_CHECK(!baseModuleDesc.isProduct);
- if (Q_UNLIKELY(!baseModuleDesc.item))
- throw ErrorInfo(Tr::tr("Cannot load base qbs module."));
- return baseModuleDesc;
-}
-
-void ModuleLoader::setupBaseModulePrototype(Item *prototype)
-{
- prototype->setProperty(QStringLiteral("hostPlatform"),
- VariantValue::create(QString::fromStdString(
- HostOsInfo::hostOSIdentifier())));
- prototype->setProperty(QStringLiteral("libexecPath"),
- VariantValue::create(m_parameters.libexecPath()));
-
- const Version qbsVersion = LanguageInfo::qbsVersion();
- prototype->setProperty(QStringLiteral("versionMajor"),
- VariantValue::create(qbsVersion.majorVersion()));
- prototype->setProperty(QStringLiteral("versionMinor"),
- VariantValue::create(qbsVersion.minorVersion()));
- prototype->setProperty(QStringLiteral("versionPatch"),
- VariantValue::create(qbsVersion.patchLevel()));
-}
-
-static void collectItemsWithId_impl(Item *item, QList<Item *> *result)
-{
- if (!item->id().isEmpty())
- result->push_back(item);
- for (Item * const child : item->children())
- collectItemsWithId_impl(child, result);
-}
-
-static QList<Item *> collectItemsWithId(Item *item)
-{
- QList<Item *> result;
- collectItemsWithId_impl(item, &result);
- return result;
-}
-
-static std::vector<std::pair<QualifiedId, ItemValuePtr>> instanceItemProperties(Item *item)
-{
- std::vector<std::pair<QualifiedId, ItemValuePtr>> result;
- QualifiedId name;
- std::function<void(Item *)> f = [&] (Item *item) {
- for (auto it = item->properties().begin(); it != item->properties().end(); ++it) {
- if (it.value()->type() != Value::ItemValueType)
- continue;
- ItemValuePtr itemValue = std::static_pointer_cast<ItemValue>(it.value());
- if (!itemValue->item())
- continue;
- name.push_back(it.key());
- if (itemValue->item()->type() == ItemType::ModulePrefix)
- f(itemValue->item());
- else
- result.emplace_back(name, itemValue);
- name.removeLast();
- }
- };
- f(item);
- return result;
-}
-
-void ModuleLoader::instantiateModule(ProductContext *productContext, Item *exportingProduct,
- Item *instanceScope, Item *moduleInstance, Item *modulePrototype,
- const QualifiedId &moduleName, ProductModuleInfo *productModuleInfo)
-{
- Item *deepestModuleInstance = findDeepestModuleInstance(moduleInstance);
- deepestModuleInstance->setPrototype(modulePrototype);
- const QString fullName = moduleName.toString();
- const QString generalOverrideKey = QStringLiteral("modules.") + fullName;
- const QString perProductOverrideKey = StringConstants::productsOverridePrefix()
- + productContext->name + QLatin1Char('.') + fullName;
- for (Item *instance = moduleInstance; instance; instance = instance->prototype()) {
- overrideItemProperties(instance, generalOverrideKey, m_parameters.overriddenValuesTree());
- if (fullName == QStringLiteral("qbs"))
- overrideItemProperties(instance, fullName, m_parameters.overriddenValuesTree());
- overrideItemProperties(instance, perProductOverrideKey,
- m_parameters.overriddenValuesTree());
- if (instance == deepestModuleInstance)
- break;
- }
-
- moduleInstance->setFile(modulePrototype->file());
- moduleInstance->setLocation(modulePrototype->location());
- QBS_CHECK(moduleInstance->type() == ItemType::ModuleInstance);
-
- // create module scope
- Item *moduleScope = Item::create(m_pool, ItemType::Scope);
- QBS_CHECK(instanceScope->file());
- moduleScope->setFile(instanceScope->file());
- moduleScope->setScope(instanceScope);
- QBS_CHECK(productContext->project->scope);
- productContext->project->scope->copyProperty(StringConstants::projectVar(), moduleScope);
- if (productContext->scope)
- productContext->scope->copyProperty(StringConstants::productVar(), moduleScope);
- else
- QBS_CHECK(fullName == StringConstants::qbsModule()); // Dummy product.
-
- if (productModuleInfo) {
- exportingProduct = productModuleInfo->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(StringConstants::productVar(),
- ItemValue::create(exportingProduct));
- moduleScope->setProperty(QStringLiteral("importingProduct"),
- ItemValue::create(productContext->item));
-
- moduleScope->setProperty(StringConstants::projectVar(),
- ItemValue::create(exportingProduct->parent()));
-
- PropertyDeclaration pd(StringConstants::qbsSourceDirPropertyInternal(),
- PropertyDeclaration::String, QString(),
- PropertyDeclaration::PropertyNotAvailableInConfig);
- moduleInstance->setPropertyDeclaration(pd.name(), pd);
- ValuePtr v = exportingProduct
- ->property(StringConstants::sourceDirectoryProperty())->clone();
- moduleInstance->setProperty(pd.name(), v);
- }
- moduleInstance->setScope(moduleScope);
-
- QHash<Item *, Item *> prototypeInstanceMap;
- prototypeInstanceMap[modulePrototype] = moduleInstance;
-
- // create instances for every child of the prototype
- createChildInstances(moduleInstance, modulePrototype, &prototypeInstanceMap);
-
- // create ids from from the prototype in the instance
- if (modulePrototype->file()->idScope()) {
- const auto items = collectItemsWithId(modulePrototype);
- for (Item * const itemWithId : items) {
- Item *idProto = itemWithId;
- Item *idInstance = prototypeInstanceMap.value(idProto);
- QBS_ASSERT(idInstance, continue);
- ItemValuePtr idInstanceValue = ItemValue::create(idInstance);
- moduleScope->setProperty(itemWithId->id(), idInstanceValue);
- }
- }
-
- // For foo.bar in modulePrototype create an item foo in moduleInstance.
- for (const auto &iip : instanceItemProperties(modulePrototype)) {
- if (iip.second->item()->properties().empty())
- continue;
- qCDebug(lcModuleLoader) << "The prototype of " << moduleName
- << " sets properties on " << iip.first.toString();
- Item *item = moduleInstanceItem(moduleInstance, iip.first);
- item->setPrototype(iip.second->item());
- if (iip.second->createdByPropertiesBlock()) {
- ItemValuePtr itemValue = moduleInstance->itemProperty(iip.first.front());
- for (int i = 1; i < iip.first.size(); ++i)
- itemValue = itemValue->item()->itemProperty(iip.first.at(i));
- itemValue->setCreatedByPropertiesBlock(true);
- }
- }
-
- // Resolve dependencies of this module instance.
- DependsContext dependsContext;
- dependsContext.product = productContext;
- dependsContext.exportingProductItem = exportingProduct;
- QBS_ASSERT(moduleInstance->modules().empty(), moduleInstance->removeModules());
- if (productModuleInfo) {
- dependsContext.productDependencies = &productContext->productModuleDependencies[fullName];
- resolveDependencies(&dependsContext, moduleInstance);
- } else if (!isBaseModule(moduleName)) {
- dependsContext.productDependencies = &productContext->info.usedProducts;
- resolveDependencies(&dependsContext, moduleInstance);
- }
-
- // Check readonly properties.
- const auto end = moduleInstance->properties().cend();
- for (auto it = moduleInstance->properties().cbegin(); it != end; ++it) {
- const PropertyDeclaration &pd = moduleInstance->propertyDeclaration(it.key());
- if (!pd.flags().testFlag(PropertyDeclaration::ReadOnlyFlag))
- continue;
- throw ErrorInfo(Tr::tr("Cannot set read-only property '%1'.").arg(pd.name()),
- moduleInstance->property(pd.name())->location());
- }
-}
-
-void ModuleLoader::createChildInstances(Item *instance, Item *prototype,
- QHash<Item *, Item *> *prototypeInstanceMap) const
-{
- instance->childrenReserve(instance->children().size() + prototype->children().size());
-
- for (Item * const childPrototype : prototype->children()) {
- Item *childInstance = Item::create(m_pool, childPrototype->type());
- prototypeInstanceMap->insert(childPrototype, childInstance);
- childInstance->setPrototype(childPrototype);
- childInstance->setFile(childPrototype->file());
- childInstance->setId(childPrototype->id());
- childInstance->setLocation(childPrototype->location());
- childInstance->setScope(instance->scope());
- Item::addChild(instance, childInstance);
- createChildInstances(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);
- for (Item * const child : item->children())
- if (child->type() == ItemType::Probe)
- resolveProbe(productContext, item, child);
-}
-
-void ModuleLoader::resolveProbe(ProductContext *productContext, Item *parent, Item *probe)
-{
- qCDebug(lcModuleLoader) << "Resolving Probe at " << probe->location().toString();
- ++m_probesEncountered;
- const QString &probeId = probeGlobalId(probe);
- if (Q_UNLIKELY(probeId.isEmpty()))
- throw ErrorInfo(Tr::tr("Probe.id must be set."), probe->location());
- const JSSourceValueConstPtr configureScript
- = probe->sourceProperty(StringConstants::configureProperty());
- QBS_CHECK(configureScript);
- if (Q_UNLIKELY(configureScript->sourceCode() == StringConstants::undefinedValue()))
- throw ErrorInfo(Tr::tr("Probe.configure must be set."), probe->location());
- using ProbeProperty = std::pair<QString, QScriptValue>;
- QList<ProbeProperty> probeBindings;
- QVariantMap initialProperties;
- for (Item *obj = probe; obj; obj = obj->prototype()) {
- const Item::PropertyMap &props = obj->properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- const QString &name = it.key();
- if (name == StringConstants::configureProperty())
- continue;
- const QScriptValue value = m_evaluator->value(probe, name);
- probeBindings += ProbeProperty(name, value);
- if (name != StringConstants::conditionProperty())
- initialProperties.insert(name, value.toVariant());
- }
- }
- ScriptEngine * const engine = m_evaluator->engine();
- QScriptValue configureScope;
- const bool condition = m_evaluator->boolValue(probe, StringConstants::conditionProperty());
- const QString &sourceCode = configureScript->sourceCode().toString();
- ProbeConstPtr resolvedProbe;
- if (parent->type() == ItemType::Project
- || productContext->name.startsWith(shadowProductPrefix())) {
- resolvedProbe = findOldProjectProbe(probeId, condition, initialProperties, sourceCode);
- } else {
- const QString &uniqueProductName = productContext->uniqueName();
- resolvedProbe
- = findOldProductProbe(uniqueProductName, condition, initialProperties, sourceCode);
- }
- if (!resolvedProbe) {
- resolvedProbe = findCurrentProbe(probe->location(), condition, initialProperties);
- if (resolvedProbe) {
- qCDebug(lcModuleLoader) << "probe results cached from current run";
- ++m_probesCachedCurrent;
- }
- } else {
- qCDebug(lcModuleLoader) << "probe results cached from earlier run";
- ++m_probesCachedOld;
- }
- std::vector<QString> importedFilesUsedInConfigure;
- if (!condition) {
- qCDebug(lcModuleLoader) << "Probe disabled; skipping";
- } else if (!resolvedProbe) {
- ++m_probesRun;
- qCDebug(lcModuleLoader) << "configure script needs to run";
- const Evaluator::FileContextScopes fileCtxScopes
- = m_evaluator->fileContextScopes(configureScript->file());
- engine->currentContext()->pushScope(fileCtxScopes.fileScope);
- engine->currentContext()->pushScope(fileCtxScopes.importScope);
- configureScope = engine->newObject();
- for (const ProbeProperty &b : qAsConst(probeBindings))
- configureScope.setProperty(b.first, b.second);
- engine->currentContext()->pushScope(configureScope);
- engine->clearRequestedProperties();
- QScriptValue sv = engine->evaluate(configureScript->sourceCodeForEvaluation());
- engine->currentContext()->popScope();
- engine->currentContext()->popScope();
- engine->currentContext()->popScope();
- engine->releaseResourcesOfScriptObjects();
- if (Q_UNLIKELY(engine->hasErrorOrException(sv)))
- throw ErrorInfo(engine->lastErrorString(sv), configureScript->location());
- importedFilesUsedInConfigure = engine->importedFilesUsedInScript();
- } else {
- importedFilesUsedInConfigure = resolvedProbe->importedFilesUsed();
- }
- QVariantMap properties;
- for (const ProbeProperty &b : qAsConst(probeBindings)) {
- QVariant newValue;
- if (resolvedProbe) {
- newValue = resolvedProbe->properties().value(b.first);
- } else {
- if (condition) {
- QScriptValue v = configureScope.property(b.first);
- m_evaluator->convertToPropertyType(probe->propertyDeclaration(
- b.first), probe->location(), v);
- if (Q_UNLIKELY(engine->hasErrorOrException(v)))
- throw ErrorInfo(engine->lastError(v));
- newValue = v.toVariant();
- } else {
- newValue = initialProperties.value(b.first);
- }
- }
- if (newValue != b.second.toVariant())
- probe->setProperty(b.first, VariantValue::create(newValue));
- if (!resolvedProbe)
- properties.insert(b.first, newValue);
- }
- if (!resolvedProbe) {
- resolvedProbe = Probe::create(probeId, probe->location(), condition,
- sourceCode, properties, initialProperties,
- importedFilesUsedInConfigure);
- m_currentProbes[probe->location()] << resolvedProbe;
- }
- productContext->info.probes << resolvedProbe;
-}
-
-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, Item *itemToDisable)
-{
- if (m_evaluator->boolValue(item, StringConstants::conditionProperty()))
- return true;
- m_disabledItems += itemToDisable ? itemToDisable : item;
- return false;
-}
-
-QStringList ModuleLoader::readExtraSearchPaths(Item *item, bool *wasSet)
-{
- QStringList result;
- const QStringList paths = m_evaluator->stringListValue(
- item, StringConstants::qbsSearchPathsProperty(), wasSet);
- const JSSourceValueConstPtr prop = item->sourceProperty(
- StringConstants::qbsSearchPathsProperty());
-
- // 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());
- for (const QString &path : paths)
- result += FileInfo::resolvePath(basePath, path);
- return result;
-}
-
-void ModuleLoader::copyProperties(const Item *sourceProject, Item *targetProject)
-{
- if (!sourceProject)
- return;
- const QList<PropertyDeclaration> builtinProjectProperties = BuiltinDeclarations::instance()
- .declarationsForType(ItemType::Project).properties();
- Set<QString> builtinProjectPropertyNames;
- for (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() == StringConstants::qbsSearchPathsProperty()
- || it.key() == StringConstants::profileProperty()
- || it.key() == StringConstants::buildDirectoryProperty()
- || it.key() == StringConstants::sourceDirectoryProperty()
- || it.key() == StringConstants::minimumQbsVersionProperty()) {
- const JSSourceValueConstPtr &v = targetProject->sourceProperty(it.key());
- QBS_ASSERT(v, continue);
- if (v->sourceCode() == StringConstants::undefinedValue())
- sourceProject->copyProperty(it.key(), targetProject);
- continue;
- }
-
- if (builtinProjectPropertyNames.contains(it.key()))
- continue;
-
- if (targetProject->hasOwnProperty(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->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 + QStringLiteral("/modules");
- for (const QString &moduleNamePart : moduleName) {
- dirPath = FileInfo::resolvePath(dirPath, moduleNamePart);
- if (!FileInfo::exists(dirPath) || !FileInfo::isFileCaseCorrect(dirPath))
- return {};
- }
- return dirPath;
-}
-
-QVariantMap ModuleLoader::moduleProviderConfig(ModuleLoader::ProductContext &product)
-{
- if (product.moduleProviderConfigRetrieved)
- return product.theModuleProviderConfig;
- const ItemValueConstPtr configItemValue
- = product.item->itemProperty(StringConstants::moduleProviders());
- if (configItemValue) {
- const std::function<void(const Item *, QualifiedId)> collectMap
- = [this, &product, &collectMap](const Item *item, QualifiedId name) {
- const Item::PropertyMap &props = item->properties();
- for (auto it = props.begin(); it != props.end(); ++it) {
- QVariant value;
- switch (it.value()->type()) {
- case Value::ItemValueType:
- collectMap(static_cast<ItemValue *>(it.value().get())->item(),
- QualifiedId(name += it.key()));
- return;
- case Value::JSSourceValueType:
- value = m_evaluator->value(item, it.key()).toVariant();
- break;
- case Value::VariantValueType:
- value = static_cast<VariantValue *>(it.value().get())->value();
- break;
- }
- QVariantMap m = product.theModuleProviderConfig.value(name.toString()).toMap();
- m.insert(it.key(), value);
- product.theModuleProviderConfig.insert(name.toString(), m);
- }
- };
- collectMap(configItemValue->item(), QualifiedId());
- }
- for (auto it = product.moduleProperties.begin(); it != product.moduleProperties.end(); ++it) {
- if (!it.key().startsWith(QStringLiteral("moduleProviders.")))
- continue;
- const QString provider = it.key().mid(QStringLiteral("moduleProviders.").size());
- const QVariantMap providerConfigFromBuildConfig = it.value().toMap();
- if (providerConfigFromBuildConfig.empty())
- continue;
- QVariantMap currentMapForProvider = product.theModuleProviderConfig.value(provider).toMap();
- for (auto propIt = providerConfigFromBuildConfig.begin();
- propIt != providerConfigFromBuildConfig.end(); ++propIt) {
- currentMapForProvider.insert(propIt.key(), propIt.value());
- }
- product.theModuleProviderConfig.insert(provider, currentMapForProvider);
- }
- product.moduleProviderConfigRetrieved = true;
- return product.theModuleProviderConfig;
-}
-
-ModuleLoader::ModuleProviderResult ModuleLoader::findModuleProvider(const QualifiedId &name,
- ModuleLoader::ProductContext &product, ModuleProviderLookup lookupType,
- const CodeLocation &dependsItemLocation)
-{
- for (const QString &path : m_reader->allSearchPaths()) {
- QString fullPath = FileInfo::resolvePath(path, QStringLiteral("module-providers"));
- switch (lookupType) {
- case ModuleProviderLookup::Regular:
- for (const QString &component : name)
- fullPath = FileInfo::resolvePath(fullPath, component);
- break;
- case ModuleProviderLookup::Fallback:
- fullPath = FileInfo::resolvePath(fullPath, QStringLiteral("__fallback"));
- break;
- }
- const QString providerFile = FileInfo::resolvePath(fullPath,
- QStringLiteral("provider.qbs"));
- if (!FileInfo::exists(providerFile)) {
- qCDebug(lcModuleLoader) << "No module provider found at" << providerFile;
- continue;
- }
- QTemporaryFile dummyItemFile;
- if (!dummyItemFile.open()) {
- throw ErrorInfo(Tr::tr("Failed to create temporary file for running module provider "
- "for dependency '%1': %2").arg(name.toString(),
- dummyItemFile.errorString()));
- }
- m_tempQbsFiles << dummyItemFile.fileName();
- qCDebug(lcModuleLoader) << "Instantiating module provider at" << providerFile;
- const QString projectBuildDir = product.project->item->variantProperty(
- StringConstants::buildDirectoryProperty())->value().toString();
- const QString searchPathBaseDir = ModuleProviderInfo::outputDirPath(projectBuildDir, name);
- const QVariant moduleConfig = moduleProviderConfig(product).value(name.toString());
- QTextStream stream(&dummyItemFile);
- stream.setCodec("UTF-8");
- stream << "import qbs.FileInfo" << endl;
- stream << "import qbs.Utilities" << endl;
- stream << "import '" << providerFile << "' as Provider" << endl;
- stream << "Provider {" << endl;
- stream << " name: " << toJSLiteral(name.toString()) << endl;
- stream << " property var config: (" << toJSLiteral(moduleConfig) << ')' << endl;
- stream << " outputBaseDir: FileInfo.joinPaths(baseDirPrefix, "
- " Utilities.getHash(JSON.stringify(config)))" << endl;
- stream << " property string baseDirPrefix: " << toJSLiteral(searchPathBaseDir) << endl;
- stream << " property stringList searchPaths: (relativeSearchPaths || [])"
- " .map(function(p) { return FileInfo.joinPaths(outputBaseDir, p); })"
- << endl;
- stream << "}" << endl;
- stream.flush();
- Item * const providerItem = loadItemFromFile(dummyItemFile.fileName(), dependsItemLocation);
- if (providerItem->type() != ItemType::ModuleProvider) {
- throw ErrorInfo(Tr::tr("File '%1' declares an item of type '%2', "
- "but '%3' was expected.")
- .arg(providerFile, providerItem->typeName(),
- BuiltinDeclarations::instance().nameForType(ItemType::ModuleProvider)));
- }
- providerItem->setParent(product.item);
- const QVariantMap configMap = moduleConfig.toMap();
- for (auto it = configMap.begin(); it != configMap.end(); ++it) {
- const PropertyDeclaration decl = providerItem->propertyDeclaration(it.key());
- if (!decl.isValid()) {
- throw ErrorInfo(Tr::tr("No such property '%1' in module provider '%2'.")
- .arg(it.key(), name.toString()));
- }
- providerItem->setProperty(it.key(), VariantValue::create(it.value()));
- }
- EvalContextSwitcher contextSwitcher(m_evaluator->engine(), EvalContext::ModuleProvider);
- const QStringList searchPaths
- = m_evaluator->stringListValue(providerItem, QStringLiteral("searchPaths"));
- const auto addToGlobalInfo = [=] {
- m_moduleProviderInfo.emplace_back(ModuleProviderInfo(name, moduleConfig.toMap(),
- searchPaths, m_parameters.dryRun()));
- };
- if (searchPaths.empty()) {
- qCDebug(lcModuleLoader) << "Module provider did run, but did not set up "
- "any modules.";
- addToGlobalInfo();
- return {true, false};
- }
- qCDebug(lcModuleLoader) << "Module provider added" << searchPaths.size()
- << "new search path(s)";
-
- // (1) is needed so the immediate new look-up works.
- // (2) is needed so the next use of SearchPathManager considers the new paths.
- // (3) is needed for the code that removes the product-specific search paths when
- // product handling is done.
- // (4) is needed for possible re-use in subsequent products and builds.
- m_reader->pushExtraSearchPaths(searchPaths); // (1)
- product.searchPaths << searchPaths; // (2)
- product.newlyAddedModuleProviderSearchPaths.push_back(searchPaths); // (3)
- addToGlobalInfo(); // (4)
- return {true, true};
- }
- return {};
-}
-
-void ModuleLoader::setScopeForDescendants(Item *item, Item *scope)
-{
- for (Item * const 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->propertyDeclaration(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())));
- }
-}
-
-void ModuleLoader::collectAllModules(Item *item, std::vector<Item::Module> *modules)
-{
- for (const Item::Module &m : item->modules()) {
- if (moduleRepresentsDisabledProduct(m))
- m.item->removeModules();
- 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->push_back(m);
- collectAllModules(m.item, modules);
- }
-}
-
-std::vector<Item::Module> ModuleLoader::allModules(Item *item)
-{
- std::vector<Item::Module> lst;
- collectAllModules(item, &lst);
- return lst;
-}
-
-bool ModuleLoader::moduleRepresentsDisabledProduct(const Item::Module &module)
-{
- if (!module.isProduct)
- return false;
- const Item *exportItem = module.item->prototype();
- while (exportItem && exportItem->type() != ItemType::Export)
- exportItem = exportItem->prototype();
- QBS_CHECK(exportItem);
- Item * const productItem = exportItem->parent();
- QBS_CHECK(productItem->type() == ItemType::Product);
- return m_disabledItems.contains(productItem) || !checkItemCondition(productItem);
-}
-
-void ModuleLoader::addProductModuleDependencies(ProductContext *productContext, const QString &name)
-{
- auto deps = productContext->productModuleDependencies.at(name);
- QList<ModuleLoaderResult::ProductInfo::Dependency> depsToAdd;
- const bool productIsMultiplexed = !productContext->multiplexConfigurationId.isEmpty();
- for (auto &dep : deps) {
- const auto productRange = m_productsByName.equal_range(dep.name);
- std::vector<const ProductContext *> dependencies;
- bool hasNonMultiplexedDependency = false;
- for (auto it = productRange.first; it != productRange.second; ++it) {
- if (!it->second->multiplexConfigurationId.isEmpty()) {
- dependencies.push_back(it->second);
- if (productIsMultiplexed && dep.profile.isEmpty())
- break;
- } else {
- hasNonMultiplexedDependency = true;
- break;
- }
- }
-
- if (hasNonMultiplexedDependency) {
- depsToAdd.push_back(dep);
- continue;
- }
-
- for (std::size_t i = 0; i < dependencies.size(); ++i) {
- const bool profileMatch = dep.profile.isEmpty()
- || dep.profile == StringConstants::star()
- || dep.profile == dependencies.at(i)->profileName;
- if (i == 0) {
- if (productIsMultiplexed && dep.profile.isEmpty()) {
- const ValuePtr &multiplexConfigIdProp = productContext->item->property(
- StringConstants::multiplexConfigurationIdProperty());
- dep.multiplexConfigurationId = std::static_pointer_cast<VariantValue>(
- multiplexConfigIdProp)->value().toString();
- depsToAdd.push_back(dep);
- break;
- } else if (profileMatch) {
- dep.multiplexConfigurationId = dependencies.at(i)->multiplexConfigurationId;
- depsToAdd.push_back(dep);
- }
- } else if (profileMatch) {
- ModuleLoaderResult::ProductInfo::Dependency newDependency = dep;
- newDependency.multiplexConfigurationId
- = dependencies.at(i)->multiplexConfigurationId;
- depsToAdd << newDependency;
- }
- }
- }
- productContext->info.usedProducts.insert(productContext->info.usedProducts.end(),
- depsToAdd.cbegin(), depsToAdd.cend());
-}
-
-static void collectProductModuleDependencies(Item *item, Set<QualifiedId> &allDeps)
-{
- for (const Item::Module &m : item->modules()) {
- if (m.isProduct && allDeps.insert(m.name).second)
- collectProductModuleDependencies(m.item, allDeps);
- }
-}
-
-void ModuleLoader::addProductModuleDependencies(ModuleLoader::ProductContext *ctx)
-{
- Set<QualifiedId> deps;
- collectProductModuleDependencies(ctx->item, deps);
- for (const QualifiedId &dep : deps)
- addProductModuleDependencies(ctx, dep.toString());
-}
-
-void ModuleLoader::addTransitiveDependencies(ProductContext *ctx)
-{
- AccumulatingTimer timer(m_parameters.logElapsedTime()
- ? &m_elapsedTimeTransitiveDependencies : nullptr);
- qCDebug(lcModuleLoader) << "addTransitiveDependencies";
-
- std::vector<Item::Module> transitiveDeps = allModules(ctx->item);
- std::sort(transitiveDeps.begin(), transitiveDeps.end());
- for (const Item::Module &m : ctx->item->modules()) {
- auto it = std::lower_bound(transitiveDeps.begin(), transitiveDeps.end(), m);
- QBS_CHECK(it != transitiveDeps.end() && it->name == m.name);
- transitiveDeps.erase(it);
- }
- for (const Item::Module &module : qAsConst(transitiveDeps)) {
- if (module.isProduct) {
- ctx->item->addModule(module);
- } else {
- Item::Module dep;
- dep.item = loadModule(ctx, nullptr, ctx->item, ctx->item->location(), QString(),
- module.name, QString(), FallbackMode::Disabled,
- module.required, &dep.isProduct, &dep.parameters);
- 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)
-{
- qCDebug(lcModuleLoader) << "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(StringConstants::nameProperty(), VariantValue::create(name));
- }
- module->setProperty(StringConstants::presentProperty(), VariantValue::falseValue());
- return module;
-}
-
-void ModuleLoader::handleProductError(const ErrorInfo &error,
- ModuleLoader::ProductContext *productContext)
-{
- const bool alreadyHadError = productContext->info.delayedError.hasError();
- if (!alreadyHadError) {
- productContext->info.delayedError.append(Tr::tr("Error while handling product '%1':")
- .arg(productContext->name),
- productContext->item->location());
- }
- if (error.isInternalError()) {
- if (alreadyHadError) {
- qCDebug(lcModuleLoader()) << "ignoring subsequent internal error" << error.toString()
- << "in product" << productContext->name;
- return;
- }
- for (const auto &kv : productContext->productModuleDependencies) {
- const auto rangeForName = m_productsByName.equal_range(kv.first);
- for (auto rangeIt = rangeForName.first; rangeIt != rangeForName.second; ++rangeIt) {
- const ProductContext * const dep = rangeIt->second;
- if (dep->info.delayedError.hasError()) {
- qCDebug(lcModuleLoader()) << "ignoring internal error" << error.toString()
- << "in product" << productContext->name
- << "assumed to be caused by erroneous dependency"
- << dep->name;
- return;
- }
- }
- }
- }
- const auto errorItems = error.items();
- for (const ErrorItem &ei : errorItems)
- productContext->info.delayedError.append(ei.description(), ei.codeLocation());
- productContext->project->result->productInfos.insert(productContext->item,
- productContext->info);
- m_disabledItems << productContext->item;
- m_erroneousProducts.insert(productContext->name);
-}
-
-static void gatherAssignedProperties(ItemValue *iv, const QualifiedId &prefix,
- QualifiedIdSet &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:
- if (iv->item()->type() == ItemType::ModulePrefix) {
- gatherAssignedProperties(std::static_pointer_cast<ItemValue>(it.value()).get(),
- QualifiedId(prefix) << it.key(), properties);
- }
- break;
- default:
- break;
- }
- }
-}
-
-QualifiedIdSet ModuleLoader::gatherModulePropertiesSetInGroup(const Item *group)
-{
- QualifiedIdSet propsSetInGroup;
- const Item::PropertyMap &props = group->properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- if (it.value()->type() == Value::ItemValueType) {
- gatherAssignedProperties(std::static_pointer_cast<ItemValue>(it.value()).get(),
- QualifiedId(it.key()), propsSetInGroup);
- }
- }
- return propsSetInGroup;
-}
-
-void ModuleLoader::markModuleTargetGroups(Item *group, const Item::Module &module)
-{
- QBS_CHECK(group->type() == ItemType::Group);
- if (m_evaluator->boolValue(group, StringConstants::filesAreTargetsProperty())) {
- group->setProperty(StringConstants::modulePropertyInternal(),
- VariantValue::create(module.name.toString()));
- }
- for (Item * const child : group->children())
- markModuleTargetGroups(child, module);
-}
-
-void ModuleLoader::copyGroupsFromModuleToProduct(const ProductContext &productContext,
- const Item::Module &module,
- const Item *modulePrototype)
-{
- for (Item * const child : modulePrototype->children()) {
- if (child->type() == ItemType::Group) {
- Item * const clonedGroup = child->clone();
- clonedGroup->setScope(productContext.scope);
- setScopeForDescendants(clonedGroup, productContext.scope);
- Item::addChild(productContext.item, clonedGroup);
- markModuleTargetGroups(clonedGroup, module);
- }
- }
-}
-
-void ModuleLoader::copyGroupsFromModulesToProduct(const ProductContext &productContext)
-{
- for (const Item::Module &module : productContext.item->modules()) {
- Item *prototype = module.item;
- bool modulePassedValidation;
- while ((modulePassedValidation = prototype->isPresentModule()) && prototype->prototype())
- prototype = prototype->prototype();
- if (modulePassedValidation)
- copyGroupsFromModuleToProduct(productContext, module, prototype);
- }
-}
-
-QString ModuleLoaderResult::ProductInfo::Dependency::uniqueName() const
-{
- return ResolvedProduct::uniqueName(name, multiplexConfigurationId);
-}
-
-QString ModuleLoader::ProductContext::uniqueName() const
-{
- return ResolvedProduct::uniqueName(name, multiplexConfigurationId);
-}
-
-} // namespace Internal
-} // namespace qbs
diff --git a/src/lib/corelib/language/moduleloader.h b/src/lib/corelib/language/moduleloader.h
deleted file mode 100644
index 2a7a89727..000000000
--- a/src/lib/corelib/language/moduleloader.h
+++ /dev/null
@@ -1,486 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "moduleproviderinfo.h"
-#include <logging/logger.h>
-#include <tools/filetime.h>
-#include <tools/qttools.h>
-#include <tools/set.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/version.h>
-
-#include <QtCore/qmap.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qvariant.h>
-
-#include <map>
-#include <memory>
-#include <unordered_map>
-#include <utility>
-#include <vector>
-
-namespace qbs {
-
-class CodeLocation;
-class Settings;
-
-namespace Internal {
-
-class Evaluator;
-class Item;
-class ItemReader;
-class ProgressObserver;
-class QualifiedId;
-
-using ModulePropertiesPerGroup = std::unordered_map<const Item *, QualifiedIdSet>;
-
-struct ModuleLoaderResult
-{
- ModuleLoaderResult()
- : itemPool(new ItemPool), root(nullptr)
- {}
-
- struct ProductInfo
- {
- struct Dependency
- {
- QString name;
- QString profile; // "*" <=> Match all profiles.
- QString multiplexConfigurationId;
- QVariantMap parameters;
- bool limitToSubProject = false;
- bool isRequired = true;
-
- QString uniqueName() const;
- };
-
- std::vector<ProbeConstPtr> probes;
- std::vector<Dependency> usedProducts;
- ModulePropertiesPerGroup modulePropertiesSetInGroups;
- ErrorInfo delayedError;
- };
-
- std::shared_ptr<ItemPool> itemPool;
- Item *root;
- QHash<Item *, ProductInfo> productInfos;
- std::vector<ProbeConstPtr> projectProbes;
- ModuleProviderInfoList moduleProviderInfo;
- Set<QString> qbsFiles;
- QVariantMap profileConfigs;
-};
-
-/*
- * Loader stage II. Responsible for
- * - loading modules and module dependencies,
- * - project references,
- * - Probe items.
- */
-class ModuleLoader
-{
-public:
- ModuleLoader(Evaluator *evaluator, Logger &logger);
- ~ModuleLoader();
-
- void setProgressObserver(ProgressObserver *progressObserver);
- void setSearchPaths(const QStringList &searchPaths);
- void setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes);
- void setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes);
- void setLastResolveTime(const FileTime &time) { m_lastResolveTime = time; }
- void setStoredProfiles(const QVariantMap &profiles);
- void setStoredModuleProviderInfo(const ModuleProviderInfoList &moduleProviderInfo);
- Evaluator *evaluator() const { return m_evaluator; }
-
- ModuleLoaderResult load(const SetupProjectParameters &parameters);
-
-private:
- class ProductSortByDependencies;
-
- class ContextBase
- {
- public:
- ContextBase()
- : item(nullptr), scope(nullptr)
- {}
-
- Item *item;
- Item *scope;
- QString name;
- };
-
- class ProjectContext;
-
- using ProductDependencies = std::vector<ModuleLoaderResult::ProductInfo::Dependency>;
-
- // This is the data we need to store at the point where a dependency is deferred
- // in order to properly resolve the dependency in pass 2.
- struct DeferredDependsContext {
- DeferredDependsContext(Item *exportingProduct, Item *parent)
- : exportingProductItem(exportingProduct), parentItem(parent) {}
- Item *exportingProductItem = nullptr;
- Item *parentItem = nullptr;
- bool operator==(const DeferredDependsContext &other) const
- {
- return exportingProductItem == other.exportingProductItem
- && parentItem == other.parentItem;
- }
- bool operator<(const DeferredDependsContext &other) const
- {
- return parentItem < other.parentItem;
- }
- };
-
- class ProductContext : public ContextBase
- {
- public:
- ProjectContext *project = nullptr;
- ModuleLoaderResult::ProductInfo info;
- QString profileName;
- QString multiplexConfigurationId;
- QVariantMap moduleProperties;
- std::map<QString, ProductDependencies> productModuleDependencies;
- std::unordered_map<const Item *, std::vector<ErrorInfo>> unknownProfilePropertyErrors;
- QStringList searchPaths;
-
- std::vector<QStringList> newlyAddedModuleProviderSearchPaths;
- Set<QualifiedId> knownModuleProviders;
- QVariantMap theModuleProviderConfig;
- bool moduleProviderConfigRetrieved = false;
-
- // The key corresponds to DeferredDependsContext.exportingProductItem, which is the
- // only value from that data structure that we still need here.
- std::unordered_map<Item *, std::vector<Item *>> deferredDependsItems;
-
- QString uniqueName() const;
- };
-
- class TopLevelProjectContext;
-
- class ProjectContext : public ContextBase
- {
- public:
- TopLevelProjectContext *topLevelProject = nullptr;
- ModuleLoaderResult *result = nullptr;
- std::vector<ProductContext> products;
- std::vector<QStringList> searchPathsStack;
- };
-
- struct ProductModuleInfo
- {
- Item *exportItem = nullptr;
- QString multiplexId;
- QVariantMap defaultParameters;
- };
-
- class TopLevelProjectContext
- {
- Q_DISABLE_COPY(TopLevelProjectContext)
- public:
- TopLevelProjectContext() = default;
- ~TopLevelProjectContext() { qDeleteAll(projects); }
-
- std::vector<ProjectContext *> projects;
- QMultiHash<QString, ProductModuleInfo> productModules;
- std::vector<ProbeConstPtr> probes;
- QString buildDirectory;
- };
-
- class DependsContext
- {
- public:
- ProductContext *product = nullptr;
- Item *exportingProductItem = nullptr;
- ProductDependencies *productDependencies = nullptr;
- };
-
- void handleTopLevelProject(ModuleLoaderResult *loadResult, Item *projectItem,
- const QString &buildDirectory, const Set<QString> &referencedFilePaths);
- void handleProject(ModuleLoaderResult *loadResult,
- TopLevelProjectContext *topLevelProjectContext, Item *projectItem,
- const Set<QString> &referencedFilePaths);
-
- using MultiplexRow = std::vector<VariantValuePtr>;
- using MultiplexTable = std::vector<MultiplexRow>;
-
- struct MultiplexInfo
- {
- std::vector<QString> properties;
- MultiplexTable table;
- bool aggregate = false;
- VariantValuePtr multiplexedType;
-
- QString toIdString(size_t row) const;
- };
-
- void dump(const MultiplexInfo &mpi);
- static MultiplexTable combine(const MultiplexTable &table, const MultiplexRow &values);
- MultiplexInfo extractMultiplexInfo(Item *productItem, Item *qbsModuleItem);
- QList<Item *> multiplexProductItem(ProductContext *dummyContext, Item *productItem);
- void normalizeDependencies(ProductContext *product,
- const DeferredDependsContext &dependsContext);
- void adjustDependenciesForMultiplexing(const TopLevelProjectContext &tlp);
- void adjustDependenciesForMultiplexing(const ProductContext &product);
- void adjustDependenciesForMultiplexing(const ProductContext &product, Item *dependsItem);
-
- void prepareProduct(ProjectContext *projectContext, Item *productItem);
- void setupProductDependencies(ProductContext *productContext,
- const Set<DeferredDependsContext> &deferredDependsContext);
- void handleProduct(ProductContext *productContext);
- void checkDependencyParameterDeclarations(const ProductContext *productContext) const;
- void handleModuleSetupError(ProductContext *productContext, const Item::Module &module,
- const ErrorInfo &error);
- void initProductProperties(const ProductContext &product);
- void handleSubProject(ProjectContext *projectContext, Item *projectItem,
- const Set<QString> &referencedFilePaths);
- QList<Item *> loadReferencedFile(const QString &relativePath,
- const CodeLocation &referencingLocation,
- const Set<QString> &referencedFilePaths,
- ProductContext &dummyContext);
- void handleAllPropertyOptionsItems(Item *item);
- void handlePropertyOptions(Item *optionsItem);
-
- using ModuleDependencies = QHash<QualifiedId, QualifiedIdSet>;
- void setupReverseModuleDependencies(const Item::Module &module, ModuleDependencies &deps,
- QualifiedIdSet &seenModules);
- ModuleDependencies setupReverseModuleDependencies(const Item *product);
- void handleGroup(ProductContext *productContext, Item *groupItem,
- const ModuleDependencies &reverseDepencencies);
- void propagateModulesFromParent(ProductContext *productContext, Item *groupItem,
- const ModuleDependencies &reverseDepencencies);
- void adjustDefiningItemsInGroupModuleInstances(const Item::Module &module,
- const Item::Modules &dependentModules);
-
- bool mergeExportItems(const ProductContext &productContext);
- void resolveDependencies(DependsContext *dependsContext, Item *item,
- ProductContext *productContext = nullptr);
- class ItemModuleList;
- void resolveDependsItem(DependsContext *dependsContext, Item *parentItem, Item *dependsItem,
- ItemModuleList *moduleResults, ProductDependencies *productResults);
- void forwardParameterDeclarations(const Item *dependsItem, const ItemModuleList &modules);
- void forwardParameterDeclarations(const QualifiedId &moduleName, Item *item,
- const ItemModuleList &modules);
- void resolveParameterDeclarations(const Item *module);
- QVariantMap extractParameters(Item *dependsItem) const;
- Item *moduleInstanceItem(Item *containerItem, const QualifiedId &moduleName);
- static ProductModuleInfo *productModule(ProductContext *productContext, const QString &name,
- const QString &multiplexId, bool &productNameMatch);
- static ProductContext *product(ProjectContext *projectContext, const QString &name);
- static ProductContext *product(TopLevelProjectContext *tlpContext, const QString &name);
-
- enum class FallbackMode { Enabled, Disabled };
- Item *loadModule(ProductContext *productContext, Item *exportingProductItem, Item *item,
- const CodeLocation &dependsItemLocation, const QString &moduleId,
- const QualifiedId &moduleName, const QString &multiplexId, FallbackMode fallbackMode,
- bool isRequired, bool *isProductDependency, QVariantMap *defaultParameters);
- Item *searchAndLoadModuleFile(ProductContext *productContext,
- const CodeLocation &dependsItemLocation, const QualifiedId &moduleName,
- FallbackMode fallbackMode, bool isRequired, Item *moduleInstance);
- Item *loadModuleFile(ProductContext *productContext, const QString &fullModuleName,
- bool isBaseModule, const QString &filePath, bool *triedToLoad, Item *moduleInstance);
- Item *getModulePrototype(ProductContext *productContext, const QString &fullModuleName,
- const QString &filePath, bool *triedToLoad);
- Item::Module loadBaseModule(ProductContext *productContext, Item *item);
- void setupBaseModulePrototype(Item *prototype);
- template <typename T, typename F>
- T callWithTemporaryBaseModule(ProductContext *productContext, const F &func);
- void instantiateModule(ProductContext *productContext, Item *exportingProductItem,
- Item *instanceScope, Item *moduleInstance, Item *modulePrototype,
- const QualifiedId &moduleName, ProductModuleInfo *productModuleInfo);
- void createChildInstances(Item *instance, Item *prototype,
- QHash<Item *, Item *> *prototypeInstanceMap) const;
- void resolveProbes(ProductContext *productContext, Item *item);
- void resolveProbe(ProductContext *productContext, Item *parent, Item *probe);
- void checkCancelation() const;
- bool checkItemCondition(Item *item, Item *itemToDisable = nullptr);
- QStringList readExtraSearchPaths(Item *item, bool *wasSet = nullptr);
- void copyProperties(const Item *sourceProject, Item *targetProject);
- Item *wrapInProjectIfNecessary(Item *item);
- static QString findExistingModulePath(const QString &searchPath,
- const QualifiedId &moduleName);
-
- enum class ModuleProviderLookup { Regular, Fallback };
- struct ModuleProviderResult
- {
- ModuleProviderResult() = default;
- ModuleProviderResult(bool ran, bool added)
- : providerFound(ran), providerAddedSearchPaths(added) {}
- bool providerFound = false;
- bool providerAddedSearchPaths = false;
- };
- ModuleProviderResult findModuleProvider(const QualifiedId &name, ProductContext &product,
- ModuleProviderLookup lookupType, const CodeLocation &dependsItemLocation);
- QVariantMap moduleProviderConfig(ProductContext &product);
-
- static void setScopeForDescendants(Item *item, Item *scope);
- void overrideItemProperties(Item *item, const QString &buildConfigKey,
- const QVariantMap &buildConfig);
- void addProductModuleDependencies(ProductContext *ctx, const QString &name);
- void addProductModuleDependencies(ProductContext *ctx);
- void addTransitiveDependencies(ProductContext *ctx);
- Item *createNonPresentModule(const QString &name, const QString &reason, Item *module);
- void copyGroupsFromModuleToProduct(const ProductContext &productContext,
- const Item::Module &module, const Item *modulePrototype);
- void copyGroupsFromModulesToProduct(const ProductContext &productContext);
- void markModuleTargetGroups(Item *group, const Item::Module &module);
- bool checkExportItemCondition(Item *exportItem, const ProductContext &productContext);
- ProbeConstPtr findOldProjectProbe(const QString &globalId, bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const;
- ProbeConstPtr findOldProductProbe(const QString &productName, bool condition,
- const QVariantMap &initialProperties,
- const QString &sourceCode) const;
- ProbeConstPtr findCurrentProbe(const CodeLocation &location, bool condition,
- const QVariantMap &initialProperties) const;
-
- enum class CompareScript { No, Yes };
- bool probeMatches(const ProbeConstPtr &probe, bool condition,
- const QVariantMap &initialProperties, const QString &configureScript,
- CompareScript compareScript) const;
-
- void printProfilingInfo();
- void handleProductError(const ErrorInfo &error, ProductContext *productContext);
- QualifiedIdSet gatherModulePropertiesSetInGroup(const Item *group);
- Item *loadItemFromFile(const QString &filePath, const CodeLocation &referencingLocation);
- void collectProductsByName(const TopLevelProjectContext &topLevelProject);
- void collectProductsByType(const TopLevelProjectContext &topLevelProject);
-
- void handleProfileItems(Item *item, ProjectContext *projectContext);
- std::vector<Item *> collectProfileItems(Item *item, ProjectContext *projectContext);
- void evaluateProfileValues(const QualifiedId &namePrefix, Item *item, Item *profileItem,
- QVariantMap &values);
- void handleProfile(Item *profileItem);
- void collectNameFromOverride(const QString &overrideString);
- void checkProjectNamesInOverrides(const TopLevelProjectContext &tlp);
- void checkProductNamesInOverrides();
- void setSearchPathsForProduct(ProductContext *product);
-
- Item::Modules modulesSortedByDependency(const Item *productItem);
- void createSortedModuleList(const Item::Module &parentModule, Item::Modules &modules);
- void collectAllModules(Item *item, std::vector<Item::Module> *modules);
- std::vector<Item::Module> allModules(Item *item);
- bool moduleRepresentsDisabledProduct(const Item::Module &module);
-
- using ShadowProductInfo = std::pair<bool, QString>;
- ShadowProductInfo getShadowProductInfo(const ProductContext &product) const;
-
- ItemPool *m_pool;
- Logger &m_logger;
- ProgressObserver *m_progressObserver;
- ItemReader *m_reader;
- Evaluator *m_evaluator;
- QMap<QString, QStringList> m_moduleDirListCache;
-
- // The keys are file paths, the values are module prototype items accompanied by a profile.
- std::unordered_map<QString, std::vector<std::pair<Item *, QString>>> m_modulePrototypes;
-
- // The keys are module prototypes and products, the values specify whether the module's
- // condition is true for that product.
- QHash<std::pair<Item *, ProductContext *>, bool> m_modulePrototypeEnabledInfo;
-
- QHash<const Item *, Item::PropertyDeclarationMap> m_parameterDeclarations;
- Set<Item *> m_disabledItems;
- std::vector<bool> m_requiredChain;
-
- struct DependsChainEntry
- {
- DependsChainEntry(const QualifiedId &name, const CodeLocation &location)
- : name(name), location(location)
- {
- }
-
- QualifiedId name;
- CodeLocation location;
- bool isProduct = false;
- };
- class DependsChainManager;
- std::vector<DependsChainEntry> m_dependsChain;
-
- QHash<QString, QList<ProbeConstPtr>> m_oldProjectProbes;
- QHash<QString, std::vector<ProbeConstPtr>> m_oldProductProbes;
- FileTime m_lastResolveTime;
- QHash<CodeLocation, QList<ProbeConstPtr>> m_currentProbes;
- QVariantMap m_storedProfiles;
- QVariantMap m_localProfiles;
- std::multimap<QString, const ProductContext *> m_productsByName;
- std::multimap<FileTag, const ProductContext *> m_productsByType;
-
- std::unordered_map<ProductContext *, Set<DeferredDependsContext>> m_productsWithDeferredDependsItems;
- Set<Item *> m_exportsWithDeferredDependsItems;
-
- ModuleProviderInfoList m_moduleProviderInfo;
- Set<QString> m_tempQbsFiles;
-
- SetupProjectParameters m_parameters;
- std::unique_ptr<Settings> m_settings;
- Version m_qbsVersion;
- Item *m_tempScopeItem = nullptr;
-
- qint64 m_elapsedTimeProbes = 0;
- qint64 m_elapsedTimePrepareProducts = 0;
- qint64 m_elapsedTimeProductDependencies = 0;
- qint64 m_elapsedTimeTransitiveDependencies = 0;
- qint64 m_elapsedTimeHandleProducts = 0;
- qint64 m_elapsedTimePropertyChecking = 0;
- quint64 m_probesEncountered = 0;
- quint64 m_probesRun = 0;
- quint64 m_probesCachedCurrent = 0;
- quint64 m_probesCachedOld = 0;
- Set<QString> m_projectNamesUsedInOverrides;
- Set<QString> m_productNamesUsedInOverrides;
- Set<QString> m_disabledProjects;
- Set<QString> m_erroneousProducts;
-
- int m_dependencyResolvingPass = 0;
-};
-
-} // 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
deleted file mode 100644
index 053e90d53..000000000
--- a/src/lib/corelib/language/modulemerger.cpp
+++ /dev/null
@@ -1,292 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <logging/translator.h>
-#include <tools/qbsassert.h>
-#include <tools/qttools.h>
-#include <tools/stlutils.h>
-#include <tools/stringconstants.h>
-
-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_isBaseModule(moduleToMerge.name.first() == StringConstants::qbsModule())
- , m_versionRange(moduleToMerge.versionRange)
-{
- QBS_CHECK(moduleToMerge.item->type() == ItemType::ModuleInstance);
-}
-
-void ModuleMerger::replaceItemInValues(QualifiedId moduleName, Item *containerItem, Item *toReplace)
-{
- QBS_CHECK(!moduleName.empty());
- QBS_CHECK(containerItem != m_mergedModule.item);
- const QString moduleNamePrefix = moduleName.takeFirst();
- const Item::PropertyMap &properties = containerItem->properties();
- for (auto it = properties.begin(); it != properties.end(); ++it) {
- if (it.key() != moduleNamePrefix)
- continue;
- Value * const val = it.value().get();
- QBS_CHECK(val);
- QBS_CHECK(val->type() == Value::ItemValueType);
- const auto itemVal = static_cast<ItemValue *>(val);
- if (moduleName.empty()) {
- 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.
- for (const Item::Module &module : toReplace->modules()) {
- for (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);
-
- for (Item *moduleInstanceContainer : qAsConst(m_moduleInstanceContainers)) {
- Item::Modules modules;
- for (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 = nullptr;
- size_t numberOfOutprops = m.item->modules().size();
- for (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;
- }
- }
-
- std::vector<Item::PropertyMap> outprops;
- outprops.reserve(numberOfOutprops);
- for (const Item::Module &dep : m.item->modules()) {
- if (dep.item != moduleInstance)
- outprops.push_back(dfs(dep, props));
- }
-
- if (!outprops.empty()) {
- props = outprops.front();
- for (size_t i = 1; i < outprops.size(); ++i)
- mergeOutProps(&props, outprops.at(i));
- }
-
- if (moduleInstance)
- insertProperties(&props, moduleInstance, ListProperties);
-
- const bool isNonPresentModule = m.item->type() != ItemType::Product
- && !m.item->isPresentModule();
- return isNonPresentModule ? Item::PropertyMap() : 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;
- }
- if (v->type() != Value::JSSourceValueType)
- continue;
- if (it.value()->type() != Value::JSSourceValueType)
- continue;
- // possible conflict
- const JSSourceValuePtr dstVal = std::static_pointer_cast<JSSourceValue>(v);
- JSSourceValuePtr srcVal = std::static_pointer_cast<JSSourceValue>(it.value());
-
- 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)
-{
- Set<const Item *> &seenInstances = type == ScalarProperties
- ? m_seenInstancesTopDown : m_seenInstancesBottomUp;
- Item *origSrcItem = srcItem;
- do {
- if (seenInstances.insert(srcItem).second) {
- for (Item::PropertyMap::const_iterator it = srcItem->properties().constBegin();
- it != srcItem->properties().constEnd(); ++it) {
- const ValuePtr &srcVal = it.value();
- if (srcVal->type() == Value::ItemValueType)
- continue;
- if (it.key() == StringConstants::qbsSourceDirPropertyInternal())
- continue;
- const PropertyDeclaration srcDecl = srcItem->propertyDeclaration(it.key());
- if (!srcDecl.isValid() || srcDecl.isScalar() != (type == ScalarProperties))
- continue;
-
- // Scalar variant values could stem from product multiplexing, in which case
- // the merged qbs module instance needs to get that value.
- if (srcVal->type() == Value::VariantValueType
- && (!srcDecl.isScalar() || !m_isBaseModule)) {
- 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 = Item::create(moduleProto->pool(), ItemType::Module);
- m_clonedModulePrototype->setScope(m_mergedModule.item);
- m_clonedModulePrototype->setLocation(moduleProto->location());
- moduleProto->copyProperty(StringConstants::nameProperty(), m_clonedModulePrototype);
- }
- const ValuePtr &protoValue = moduleProto->property(propertyName);
- QBS_CHECK(protoValue);
- const ValuePtr clonedValue = protoValue->clone();
- lastInNextChain(sv)->setNext(clonedValue);
- clonedValue->setDefiningItem(m_clonedModulePrototype);
- m_clonedModulePrototype->setPropertyDeclaration(propertyName, pd);
- m_clonedModulePrototype->setProperty(propertyName, 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
deleted file mode 100644
index 3cc3ba08a..000000000
--- a/src/lib/corelib/language/modulemerger.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 <logging/logger.h>
-#include <tools/set.h>
-#include <tools/version.h>
-
-#include <QtCore/qhash.h>
-
-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<ValuePtr, PropertyDeclaration> m_decls;
- Set<const Item *> m_seenInstancesTopDown;
- Set<const Item *> m_seenInstancesBottomUp;
- Set<Item *> m_moduleInstanceContainers;
- bool m_required;
- const bool m_isBaseModule;
- VersionRange m_versionRange;
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // QBS_MODULEMERGER_H
diff --git a/src/lib/corelib/language/moduleproviderinfo.h b/src/lib/corelib/language/moduleproviderinfo.h
index fef9d9765..c35ed220a 100644
--- a/src/lib/corelib/language/moduleproviderinfo.h
+++ b/src/lib/corelib/language/moduleproviderinfo.h
@@ -55,9 +55,12 @@ class ModuleProviderInfo
{
public:
ModuleProviderInfo() = default;
- ModuleProviderInfo(const QualifiedId &name, const QVariantMap &config,
- const QStringList &searchPaths, bool transientOutput)
- : name(name), config(config), searchPaths(searchPaths), transientOutput(transientOutput)
+ ModuleProviderInfo(QualifiedId name, QVariantMap config,
+ QStringList searchPaths, bool transientOutput)
+ : name(std::move(name))
+ , config(std::move(config))
+ , searchPaths(std::move(searchPaths))
+ , transientOutput(transientOutput)
{}
static QString outputBaseDirName() { return QStringLiteral("genmodules"); }
@@ -73,16 +76,39 @@ public:
template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool)
{
- pool.serializationOp<opType>(reinterpret_cast<QStringList &>(name), config, searchPaths);
+ pool.serializationOp<opType>(
+ reinterpret_cast<QStringList &>(name), config, providerFile, searchPaths);
}
QualifiedId name;
QVariantMap config;
+ QString providerFile;
+ bool isEager{true};
QStringList searchPaths;
+ QHash<QString, QStringList> searchPathsByModule;
bool transientOutput = false; // Not to be serialized.
};
-using ModuleProviderInfoList = std::vector<ModuleProviderInfo>;
+using ModuleProvidersCacheKey = std::tuple<
+ QString /*name*/,
+ QString /*moduleName*/,
+ QVariantMap /*config*/,
+ QVariantMap /*qbsModule*/,
+ int /*lookup*/
+>;
+using ModuleProvidersCache = QHash<ModuleProvidersCacheKey, ModuleProviderInfo>;
+
+// Persistent info stored between sessions
+class StoredModuleProviderInfo
+{
+public:
+ ModuleProvidersCache providers;
+
+ template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool)
+ {
+ pool.serializationOp<opType>(providers);
+ }
+};
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/preparescriptobserver.cpp b/src/lib/corelib/language/preparescriptobserver.cpp
index 632cbfb51..11536e74d 100644
--- a/src/lib/corelib/language/preparescriptobserver.cpp
+++ b/src/lib/corelib/language/preparescriptobserver.cpp
@@ -48,8 +48,6 @@
#include <tools/stlutils.h>
#include <tools/stringconstants.h>
-#include <QtScript/qscriptvalue.h>
-
namespace qbs {
namespace Internal {
@@ -58,22 +56,23 @@ PrepareScriptObserver::PrepareScriptObserver(ScriptEngine *engine, UnobserveMode
{
}
-void PrepareScriptObserver::onPropertyRead(const QScriptValue &object, const QString &name,
- const QScriptValue &value)
+void PrepareScriptObserver::onPropertyRead(const JSValue &object, const QString &name,
+ const JSValue &value)
{
- const auto objectId = object.objectId();
+ JSContext * const ctx = engine()->context();
+ const auto objectId = jsObjectId(object);
const auto projectIt = m_projectObjectIds.find(objectId);
if (projectIt != m_projectObjectIds.cend()) {
engine()->addPropertyRequestedInScript(
- Property(projectIt->second, QString(), name, value.toVariant(),
+ Property(projectIt->second, QString(), name, getJsVariant(ctx, value),
Property::PropertyInProject));
return;
}
if (m_importIds.contains(objectId)) {
- engine()->addImportRequestedInScript(object.objectId());
+ engine()->addImportRequestedInScript(jsObjectId(object));
return;
}
- const auto exportsIt = m_exportsObjectIds.find(value.objectId());
+ const auto exportsIt = m_exportsObjectIds.find(jsObjectId(object));
if (exportsIt != m_exportsObjectIds.cend()) {
engine()->addRequestedExport(exportsIt->second);
return;
@@ -81,13 +80,14 @@ void PrepareScriptObserver::onPropertyRead(const QScriptValue &object, const QSt
const auto it = m_parameterObjects.find(objectId);
if (it != m_parameterObjects.cend()) {
engine()->addPropertyRequestedInScript(
- Property(it->second.first, it->second.second, name, value.toVariant(),
+ Property(it->second.first, it->second.second, name, getJsVariant(ctx, value),
Property::PropertyInParameters));
}
if (name == StringConstants::fileTagsProperty() && m_artifactIds.contains(objectId)) {
- const Artifact * const artifact = attachedPointer<Artifact>(object);
+ const Artifact * const artifact = attachedPointer<Artifact>(object,
+ engine()->dataWithPtrClass());
QBS_CHECK(artifact);
- const Property p(artifact->product->uniqueName(), QString(), name, value.toVariant(),
+ const Property p(artifact->product->uniqueName(), QString(), name, getJsVariant(ctx, value),
Property::PropertyInArtifact);
engine()->addPropertyRequestedFromArtifact(artifact, p);
}
diff --git a/src/lib/corelib/language/preparescriptobserver.h b/src/lib/corelib/language/preparescriptobserver.h
index 36e395efc..5dc54cbb6 100644
--- a/src/lib/corelib/language/preparescriptobserver.h
+++ b/src/lib/corelib/language/preparescriptobserver.h
@@ -44,6 +44,8 @@
#include <tools/set.h>
+#include <quickjs.h>
+
#include <QtCore/qstring.h>
#include <unordered_map>
@@ -69,7 +71,7 @@ public:
}
void addArtifactId(qint64 artifactId) { m_artifactIds.insert(artifactId); }
- bool addImportId(qint64 importId) { return m_importIds.insert(importId).second; }
+ bool addImportId(quintptr importId) { return m_importIds.insert(importId).second; }
void clearImportIds() { m_importIds.clear(); }
void addParameterObjectId(qint64 id, const QString &productName, const QString &depName,
const QualifiedId &moduleName)
@@ -79,14 +81,13 @@ public:
m_parameterObjects.insert(std::make_pair(id, value));
}
-private:
- void onPropertyRead(const QScriptValue &object, const QString &name,
- const QScriptValue &value) override;
+ void onPropertyRead(const JSValue &object, const QString &name, const JSValue &value) override;
+private:
std::unordered_map<qint64, QString> m_projectObjectIds;
std::unordered_map<qint64, std::pair<QString, QString>> m_parameterObjects;
std::unordered_map<qint64, const ResolvedProduct *> m_exportsObjectIds;
- Set<qint64> m_importIds;
+ Set<quintptr> m_importIds;
Set<qint64> m_artifactIds;
};
diff --git a/src/lib/corelib/language/projectresolver.cpp b/src/lib/corelib/language/projectresolver.cpp
deleted file mode 100644
index d5b84af16..000000000
--- a/src/lib/corelib/language/projectresolver.cpp
+++ /dev/null
@@ -1,1875 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "builtindeclarations.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 <jsextensions/jsextensions.h>
-#include <jsextensions/moduleproperties.h>
-#include <logging/categories.h>
-#include <logging/translator.h>
-#include <tools/error.h>
-#include <tools/fileinfo.h>
-#include <tools/joblimits.h>
-#include <tools/jsliterals.h>
-#include <tools/profiling.h>
-#include <tools/progressobserver.h>
-#include <tools/scripttools.h>
-#include <tools/qbsassert.h>
-#include <tools/qttools.h>
-#include <tools/setupprojectparameters.h>
-#include <tools/stlutils.h>
-#include <tools/stringconstants.h>
-
-#include <QtCore/qdir.h>
-#include <QtCore/qregexp.h>
-
-#include <algorithm>
-#include <memory>
-#include <queue>
-
-namespace qbs {
-namespace Internal {
-
-extern bool debugProperties;
-
-static const FileTag unknownFileTag()
-{
- static const FileTag tag("unknown-file-tag");
- return tag;
-}
-
-struct ProjectResolver::ProjectContext
-{
- ProjectContext *parentContext = nullptr;
- ResolvedProjectPtr project;
- std::vector<FileTaggerConstPtr> fileTaggers;
- std::vector<RulePtr> rules;
- JobLimits jobLimits;
- ResolvedModulePtr dummyModule;
-};
-
-struct ProjectResolver::ProductContext
-{
- ResolvedProductPtr product;
- QString buildDirectory;
- Item *item = nullptr;
- using ArtifactPropertiesInfo = std::pair<ArtifactPropertiesPtr, std::vector<CodeLocation>>;
- QHash<QStringList, ArtifactPropertiesInfo> artifactPropertiesPerFilter;
- ProjectResolver::FileLocations sourceArtifactLocations;
- GroupConstPtr currentGroup;
-};
-
-struct ProjectResolver::ModuleContext
-{
- ResolvedModulePtr module;
- JobLimits jobLimits;
-};
-
-class CancelException { };
-
-
-ProjectResolver::ProjectResolver(Evaluator *evaluator, ModuleLoaderResult loadResult,
- SetupProjectParameters setupParameters, Logger &logger)
- : m_evaluator(evaluator)
- , m_logger(logger)
- , m_engine(m_evaluator->engine())
- , m_progressObserver(nullptr)
- , m_setupParams(std::move(setupParameters))
- , m_loadResult(std::move(loadResult))
-{
- QBS_CHECK(FileInfo::isAbsolute(m_setupParams.buildRoot()));
-}
-
-ProjectResolver::~ProjectResolver() = default;
-
-void ProjectResolver::setProgressObserver(ProgressObserver *observer)
-{
- m_progressObserver = observer;
-}
-
-static void checkForDuplicateProductNames(const TopLevelProjectConstPtr &project)
-{
- const std::vector<ResolvedProductPtr> allProducts = project->allProducts();
- for (size_t i = 0; i < allProducts.size(); ++i) {
- const ResolvedProductConstPtr product1 = allProducts.at(i);
- const QString productName = product1->uniqueName();
- for (size_t j = i + 1; j < allProducts.size(); ++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());
- qCDebug(lcProjectResolver) << "resolving" << m_loadResult.root->file()->filePath();
-
- m_productContext = nullptr;
- m_moduleContext = nullptr;
- m_elapsedTimeModPropEval = m_elapsedTimeAllPropEval = m_elapsedTimeGroups = 0;
- TopLevelProjectPtr tlp;
- try {
- tlp = resolveTopLevelProject();
- printProfilingInfo();
- } catch (const CancelException &) {
- throw ErrorInfo(Tr::tr("Project resolving canceled for configuration '%1'.")
- .arg(TopLevelProject::deriveId(m_setupParams.finalBuildConfigurationTree())));
- }
- return tlp;
-}
-
-void ProjectResolver::checkCancelation() const
-{
- if (m_progressObserver && m_progressObserver->canceled())
- throw CancelException();
-}
-
-QString ProjectResolver::verbatimValue(const ValueConstPtr &value, bool *propertyWasSet) const
-{
- QString result;
- if (value && value->type() == Value::JSSourceValueType) {
- const JSSourceValueConstPtr sourceValue = std::static_pointer_cast<const JSSourceValue>(
- value);
- result = sourceCodeForEvaluation(sourceValue);
- if (propertyWasSet)
- *propertyWasSet = !sourceValue->isBuiltinDefaultValue();
- } 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)
-{
- Set<QString> subProjectNames;
- Set<ResolvedProjectPtr> projectsInNeedOfNameChange;
- for (const ResolvedProjectPtr &p : qAsConst(parentProject->subProjects)) {
- if (!subProjectNames.insert(p->name).second)
- projectsInNeedOfNameChange << p;
- makeSubProjectNamesUniqe(p);
- }
- while (!projectsInNeedOfNameChange.empty()) {
- auto it = projectsInNeedOfNameChange.begin();
- while (it != projectsInNeedOfNameChange.end()) {
- const ResolvedProjectPtr p = *it;
- p->name += QLatin1Char('_');
- if (subProjectNames.insert(p->name).second) {
- it = projectsInNeedOfNameChange.erase(it);
- } else {
- ++it;
- }
- }
- }
-}
-
-TopLevelProjectPtr ProjectResolver::resolveTopLevelProject()
-{
- if (m_progressObserver)
- m_progressObserver->setMaximum(m_loadResult.productInfos.size());
- 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;
- project->probes = m_loadResult.projectProbes;
- project->moduleProviderInfo = m_loadResult.moduleProviderInfo;
- ProjectContext projectContext;
- projectContext.project = project;
-
- resolveProject(m_loadResult.root, &projectContext);
- ErrorInfo accumulatedErrors;
- for (const ErrorInfo &e : m_queuedErrors)
- appendError(accumulatedErrors, e);
- if (accumulatedErrors.hasError())
- throw accumulatedErrors;
-
- project->setBuildConfiguration(m_setupParams.finalBuildConfigurationTree());
- project->overriddenValues = m_setupParams.overriddenValues();
- 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);
- collectExportedProductDependencies();
- checkForDuplicateProductNames(project);
-
- for (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".
- for (const SourceArtifactPtr &artifact : product->allFiles()) {
- if (artifact->properties->qbsPropertyValue(StringConstants::installProperty()).toBool())
- artifact->fileTags += "installable";
- }
- }
- project->warningsEncountered = m_logger.warnings();
- return project;
-}
-
-void ProjectResolver::resolveProject(Item *item, ProjectContext *projectContext)
-{
- checkCancelation();
-
- if (projectContext->parentContext)
- projectContext->project->enabled = projectContext->parentContext->project->enabled;
- projectContext->project->location = item->location();
- try {
- resolveProjectFully(item, projectContext);
- } catch (const ErrorInfo &error) {
- if (!projectContext->project->enabled) {
- qCDebug(lcProjectResolver) << "error resolving project"
- << projectContext->project->location << error.toString();
- return;
- }
- if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict)
- throw;
- m_logger.printWarning(error);
- }
-}
-
-void ProjectResolver::resolveProjectFully(Item *item, ProjectResolver::ProjectContext *projectContext)
-{
- projectContext->project->enabled = projectContext->project->enabled
- && m_evaluator->boolValue(item, StringConstants::conditionProperty());
- projectContext->project->name = m_evaluator->stringValue(item, StringConstants::nameProperty());
- if (projectContext->project->name.isEmpty())
- projectContext->project->name = FileInfo::baseName(item->location().filePath()); // FIXME: Must also be changed in item?
- QVariantMap projectProperties;
- if (!projectContext->project->enabled) {
- projectProperties.insert(StringConstants::profileProperty(),
- m_evaluator->stringValue(item,
- StringConstants::profileProperty()));
- 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::Probe, &ProjectResolver::ignoreItem },
- { ItemType::FileTagger, &ProjectResolver::resolveFileTagger },
- { ItemType::JobLimit, &ProjectResolver::resolveJobLimit },
- { ItemType::Rule, &ProjectResolver::resolveRule },
- { ItemType::PropertyOptions, &ProjectResolver::ignoreItem }
- };
-
- for (Item * const child : item->children()) {
- try {
- callItemFunction(mapping, child, projectContext);
- } catch (const ErrorInfo &e) {
- m_queuedErrors.push_back(e);
- }
- }
-
- for (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, StringConstants::nameProperty());
- }
-}
-
-class ProjectResolver::ProductContextSwitcher
-{
-public:
- ProductContextSwitcher(ProjectResolver *resolver, ProductContext *newContext,
- ProgressObserver *progressObserver)
- : m_resolver(resolver), m_progressObserver(progressObserver)
- {
- QBS_CHECK(!m_resolver->m_productContext);
- m_resolver->m_productContext = newContext;
- }
-
- ~ProductContextSwitcher()
- {
- if (m_progressObserver)
- m_progressObserver->incrementProgressValue();
- m_resolver->m_productContext = nullptr;
- }
-
-private:
- ProjectResolver * const m_resolver;
- ProgressObserver * const m_progressObserver;
-};
-
-void ProjectResolver::resolveProduct(Item *item, ProjectContext *projectContext)
-{
- checkCancelation();
- m_evaluator->clearPropertyDependencies();
- ProductContext productContext;
- productContext.item = item;
- ResolvedProductPtr product = ResolvedProduct::create();
- product->enabled = projectContext->project->enabled;
- product->moduleProperties = PropertyMapInternal::create();
- product->project = projectContext->project;
- productContext.product = product;
- product->location = item->location();
- ProductContextSwitcher contextSwitcher(this, &productContext, m_progressObserver);
- try {
- resolveProductFully(item, projectContext);
- } catch (const ErrorInfo &e) {
- QString mainErrorString = !product->name.isEmpty()
- ? Tr::tr("Error while handling product '%1':").arg(product->name)
- : Tr::tr("Error while handling product:");
- ErrorInfo fullError(mainErrorString, item->location());
- appendError(fullError, e);
- if (!product->enabled) {
- qCDebug(lcProjectResolver) << fullError.toString();
- return;
- }
- if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict)
- throw fullError;
- m_logger.printWarning(fullError);
- m_logger.printWarning(ErrorInfo(Tr::tr("Product '%1' had errors and was disabled.")
- .arg(product->name), item->location()));
- product->enabled = false;
- }
-}
-
-void ProjectResolver::resolveProductFully(Item *item, ProjectContext *projectContext)
-{
- const ResolvedProductPtr product = m_productContext->product;
- m_productItemMap.insert(product, item);
- projectContext->project->products.push_back(product);
- product->name = m_evaluator->stringValue(item, StringConstants::nameProperty());
-
- // product->buildDirectory() isn't valid yet, because the productProperties map is not ready.
- m_productContext->buildDirectory
- = m_evaluator->stringValue(item, StringConstants::buildDirectoryProperty());
- product->multiplexConfigurationId
- = m_evaluator->stringValue(item, StringConstants::multiplexConfigurationIdProperty());
- qCDebug(lcProjectResolver) << "resolveProduct" << product->uniqueName();
- m_productsByName.insert(product->uniqueName(), product);
- product->enabled = product->enabled
- && m_evaluator->boolValue(item, StringConstants::conditionProperty());
- ModuleLoaderResult::ProductInfo &pi = m_loadResult.productInfos[item];
- if (pi.delayedError.hasError()) {
- ErrorInfo errorInfo;
-
- // First item is "main error", gets prepended again in the catch clause.
- const QList<ErrorItem> &items = pi.delayedError.items();
- for (int i = 1; i < items.size(); ++i)
- errorInfo.append(items.at(i));
-
- pi.delayedError.clear();
- throw errorInfo;
- }
- gatherProductTypes(product.get(), item);
- product->targetName = m_evaluator->stringValue(item, StringConstants::targetNameProperty());
- product->sourceDirectory = m_evaluator->stringValue(
- item, StringConstants::sourceDirectoryProperty());
- product->destinationDirectory = m_evaluator->stringValue(
- item, StringConstants::destinationDirProperty());
-
- if (product->destinationDirectory.isEmpty()) {
- product->destinationDirectory = m_productContext->buildDirectory;
- } else {
- product->destinationDirectory = FileInfo::resolvePath(
- product->topLevelProject()->buildDirectory,
- product->destinationDirectory);
- }
- product->probes = pi.probes;
- createProductConfig(product.get());
- product->productProperties.insert(StringConstants::destinationDirProperty(),
- product->destinationDirectory);
- ModuleProperties::init(m_evaluator->scriptValue(item), product.get());
-
- QList<Item *> subItems = item->children();
- const ValuePtr filesProperty = item->property(StringConstants::filesProperty());
- if (filesProperty) {
- Item *fakeGroup = Item::create(item->pool(), ItemType::Group);
- fakeGroup->setFile(item->file());
- fakeGroup->setLocation(item->location());
- fakeGroup->setScope(item);
- fakeGroup->setProperty(StringConstants::nameProperty(), VariantValue::create(product->name));
- fakeGroup->setProperty(StringConstants::filesProperty(), filesProperty);
- fakeGroup->setProperty(StringConstants::excludeFilesProperty(),
- item->property(StringConstants::excludeFilesProperty()));
- fakeGroup->setProperty(StringConstants::overrideTagsProperty(),
- VariantValue::falseValue());
- fakeGroup->setupForBuiltinType(m_logger);
- subItems.prepend(fakeGroup);
- }
-
- static const ItemFuncMap mapping = {
- { ItemType::Depends, &ProjectResolver::ignoreItem },
- { ItemType::Rule, &ProjectResolver::resolveRule },
- { ItemType::FileTagger, &ProjectResolver::resolveFileTagger },
- { ItemType::JobLimit, &ProjectResolver::resolveJobLimit },
- { ItemType::Group, &ProjectResolver::resolveGroup },
- { ItemType::Product, &ProjectResolver::resolveShadowProduct },
- { ItemType::Export, &ProjectResolver::resolveExport },
- { ItemType::Probe, &ProjectResolver::ignoreItem },
- { ItemType::PropertyOptions, &ProjectResolver::ignoreItem }
- };
-
- for (Item * const child : qAsConst(subItems))
- callItemFunction(mapping, child, projectContext);
-
- for (const ProjectContext *p = projectContext; p; p = p->parentContext) {
- JobLimits tempLimits = p->jobLimits;
- product->jobLimits = tempLimits.update(product->jobLimits);
- }
-
- resolveModules(item, projectContext);
-
- for (const FileTag &t : qAsConst(product->fileTags))
- m_productsByType[t].push_back(product);
-}
-
-void ProjectResolver::resolveModules(const Item *item, ProjectContext *projectContext)
-{
- JobLimits jobLimits;
- for (const Item::Module &m : item->modules())
- resolveModule(m.name, m.item, m.isProduct, m.parameters, jobLimits, projectContext);
- for (int i = 0; i < jobLimits.count(); ++i) {
- const JobLimit &moduleJobLimit = jobLimits.jobLimitAt(i);
- if (m_productContext->product->jobLimits.getLimit(moduleJobLimit.pool()) == -1)
- m_productContext->product->jobLimits.setJobLimit(moduleJobLimit);
- }
-}
-
-void ProjectResolver::resolveModule(const QualifiedId &moduleName, Item *item, bool isProduct,
- const QVariantMap &parameters, JobLimits &jobLimits,
- ProjectContext *projectContext)
-{
- checkCancelation();
- if (!item->isPresentModule())
- return;
-
- ModuleContext * const oldModuleContext = m_moduleContext;
- ModuleContext moduleContext;
- moduleContext.module = ResolvedModule::create();
- m_moduleContext = &moduleContext;
-
- const ResolvedModulePtr &module = moduleContext.module;
- module->name = moduleName.toString();
- module->isProduct = isProduct;
- module->product = m_productContext->product.get();
- module->setupBuildEnvironmentScript.initialize(
- scriptFunctionValue(item, StringConstants::setupBuildEnvironmentProperty()));
- module->setupRunEnvironmentScript.initialize(
- scriptFunctionValue(item, StringConstants::setupRunEnvironmentProperty()));
-
- for (const Item::Module &m : item->modules()) {
- if (m.item->isPresentModule())
- module->moduleDependencies += m.name.toString();
- }
-
- m_productContext->product->modules.push_back(module);
- if (!parameters.empty())
- m_productContext->product->moduleParameters[module] = parameters;
-
- static const ItemFuncMap mapping {
- { ItemType::Group, &ProjectResolver::ignoreItem },
- { ItemType::Rule, &ProjectResolver::resolveRule },
- { ItemType::FileTagger, &ProjectResolver::resolveFileTagger },
- { ItemType::JobLimit, &ProjectResolver::resolveJobLimit },
- { ItemType::Scanner, &ProjectResolver::resolveScanner },
- { ItemType::PropertyOptions, &ProjectResolver::ignoreItem },
- { ItemType::Depends, &ProjectResolver::ignoreItem },
- { ItemType::Parameter, &ProjectResolver::ignoreItem },
- { ItemType::Properties, &ProjectResolver::ignoreItem },
- { ItemType::Probe, &ProjectResolver::ignoreItem }
- };
- for (Item *child : item->children())
- callItemFunction(mapping, child, projectContext);
- for (int i = 0; i < moduleContext.jobLimits.count(); ++i) {
- const JobLimit &newJobLimit = moduleContext.jobLimits.jobLimitAt(i);
- const int oldLimit = jobLimits.getLimit(newJobLimit.pool());
- if (oldLimit == -1 || oldLimit > newJobLimit.limit())
- jobLimits.setJobLimit(newJobLimit);
- }
-
- m_moduleContext = oldModuleContext;
-}
-
-void ProjectResolver::gatherProductTypes(ResolvedProduct *product, Item *item)
-{
- product->fileTags = m_evaluator->fileTagsValue(item, StringConstants::typeProperty());
- for (const Item::Module &m : item->modules()) {
- if (m.item->isPresentModule()) {
- product->fileTags += m_evaluator->fileTagsValue(m.item,
- StringConstants::additionalProductTypesProperty());
- }
- }
- item->setProperty(StringConstants::typeProperty(),
- VariantValue::create(sorted(product->fileTags.toStringList())));
-}
-
-SourceArtifactPtr ProjectResolver::createSourceArtifact(const ResolvedProductPtr &rproduct,
- const QString &fileName, const GroupPtr &group, bool wildcard,
- const CodeLocation &filesLocation, FileLocations *fileLocations,
- ErrorInfo *errorInfo)
-{
- const QString &baseDir = FileInfo::path(group->location.filePath());
- const QString absFilePath = QDir::cleanPath(FileInfo::resolvePath(baseDir, fileName));
- if (!wildcard && !FileInfo(absFilePath).exists()) {
- if (errorInfo)
- errorInfo->append(Tr::tr("File '%1' does not exist.").arg(absFilePath), filesLocation);
- rproduct->missingSourceFiles << absFilePath;
- return {};
- }
- if (group->enabled && fileLocations) {
- CodeLocation &loc = (*fileLocations)[std::make_pair(group->targetOfModule, 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 {};
- }
- loc = filesLocation;
- }
- SourceArtifactPtr artifact = SourceArtifactInternal::create();
- artifact->absoluteFilePath = absFilePath;
- artifact->fileTags = group->fileTags;
- artifact->overrideFileTags = group->overrideTags;
- artifact->properties = group->properties;
- artifact->targetOfModule = group->targetOfModule;
- (wildcard ? group->wildcards->files : group->files).push_back(artifact);
- return artifact;
-}
-
-static QualifiedIdSet propertiesToEvaluate(const QList<QualifiedId> &initialProps,
- const PropertyDependencies &deps)
-{
- QList<QualifiedId> remainingProps = initialProps;
- QualifiedIdSet allProperties;
- while (!remainingProps.empty()) {
- const QualifiedId prop = remainingProps.takeFirst();
- const auto insertResult = allProperties.insert(prop);
- if (!insertResult.second)
- continue;
- for (const QualifiedId &directDep : deps.value(prop))
- remainingProps.push_back(directDep);
- }
- return allProperties;
-}
-
-QVariantMap ProjectResolver::resolveAdditionalModuleProperties(const Item *group,
- const QVariantMap &currentValues)
-{
- // Step 1: Retrieve the properties directly set in the group
- const ModulePropertiesPerGroup &mp = m_loadResult.productInfos.value(m_productContext->item)
- .modulePropertiesSetInGroups;
- const auto it = mp.find(group);
- if (it == mp.end())
- return {};
- const QualifiedIdSet &propsSetInGroup = it->second;
-
- // Step 2: Gather all properties that depend on these properties.
- const QualifiedIdSet &propsToEval
- = propertiesToEvaluate(propsSetInGroup.toList(), m_evaluator->propertyDependencies());
-
- // Step 3: Evaluate all these properties and replace their values in the map
- QVariantMap modulesMap = currentValues;
- QHash<QString, QStringList> propsPerModule;
- for (auto fullPropName : propsToEval) {
- const QString moduleName
- = QualifiedId(fullPropName.mid(0, fullPropName.size() - 1)).toString();
- propsPerModule[moduleName] << fullPropName.last();
- }
- EvalCacheEnabler cachingEnabler(m_evaluator);
- m_evaluator->setPathPropertiesBaseDir(m_productContext->product->sourceDirectory);
- for (const Item::Module &module : group->modules()) {
- const QString &fullModName = module.name.toString();
- const QStringList propsForModule = propsPerModule.take(fullModName);
- if (propsForModule.empty())
- continue;
- QVariantMap reusableValues = modulesMap.value(fullModName).toMap();
- for (const QString &prop : qAsConst(propsForModule))
- reusableValues.remove(prop);
- modulesMap.insert(fullModName,
- evaluateProperties(module.item, module.item, reusableValues, true, true));
- }
- m_evaluator->clearPathPropertiesBaseDir();
- return modulesMap;
-}
-
-void ProjectResolver::resolveGroup(Item *item, ProjectContext *projectContext)
-{
- checkCancelation();
- const bool parentEnabled = m_productContext->currentGroup
- ? m_productContext->currentGroup->enabled
- : m_productContext->product->enabled;
- const bool isEnabled = parentEnabled
- && m_evaluator->boolValue(item, StringConstants::conditionProperty());
- try {
- resolveGroupFully(item, projectContext, isEnabled);
- } catch (const ErrorInfo &error) {
- if (!isEnabled) {
- qCDebug(lcProjectResolver) << "error resolving group at" << item->location()
- << error.toString();
- return;
- }
- if (m_setupParams.productErrorMode() == ErrorHandlingMode::Strict)
- throw;
- m_logger.printWarning(error);
- }
-}
-
-void ProjectResolver::resolveGroupFully(Item *item, ProjectResolver::ProjectContext *projectContext,
- bool isEnabled)
-{
- AccumulatingTimer groupTimer(m_setupParams.logElapsedTime()
- ? &m_elapsedTimeGroups : nullptr);
-
- const auto getGroupPropertyMap = [this, item](const ArtifactProperties *existingProps) {
- PropertyMapPtr moduleProperties;
- bool newPropertyMapRequired = false;
- if (existingProps)
- moduleProperties = existingProps->propertyMap();
- if (!moduleProperties) {
- newPropertyMapRequired = true;
- moduleProperties = m_productContext->currentGroup
- ? m_productContext->currentGroup->properties
- : m_productContext->product->moduleProperties;
- }
- const QVariantMap newModuleProperties
- = resolveAdditionalModuleProperties(item, moduleProperties->value());
- if (!newModuleProperties.empty()) {
- if (newPropertyMapRequired)
- moduleProperties = PropertyMapInternal::create();
- moduleProperties->setValue(newModuleProperties);
- }
- return moduleProperties;
- };
-
- QStringList files = m_evaluator->stringListValue(item, StringConstants::filesProperty());
- bool fileTagsSet;
- const FileTags fileTags = m_evaluator->fileTagsValue(item, StringConstants::fileTagsProperty(),
- &fileTagsSet);
- const QStringList fileTagsFilter
- = m_evaluator->stringListValue(item, StringConstants::fileTagsFilterProperty());
- if (!fileTagsFilter.empty()) {
- if (Q_UNLIKELY(!files.empty()))
- throw ErrorInfo(Tr::tr("Group.files and Group.fileTagsFilters are exclusive."),
- item->location());
-
- if (!isEnabled)
- return;
-
- ProductContext::ArtifactPropertiesInfo &apinfo
- = m_productContext->artifactPropertiesPerFilter[fileTagsFilter];
- if (apinfo.first) {
- const auto it = std::find_if(apinfo.second.cbegin(), apinfo.second.cend(),
- [item](const CodeLocation &loc) {
- return item->location().filePath() == loc.filePath();
- });
- if (it != apinfo.second.cend()) {
- ErrorInfo error(Tr::tr("Conflicting fileTagsFilter in Group items."));
- error.append(Tr::tr("First item"), *it);
- error.append(Tr::tr("Second item"), item->location());
- throw error;
- }
- } else {
- apinfo.first = ArtifactProperties::create();
- apinfo.first->setFileTagsFilter(FileTags::fromStringList(fileTagsFilter));
- m_productContext->product->artifactProperties.push_back(apinfo.first);
- }
- apinfo.second.push_back(item->location());
- apinfo.first->setPropertyMapInternal(getGroupPropertyMap(apinfo.first.get()));
- apinfo.first->addExtraFileTags(fileTags);
- return;
- }
- QStringList patterns;
- for (int i = files.size(); --i >= 0;) {
- if (FileInfo::isPattern(files[i]))
- patterns.push_back(files.takeAt(i));
- }
- GroupPtr group = ResolvedGroup::create();
- bool prefixWasSet = false;
- group->prefix = m_evaluator->stringValue(item, StringConstants::prefixProperty(), QString(),
- &prefixWasSet);
- if (!prefixWasSet && m_productContext->currentGroup)
- group->prefix = m_productContext->currentGroup->prefix;
- if (!group->prefix.isEmpty()) {
- for (auto it = files.rbegin(), end = files.rend(); it != end; ++it)
- it->prepend(group->prefix);
- }
- group->location = item->location();
- group->enabled = isEnabled;
- group->properties = getGroupPropertyMap(nullptr);
- group->fileTags = fileTags;
- group->overrideTags = m_evaluator->boolValue(item, StringConstants::overrideTagsProperty());
- if (group->overrideTags && fileTagsSet) {
- if (group->fileTags.empty() )
- group->fileTags.insert(unknownFileTag());
- } else if (m_productContext->currentGroup) {
- group->fileTags.unite(m_productContext->currentGroup->fileTags);
- }
-
- const CodeLocation filesLocation = item->property(StringConstants::filesProperty())->location();
- const VariantValueConstPtr moduleProp = item->variantProperty(
- StringConstants::modulePropertyInternal());
- if (moduleProp)
- group->targetOfModule = moduleProp->value().toString();
- ErrorInfo fileError;
- if (!patterns.empty()) {
- group->wildcards = std::make_unique<SourceWildCards>();
- SourceWildCards *wildcards = group->wildcards.get();
- wildcards->group = group.get();
- wildcards->excludePatterns = m_evaluator->stringListValue(
- item, StringConstants::excludeFilesProperty());
- wildcards->patterns = patterns;
- const Set<QString> files = wildcards->expandPatterns(group,
- FileInfo::path(item->file()->filePath()),
- projectContext->project->topLevelProject()->buildDirectory);
- for (const QString &fileName : files)
- createSourceArtifact(m_productContext->product, fileName, group, true, filesLocation,
- &m_productContext->sourceArtifactLocations, &fileError);
- }
-
- for (const QString &fileName : qAsConst(files)) {
- 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 {
- qCDebug(lcProjectResolver) << "error for disabled group:" << fileError.toString();
- }
- }
- group->name = m_evaluator->stringValue(item, StringConstants::nameProperty());
- if (group->name.isEmpty())
- group->name = Tr::tr("Group %1").arg(m_productContext->product->groups.size());
- m_productContext->product->groups.push_back(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);
- for (Item * const childItem : item->children())
- resolveGroup(childItem, projectContext);
-}
-
-void ProjectResolver::adaptExportedPropertyValues(const Item *shadowProductItem)
-{
- ExportedModule &m = m_productContext->product->exportedModule;
- const QVariantList prefixList = m.propertyValues.take(
- StringConstants::prefixMappingProperty()).toList();
- const QString shadowProductName = m_evaluator->stringValue(
- shadowProductItem, StringConstants::nameProperty());
- const QString shadowProductBuildDir = m_evaluator->stringValue(
- shadowProductItem, StringConstants::buildDirectoryProperty());
- QVariantMap prefixMap;
- for (const QVariant &v : prefixList) {
- const QVariantMap o = v.toMap();
- prefixMap.insert(o.value(QStringLiteral("prefix")).toString(),
- o.value(QStringLiteral("replacement")).toString());
- }
- const auto valueRefersToImportingProduct
- = [shadowProductName, shadowProductBuildDir](const QString &value) {
- return value.toLower().contains(shadowProductName.toLower())
- || value.contains(shadowProductBuildDir);
- };
- static const auto stringMapper = [](const QVariantMap &mappings, const QString &value)
- -> QString {
- for (auto it = mappings.cbegin(); it != mappings.cend(); ++it) {
- if (value.startsWith(it.key()))
- return it.value().toString() + value.mid(it.key().size());
- }
- return value;
- };
- const auto stringListMapper = [&valueRefersToImportingProduct](
- const QVariantMap &mappings, const QStringList &value) -> QStringList {
- QStringList result;
- result.reserve(value.size());
- for (const QString &s : value) {
- if (!valueRefersToImportingProduct(s))
- result.push_back(stringMapper(mappings, s));
- }
- return result;
- };
- const std::function<QVariant(const QVariantMap &, const QVariant &)> mapper
- = [&stringListMapper, &mapper](
- const QVariantMap &mappings, const QVariant &value) -> QVariant {
- switch (static_cast<QMetaType::Type>(value.type())) {
- case QMetaType::QString:
- return stringMapper(mappings, value.toString());
- case QMetaType::QStringList:
- return stringListMapper(mappings, value.toStringList());
- case QMetaType::QVariantMap: {
- QVariantMap m = value.toMap();
- for (auto it = m.begin(); it != m.end(); ++it)
- it.value() = mapper(mappings, it.value());
- return m;
- }
- default:
- return value;
- }
- };
- for (auto it = m.propertyValues.begin(); it != m.propertyValues.end(); ++it)
- it.value() = mapper(prefixMap, it.value());
- for (auto it = m.modulePropertyValues.begin(); it != m.modulePropertyValues.end(); ++it)
- it.value() = mapper(prefixMap, it.value());
- for (ExportedModuleDependency &dep : m.moduleDependencies) {
- for (auto it = dep.moduleProperties.begin(); it != dep.moduleProperties.end(); ++it)
- it.value() = mapper(prefixMap, it.value());
- }
-}
-
-void ProjectResolver::collectExportedProductDependencies()
-{
- ResolvedProductPtr dummyProduct = ResolvedProduct::create();
- dummyProduct->enabled = false;
- for (const auto &exportingProductInfo : qAsConst(m_productExportInfo)) {
- const ResolvedProductPtr exportingProduct = exportingProductInfo.first;
- if (!exportingProduct->enabled)
- continue;
- Item * const importingProductItem = exportingProductInfo.second;
- std::vector<QString> directDepNames;
- for (const Item::Module &m : importingProductItem->modules()) {
- if (m.name.toString() == exportingProduct->name) {
- for (const Item::Module &dep : m.item->modules()) {
- if (dep.isProduct)
- directDepNames.push_back(dep.name.toString());
- }
- break;
- }
- }
- const ModuleLoaderResult::ProductInfo &importingProductInfo
- = m_loadResult.productInfos.value(importingProductItem);
- const ProductDependencyInfos &depInfos
- = getProductDependencies(dummyProduct, importingProductInfo);
- for (const auto &dep : depInfos.dependencies) {
- if (dep.product == exportingProduct)
- continue;
-
- // Filter out indirect dependencies.
- // TODO: Depends items using "profile" or "productTypes" will not work.
- if (!contains(directDepNames, dep.product->name))
- continue;
-
- if (!contains(exportingProduct->exportedModule.productDependencies,
- dep.product->uniqueName())) {
- exportingProduct->exportedModule.productDependencies.push_back(
- dep.product->uniqueName());
- }
- if (!dep.parameters.isEmpty()) {
- exportingProduct->exportedModule.dependencyParameters.insert(dep.product,
- dep.parameters);
- }
- }
- auto &productDeps = exportingProduct->exportedModule.productDependencies;
- std::sort(productDeps.begin(), productDeps.end());
- }
-}
-
-void ProjectResolver::resolveShadowProduct(Item *item, ProjectResolver::ProjectContext *)
-{
- if (!m_productContext->product->enabled)
- return;
- for (const auto &m : item->modules()) {
- if (m.name.toString() != m_productContext->product->name)
- continue;
- collectPropertiesForExportItem(m.item);
- for (const auto &dep : m.item->modules())
- collectPropertiesForModuleInExportItem(dep);
- break;
- }
- try {
- adaptExportedPropertyValues(item);
- } catch (const ErrorInfo &) {}
- m_productExportInfo.emplace_back(m_productContext->product, item);
-}
-
-void ProjectResolver::setupExportedProperties(const Item *item, const QString &namePrefix,
- std::vector<ExportedProperty> &properties)
-{
- const auto &props = item->properties();
- for (auto it = props.cbegin(); it != props.cend(); ++it) {
- const QString qualifiedName = namePrefix.isEmpty()
- ? it.key() : namePrefix + QLatin1Char('.') + it.key();
- if ((item->type() == ItemType::Export || item->type() == ItemType::Properties)
- && qualifiedName == StringConstants::prefixMappingProperty()) {
- continue;
- }
- const ValuePtr &v = it.value();
- if (v->type() == Value::ItemValueType) {
- setupExportedProperties(std::static_pointer_cast<ItemValue>(v)->item(),
- qualifiedName, properties);
- continue;
- }
- ExportedProperty exportedProperty;
- exportedProperty.fullName = qualifiedName;
- exportedProperty.type = item->propertyDeclaration(it.key()).type();
- if (v->type() == Value::VariantValueType) {
- exportedProperty.sourceCode = toJSLiteral(
- std::static_pointer_cast<VariantValue>(v)->value());
- } else {
- QBS_CHECK(v->type() == Value::JSSourceValueType);
- const JSSourceValue * const sv = static_cast<JSSourceValue *>(v.get());
- exportedProperty.sourceCode = sv->sourceCode().toString();
- }
- const ItemDeclaration itemDecl
- = BuiltinDeclarations::instance().declarationsForType(item->type());
- PropertyDeclaration propertyDecl;
- const auto itemProperties = itemDecl.properties();
- for (const PropertyDeclaration &decl : itemProperties) {
- if (decl.name() == it.key()) {
- propertyDecl = decl;
- exportedProperty.isBuiltin = true;
- break;
- }
- }
-
- // Do not add built-in properties that were left at their default value.
- if (!exportedProperty.isBuiltin || m_evaluator->isNonDefaultValue(item, it.key()))
- properties.push_back(exportedProperty);
- }
-
- // Order the list of properties, so the output won't look so random.
- static const auto less = [](const ExportedProperty &p1, const ExportedProperty &p2) -> bool {
- const int p1ComponentCount = p1.fullName.count(QLatin1Char('.'));
- const int p2ComponentCount = p2.fullName.count(QLatin1Char('.'));
- if (p1.isBuiltin && !p2.isBuiltin)
- return true;
- if (!p1.isBuiltin && p2.isBuiltin)
- return false;
- if (p1ComponentCount < p2ComponentCount)
- return true;
- if (p1ComponentCount > p2ComponentCount)
- return false;
- return p1.fullName < p2.fullName;
- };
- std::sort(properties.begin(), properties.end(), less);
-}
-
-static bool usesImport(const ExportedProperty &prop, const QRegExp &regex)
-{
- return regex.indexIn(prop.sourceCode) != -1;
-}
-
-static bool usesImport(const ExportedItem &item, const QRegExp &regex)
-{
- return any_of(item.properties,
- [regex](const ExportedProperty &p) { return usesImport(p, regex); })
- || any_of(item.children,
- [regex](const ExportedItemPtr &child) { return usesImport(*child, regex); });
-}
-
-static bool usesImport(const ExportedModule &module, const QString &name)
-{
- // Imports are used in three ways:
- // (1) var f = new TextFile(...);
- // (2) var path = FileInfo.joinPaths(...)
- // (3) var obj = DataCollection;
- const QString pattern = QStringLiteral("\\b%1\\b");
-
- const QRegExp regex(pattern.arg(name)); // std::regex is much slower
- return any_of(module.m_properties,
- [regex](const ExportedProperty &p) { return usesImport(p, regex); })
- || any_of(module.children,
- [regex](const ExportedItemPtr &child) { return usesImport(*child, regex); });
-}
-
-static QString getLineAtLocation(const CodeLocation &loc, const QString &content)
-{
- int pos = 0;
- int currentLine = 1;
- while (currentLine < loc.line()) {
- while (content.at(pos++) != QLatin1Char('\n'))
- ;
- ++currentLine;
- }
- const int eolPos = content.indexOf(QLatin1Char('\n'), pos);
- return content.mid(pos, eolPos - pos);
-}
-
-void ProjectResolver::resolveExport(Item *exportItem, ProjectContext *)
-{
- ExportedModule &exportedModule = m_productContext->product->exportedModule;
- setupExportedProperties(exportItem, QString(), exportedModule.m_properties);
- static const auto cmpFunc = [](const ExportedProperty &p1, const ExportedProperty &p2) {
- return p1.fullName < p2.fullName;
- };
- std::sort(exportedModule.m_properties.begin(), exportedModule.m_properties.end(), cmpFunc);
- for (const Item * const child : exportItem->children())
- exportedModule.children.push_back(resolveExportChild(child, exportedModule));
- for (const JsImport &jsImport : exportItem->file()->jsImports()) {
- if (usesImport(exportedModule, jsImport.scopeName)) {
- exportedModule.importStatements << getLineAtLocation(jsImport.location,
- exportItem->file()->content());
- }
- }
- const auto builtInImports = JsExtensions::extensionNames();
- for (const QString &builtinImport: builtInImports) {
- if (usesImport(exportedModule, builtinImport))
- exportedModule.importStatements << QStringLiteral("import qbs.") + builtinImport;
- }
- exportedModule.importStatements.sort();
-}
-
-// TODO: This probably wouldn't be necessary if we had item serialization.
-std::unique_ptr<ExportedItem> ProjectResolver::resolveExportChild(const Item *item,
- const ExportedModule &module)
-{
- std::unique_ptr<ExportedItem> exportedItem(new ExportedItem);
-
- // This is the type of the built-in base item. It may turn out that we need to support
- // derived items under Export. In that case, we probably need a new Item member holding
- // the original type name.
- exportedItem->name = item->typeName();
-
- for (const Item * const child : item->children())
- exportedItem->children.push_back(resolveExportChild(child, module));
- setupExportedProperties(item, QString(), exportedItem->properties);
- return exportedItem;
-}
-
-
-QString ProjectResolver::sourceCodeAsFunction(const JSSourceValueConstPtr &value,
- const PropertyDeclaration &decl) const
-{
- QString &scriptFunction = m_scriptFunctions[std::make_pair(value->sourceCode(),
- decl.functionArgumentNames())];
- if (!scriptFunction.isNull())
- return scriptFunction;
- const QString args = decl.functionArgumentNames().join(QLatin1Char(','));
- if (value->hasFunctionForm()) {
- // Insert the argument list.
- scriptFunction = value->sourceCodeForEvaluation();
- scriptFunction.insert(10, args);
- // Remove the function application "()" that has been
- // added in ItemReaderASTVisitor::visitStatement.
- scriptFunction.chop(2);
- } else {
- scriptFunction = QLatin1String("(function(") + args + QLatin1String("){return ")
- + value->sourceCode().toString() + QLatin1String(";})");
- }
- return scriptFunction;
-}
-
-QString ProjectResolver::sourceCodeForEvaluation(const JSSourceValueConstPtr &value) const
-{
- QString &code = m_sourceCode[value->sourceCode()];
- if (!code.isNull())
- return code;
- code = value->sourceCodeForEvaluation();
- return code;
-}
-
-ScriptFunctionPtr ProjectResolver::scriptFunctionValue(Item *item, const QString &name) const
-{
- JSSourceValuePtr value = item->sourceProperty(name);
- ScriptFunctionPtr &script = m_scriptFunctionMap[value ? value->location() : CodeLocation()];
- if (!script.get()) {
- script = ScriptFunction::create();
- const PropertyDeclaration decl = item->propertyDeclaration(name);
- script->sourceCode = sourceCodeAsFunction(value, decl);
- 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, StringConstants::conditionProperty()))
- return;
-
- RulePtr rule = Rule::create();
-
- // read artifacts
- bool hasArtifactChildren = false;
- for (Item * const 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, StringConstants::nameProperty());
- rule->prepareScript.initialize(scriptFunctionValue(item, StringConstants::prepareProperty()));
- rule->outputArtifactsScript.initialize(scriptFunctionValue(
- item, StringConstants::outputArtifactsProperty()));
- rule->outputFileTags = m_evaluator->fileTagsValue(
- item, StringConstants::outputFileTagsProperty());
- 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());
- }
- if (!hasArtifactChildren && rule->outputFileTags.empty()) {
- throw ErrorInfo(Tr::tr("A rule needs to have Artifact items or a non-empty "
- "outputFileTags property."), item->location());
- }
- rule->multiplex = m_evaluator->boolValue(item, StringConstants::multiplexProperty());
- rule->alwaysRun = m_evaluator->boolValue(item, StringConstants::alwaysRunProperty());
- rule->inputs = m_evaluator->fileTagsValue(item, StringConstants::inputsProperty());
- rule->inputsFromDependencies
- = m_evaluator->fileTagsValue(item, StringConstants::inputsFromDependenciesProperty());
- bool requiresInputsSet = false;
- rule->requiresInputs = m_evaluator->boolValue(item, StringConstants::requiresInputsProperty(),
- &requiresInputsSet);
- if (!requiresInputsSet)
- rule->requiresInputs = rule->declaresInputs();
- rule->auxiliaryInputs
- = m_evaluator->fileTagsValue(item, StringConstants::auxiliaryInputsProperty());
- rule->excludedInputs
- = m_evaluator->fileTagsValue(item, StringConstants::excludedInputsProperty());
- if (rule->excludedInputs.empty()) {
- rule->excludedInputs = m_evaluator->fileTagsValue(
- item, StringConstants::excludedAuxiliaryInputsProperty());
- }
- rule->explicitlyDependsOn
- = m_evaluator->fileTagsValue(item, StringConstants::explicitlyDependsOnProperty());
- rule->explicitlyDependsOnFromDependencies = m_evaluator->fileTagsValue(
- item, StringConstants::explicitlyDependsOnFromDependenciesProperty());
- rule->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule;
- if (!rule->multiplex && !rule->declaresInputs()) {
- throw ErrorInfo(Tr::tr("Rule has no inputs, but is not a multiplex rule."),
- item->location());
- }
- if (!rule->multiplex && !rule->requiresInputs) {
- throw ErrorInfo(Tr::tr("Rule.requiresInputs is false for non-multiplex rule."),
- item->location());
- }
- if (!rule->declaresInputs() && rule->requiresInputs) {
- throw ErrorInfo(Tr::tr("Rule.requiresInputs is true, but the rule "
- "does not declare any input tags."), item->location());
- }
- if (m_productContext) {
- rule->product = m_productContext->product.get();
- m_productContext->product->rules.push_back(rule);
- } else {
- projectContext->rules.push_back(rule);
- }
-}
-
-void ProjectResolver::resolveRuleArtifact(const RulePtr &rule, Item *item)
-{
- RuleArtifactPtr artifact = RuleArtifact::create();
- rule->artifacts.push_back(artifact);
- artifact->location = item->location();
-
- if (const auto sourceProperty = item->sourceProperty(StringConstants::filePathProperty()))
- artifact->filePathLocation = sourceProperty->location();
-
- artifact->filePath = verbatimValue(item, StringConstants::filePathProperty());
- artifact->fileTags = m_evaluator->fileTagsValue(item, StringConstants::fileTagsProperty());
- artifact->alwaysUpdated = m_evaluator->boolValue(item,
- StringConstants::alwaysUpdatedProperty());
-
- QualifiedIdSet seenBindings;
- for (Item *obj = item; obj; obj = obj->prototype()) {
- for (QMap<QString, ValuePtr>::const_iterator it = obj->properties().constBegin();
- it != obj->properties().constEnd(); ++it)
- {
- if (it.value()->type() != Value::ItemValueType)
- continue;
- resolveRuleArtifactBinding(artifact,
- std::static_pointer_cast<ItemValue>(it.value())->item(),
- QStringList(it.key()), &seenBindings);
- }
- }
-}
-
-void ProjectResolver::resolveRuleArtifactBinding(const RuleArtifactPtr &ruleArtifact,
- Item *item,
- const QStringList &namePrefix,
- QualifiedIdSet *seenBindings)
-{
- for (QMap<QString, ValuePtr>::const_iterator it = item->properties().constBegin();
- it != item->properties().constEnd(); ++it)
- {
- const QStringList name = QStringList(namePrefix) << it.key();
- if (it.value()->type() == Value::ItemValueType) {
- resolveRuleArtifactBinding(ruleArtifact,
- std::static_pointer_cast<ItemValue>(it.value())->item(), name,
- seenBindings);
- } else if (it.value()->type() == Value::JSSourceValueType) {
- const auto insertResult = seenBindings->insert(name);
- if (!insertResult.second)
- continue;
- JSSourceValuePtr sourceValue = std::static_pointer_cast<JSSourceValue>(it.value());
- RuleArtifact::Binding rab;
- rab.name = name;
- rab.code = sourceCodeForEvaluation(sourceValue);
- rab.location = sourceValue->location();
- ruleArtifact->bindings.push_back(rab);
- } else {
- QBS_ASSERT(!"unexpected value type", continue);
- }
- }
-}
-
-void ProjectResolver::resolveFileTagger(Item *item, ProjectContext *projectContext)
-{
- checkCancelation();
- if (!m_evaluator->boolValue(item, StringConstants::conditionProperty()))
- return;
- std::vector<FileTaggerConstPtr> &fileTaggers = m_productContext
- ? m_productContext->product->fileTaggers
- : projectContext->fileTaggers;
- const QStringList patterns = m_evaluator->stringListValue(item,
- StringConstants::patternsProperty());
- if (patterns.empty())
- throw ErrorInfo(Tr::tr("FileTagger.patterns must be a non-empty list."), item->location());
-
- const FileTags fileTags = m_evaluator->fileTagsValue(item, StringConstants::fileTagsProperty());
- if (fileTags.empty())
- throw ErrorInfo(Tr::tr("FileTagger.fileTags must not be empty."), item->location());
-
- for (const QString &pattern : patterns) {
- if (pattern.isEmpty())
- throw ErrorInfo(Tr::tr("A FileTagger pattern must not be empty."), item->location());
- }
-
- const int priority = m_evaluator->intValue(item, StringConstants::priorityProperty());
- fileTaggers.push_back(FileTagger::create(patterns, fileTags, priority));
-}
-
-void ProjectResolver::resolveJobLimit(Item *item, ProjectResolver::ProjectContext *projectContext)
-{
- if (!m_evaluator->boolValue(item, StringConstants::conditionProperty()))
- return;
- const QString jobPool = m_evaluator->stringValue(item, StringConstants::jobPoolProperty());
- if (jobPool.isEmpty())
- throw ErrorInfo(Tr::tr("A JobLimit item needs to have a non-empty '%1' property.")
- .arg(StringConstants::jobPoolProperty()), item->location());
- bool jobCountWasSet;
- const int jobCount = m_evaluator->intValue(item, StringConstants::jobCountProperty(), -1,
- &jobCountWasSet);
- if (!jobCountWasSet) {
- throw ErrorInfo(Tr::tr("A JobLimit item needs to have a '%1' property.")
- .arg(StringConstants::jobCountProperty()), item->location());
- }
- if (jobCount < 0) {
- throw ErrorInfo(Tr::tr("A JobLimit item must have a non-negative '%1' property.")
- .arg(StringConstants::jobCountProperty()), item->location());
- }
- JobLimits &jobLimits = m_moduleContext
- ? m_moduleContext->jobLimits
- : m_productContext ? m_productContext->product->jobLimits
- : projectContext->jobLimits;
- JobLimit jobLimit(jobPool, jobCount);
- const int oldLimit = jobLimits.getLimit(jobPool);
- if (oldLimit == -1 || oldLimit > jobCount)
- jobLimits.setJobLimit(jobLimit);
-}
-
-void ProjectResolver::resolveScanner(Item *item, ProjectResolver::ProjectContext *projectContext)
-{
- checkCancelation();
- if (!m_evaluator->boolValue(item, StringConstants::conditionProperty())) {
- qCDebug(lcProjectResolver) << "scanner condition is false";
- return;
- }
-
- ResolvedScannerPtr scanner = ResolvedScanner::create();
- scanner->module = m_moduleContext ? m_moduleContext->module : projectContext->dummyModule;
- scanner->inputs = m_evaluator->fileTagsValue(item, StringConstants::inputsProperty());
- scanner->recursive = m_evaluator->boolValue(item, StringConstants::recursiveProperty());
- scanner->searchPathsScript.initialize(scriptFunctionValue(
- item, StringConstants::searchPathsProperty()));
- scanner->scanScript.initialize(scriptFunctionValue(item, StringConstants::scanProperty()));
- m_productContext->product->scanners.push_back(scanner);
-}
-
-ProjectResolver::ProductDependencyInfos ProjectResolver::getProductDependencies(
- const ResolvedProductConstPtr &product, const ModuleLoaderResult::ProductInfo &productInfo)
-{
- ProductDependencyInfos result;
- result.dependencies.reserve(productInfo.usedProducts.size());
- for (const auto &dependency : productInfo.usedProducts) {
- QBS_CHECK(!dependency.name.isEmpty());
- if (dependency.profile == StringConstants::star()) {
- for (const ResolvedProductPtr &p : qAsConst(m_productsByName)) {
- if (p->name != dependency.name || p == product || !p->enabled
- || (dependency.limitToSubProject && !product->isInParentProject(p))) {
- continue;
- }
- result.dependencies.emplace_back(p, dependency.parameters);
- }
- } else {
- ResolvedProductPtr usedProduct = m_productsByName.value(dependency.uniqueName());
- const QString depDisplayName = ResolvedProduct::fullDisplayName(dependency.name,
- dependency.multiplexConfigurationId);
- if (!usedProduct) {
- throw ErrorInfo(Tr::tr("Product '%1' depends on '%2', which does not exist.")
- .arg(product->fullDisplayName(), depDisplayName),
- product->location);
- }
- if (!dependency.profile.isEmpty() && usedProduct->profile() != dependency.profile) {
- usedProduct.reset();
- for (const ResolvedProductPtr &p : qAsConst(m_productsByName)) {
- if (p->name == dependency.name && p->profile() == dependency.profile) {
- usedProduct = p;
- break;
- }
- }
- if (!usedProduct) {
- throw ErrorInfo(Tr::tr("Product '%1' depends on '%2', which does not exist "
- "for the requested profile '%3'.")
- .arg(product->fullDisplayName(), depDisplayName,
- dependency.profile),
- 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;
- result.hasDisabledDependency = true;
- }
- result.dependencies.emplace_back(usedProduct, dependency.parameters);
- }
- }
- return result;
-}
-
-void ProjectResolver::matchArtifactProperties(const ResolvedProductPtr &product,
- const std::vector<SourceArtifactPtr> &artifacts)
-{
- for (const SourceArtifactPtr &artifact : artifacts) {
- for (const ArtifactPropertiesConstPtr &artifactProperties : product->artifactProperties) {
- if (!artifact->isTargetOfModule()
- && artifact->fileTags.intersects(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));
-}
-
-class TempScopeSetter
-{
-public:
- TempScopeSetter(Item * item, Item *newScope) : m_item(item), m_oldScope(item->scope())
- {
- item->setScope(newScope);
- }
- ~TempScopeSetter() { m_item->setScope(m_oldScope); }
-private:
- Item * const m_item;
- Item * const m_oldScope;
-};
-
-void ProjectResolver::collectPropertiesForExportItem(Item *productModuleInstance)
-{
- if (!productModuleInstance->isPresentModule())
- return;
- Item * const exportItem = productModuleInstance->prototype();
- QBS_CHECK(exportItem && exportItem->type() == ItemType::Export);
- TempScopeSetter tempScopeSetter(exportItem, productModuleInstance->scope());
- const ItemDeclaration::Properties exportDecls = BuiltinDeclarations::instance()
- .declarationsForType(ItemType::Export).properties();
- ExportedModule &exportedModule = m_productContext->product->exportedModule;
- const auto &props = exportItem->properties();
- for (auto it = props.begin(); it != props.end(); ++it) {
- const auto match
- = [it](const PropertyDeclaration &decl) { return decl.name() == it.key(); };
- if (it.key() != StringConstants::prefixMappingProperty() &&
- std::find_if(exportDecls.begin(), exportDecls.end(), match) != exportDecls.end()) {
- continue;
- }
- if (it.value()->type() == Value::ItemValueType) {
- collectPropertiesForExportItem(it.key(), it.value(), productModuleInstance,
- exportedModule.modulePropertyValues);
- } else {
- evaluateProperty(exportItem, it.key(), it.value(), exportedModule.propertyValues,
- false);
- }
- }
-}
-
-// Collects module properties assigned to in other (higher-level) modules.
-void ProjectResolver::collectPropertiesForModuleInExportItem(const Item::Module &module)
-{
- if (!module.item->isPresentModule())
- return;
- ExportedModule &exportedModule = m_productContext->product->exportedModule;
- if (module.isProduct || module.name.first() == StringConstants::qbsModule())
- return;
- const auto checkName = [module](const ExportedModuleDependency &d) {
- return module.name.toString() == d.name;
- };
- if (any_of(exportedModule.moduleDependencies, checkName))
- return;
-
- Item *modulePrototype = module.item->prototype();
- while (modulePrototype && modulePrototype->type() != ItemType::Module)
- modulePrototype = modulePrototype->prototype();
- if (!modulePrototype) // Can happen for broken products in relaxed mode.
- return;
- TempScopeSetter tempScopeSetter(modulePrototype, module.item->scope());
- const Item::PropertyMap &props = modulePrototype->properties();
- ExportedModuleDependency dep;
- dep.name = module.name.toString();
- for (auto it = props.begin(); it != props.end(); ++it) {
- if (it.value()->type() == Value::ItemValueType)
- collectPropertiesForExportItem(it.key(), it.value(), module.item, dep.moduleProperties);
- }
- exportedModule.moduleDependencies.push_back(dep);
-
- for (const auto &dep : module.item->modules())
- collectPropertiesForModuleInExportItem(dep);
-}
-
-static bool hasDependencyCycle(Set<ResolvedProduct *> *checked,
- Set<ResolvedProduct *> *branch,
- const ResolvedProductPtr &product,
- ErrorInfo *error)
-{
- if (branch->contains(product.get()))
- return true;
- if (checked->contains(product.get()))
- return false;
- checked->insert(product.get());
- branch->insert(product.get());
- for (const ResolvedProductPtr &dep : qAsConst(product->dependencies)) {
- if (hasDependencyCycle(checked, branch, dep, error)) {
- error->prepend(dep->name, dep->location);
- return true;
- }
- }
- branch->remove(product.get());
- return false;
-}
-
-using DependencyMap = QHash<ResolvedProduct *, Set<ResolvedProduct *>>;
-void gatherDependencies(ResolvedProduct *product, DependencyMap &dependencies)
-{
- if (dependencies.contains(product))
- return;
- Set<ResolvedProduct *> &productDeps = dependencies[product];
- for (const ResolvedProductPtr &dep : qAsConst(product->dependencies)) {
- productDeps << dep.get();
- gatherDependencies(dep.get(), dependencies);
- productDeps += dependencies.value(dep.get());
- }
-}
-
-
-
-static DependencyMap allDependencies(const std::vector<ResolvedProductPtr> &products)
-{
- DependencyMap dependencies;
- for (const ResolvedProductPtr &product : products)
- gatherDependencies(product.get(), dependencies);
- return dependencies;
-}
-
-void ProjectResolver::resolveProductDependencies(const ProjectContext &projectContext)
-{
- // Resolve all inter-product dependencies.
- const std::vector<ResolvedProductPtr> allProducts = projectContext.project->allProducts();
- bool disabledDependency = false;
- for (const ResolvedProductPtr &rproduct : allProducts) {
- if (!rproduct->enabled)
- continue;
- Item *productItem = m_productItemMap.value(rproduct);
- const ModuleLoaderResult::ProductInfo &productInfo
- = m_loadResult.productInfos.value(productItem);
- const ProductDependencyInfos &depInfos = getProductDependencies(rproduct, productInfo);
- if (depInfos.hasDisabledDependency)
- disabledDependency = true;
- for (const auto &dep : depInfos.dependencies) {
- if (!contains(rproduct->dependencies, dep.product))
- rproduct->dependencies.push_back(dep.product);
- if (!dep.parameters.empty())
- rproduct->dependencyParameters.insert(dep.product, dep.parameters);
- }
- }
-
- // Check for cyclic dependencies.
- Set<ResolvedProduct *> checked;
- for (const ResolvedProductPtr &rproduct : allProducts) {
- Set<ResolvedProduct *> 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) {
- for (ResolvedProduct *dep : qAsConst(it.value()))
- allDepsReversed[dep] << it.key();
- }
- for (auto it = allDepsReversed.constBegin(); it != allDepsReversed.constEnd(); ++it) {
- if (it.key()->enabled)
- continue;
- for (ResolvedProduct * const dependingProduct : qAsConst(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;
- std::sort(std::begin(product->fileTaggers), std::end(product->fileTaggers),
- [] (const FileTaggerConstPtr &a, const FileTaggerConstPtr &b) {
- return a->priority() > b->priority();
- });
- for (const RulePtr &rule : projectContext->rules) {
- RulePtr clonedRule = rule->clone();
- clonedRule->product = product.get();
- product->rules.push_back(clonedRule);
- }
-}
-
-void ProjectResolver::applyFileTaggers(const ResolvedProductPtr &product) const
-{
- for (const SourceArtifactPtr &artifact : product->allEnabledFiles())
- applyFileTaggers(artifact, product);
-}
-
-void ProjectResolver::applyFileTaggers(const SourceArtifactPtr &artifact,
- const ResolvedProductConstPtr &product)
-{
- if (!artifact->overrideFileTags || artifact->fileTags.empty()) {
- const QString fileName = FileInfo::fileName(artifact->absoluteFilePath);
- const FileTags fileTags = product->fileTagsForFileName(fileName);
- artifact->fileTags.unite(fileTags);
- if (artifact->fileTags.empty())
- artifact->fileTags.insert(unknownFileTag());
- qCDebug(lcProjectResolver) << "adding file tags" << artifact->fileTags
- << "to" << fileName;
- }
-}
-
-QVariantMap ProjectResolver::evaluateModuleValues(Item *item, bool lookupPrototype)
-{
- AccumulatingTimer modPropEvalTimer(m_setupParams.logElapsedTime()
- ? &m_elapsedTimeModPropEval : nullptr);
- QVariantMap moduleValues;
- for (const Item::Module &module : item->modules()) {
- if (!module.item->isPresentModule())
- continue;
- const QString fullName = module.name.toString();
- moduleValues[fullName] = evaluateProperties(module.item, lookupPrototype, true);
- }
-
- return moduleValues;
-}
-
-QVariantMap ProjectResolver::evaluateProperties(Item *item, bool lookupPrototype, bool checkErrors)
-{
- const QVariantMap tmplt;
- return evaluateProperties(item, item, tmplt, lookupPrototype, checkErrors);
-}
-
-QVariantMap ProjectResolver::evaluateProperties(const Item *item, const Item *propertiesContainer,
- const QVariantMap &tmplt, bool lookupPrototype, bool checkErrors)
-{
- AccumulatingTimer propEvalTimer(m_setupParams.logElapsedTime()
- ? &m_elapsedTimeAllPropEval : nullptr);
- QVariantMap result = tmplt;
- for (QMap<QString, ValuePtr>::const_iterator it = propertiesContainer->properties().begin();
- it != propertiesContainer->properties().end(); ++it) {
- checkCancelation();
- evaluateProperty(item, it.key(), it.value(), result, checkErrors);
- }
- return lookupPrototype && propertiesContainer->prototype()
- ? evaluateProperties(item, propertiesContainer->prototype(), result, true, checkErrors)
- : result;
-}
-
-void ProjectResolver::evaluateProperty(const Item *item, const QString &propName,
- const ValuePtr &propValue, QVariantMap &result, bool checkErrors)
-{
- switch (propValue->type()) {
- case Value::ItemValueType:
- {
- // Ignore items. Those point to module instances
- // and are handled in evaluateModuleValues().
- break;
- }
- case Value::JSSourceValueType:
- {
- if (result.contains(propName))
- break;
- const PropertyDeclaration pd = item->propertyDeclaration(propName);
- if (pd.flags().testFlag(PropertyDeclaration::PropertyNotAvailableInConfig)) {
- break;
- }
- const QScriptValue scriptValue = m_evaluator->property(item, propName);
- if (checkErrors && Q_UNLIKELY(m_evaluator->engine()->hasErrorOrException(scriptValue))) {
- throw ErrorInfo(m_evaluator->engine()->lastError(scriptValue,
- propValue->location()));
- }
-
- // NOTE: Loses type information if scriptValue.isUndefined == true,
- // as such QScriptValues become invalid QVariants.
- QVariant v;
- if (scriptValue.isFunction()) {
- v = scriptValue.toString();
- } else {
- v = scriptValue.toVariant();
- QVariantMap m = v.toMap();
- if (m.contains(StringConstants::importScopeNamePropertyInternal())) {
- QVariantMap tmp = m;
- m = scriptValue.prototype().toVariant().toMap();
- for (auto it = tmp.begin(); it != tmp.end(); ++it)
- m.insert(it.key(), it.value());
- v = m;
- }
- }
-
- if (pd.type() == PropertyDeclaration::Path && v.isValid()) {
- v = v.toString();
- } else if (pd.type() == PropertyDeclaration::PathList
- || pd.type() == PropertyDeclaration::StringList) {
- v = v.toStringList();
- } else if (pd.type() == PropertyDeclaration::VariantList) {
- v = v.toList();
- }
- result[propName] = v;
- break;
- }
- case Value::VariantValueType:
- {
- if (result.contains(propName))
- break;
- VariantValuePtr vvp = std::static_pointer_cast<VariantValue>(propValue);
- QVariant v = vvp->value();
-
- if (v.isNull() && !item->propertyDeclaration(propName).isScalar()) // QTBUG-51237
- v = QStringList();
-
- result[propName] = v;
- break;
- }
- }
-}
-
-void ProjectResolver::collectPropertiesForExportItem(const QualifiedId &moduleName,
- const ValuePtr &value, Item *moduleInstance, QVariantMap &moduleProps)
-{
- QBS_CHECK(value->type() == Value::ItemValueType);
- Item * const itemValueItem = std::static_pointer_cast<ItemValue>(value)->item();
- if (itemValueItem->type() == ItemType::ModuleInstance) {
- struct EvalPreparer {
- EvalPreparer(Item *valueItem, Item *moduleInstance, const QualifiedId &moduleName)
- : valueItem(valueItem), oldScope(valueItem->scope()),
- hadName(!!valueItem->variantProperty(StringConstants::nameProperty()))
- {
- valueItem->setScope(moduleInstance);
- if (!hadName) {
- // EvaluatorScriptClass expects a name here.
- valueItem->setProperty(StringConstants::nameProperty(),
- VariantValue::create(moduleName.toString()));
- }
- }
- ~EvalPreparer()
- {
- valueItem->setScope(oldScope);
- if (!hadName)
- valueItem->setProperty(StringConstants::nameProperty(), VariantValuePtr());
- }
- Item * const valueItem;
- Item * const oldScope;
- const bool hadName;
- };
- EvalPreparer ep(itemValueItem, moduleInstance, moduleName);
- moduleProps.insert(moduleName.toString(), evaluateProperties(itemValueItem, false, false));
- return;
- }
- QBS_CHECK(itemValueItem->type() == ItemType::ModulePrefix);
- const Item::PropertyMap &props = itemValueItem->properties();
- for (auto it = props.begin(); it != props.end(); ++it) {
- QualifiedId fullModuleName = moduleName;
- fullModuleName << it.key();
- collectPropertiesForExportItem(fullModuleName, it.value(), moduleInstance, moduleProps);
- }
-}
-
-void ProjectResolver::createProductConfig(ResolvedProduct *product)
-{
- EvalCacheEnabler cachingEnabler(m_evaluator);
- m_evaluator->setPathPropertiesBaseDir(m_productContext->product->sourceDirectory);
- product->moduleProperties->setValue(evaluateModuleValues(m_productContext->item));
- product->productProperties = evaluateProperties(m_productContext->item, m_productContext->item,
- QVariantMap(), true, true);
- m_evaluator->clearPathPropertiesBaseDir();
-}
-
-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.parentContext = parentProjectContext;
- subProjectContext.project = ResolvedProject::create();
- parentProjectContext->project->subProjects.push_back(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
deleted file mode 100644
index 428ba144d..000000000
--- a/src/lib/corelib/language/projectresolver.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "qualifiedid.h"
-
-#include <logging/logger.h>
-#include <tools/set.h>
-
-#include <QtCore/qhash.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qstringlist.h>
-
-#include <utility>
-#include <vector>
-
-namespace qbs {
-class JobLimits;
-namespace Internal {
-
-class Evaluator;
-class Item;
-class ProgressObserver;
-class ScriptEngine;
-
-class ProjectResolver
-{
-public:
- ProjectResolver(Evaluator *evaluator, ModuleLoaderResult loadResult,
- SetupProjectParameters setupParameters, Logger &logger);
- ~ProjectResolver();
-
- void setProgressObserver(ProgressObserver *observer);
- TopLevelProjectPtr resolve();
-
- static void applyFileTaggers(const SourceArtifactPtr &artifact,
- const ResolvedProductConstPtr &product);
-
- using FileLocations = QHash<std::pair<QString, QString>, CodeLocation>;
- static SourceArtifactPtr createSourceArtifact(const ResolvedProductPtr &rproduct,
- const QString &fileName, const GroupPtr &group, bool wildcard,
- const CodeLocation &filesLocation = CodeLocation(),
- FileLocations *fileLocations = nullptr, ErrorInfo *errorInfo = nullptr);
-
-private:
- struct ProjectContext;
- struct ProductContext;
- struct ModuleContext;
- class ProductContextSwitcher;
-
- void checkCancelation() const;
- QString verbatimValue(const ValueConstPtr &value, bool *propertyWasSet = nullptr) const;
- QString verbatimValue(Item *item, const QString &name, bool *propertyWasSet = nullptr) 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 resolveProjectFully(Item *item, ProjectContext *projectContext);
- void resolveSubProject(Item *item, ProjectContext *projectContext);
- void resolveProduct(Item *item, ProjectContext *projectContext);
- void resolveProductFully(Item *item, ProjectContext *projectContext);
- void resolveModules(const Item *item, ProjectContext *projectContext);
- void resolveModule(const QualifiedId &moduleName, Item *item, bool isProduct,
- const QVariantMap &parameters, JobLimits &jobLimits,
- ProjectContext *projectContext);
- void gatherProductTypes(ResolvedProduct *product, Item *item);
- QVariantMap resolveAdditionalModuleProperties(const Item *group,
- const QVariantMap &currentValues);
- void resolveGroup(Item *item, ProjectContext *projectContext);
- void resolveGroupFully(Item *item, ProjectContext *projectContext, bool isEnabled);
- void resolveShadowProduct(Item *item, ProjectContext *);
- void resolveExport(Item *exportItem, ProjectContext *);
- std::unique_ptr<ExportedItem> resolveExportChild(const Item *item,
- const ExportedModule &module);
- void resolveRule(Item *item, ProjectContext *projectContext);
- void resolveRuleArtifact(const RulePtr &rule, Item *item);
- void resolveRuleArtifactBinding(const RuleArtifactPtr &ruleArtifact, Item *item,
- const QStringList &namePrefix,
- QualifiedIdSet *seenBindings);
- void resolveFileTagger(Item *item, ProjectContext *projectContext);
- void resolveJobLimit(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, bool checkErrors);
- QVariantMap evaluateProperties(const Item *item, const Item *propertiesContainer,
- const QVariantMap &tmplt, bool lookupPrototype,
- bool checkErrors);
- void evaluateProperty(const Item *item, const QString &propName, const ValuePtr &propValue,
- QVariantMap &result, bool checkErrors);
- void createProductConfig(ResolvedProduct *product);
- ProjectContext createProjectContext(ProjectContext *parentProjectContext) const;
- void adaptExportedPropertyValues(const Item *shadowProductItem);
- void collectExportedProductDependencies();
-
- struct ProductDependencyInfo
- {
- ProductDependencyInfo(const ResolvedProductPtr &product,
- const QVariantMap &parameters = QVariantMap())
- : product(product), parameters(parameters)
- {
- }
-
- ResolvedProductPtr product;
- QVariantMap parameters;
- };
-
- struct ProductDependencyInfos
- {
- std::vector<ProductDependencyInfo> dependencies;
- bool hasDisabledDependency = false;
- };
-
- ProductDependencyInfos getProductDependencies(const ResolvedProductConstPtr &product,
- const ModuleLoaderResult::ProductInfo &productInfo);
- QString sourceCodeAsFunction(const JSSourceValueConstPtr &value,
- const PropertyDeclaration &decl) const;
- QString sourceCodeForEvaluation(const JSSourceValueConstPtr &value) const;
- static void matchArtifactProperties(const ResolvedProductPtr &product,
- const std::vector<SourceArtifactPtr> &artifacts);
- void printProfilingInfo();
-
- void collectPropertiesForExportItem(Item *productModuleInstance);
- void collectPropertiesForModuleInExportItem(const Item::Module &module);
-
- void collectPropertiesForExportItem(const QualifiedId &moduleName,
- const ValuePtr &value, Item *moduleInstance, QVariantMap &moduleProps);
- void setupExportedProperties(const Item *item, const QString &namePrefix,
- std::vector<ExportedProperty> &properties);
-
- Evaluator *m_evaluator = nullptr;
- Logger &m_logger;
- ScriptEngine *m_engine = nullptr;
- ProgressObserver *m_progressObserver = nullptr;
- ProductContext *m_productContext = nullptr;
- ModuleContext *m_moduleContext = nullptr;
- QMap<QString, ResolvedProductPtr> m_productsByName;
- QHash<FileTag, QList<ResolvedProductPtr> > m_productsByType;
- QHash<ResolvedProductPtr, Item *> m_productItemMap;
- mutable QHash<FileContextConstPtr, ResolvedFileContextPtr> m_fileContextMap;
- mutable QHash<CodeLocation, ScriptFunctionPtr> m_scriptFunctionMap;
- mutable QHash<std::pair<QStringRef, QStringList>, QString> m_scriptFunctions;
- mutable QHash<QStringRef, QString> m_sourceCode;
- const SetupProjectParameters m_setupParams;
- ModuleLoaderResult m_loadResult;
- Set<CodeLocation> m_groupLocationWarnings;
- std::vector<std::pair<ResolvedProductPtr, Item *>> m_productExportInfo;
- std::vector<ErrorInfo> m_queuedErrors;
- qint64 m_elapsedTimeModPropEval = 0;
- qint64 m_elapsedTimeAllPropEval = 0;
- qint64 m_elapsedTimeGroups = 0;
-
- typedef void (ProjectResolver::*ItemFuncPtr)(Item *item, ProjectContext *projectContext);
- using ItemFuncMap = QMap<ItemType, ItemFuncPtr>;
- void callItemFunction(const ItemFuncMap &mappings, Item *item, ProjectContext *projectContext);
-};
-
-} // namespace Internal
-} // namespace qbs
-
-#endif // PROJECTRESOLVER_H
diff --git a/src/lib/corelib/language/property.h b/src/lib/corelib/language/property.h
index 204704672..302973d19 100644
--- a/src/lib/corelib/language/property.h
+++ b/src/lib/corelib/language/property.h
@@ -65,9 +65,13 @@ public:
{
}
- Property(const QString &product, const QString &module, const QString &property,
- const QVariant &v, Kind k)
- : productName(product), moduleName(module), propertyName(property), value(v), kind(k)
+ Property(QString product, QString module, QString property,
+ QVariant v, Kind k)
+ : productName(std::move(product))
+ , moduleName(std::move(module))
+ , propertyName(std::move(property))
+ , value(std::move(v))
+ , kind(k)
{
}
@@ -90,7 +94,7 @@ inline bool operator==(const Property &p1, const Property &p2)
}
bool operator<(const Property &p1, const Property &p2);
-inline uint qHash(const Property &p)
+inline auto qHash(const Property &p)
{
return QT_PREPEND_NAMESPACE(qHash)(p.productName + p.moduleName + p.propertyName);
}
diff --git a/src/lib/corelib/language/propertydeclaration.cpp b/src/lib/corelib/language/propertydeclaration.cpp
index abe6a1626..d56ab3bb0 100644
--- a/src/lib/corelib/language/propertydeclaration.cpp
+++ b/src/lib/corelib/language/propertydeclaration.cpp
@@ -40,16 +40,53 @@
#include "propertydeclaration.h"
#include "deprecationinfo.h"
-#include <api/languageinfo.h>
+#include "filecontext.h"
+#include "item.h"
+#include "qualifiedid.h"
+#include "value.h"
+#include <api/languageinfo.h>
+#include <loader/loaderutils.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/qttools.h>
#include <tools/stringconstants.h>
+#include <QtCore/qmetatype.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qstringlist.h>
+#include <QtCore/qvariant.h>
namespace qbs {
namespace Internal {
+// returns QMetaType::UnknownType for types that do not need conversion
+static QMetaType::Type variantType(PropertyDeclaration::Type t)
+{
+ switch (t) {
+ case PropertyDeclaration::UnknownType:
+ break;
+ case PropertyDeclaration::Boolean:
+ return QMetaType::Bool;
+ case PropertyDeclaration::Integer:
+ return QMetaType::Int;
+ case PropertyDeclaration::Path:
+ return QMetaType::QString;
+ case PropertyDeclaration::PathList:
+ return QMetaType::QStringList;
+ case PropertyDeclaration::String:
+ return QMetaType::QString;
+ case PropertyDeclaration::StringList:
+ return QMetaType::QStringList;
+ case PropertyDeclaration::VariantList:
+ return QMetaType::QVariantList;
+ case PropertyDeclaration::Variant:
+ break;
+ }
+ return QMetaType::UnknownType;
+}
+
class PropertyDeclarationData : public QSharedData
{
public:
@@ -62,13 +99,13 @@ public:
QString name;
PropertyDeclaration::Type type;
PropertyDeclaration::Flags flags;
+ QStringList allowedValues;
QString description;
QString initialValueSource;
QStringList functionArgumentNames;
DeprecationInfo deprecationInfo;
};
-
PropertyDeclaration::PropertyDeclaration()
: d(new PropertyDeclarationData)
{
@@ -182,6 +219,16 @@ void PropertyDeclaration::setFlags(Flags f)
d->flags = f;
}
+const QStringList &PropertyDeclaration::allowedValues() const
+{
+ return d->allowedValues;
+}
+
+void PropertyDeclaration::setAllowedValues(const QStringList &v)
+{
+ d->allowedValues = v;
+}
+
const QString &PropertyDeclaration::description() const
{
return d->description;
@@ -232,5 +279,236 @@ void PropertyDeclaration::setDeprecationInfo(const DeprecationInfo &deprecationI
d->deprecationInfo = deprecationInfo;
}
+ErrorInfo PropertyDeclaration::checkForDeprecation(DeprecationWarningMode mode,
+ const CodeLocation &loc, Logger &logger) const
+{
+ return deprecationInfo().checkForDeprecation(mode, name(), loc, false, logger);
+}
+
+QVariant PropertyDeclaration::convertToPropertyType(const QVariant &v, Type t,
+ const QStringList &namePrefix, const QString &key)
+{
+ if (v.isNull() || !v.isValid())
+ return v;
+ const auto vt = variantType(t);
+ if (vt == QMetaType::UnknownType)
+ return v;
+
+ // Handle the foo,bar,bla stringlist syntax.
+ if (t == PropertyDeclaration::StringList && v.userType() == QMetaType::QString)
+ return v.toString().split(QLatin1Char(','));
+
+ QVariant c = v;
+ if (!qVariantConvert(c, 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;
+}
+
+QVariant PropertyDeclaration::typedNullValue() const
+{
+ switch (type()) {
+ case PropertyDeclaration::Boolean:
+ return typedNullVariant<bool>();
+ case PropertyDeclaration::Integer:
+ return typedNullVariant<int>();
+ case PropertyDeclaration::VariantList:
+ return typedNullVariant<QVariantList>();
+ case PropertyDeclaration::String:
+ case PropertyDeclaration::Path:
+ return typedNullVariant<QString>();
+ case PropertyDeclaration::StringList:
+ case PropertyDeclaration::PathList:
+ return typedNullVariant<QStringList>();
+ default:
+ return {};
+ }
+}
+
+bool PropertyDeclaration::shouldCheckAllowedValues() const
+{
+ return isValid()
+ && (d->type == PropertyDeclaration::String || d->type == PropertyDeclaration::StringList)
+ && !d->allowedValues.empty();
+}
+
+void PropertyDeclaration::checkAllowedValues(
+ const QVariant &value,
+ const CodeLocation &loc,
+ const QString &key,
+ LoaderState &loaderState) const
+{
+ const auto type = d->type;
+ if (!shouldCheckAllowedValues())
+ return;
+
+ if (value.isNull())
+ return;
+
+ const auto &allowedValues = d->allowedValues;
+
+ const auto checkValue = [&loc, &allowedValues, &key, &loaderState](const QString &value)
+ {
+ if (!allowedValues.contains(value)) {
+ const auto message = Tr::tr("Value '%1' is not allowed for property '%2'.")
+ .arg(value, key);
+ ErrorInfo error(message, loc);
+ handlePropertyError(error, loaderState.parameters(), loaderState.logger());
+ }
+ };
+
+ if (type == PropertyDeclaration::StringList) {
+ const auto strings = value.toStringList();
+ for (const auto &string: strings) {
+ checkValue(string);
+ }
+ } else if (type == PropertyDeclaration::String) {
+ checkValue(value.toString());
+ }
+}
+
+namespace {
+class PropertyDeclarationCheck : public ValueHandler
+{
+public:
+ PropertyDeclarationCheck(LoaderState &loaderState) : m_loaderState(loaderState) {}
+ void operator()(Item *item)
+ {
+ m_checkingProject = item->type() == ItemType::Project;
+ handleItem(item);
+ }
+
+private:
+ void handle(JSSourceValue *value) override
+ {
+ if (!value->createdByPropertiesBlock()) {
+ const ErrorInfo error(Tr::tr("Property '%1' is not declared.")
+ .arg(m_currentName), value->location());
+ handlePropertyError(error, m_loaderState.parameters(), m_loaderState.logger());
+ }
+ }
+ void handle(ItemValue *value) override
+ {
+ if (checkItemValue(value))
+ handleItem(value->item());
+ }
+ bool checkItemValue(ItemValue *value)
+ {
+ // TODO: Remove once QBS-1030 is fixed.
+ if (parentItem()->type() == ItemType::Artifact)
+ return false;
+
+ if (parentItem()->type() == ItemType::Properties)
+ return false;
+
+ // TODO: Check where the in-between module instances come from.
+ if (value->item()->type() == ItemType::ModuleInstancePlaceholder) {
+ for (auto it = m_parentItems.rbegin(); it != m_parentItems.rend(); ++it) {
+ if ((*it)->type() == ItemType::Group)
+ return false;
+ if ((*it)->type() == ItemType::ModulePrefix)
+ continue;
+ break;
+ }
+ }
+
+ if (value->item()->type() != ItemType::ModuleInstance
+ && value->item()->type() != ItemType::ModulePrefix
+ && (!parentItem()->file() || !parentItem()->file()->idScope()
+ || !parentItem()->file()->idScope()->hasProperty(m_currentName))
+ && !value->createdByPropertiesBlock()) {
+ CodeLocation location = value->location();
+ for (int i = int(m_parentItems.size() - 1); !location.isValid() && i >= 0; --i)
+ location = m_parentItems.at(i)->location();
+ const ErrorInfo error(Tr::tr("Item '%1' is not declared. "
+ "Did you forget to add a Depends item?")
+ .arg(m_currentModuleName.toString()), location);
+ handlePropertyError(error, m_loaderState.parameters(), m_loaderState.logger());
+ return false;
+ }
+
+ return true;
+ }
+ void handleItem(Item *item)
+ {
+ if (m_checkingProject && item->type() == ItemType::Product)
+ return;
+ if (!m_handledItems.insert(item).second)
+ return;
+ if (item->type() == ItemType::Module
+ || item->type() == ItemType::Export
+ || (item->type() == ItemType::ModuleInstance && !item->isPresentModule())
+ || item->type() == ItemType::Properties
+
+ // The Properties child of a SubProject item is not a regular item.
+ || item->type() == ItemType::PropertiesInSubProject
+
+ || m_loaderState.topLevelProject().isDisabledItem(item)) {
+ return;
+ }
+
+ m_parentItems.push_back(item);
+ for (Item::PropertyMap::const_iterator it = item->properties().constBegin();
+ it != item->properties().constEnd(); ++it) {
+ if (item->type() == ItemType::Product && it.key() == StringConstants::moduleProviders()
+ && it.value()->type() == Value::ItemValueType)
+ continue;
+ const PropertyDeclaration decl = item->propertyDeclaration(it.key());
+ if (decl.isValid()) {
+ const ErrorInfo deprecationError = decl.checkForDeprecation(
+ m_loaderState.parameters().deprecationWarningMode(), it.value()->location(),
+ m_loaderState.logger());
+ if (deprecationError.hasError()) {
+ handlePropertyError(deprecationError, m_loaderState.parameters(),
+ m_loaderState.logger());
+ }
+ continue;
+ }
+ m_currentName = it.key();
+ const QualifiedId oldModuleName = m_currentModuleName;
+ if (parentItem()->type() != ItemType::ModulePrefix)
+ m_currentModuleName.clear();
+ m_currentModuleName.push_back(m_currentName);
+ it.value()->apply(this);
+ m_currentModuleName = oldModuleName;
+ }
+ m_parentItems.pop_back();
+ for (Item * const child : item->children()) {
+ switch (child->type()) {
+ case ItemType::Export:
+ case ItemType::Depends:
+ case ItemType::Parameter:
+ case ItemType::Parameters:
+ break;
+ case ItemType::Group:
+ if (item->type() == ItemType::Module || item->type() == ItemType::ModuleInstance)
+ break;
+ Q_FALLTHROUGH();
+ default:
+ handleItem(child);
+ }
+ }
+ }
+ void handle(VariantValue *) override { /* only created internally - no need to check */ }
+
+ Item *parentItem() const { return m_parentItems.back(); }
+
+ LoaderState &m_loaderState;
+ Set<Item *> m_handledItems;
+ std::vector<Item *> m_parentItems;
+ QualifiedId m_currentModuleName;
+ QString m_currentName;
+ bool m_checkingProject = false;
+};
+} // namespace
+
+void checkPropertyDeclarations(Item *topLevelItem, LoaderState &loaderState)
+{
+ (PropertyDeclarationCheck(loaderState))(topLevelItem);
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/propertydeclaration.h b/src/lib/corelib/language/propertydeclaration.h
index 874275bde..79a39ecbd 100644
--- a/src/lib/corelib/language/propertydeclaration.h
+++ b/src/lib/corelib/language/propertydeclaration.h
@@ -40,17 +40,24 @@
#ifndef QBS_PROPERTYDECLARATION_H
#define QBS_PROPERTYDECLARATION_H
+#include <tools/deprecationwarningmode.h>
+
#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h>
QT_BEGIN_NAMESPACE
-class QStringList;
+class QVariant;
QT_END_NAMESPACE
namespace qbs {
+class CodeLocation;
+class ErrorInfo;
namespace Internal {
class DeprecationInfo;
class PropertyDeclarationData;
+class Item;
+class LoaderState;
+class Logger;
class PropertyDeclaration
{
@@ -100,6 +107,9 @@ public:
Flags flags() const;
void setFlags(Flags f);
+ const QStringList &allowedValues() const;
+ void setAllowedValues(const QStringList &v);
+
const QString &description() const;
void setDescription(const QString &str);
@@ -113,11 +123,27 @@ public:
bool isExpired() const;
const DeprecationInfo &deprecationInfo() const;
void setDeprecationInfo(const DeprecationInfo &deprecationInfo);
+ ErrorInfo checkForDeprecation(DeprecationWarningMode mode, const CodeLocation &loc,
+ Logger &logger) const;
+
+ static QVariant convertToPropertyType(
+ const QVariant &v, Type t, const QStringList &namePrefix, const QString &key);
+ QVariant typedNullValue() const;
+
+ bool shouldCheckAllowedValues() const;
+ void checkAllowedValues(
+ const QVariant &value,
+ const CodeLocation &loc,
+ const QString &key,
+ LoaderState &loaderState) const;
private:
QSharedDataPointer<PropertyDeclarationData> d;
};
+void checkPropertyDeclarations(Item *topLevelItem, LoaderState &loaderState);
+
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/propertymapinternal.h b/src/lib/corelib/language/propertymapinternal.h
index 83e18ba48..af551cf6f 100644
--- a/src/lib/corelib/language/propertymapinternal.h
+++ b/src/lib/corelib/language/propertymapinternal.h
@@ -77,7 +77,7 @@ private:
inline bool operator==(const PropertyMapInternal &lhs, const PropertyMapInternal &rhs)
{
- return lhs.m_value == rhs.m_value;
+ return qVariantsEqual(lhs.m_value, rhs.m_value);
}
QVariant QBS_AUTOTEST_EXPORT moduleProperty(const QVariantMap &properties,
diff --git a/src/lib/corelib/language/qualifiedid.cpp b/src/lib/corelib/language/qualifiedid.cpp
index 9eb0e9463..87248ac21 100644
--- a/src/lib/corelib/language/qualifiedid.cpp
+++ b/src/lib/corelib/language/qualifiedid.cpp
@@ -58,7 +58,7 @@ QualifiedId::QualifiedId(const QStringList &nameParts)
QualifiedId QualifiedId::fromString(const QString &str)
{
- return QualifiedId(str.split(QLatin1Char('.')));
+ return {str.split(QLatin1Char('.'))};
}
QString QualifiedId::toString() const
diff --git a/src/lib/corelib/language/qualifiedid.h b/src/lib/corelib/language/qualifiedid.h
index 2b4c9d286..7e8b388d3 100644
--- a/src/lib/corelib/language/qualifiedid.h
+++ b/src/lib/corelib/language/qualifiedid.h
@@ -60,7 +60,7 @@ public:
QString toString() const;
};
-inline uint qHash(const QualifiedId &qid) { return qHash(qid.toString()); }
+inline auto qHash(const QualifiedId &qid) { return qHash(qid.toString()); }
using QualifiedIdSet = Set<QualifiedId>;
diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp
index 7c531e764..9131db7f5 100644
--- a/src/lib/corelib/language/scriptengine.cpp
+++ b/src/lib/corelib/language/scriptengine.cpp
@@ -46,16 +46,19 @@
#include "preparescriptobserver.h"
#include <buildgraph/artifact.h>
+#include <buildgraph/rulenode.h>
#include <jsextensions/jsextensions.h>
#include <logging/translator.h>
#include <tools/error.h>
#include <tools/fileinfo.h>
#include <tools/profiling.h>
#include <tools/qbsassert.h>
+#include <tools/scripttools.h>
#include <tools/qttools.h>
#include <tools/stlutils.h>
#include <tools/stringconstants.h>
+#include <QtCore/qdatetime.h>
#include <QtCore/qdebug.h>
#include <QtCore/qdiriterator.h>
#include <QtCore/qfile.h>
@@ -63,18 +66,15 @@
#include <QtCore/qtextstream.h>
#include <QtCore/qtimer.h>
-#include <QtScript/qscriptclass.h>
-#include <QtScript/qscriptvalueiterator.h>
-
+#include <cstring>
#include <functional>
#include <set>
#include <utility>
+#include <vector>
namespace qbs {
namespace Internal {
-static QString getterFuncHelperProperty() { return QStringLiteral("qbsdata"); }
-
const bool debugJSImports = false;
bool operator==(const ScriptEngine::PropertyCacheKey &lhs,
@@ -85,133 +85,222 @@ bool operator==(const ScriptEngine::PropertyCacheKey &lhs,
&& lhs.m_propertyName == rhs.m_propertyName;
}
-static inline uint combineHash(uint h1, uint h2, uint seed)
+static QHashValueType combineHash(QHashValueType h1, QHashValueType h2, QHashValueType seed)
{
// stolen from qHash(QPair)
return ((h1 << 16) | (h1 >> 16)) ^ h2 ^ seed;
}
-uint qHash(const ScriptEngine::PropertyCacheKey &k, uint seed = 0)
+QHashValueType qHash(const ScriptEngine::PropertyCacheKey &k, QHashValueType seed = 0)
{
return combineHash(qHash(k.m_moduleName),
combineHash(qHash(k.m_propertyName), qHash(k.m_propertyMap), seed), seed);
}
-std::mutex ScriptEngine::m_creationDestructionMutex;
-
-ScriptEngine::ScriptEngine(Logger &logger, EvalContext evalContext, QObject *parent)
- : QScriptEngine(parent), m_scriptImporter(new ScriptImporter(this)),
- m_modulePropertyScriptClass(nullptr),
- m_propertyCacheEnabled(true), m_active(false), m_logger(logger), m_evalContext(evalContext),
+ScriptEngine::ScriptEngine(Logger &logger, EvalContext evalContext, PrivateTag)
+ : m_scriptImporter(new ScriptImporter(this)),
+ m_logger(logger), m_evalContext(evalContext),
m_observer(new PrepareScriptObserver(this, UnobserveMode::Disabled))
{
- setProcessEventsInterval(1000); // For the cancelation mechanism to work.
- m_cancelationError = currentContext()->throwValue(tr("Execution canceled"));
- QScriptValue objectProto = globalObject().property(QStringLiteral("Object"));
- m_definePropertyFunction = objectProto.property(QStringLiteral("defineProperty"));
- QBS_ASSERT(m_definePropertyFunction.isFunction(), /* ignore */);
- m_emptyFunction = evaluate(QStringLiteral("(function(){})"));
- QBS_ASSERT(m_emptyFunction.isFunction(), /* ignore */);
- // Initially push a new context to turn off scope chain insanity mode.
- QScriptEngine::pushContext();
+ setMaxStackSize();
+ JS_SetRuntimeOpaque(m_jsRuntime, this);
+ JS_SetInterruptHandler(m_jsRuntime, interruptor, this);
+ setScopeLookup(m_context, &ScriptEngine::doExtraScopeLookup);
+ setFoundUndefinedHandler(m_context, &ScriptEngine::handleUndefinedFound);
+ setFunctionEnteredHandler(m_context, &ScriptEngine::handleFunctionEntered);
+ setFunctionExitedHandler(m_context, &ScriptEngine::handleFunctionExited);
+ m_dataWithPtrClass = registerClass("__data", nullptr, nullptr, JS_UNDEFINED);
installQbsBuiltins();
extendJavaScriptBuiltins();
}
-ScriptEngine *ScriptEngine::create(Logger &logger, EvalContext evalContext, QObject *parent)
+std::unique_ptr<ScriptEngine> ScriptEngine::create(Logger &logger, EvalContext evalContext)
{
- std::lock_guard<std::mutex> lock(m_creationDestructionMutex);
- return new ScriptEngine(logger, evalContext, parent);
+ return std::make_unique<ScriptEngine>(logger, evalContext, PrivateTag());
}
-ScriptEngine::~ScriptEngine()
+ScriptEngine *ScriptEngine::engineForRuntime(const JSRuntime *runtime)
+{
+ return static_cast<ScriptEngine *>(JS_GetRuntimeOpaque(const_cast<JSRuntime *>(runtime)));
+
+}
+
+ScriptEngine *ScriptEngine::engineForContext(const JSContext *ctx)
+{
+ return engineForRuntime(JS_GetRuntime(const_cast<JSContext *>(ctx)));
+}
+
+LookupResult ScriptEngine::doExtraScopeLookup(JSContext *ctx, JSAtom prop)
+{
+ static const LookupResult fail{JS_UNDEFINED, JS_UNDEFINED, false};
+
+ ScriptEngine * const engine = engineForContext(ctx);
+ engine->m_lastLookupWasSuccess = false;
+
+ JSValueList scopes;
+ if (!engine->m_scopeChains.isEmpty())
+ scopes = engine->m_scopeChains.last();
+ if (JS_IsObject(engine->m_globalObject))
+ scopes.insert(scopes.begin(), engine->m_globalObject);
+ for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) {
+ const JSValue v = JS_GetProperty(ctx, *it, prop);
+ if (!JS_IsUndefined(v) || engine->m_lastLookupWasSuccess) {
+ engine->m_lastLookupWasSuccess = false;
+ return {v, *it, true};
+ }
+ }
+ return fail;
+}
+
+void ScriptEngine::handleUndefinedFound(JSContext *ctx)
+{
+ engineForContext(ctx)->setLastLookupStatus(true);
+}
+
+void ScriptEngine::handleFunctionEntered(JSContext *ctx, JSValue this_obj)
+{
+ ScriptEngine::engineForContext(ctx)->m_contextStack.push_back(this_obj);
+}
+
+void ScriptEngine::handleFunctionExited(JSContext *ctx)
{
- m_creationDestructionMutex.lock();
- connect(this, &QObject::destroyed, std::bind(&std::mutex::unlock, &m_creationDestructionMutex));
+ ScriptEngine::engineForContext(ctx)->m_contextStack.pop_back();
+}
- releaseResourcesOfScriptObjects();
- delete (m_scriptImporter);
+ScriptEngine::~ScriptEngine()
+{
+ reset();
+ delete m_scriptImporter;
if (m_elapsedTimeImporting != -1) {
m_logger.qbsLog(LoggerInfo, true) << Tr::tr("Setting up imports took %1.")
.arg(elapsedTimeString(m_elapsedTimeImporting));
}
- delete m_modulePropertyScriptClass;
- delete m_productPropertyScriptClass;
+ for (const auto &ext : std::as_const(m_internalExtensions))
+ JS_FreeValue(m_context, ext);
+ for (const JSValue &s : std::as_const(m_stringCache))
+ JS_FreeValue(m_context, s);
+ for (JSValue * const externalRef : std::as_const(m_externallyCachedValues)) {
+ JS_FreeValue(m_context, *externalRef);
+ *externalRef = JS_UNDEFINED;
+ }
+ setPropertyOnGlobalObject(QLatin1String("console"), JS_UNDEFINED);
+ JS_FreeContext(m_context);
+ JS_FreeRuntime(m_jsRuntime);
}
-void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, QScriptValue &targetObject,
- ObserveMode observeMode)
+void ScriptEngine::reset()
{
- installImportFunctions();
- m_currentDirPathStack.push(FileInfo::path(fileCtx->filePath()));
- m_extensionSearchPathsStack.push(fileCtx->searchPaths());
- m_observeMode = observeMode;
-
- for (const JsImport &jsImport : fileCtx->jsImports())
- import(jsImport, targetObject);
- if (m_observeMode == ObserveMode::Enabled) {
- for (QScriptValue &sv : m_requireResults)
- observeImport(sv);
- m_requireResults.clear();
+ // TODO: Check whether we can keep file and imports cache.
+ // We'd have to find a solution for the scope name problem then.
+ clearImportsCache();
+ for (const auto &e : std::as_const(m_jsFileCache))
+ JS_FreeValue(m_context, e.second);
+ m_jsFileCache.clear();
+
+ for (const JSValue &s : std::as_const(m_jsValueCache))
+ JS_FreeValue(m_context, s);
+ m_jsValueCache.clear();
+
+ for (auto it = m_evalResults.cbegin(); it != m_evalResults.cend(); ++it) {
+ for (int i = 0; i < it.value(); ++i)
+ JS_FreeValue(m_context, it.key());
+ }
+ m_evalResults.clear();
+ for (const auto &e : std::as_const(m_projectScriptValues))
+ JS_FreeValue(m_context, e.second);
+ m_projectScriptValues.clear();
+ for (const auto &e : std::as_const(m_baseProductScriptValues))
+ JS_FreeValue(m_context, e.second);
+ m_baseProductScriptValues.clear();
+ for (const auto &e : std::as_const(m_productArtifactsMapScriptValues))
+ JS_FreeValue(m_context, e.second);
+ m_productArtifactsMapScriptValues.clear();
+ for (const auto &e : std::as_const(m_moduleArtifactsMapScriptValues))
+ JS_FreeValue(m_context, e.second);
+ m_moduleArtifactsMapScriptValues.clear();
+ for (const auto &e : std::as_const(m_baseModuleScriptValues))
+ JS_FreeValue(m_context, e.second);
+ m_baseModuleScriptValues.clear();
+ for (auto it = m_artifactsScriptValues.cbegin(); it != m_artifactsScriptValues.cend(); ++it) {
+ it.key().first->setDeregister({});
+ JS_FreeValue(m_context, it.value());
}
+ m_artifactsScriptValues.clear();
+}
- m_currentDirPathStack.pop();
- m_extensionSearchPathsStack.pop();
- uninstallImportFunctions();
+void ScriptEngine::import(const FileContextBaseConstPtr &fileCtx, JSValue &targetObject,
+ ObserveMode observeMode)
+{
+ Importer(*this, fileCtx, targetObject, observeMode).run();
}
-void ScriptEngine::import(const JsImport &jsImport, QScriptValue &targetObject)
+void ScriptEngine::import(const JsImport &jsImport, JSValue &targetObject)
{
- QBS_ASSERT(targetObject.isObject(), return);
- QBS_ASSERT(targetObject.engine() == this, return);
+ QBS_ASSERT(JS_IsObject(targetObject), return);
if (debugJSImports)
qDebug() << "[ENGINE] import into " << jsImport.scopeName;
- QScriptValue jsImportValue = m_jsImportCache.value(jsImport);
- if (jsImportValue.isValid()) {
+ JSValue jsImportValue = m_jsImportCache.value(jsImport);
+ if (JS_IsObject(jsImportValue)) {
if (debugJSImports)
qDebug() << "[ENGINE] " << jsImport.filePaths << " (cache hit)";
} else {
if (debugJSImports)
qDebug() << "[ENGINE] " << jsImport.filePaths << " (cache miss)";
- jsImportValue = newObject();
+
+ ScopedJsValue scopedImportValue(m_context, JS_NewObject(m_context));
for (const QString &filePath : jsImport.filePaths)
- importFile(filePath, jsImportValue);
+ importFile(filePath, scopedImportValue);
+ jsImportValue = scopedImportValue.release();
m_jsImportCache.insert(jsImport, jsImportValue);
std::vector<QString> &filePathsForScriptValue
- = m_filePathsPerImport[jsImportValue.objectId()];
- for (const QString &fp : jsImport.filePaths)
- filePathsForScriptValue.push_back(fp);
+ = m_filePathsPerImport[jsObjectId(jsImportValue)];
+ transform(jsImport.filePaths, filePathsForScriptValue, [](const auto &fp) {
+ return fp; });
}
- QScriptValue sv = newObject();
- sv.setPrototype(jsImportValue);
- sv.setProperty(StringConstants::importScopeNamePropertyInternal(), jsImport.scopeName);
- targetObject.setProperty(jsImport.scopeName, sv);
+ JSValue sv = JS_NewObjectProto(m_context, jsImportValue);
+ setJsProperty(m_context, sv, StringConstants::importScopeNamePropertyInternal(),
+ jsImport.scopeName);
+ setJsProperty(m_context, targetObject, jsImport.scopeName, sv);
if (m_observeMode == ObserveMode::Enabled)
observeImport(jsImportValue);
}
-void ScriptEngine::observeImport(QScriptValue &jsImport)
+void ScriptEngine::observeImport(JSValue &jsImport)
{
- if (!m_observer->addImportId(jsImport.objectId()))
+ if (!m_observer->addImportId(quintptr((JS_VALUE_GET_OBJ(jsImport)))))
return;
- QScriptValueIterator it(jsImport);
- while (it.hasNext()) {
- it.next();
- if (it.flags() & QScriptValue::PropertyGetter)
- continue;
- QScriptValue property = it.value();
- if (!property.isFunction())
- continue;
- setObservedProperty(jsImport, it.name(), property);
- }
+ handleJsProperties(jsImport, [this, &jsImport](const JSAtom &name,
+ const JSPropertyDescriptor &desc) {
+ if (!JS_IsFunction(m_context, desc.value))
+ return;
+ const char *const nameStr = JS_AtomToCString(m_context, name);
+ setObservedProperty(jsImport, QString::fromUtf8(nameStr, std::strlen(nameStr)), desc.value);
+ JS_FreeCString(m_context, nameStr);
+ });
}
void ScriptEngine::clearImportsCache()
{
+ for (const auto &jsImport : std::as_const(m_jsImportCache))
+ JS_FreeValue(m_context, jsImport);
m_jsImportCache.clear();
+ m_filePathsPerImport.clear();
+ m_observer->clearImportIds();
+}
+
+void ScriptEngine::registerEvaluator(Evaluator *evaluator)
+{
+ QBS_ASSERT(!m_evaluator, return);
+ m_evaluator = evaluator;
+}
+
+void ScriptEngine::unregisterEvaluator(const Evaluator *evaluator)
+{
+ QBS_ASSERT(m_evaluator == evaluator, return);
+ m_evaluator = nullptr;
}
void ScriptEngine::checkContext(const QString &operation,
@@ -240,7 +329,15 @@ void ScriptEngine::checkContext(const QString &operation,
QBS_ASSERT(false, continue);
break;
}
- m_logger.printWarning(ErrorInfo(warning, currentContext()->backtrace()));
+ if (!m_evalPositions.empty()) {
+ const JSValue exVal = JS_NewObject(m_context);
+ const auto &[file, line] = m_evalPositions.top();
+ build_backtrace(m_context, exVal, file.toUtf8().constData(), line, 0);
+ const JsException ex(m_context, exVal, {});
+ m_logger.printWarning(ErrorInfo(warning, ex.stackTrace()));
+ } else {
+ m_logger.printWarning(ErrorInfo(warning));
+ }
return;
}
}
@@ -251,7 +348,7 @@ void ScriptEngine::addPropertyRequestedFromArtifact(const Artifact *artifact,
m_propertiesRequestedFromArtifact[artifact->filePath()] << property;
}
-void ScriptEngine::addImportRequestedInScript(qint64 importValueId)
+void ScriptEngine::addImportRequestedInScript(quintptr importValueId)
{
// Import list is assumed to be small, so let's not use a set.
if (!contains(m_importsRequestedInScript, importValueId))
@@ -289,37 +386,21 @@ QVariant ScriptEngine::retrieveFromPropertyCache(const QString &moduleName,
return m_propertyCache.value(PropertyCacheKey(moduleName, propertyName, propertyMap));
}
-void ScriptEngine::defineProperty(QScriptValue &object, const QString &name,
- const QScriptValue &descriptor)
+static JSValue js_observedGet(JSContext *ctx, JSValueConst, int, JSValueConst *, int, JSValue *data)
{
- 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());
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ engine->observer()->onPropertyRead(data[0], getJsString(ctx, data[1]), data[2]);
+ return JS_DupValue(engine->context(), data[2]);
}
-static QScriptValue js_observedGet(QScriptContext *context, QScriptEngine *,
- ScriptPropertyObserver * const observer)
+void ScriptEngine::setObservedProperty(JSValue &object, const QString &name,
+ const JSValue &value)
{
- const QScriptValue data = context->callee().property(getterFuncHelperProperty());
- 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)
-{
- QScriptValue data = newArray();
- data.setProperty(0, object);
- data.setProperty(1, name);
- data.setProperty(2, value);
- QScriptValue getterFunc = newFunction(js_observedGet,
- static_cast<ScriptPropertyObserver *>(m_observer.get()));
- getterFunc.setProperty(getterFuncHelperProperty(), data);
- object.setProperty(name, getterFunc, QScriptValue::PropertyGetter);
+ ScopedJsValue jsName(m_context, makeJsString(m_context, name));
+ JSValueList funcData{object, jsName, value};
+ JSValue getterFunc = JS_NewCFunctionData(m_context, &js_observedGet, 0, 0, 3, funcData.data());
+ const ScopedJsAtom nameAtom(m_context, name);
+ JS_DefinePropertyGetSet(m_context, object, nameAtom, getterFunc, JS_UNDEFINED, JS_PROP_HAS_GET | JS_PROP_ENUMERABLE);
if (m_observer->unobserveMode() == UnobserveMode::Enabled)
m_observedProperties.emplace_back(object, name, value);
}
@@ -327,38 +408,16 @@ void ScriptEngine::setObservedProperty(QScriptValue &object, const QString &name
void ScriptEngine::unobserveProperties()
{
for (auto &elem : m_observedProperties) {
- QScriptValue &object = std::get<0>(elem);
+ JSValue &object = std::get<0>(elem);
const QString &name = std::get<1>(elem);
- const QScriptValue &value = std::get<2>(elem);
- object.setProperty(name, QScriptValue(), QScriptValue::PropertyGetter);
- object.setProperty(name, value, QScriptValue::PropertyFlags());
+ const JSValue &value = std::get<2>(elem);
+ const ScopedJsAtom jsName(m_context, name);
+ JS_DefineProperty(m_context, object, jsName, value, JS_UNDEFINED, JS_UNDEFINED,
+ JS_PROP_HAS_VALUE);
}
m_observedProperties.clear();
}
-static QScriptValue js_deprecatedGet(QScriptContext *context, QScriptEngine *qtengine)
-{
- const auto engine = static_cast<const ScriptEngine *>(qtengine);
- const QScriptValue data = context->callee().property(getterFuncHelperProperty());
- 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(getterFuncHelperProperty(), data);
- object.setProperty(oldName, getterFunc, QScriptValue::PropertyGetter
- | QScriptValue::SkipInEnumeration);
-}
-
QProcessEnvironment ScriptEngine::environment() const
{
return m_environment;
@@ -369,19 +428,19 @@ void ScriptEngine::setEnvironment(const QProcessEnvironment &env)
m_environment = env;
}
-void ScriptEngine::importFile(const QString &filePath, QScriptValue &targetObject)
+void ScriptEngine::importFile(const QString &filePath, JSValue targetObject)
{
AccumulatingTimer importTimer(m_elapsedTimeImporting != -1 ? &m_elapsedTimeImporting : nullptr);
- QScriptValue &evaluationResult = m_jsFileCache[filePath];
- if (evaluationResult.isValid()) {
- ScriptImporter::copyProperties(evaluationResult, targetObject);
+ JSValue &evaluationResult = m_jsFileCache[filePath];
+ if (JS_IsObject(evaluationResult)) {
+ ScriptImporter::copyProperties(m_context, evaluationResult, targetObject);
return;
}
QFile file(filePath);
if (Q_UNLIKELY(!file.open(QFile::ReadOnly)))
- throw ErrorInfo(tr("Cannot open '%1'.").arg(filePath));
+ throw ErrorInfo(Tr::tr("Cannot open '%1'.").arg(filePath));
QTextStream stream(&file);
- stream.setCodec("UTF-8");
+ setupDefaultCodec(stream);
const QString sourceCode = stream.readAll();
file.close();
m_currentDirPathStack.push(FileInfo::path(filePath));
@@ -392,7 +451,7 @@ void ScriptEngine::importFile(const QString &filePath, QScriptValue &targetObjec
static QString findExtensionDir(const QStringList &searchPaths, const QString &extensionPath)
{
for (const QString &searchPath : searchPaths) {
- const QString dirPath = searchPath + QStringLiteral("/imports/") + extensionPath;
+ QString dirPath = searchPath + QStringLiteral("/imports/") + extensionPath;
QFileInfo fi(dirPath);
if (fi.exists() && fi.isDir())
return dirPath;
@@ -400,101 +459,178 @@ static QString findExtensionDir(const QStringList &searchPaths, const QString &e
return {};
}
-static QScriptValue mergeExtensionObjects(const QScriptValueList &lst)
+JSValue ScriptEngine::mergeExtensionObjects(const JSValueList &lst)
{
- QScriptValue result;
- for (const QScriptValue &v : lst) {
- if (!result.isValid()) {
+ JSValue result = JS_UNDEFINED;
+ for (const JSValue &v : lst) {
+ if (!JS_IsObject(result)) {
result = v;
continue;
}
- QScriptValueIterator svit(v);
- while (svit.hasNext()) {
- svit.next();
- result.setProperty(svit.name(), svit.value());
- }
+ ScriptImporter::copyProperties(m_context, v, result);
+ JS_FreeValue(m_context, v);
}
return result;
}
-static QScriptValue loadInternalExtension(QScriptContext *context, ScriptEngine *engine,
- const QString &uri)
+JSValue ScriptEngine::getInternalExtension(const char *name) const
{
- 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;
+ const auto cached = m_internalExtensions.constFind(QLatin1String(name));
+ if (cached != m_internalExtensions.constEnd())
+ return JS_DupValue(m_context, cached.value());
+ return JS_UNDEFINED;
+}
+
+void ScriptEngine::addInternalExtension(const char *name, JSValue ext)
+{
+ m_internalExtensions.insert(QLatin1String(name), JS_DupValue(m_context, ext));
}
-QScriptValue ScriptEngine::js_loadExtension(QScriptContext *context, QScriptEngine *qtengine)
+JSValue ScriptEngine::asJsValue(const QVariant &v, quintptr id, bool frozen)
{
- if (context->argumentCount() < 1) {
- return context->throwError(
- ScriptEngine::tr("The loadExtension function requires "
- "an extension name."));
+ if (v.isNull())
+ return JS_UNDEFINED;
+ switch (static_cast<QMetaType::Type>(v.userType())) {
+ case QMetaType::QByteArray:
+ return asJsValue(v.toByteArray());
+ case QMetaType::QString:
+ return asJsValue(v.toString());
+ case QMetaType::QStringList:
+ return asJsValue(v.toStringList());
+ case QMetaType::QVariantList:
+ return asJsValue(v.toList(), id, frozen);
+ case QMetaType::Int:
+ case QMetaType::UInt:
+ return JS_NewInt32(m_context, v.toInt());
+ case QMetaType::Long:
+ case QMetaType::ULong:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ return JS_NewInt64(m_context, v.toInt());
+ case QMetaType::Bool:
+ return JS_NewBool(m_context, v.toBool());
+ case QMetaType::QDateTime:
+ return JS_NewDate(
+ m_context, v.toDateTime().toString(Qt::ISODateWithMs).toUtf8().constData());
+ case QMetaType::QVariantMap:
+ return asJsValue(v.toMap(), id, frozen);
+ default:
+ return JS_UNDEFINED;
}
+}
- const auto engine = static_cast<const ScriptEngine *>(qtengine);
- ErrorInfo deprWarning(Tr::tr("The loadExtension() function is deprecated and will be "
- "removed in a future version of Qbs. Use require() "
- "instead."), context->backtrace());
- engine->logger().printWarning(deprWarning);
+JSValue ScriptEngine::asJsValue(const QByteArray &s)
+{
+ return JS_NewArrayBufferCopy(
+ m_context, reinterpret_cast<const uint8_t *>(s.constData()), s.size());
+}
- return js_require(context, qtengine);
+JSValue ScriptEngine::asJsValue(const QString &s)
+{
+ const auto it = m_stringCache.constFind(s);
+ if (it != m_stringCache.constEnd())
+ return JS_DupValue(m_context, it.value());
+ const JSValue sv = JS_NewString(m_context, s.toUtf8().constData());
+ m_stringCache.insert(s, sv);
+ return JS_DupValue(m_context, sv);
}
-QScriptValue ScriptEngine::js_loadFile(QScriptContext *context, QScriptEngine *qtengine)
+JSValue ScriptEngine::asJsValue(const QStringList &l)
{
- if (context->argumentCount() < 1) {
- return context->throwError(
- ScriptEngine::tr("The loadFile function requires a file path."));
- }
+ JSValue array = JS_NewArray(m_context);
+ setJsProperty(m_context, array, QLatin1String("length"), JS_NewInt32(m_context, l.size()));
+ for (int i = 0; i < l.size(); ++i)
+ JS_SetPropertyUint32(m_context, array, i, asJsValue(l.at(i)));
+ return array;
+}
- const auto engine = static_cast<const ScriptEngine *>(qtengine);
- ErrorInfo deprWarning(Tr::tr("The loadFile() function is deprecated and will be "
- "removed in a future version of Qbs. Use require() "
- "instead."), context->backtrace());
- engine->logger().printWarning(deprWarning);
+JSValue ScriptEngine::asJsValue(const QVariantMap &m, quintptr id, bool frozen)
+{
+ const auto it = id ? m_jsValueCache.constFind(id) : m_jsValueCache.constEnd();
+ if (it != m_jsValueCache.constEnd())
+ return JS_DupValue(m_context, it.value());
+ frozen = id || frozen;
+ JSValue obj = JS_NewObject(m_context);
+ for (auto it = m.begin(); it != m.end(); ++it)
+ setJsProperty(m_context, obj, it.key(), asJsValue(it.value(), 0, frozen));
+ if (frozen)
+ JS_ObjectSeal(m_context, obj, true);
+ if (!id)
+ return obj;
+ m_jsValueCache[id] = obj;
+ return JS_DupValue(m_context, obj);
+}
- return js_require(context, qtengine);
+void ScriptEngine::setPropertyOnGlobalObject(const QString &property, JSValue value)
+{
+ const ScopedJsValue globalObject(m_context, JS_GetGlobalObject(m_context));
+ setJsProperty(m_context, globalObject, property, value);
}
-QScriptValue ScriptEngine::js_require(QScriptContext *context, QScriptEngine *qtengine)
+JSValue ScriptEngine::asJsValue(const QVariantList &l, quintptr id, bool frozen)
{
- const auto engine = static_cast<ScriptEngine *>(qtengine);
- if (context->argumentCount() < 1) {
- return context->throwError(
- ScriptEngine::tr("The require function requires a module name or path."));
- }
+ const auto it = id ? m_jsValueCache.constFind(id) : m_jsValueCache.constEnd();
+ if (it != m_jsValueCache.constEnd())
+ return JS_DupValue(m_context, it.value());
+ frozen = id || frozen;
+ JSValue array = JS_NewArray(m_context);
+ setJsProperty(m_context, array, QLatin1String("length"), JS_NewInt32(m_context, l.size()));
+ for (int i = 0; i < l.size(); ++i)
+ JS_SetPropertyUint32(m_context, array, i, asJsValue(l.at(i), 0, frozen));
+ if (frozen)
+ JS_ObjectSeal(m_context, array, true);
+ if (!id)
+ return array;
+ m_jsValueCache[id] = array;
+ return JS_DupValue(m_context, array);
+}
+
+JSValue ScriptEngine::loadInternalExtension(const QString &uri)
+{
+ const QString name = uri.mid(4); // remove the "qbs." part
+ const auto cached = m_internalExtensions.constFind(name);
+ if (cached != m_internalExtensions.constEnd())
+ return cached.value();
+ JSValue extensionObj = JsExtensions::loadExtension(this, name);
+ if (!JS_IsObject(extensionObj))
+ return throwError(Tr::tr("loadExtension: cannot load extension '%1'.").arg(uri));
+ m_internalExtensions.insert(name, extensionObj);
+ return extensionObj;
+}
+
+JSValue ScriptEngine::js_require(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int, JSValue *func_data)
+{
+ Q_UNUSED(this_val)
+
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ QBS_ASSERT(engine, return JS_EXCEPTION);
+ if (argc < 1)
+ return engine->throwError(Tr::tr("The require function requires a module name or path."));
- const QString moduleName = context->argument(0).toString();
+ const QString moduleName = getJsString(ctx, argv[0]);
// First try to load a named module if the argument doesn't look like a file path
if (!moduleName.contains(QLatin1Char('/'))) {
if (engine->m_extensionSearchPathsStack.empty())
- return context->throwError(
- ScriptEngine::tr("require: internal error. No search paths."));
+ return engine->throwError(Tr::tr("require: internal error. No search paths."));
- const QString uri = moduleName;
if (engine->m_logger.debugEnabled()) {
engine->m_logger.qbsDebug()
- << "[require] loading extension " << uri;
+ << "[require] loading extension " << moduleName;
}
- QString uriAsPath = uri;
- uriAsPath.replace(QLatin1Char('.'), QLatin1Char('/'));
+ QString moduleNameAsPath = moduleName;
+ moduleNameAsPath.replace(QLatin1Char('.'), QLatin1Char('/'));
const QStringList searchPaths = engine->m_extensionSearchPathsStack.top();
- const QString dirPath = findExtensionDir(searchPaths, uriAsPath);
+ const QString dirPath = findExtensionDir(searchPaths, moduleNameAsPath);
if (dirPath.isEmpty()) {
- if (uri.startsWith(QStringLiteral("qbs.")))
- return loadInternalExtension(context, engine, uri);
+ if (moduleName.startsWith(QStringLiteral("qbs.")))
+ return JS_DupValue(ctx, engine->loadInternalExtension(moduleName));
} else {
QDirIterator dit(dirPath, StringConstants::jsFileWildcards(),
QDir::Files | QDir::Readable);
- QScriptValueList values;
+ JSValueList values;
std::vector<QString> filePaths;
try {
while (dit.hasNext()) {
@@ -503,19 +639,19 @@ QScriptValue ScriptEngine::js_require(QScriptContext *context, QScriptEngine *qt
engine->m_logger.qbsDebug()
<< "[require] importing file " << filePath;
}
- QScriptValue obj = engine->newObject();
+ ScopedJsValue obj(engine->context(), engine->newObject());
engine->importFile(filePath, obj);
- values << obj;
+ values << obj.release();
filePaths.push_back(filePath);
}
} catch (const ErrorInfo &e) {
- return context->throwError(e.toString());
+ return engine->throwError(e.toString());
}
if (!values.empty()) {
- const QScriptValue mergedValue = mergeExtensionObjects(values);
+ const JSValue mergedValue = engine->mergeExtensionObjects(values);
engine->m_requireResults.push_back(mergedValue);
- engine->m_filePathsPerImport[mergedValue.objectId()] = filePaths;
+ engine->m_filePathsPerImport[jsObjectId(mergedValue)] = filePaths;
return mergedValue;
}
}
@@ -524,52 +660,88 @@ QScriptValue ScriptEngine::js_require(QScriptContext *context, QScriptEngine *qt
// file located in the current directory search path; try that next
}
- if (engine->m_currentDirPathStack.empty()) {
- return context->throwError(
- ScriptEngine::tr("require: internal error. No current directory."));
- }
+ if (engine->m_currentDirPathStack.empty())
+ return engine->throwError(Tr::tr("require: internal error. No current directory."));
- QScriptValue result;
+ JSValue result;
try {
const QString filePath = FileInfo::resolvePath(engine->m_currentDirPathStack.top(),
moduleName);
- result = engine->newObject();
- engine->importFile(filePath, result);
static const QString scopeNamePrefix = QStringLiteral("_qbs_scope_");
const QString scopeName = scopeNamePrefix + QString::number(qHash(filePath), 16);
- result.setProperty(StringConstants::importScopeNamePropertyInternal(), scopeName);
- context->thisObject().setProperty(scopeName, result);
+ result = getJsProperty(ctx, func_data[0], scopeName);
+ if (JS_IsObject(result))
+ return result; // Same JS file imported from same qbs file via different JS files (e.g. codesign.js from DarwinGCC.qbs via gcc.js and darwin.js).
+ ScopedJsValue scopedResult(engine->context(), engine->newObject());
+ engine->importFile(filePath, scopedResult);
+ result = scopedResult.release();
+ setJsProperty(ctx, result, StringConstants::importScopeNamePropertyInternal(), scopeName);
+ setJsProperty(ctx, func_data[0], scopeName, result);
engine->m_requireResults.push_back(result);
- engine->m_filePathsPerImport[result.objectId()] = { filePath };
+ engine->m_filePathsPerImport[jsObjectId(JS_DupValue(ctx, result))] = { filePath };
} catch (const ErrorInfo &e) {
- result = context->throwError(e.toString());
+ result = engine->throwError(e.toString());
}
return result;
}
-QScriptClass *ScriptEngine::modulePropertyScriptClass() const
+JSClassID ScriptEngine::modulePropertyScriptClass() const
{
return m_modulePropertyScriptClass;
}
-void ScriptEngine::setModulePropertyScriptClass(QScriptClass *modulePropertyScriptClass)
+void ScriptEngine::setModulePropertyScriptClass(JSClassID modulePropertyScriptClass)
{
m_modulePropertyScriptClass = modulePropertyScriptClass;
}
-void ScriptEngine::addResourceAcquiringScriptObject(ResourceAcquiringScriptObject *obj)
+template<typename T> JSValue getScriptValue(JSContext *ctx, const T *t,
+ const std::unordered_map<const T *, JSValue> &map)
{
- m_resourceAcquiringScriptObjects.push_back(obj);
+ const auto it = map.find(t);
+ return it == map.end() ? JS_UNDEFINED : JS_DupValue(ctx, it->second);
}
-void ScriptEngine::releaseResourcesOfScriptObjects()
+template<typename T> void setScriptValue(JSContext *ctx, const T *t, JSValue value,
+ std::unordered_map<const T *, JSValue> &map)
{
- if (m_resourceAcquiringScriptObjects.empty())
+ value = JS_DupValue(ctx, value);
+ const auto it = map.find(t);
+ if (it == map.end()) {
+ map.insert(std::make_pair(t, value));
return;
- std::for_each(m_resourceAcquiringScriptObjects.begin(), m_resourceAcquiringScriptObjects.end(),
- std::mem_fn(&ResourceAcquiringScriptObject::releaseResources));
- m_resourceAcquiringScriptObjects.clear();
+ }
+ JS_FreeValue(ctx, it->second);
+ it->second = value;
+}
+
+JSValue ScriptEngine::artifactsMapScriptValue(const ResolvedProduct *product)
+{
+ return getScriptValue(m_context, product, m_productArtifactsMapScriptValues);
+}
+
+void ScriptEngine::setArtifactsMapScriptValue(const ResolvedProduct *product, JSValue value)
+{
+ setScriptValue(m_context, product, value, m_productArtifactsMapScriptValues);
+}
+
+JSValue ScriptEngine::artifactsMapScriptValue(const ResolvedModule *module)
+{
+ return getScriptValue(m_context, module, m_moduleArtifactsMapScriptValues);
+}
+
+void ScriptEngine::setArtifactsMapScriptValue(const ResolvedModule *module, JSValue value)
+{
+ setScriptValue(m_context, module, value, m_moduleArtifactsMapScriptValues);
+}
+
+JSValue ScriptEngine::getArtifactProperty(JSValue obj,
+ const std::function<JSValue (const Artifact *)> &propGetter)
+{
+ std::lock_guard lock(m_artifactsMutex);
+ const Artifact * const a = attachedPointer<Artifact>(obj, dataWithPtrClass());
+ return a ? propGetter(a) : JS_EXCEPTION;
}
void ScriptEngine::addCanonicalFilePathResult(const QString &filePath,
@@ -616,49 +788,100 @@ Set<QString> ScriptEngine::imports() const
return filePaths;
}
-QScriptValueList ScriptEngine::argumentList(const QStringList &argumentNames,
- const QScriptValue &context)
+JSValue ScriptEngine::newObject() const
{
- QScriptValueList result;
- for (const auto &name : argumentNames)
- result += context.property(name);
- return result;
+ return JS_NewObject(m_context);
+}
+
+JSValue ScriptEngine::newArray(int length, JsValueOwner owner)
+{
+ JSValue arr = JS_NewArray(m_context);
+ JS_SetPropertyStr(m_context, arr, "length", JS_NewInt32(m_context, length));
+ if (owner == JsValueOwner::ScriptEngine)
+ ++m_evalResults[arr];
+ return arr;
+}
+
+JSValue ScriptEngine::evaluate(JsValueOwner resultOwner, const QString &code,
+ const QString &filePath, int line, const JSValueList &scopeChain)
+{
+ m_scopeChains << scopeChain;
+ const QByteArray &codeStr = code.toUtf8();
+
+ m_evalPositions.emplace(filePath, line);
+ const JSValue v = JS_EvalThis(m_context, globalObject(), codeStr.constData(), codeStr.length(),
+ filePath.toUtf8().constData(), line, JS_EVAL_TYPE_GLOBAL);
+ m_evalPositions.pop();
+ m_scopeChains.removeLast();
+ if (resultOwner == JsValueOwner::ScriptEngine && JS_VALUE_HAS_REF_COUNT(v))
+ ++m_evalResults[v];
+ return v;
}
-CodeLocation ScriptEngine::lastErrorLocation(const QScriptValue &v,
- const CodeLocation &fallbackLocation) const
+void ScriptEngine::handleJsProperties(JSValue obj, const PropertyHandler &handler)
{
- const QScriptValue &errorVal = lastErrorValue(v);
- const CodeLocation errorLoc(errorVal.property(StringConstants::fileNameProperty()).toString(),
- errorVal.property(QStringLiteral("lineNumber")).toInt32(),
- errorVal.property(QStringLiteral("expressionCaretOffset")).toInt32(),
- false);
- return errorLoc.isValid() ? errorLoc : fallbackLocation;
+ qbs::Internal::handleJsProperties(m_context, obj, handler);
}
-ErrorInfo ScriptEngine::lastError(const QScriptValue &v, const CodeLocation &fallbackLocation) const
+ScopedJsValueList ScriptEngine::argumentList(const QStringList &argumentNames,
+ const JSValue &context) const
{
- const QString msg = lastErrorString(v);
- CodeLocation errorLocation = lastErrorLocation(v);
- if (errorLocation.isValid())
- return ErrorInfo(msg, errorLocation);
- const QStringList backtrace = uncaughtExceptionBacktraceOrEmpty();
- if (!backtrace.empty()) {
- ErrorInfo e(msg, backtrace);
- if (e.hasLocation())
- return e;
+ JSValueList result;
+ for (const auto &name : argumentNames)
+ result.push_back(getJsProperty(m_context, context, name));
+ return {m_context, result};
+}
+
+JSClassID ScriptEngine::registerClass(const char *name, JSClassCall *constructor,
+ JSClassFinalizer *finalizer, JSValue scope,
+ GetPropertyNames getPropertyNames, GetProperty getProperty)
+{
+ JSClassID id = 0;
+ const auto classIt = m_classes.constFind(QLatin1String(name));
+ if (classIt == m_classes.constEnd()) {
+ JS_NewClassID(&id);
+ const auto it = getProperty
+ ? m_exoticMethods.insert(id, JSClassExoticMethods{getProperty, getPropertyNames})
+ : m_exoticMethods.end();
+ JSClassDef jsClass{name, finalizer, nullptr, constructor,
+ it != m_exoticMethods.end() ? &it.value() : nullptr};
+ const int status = JS_NewClass(m_jsRuntime, id, &jsClass);
+ QBS_ASSERT(status == 0, return 0);
+ m_classes.insert(QLatin1String(name), id);
+ } else {
+ id = classIt.value();
+ }
+ if (!JS_IsUndefined(scope)) {
+ const JSValue classObj = JS_NewObjectClass(m_context, id);
+ JS_SetConstructorBit(m_context, classObj, constructor != nullptr);
+ JS_SetPropertyStr(m_context, scope, name, classObj);
}
- return ErrorInfo(msg, fallbackLocation);
+ return id;
+}
+
+JSClassID ScriptEngine::getClassId(const char *name) const
+{
+ return m_classes.value(QLatin1String(name));
+}
+
+JSValue ScriptEngine::throwError(const QString &message) const
+{
+ return qbs::Internal::throwError(m_context, message);
}
void ScriptEngine::cancel()
{
- QTimer::singleShot(0, this, [this] { abort(); });
+ m_canceling = true;
}
-void ScriptEngine::abort()
+int ScriptEngine::interruptor(JSRuntime *, void *opaqueEngine)
{
- abortEvaluation(m_cancelationError);
+ const auto engine = reinterpret_cast<ScriptEngine *>(opaqueEngine);
+ if (engine->m_canceling) {
+ engine->m_canceling = false;
+ return 1;
+ }
+ return 0;
}
bool ScriptEngine::gatherFileResults() const
@@ -667,83 +890,105 @@ bool ScriptEngine::gatherFileResults() const
|| evalContext() == EvalContext::ProbeExecution;
}
+void ScriptEngine::setMaxStackSize()
+{
+ size_t stackSize = 0; // Turn check off by default.
+ bool ok;
+ const int stackSizeFromEnv = qEnvironmentVariableIntValue("QBS_MAX_JS_STACK_SIZE", &ok);
+ if (ok && stackSizeFromEnv >= 0)
+ stackSize = stackSizeFromEnv;
+ JS_SetMaxStackSize(m_jsRuntime, stackSize);
+}
+
+JSValue ScriptEngine::getArtifactScriptValue(Artifact *a, const QString &moduleName,
+ const std::function<void(JSValue obj)> &setup)
+{
+ std::lock_guard lock(m_artifactsMutex);
+ const auto it = m_artifactsScriptValues.constFind(qMakePair(a, moduleName));
+ if (it != m_artifactsScriptValues.constEnd())
+ return JS_DupValue(m_context, *it);
+ a->setDeregister([this](const Artifact *a) {
+ const std::lock_guard lock(m_artifactsMutex);
+ for (auto it = m_artifactsScriptValues.begin(); it != m_artifactsScriptValues.end(); ) {
+ if (it.key().first == a) {
+ JS_SetOpaque(it.value(), nullptr);
+ JS_FreeValue(m_context, it.value());
+ it = m_artifactsScriptValues.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ });
+ JSValue obj = JS_NewObjectClass(context(), dataWithPtrClass());
+ attachPointerTo(obj, a);
+ setup(obj);
+ m_artifactsScriptValues.insert(qMakePair(a, moduleName), JS_DupValue(m_context, obj));
+ return obj;
+}
+
+void ScriptEngine::releaseInputArtifactScriptValues(const RuleNode *ruleNode)
+{
+ for (auto it = m_artifactsScriptValues.begin(); it != m_artifactsScriptValues.end();) {
+ Artifact * const a = it.key().first;
+ if (ruleNode->children.contains(a)) {
+ a->setDeregister({});
+ JS_FreeValue(m_context, it.value());
+ it = m_artifactsScriptValues.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
class JSTypeExtender
{
public:
- JSTypeExtender(ScriptEngine *engine, const QString &typeName)
- : m_engine(engine)
+ JSTypeExtender(ScriptEngine *engine, const QString &typeName) : m_engine(engine)
{
- m_proto = engine->globalObject().property(typeName)
- .property(QStringLiteral("prototype"));
- QBS_ASSERT(m_proto.isObject(), return);
- m_descriptor = engine->newObject();
+ const ScopedJsValue globalObject(engine->context(), JS_GetGlobalObject(engine->context()));
+ const ScopedJsValue type(engine->context(), getJsProperty(engine->context(),
+ globalObject, typeName));
+ m_proto = getJsProperty(engine->context(), type, QStringLiteral("prototype"));
+ QBS_ASSERT(JS_IsObject(m_proto), return);
+ }
+
+ ~JSTypeExtender()
+ {
+ JS_FreeValue(m_engine->context(), m_proto);
}
void addFunction(const QString &name, const QString &code)
{
- QScriptValue f = m_engine->evaluate(code);
- QBS_ASSERT(f.isFunction(), return);
- m_descriptor.setProperty(QStringLiteral("value"), f);
- m_engine->defineProperty(m_proto, name, m_descriptor);
+ const JSValue f = m_engine->evaluate(JsValueOwner::Caller, code);
+ QBS_ASSERT(JS_IsFunction(m_engine->context(), f), return);
+ JS_DefinePropertyValueStr(m_engine->context(), m_proto, name.toUtf8().constData(), f, 0);
}
private:
- ScriptEngine *const m_engine;
- QScriptValue m_proto;
- QScriptValue m_descriptor;
+ ScriptEngine * const m_engine;
+ JSValue m_proto = JS_UNDEFINED;
};
-static QScriptValue js_consoleError(QScriptContext *context, QScriptEngine *engine, Logger *logger)
-{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("console.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,
- QStringLiteral("console.warn() 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,
- QStringLiteral("console.info() expects 1 argument"));
- logger->qbsInfo() << context->argument(0).toString();
- return engine->undefinedValue();
-}
-
-static QScriptValue js_consoleDebug(QScriptContext *context, QScriptEngine *engine, Logger *logger)
+static JSValue js_consoleFunc(JSContext *ctx, JSValueConst, int argc, JSValueConst *argv,
+ int level)
{
- if (Q_UNLIKELY(context->argumentCount() != 1))
- return context->throwError(QScriptContext::SyntaxError,
- QStringLiteral("console.debug() expects 1 argument"));
- logger->qbsDebug() << context->argument(0).toString();
+ ScriptEngine * const engine = ScriptEngine::engineForContext(ctx);
+ QBS_ASSERT(engine, return JS_EXCEPTION);
+ if (Q_UNLIKELY(argc != 1))
+ return engine->throwError(Tr::tr("The console functions expect 1 argument."));
+ engine->logger().qbsLog(static_cast<LoggerLevel>(level)) << getJsString(ctx, argv[0]);
return engine->undefinedValue();
}
-static QScriptValue js_consoleLog(QScriptContext *context, QScriptEngine *engine, Logger *logger)
-{
- return js_consoleDebug(context, engine, logger);
-}
-
void ScriptEngine::installQbsBuiltins()
{
- globalObject().setProperty(StringConstants::qbsModule(), m_qbsObject = newObject());
-
- globalObject().setProperty(QStringLiteral("console"), m_consoleObject = newObject());
- installConsoleFunction(QStringLiteral("debug"), &js_consoleDebug);
- installConsoleFunction(QStringLiteral("error"), &js_consoleError);
- installConsoleFunction(QStringLiteral("info"), &js_consoleInfo);
- installConsoleFunction(QStringLiteral("log"), &js_consoleLog);
- installConsoleFunction(QStringLiteral("warn"), &js_consoleWarn);
+ const JSValue consoleObj = newObject();
+ setPropertyOnGlobalObject(QLatin1String("console"), consoleObj);
+ installConsoleFunction(consoleObj, QStringLiteral("debug"), LoggerDebug);
+ installConsoleFunction(consoleObj, QStringLiteral("error"), LoggerError);
+ installConsoleFunction(consoleObj, QStringLiteral("info"), LoggerInfo);
+ installConsoleFunction(consoleObj, QStringLiteral("log"), LoggerDebug);
+ installConsoleFunction(consoleObj, QStringLiteral("warn"), LoggerWarning);
}
void ScriptEngine::extendJavaScriptBuiltins()
@@ -773,48 +1018,27 @@ void ScriptEngine::extendJavaScriptBuiltins()
JSTypeExtender stringExtender(this, QStringLiteral("String"));
stringExtender.addFunction(QStringLiteral("contains"),
QStringLiteral("(function(e){return this.indexOf(e) !== -1;})"));
- stringExtender.addFunction(QStringLiteral("startsWith"),
- QStringLiteral("(function(e){return this.slice(0, e.length) === e;})"));
- stringExtender.addFunction(QStringLiteral("endsWith"),
- QStringLiteral("(function(e){return this.slice(-e.length) === e;})"));
}
-void ScriptEngine::installFunction(const QString &name, int length, QScriptValue *functionValue,
- FunctionSignature f, QScriptValue *targetObject = nullptr)
+void ScriptEngine::installConsoleFunction(JSValue consoleObj, const QString &name,
+ LoggerLevel level)
{
- if (!functionValue->isValid())
- *functionValue = newFunction(f, length);
- (targetObject ? *targetObject : globalObject()).setProperty(name, *functionValue);
+ JS_SetPropertyStr(m_context, consoleObj, name.toUtf8().constData(),
+ JS_NewCFunctionMagic(m_context, js_consoleFunc, name.toUtf8().constData(), 1,
+ JS_CFUNC_generic_magic, level));
}
-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,
- QScriptValue (*f)(QScriptContext *, QScriptEngine *, Logger *))
-{
- m_consoleObject.setProperty(name, newFunction(f, &m_logger));
-}
-
-static QString loadFileString() { return QStringLiteral("loadFile"); }
-static QString loadExtensionString() { return QStringLiteral("loadExtension"); }
static QString requireString() { return QStringLiteral("require"); }
-void ScriptEngine::installImportFunctions()
+void ScriptEngine::installImportFunctions(JSValue importScope)
{
- installFunction(loadFileString(), 1, &m_loadFileFunction, js_loadFile);
- installFunction(loadExtensionString(), 1, &m_loadExtensionFunction, js_loadExtension);
- installFunction(requireString(), 1, &m_requireFunction, js_require);
+ const JSValue require = JS_NewCFunctionData(m_context, js_require, 1, 0, 1, &importScope);
+ setPropertyOnGlobalObject(requireString(), require);
}
void ScriptEngine::uninstallImportFunctions()
{
- globalObject().setProperty(loadFileString(), QScriptValue());
- globalObject().setProperty(loadExtensionString(), QScriptValue());
- globalObject().setProperty(requireString(), QScriptValue());
+ setPropertyOnGlobalObject(requireString(), JS_UNDEFINED);
}
ScriptEngine::PropertyCacheKey::PropertyCacheKey(QString moduleName,
@@ -825,5 +1049,54 @@ ScriptEngine::PropertyCacheKey::PropertyCacheKey(QString moduleName,
{
}
+JsException ScriptEngine::checkAndClearException(const CodeLocation &fallbackLocation) const
+{
+ return {m_context, JS_GetException(m_context), fallbackLocation};
+}
+
+void ScriptEngine::clearRequestedProperties()
+{
+ m_propertiesRequestedInScript.clear();
+ m_propertiesRequestedFromArtifact.clear();
+ m_importsRequestedInScript.clear();
+ m_productsWithRequestedDependencies.clear();
+ m_requestedArtifacts.clear();
+ m_requestedExports.clear();
+};
+
+void ScriptEngine::takeOwnership(JSValue v)
+{
+ ++m_evalResults[v];
+}
+
+ScriptEngine::Importer::Importer(
+ ScriptEngine &engine, const FileContextBaseConstPtr &fileCtx, JSValue &targetObject,
+ ObserveMode observeMode)
+ : m_engine(engine), m_fileCtx(fileCtx), m_targetObject(targetObject)
+{
+ m_engine.installImportFunctions(targetObject);
+ m_engine.m_currentDirPathStack.push(FileInfo::path(fileCtx->filePath()));
+ m_engine.m_extensionSearchPathsStack.push(fileCtx->searchPaths());
+ m_engine.m_observeMode = observeMode;
+}
+
+ScriptEngine::Importer::~Importer()
+{
+ m_engine.m_requireResults.clear();
+ m_engine.m_currentDirPathStack.pop();
+ m_engine.m_extensionSearchPathsStack.pop();
+ m_engine.uninstallImportFunctions();
+}
+
+void ScriptEngine::Importer::run()
+{
+ for (const JsImport &jsImport : m_fileCtx->jsImports())
+ m_engine.import(jsImport, m_targetObject);
+ if (m_engine.m_observeMode == ObserveMode::Enabled) {
+ for (JSValue &sv : m_engine.m_requireResults)
+ m_engine.observeImport(sv);
+ }
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/language/scriptengine.h b/src/lib/corelib/language/scriptengine.h
index 24e133dff..4a55392e3 100644
--- a/src/lib/corelib/language/scriptengine.h
+++ b/src/lib/corelib/language/scriptengine.h
@@ -45,8 +45,11 @@
#include <buildgraph/requestedartifacts.h>
#include <buildgraph/requesteddependencies.h>
#include <logging/logger.h>
+#include <quickjs.h>
#include <tools/codelocation.h>
#include <tools/filetime.h>
+#include <tools/porting.h>
+#include <tools/scripttools.h>
#include <tools/set.h>
#include <QtCore/qdir.h>
@@ -55,8 +58,8 @@
#include <QtCore/qprocess.h>
#include <QtCore/qstring.h>
-#include <QtScript/qscriptengine.h>
-
+#include <atomic>
+#include <functional>
#include <memory>
#include <mutex>
#include <stack>
@@ -67,8 +70,10 @@
namespace qbs {
namespace Internal {
class Artifact;
+class Evaluator;
class JsImport;
class PrepareScriptObserver;
+class RuleNode;
class ScriptImporter;
class ScriptPropertyObserver;
@@ -85,33 +90,33 @@ public:
};
using DubiousContextList = std::vector<DubiousContext>;
-
-/*
- * ScriptObject that acquires resources, for example a file handle.
- * The ScriptObject should have QtOwnership and deleteLater() itself in releaseResources.
- */
-class ResourceAcquiringScriptObject
-{
-public:
- virtual ~ResourceAcquiringScriptObject() = default;
- virtual void releaseResources() = 0;
-};
+enum class JsValueOwner { Caller, ScriptEngine }; // TODO: This smells like cheating. Should always be Caller.
enum class ObserveMode { Enabled, Disabled };
-class QBS_AUTOTEST_EXPORT ScriptEngine : public QScriptEngine
+class QBS_AUTOTEST_EXPORT ScriptEngine
{
- Q_OBJECT
- ScriptEngine(Logger &logger, EvalContext evalContext, QObject *parent = nullptr);
+ struct PrivateTag {};
public:
- static ScriptEngine *create(Logger &logger, EvalContext evalContext, QObject *parent = nullptr);
- ~ScriptEngine() override;
+ ScriptEngine(Logger &logger, EvalContext evalContext, PrivateTag);
+ ~ScriptEngine();
+
+ static std::unique_ptr<ScriptEngine> create(Logger &logger, EvalContext evalContext);
+ static ScriptEngine *engineForRuntime(const JSRuntime *runtime);
+ static ScriptEngine *engineForContext(const JSContext *ctx);
+ static LookupResult doExtraScopeLookup(JSContext *ctx, JSAtom prop);
+
+ void reset();
Logger &logger() const { return m_logger; }
- void import(const FileContextBaseConstPtr &fileCtx, QScriptValue &targetObject,
+ void import(const FileContextBaseConstPtr &fileCtx, JSValue &targetObject,
ObserveMode observeMode);
void clearImportsCache();
+ void registerEvaluator(Evaluator *evaluator);
+ void unregisterEvaluator(const Evaluator *evaluator);
+ Evaluator *evaluator() const { return m_evaluator; }
+
void setEvalContext(EvalContext c) { m_evalContext = c; }
EvalContext evalContext() const { return m_evalContext; }
void checkContext(const QString &operation, const DubiousContextList &dubiousContexts);
@@ -141,14 +146,7 @@ public:
}
void addPropertyRequestedFromArtifact(const Artifact *artifact, const Property &property);
void addRequestedExport(const ResolvedProduct *product) { m_requestedExports.insert(product); }
- void clearRequestedProperties() {
- m_propertiesRequestedInScript.clear();
- m_propertiesRequestedFromArtifact.clear();
- m_importsRequestedInScript.clear();
- m_productsWithRequestedDependencies.clear();
- m_requestedArtifacts.clear();
- m_requestedExports.clear();
- }
+ void clearRequestedProperties();
PropertySet propertiesRequestedInScript() const { return m_propertiesRequestedInScript; }
QHash<QString, PropertySet> propertiesRequestedFromArtifact() const {
return m_propertiesRequestedFromArtifact;
@@ -164,9 +162,11 @@ public:
RequestedArtifacts requestedArtifacts() const { return m_requestedArtifacts; }
Set<const ResolvedProduct *> requestedExports() const { return m_requestedExports; }
- void addImportRequestedInScript(qint64 importValueId);
+ void addImportRequestedInScript(quintptr importValueId);
std::vector<QString> importedFilesUsedInScript() const;
+ void addExternallyCachedValue(JSValue *v) { m_externallyCachedValues.push_back(v); }
+
void setUsesIo() { m_usesIo = true; }
void clearUsesIo() { m_usesIo = false; }
bool usesIo() const { return m_usesIo; }
@@ -180,11 +180,8 @@ public:
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);
+ void setObservedProperty(JSValue &object, const QString &name, const JSValue &value);
void unobserveProperties();
- void setDeprecatedProperty(QScriptValue &object, const QString &name, const QString &newName,
- const QScriptValue &value);
PrepareScriptObserver *observer() const { return m_observer.get(); }
QProcessEnvironment environment() const;
@@ -203,23 +200,34 @@ public:
QHash<QString, FileTime> fileLastModifiedResults() const { return m_fileLastModifiedResult; }
Set<QString> imports() const;
- static QScriptValueList argumentList(const QStringList &argumentNames,
- const QScriptValue &context);
- 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(); }
- CodeLocation lastErrorLocation(const QScriptValue &v,
- const CodeLocation &fallbackLocation = CodeLocation()) const;
- ErrorInfo lastError(const QScriptValue &v,
- const CodeLocation &fallbackLocation = CodeLocation()) const;
+ JSValue newObject() const;
+ JSValue newArray(int length, JsValueOwner owner);
+ void takeOwnership(JSValue v);
+ JSValue undefinedValue() const { return JS_UNDEFINED; }
+ JSValue toScriptValue(const QVariant &v, quintptr id = 0) { return asJsValue(v, id); }
+ JSValue evaluate(JsValueOwner resultOwner, const QString &code,
+ const QString &filePath = QString(), int line = 1,
+ const JSValueList &scopeChain = {});
+ void setLastLookupStatus(bool success) { m_lastLookupWasSuccess = success; }
+ JSContext *context() const { return m_context; }
+ JSValue globalObject() const { return m_globalObject; }
+ void setGlobalObject(JSValue obj) { m_globalObject = obj; }
+ void handleJsProperties(JSValueConst obj, const PropertyHandler &handler);
+ ScopedJsValueList argumentList(const QStringList &argumentNames, const JSValue &context) const;
+
+ using GetProperty = int (*)(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop);
+ using GetPropertyNames = int (*)(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen,
+ JSValueConst obj);
+ JSClassID registerClass(const char *name, JSClassCall *constructor, JSClassFinalizer *finalizer,
+ JSValue scope,
+ GetPropertyNames getPropertyNames = nullptr,
+ GetProperty getProperty = nullptr);
+ JSClassID getClassId(const char *name) const;
+
+ JsException checkAndClearException(const CodeLocation &fallbackLocation) const;
+ JSValue throwError(const QString &message) const;
void cancel();
@@ -228,77 +236,100 @@ public:
bool isActive() const { return m_active; }
void setActive(bool on) { m_active = on; }
- using QScriptEngine::newFunction;
-
- template <typename T, typename E,
- typename = std::enable_if_t<std::is_pointer<T>::value>,
- typename = std::enable_if_t<std::is_pointer<E>::value>,
- typename = std::enable_if_t<std::is_base_of<
- QScriptEngine, std::remove_pointer_t<E>>::value>
- > QScriptValue newFunction(QScriptValue (*signature)(QScriptContext *, E, T), T arg) {
- return QScriptEngine::newFunction(
- reinterpret_cast<FunctionWithArgSignature>(signature),
- reinterpret_cast<void *>(const_cast<
- std::add_pointer_t<
- std::remove_const_t<
- std::remove_pointer_t<T>>>>(arg)));
- }
-
- QScriptClass *modulePropertyScriptClass() const;
- void setModulePropertyScriptClass(QScriptClass *modulePropertyScriptClass);
+ JSClassID modulePropertyScriptClass() const;
+ void setModulePropertyScriptClass(JSClassID modulePropertyScriptClass);
- QScriptClass *productPropertyScriptClass() const { return m_productPropertyScriptClass; }
- void setProductPropertyScriptClass(QScriptClass *productPropertyScriptClass)
+ JSClassID productPropertyScriptClass() const { return m_productPropertyScriptClass; }
+ void setProductPropertyScriptClass(JSClassID productPropertyScriptClass)
{
m_productPropertyScriptClass = productPropertyScriptClass;
}
- QScriptClass *artifactsScriptClass() const { return m_artifactsScriptClass; }
- void setArtifactsScriptClass(QScriptClass *artifactsScriptClass)
+ JSClassID artifactsScriptClass(int index) const { return m_artifactsScriptClass[index]; }
+ void setArtifactsScriptClass(int index, JSClassID artifactsScriptClass)
{
- m_artifactsScriptClass = artifactsScriptClass;
+ m_artifactsScriptClass[index] = artifactsScriptClass;
}
- void addResourceAcquiringScriptObject(ResourceAcquiringScriptObject *obj);
- void releaseResourcesOfScriptObjects();
+ JSValue artifactsMapScriptValue(const ResolvedProduct *product);
+ void setArtifactsMapScriptValue(const ResolvedProduct *product, JSValue value);
+ JSValue artifactsMapScriptValue(const ResolvedModule *module);
+ void setArtifactsMapScriptValue(const ResolvedModule *module, JSValue value);
+
+ JSValue getArtifactProperty(JSValue obj,
+ const std::function<JSValue(const Artifact *)> &propGetter);
- QScriptValue &productScriptValuePrototype(const ResolvedProduct *product)
+ JSValue& baseProductScriptValue(const ResolvedProduct *product)
{
- return m_productScriptValues[product];
+ return m_baseProductScriptValues[product];
}
- QScriptValue &projectScriptValue(const ResolvedProject *project)
+ JSValue &projectScriptValue(const ResolvedProject *project)
{
return m_projectScriptValues[project];
}
- QScriptValue &moduleScriptValuePrototype(const ResolvedModule *module)
+ JSValue &baseModuleScriptValue(const ResolvedModule *module)
{
- return m_moduleScriptValues[module];
+ return m_baseModuleScriptValues[module];
}
+ JSValue getArtifactScriptValue(Artifact *a, const QString &moduleName,
+ const std::function<void(JSValue obj)> &setup);
+ void releaseInputArtifactScriptValues(const RuleNode *ruleNode);
+
+ const JSValueList &contextStack() const { return m_contextStack; }
+
+ JSClassID dataWithPtrClass() const { return m_dataWithPtrClass; }
+
+ JSValue getInternalExtension(const char *name) const;
+ void addInternalExtension(const char *name, JSValue ext);
+ JSValue asJsValue(const QVariant &v, quintptr id = 0, bool frozen = false);
+ JSValue asJsValue(const QByteArray &s);
+ JSValue asJsValue(const QString &s);
+ JSValue asJsValue(const QStringList &l);
+ JSValue asJsValue(const QVariantList &l, quintptr id = 0, bool frozen = false);
+ JSValue asJsValue(const QVariantMap &m, quintptr id = 0, bool frozen = false);
+
+ QVariant property(const char *name) const { return m_properties.value(QLatin1String(name)); }
+ void setProperty(const char *k, const QVariant &v) { m_properties.insert(QLatin1String(k), v); }
+
private:
- QScriptValue newFunction(FunctionWithArgSignature signature, void *arg) Q_DECL_EQ_DELETE;
+ class Importer {
+ public:
+ Importer(ScriptEngine &engine, const FileContextBaseConstPtr &fileCtx,
+ JSValue &targetObject, ObserveMode observeMode);
+ ~Importer();
+ void run();
+
+ private:
+ ScriptEngine &m_engine;
+ const FileContextBaseConstPtr &m_fileCtx;
+ JSValue &m_targetObject;
+ };
- void abort();
+ static int interruptor(JSRuntime *rt, void *opaqueEngine);
bool gatherFileResults() const;
+ void setMaxStackSize();
+ void setPropertyOnGlobalObject(const QString &property, JSValue value);
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,
- QScriptValue (*f)(QScriptContext *, QScriptEngine *, Logger *));
- void installImportFunctions();
+ void installConsoleFunction(JSValue consoleObj, const QString &name, LoggerLevel level);
+ void installImportFunctions(JSValue importScope);
void uninstallImportFunctions();
- void import(const JsImport &jsImport, QScriptValue &targetObject);
- void observeImport(QScriptValue &jsImport);
- void importFile(const QString &filePath, QScriptValue &targetObject);
- static QScriptValue js_loadExtension(QScriptContext *context, QScriptEngine *qtengine);
- static QScriptValue js_loadFile(QScriptContext *context, QScriptEngine *qtengine);
- static QScriptValue js_require(QScriptContext *context, QScriptEngine *qtengine);
+ void import(const JsImport &jsImport, JSValue &targetObject);
+ void observeImport(JSValue &jsImport);
+ void importFile(const QString &filePath, JSValue targetObject);
+ static JSValue js_require(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic, JSValue *func_data);
+ JSValue mergeExtensionObjects(const JSValueList &lst);
+ JSValue loadInternalExtension(const QString &uri);
+
+ static void handleUndefinedFound(JSContext *ctx);
+ static void handleFunctionEntered(JSContext *ctx, JSValue this_obj);
+ static void handleFunctionExited(JSContext *ctx);
class PropertyCacheKey
{
@@ -311,27 +342,30 @@ private:
const PropertyMapConstPtr m_propertyMap;
friend bool operator==(const PropertyCacheKey &lhs, const PropertyCacheKey &rhs);
- friend uint qHash(const ScriptEngine::PropertyCacheKey &k, uint seed);
+ friend QHashValueType qHash(const ScriptEngine::PropertyCacheKey &k, QHashValueType seed);
};
friend bool operator==(const PropertyCacheKey &lhs, const PropertyCacheKey &rhs);
- friend uint qHash(const ScriptEngine::PropertyCacheKey &k, uint seed);
+ friend QHashValueType qHash(const ScriptEngine::PropertyCacheKey &k, QHashValueType seed);
- static std::mutex m_creationDestructionMutex;
+ JSRuntime * const m_jsRuntime = JS_NewRuntime();
+ JSContext * const m_context = JS_NewContext(m_jsRuntime);
+ JSValue m_globalObject = JS_NULL;
ScriptImporter *m_scriptImporter;
- QScriptClass *m_modulePropertyScriptClass;
- QScriptClass *m_productPropertyScriptClass = nullptr;
- QScriptClass *m_artifactsScriptClass = nullptr;
- QHash<JsImport, QScriptValue> m_jsImportCache;
- std::unordered_map<QString, QScriptValue> m_jsFileCache;
- bool m_propertyCacheEnabled;
- bool m_active;
+ JSClassID m_modulePropertyScriptClass = 0;
+ JSClassID m_productPropertyScriptClass = 0;
+ JSClassID m_artifactsScriptClass[2] = {0, 0};
+ JSClassID m_dataWithPtrClass = 0;
+ Evaluator *m_evaluator = nullptr;
+ QHash<JsImport, JSValue> m_jsImportCache;
+ std::unordered_map<QString, JSValue> m_jsFileCache;
+ bool m_propertyCacheEnabled = true;
+ bool m_active = false;
+ std::atomic_bool m_canceling = false;
QHash<PropertyCacheKey, QVariant> m_propertyCache;
PropertySet m_propertiesRequestedInScript;
QHash<QString, PropertySet> m_propertiesRequestedFromArtifact;
Logger &m_logger;
- QScriptValue m_definePropertyFunction;
- QScriptValue m_emptyFunction;
QProcessEnvironment m_environment;
QHash<QString, QString> m_canonicalFilePathResult;
QHash<QString, bool> m_fileExistsResult;
@@ -339,28 +373,38 @@ private:
QHash<QString, FileTime> m_fileLastModifiedResult;
std::stack<QString> m_currentDirPathStack;
std::stack<QStringList> m_extensionSearchPathsStack;
- QScriptValue m_loadFileFunction;
- QScriptValue m_loadExtensionFunction;
- QScriptValue m_requireFunction;
- QScriptValue m_qbsObject;
- QScriptValue m_consoleObject;
- QScriptValue m_cancelationError;
+ std::stack<std::pair<QString, int>> m_evalPositions;
+ JSValue m_qbsObject = JS_UNDEFINED;
qint64 m_elapsedTimeImporting = -1;
bool m_usesIo = false;
EvalContext m_evalContext;
- std::vector<ResourceAcquiringScriptObject *> m_resourceAcquiringScriptObjects;
const std::unique_ptr<PrepareScriptObserver> m_observer;
- std::vector<std::tuple<QScriptValue, QString, QScriptValue>> m_observedProperties;
- std::vector<QScriptValue> m_requireResults;
- std::unordered_map<qint64, std::vector<QString>> m_filePathsPerImport;
+ std::vector<std::tuple<JSValue, QString, JSValue>> m_observedProperties;
+ JSValueList m_requireResults;
+ std::unordered_map<quintptr, std::vector<QString>> m_filePathsPerImport;
std::vector<qint64> m_importsRequestedInScript;
Set<const ResolvedProduct *> m_productsWithRequestedDependencies;
RequestedArtifacts m_requestedArtifacts;
Set<const ResolvedProduct *> m_requestedExports;
ObserveMode m_observeMode = ObserveMode::Disabled;
- std::unordered_map<const ResolvedProduct *, QScriptValue> m_productScriptValues;
- std::unordered_map<const ResolvedProject *, QScriptValue> m_projectScriptValues;
- std::unordered_map<const ResolvedModule *, QScriptValue> m_moduleScriptValues;
+ std::unordered_map<const ResolvedProduct *, JSValue> m_baseProductScriptValues;
+ std::unordered_map<const ResolvedProduct *, JSValue> m_productArtifactsMapScriptValues;
+ std::unordered_map<const ResolvedModule *, JSValue> m_moduleArtifactsMapScriptValues;
+ std::unordered_map<const ResolvedProject *, JSValue> m_projectScriptValues;
+ std::unordered_map<const ResolvedModule *, JSValue> m_baseModuleScriptValues;
+ QList<JSValueList> m_scopeChains;
+ JSValueList m_contextStack;
+ QHash<JSClassID, JSClassExoticMethods> m_exoticMethods;
+ QHash<QString, JSClassID> m_classes;
+ QHash<QString, JSValue> m_internalExtensions;
+ QHash<QString, JSValue> m_stringCache;
+ QHash<quintptr, JSValue> m_jsValueCache;
+ QHash<JSValue, int> m_evalResults;
+ std::vector<JSValue *> m_externallyCachedValues;
+ QHash<QPair<Artifact *, QString>, JSValue> m_artifactsScriptValues;
+ QVariantMap m_properties;
+ std::recursive_mutex m_artifactsMutex;
+ bool m_lastLookupWasSuccess = false;
};
class EvalContextSwitcher
diff --git a/src/lib/corelib/language/scriptimporter.cpp b/src/lib/corelib/language/scriptimporter.cpp
index 9c6d4d38e..fdb0689ad 100644
--- a/src/lib/corelib/language/scriptimporter.cpp
+++ b/src/lib/corelib/language/scriptimporter.cpp
@@ -48,8 +48,6 @@
#include <parser/qmljsparser_p.h>
#include <tools/error.h>
-#include <QtScript/qscriptvalueiterator.h>
-
namespace qbs {
namespace Internal {
@@ -95,7 +93,7 @@ private:
return false;
}
- void add(const QStringRef &name)
+ void add(QStringView name)
{
if (m_first) {
m_first = false;
@@ -104,9 +102,10 @@ private:
m_suffix.reserve(m_suffix.length() + name.length() * 2 + 2);
m_suffix += QLatin1Char(',');
}
- m_suffix += name;
- m_suffix += QLatin1Char(':');
- m_suffix += name;
+ // ugly, but qt5 does not have append overload for QStringView
+ m_suffix.append(name.data(), name.size());
+ m_suffix.append(QLatin1Char(':'));
+ m_suffix.append(name.data(), name.size());
}
bool m_first = false;
@@ -120,10 +119,10 @@ ScriptImporter::ScriptImporter(ScriptEngine *scriptEngine)
{
}
-QScriptValue ScriptImporter::importSourceCode(const QString &sourceCode, const QString &filePath,
- QScriptValue &targetObject)
+JSValue ScriptImporter::importSourceCode(const QString &sourceCode, const QString &filePath,
+ JSValue &targetObject)
{
- Q_ASSERT(targetObject.isObject());
+ Q_ASSERT(JS_IsObject(targetObject));
// 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.
@@ -143,19 +142,18 @@ QScriptValue ScriptImporter::importSourceCode(const QString &sourceCode, const Q
code = QLatin1String("(function(){\n") + sourceCode + extractor.suffix();
}
- QScriptValue result = m_engine->evaluate(code, filePath, 0);
- throwOnEvaluationError(m_engine, result, [&filePath] () { return CodeLocation(filePath, 0); });
- copyProperties(result, targetObject);
- return result;
+ ScopedJsValue result(m_engine->context(),
+ m_engine->evaluate(JsValueOwner::Caller, code, filePath, 0));
+ throwOnEvaluationError(m_engine, [&filePath] () { return CodeLocation(filePath, 0); });
+ copyProperties(m_engine->context(), result, targetObject);
+ return result.release();
}
-void ScriptImporter::copyProperties(const QScriptValue &src, QScriptValue &dst)
+void ScriptImporter::copyProperties(JSContext *ctx, const JSValue &src, JSValue &dst)
{
- QScriptValueIterator it(src);
- while (it.hasNext()) {
- it.next();
- dst.setProperty(it.name(), it.value());
- }
+ handleJsProperties(ctx, src, [ctx, &dst](const JSAtom &name, const JSPropertyDescriptor &desc) {
+ JS_SetProperty(ctx, dst, name, JS_DupValue(ctx, desc.value));
+ });
}
} // namespace Internal
diff --git a/src/lib/corelib/language/scriptimporter.h b/src/lib/corelib/language/scriptimporter.h
index 8cff09382..6bec9b088 100644
--- a/src/lib/corelib/language/scriptimporter.h
+++ b/src/lib/corelib/language/scriptimporter.h
@@ -40,9 +40,9 @@
#ifndef SCRIPTIMPORTER_H
#define SCRIPTIMPORTER_H
-#include <QtCore/qhash.h>
+#include <quickjs.h>
-#include <QtScript/qscriptvalue.h>
+#include <QtCore/qhash.h>
namespace qbs {
namespace Internal {
@@ -53,9 +53,10 @@ class ScriptImporter
{
public:
ScriptImporter(ScriptEngine *scriptEngine);
- QScriptValue importSourceCode(const QString &sourceCode, const QString &filePath, QScriptValue &targetObject);
+ JSValue importSourceCode(const QString &sourceCode, const QString &filePath,
+ JSValue &targetObject);
- static void copyProperties(const QScriptValue &src, QScriptValue &dst);
+ static void copyProperties(JSContext *ctx, const JSValue &src, JSValue &dst);
private:
ScriptEngine *m_engine;
diff --git a/src/lib/corelib/language/scriptpropertyobserver.h b/src/lib/corelib/language/scriptpropertyobserver.h
index 7fb362b95..80da705ee 100644
--- a/src/lib/corelib/language/scriptpropertyobserver.h
+++ b/src/lib/corelib/language/scriptpropertyobserver.h
@@ -40,10 +40,11 @@
#ifndef QBS_SCRIPTPROPERTYOBSERVER_H
#define QBS_SCRIPTPROPERTYOBSERVER_H
+#include <quickjs.h>
+
#include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
-class QScriptValue;
class QString;
QT_END_NAMESPACE
@@ -64,8 +65,8 @@ public:
virtual ~ScriptPropertyObserver();
- virtual void onPropertyRead(const QScriptValue &object, const QString &name,
- const QScriptValue &value) = 0;
+ virtual void onPropertyRead(const JSValue &object, const QString &name,
+ const JSValue &value) = 0;
protected:
ScriptEngine * engine() const { return m_engine; }
diff --git a/src/lib/corelib/language/value.cpp b/src/lib/corelib/language/value.cpp
index f8cd3ad26..634f54faf 100644
--- a/src/lib/corelib/language/value.cpp
+++ b/src/lib/corelib/language/value.cpp
@@ -48,29 +48,65 @@
namespace qbs {
namespace Internal {
-Value::Value(Type t, bool createdByPropertiesBlock)
- : m_type(t), m_definingItem(nullptr), m_createdByPropertiesBlock(createdByPropertiesBlock)
+Value::Value(Type t, bool createdByPropertiesBlock) : m_type(t)
{
+ if (createdByPropertiesBlock)
+ m_flags |= OriginPropertiesBlock;
}
-Value::Value(const Value &other)
+Value::Value(const Value &other, ItemPool &pool)
: 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)
+ m_scope(other.m_scope),
+ m_scopeName(other.m_scopeName),
+ m_next(other.m_next ? other.m_next->clone(pool) : ValuePtr()),
+ m_candidates(other.m_candidates),
+ m_flags(other.m_flags)
{
}
Value::~Value() = default;
-Item *Value::definingItem() const
+void Value::setScope(Item *scope, const QString &scopeName)
{
- return m_definingItem;
+ m_scope = scope;
+ m_scopeName = scopeName;
}
-void Value::setDefiningItem(Item *item)
+int Value::priority(const Item *productItem) const
{
- m_definingItem = item;
+ if (m_priority == -1)
+ m_priority = calculatePriority(productItem);
+ return m_priority;
+}
+
+int Value::calculatePriority(const Item *productItem) const
+{
+ if (setInternally())
+ return INT_MAX;
+ if (setByCommandLine())
+ return INT_MAX - 1;
+ if (setByProfile())
+ return 2;
+ if (!scope())
+ return 1;
+ if (scope()->type() == ItemType::Product)
+ return INT_MAX - 2;
+ if (!scope()->isPresentModule())
+ return 0;
+ const auto it = std::find_if(
+ productItem->modules().begin(), productItem->modules().end(),
+ [this](const Item::Module &m) { return m.item == scope(); });
+ QBS_CHECK(it != productItem->modules().end());
+ return INT_MAX - 3 - it->maxDependsChainLength;
+}
+
+void Value::resetPriority()
+{
+ m_priority = -1;
+ if (m_next)
+ m_next->resetPriority();
+ for (const ValuePtr &v : m_candidates)
+ v->resetPriority();
}
ValuePtr Value::next() const
@@ -85,6 +121,11 @@ void Value::setNext(const ValuePtr &next)
m_next = next;
}
+bool Value::setInternally() const
+{
+ return type() == VariantValueType && !setByProfile() && !setByCommandLine();
+}
+
JSSourceValue::JSSourceValue(bool createdByPropertiesBlock)
: Value(JSSourceValueType, createdByPropertiesBlock)
@@ -93,31 +134,30 @@ JSSourceValue::JSSourceValue(bool createdByPropertiesBlock)
{
}
-JSSourceValue::JSSourceValue(const JSSourceValue &other) : Value(other)
+JSSourceValue::JSSourceValue(const JSSourceValue &other, ItemPool &pool) : Value(other, pool)
{
m_sourceCode = other.m_sourceCode;
m_line = other.m_line;
m_column = other.m_column;
m_file = other.m_file;
- m_flags = other.m_flags;
m_baseValue = other.m_baseValue
- ? std::static_pointer_cast<JSSourceValue>(other.m_baseValue->clone())
+ ? std::static_pointer_cast<JSSourceValue>(other.m_baseValue->clone(pool))
: JSSourceValuePtr();
- m_alternatives.reserve(other.m_alternatives.size());
- for (const Alternative &otherAlt : other.m_alternatives)
- m_alternatives.push_back(otherAlt.clone());
+ m_alternatives = transformed<std::vector<Alternative>>(
+ other.m_alternatives, [&pool](const auto &alternative) {
+ return alternative.clone(pool); });
}
JSSourceValuePtr JSSourceValue::create(bool createdByPropertiesBlock)
{
- return JSSourceValuePtr(new JSSourceValue(createdByPropertiesBlock));
+ return std::make_shared<JSSourceValue>(createdByPropertiesBlock);
}
JSSourceValue::~JSSourceValue() = default;
-ValuePtr JSSourceValue::clone() const
+ValuePtr JSSourceValue::clone(ItemPool &pool) const
{
- return JSSourceValuePtr(new JSSourceValue(*this));
+ return std::make_shared<JSSourceValue>(*this, pool);
}
QString JSSourceValue::sourceCodeForEvaluation() const
@@ -142,24 +182,45 @@ CodeLocation JSSourceValue::location() const
return CodeLocation(m_file->filePath(), m_line, m_column);
}
-void JSSourceValue::setHasFunctionForm(bool b)
+void JSSourceValue::clearAlternatives()
{
- if (b)
- m_flags |= HasFunctionForm;
- else
- m_flags &= ~HasFunctionForm;
+ m_alternatives.clear();
}
-void JSSourceValue::clearAlternatives()
+void JSSourceValue::setScope(Item *scope, const QString &scopeName)
{
- m_alternatives.clear();
+ Value::setScope(scope, scopeName);
+ if (m_baseValue)
+ m_baseValue->setScope(scope, scopeName);
+ for (const JSSourceValue::Alternative &a : m_alternatives)
+ a.value->setScope(scope, scopeName);
+}
+
+void JSSourceValue::resetPriority()
+{
+ Value::resetPriority();
+ if (m_baseValue)
+ m_baseValue->resetPriority();
+ for (const JSSourceValue::Alternative &a : m_alternatives)
+ a.value->resetPriority();
+}
+
+void JSSourceValue::addCandidate(const ValuePtr &v)
+{
+ Value::addCandidate(v);
+ if (m_baseValue)
+ m_baseValue->addCandidate(v);
+ for (const JSSourceValue::Alternative &a : m_alternatives)
+ a.value->addCandidate(v);
}
-void JSSourceValue::setDefiningItem(Item *item)
+void JSSourceValue::setCandidates(const std::vector<ValuePtr> &candidates)
{
- Value::setDefiningItem(item);
+ Value::setCandidates(candidates);
+ if (m_baseValue)
+ m_baseValue->setCandidates(candidates);
for (const JSSourceValue::Alternative &a : m_alternatives)
- a.value->setDefiningItem(item);
+ a.value->setCandidates(candidates);
}
ItemValue::ItemValue(Item *item, bool createdByPropertiesBlock)
@@ -171,49 +232,71 @@ ItemValue::ItemValue(Item *item, bool createdByPropertiesBlock)
ItemValuePtr ItemValue::create(Item *item, bool createdByPropertiesBlock)
{
- return ItemValuePtr(new ItemValue(item, createdByPropertiesBlock));
+ return std::make_shared<ItemValue>(item, createdByPropertiesBlock);
}
-ValuePtr ItemValue::clone() const
+ValuePtr ItemValue::clone(ItemPool &pool) const
{
- return create(m_item->clone(), createdByPropertiesBlock());
+ return create(m_item->clone(pool), createdByPropertiesBlock());
}
+class StoredVariantValue : public VariantValue
+{
+public:
+ explicit StoredVariantValue(QVariant v) : VariantValue(std::move(v)) {}
+
+ quintptr id() const override { return quintptr(this); }
+};
+
VariantValue::VariantValue(QVariant v)
: Value(VariantValueType, false)
, m_value(std::move(v))
{
}
-VariantValuePtr VariantValue::create(const QVariant &v)
+VariantValue::VariantValue(const VariantValue &other, ItemPool &pool)
+ : Value(other, pool), m_value(other.m_value) {}
+
+template<typename T>
+VariantValuePtr createImpl(const QVariant &v)
{
if (!v.isValid())
- return invalidValue();
- if (static_cast<QMetaType::Type>(v.type()) == QMetaType::Bool)
+ return VariantValue::invalidValue();
+ if (static_cast<QMetaType::Type>(v.userType()) == QMetaType::Bool)
return v.toBool() ? VariantValue::trueValue() : VariantValue::falseValue();
- return VariantValuePtr(new VariantValue(v));
+ return std::make_shared<T>(v);
+}
+
+VariantValuePtr VariantValue::create(const QVariant &v)
+{
+ return createImpl<VariantValue>(v);
+}
+
+VariantValuePtr VariantValue::createStored(const QVariant &v)
+{
+ return createImpl<StoredVariantValue>(v);
}
-ValuePtr VariantValue::clone() const
+ValuePtr VariantValue::clone(ItemPool &pool) const
{
- return VariantValuePtr(new VariantValue(*this));
+ return std::make_shared<VariantValue>(*this, pool);
}
const VariantValuePtr &VariantValue::falseValue()
{
- static const VariantValuePtr v = VariantValuePtr(new VariantValue(false));
+ static const VariantValuePtr v = std::make_shared<VariantValue>(false);
return v;
}
const VariantValuePtr &VariantValue::trueValue()
{
- static const VariantValuePtr v = VariantValuePtr(new VariantValue(true));
+ static const VariantValuePtr v = std::make_shared<VariantValue>(true);
return v;
}
const VariantValuePtr &VariantValue::invalidValue()
{
- static const VariantValuePtr v = VariantValuePtr(new VariantValue(QVariant()));
+ static const VariantValuePtr v = std::make_shared<VariantValue>(QVariant());
return v;
}
diff --git a/src/lib/corelib/language/value.h b/src/lib/corelib/language/value.h
index a48e974d3..1a6746e24 100644
--- a/src/lib/corelib/language/value.h
+++ b/src/lib/corelib/language/value.h
@@ -42,6 +42,7 @@
#include "forward_decls.h"
#include <tools/codelocation.h>
+#include <QtCore/qstring.h>
#include <QtCore/qvariant.h>
#include <vector>
@@ -49,42 +50,86 @@
namespace qbs {
namespace Internal {
class Item;
+class ItemPool;
class ValueHandler;
class Value
{
public:
- enum Type
- {
+ enum Type {
JSSourceValueType,
ItemValueType,
VariantValueType
};
+ enum Flag {
+ NoFlags = 0x00,
+ SourceUsesBase = 0x01,
+ SourceUsesOuter = 0x02,
+ SourceUsesOriginal = 0x04,
+ HasFunctionForm = 0x08,
+ ExclusiveListValue = 0x10,
+ BuiltinDefaultValue = 0x20,
+ OriginPropertiesBlock = 0x40,
+ OriginProfile = 0x80,
+ OriginCommandLine = 0x100,
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
Value(Type t, bool createdByPropertiesBlock);
- Value(const Value &other);
+ Value(const Value &other) = delete;
+ Value(const Value &other, ItemPool &pool);
virtual ~Value();
Type type() const { return m_type; }
virtual void apply(ValueHandler *) = 0;
- virtual ValuePtr clone() const = 0;
+ virtual ValuePtr clone(ItemPool &) const = 0;
virtual CodeLocation location() const { return {}; }
- Item *definingItem() const;
- virtual void setDefiningItem(Item *item);
+ Item *scope() const { return m_scope; }
+ virtual void setScope(Item *scope, const QString &scopeName);
+ QString scopeName() const { return m_scopeName; }
+ int priority(const Item *productItem) const;
+ virtual void resetPriority();
ValuePtr next() const;
void setNext(const ValuePtr &next);
- bool createdByPropertiesBlock() const { return m_createdByPropertiesBlock; }
- void setCreatedByPropertiesBlock(bool b) { m_createdByPropertiesBlock = b; }
- void clearCreatedByPropertiesBlock() { m_createdByPropertiesBlock = false; }
+ virtual void addCandidate(const ValuePtr &v) { m_candidates.push_back(v); }
+ const std::vector<ValuePtr> &candidates() const { return m_candidates; }
+ virtual void setCandidates(const std::vector<ValuePtr> &candidates) { m_candidates = candidates; }
+
+ bool createdByPropertiesBlock() const { return m_flags & OriginPropertiesBlock; }
+ void markAsSetByProfile() { m_flags |= OriginProfile; }
+ bool setByProfile() const { return m_flags & OriginProfile; }
+ void markAsSetByCommandLine() { m_flags |= OriginCommandLine; }
+ bool setByCommandLine() const { return m_flags & OriginCommandLine; }
+ bool setInternally() const;
+ bool expired(const Item *productItem) const { return priority(productItem) == 0; }
+
+ void setSourceUsesBase() { m_flags |= SourceUsesBase; }
+ bool sourceUsesBase() const { return m_flags.testFlag(SourceUsesBase); }
+ void setSourceUsesOuter() { m_flags |= SourceUsesOuter; }
+ bool sourceUsesOuter() const { return m_flags.testFlag(SourceUsesOuter); }
+ void setSourceUsesOriginal() { m_flags |= SourceUsesOriginal; }
+ bool sourceUsesOriginal() const { return m_flags.testFlag(SourceUsesOriginal); }
+ void setHasFunctionForm() { m_flags |= HasFunctionForm; }
+ bool hasFunctionForm() const { return m_flags.testFlag(HasFunctionForm); }
+ void setIsExclusiveListValue() { m_flags |= ExclusiveListValue; }
+ bool isExclusiveListValue() { return m_flags.testFlag(ExclusiveListValue); }
+ void setIsBuiltinDefaultValue() { m_flags |= BuiltinDefaultValue; }
+ bool isBuiltinDefaultValue() const { return m_flags.testFlag(BuiltinDefaultValue); }
private:
+ int calculatePriority(const Item *productItem) const;
+
Type m_type;
- Item *m_definingItem;
+ Item *m_scope = nullptr;
+ QString m_scopeName;
ValuePtr m_next;
- bool m_createdByPropertiesBlock;
+ std::vector<ValuePtr> m_candidates;
+ Flags m_flags;
+ mutable int m_priority = -1;
};
class ValueHandler
@@ -98,30 +143,19 @@ public:
class JSSourceValue : public Value
{
friend class ItemReaderASTVisitor;
- JSSourceValue(bool createdByPropertiesBlock);
- JSSourceValue(const JSSourceValue &other);
-
- enum Flag
- {
- NoFlags = 0x00,
- SourceUsesBase = 0x01,
- SourceUsesOuter = 0x02,
- SourceUsesOriginal = 0x04,
- HasFunctionForm = 0x08,
- ExclusiveListValue = 0x10,
- BuiltinDefaultValue = 0x20,
- };
- Q_DECLARE_FLAGS(Flags, Flag)
public:
+ explicit JSSourceValue(bool createdByPropertiesBlock);
+ JSSourceValue(const JSSourceValue &other, ItemPool &pool);
+
static JSSourceValuePtr QBS_AUTOTEST_EXPORT create(bool createdByPropertiesBlock = false);
~JSSourceValue() override;
void apply(ValueHandler *handler) override { handler->handle(this); }
- ValuePtr clone() const override;
+ ValuePtr clone(ItemPool &pool) const override;
- void setSourceCode(const QStringRef &sourceCode) { m_sourceCode = sourceCode; }
- const QStringRef &sourceCode() const { return m_sourceCode; }
+ void setSourceCode(QStringView sourceCode) { m_sourceCode = sourceCode; }
+ QStringView sourceCode() const { return m_sourceCode; }
QString sourceCodeForEvaluation() const;
void setLocation(int line, int column);
@@ -132,17 +166,6 @@ public:
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); }
- void setIsBuiltinDefaultValue() { m_flags |= BuiltinDefaultValue; }
- bool isBuiltinDefaultValue() const { return m_flags.testFlag(BuiltinDefaultValue); }
-
const JSSourceValuePtr &baseValue() const { return m_baseValue; }
void setBaseValue(const JSSourceValuePtr &v) { m_baseValue = v; }
@@ -151,18 +174,18 @@ public:
struct PropertyData
{
PropertyData() = default;
- PropertyData(const QString &v, const CodeLocation &l) : value(v), location(l) {}
+ PropertyData(QString v, const CodeLocation &l) : value(std::move(v)), location(l) {}
QString value;
CodeLocation location;
};
Alternative() = default;
- Alternative(const PropertyData &c, const PropertyData &o, const JSSourceValuePtr &v)
- : condition(c), overrideListProperties(o), value(v) {}
- Alternative clone() const
+ Alternative(PropertyData c, PropertyData o, JSSourceValuePtr v)
+ : condition(std::move(c)), overrideListProperties(std::move(o)), value(std::move(v)) {}
+ Alternative clone(ItemPool &pool) const
{
return Alternative(condition, overrideListProperties,
- std::static_pointer_cast<JSSourceValue>(value->clone()));
+ std::static_pointer_cast<JSSourceValue>(value->clone(pool)));
}
PropertyData condition;
@@ -175,14 +198,16 @@ public:
void addAlternative(const Alternative &alternative) { m_alternatives.push_back(alternative); }
void clearAlternatives();
- void setDefiningItem(Item *item) override;
+ void setScope(Item *scope, const QString &scopeName) override;
+ void resetPriority() override;
+ void addCandidate(const ValuePtr &v) override;
+ void setCandidates(const std::vector<ValuePtr> &candidates) override;
private:
- QStringRef m_sourceCode;
+ QStringView m_sourceCode;
int m_line;
int m_column;
FileContextPtr m_file;
- Flags m_flags;
JSSourceValuePtr m_baseValue;
std::vector<Alternative> m_alternatives;
};
@@ -190,8 +215,8 @@ private:
class ItemValue : public Value
{
- ItemValue(Item *item, bool createdByPropertiesBlock);
public:
+ ItemValue(Item *item, bool createdByPropertiesBlock);
static ItemValuePtr create(Item *item, bool createdByPropertiesBlock = false);
Item *item() const { return m_item; }
@@ -199,7 +224,7 @@ public:
private:
void apply(ValueHandler *handler) override { handler->handle(this); }
- ValuePtr clone() const override;
+ ValuePtr clone(ItemPool &pool) const override;
Item *m_item;
};
@@ -207,14 +232,17 @@ private:
class VariantValue : public Value
{
- VariantValue(QVariant v);
public:
+ explicit VariantValue(QVariant v);
+ VariantValue(const VariantValue &v, ItemPool &pool);
static VariantValuePtr create(const QVariant &v = QVariant());
+ static VariantValuePtr createStored(const QVariant &v = QVariant());
void apply(ValueHandler *handler) override { handler->handle(this); }
- ValuePtr clone() const override;
+ ValuePtr clone(ItemPool &pool) const override;
const QVariant &value() const { return m_value; }
+ virtual quintptr id() const { return 0; }
static const VariantValuePtr &falseValue();
static const VariantValuePtr &trueValue();
diff --git a/src/lib/corelib/loader/astimportshandler.cpp b/src/lib/corelib/loader/astimportshandler.cpp
new file mode 100644
index 000000000..c0281ca24
--- /dev/null
+++ b/src/lib/corelib/loader/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 "itemreadervisitorstate.h"
+
+#include <jsextensions/jsextensions.h>
+#include <language/asttools.h>
+#include <language/builtindeclarations.h>
+#include <language/filecontext.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <parser/qmljsast_p.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/qttools.h>
+#include <tools/stringconstants.h>
+#include <tools/version.h>
+
+#include <QtCore/qdiriterator.h>
+
+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)
+{
+ const auto searchPaths = m_file->searchPaths();
+ for (const QString &searchPath : searchPaths)
+ collectPrototypes(searchPath + QStringLiteral("/imports"), QString());
+
+ // files in the same directory are available as prototypes
+ collectPrototypes(m_directory, QString());
+
+ bool baseImported = false;
+ for (const auto *it = uiImportList; it; it = it->next)
+ handleImport(it->import, &baseImported);
+ if (!baseImported) {
+ QStringRef qbsref(&StringConstants::qbsModule());
+ QbsQmlJS::AST::UiQualifiedId qbsURI(qbsref);
+ qbsURI.finish();
+ QbsQmlJS::AST::UiImport imp(&qbsURI);
+ handleImport(&imp, &baseImported);
+ }
+
+ for (auto it = m_jsImports.constBegin(); it != m_jsImports.constEnd(); ++it)
+ m_file->addJsImport(it.value());
+}
+
+void ASTImportsHandler::handleImport(const QbsQmlJS::AST::UiImport *import, bool *baseImported)
+{
+ QStringList importUri;
+ bool isBase = false;
+ if (import->importUri) {
+ importUri = toStringList(import->importUri);
+ isBase = (importUri.size() == 1 && importUri.front() == StringConstants::qbsModule())
+ || (importUri.size() == 2 && importUri.front() == StringConstants::qbsModule()
+ && importUri.last() == StringConstants::baseVar());
+ if (isBase) {
+ *baseImported = true;
+ 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 <Name>'"),
+ toCodeLocation(m_file->filePath(), import->importIdToken));
+ }
+ } else {
+ if (importUri.size() == 2 && importUri.front() == StringConstants::qbsModule()) {
+ 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 <Name>'"),
+ toCodeLocation(m_file->filePath(), import->importToken));
+ }
+ if (importUri.empty()) {
+ throw ErrorInfo(Tr::tr("Invalid import URI."),
+ toCodeLocation(m_file->filePath(), import->importToken));
+ }
+ as = importUri.last();
+ } else {
+ as = import->importId.toString();
+ }
+
+ 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));
+ if (Q_UNLIKELY(!m_importAsNames.insert(as).second)) {
+ throw ErrorInfo(Tr::tr("Cannot import into the same name more than once."),
+ toCodeLocation(m_file->filePath(), import->importIdToken));
+ }
+ }
+
+ 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(QStringLiteral(".js"), Qt::CaseInsensitive)) {
+ JsImport &jsImport = m_jsImports[as];
+ jsImport.scopeName = as;
+ jsImport.filePaths.push_back(filePath);
+ jsImport.location
+ = toCodeLocation(m_file->filePath(), import->firstSourceLocation());
+ } else if (filePath.endsWith(QStringLiteral(".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.empty()) {
+ const QString importPath = isBase
+ ? QStringLiteral("qbs/base") : importUri.join(QDir::separator());
+ bool found = m_typeNameToFile.contains(importUri);
+ if (!found) {
+ const auto searchPaths = m_file->searchPaths();
+ for (const QString &searchPath : searchPaths) {
+ const QFileInfo fi(FileInfo::resolvePath(
+ FileInfo::resolvePath(searchPath,
+ StringConstants::importsDir()),
+ importPath));
+ if (fi.isDir()) {
+ // ### versioning, qbsdir file, etc.
+ const QString &resultPath = fi.absoluteFilePath();
+ collectPrototypesAndJsCollections(resultPath, as,
+ toCodeLocation(m_file->filePath(), import->fileNameToken));
+ 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.push_back(as);
+ prototypeName.push_back(componentName);
+ if (!m_typeNameToFile.contains(prototypeName))
+ 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*.
+ m_visitorState.findDirectoryEntries(path, &fileNames);
+ for (const QString &fileName : std::as_const(fileNames))
+ addPrototype(fileName, path + QLatin1Char('/') + fileName, as, false);
+}
+
+void ASTImportsHandler::collectPrototypesAndJsCollections(const QString &path, const QString &as,
+ const CodeLocation &location)
+{
+ collectPrototypes(path, as);
+ QDirIterator dirIter(path, StringConstants::jsFileWildcards());
+ while (dirIter.hasNext()) {
+ dirIter.next();
+ JsImport &jsImport = m_jsImports[as];
+ if (jsImport.scopeName.isNull()) {
+ jsImport.scopeName = as;
+ jsImport.location = location;
+ }
+ jsImport.filePaths.push_back(dirIter.filePath());
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/loader/astimportshandler.h b/src/lib/corelib/loader/astimportshandler.h
new file mode 100644
index 000000000..582e1c698
--- /dev/null
+++ b/src/lib/corelib/loader/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 <language/forward_decls.h>
+
+#include <parser/qmljsastfwd_p.h>
+#include <tools/set.h>
+
+#include <QtCore/qhash.h>
+#include <QtCore/qstringlist.h>
+
+namespace qbs {
+class CodeLocation;
+class Version;
+
+namespace Internal {
+class ItemReaderVisitorState;
+class JsImport;
+class Logger;
+
+class ASTImportsHandler
+{
+public:
+ ASTImportsHandler(ItemReaderVisitorState &visitorState, Logger &logger,
+ const FileContextPtr &file);
+
+ void handleImports(const QbsQmlJS::AST::UiImportList *uiImportList);
+
+ QHash<QStringList, QString> 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, bool *baseImported);
+
+ ItemReaderVisitorState &m_visitorState;
+ Logger &m_logger;
+ const FileContextPtr &m_file;
+ const QString m_directory;
+ QHash<QStringList, QString> m_typeNameToFile;
+ Set<QString> m_importAsNames;
+
+ using JsImportsHash = QHash<QString, JsImport>;
+ JsImportsHash m_jsImports;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard
diff --git a/src/lib/corelib/loader/astpropertiesitemhandler.cpp b/src/lib/corelib/loader/astpropertiesitemhandler.cpp
new file mode 100644
index 000000000..8183e6b79
--- /dev/null
+++ b/src/lib/corelib/loader/astpropertiesitemhandler.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 <language/item.h>
+#include <language/value.h>
+
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/qbsassert.h>
+#include <tools/stringconstants.h>
+
+namespace qbs {
+namespace Internal {
+
+ASTPropertiesItemHandler::ASTPropertiesItemHandler(Item *parentItem, ItemPool &itemPool)
+ : m_parentItem(parentItem), m_itemPool(itemPool)
+{
+}
+
+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;
+ bool remove = false;
+ if (child->type() == ItemType::Properties) {
+ handlePropertiesBlock(child);
+ remove = m_parentItem->type() != ItemType::Export;
+ }
+ if (remove)
+ it = m_parentItem->m_children.erase(it);
+ else
+ ++it;
+ }
+}
+
+class PropertiesBlockConverter
+{
+public:
+ PropertiesBlockConverter(const JSSourceValue::AltProperty &condition,
+ const JSSourceValue::AltProperty &overrideListProperties,
+ Item *propertiesBlockContainer, const Item *propertiesBlock,
+ ItemPool &pool)
+ : m_propertiesBlockContainer(propertiesBlockContainer)
+ , m_propertiesBlock(propertiesBlock)
+ , m_itemPool(pool)
+ {
+ 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;
+ ItemPool &m_itemPool;
+
+ void doApply(Item *outer, const Item *inner)
+ {
+ for (auto it = inner->properties().constBegin();
+ it != inner->properties().constEnd(); ++it) {
+ if (inner == m_propertiesBlock
+ && (it.key() == StringConstants::conditionProperty()
+ || it.key() == StringConstants::overrideListPropertiesProperty())) {
+ continue;
+ }
+ if (it.value()->type() == Value::ItemValueType) {
+ Item * const innerVal = std::static_pointer_cast<ItemValue>(it.value())->item();
+ ItemValuePtr outerVal = outer->itemProperty(it.key(), m_itemPool);
+ if (!outerVal) {
+ outerVal = ItemValue::create(Item::create(&m_itemPool, innerVal->type()),
+ true);
+ outer->setProperty(it.key(), outerVal);
+ }
+ doApply(outerVal->item(), innerVal);
+ } 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, std::static_pointer_cast<JSSourceValue>(outerVal),
+ std::static_pointer_cast<JSSourceValue>(it.value()));
+ } else {
+ QBS_CHECK(!"Unexpected value type in conditional value.");
+ }
+ }
+ }
+
+ void doApply(const QString &propertyName, Item *item, JSSourceValuePtr value,
+ const JSSourceValuePtr &conditionalValue)
+ {
+ if (!value) {
+ value = JSSourceValue::create(true);
+ value->setFile(conditionalValue->file());
+ item->setProperty(propertyName, value);
+ value->setSourceCode(StringConstants::baseVar());
+ value->setSourceUsesBase();
+ }
+ m_alternative.value = conditionalValue;
+ value->addAlternative(m_alternative);
+ }
+};
+
+static JSSourceValue::AltProperty getPropertyData(const Item *propertiesItem, const QString &name)
+{
+ const ValuePtr value = propertiesItem->property(name);
+ if (!value) {
+ if (name == StringConstants::conditionProperty()) {
+ throw ErrorInfo(Tr::tr("Properties.condition must be provided."),
+ propertiesItem->location());
+ }
+ return {StringConstants::falseValue(), propertiesItem->location()};
+ }
+ if (Q_UNLIKELY(value->type() != Value::JSSourceValueType)) {
+ throw ErrorInfo(Tr::tr("Properties.%1 must be a value binding.").arg(name),
+ propertiesItem->location());
+ }
+ if (name == StringConstants::overrideListPropertiesProperty()) {
+ 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 = std::static_pointer_cast<JSSourceValue>(value);
+ return {srcval->sourceCodeForEvaluation(), srcval->location()};
+}
+
+void ASTPropertiesItemHandler::handlePropertiesBlock(const Item *propertiesItem)
+{
+ const auto condition = getPropertyData(propertiesItem, StringConstants::conditionProperty());
+ const auto overrideListProperties = getPropertyData(propertiesItem,
+ StringConstants::overrideListPropertiesProperty());
+ PropertiesBlockConverter(condition, overrideListProperties, m_parentItem,
+ propertiesItem, m_itemPool).apply();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/loader/astpropertiesitemhandler.h b/src/lib/corelib/loader/astpropertiesitemhandler.h
new file mode 100644
index 000000000..804abb8a1
--- /dev/null
+++ b/src/lib/corelib/loader/astpropertiesitemhandler.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_ASTPROPERTIESITEMHANDLER_H
+#define QBS_ASTPROPERTIESITEMHANDLER_H
+
+namespace qbs {
+namespace Internal {
+class Item;
+class ItemPool;
+
+class ASTPropertiesItemHandler
+{
+public:
+ ASTPropertiesItemHandler(Item *parentItem, ItemPool &itemPool);
+
+ void handlePropertiesItems();
+
+private:
+ void setupAlternatives();
+ void handlePropertiesBlock(const Item *propertiesItem);
+
+ Item * const m_parentItem;
+ ItemPool &m_itemPool;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/loader/dependenciesresolver.cpp b/src/lib/corelib/loader/dependenciesresolver.cpp
new file mode 100644
index 000000000..059592e9c
--- /dev/null
+++ b/src/lib/corelib/loader/dependenciesresolver.cpp
@@ -0,0 +1,1175 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "dependenciesresolver.h"
+
+#include "itemreader.h"
+#include "loaderutils.h"
+#include "moduleinstantiator.h"
+#include "moduleloader.h"
+
+#include <language/scriptengine.h>
+#include <language/evaluator.h>
+#include <language/item.h>
+#include <language/itempool.h>
+#include <language/value.h>
+#include <logging/categories.h>
+#include <logging/translator.h>
+#include <tools/fileinfo.h>
+#include <tools/preferences.h>
+#include <tools/profiling.h>
+#include <tools/settings.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+
+#include <optional>
+#include <queue>
+#include <unordered_map>
+
+namespace qbs::Internal {
+namespace {
+enum class HandleDependency { Use, Ignore, Defer };
+
+class LoadModuleResult
+{
+public:
+ Item *moduleItem = nullptr;
+ ProductContext *product = nullptr;
+ HandleDependency handleDependency = HandleDependency::Use;
+};
+
+// Corresponds completely to a Depends item.
+// May result in more than one module, due to "multiplexing" properties such as subModules etc.
+// May also result in no module at all, e.g. if productTypes does not match anything.
+class EvaluatedDependsItem
+{
+public:
+ Item *item = nullptr;
+ QualifiedId name;
+ QStringList subModules;
+ FileTags productTypes;
+ QStringList multiplexIds;
+ std::optional<QStringList> profiles;
+ VersionRange versionRange;
+ QVariantMap parameters;
+ bool limitToSubProject = false;
+ bool requiredLocally = true;
+ bool requiredGlobally = true;
+};
+
+// As opposed to EvaluatedDependsItem, one of these corresponds exactly to one module
+// to be loaded. Such an attempt might still fail, though, which may or may not result
+// in an error, depending on the value of Depends.required and other circumstances.
+class FullyResolvedDependsItem
+{
+public:
+ FullyResolvedDependsItem(ProductContext *product, const EvaluatedDependsItem &dependency);
+ FullyResolvedDependsItem(const EvaluatedDependsItem &dependency, QualifiedId name,
+ QString profile, QString multiplexId);
+ FullyResolvedDependsItem() = default;
+ static FullyResolvedDependsItem makeBaseDependency();
+
+ QString id() const;
+ CodeLocation location() const;
+ QString displayName() const;
+
+ // If product is non-null, we already know which product the dependency targets.
+ // This happens either if Depends.productTypes was set, or if we tried to load the
+ // dependency before and already identified the product, but could not complete the
+ // procedure because said product had itself not been handled yet.
+ ProductContext *product = nullptr;
+
+ Item *item = nullptr;
+ QualifiedId name;
+ QString profile;
+ QString multiplexId;
+ VersionRange versionRange;
+ QVariantMap parameters;
+ bool limitToSubProject = false;
+ bool requiredLocally = true;
+ bool requiredGlobally = true;
+ bool checkProduct = true;
+};
+
+class DependenciesResolvingState
+{
+public:
+ Item *loadingItem = nullptr;
+ FullyResolvedDependsItem loadingItemOrigin;
+ std::queue<Item *> pendingDependsItems;
+ std::optional<EvaluatedDependsItem> currentDependsItem;
+ std::queue<FullyResolvedDependsItem> pendingResolvedDependencies;
+ bool requiredByLoadingItem = true;
+};
+
+class DependenciesContextImpl : public DependenciesContext
+{
+public:
+ DependenciesContextImpl(ProductContext &product, LoaderState &loaderState);
+
+ std::list<DependenciesResolvingState> stateStack;
+
+private:
+ std::pair<ProductDependency, ProductContext *> pendingDependency() const override;
+
+ void setSearchPathsForProduct(LoaderState &loaderState);
+
+ ProductContext &m_product;
+};
+
+class DependenciesResolver
+{
+public:
+ DependenciesResolver(LoaderState &loaderState, ProductContext &product, Deferral deferral)
+ : m_loaderState(loaderState), m_product(product), m_deferral(deferral) {}
+
+ void resolve();
+ LoadModuleResult loadModule(Item *loadingItem, const FullyResolvedDependsItem &dependency);
+
+private:
+ void evaluateNextDependsItem();
+ HandleDependency handleResolvedDependencies();
+ std::pair<Item::Module *, Item *> findExistingModule(const FullyResolvedDependsItem &dependency,
+ Item *item);
+ void updateModule(Item::Module &module, const FullyResolvedDependsItem &dependency);
+ int dependsChainLength();
+ ProductContext *findMatchingProduct(const FullyResolvedDependsItem &dependency);
+ Item *findMatchingModule(const FullyResolvedDependsItem &dependency);
+ std::pair<bool, HandleDependency> checkProductDependency(
+ const FullyResolvedDependsItem &depSpec, const ProductContext &dep);
+ void checkModule(const FullyResolvedDependsItem &dependency, Item *moduleItem,
+ ProductContext *productDep);
+ void checkForModuleNamePrefixCollision(const FullyResolvedDependsItem &dependency);
+ Item::Module createModule(const FullyResolvedDependsItem &dependency, Item *item,
+ ProductContext *productDep);
+ void adjustDependsItemForMultiplexing(Item *dependsItem);
+ std::optional<EvaluatedDependsItem> evaluateDependsItem(Item *item);
+ std::queue<FullyResolvedDependsItem> multiplexDependency(
+ const EvaluatedDependsItem &dependency);
+ QVariantMap extractParameters(Item *dependsItem) const;
+ void forwardParameterDeclarations(const Item *dependsItem, const Item::Modules &modules);
+ void forwardParameterDeclarations(const QualifiedId &moduleName, Item *item,
+ const Item::Modules &modules);
+ std::list<DependenciesResolvingState> &stateStack();
+
+ LoaderState &m_loaderState;
+ ProductContext &m_product;
+ Deferral m_deferral;
+};
+
+static bool haveSameSubProject(const ProductContext &p1, const ProductContext &p2);
+static QVariantMap safeToVariant(JSContext *ctx, const JSValue &v);
+
+} // namespace
+
+void resolveDependencies(ProductContext &product, Deferral deferral, LoaderState &loaderState)
+{
+ DependenciesResolver(loaderState, product, deferral).resolve();
+}
+
+Item *loadBaseModule(ProductContext &product, Item *item, LoaderState &loaderState)
+{
+ const auto baseDependency = FullyResolvedDependsItem::makeBaseDependency();
+ Item * const moduleItem = DependenciesResolver(loaderState, product, Deferral::NotAllowed)
+ .loadModule(item, baseDependency).moduleItem;
+ if (Q_UNLIKELY(!moduleItem))
+ throw ErrorInfo(Tr::tr("Cannot load base qbs module."));
+ return moduleItem;
+}
+
+namespace {
+
+void DependenciesResolver::resolve()
+{
+ AccumulatingTimer timer(m_loaderState.parameters().logElapsedTime()
+ ? &m_product.timingData.dependenciesResolving : nullptr);
+
+ if (!m_product.dependenciesContext) {
+ m_product.dependenciesContext = std::make_unique<DependenciesContextImpl>(
+ m_product, m_loaderState);
+ } else {
+ QBS_CHECK(!m_product.dependenciesContext->dependenciesResolved);
+ }
+ SearchPathsManager searchPathsMgr(m_loaderState.itemReader(), m_product.searchPaths);
+
+ while (!stateStack().empty()) {
+ auto &state = stateStack().front();
+
+ // If we have pending FullyResolvedDependsItems, then these are handled first.
+ if (handleResolvedDependencies() == HandleDependency::Defer)
+ return;
+
+ // The above procedure might have pushed another state to the stack due to recursive
+ // dependencies (i.e. Depends items in the newly loaded module), in which case we
+ // continue with that one.
+ if (&state != &stateStack().front())
+ continue;
+
+ // If we have a pending EvaluatedDependsItem, we multiplex it and then handle
+ // the resulting FullyResolvedDependsItems, if there were any.
+ if (state.currentDependsItem) {
+ QBS_CHECK(state.pendingResolvedDependencies.empty());
+
+ // We postpone handling Depends.productTypes for as long as possible, because
+ // the full type of a product becomes available only after its modules have been loaded.
+ if (!state.currentDependsItem->productTypes.empty() && m_deferral == Deferral::Allowed)
+ return;
+
+ state.pendingResolvedDependencies = multiplexDependency(*state.currentDependsItem);
+ state.currentDependsItem.reset();
+ m_deferral = Deferral::Allowed; // We made progress.
+
+ continue;
+ }
+
+ // Here we have no resolved/evaluated Depends items of any kind, so we evaluate the next
+ // pending Depends item.
+ evaluateNextDependsItem();
+ if (state.currentDependsItem)
+ continue;
+
+ // No resolved or unresolved Depends items are left, so we're done with the current state.
+ QBS_CHECK(!state.currentDependsItem);
+ QBS_CHECK(state.pendingResolvedDependencies.empty());
+ QBS_CHECK(state.pendingDependsItems.empty());
+
+ // This ensures our invariant: A sorted module list in the product
+ // (dependers after dependencies).
+ if (stateStack().size() > 1) {
+ QBS_CHECK(state.loadingItem->type() == ItemType::ModuleInstance);
+ Item::Modules &modules = m_product.item->modules();
+ const auto loadingItemModule = std::find_if(modules.begin(), modules.end(),
+ [&](const Item::Module &m) {
+ return m.item == state.loadingItem;
+ });
+ QBS_CHECK(loadingItemModule != modules.end());
+ const Item::Module tempModule = *loadingItemModule;
+ modules.erase(loadingItemModule);
+ modules.push_back(tempModule);
+ }
+ stateStack().pop_front();
+ }
+ m_product.dependenciesContext->dependenciesResolved = true;
+}
+
+void DependenciesResolver::evaluateNextDependsItem()
+{
+ auto &state = stateStack().front();
+ while (!state.pendingDependsItems.empty()) {
+ QBS_CHECK(!state.currentDependsItem);
+ QBS_CHECK(state.pendingResolvedDependencies.empty());
+ Item * const dependsItem = state.pendingDependsItems.front();
+ state.pendingDependsItems.pop();
+ adjustDependsItemForMultiplexing(dependsItem);
+ if (auto evaluated = evaluateDependsItem(dependsItem)) {
+ evaluated->requiredGlobally = evaluated->requiredLocally
+ && state.loadingItemOrigin.requiredGlobally;
+ state.currentDependsItem = evaluated;
+ return;
+ }
+ }
+}
+
+HandleDependency DependenciesResolver::handleResolvedDependencies()
+{
+ DependenciesResolvingState &state = stateStack().front();
+ while (!state.pendingResolvedDependencies.empty()) {
+ QBS_CHECK(!state.currentDependsItem);
+ const FullyResolvedDependsItem dependency = state.pendingResolvedDependencies.front();
+ try {
+ const LoadModuleResult res = loadModule(state.loadingItem, dependency);
+ switch (res.handleDependency) {
+ case HandleDependency::Defer:
+ QBS_CHECK(m_deferral == Deferral::Allowed);
+
+ // Optimization: We already looked up the product, so let's not do that again
+ // next time.
+ if (res.product)
+ state.pendingResolvedDependencies.front().product = res.product;
+
+ return HandleDependency::Defer;
+ case HandleDependency::Ignore:
+ // This happens if the dependency was not required or the module was already
+ // loaded via another path.
+ state.pendingResolvedDependencies.pop();
+ continue;
+ case HandleDependency::Use:
+ if (dependency.name.toString() == StringConstants::qbsModule()) {
+ // Shortcut: No need to look for recursive dependencies in the base module.
+ state.pendingResolvedDependencies.pop();
+ continue;
+ }
+ m_deferral = Deferral::Allowed; // We made progress.
+ break;
+ }
+
+ QBS_CHECK(res.moduleItem);
+
+ // Now continue with the dependencies of the just-loaded module.
+ std::queue<Item *> moduleDependsItems;
+ for (Item * const child : res.moduleItem->children()) {
+ if (child->type() == ItemType::Depends)
+ moduleDependsItems.push(child);
+ }
+ state.pendingResolvedDependencies.pop();
+ stateStack().push_front(
+ {res.moduleItem, dependency, moduleDependsItems, {}, {},
+ dependency.requiredGlobally || state.requiredByLoadingItem});
+ stateStack().front().pendingResolvedDependencies.push(
+ FullyResolvedDependsItem::makeBaseDependency());
+ break;
+ } catch (const ErrorInfo &e) {
+ if (dependency.name.toString() == StringConstants::qbsModule())
+ throw e;
+
+ // See QBS-1338 for why we do not abort handling the product.
+ state.pendingResolvedDependencies.pop();
+ Item::Modules &modules = m_product.item->modules();
+
+ // Unwind.
+ while (stateStack().size() > 1) {
+ const auto loadingItemModule = std::find_if(
+ modules.begin(), modules.end(), [&](const Item::Module &m) {
+ return m.item == stateStack().front().loadingItem;
+ });
+ for (auto it = loadingItemModule; it != modules.end(); ++it) {
+ createNonPresentModule(m_loaderState.itemPool(), it->name.toString(),
+ QLatin1String("error in Depends chain"), it->item);
+ }
+ modules.erase(loadingItemModule, modules.end());
+ stateStack().pop_front();
+ }
+
+ m_product.handleError(e);
+ return HandleDependency::Ignore;
+ }
+ }
+ return HandleDependency::Ignore;
+}
+
+// Produces an item of type ModuleInstance corresponding to the specified dependency.
+// The instance's prototype item is either of type Export (if the dependency is a product)
+// or of type Module (for an actual module).
+// The loadingItem parameter is either the depending product or another module. The newly
+// created module is added to the module list of the product item and additionally to the
+// loading item's one, if it is not the product. Its name is also injected into the respective
+// scopes.
+LoadModuleResult DependenciesResolver::loadModule(
+ Item *loadingItem, const FullyResolvedDependsItem &dependency)
+{
+ qCDebug(lcModuleLoader) << "loadModule name:" << dependency.name.toString()
+ << "id:" << dependency.id();
+
+ QBS_CHECK(loadingItem);
+
+ ProductContext *productDep = nullptr;
+ Item *moduleItem = nullptr;
+
+ const auto addLoadContext = [&](Item::Module &module) {
+ module.loadContexts.emplace_back(dependency.item,
+ std::make_pair(dependency.parameters,
+ INT_MAX - dependsChainLength()));
+ };
+
+ // The module might already have been loaded for this product (directly or indirectly).
+ const auto &[existingModule, moduleWithSameName]
+ = findExistingModule(dependency, m_product.item);
+ if (existingModule) {
+ // Merge version range and required property. These will be checked again
+ // after probes resolving.
+ if (existingModule->name.toString() != StringConstants::qbsModule())
+ updateModule(*existingModule, dependency);
+
+ QBS_CHECK(existingModule->item);
+ moduleItem = existingModule->item;
+ const auto matcher = [loadingItem](const Item::Module::LoadContext &context) {
+ return context.loadingItem() == loadingItem;
+ };
+ const auto it = std::find_if(existingModule->loadContexts.begin(),
+ existingModule->loadContexts.end(), matcher);
+ if (it == existingModule->loadContexts.end())
+ addLoadContext(*existingModule);
+ else
+ it->parameters.first = mergeDependencyParameters(it->parameters.first,
+ dependency.parameters);
+ } else if (dependency.product) {
+ productDep = dependency.product; // We have already done the look-up.
+ } else if (productDep = findMatchingProduct(dependency); !productDep) {
+ moduleItem = findMatchingModule(dependency);
+ }
+
+ if (productDep) {
+ const auto checkResult = checkProductDependency(dependency, *productDep);
+
+ // We found a product dependency, but that product has not been handled yet,
+ // so stop dependency resolving for the current product and resume it later, when the
+ // dependency is ready.
+ if (checkResult.second == HandleDependency::Defer)
+ return {nullptr, productDep, HandleDependency::Defer};
+
+ if (checkResult.first) {
+ QBS_CHECK(productDep->mergedExportItem);
+ moduleItem = productDep->mergedExportItem->clone(m_loaderState.itemPool());
+ moduleItem->setParent(nullptr);
+
+ // Needed for isolated Export item evaluation.
+ moduleItem->setPrototype(productDep->mergedExportItem);
+ } else {
+ // The product dependency is faulty, but Depends.reqired was false.
+ productDep = nullptr;
+ }
+ }
+
+ if (moduleItem)
+ checkModule(dependency, moduleItem, productDep);
+
+ // Can only happen with multiplexing.
+ if (moduleWithSameName && moduleWithSameName != moduleItem)
+ QBS_CHECK(productDep);
+
+ // The loading name is only used to ensure consistent sorting in case of equal
+ // value priorities; see ModulePropertyMerger.
+ QString loadingName;
+ if (loadingItem == m_product.item) {
+ loadingName = m_product.name;
+ } else if (m_product.dependenciesContext && !stateStack().empty()) {
+ const auto &loadingItemOrigin = stateStack().front().loadingItemOrigin;
+ loadingName = loadingItemOrigin.name.toString() + loadingItemOrigin.multiplexId
+ + loadingItemOrigin.profile;
+ }
+ instantiateModule({m_product, loadingItem, loadingName, moduleItem, moduleWithSameName,
+ productDep ? productDep->item : nullptr, dependency.name, dependency.id(),
+ bool(existingModule)}, m_loaderState);
+
+ // At this point, a null module item is only possible for a non-required dependency.
+ // Note that we still needed to to the instantiation above, as that injects the module
+ // name into the surrounding item for the ".present" check.
+ if (!moduleItem) {
+ QBS_CHECK(!dependency.requiredGlobally);
+ return {nullptr, nullptr, HandleDependency::Ignore};
+ }
+
+ const auto addLocalModule = [&] {
+ if (loadingItem->type() == ItemType::ModuleInstance
+ && !findExistingModule(dependency, loadingItem).first) {
+ loadingItem->addModule(createModule(dependency, moduleItem, productDep));
+ }
+ };
+
+ // The module has already been loaded, so we don't need to add it to the product's list of
+ // modules, nor do we need to handle its dependencies. The only thing we might need to
+ // do is to add it to the "local" list of modules of the loading item, if it isn't in there yet.
+ if (existingModule) {
+ addLocalModule();
+ return {nullptr, nullptr, HandleDependency::Ignore};
+ }
+
+ qCDebug(lcModuleLoader) << "module loaded:" << dependency.name.toString();
+ if (m_product.item) {
+ Item::Module module = createModule(dependency, moduleItem, productDep);
+ module.required = dependency.requiredGlobally;
+ addLoadContext(module);
+ module.maxDependsChainLength = dependsChainLength();
+ m_product.item->addModule(module);
+ addLocalModule();
+ }
+ return {moduleItem, nullptr, HandleDependency::Use};
+}
+
+std::pair<Item::Module *, Item *> DependenciesResolver::findExistingModule(
+ const FullyResolvedDependsItem &dependency, Item *item)
+{
+ if (!item) // Happens if and only if called via loadBaseModule().
+ return {};
+ Item *moduleWithSameName = nullptr;
+ for (Item::Module &m : item->modules()) {
+ if (m.name != dependency.name)
+ continue;
+ if (!m.product) {
+ QBS_CHECK(!dependency.product);
+ return {&m, m.item};
+ }
+ if ((dependency.profile.isEmpty() || (m.product->profileName == dependency.profile))
+ && m.product->multiplexConfigurationId == dependency.multiplexId) {
+ return {&m, m.item};
+ }
+
+ // This can happen if there are dependencies to several variants of a multiplexed
+ // product.
+ moduleWithSameName = m.item;
+ }
+ return {nullptr, moduleWithSameName};
+}
+
+void DependenciesResolver::updateModule(
+ Item::Module &module, const FullyResolvedDependsItem &dependency)
+{
+ forwardParameterDeclarations(dependency.item, m_product.item->modules());
+ module.versionRange.narrowDown(dependency.versionRange);
+ module.required |= dependency.requiredGlobally;
+ if (dependsChainLength() > module.maxDependsChainLength)
+ module.maxDependsChainLength = dependsChainLength();
+}
+
+int DependenciesResolver::dependsChainLength()
+{
+ return m_product.dependenciesContext ? stateStack().size() : 1;
+}
+
+ProductContext *DependenciesResolver::findMatchingProduct(
+ const FullyResolvedDependsItem &dependency)
+{
+ const auto constraint = [this, &dependency](ProductContext &product) {
+ if (product.multiplexConfigurationId != dependency.multiplexId)
+ return false;
+ if (!dependency.profile.isEmpty() && dependency.profile != product.profileName)
+ return false;
+ if (dependency.limitToSubProject && !haveSameSubProject(m_product, product))
+ return false;
+ return true;
+
+ };
+ return m_product.project->topLevelProject->productWithNameAndConstraint(
+ dependency.name.toString(), constraint);
+}
+
+Item *DependenciesResolver::findMatchingModule(
+ const FullyResolvedDependsItem &dependency)
+{
+ // If we can tell that this is supposed to be a product dependency, we can skip
+ // the module look-up.
+ if (!dependency.profile.isEmpty() || !dependency.multiplexId.isEmpty()) {
+ if (dependency.requiredGlobally) {
+ if (!dependency.profile.isEmpty()) {
+ throw ErrorInfo(Tr::tr("Product '%1' depends on '%2', which does not exist "
+ "for the requested profile '%3'.")
+ .arg(m_product.displayName(), dependency.displayName(),
+ dependency.profile),
+ m_product.item->location());
+ }
+ throw ErrorInfo(Tr::tr("Product '%1' depends on '%2', which does not exist.")
+ .arg(m_product.displayName(), dependency.displayName()),
+ m_product.item->location());
+ }
+ return nullptr;
+ }
+
+ if (Item *moduleItem = searchAndLoadModuleFile(
+ m_loaderState, m_product, dependency.location(), dependency.name)) {
+ QBS_CHECK(moduleItem->type() == ItemType::Module);
+ Item * const proto = moduleItem;
+ ModuleItemLocker locker(*moduleItem);
+ moduleItem = moduleItem->clone(m_loaderState.itemPool());
+ moduleItem->setPrototype(proto); // For parameter declarations.
+ return moduleItem;
+ }
+
+ if (!dependency.requiredGlobally) {
+ return createNonPresentModule(m_loaderState.itemPool(), dependency.name.toString(),
+ QStringLiteral("not found"), nullptr);
+ }
+
+ throw ErrorInfo(Tr::tr("Dependency '%1' not found for product '%2'.")
+ .arg(dependency.name.toString(), m_product.displayName()),
+ dependency.location());
+}
+
+std::pair<bool, HandleDependency> DependenciesResolver::checkProductDependency(
+ const FullyResolvedDependsItem &depSpec, const ProductContext &dep)
+{
+ // Optimization: If we already checked the product earlier and then deferred, we don't
+ // need to check it again.
+ if (!depSpec.checkProduct)
+ return {true, HandleDependency::Use};
+
+ if (&dep == &m_product) {
+ throw ErrorInfo(Tr::tr("Dependency '%1' refers to itself.").arg(depSpec.name.toString()),
+ depSpec.location());
+ }
+
+ if (m_product.project->topLevelProject->isProductQueuedForHandling(dep)) {
+ if (m_deferral == Deferral::Allowed)
+ return {false, HandleDependency::Defer};
+ ErrorInfo e;
+ e.append(Tr::tr("Cyclic dependencies detected:"));
+ e.append(Tr::tr("First product is '%1'.")
+ .arg(m_product.displayName()), m_product.item->location());
+ e.append(Tr::tr("Second product is '%1'.")
+ .arg(dep.displayName()), dep.item->location());
+ e.append(Tr::tr("Requested here."), depSpec.location());
+ throw e;
+ }
+
+ // This covers both the case of user-disabled products and products with errors.
+ // The latter are force-disabled in ProductContext::handleError().
+ if (m_product.project->topLevelProject->isDisabledItem(dep.item)) {
+ if (depSpec.requiredGlobally) {
+ ErrorInfo e;
+ e.append(Tr::tr("Product '%1' depends on '%2',")
+ .arg(m_product.displayName(), dep.displayName()),
+ m_product.item->location());
+ e.append(Tr::tr("but product '%1' is disabled.").arg(dep.displayName()),
+ dep.item->location());
+ throw e;
+ }
+ return {false, HandleDependency::Ignore};
+ }
+ return {true, HandleDependency::Use};
+}
+
+void DependenciesResolver::checkModule(
+ const FullyResolvedDependsItem &dependency, Item *moduleItem, ProductContext *productDep)
+{
+ // When loading a pseudo or temporary qbs module in early setup via loadBaseModule(),
+ // there is no proper state yet.
+ if (!m_product.dependenciesContext)
+ return;
+
+ for (auto it = stateStack().begin(); it != stateStack().end(); ++it) {
+ Item *itemToCheck = moduleItem;
+ if (it->loadingItem != itemToCheck) {
+ if (!productDep)
+ continue;
+ itemToCheck = productDep->item;
+ }
+ if (it->loadingItem != itemToCheck)
+ continue;
+ ErrorInfo e;
+ e.append(Tr::tr("Cyclic dependencies detected:"));
+ while (true) {
+ e.append(it->loadingItemOrigin.name.toString(),
+ it->loadingItemOrigin.location());
+ if (it->loadingItem->type() == ItemType::ModuleInstance) {
+ createNonPresentModule(m_loaderState.itemPool(),
+ it->loadingItemOrigin.name.toString(),
+ QLatin1String("cyclic dependency"), it->loadingItem);
+ }
+ if (it == stateStack().begin())
+ break;
+ --it;
+ }
+ e.append(dependency.name.toString(), dependency.location());
+ throw e;
+ }
+ checkForModuleNamePrefixCollision(dependency);
+}
+
+void DependenciesResolver::adjustDependsItemForMultiplexing(Item *dependsItem)
+{
+ if (m_product.name.startsWith(StringConstants::shadowProductPrefix()))
+ return;
+
+ Evaluator &evaluator = m_loaderState.evaluator();
+ const QString name = evaluator.stringValue(dependsItem, StringConstants::nameProperty());
+ const bool productIsMultiplexed = !m_product.multiplexConfigurationId.isEmpty();
+ if (name == m_product.name) {
+ QBS_CHECK(!productIsMultiplexed); // This product must be an aggregator.
+ return;
+ }
+
+ bool hasNonMultiplexedDependency = false;
+ const std::vector<ProductContext *> multiplexedDependencies = m_product.project
+ ->topLevelProject->productsWithNameAndConstraint(name, [&hasNonMultiplexedDependency]
+ (const ProductContext &product) {
+ if (product.multiplexConfigurationId.isEmpty()) {
+ hasNonMultiplexedDependency = true;
+ return false;
+ }
+ return true;
+ });
+ const bool hasMultiplexedDependencies = !multiplexedDependencies.empty();
+
+ // These are the allowed cases:
+ // (1) Normal dependency with no multiplexing whatsoever.
+ // (2) Both product and dependency are multiplexed.
+ // (2a) The profiles property is not set, we want to depend on the best
+ // matching variant.
+ // (2b) The profiles property is set, we want to depend on all variants
+ // with a matching profile.
+ // (3) The product is not multiplexed, but the dependency is.
+ // (3a) The profiles property is not set, the dependency has an aggregator.
+ // We want to depend on the aggregator.
+ // (3b) The profiles property is not set, the dependency does not have an
+ // aggregator. We want to depend on all the multiplexed variants.
+ // (3c) The profiles property is set, we want to depend on all variants
+ // with a matching profile regardless of whether an aggregator exists or not.
+ // (4) The product is multiplexed, but the dependency is not. We don't have to adapt
+ // any Depends items.
+ // (1) and (4)
+ if (!hasMultiplexedDependencies)
+ return;
+
+ bool profilesPropertyIsSet;
+ const QStringList profiles = evaluator.stringListValue(
+ dependsItem, StringConstants::profilesProperty(), &profilesPropertyIsSet);
+
+ // (3a)
+ if (!productIsMultiplexed && hasNonMultiplexedDependency && !profilesPropertyIsSet)
+ return;
+
+ static const auto multiplexConfigurationIntersects = [](const QVariantMap &lhs,
+ const QVariantMap &rhs) {
+ QBS_CHECK(!lhs.isEmpty() && !rhs.isEmpty());
+ for (auto lhsProperty = lhs.constBegin(); lhsProperty != lhs.constEnd(); lhsProperty++) {
+ const auto rhsProperty = rhs.find(lhsProperty.key());
+ const bool isCommonProperty = rhsProperty != rhs.constEnd();
+ if (isCommonProperty && !qVariantsEqual(lhsProperty.value(), rhsProperty.value()))
+ return false;
+ }
+ return true;
+ };
+
+ QStringList multiplexIds;
+ const auto productMultiplexConfig = m_loaderState.topLevelProject().multiplexConfiguration(
+ m_product.multiplexConfigurationId);
+
+ for (const ProductContext *dependency : multiplexedDependencies) {
+ if (productIsMultiplexed && !profilesPropertyIsSet) { // 2a
+ if (dependency->multiplexConfigurationId == m_product.multiplexConfigurationId) {
+ const ValuePtr &multiplexId = m_product.item->property(
+ StringConstants::multiplexConfigurationIdProperty());
+ dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
+ multiplexId);
+ return;
+
+ }
+ // Otherwise collect partial matches and decide later
+ const auto dependencyMultiplexConfig = m_loaderState.topLevelProject()
+ .multiplexConfiguration(dependency->multiplexConfigurationId);
+
+ if (multiplexConfigurationIntersects(dependencyMultiplexConfig, productMultiplexConfig))
+ multiplexIds << dependency->multiplexConfigurationId;
+ } else {
+ // (2b), (3b) or (3c)
+ const bool profileMatch = !profilesPropertyIsSet || profiles.empty()
+ || profiles.contains(dependency->profileName);
+ if (profileMatch)
+ multiplexIds << dependency->multiplexConfigurationId;
+ }
+ }
+ if (multiplexIds.empty()) {
+ const QString productName = m_product.displayName();
+ throw ErrorInfo(Tr::tr("Dependency from product '%1' to product '%2' not fulfilled. "
+ "There are no eligible multiplex candidates.").arg(productName,
+ name),
+ dependsItem->location());
+ }
+
+ // In case of (2a), at most 1 match is allowed
+ if (productIsMultiplexed && !profilesPropertyIsSet && multiplexIds.size() > 1) {
+ QStringList candidateNames;
+ for (const auto &id : std::as_const(multiplexIds))
+ candidateNames << fullProductDisplayName(name, id);
+ throw ErrorInfo(
+ Tr::tr("Dependency from product '%1' to product '%2' is ambiguous. "
+ "Eligible multiplex candidates: %3.").arg(
+ m_product.displayName(), name, candidateNames.join(QLatin1String(", "))),
+ dependsItem->location());
+ }
+
+ dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
+ VariantValue::create(multiplexIds));
+
+}
+
+std::optional<EvaluatedDependsItem> DependenciesResolver::evaluateDependsItem(Item *item)
+{
+ Evaluator &evaluator = m_loaderState.evaluator();
+ if (!m_product.project->topLevelProject->checkItemCondition(item, evaluator)) {
+ qCDebug(lcModuleLoader) << "Depends item disabled, ignoring.";
+ return {};
+ }
+
+ const QString name = evaluator.stringValue(item, StringConstants::nameProperty());
+ if (name == StringConstants::qbsModule()) // Redundant
+ return {};
+
+ bool submodulesPropertySet;
+ const QStringList submodules = evaluator.stringListValue(
+ item, StringConstants::submodulesProperty(), &submodulesPropertySet);
+ if (submodules.empty() && submodulesPropertySet) {
+ qCDebug(lcModuleLoader) << "Ignoring Depends item with empty submodules list.";
+ return {};
+ }
+ if (Q_UNLIKELY(submodules.size() > 1 && !item->id().isEmpty())) {
+ QString msg = Tr::tr("A Depends item with more than one module cannot have an id.");
+ throw ErrorInfo(msg, item->location());
+ }
+ bool productTypesWasSet;
+ const QStringList productTypes = evaluator.stringListValue(
+ item, StringConstants::productTypesProperty(), &productTypesWasSet);
+ if (!name.isEmpty() && !productTypes.isEmpty()) {
+ throw ErrorInfo(Tr::tr("The name and productTypes are mutually exclusive "
+ "in a Depends item."), item->location());
+ }
+ if (productTypes.isEmpty() && productTypesWasSet) {
+ qCDebug(lcModuleLoader) << "Ignoring Depends item with empty productTypes list.";
+ return {};
+ }
+ if (name.isEmpty() && !productTypesWasSet) {
+ throw ErrorInfo(Tr::tr("Either name or productTypes must be set in a Depends item"),
+ item->location());
+ }
+
+ bool profilesPropertyWasSet = false;
+ std::optional<QStringList> profiles;
+ bool required = true;
+ if (productTypes.isEmpty()) {
+ const QStringList profileList = evaluator.stringListValue(
+ item, StringConstants::profilesProperty(), &profilesPropertyWasSet);
+ if (profilesPropertyWasSet)
+ profiles.emplace(profileList);
+ required = evaluator.boolValue(item, StringConstants::requiredProperty());
+ }
+ const Version minVersion = Version::fromString(
+ evaluator.stringValue(item, StringConstants::versionAtLeastProperty()));
+ const Version maxVersion = Version::fromString(
+ evaluator.stringValue(item, StringConstants::versionBelowProperty()));
+ const bool limitToSubProject = evaluator.boolValue(
+ item, StringConstants::limitToSubProjectProperty());
+ const QStringList multiplexIds = evaluator.stringListValue(
+ item, StringConstants::multiplexConfigurationIdsProperty());
+ adjustParametersScopes(item, item);
+ forwardParameterDeclarations(item, m_product.item->modules());
+ const QVariantMap parameters = extractParameters(item);
+
+ const FileTags productTypeTags = FileTags::fromStringList(productTypes);
+ if (!productTypeTags.empty())
+ m_product.bulkDependencies.emplace_back(productTypeTags, item->location());
+ return EvaluatedDependsItem{
+ item,
+ QualifiedId::fromString(name),
+ submodules,
+ productTypeTags,
+ multiplexIds,
+ profiles,
+ {minVersion, maxVersion},
+ parameters,
+ limitToSubProject,
+ required};
+}
+
+// Potentially multiplexes a dependency along Depends.productTypes, Depends.subModules and
+// Depends.profiles, as well as internally set up multiplexing axes.
+// Each entry in the resulting queue corresponds to exactly one product or module to pull in.
+std::queue<FullyResolvedDependsItem>
+DependenciesResolver::multiplexDependency(const EvaluatedDependsItem &dependency)
+{
+ std::queue<FullyResolvedDependsItem> dependencies;
+ if (!dependency.productTypes.empty()) {
+ const auto constraint = [&](const ProductContext &product) {
+ return &product != &m_product && product.name != m_product.name
+ && (!dependency.limitToSubProject || haveSameSubProject(m_product, product));
+ };
+ const std::vector<ProductContext *> matchingProducts = m_product.project->topLevelProject
+ ->productsWithTypeAndConstraint(dependency.productTypes, constraint);
+ if (matchingProducts.empty()) {
+ qCDebug(lcModuleLoader) << "Depends.productTypes does not match anything."
+ << dependency.item->location();
+ return {};
+ }
+ for (ProductContext * const match : matchingProducts)
+ dependencies.emplace(match, dependency);
+ return dependencies;
+ }
+
+ const QStringList profiles = dependency.profiles && !dependency.profiles->isEmpty()
+ ? *dependency.profiles : QStringList(QString());
+ const QStringList multiplexIds = !dependency.multiplexIds.isEmpty()
+ ? dependency.multiplexIds : QStringList(QString());
+ const QStringList subModules = !dependency.subModules.isEmpty()
+ ? dependency.subModules : QStringList(QString());
+ for (const QString &profile : profiles) {
+ for (const QString &multiplexId : multiplexIds) {
+ for (const QString &subModule : subModules) {
+ QualifiedId name = dependency.name;
+ if (!subModule.isEmpty())
+ name << subModule.split(QLatin1Char('.'));
+ dependencies.emplace(dependency, name, profile, multiplexId);
+ }
+ }
+ }
+ return dependencies;
+}
+
+QVariantMap DependenciesResolver::extractParameters(Item *dependsItem) const
+{
+ try {
+ QVariantMap result;
+ const auto &properties = dependsItem->properties();
+ Evaluator &evaluator = m_loaderState.evaluator();
+ for (auto it = properties.begin(); it != properties.end(); ++it) {
+ if (it.value()->type() != Value::ItemValueType)
+ continue;
+ const JSValue sv = evaluator.scriptValue(
+ std::static_pointer_cast<ItemValue>(it.value())->item());
+ result.insert(it.key(), safeToVariant(evaluator.engine()->context(), sv));
+ }
+ return result;
+ } catch (const ErrorInfo &exception) {
+ auto ei = exception;
+ ei.prepend(Tr::tr("Error in dependency parameter."), dependsItem->location());
+ throw ei;
+ }
+}
+
+void DependenciesResolver::forwardParameterDeclarations(const Item *dependsItem,
+ const Item::Modules &modules)
+{
+ for (auto it = dependsItem->properties().begin(); it != dependsItem->properties().end(); ++it) {
+ if (it.value()->type() != Value::ItemValueType)
+ continue;
+ forwardParameterDeclarations(it.key(),
+ std::static_pointer_cast<ItemValue>(it.value())->item(),
+ modules);
+ }
+}
+
+void DependenciesResolver::forwardParameterDeclarations(
+ const QualifiedId &moduleName, Item *item, const Item::Modules &modules)
+{
+ auto it = std::find_if(modules.begin(), modules.end(), [&moduleName] (const Item::Module &m) {
+ return m.name == moduleName;
+ });
+ if (it != modules.end()) {
+ item->setPropertyDeclarations(m_loaderState.topLevelProject().parameterDeclarations(
+ it->item->rootPrototype()));
+ } else {
+ for (auto it = item->properties().begin(); it != item->properties().end(); ++it) {
+ if (it.value()->type() != Value::ItemValueType)
+ continue;
+ forwardParameterDeclarations(QualifiedId(moduleName) << it.key(),
+ std::static_pointer_cast<ItemValue>(it.value())->item(),
+ modules);
+ }
+ }
+}
+
+std::list<DependenciesResolvingState> &DependenciesResolver::stateStack()
+{
+ QBS_CHECK(m_product.dependenciesContext);
+ return static_cast<DependenciesContextImpl *>(m_product.dependenciesContext.get())->stateStack;
+}
+
+void DependenciesResolver::checkForModuleNamePrefixCollision(
+ const FullyResolvedDependsItem &dependency)
+{
+ if (!m_product.item)
+ return;
+
+ for (const Item::Module &m : m_product.item->modules()) {
+ if (m.name.length() == dependency.name.length())
+ continue;
+
+ QualifiedId shortName;
+ QualifiedId longName;
+ if (m.name.length() < dependency.name.length()) {
+ shortName = m.name;
+ longName = dependency.name;
+ } else {
+ shortName = dependency.name;
+ longName = m.name;
+ }
+ const auto isPrefix = [&] {
+ for (int i = 0; i < shortName.length(); ++i) {
+ if (shortName.at(i) != longName.at(i))
+ return false;
+ }
+ return true;
+ };
+ if (!isPrefix())
+ continue;
+
+ throw ErrorInfo(Tr::tr("The name of module '%1' is a prefix of the name of module '%2', "
+ "which is not allowed")
+ .arg(shortName.toString(), longName.toString()), dependency.location());
+ }
+}
+
+Item::Module DependenciesResolver::createModule(
+ const FullyResolvedDependsItem &dependency, Item *item, ProductContext *productDep)
+{
+ Item::Module m;
+ m.item = item;
+ m.product = productDep;
+ m.name = dependency.name;
+ m.required = dependency.requiredLocally;
+ m.versionRange = dependency.versionRange;
+ return m;
+}
+
+FullyResolvedDependsItem::FullyResolvedDependsItem(
+ ProductContext *product, const EvaluatedDependsItem &dependency)
+ : product(product)
+ , item(dependency.item)
+ , name(product->name)
+ , versionRange(dependency.versionRange)
+ , parameters(dependency.parameters)
+ , checkProduct(false)
+{}
+
+FullyResolvedDependsItem FullyResolvedDependsItem::makeBaseDependency()
+{
+ FullyResolvedDependsItem item;
+ item.name = StringConstants::qbsModule();
+ return item;
+}
+
+FullyResolvedDependsItem::FullyResolvedDependsItem(
+ const EvaluatedDependsItem &dependency, QualifiedId name, QString profile, QString multiplexId)
+ : item(dependency.item)
+ , name(std::move(name))
+ , profile(std::move(profile))
+ , multiplexId(std::move(multiplexId))
+ , versionRange(dependency.versionRange)
+ , parameters(dependency.parameters)
+ , limitToSubProject(dependency.limitToSubProject)
+ , requiredLocally(dependency.requiredLocally)
+ , requiredGlobally(dependency.requiredGlobally)
+{}
+
+QString FullyResolvedDependsItem::id() const
+{
+ if (!item) {
+ QBS_CHECK(name.toString() == StringConstants::qbsModule());
+ return {};
+ }
+ return item->id();
+}
+
+CodeLocation FullyResolvedDependsItem::location() const
+{
+ if (!item) {
+ QBS_CHECK(name.toString() == StringConstants::qbsModule());
+ return {};
+ }
+ return item->location();
+}
+
+QString FullyResolvedDependsItem::displayName() const
+{
+ return fullProductDisplayName(name.toString(), multiplexId);
+}
+
+bool haveSameSubProject(const ProductContext &p1, const ProductContext &p2)
+{
+ for (const Item *otherParent = p2.item->parent(); otherParent;
+ otherParent = otherParent->parent()) {
+ if (otherParent == p1.item->parent())
+ return true;
+ }
+ return false;
+}
+
+QVariantMap safeToVariant(JSContext *ctx, const JSValue &v)
+{
+ QVariantMap result;
+ handleJsProperties(ctx, v, [&](const JSAtom &prop, const JSPropertyDescriptor &desc) {
+ const JSValue u = desc.value;
+ if (JS_IsError(ctx, u))
+ throw ErrorInfo(getJsString(ctx, u));
+ const QString name = getJsString(ctx, prop);
+ result[name] = (JS_IsObject(u) && !JS_IsArray(ctx, u) && !JS_IsRegExp(ctx, u))
+ ? safeToVariant(ctx, u) : getJsVariant(ctx, u);
+ });
+ return result;
+}
+
+DependenciesContextImpl::DependenciesContextImpl(ProductContext &product, LoaderState &loaderState)
+ : m_product(product)
+{
+ setSearchPathsForProduct(loaderState);
+
+ // Initialize the state with the direct Depends items of the product item.
+ DependenciesResolvingState newState{product.item,};
+ for (Item * const child : product.item->children()) {
+ if (child->type() == ItemType::Depends)
+ newState.pendingDependsItems.push(child);
+ }
+ stateStack.push_front(std::move(newState));
+ stateStack.front().pendingResolvedDependencies.push(
+ FullyResolvedDependsItem::makeBaseDependency());
+}
+
+std::pair<ProductDependency, ProductContext *> DependenciesContextImpl::pendingDependency() const
+{
+ QBS_CHECK(!stateStack.empty());
+ if (const auto &currentDependsItem = stateStack.front().currentDependsItem;
+ currentDependsItem && !currentDependsItem->productTypes.empty()) {
+ qCDebug(lcLoaderScheduling) << "product" << m_product.displayName()
+ << "to be delayed because of bulk dependency";
+ return {ProductDependency::Bulk, nullptr};
+ }
+ if (!stateStack.front().pendingResolvedDependencies.empty()) {
+ if (ProductContext * const dep = stateStack.front().pendingResolvedDependencies
+ .front().product) {
+ if (m_product.project->topLevelProject->isProductQueuedForHandling(*dep)) {
+ qCDebug(lcLoaderScheduling) << "product" << m_product.displayName()
+ << "to be delayed because of dependency "
+ "to unfinished product" << dep->displayName();
+ return {ProductDependency::Single, dep};
+ } else {
+ qCDebug(lcLoaderScheduling) << "product" << m_product.displayName()
+ << "to be re-scheduled, as dependency "
+ << dep->displayName()
+ << "appears to have finished in the meantime";
+ return {ProductDependency::None, dep};
+ }
+ }
+ }
+ return {ProductDependency::None, nullptr};
+}
+
+void DependenciesContextImpl::setSearchPathsForProduct(LoaderState &loaderState)
+{
+ QBS_CHECK(m_product.searchPaths.isEmpty());
+
+ m_product.searchPaths = loaderState.itemReader().readExtraSearchPaths(m_product.item);
+ Settings settings(loaderState.parameters().settingsDirectory());
+ const QStringList prefsSearchPaths = Preferences(&settings, m_product.profileModuleProperties)
+ .searchPaths();
+ const QStringList &currentSearchPaths = loaderState.itemReader().allSearchPaths();
+ for (const QString &p : prefsSearchPaths) {
+ if (!currentSearchPaths.contains(p) && FileInfo(p).exists())
+ m_product.searchPaths << p;
+ }
+}
+
+} // namespace
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/dependenciesresolver.h b/src/lib/corelib/loader/dependenciesresolver.h
new file mode 100644
index 000000000..48502adb7
--- /dev/null
+++ b/src/lib/corelib/loader/dependenciesresolver.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+namespace qbs::Internal {
+class Item;
+class LoaderState;
+class ProductContext;
+enum class Deferral;
+
+// Collects the products' dependencies and builds the list of modules from them.
+// Actual loading of module files is offloaded to ModuleLoader.
+void resolveDependencies(ProductContext &product, Deferral deferral, LoaderState &loaderState);
+
+// Note: This function is never called for regular loading of the base module into a product,
+// but only for the special cases of loading the dummy base module into a project
+// and temporarily providing a base module for product multiplexing.
+Item *loadBaseModule(ProductContext &product, Item *item, LoaderState &loaderState);
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/groupshandler.cpp b/src/lib/corelib/loader/groupshandler.cpp
new file mode 100644
index 000000000..3dbfad352
--- /dev/null
+++ b/src/lib/corelib/loader/groupshandler.cpp
@@ -0,0 +1,300 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "groupshandler.h"
+
+#include "loaderutils.h"
+#include "moduleinstantiator.h"
+
+#include <language/evaluator.h>
+#include <language/item.h>
+#include <language/value.h>
+#include <logging/translator.h>
+#include <tools/profiling.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+
+namespace qbs::Internal {
+class GroupsHandler
+{
+public:
+ GroupsHandler(ProductContext &product, LoaderState &loaderState)
+ : m_product(product), m_loaderState(loaderState) {}
+
+ void run();
+
+private:
+ void gatherAssignedProperties(ItemValue *iv, const QualifiedId &prefix,
+ QualifiedIdSet &properties);
+ void markModuleTargetGroups(Item *group, const Item::Module &module);
+ void moveGroupsFromModuleToProduct(const Item::Module &module);
+ void moveGroupsFromModulesToProduct();
+ void propagateModulesFromParent(Item *group);
+ void handleGroup(Item *group);
+ void adjustScopesInGroupModuleInstances(Item *groupItem, const Item::Module &module);
+ QualifiedIdSet gatherModulePropertiesSetInGroup(const Item *group);
+
+ ProductContext &m_product;
+ LoaderState &m_loaderState;
+};
+
+void setupGroups(ProductContext &product, LoaderState &loaderState)
+{
+ GroupsHandler(product, loaderState).run();
+}
+
+void GroupsHandler::run()
+{
+ AccumulatingTimer timer(m_loaderState.parameters().logElapsedTime()
+ ? &m_product.timingData.groupsSetup : nullptr);
+
+ moveGroupsFromModulesToProduct();
+ for (Item * const child : m_product.item->children()) {
+ if (child->type() == ItemType::Group)
+ handleGroup(child);
+ }
+}
+
+void GroupsHandler::gatherAssignedProperties(ItemValue *iv, const QualifiedId &prefix,
+ QualifiedIdSet &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:
+ if (iv->item()->type() == ItemType::ModulePrefix) {
+ gatherAssignedProperties(std::static_pointer_cast<ItemValue>(it.value()).get(),
+ QualifiedId(prefix) << it.key(), properties);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void GroupsHandler::markModuleTargetGroups(Item *group, const Item::Module &module)
+{
+ QBS_CHECK(group->type() == ItemType::Group);
+ if (m_loaderState.evaluator().boolValue(group, StringConstants::filesAreTargetsProperty())) {
+ group->setProperty(StringConstants::modulePropertyInternal(),
+ VariantValue::create(module.name.toString()));
+ }
+ for (Item * const child : group->children())
+ markModuleTargetGroups(child, module);
+}
+
+void GroupsHandler::moveGroupsFromModuleToProduct(const Item::Module &module)
+{
+ if (!module.item->isPresentModule())
+ return;
+ for (auto it = module.item->children().begin(); it != module.item->children().end();) {
+ Item * const child = *it;
+ if (child->type() != ItemType::Group) {
+ ++it;
+ continue;
+ }
+
+ Item * const scope = Item::create(&m_loaderState.itemPool(), ItemType::Scope);
+ scope->setProperties(module.item->properties());
+ scope->setScope(m_product.scope);
+ child->setScope(scope);
+ setScopeForDescendants(child, scope);
+
+ Item::addChild(m_product.item, child);
+ markModuleTargetGroups(child, module);
+ it = module.item->children().erase(it);
+ }
+}
+
+void GroupsHandler::moveGroupsFromModulesToProduct()
+{
+ for (const Item::Module &module : m_product.item->modules())
+ moveGroupsFromModuleToProduct(module);
+}
+
+// TODO: I don't completely understand this function, and I suspect we do both too much
+// and too little here. In particular, I'm not sure why we should even have to do anything
+// with groups that don't attach properties.
+// Set aside a day or two at some point to fully grasp all the details and rewrite accordingly.
+void GroupsHandler::propagateModulesFromParent(Item *group)
+{
+ QBS_CHECK(group->type() == ItemType::Group);
+ QHash<QualifiedId, Item *> moduleInstancesForGroup;
+
+ // Step 1: "Instantiate" the product's modules for the group.
+ for (Item::Module m : group->parent()->modules()) {
+ Item * const targetItem = retrieveModuleInstanceItem(group, m.name, m_loaderState);
+ QBS_CHECK(targetItem->type() == ItemType::ModuleInstancePlaceholder);
+ targetItem->setPrototype(m.item);
+
+ Item * const moduleScope = Item::create(&m_loaderState.itemPool(), ItemType::Scope);
+ moduleScope->setFile(group->file());
+ moduleScope->setProperties(m.item->scope()->properties()); // "project", "product", ids
+ moduleScope->setScope(group);
+ targetItem->setScope(moduleScope);
+
+ targetItem->setFile(m.item->file());
+
+ // "parent" should point to the group/artifact parent
+ targetItem->setParent(group->parent());
+
+ targetItem->setOuterItem(m.item);
+
+ m.item = targetItem;
+ group->addModule(m);
+ moduleInstancesForGroup.insert(m.name, targetItem);
+ }
+
+ // Step 2: Make the inter-module references point to the instances created in step 1.
+ for (const Item::Module &module : group->modules()) {
+ Item::Modules adaptedModules;
+ const Item::Modules &oldModules = module.item->prototype()->modules();
+ for (Item::Module depMod : oldModules) {
+ depMod.item = moduleInstancesForGroup.value(depMod.name);
+ adaptedModules << depMod;
+ if (depMod.name.front() == module.name.front())
+ continue;
+ const ItemValuePtr &modulePrefix = group->itemProperty(depMod.name.front(),
+ m_loaderState.itemPool());
+ QBS_CHECK(modulePrefix);
+ module.item->setProperty(depMod.name.front(), modulePrefix);
+ }
+ module.item->setModules(adaptedModules);
+ }
+
+ const QualifiedIdSet &propsSetInGroup = gatherModulePropertiesSetInGroup(group);
+ if (propsSetInGroup.empty())
+ return;
+ m_product.modulePropertiesSetInGroups.insert(std::make_pair(group, propsSetInGroup));
+
+ // Step 3: Adapt scopes in values. This is potentially necessary if module properties
+ // get assigned on the group level.
+ for (const Item::Module &module : group->modules())
+ adjustScopesInGroupModuleInstances(group, module);
+}
+
+void GroupsHandler::handleGroup(Item *group)
+{
+ propagateModulesFromParent(group);
+ m_loaderState.topLevelProject().checkItemCondition(group, m_loaderState.evaluator());
+ for (Item * const child : group->children()) {
+ if (child->type() == ItemType::Group)
+ handleGroup(child);
+ }
+}
+
+void GroupsHandler::adjustScopesInGroupModuleInstances(Item *groupItem,
+ const Item::Module &module)
+{
+ if (!module.item->isPresentModule())
+ return;
+
+ Item *modulePrototype = module.item->rootPrototype();
+ QBS_CHECK(modulePrototype->type() == ItemType::Module
+ || modulePrototype->type() == ItemType::Export);
+
+ const Item::PropertyDeclarationMap &propDecls = modulePrototype->propertyDeclarations();
+ for (const auto &decl : propDecls) {
+ const QString &propName = decl.name();
+
+ // Nothing gets inherited for module properties assigned directly in the group.
+ // 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()).
+ ValuePtr propValue = module.item->ownProperty(propName);
+ if (propValue) {
+ propValue->setScope(module.item->scope(), {});
+ 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;
+ do {
+ instanceWithProperty = instanceWithProperty->prototype();
+ QBS_CHECK(instanceWithProperty);
+ propValue = instanceWithProperty->ownProperty(propName);
+ } while (!propValue);
+ QBS_CHECK(propValue);
+
+ if (propValue->type() != Value::JSSourceValueType)
+ continue;
+
+ if (propValue->scope())
+ module.item->setProperty(propName, propValue->clone(m_loaderState.itemPool()));
+ }
+
+ for (const ValuePtr &prop : module.item->properties()) {
+ if (prop->type() != Value::JSSourceValueType) {
+ QBS_CHECK(!prop->next());
+ continue;
+ }
+ for (ValuePtr v = prop; v; v = v->next()) {
+ if (!v->scope())
+ continue;
+ for (const Item::Module &module : groupItem->modules()) {
+ if (v->scope() == module.item->prototype()) {
+ v->setScope(module.item, {});
+ break;
+ }
+ }
+ }
+ }
+}
+
+QualifiedIdSet GroupsHandler::gatherModulePropertiesSetInGroup(const Item *group)
+{
+ QualifiedIdSet propsSetInGroup;
+ const Item::PropertyMap &props = group->properties();
+ for (auto it = props.cbegin(); it != props.cend(); ++it) {
+ if (it.value()->type() == Value::ItemValueType) {
+ gatherAssignedProperties(std::static_pointer_cast<ItemValue>(it.value()).get(),
+ QualifiedId(it.key()), propsSetInGroup);
+ }
+ }
+ return propsSetInGroup;
+}
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/groupshandler.h b/src/lib/corelib/loader/groupshandler.h
new file mode 100644
index 000000000..5de94ce7c
--- /dev/null
+++ b/src/lib/corelib/loader/groupshandler.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+namespace qbs::Internal {
+class LoaderState;
+class ProductContext;
+
+// Sets up Group items for the actual resolving stage. Responsibilities:
+// - Moving Group items located in modules over to the product.
+// - Identifying groups declaring target artifacts and marking them accordingly.
+// - Setting up group-level module instances to ensure proper resolving of per-group module
+// properties.
+// - As a side effect of the above point, collecting all properties set on the Group level
+// to help the ProjectResolver decide which properties need to be re-evaluated at all,
+// which is an important optimization (see commit 9cd8653eef).
+void setupGroups(ProductContext &product, LoaderState &loaderState);
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/itemreader.cpp b/src/lib/corelib/loader/itemreader.cpp
new file mode 100644
index 000000000..0638d1af5
--- /dev/null
+++ b/src/lib/corelib/loader/itemreader.cpp
@@ -0,0 +1,274 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "loaderutils.h"
+
+#include <language/deprecationinfo.h>
+#include <language/evaluator.h>
+#include <language/filecontext.h>
+#include <language/item.h>
+#include <language/value.h>
+#include <logging/categories.h>
+#include <tools/fileinfo.h>
+#include <tools/profiling.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+#include <tools/stlutils.h>
+
+#include <QtCore/qfileinfo.h>
+
+#include <algorithm>
+
+namespace qbs {
+namespace Internal {
+
+static void makePathsCanonical(QStringList &paths)
+{
+ Internal::removeIf(paths, [](QString &p) {
+ p = QFileInfo(p).canonicalFilePath();
+ return p.isEmpty();
+ });
+}
+
+ItemReader::ItemReader(LoaderState &loaderState) : m_loaderState(loaderState) {}
+
+void ItemReader::init()
+{
+ m_visitorState = std::make_unique<ItemReaderVisitorState>(
+ m_loaderState.topLevelProject().itemReaderCache(), m_loaderState.logger());
+ m_visitorState->setDeprecationWarningMode(m_loaderState.parameters().deprecationWarningMode());
+ m_projectFilePath = m_loaderState.parameters().projectFilePath();
+ setSearchPaths(m_loaderState.parameters().searchPaths());
+ m_elapsedTime = m_loaderState.parameters().logElapsedTime() ? 0 : -1;
+}
+
+ItemReader::~ItemReader() = default;
+
+void ItemReader::setSearchPaths(const QStringList &searchPaths)
+{
+ qCDebug(lcModuleLoader) << "initial search paths:" << searchPaths;
+ m_searchPaths = searchPaths;
+ makePathsCanonical(m_searchPaths);
+ m_allSearchPaths.clear();
+}
+
+void ItemReader::pushExtraSearchPaths(const QStringList &extraSearchPaths)
+{
+ m_extraSearchPaths.push_back(extraSearchPaths);
+ makePathsCanonical(m_extraSearchPaths.back());
+ m_allSearchPaths.clear();
+}
+
+void ItemReader::popExtraSearchPaths()
+{
+ m_extraSearchPaths.pop_back();
+ m_allSearchPaths.clear();
+}
+
+const std::vector<QStringList> &ItemReader::extraSearchPathsStack() const
+{
+ return m_extraSearchPaths;
+}
+
+void ItemReader::setExtraSearchPathsStack(const std::vector<QStringList> &s)
+{
+ m_extraSearchPaths = s;
+ m_allSearchPaths.clear();
+}
+
+void ItemReader::clearExtraSearchPathsStack()
+{
+ m_extraSearchPaths.clear();
+ m_allSearchPaths.clear();
+}
+
+const QStringList &ItemReader::allSearchPaths() const
+{
+ if (m_allSearchPaths.empty()) {
+ std::for_each(m_extraSearchPaths.crbegin(), m_extraSearchPaths.crend(),
+ [this] (const QStringList &paths) {
+ m_allSearchPaths += paths;
+ });
+ m_allSearchPaths += m_searchPaths;
+ m_allSearchPaths.removeDuplicates();
+ }
+ return m_allSearchPaths;
+}
+
+Item *ItemReader::readFile(const QString &filePath)
+{
+ AccumulatingTimer readFileTimer(m_elapsedTime != -1 ? &m_elapsedTime : nullptr);
+ return m_visitorState->readFile(filePath, allSearchPaths(), &m_loaderState.itemPool());
+}
+
+Item *ItemReader::readFile(const QString &filePath, const CodeLocation &referencingLocation)
+{
+ try {
+ return readFile(filePath);
+ } catch (const ErrorInfo &e) {
+ if (e.hasLocation())
+ throw;
+ throw ErrorInfo(e.toString(), referencingLocation);
+ }
+}
+
+void ItemReader::handlePropertyOptions(Item *optionsItem)
+{
+ Evaluator &evaluator = m_loaderState.evaluator();
+ const QString name = evaluator.stringValue(optionsItem, StringConstants::nameProperty());
+ if (name.isEmpty()) {
+ throw ErrorInfo(Tr::tr("PropertyOptions item needs a name property"),
+ optionsItem->location());
+ }
+ const QString description = evaluator.stringValue(
+ optionsItem, StringConstants::descriptionProperty());
+ const auto removalVersion = Version::fromString(
+ evaluator.stringValue(optionsItem, StringConstants::removalVersionProperty()));
+ const auto allowedValues = evaluator.stringListValue(
+ optionsItem, StringConstants::allowedValuesProperty());
+
+ 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);
+ }
+ decl.setAllowedValues(allowedValues);
+ const ValuePtr property = optionsItem->parent()->property(name);
+ if (!property && !decl.isExpired()) {
+ throw ErrorInfo(Tr::tr("PropertyOptions item refers to non-existing property '%1'")
+ .arg(name), optionsItem->location());
+ }
+ if (property && decl.isExpired()) {
+ ErrorInfo e(Tr::tr("Property '%1' was scheduled for removal in version %2, but "
+ "is still present.")
+ .arg(name, removalVersion.toString()),
+ property->location());
+ e.append(Tr::tr("Removal version for '%1' specified here.").arg(name),
+ optionsItem->location());
+ m_visitorState->logger().printWarning(e);
+ }
+ optionsItem->parent()->setPropertyDeclaration(name, decl);
+}
+
+void ItemReader::handleAllPropertyOptionsItems(Item *item)
+{
+ QList<Item *> childItems = item->children();
+ auto childIt = childItems.begin();
+ while (childIt != childItems.end()) {
+ Item * const child = *childIt;
+ if (child->type() == ItemType::PropertyOptions) {
+ handlePropertyOptions(child);
+ childIt = childItems.erase(childIt);
+ } else {
+ handleAllPropertyOptionsItems(child);
+ ++childIt;
+ }
+ }
+ item->setChildren(childItems);
+}
+
+Item *ItemReader::setupItemFromFile(const QString &filePath, const CodeLocation &referencingLocation)
+{
+ Item *item = readFile(filePath, referencingLocation);
+
+ // This is technically not needed, because files are only set up once and then served
+ // from a cache. But it simplifies the checks in item.cpp if we require the locking invariant
+ // to always hold.
+ std::unique_ptr<ModuleItemLocker> locker;
+ if (item->type() == ItemType::Module)
+ locker = std::make_unique<ModuleItemLocker>(*item);
+
+ handleAllPropertyOptionsItems(item);
+ return item;
+}
+
+Item *ItemReader::wrapInProjectIfNecessary(Item *item)
+{
+ if (item->type() == ItemType::Project)
+ return item;
+ Item *prj = Item::create(&m_loaderState.itemPool(), ItemType::Project);
+ Item::addChild(prj, item);
+ prj->setFile(item->file());
+ prj->setLocation(item->location());
+ prj->setupForBuiltinType(m_loaderState.parameters().deprecationWarningMode(),
+ m_visitorState->logger());
+ return prj;
+}
+
+QStringList ItemReader::readExtraSearchPaths(Item *item, bool *wasSet)
+{
+ QStringList result;
+ const QStringList paths = m_loaderState.evaluator().stringListValue(
+ item, StringConstants::qbsSearchPathsProperty(), wasSet);
+ const JSSourceValueConstPtr prop = item->sourceProperty(
+ StringConstants::qbsSearchPathsProperty());
+
+ // 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_projectFilePath);
+ for (const QString &path : paths)
+ result += FileInfo::resolvePath(basePath, path);
+ return result;
+}
+
+SearchPathsManager::SearchPathsManager(ItemReader &itemReader, const QStringList &extraSearchPaths)
+ : m_itemReader(itemReader),
+ m_oldSize(itemReader.extraSearchPathsStack().size())
+{
+ if (!extraSearchPaths.isEmpty())
+ m_itemReader.pushExtraSearchPaths(extraSearchPaths);
+}
+
+SearchPathsManager::~SearchPathsManager()
+{
+ while (m_itemReader.extraSearchPathsStack().size() > m_oldSize)
+ m_itemReader.popExtraSearchPaths();
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/loader/itemreader.h b/src/lib/corelib/loader/itemreader.h
new file mode 100644
index 000000000..e444db16c
--- /dev/null
+++ b/src/lib/corelib/loader/itemreader.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_ITEMREADER_H
+#define QBS_ITEMREADER_H
+
+#include <logging/logger.h>
+#include <tools/deprecationwarningmode.h>
+#include <tools/set.h>
+
+#include <QtCore/qstringlist.h>
+
+#include <memory>
+
+namespace qbs {
+class SetupProjectParameters;
+namespace Internal {
+class Evaluator;
+class Item;
+class ItemPool;
+class ItemReaderVisitorState;
+class LoaderState;
+
+/*
+ * 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(LoaderState &loaderState);
+ ~ItemReader();
+ void init();
+
+ void pushExtraSearchPaths(const QStringList &extraSearchPaths);
+ void popExtraSearchPaths();
+ const std::vector<QStringList> &extraSearchPathsStack() const;
+ void setExtraSearchPathsStack(const std::vector<QStringList> &s);
+ void clearExtraSearchPathsStack();
+ const QStringList &allSearchPaths() const;
+
+ // Parses a file, creates an item for it, generates PropertyDeclarations from
+ // PropertyOptions items and removes said items from the item tree.
+ Item *setupItemFromFile(const QString &filePath, const CodeLocation &referencingLocation);
+
+ Item *wrapInProjectIfNecessary(Item *item);
+ QStringList readExtraSearchPaths(Item *item, bool *wasSet = nullptr);
+
+ qint64 elapsedTime() const { return m_elapsedTime; }
+
+private:
+ void setSearchPaths(const QStringList &searchPaths);
+ Item *readFile(const QString &filePath);
+ Item *readFile(const QString &filePath, const CodeLocation &referencingLocation);
+ void handlePropertyOptions(Item *optionsItem);
+ void handleAllPropertyOptionsItems(Item *item);
+
+ LoaderState &m_loaderState;
+ QStringList m_searchPaths;
+ std::vector<QStringList> m_extraSearchPaths;
+ mutable QStringList m_allSearchPaths;
+ std::unique_ptr<ItemReaderVisitorState> m_visitorState;
+ QString m_projectFilePath;
+ qint64 m_elapsedTime = -1;
+};
+
+class SearchPathsManager {
+public:
+ SearchPathsManager(ItemReader &itemReader, const QStringList &extraSearchPaths = {});
+ ~SearchPathsManager();
+
+private:
+ ItemReader &m_itemReader;
+ size_t m_oldSize{0};
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ITEMREADER_H
diff --git a/src/lib/corelib/loader/itemreaderastvisitor.cpp b/src/lib/corelib/loader/itemreaderastvisitor.cpp
new file mode 100644
index 000000000..fe0d6c190
--- /dev/null
+++ b/src/lib/corelib/loader/itemreaderastvisitor.cpp
@@ -0,0 +1,403 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "itemreadervisitorstate.h"
+
+#include <api/languageinfo.h>
+#include <jsextensions/jsextensions.h>
+#include <language/asttools.h>
+#include <language/builtindeclarations.h>
+#include <language/filecontext.h>
+#include <language/identifiersearch.h>
+#include <language/item.h>
+#include <language/value.h>
+#include <parser/qmljsast_p.h>
+#include <tools/codelocation.h>
+#include <tools/error.h>
+#include <tools/qbsassert.h>
+#include <tools/qttools.h>
+#include <tools/stringconstants.h>
+#include <logging/translator.h>
+
+#include <algorithm>
+
+using namespace QbsQmlJS;
+
+namespace qbs {
+namespace Internal {
+
+ItemReaderASTVisitor::ItemReaderASTVisitor(ItemReaderVisitorState &visitorState,
+ FileContextPtr file, ItemPool *itemPool, Logger &logger)
+ : m_visitorState(visitorState)
+ , m_file(std::move(file))
+ , m_itemPool(itemPool)
+ , m_logger(logger)
+{
+}
+
+ItemReaderASTVisitor::~ItemReaderASTVisitor() = default;
+
+bool ItemReaderASTVisitor::visit(AST::UiProgram *uiProgram)
+{
+ ASTImportsHandler importsHandler(m_visitorState, m_logger, m_file);
+ importsHandler.handleImports(uiProgram->imports);
+ m_typeNameToFile = importsHandler.typeNameFileMap();
+ return true;
+}
+
+static ItemValuePtr findItemProperty(const Item *container, const Item *item)
+{
+ ItemValuePtr itemValue;
+ const auto &srcprops = container->properties();
+ auto it = std::find_if(srcprops.begin(), srcprops.end(), [item] (const ValuePtr &v) {
+ return v->type() == Value::ItemValueType
+ && std::static_pointer_cast<ItemValue>(v)->item() == item;
+ });
+ if (it != srcprops.end())
+ itemValue = std::static_pointer_cast<ItemValue>(it.value());
+ return itemValue;
+}
+
+bool ItemReaderASTVisitor::visit(AST::UiObjectDefinition *ast)
+{
+ const QString typeName = ast->qualifiedTypeNameId->name.toString();
+ const CodeLocation itemLocation = toCodeLocation(ast->qualifiedTypeNameId->identifierToken);
+ const Item *baseItem = nullptr;
+ Item *mostDerivingItem = nullptr;
+
+ Item *item = Item::create(m_itemPool, ItemType::Unknown);
+ item->setFile(m_file);
+ item->setLocation(itemLocation);
+ const AST::SourceLocation &endLoc = ast->lastSourceLocation();
+ item->setEndPosition(CodePosition(endLoc.startLine, endLoc.startColumn));
+
+ // 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);
+ ItemType itemType;
+ if (!baseTypeFileName.isEmpty()) {
+ const bool isMostDerivingItem = (m_visitorState.mostDerivingItem() == nullptr);
+ if (isMostDerivingItem)
+ m_visitorState.setMostDerivingItem(item);
+ mostDerivingItem = m_visitorState.mostDerivingItem();
+ baseItem = m_visitorState.readFile(baseTypeFileName, m_file->searchPaths(), m_itemPool);
+ if (isMostDerivingItem)
+ m_visitorState.setMostDerivingItem(nullptr);
+ QBS_CHECK(baseItem->type() <= ItemType::LastActualItem);
+ itemType = baseItem->type();
+ } else {
+ if (fullTypeName.size() > 1) {
+ throw ErrorInfo(Tr::tr("Invalid item '%1'. Did you mean to set a module property?")
+ .arg(fullTypeName.join(QLatin1Char('.'))), itemLocation);
+ }
+ itemType = BuiltinDeclarations::instance().typeForName(typeName, itemLocation);
+ checkDeprecationStatus(itemType, typeName, itemLocation);
+ if (itemType == ItemType::Properties && m_item && m_item->type() == ItemType::SubProject)
+ itemType = ItemType::PropertiesInSubProject;
+ }
+
+ item->m_type = itemType;
+
+ 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.
+ if (itemType == ItemType::Module) {
+ QBS_CHECK(!m_moduleItemLocker);
+ m_moduleItemLocker = std::make_unique<ModuleItemLocker>(*m_item);
+ }
+ }
+
+ if (ast->initializer) {
+ Item *mdi = m_visitorState.mostDerivingItem();
+ m_visitorState.setMostDerivingItem(nullptr);
+ qSwap(m_item, item);
+ const ItemType oldInstanceItemType = m_instanceItemType;
+ if (itemType == ItemType::Parameters || itemType == ItemType::Depends)
+ m_instanceItemType = ItemType::ModuleParameters;
+ ast->initializer->accept(this);
+ m_instanceItemType = oldInstanceItemType;
+ qSwap(m_item, item);
+ m_visitorState.setMostDerivingItem(mdi);
+ }
+
+ ASTPropertiesItemHandler(item, *m_itemPool).handlePropertiesItems();
+
+ // Inheritance resolving, part 2 (depends on alternatives having been set up).
+ if (baseItem) {
+ inheritItem(item, baseItem);
+ if (baseItem->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);
+ baseItem->file()->idScope()->setPrototype(item->file()->idScope());
+
+ // Replace the base item with the most deriving item.
+ ItemValuePtr baseItemIdValue = findItemProperty(baseItem->file()->idScope(), baseItem);
+ if (baseItemIdValue)
+ baseItemIdValue->setItem(mostDerivingItem);
+ }
+ } 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_visitorState.deprecationWarningMode(), m_logger);
+ }
+
+ return false;
+}
+
+void ItemReaderASTVisitor::checkDuplicateBinding(Item *item, const QStringList &bindingName,
+ const AST::SourceLocation &sourceLocation)
+{
+ if (Q_UNLIKELY(item->hasOwnProperty(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 (Q_UNLIKELY(!ast->typeModifier.isEmpty())) {
+ throw ErrorInfo(Tr::tr("public member with type modifier '%1' not supported").arg(
+ ast->typeModifier.toString()));
+ }
+ if (ast->isReadonlyMember)
+ p.setFlags(PropertyDeclaration::ReadOnlyFlag);
+
+ 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.front() == QStringLiteral("id")) {
+ const auto * const expStmt = AST::cast<AST::ExpressionStatement *>(ast->statement);
+ if (Q_UNLIKELY(!expStmt))
+ throw ErrorInfo(Tr::tr("id: must be followed by identifier"));
+ const auto * const idExp = AST::cast<AST::IdentifierExpression *>(expStmt->expression);
+ if (Q_UNLIKELY(!idExp || idExp->name.isEmpty()))
+ throw ErrorInfo(Tr::tr("id: must be followed by identifier"));
+ m_item->m_id = idExp->name.toString();
+ m_file->ensureIdScope(m_itemPool);
+ ItemValueConstPtr existingId = m_file->idScope()->itemProperty(m_item->id(), *m_itemPool);
+ 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::handleBindingRhs(AST::Statement *statement,
+ const JSSourceValuePtr &value)
+{
+ QBS_CHECK(statement);
+ QBS_CHECK(value);
+
+ if (AST::cast<AST::Block *>(statement))
+ value->setHasFunctionForm();
+
+ value->setFile(m_file);
+ value->setSourceCode(textViewOf(m_file->content(), statement));
+ value->setLocation(statement->firstSourceLocation().startLine,
+ statement->firstSourceLocation().startColumn);
+
+ bool usesBase, usesOuter, usesOriginal;
+ IdentifierSearch idsearch;
+ idsearch.add(StringConstants::baseVar(), &usesBase);
+ idsearch.add(StringConstants::outerVar(), &usesOuter);
+ idsearch.add(StringConstants::originalVar(), &usesOriginal);
+ idsearch.start(statement);
+ if (usesBase)
+ value->setSourceUsesBase();
+ if (usesOuter)
+ value->setSourceUsesOuter();
+ if (usesOriginal)
+ value->setSourceUsesOriginal();
+ 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.size() - 1;
+ for (int i = 0; i < c; ++i) {
+ ValuePtr v = targetItem->ownProperty(bindingName.at(i));
+ if (!v) {
+ const ItemType itemType = i < c - 1 ? ItemType::ModulePrefix : m_instanceItemType;
+ Item *newItem = Item::create(m_itemPool, itemType);
+ newItem->setLocation(value->location());
+ 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 = std::static_pointer_cast<ItemValue>(v)->item();
+ }
+ return targetItem;
+}
+
+void ItemReaderASTVisitor::inheritItem(Item *dst, const Item *src)
+{
+ int insertPos = 0;
+ for (Item *child : std::as_const(src->m_children)) {
+ dst->m_children.insert(insertPos++, child);
+ child->m_parent = dst;
+ }
+
+ for (const PropertyDeclaration &pd : src->propertyDeclarations()) {
+ if (pd.flags().testFlag(PropertyDeclaration::ReadOnlyFlag)
+ && dst->hasOwnProperty(pd.name())) {
+ throw ErrorInfo(Tr::tr("Cannot set read-only property '%1'.").arg(pd.name()),
+ dst->property(pd.name())->location());
+ }
+ dst->setPropertyDeclaration(pd.name(), pd);
+ }
+
+ std::unique_ptr<ModuleItemLocker> locker;
+ if (src->type() == ItemType::Module)
+ locker = std::make_unique<ModuleItemLocker>(*src);
+ 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 = std::static_pointer_cast<JSSourceValue>(v);
+ QBS_CHECK(!sv->baseValue());
+ const JSSourceValuePtr baseValue = std::static_pointer_cast<JSSourceValue>(it.value());
+ sv->setBaseValue(baseValue);
+ for (const JSSourceValue::Alternative &alt : sv->m_alternatives)
+ alt.value->setBaseValue(baseValue);
+ break;
+ }
+ case Value::ItemValueType:
+ inheritItem(std::static_pointer_cast<ItemValue>(v)->item(),
+ std::static_pointer_cast<const ItemValue>(it.value())->item());
+ break;
+ default:
+ QBS_CHECK(!"unexpected value type");
+ }
+ }
+}
+
+void ItemReaderASTVisitor::checkDeprecationStatus(ItemType itemType, const QString &itemName,
+ const CodeLocation &itemLocation)
+{
+ const ItemDeclaration itemDecl = BuiltinDeclarations::instance().declarationsForType(itemType);
+ const ErrorInfo error = itemDecl.checkForDeprecation(m_visitorState.deprecationWarningMode(),
+ itemName, itemLocation, m_logger);
+ if (error.hasError())
+ throw error;
+}
+
+void ItemReaderASTVisitor::doCheckItemTypes(const Item *item)
+{
+ const ItemDeclaration decl = BuiltinDeclarations::instance().declarationsForType(item->type());
+ for (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/loader/itemreaderastvisitor.h b/src/lib/corelib/loader/itemreaderastvisitor.h
new file mode 100644
index 000000000..c47501f2f
--- /dev/null
+++ b/src/lib/corelib/loader/itemreaderastvisitor.h
@@ -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$
+**
+****************************************************************************/
+
+#ifndef QBS_ITEMREADERASTVISITOR_H
+#define QBS_ITEMREADERASTVISITOR_H
+
+#include <language/forward_decls.h>
+#include <language/itemtype.h>
+
+#include <logging/logger.h>
+#include <parser/qmljsastvisitor_p.h>
+
+#include <QtCore/qhash.h>
+#include <QtCore/qstringlist.h>
+
+#include <memory.h>
+
+namespace qbs {
+class CodeLocation;
+
+namespace Internal {
+class Item;
+class ItemPool;
+class ItemReaderVisitorState;
+class ModuleItemLocker;
+
+class ItemReaderASTVisitor : public QbsQmlJS::AST::Visitor
+{
+public:
+ ItemReaderASTVisitor(ItemReaderVisitorState &visitorState, FileContextPtr file,
+ ItemPool *itemPool, Logger &logger);
+ ~ItemReaderASTVisitor();
+
+ void checkItemTypes() { doCheckItemTypes(rootItem()); }
+
+ Item *rootItem() const { return m_item; }
+
+private:
+ bool visit(QbsQmlJS::AST::UiProgram *uiProgram) override;
+ bool visit(QbsQmlJS::AST::UiObjectDefinition *ast) override;
+ bool visit(QbsQmlJS::AST::UiPublicMember *ast) override;
+ bool visit(QbsQmlJS::AST::UiScriptBinding *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<QStringList, QString> m_typeNameToFile;
+ Item *m_item = nullptr;
+ std::unique_ptr<ModuleItemLocker> m_moduleItemLocker;
+ ItemType m_instanceItemType = ItemType::ModuleInstancePlaceholder;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_ITEMREADERASTVISITOR_H
diff --git a/src/lib/corelib/loader/itemreadervisitorstate.cpp b/src/lib/corelib/loader/itemreadervisitorstate.cpp
new file mode 100644
index 000000000..6ea23c021
--- /dev/null
+++ b/src/lib/corelib/loader/itemreadervisitorstate.cpp
@@ -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$
+**
+****************************************************************************/
+#include "itemreadervisitorstate.h"
+
+#include "itemreaderastvisitor.h"
+#include "loaderutils.h"
+
+#include <language/asttools.h>
+#include <language/filecontext.h>
+#include <logging/translator.h>
+#include <parser/qmljsengine_p.h>
+#include <parser/qmljslexer_p.h>
+#include <parser/qmljsparser_p.h>
+#include <tools/error.h>
+#include <tools/stringconstants.h>
+
+#include <QtCore/qdiriterator.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qtextstream.h>
+
+namespace qbs {
+namespace Internal {
+
+ItemReaderVisitorState::ItemReaderVisitorState(ItemReaderCache &cache, Logger &logger)
+ : m_cache(cache), m_logger(logger)
+{
+}
+
+Item *ItemReaderVisitorState::readFile(const QString &filePath, const QStringList &searchPaths,
+ ItemPool *itemPool)
+{
+ const auto setupCacheEntry = [&](ItemReaderCache::AstCacheEntry &entry) {
+ QFile file(filePath);
+ if (Q_UNLIKELY(!file.open(QFile::ReadOnly)))
+ throw ErrorInfo(Tr::tr("Cannot open '%1'.").arg(filePath));
+
+ QTextStream stream(&file);
+ setupDefaultCodec(stream);
+ const QString &code = stream.readAll();
+ QbsQmlJS::Lexer lexer(&entry.engine);
+ lexer.setCode(code, 1);
+ QbsQmlJS::Parser parser(&entry.engine);
+
+ file.close();
+ if (!parser.parse()) {
+ const QList<QbsQmlJS::DiagnosticMessage> &parserMessages = parser.diagnosticMessages();
+ if (Q_UNLIKELY(!parserMessages.empty())) {
+ ErrorInfo err;
+ for (const QbsQmlJS::DiagnosticMessage &msg : parserMessages)
+ err.append(msg.message, toCodeLocation(filePath, msg.loc));
+ throw err;
+ }
+ }
+
+ entry.code = code;
+ entry.ast = parser.ast();
+ };
+
+ ItemReaderCache::AstCacheEntry &cacheEntry = m_cache.retrieveOrSetupCacheEntry(
+ filePath, setupCacheEntry);
+ const FileContextPtr file = FileContext::create();
+ file->setFilePath(QFileInfo(filePath).absoluteFilePath());
+ file->setContent(cacheEntry.code);
+ file->setSearchPaths(searchPaths);
+
+ ItemReaderASTVisitor astVisitor(*this, file, itemPool, m_logger);
+ {
+ class ProcessingFlagManager {
+ public:
+ ProcessingFlagManager(ItemReaderCache::AstCacheEntry &e, const QString &filePath)
+ : m_cacheEntry(e)
+ {
+ if (!e.addProcessingThread())
+ throw ErrorInfo(Tr::tr("Loop detected when importing '%1'.").arg(filePath));
+ }
+ ~ProcessingFlagManager() { m_cacheEntry.removeProcessingThread(); }
+
+ private:
+ ItemReaderCache::AstCacheEntry &m_cacheEntry;
+ } processingFlagManager(cacheEntry, filePath);
+ cacheEntry.ast->accept(&astVisitor);
+ }
+ astVisitor.checkItemTypes();
+ return astVisitor.rootItem();
+}
+
+void ItemReaderVisitorState::findDirectoryEntries(const QString &dirPath, QStringList *entries) const
+{
+ *entries = m_cache.retrieveOrSetDirectoryEntries(dirPath, [&dirPath] {
+ QStringList fileNames;
+ QDirIterator dirIter(dirPath, StringConstants::qbsFileWildcards());
+ while (dirIter.hasNext()) {
+ dirIter.next();
+ fileNames << dirIter.fileName();
+ }
+ return fileNames;
+ });
+}
+
+Item *ItemReaderVisitorState::mostDerivingItem() const
+{
+ return m_mostDerivingItem;
+}
+
+void ItemReaderVisitorState::setMostDerivingItem(Item *item)
+{
+ m_mostDerivingItem = item;
+}
+
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/loader/itemreadervisitorstate.h b/src/lib/corelib/loader/itemreadervisitorstate.h
new file mode 100644
index 000000000..cbf1966b6
--- /dev/null
+++ b/src/lib/corelib/loader/itemreadervisitorstate.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_ITEMREADERVISITORSTATE_H
+#define QBS_ITEMREADERVISITORSTATE_H
+
+#include <tools/deprecationwarningmode.h>
+#include <tools/set.h>
+
+#include <QtCore/qstringlist.h>
+
+#include <memory>
+
+namespace qbs {
+namespace Internal {
+class Item;
+class ItemPool;
+class ItemReaderCache;
+class Logger;
+
+class ItemReaderVisitorState
+{
+public:
+ ItemReaderVisitorState(ItemReaderCache &cache, Logger &logger);
+
+ Logger &logger() { return m_logger; }
+
+ Item *readFile(const QString &filePath, const QStringList &searchPaths, ItemPool *itemPool);
+
+ void findDirectoryEntries(const QString &dirPath, QStringList *entries) const;
+
+ Item *mostDerivingItem() const;
+ void setMostDerivingItem(Item *item);
+
+ void setDeprecationWarningMode(DeprecationWarningMode mode) { m_deprecationWarningMode = mode; }
+ DeprecationWarningMode deprecationWarningMode() const { return m_deprecationWarningMode; }
+
+private:
+ DeprecationWarningMode m_deprecationWarningMode = defaultDeprecationWarningMode();
+ ItemReaderCache &m_cache;
+ Logger &m_logger;
+ Item *m_mostDerivingItem = nullptr;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // Include guard.
diff --git a/src/lib/corelib/loader/loaderutils.cpp b/src/lib/corelib/loader/loaderutils.cpp
new file mode 100644
index 000000000..05a077dbe
--- /dev/null
+++ b/src/lib/corelib/loader/loaderutils.cpp
@@ -0,0 +1,961 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "loaderutils.h"
+
+#include "itemreader.h"
+
+#include <language/evaluator.h>
+#include <language/filecontext.h>
+#include <language/itempool.h>
+#include <language/language.h>
+#include <language/resolvedfilecontext.h>
+#include <language/scriptengine.h>
+#include <language/value.h>
+#include <logging/categories.h>
+#include <logging/translator.h>
+#include <tools/fileinfo.h>
+#include <tools/progressobserver.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+
+namespace qbs::Internal {
+
+QString fullProductDisplayName(const QString &name, const QString &multiplexId)
+{
+ static const auto multiplexIdToString =[](const QString &id) {
+ return QString::fromUtf8(QByteArray::fromBase64(id.toUtf8()));
+ };
+ QString result = name;
+ if (!multiplexId.isEmpty())
+ result.append(QLatin1Char(' ')).append(multiplexIdToString(multiplexId));
+ return result;
+}
+
+void adjustParametersScopes(Item *item, Item *scope)
+{
+ if (item->type() == ItemType::ModuleParameters) {
+ item->setScope(scope);
+ return;
+ }
+
+ for (const auto &value : item->properties()) {
+ if (value->type() == Value::ItemValueType)
+ adjustParametersScopes(std::static_pointer_cast<ItemValue>(value)->item(), scope);
+ }
+}
+
+QString ProductContext::uniqueName() const
+{
+ return ResolvedProduct::uniqueName(name, multiplexConfigurationId);
+}
+
+QString ProductContext::displayName() const
+{
+ return fullProductDisplayName(name, multiplexConfigurationId);
+}
+
+void ProductContext::handleError(const ErrorInfo &error)
+{
+ const bool alreadyHadError = delayedError.hasError();
+ if (!alreadyHadError) {
+ delayedError.append(Tr::tr("Error while handling product '%1':")
+ .arg(name), item->location());
+ }
+ if (error.isInternalError()) {
+ if (alreadyHadError) {
+ qCDebug(lcModuleLoader()) << "ignoring subsequent internal error" << error.toString()
+ << "in product" << name;
+ return;
+ }
+ }
+ const auto errorItems = error.items();
+ for (const ErrorItem &ei : errorItems)
+ delayedError.append(ei.description(), ei.codeLocation());
+ project->topLevelProject->addDisabledItem(item);
+}
+
+TopLevelProjectContext::~TopLevelProjectContext() { qDeleteAll(m_projects); }
+
+bool TopLevelProjectContext::checkItemCondition(Item *item, Evaluator &evaluator)
+{
+ if (evaluator.boolValue(item, StringConstants::conditionProperty()))
+ return true;
+ addDisabledItem(item);
+ return false;
+}
+
+void TopLevelProjectContext::checkCancelation()
+{
+ if (m_progressObserver && m_progressObserver->canceled())
+ m_canceled = true;
+ if (m_canceled)
+ throw CancelException();
+}
+
+QString TopLevelProjectContext::sourceCodeForEvaluation(const JSSourceValueConstPtr &value)
+{
+ const auto sourceCodeGuard = m_sourceCode.lock();
+ QString &code = sourceCodeGuard.get()[value->sourceCode()];
+ if (!code.isNull())
+ return code;
+ code = value->sourceCodeForEvaluation();
+ return code;
+}
+
+ScriptFunctionPtr TopLevelProjectContext::scriptFunctionValue(Item *item, const QString &name)
+{
+ const JSSourceValuePtr value = item->sourceProperty(name);
+ QBS_CHECK(value);
+ const auto scriptFunctionMapGuard = m_scriptFunctionMap.lock();
+ ScriptFunctionPtr &script = scriptFunctionMapGuard.get()[value->location()];
+ if (!script.get()) {
+ script = ScriptFunction::create();
+ const PropertyDeclaration decl = item->propertyDeclaration(name);
+ script->sourceCode = sourceCodeAsFunction(value, decl);
+ script->location = value->location();
+ script->fileContext = resolvedFileContext(value->file());
+ }
+ return script;
+}
+
+QString TopLevelProjectContext::sourceCodeAsFunction(const JSSourceValueConstPtr &value,
+ const PropertyDeclaration &decl)
+{
+ const auto scriptFunctionMapGuard = m_scriptFunctions.lock();
+ QString &scriptFunction = scriptFunctionMapGuard.get()[std::make_pair(value->sourceCode(),
+ decl.functionArgumentNames())];
+ if (!scriptFunction.isNull())
+ return scriptFunction;
+ const QString args = decl.functionArgumentNames().join(QLatin1Char(','));
+ if (value->hasFunctionForm()) {
+ // Insert the argument list.
+ scriptFunction = value->sourceCodeForEvaluation();
+ scriptFunction.insert(10, args);
+ // Remove the function application "()" that has been
+ // added in ItemReaderASTVisitor::visitStatement.
+ scriptFunction.chop(2);
+ } else {
+ scriptFunction = QLatin1String("(function(") + args + QLatin1String("){return ")
+ + value->sourceCode().toString() + QLatin1String(";})");
+ }
+ return scriptFunction;
+}
+
+const ResolvedFileContextPtr &TopLevelProjectContext::resolvedFileContext(
+ const FileContextConstPtr &ctx)
+{
+ ResolvedFileContextPtr &result = m_fileContextMap[ctx];
+ if (!result)
+ result = ResolvedFileContext::create(*ctx);
+ return result;
+}
+
+void TopLevelProjectContext::removeProductToHandle(const ProductContext &product)
+{
+ std::unique_lock lock(m_productsToHandle.mutex);
+ m_productsToHandle.data.remove(&product);
+}
+
+bool TopLevelProjectContext::isProductQueuedForHandling(const ProductContext &product) const
+{
+ std::shared_lock lock(m_productsToHandle.mutex);
+ return m_productsToHandle.data.contains(&product);
+}
+
+void TopLevelProjectContext::addDisabledItem(Item *item)
+{
+ m_disabledItems.lock().get() << item;
+}
+
+bool TopLevelProjectContext::isDisabledItem(const Item *item) const
+{
+ return m_disabledItems.lock_shared().get().contains(item);
+}
+
+void TopLevelProjectContext::setProgressObserver(ProgressObserver *observer)
+{
+ m_progressObserver = observer;
+}
+
+ProgressObserver *TopLevelProjectContext::progressObserver() const { return m_progressObserver; }
+
+void TopLevelProjectContext::addQueuedError(const ErrorInfo &error)
+{
+ m_queuedErrors.lock().get() << error;
+}
+
+void TopLevelProjectContext::addProfileConfig(const QString &profileName,
+ const QVariantMap &profileConfig)
+{
+ m_profileConfigs.insert(profileName, profileConfig);
+}
+
+std::optional<QVariantMap> TopLevelProjectContext::profileConfig(const QString &profileName) const
+{
+ const auto it = m_profileConfigs.constFind(profileName);
+ if (it == m_profileConfigs.constEnd())
+ return {};
+ return it.value().toMap();
+}
+
+void TopLevelProjectContext::addProjectLevelProbe(const ProbeConstPtr &probe)
+{
+ m_probesInfo.projectLevelProbes << probe;
+}
+
+const std::vector<ProbeConstPtr> TopLevelProjectContext::projectLevelProbes() const
+{
+ return m_probesInfo.projectLevelProbes;
+}
+
+void TopLevelProjectContext::addProduct(ProductContext &product)
+{
+ m_productsByName.insert({product.name, &product});
+}
+
+void TopLevelProjectContext::addProductByType(ProductContext &product, const FileTags &tags)
+{
+ const auto productsByTypeGuard = m_productsByType.lock();
+ for (const FileTag &tag : tags)
+ productsByTypeGuard.get().insert({tag, &product});
+}
+
+ProductContext *TopLevelProjectContext::productWithNameAndConstraint(
+ const QString &name, const std::function<bool (ProductContext &)> &constraint)
+{
+ const auto candidates = m_productsByName.equal_range(name);
+ for (auto it = candidates.first; it != candidates.second; ++it) {
+ ProductContext * const candidate = it->second;
+ if (constraint(*candidate))
+ return candidate;
+ }
+ return nullptr;
+}
+
+std::vector<ProductContext *> TopLevelProjectContext::productsWithNameAndConstraint(
+ const QString &name, const std::function<bool (ProductContext &)> &constraint)
+{
+ std::vector<ProductContext *> result;
+ const auto candidates = m_productsByName.equal_range(name);
+ for (auto it = candidates.first; it != candidates.second; ++it) {
+ ProductContext * const candidate = it->second;
+ if (constraint(*candidate))
+ result << candidate;
+ }
+ return result;
+}
+
+std::vector<ProductContext *> TopLevelProjectContext::productsWithTypeAndConstraint(
+ const FileTags &tags, const std::function<bool (ProductContext &)> &constraint)
+{
+ const auto productsByTypeGuard = m_productsByType.lock_shared();
+ std::vector<ProductContext *> matchingProducts;
+ for (const FileTag &typeTag : tags) {
+ const auto range = productsByTypeGuard.get().equal_range(typeTag);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (constraint(*it->second))
+ matchingProducts.push_back(it->second);
+ }
+ }
+ return matchingProducts;
+}
+
+std::vector<std::pair<ProductContext *, CodeLocation>>
+TopLevelProjectContext::finishedProductsWithBulkDependency(const FileTag &tag) const
+{
+ return m_reverseBulkDependencies.value(tag);
+}
+
+void TopLevelProjectContext::registerBulkDependencies(ProductContext &product)
+{
+ for (const auto &tagAndLoc : product.bulkDependencies) {
+ for (const FileTag &tag : tagAndLoc.first)
+ m_reverseBulkDependencies[tag].emplace_back(&product, tagAndLoc.second);
+ }
+}
+
+void TopLevelProjectContext::addProjectNameUsedInOverrides(const QString &name)
+{
+ m_projectNamesUsedInOverrides << name;
+}
+
+const Set<QString> &TopLevelProjectContext::projectNamesUsedInOverrides() const
+{
+ return m_projectNamesUsedInOverrides;
+}
+
+void TopLevelProjectContext::addProductNameUsedInOverrides(const QString &name)
+{
+ m_productNamesUsedInOverrides << name;
+}
+
+const Set<QString> &TopLevelProjectContext::productNamesUsedInOverrides() const
+{
+ return m_productNamesUsedInOverrides;
+}
+
+void TopLevelProjectContext::addMultiplexConfiguration(const QString &id, const QVariantMap &config)
+{
+ m_multiplexConfigsById.insert(std::make_pair(id, config));
+}
+
+QVariantMap TopLevelProjectContext::multiplexConfiguration(const QString &id) const
+{
+ if (id.isEmpty())
+ return {};
+ const auto it = m_multiplexConfigsById.find(id);
+ QBS_CHECK(it != m_multiplexConfigsById.end() && !it->second.isEmpty());
+ return it->second;
+}
+
+std::lock_guard<std::mutex> TopLevelProjectContext::moduleProvidersCacheLock()
+{
+ return std::lock_guard<std::mutex>(m_moduleProvidersCacheMutex);
+}
+
+void TopLevelProjectContext::setModuleProvidersCache(const ModuleProvidersCache &cache)
+{
+ m_moduleProvidersCache = cache;
+}
+
+ModuleProviderInfo *TopLevelProjectContext::moduleProvider(const ModuleProvidersCacheKey &key)
+{
+ if (const auto it = m_moduleProvidersCache.find(key); it != m_moduleProvidersCache.end())
+ return &(*it);
+ return nullptr;
+}
+
+ModuleProviderInfo &TopLevelProjectContext::addModuleProvider(const ModuleProvidersCacheKey &key,
+ const ModuleProviderInfo &provider)
+{
+ return m_moduleProvidersCache[key] = provider;
+}
+
+void TopLevelProjectContext::addParameterDeclarations(const Item *moduleProto,
+ const Item::PropertyDeclarationMap &decls)
+{
+ m_parameterDeclarations.lock().get().insert({moduleProto, decls});
+}
+
+Item::PropertyDeclarationMap TopLevelProjectContext::parameterDeclarations(Item *moduleProto) const
+{
+ const auto parameterDeclarationsGuard = m_parameterDeclarations.lock_shared();
+ const auto &declarations = parameterDeclarationsGuard.get();
+ if (const auto it = declarations.find(moduleProto); it != declarations.end()) {
+ return it->second;
+ }
+ return {};
+}
+
+void TopLevelProjectContext::setParameters(const Item *moduleProto, const QVariantMap &parameters)
+{
+ m_parameters.lock().get().insert({moduleProto, parameters});
+}
+
+QVariantMap TopLevelProjectContext::parameters(Item *moduleProto) const
+{
+ const auto parametersGuard = m_parameters.lock_shared();
+ const auto &parameters = parametersGuard.get();
+ if (const auto it = parameters.find(moduleProto); it != parameters.end()) {
+ return it->second;
+ }
+ return {};
+}
+
+void TopLevelProjectContext::addCodeLink(const QString &sourceFile, const CodeRange &sourceRange,
+ const CodeLocation &target)
+{
+ const auto codeLinksGuard = m_codeLinks.lock();
+ QList<CodeLocation> &links = codeLinksGuard.get()[sourceFile][sourceRange];
+ if (!links.contains(target))
+ links << target;
+}
+
+QString TopLevelProjectContext::findModuleDirectory(
+ const QualifiedId &module, const QString &searchPath,
+ const std::function<QString()> &findOnDisk)
+{
+ const auto modulePathCacheGuard = m_modulePathCache.lock();
+ auto &path = modulePathCacheGuard.get()[{searchPath, module}];
+ if (!path)
+ path = findOnDisk();
+ return *path;
+}
+
+QStringList TopLevelProjectContext::getModuleFilesForDirectory(
+ const QString &dir, const std::function<QStringList ()> &findOnDisk)
+{
+ const auto moduleFilesGuard = m_moduleFilesPerDirectory.lock();
+ auto &list = moduleFilesGuard.get()[dir];
+ if (!list)
+ list = findOnDisk();
+ return *list;
+}
+
+void TopLevelProjectContext::removeModuleFileFromDirectoryCache(const QString &filePath)
+{
+ const auto moduleFilesGuard = m_moduleFilesPerDirectory.lock();
+ auto &moduleFiles = moduleFilesGuard.get();
+ const auto it = moduleFiles.find(FileInfo::path(filePath));
+ QBS_CHECK(it != moduleFiles.end());
+ auto &files = it->second;
+ QBS_CHECK(files);
+ files->removeOne(filePath);
+}
+
+void TopLevelProjectContext::addUnknownProfilePropertyError(const Item *moduleProto,
+ const ErrorInfo &error)
+{
+ m_unknownProfilePropertyErrors.lock().get()[moduleProto].push_back(error);
+}
+
+const std::vector<ErrorInfo> &TopLevelProjectContext::unknownProfilePropertyErrors(
+ const Item *moduleProto) const
+{
+ const auto errorsGuard = m_unknownProfilePropertyErrors.lock_shared();
+ const auto &errors = errorsGuard.get();
+ if (const auto it = errors.find(moduleProto); it != errors.end()) {
+ return it->second;
+ }
+ static const std::vector<ErrorInfo> empty;
+ return empty;
+}
+
+Item *TopLevelProjectContext::getModulePrototype(const QString &filePath, const QString &profile,
+ const std::function<Item *()> &produce)
+{
+ const auto modulePrototypesGuard = m_modulePrototypes.lock();
+ auto &prototypeList = modulePrototypesGuard.get()[filePath];
+ for (const auto &prototype : prototypeList) {
+ if (prototype.second == profile)
+ return prototype.first;
+ }
+ Item * const module = produce();
+ if (module)
+ prototypeList.emplace_back(module, profile);
+ return module;
+}
+
+void TopLevelProjectContext::addLocalProfile(const QString &name, const QVariantMap &values,
+ const CodeLocation &location)
+{
+ if (m_localProfiles.contains(name))
+ throw ErrorInfo(Tr::tr("Local profile '%1' redefined.").arg(name), location);
+ m_localProfiles.insert(name, values);
+}
+
+void TopLevelProjectContext::checkForLocalProfileAsTopLevelProfile(const QString &topLevelProfile)
+{
+ for (auto it = m_localProfiles.cbegin(); it != m_localProfiles.cend(); ++it) {
+ if (it.key() != topLevelProfile)
+ continue;
+
+ // This covers the edge case that a locally defined profile was specified as the
+ // top-level profile, in which case we must invalidate the qbs module prototype that was
+ // created in early setup before local profiles were handled.
+ const auto prototypesGuard = m_modulePrototypes.lock();
+ auto &modulePrototypes = prototypesGuard.get();
+ QBS_CHECK(modulePrototypes.size() == 1);
+ modulePrototypes.clear();
+ break;
+ }
+}
+
+std::lock_guard<std::mutex> TopLevelProjectContext::probesCacheLock()
+{
+ return std::lock_guard<std::mutex>(m_probesMutex);
+}
+
+void TopLevelProjectContext::setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes)
+{
+ for (const ProbeConstPtr& probe : oldProbes)
+ m_probesInfo.oldProjectProbes[probe->globalId()] << probe;
+}
+
+ProbeConstPtr TopLevelProjectContext::findOldProjectProbe(const QString &id,
+ const ProbeFilter &filter) const
+{
+ for (const ProbeConstPtr &oldProbe : m_probesInfo.oldProjectProbes.value(id)) {
+ if (filter(oldProbe))
+ return oldProbe;
+ }
+ return {};
+}
+
+void TopLevelProjectContext::setOldProductProbes(
+ const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes)
+{
+ m_probesInfo.oldProductProbes = oldProbes;
+}
+
+ProbeConstPtr TopLevelProjectContext::findOldProductProbe(const QString &productName,
+ const ProbeFilter &filter) const
+{
+ for (const ProbeConstPtr &oldProbe : m_probesInfo.oldProductProbes.value(productName)) {
+ if (filter(oldProbe))
+ return oldProbe;
+ }
+ return {};
+}
+
+void TopLevelProjectContext::addNewlyResolvedProbe(const ProbeConstPtr &probe)
+{
+ m_probesInfo.currentProbes[probe->location()] << probe;
+}
+
+ProbeConstPtr TopLevelProjectContext::findCurrentProbe(const CodeLocation &location,
+ const ProbeFilter &filter) const
+{
+ for (const ProbeConstPtr &probe : m_probesInfo.currentProbes.value(location)) {
+ if (filter(probe))
+ return probe;
+ }
+ return {};
+}
+
+void TopLevelProjectContext::collectDataFromEngine(const ScriptEngine &engine)
+{
+ const auto project = dynamic_cast<TopLevelProject *>(m_projects.front()->project.get());
+ QBS_CHECK(project);
+ project->canonicalFilePathResults.insert(engine.canonicalFilePathResults());
+ project->fileExistsResults.insert(engine.fileExistsResults());
+ project->directoryEntriesResults.insert(engine.directoryEntriesResults());
+ project->fileLastModifiedResults.insert(engine.fileLastModifiedResults());
+ project->environment.insert(engine.environment());
+ project->buildSystemFiles.unite(engine.imports());
+}
+
+ItemPool &TopLevelProjectContext::createItemPool()
+{
+ m_itemPools.push_back(std::make_unique<ItemPool>());
+ return *m_itemPools.back();
+}
+
+class LoaderState::Private
+{
+public:
+ Private(LoaderState &q, const SetupProjectParameters &parameters,
+ TopLevelProjectContext &topLevelProject, ItemPool &itemPool, ScriptEngine &engine,
+ Logger &&logger)
+ : parameters(parameters), topLevelProject(topLevelProject), itemPool(itemPool),
+ logger(std::move(logger)), itemReader(q), evaluator(&engine)
+ {
+ this->logger.clearWarnings();
+ this->logger.storeWarnings();
+ }
+
+ const SetupProjectParameters &parameters;
+ TopLevelProjectContext &topLevelProject;
+ ItemPool &itemPool;
+
+ Logger logger;
+ ItemReader itemReader;
+ Evaluator evaluator;
+};
+
+LoaderState::LoaderState(const SetupProjectParameters &parameters,
+ TopLevelProjectContext &topLevelProject, ItemPool &itemPool,
+ ScriptEngine &engine, Logger logger)
+ : d(makePimpl<Private>(*this, parameters, topLevelProject, itemPool, engine, std::move(logger)))
+{
+ d->itemReader.init();
+}
+
+LoaderState::~LoaderState() = default;
+const SetupProjectParameters &LoaderState::parameters() const { return d->parameters; }
+ItemPool &LoaderState::itemPool() { return d->itemPool; }
+Evaluator &LoaderState::evaluator() { return d->evaluator; }
+Logger &LoaderState::logger() { return d->logger; }
+ItemReader &LoaderState::itemReader() { return d->itemReader; }
+TopLevelProjectContext &LoaderState::topLevelProject() { return d->topLevelProject; }
+
+static QString verbatimValue(LoaderState &state, const ValueConstPtr &value)
+{
+ QString result;
+ if (value && value->type() == Value::JSSourceValueType) {
+ const JSSourceValueConstPtr sourceValue = std::static_pointer_cast<const JSSourceValue>(
+ value);
+ result = state.topLevelProject().sourceCodeForEvaluation(sourceValue);
+ }
+ return result;
+}
+
+static void resolveRuleArtifactBinding(
+ LoaderState &state, const RuleArtifactPtr &ruleArtifact, Item *item,
+ const QStringList &namePrefix, QualifiedIdSet *seenBindings)
+{
+ for (auto it = item->properties().constBegin(); it != item->properties().constEnd(); ++it) {
+ const QStringList name = QStringList(namePrefix) << it.key();
+ if (it.value()->type() == Value::ItemValueType) {
+ resolveRuleArtifactBinding(state, ruleArtifact,
+ std::static_pointer_cast<ItemValue>(it.value())->item(),
+ name, seenBindings);
+ } else if (it.value()->type() == Value::JSSourceValueType) {
+ const auto insertResult = seenBindings->insert(name);
+ if (!insertResult.second)
+ continue;
+ JSSourceValuePtr sourceValue = std::static_pointer_cast<JSSourceValue>(it.value());
+ RuleArtifact::Binding rab;
+ rab.name = name;
+ rab.code = state.topLevelProject().sourceCodeForEvaluation(sourceValue);
+ rab.location = sourceValue->location();
+ ruleArtifact->bindings.push_back(rab);
+ } else {
+ QBS_ASSERT(!"unexpected value type", continue);
+ }
+ }
+}
+
+static void resolveRuleArtifact(LoaderState &state, const RulePtr &rule, Item *item)
+{
+ RuleArtifactPtr artifact = RuleArtifact::create();
+ rule->artifacts.push_back(artifact);
+ artifact->location = item->location();
+
+ if (const auto sourceProperty = item->sourceProperty(StringConstants::filePathProperty()))
+ artifact->filePathLocation = sourceProperty->location();
+
+ artifact->filePath = verbatimValue(state, item->property(StringConstants::filePathProperty()));
+ artifact->fileTags = state.evaluator().fileTagsValue(item, StringConstants::fileTagsProperty());
+ artifact->alwaysUpdated = state.evaluator().boolValue(
+ item, StringConstants::alwaysUpdatedProperty());
+
+ QualifiedIdSet seenBindings;
+ for (Item *obj = item; obj; obj = obj->prototype()) {
+ for (QMap<QString, ValuePtr>::const_iterator it = obj->properties().constBegin();
+ it != obj->properties().constEnd(); ++it)
+ {
+ if (it.value()->type() != Value::ItemValueType)
+ continue;
+ resolveRuleArtifactBinding(
+ state, artifact, std::static_pointer_cast<ItemValue>(it.value())->item(),
+ QStringList(it.key()), &seenBindings);
+ }
+ }
+}
+
+void resolveRule(LoaderState &state, Item *item, ProjectContext *projectContext,
+ ProductContext *productContext, ModuleContext *moduleContext)
+{
+ Evaluator &evaluator = state.evaluator();
+ if (!evaluator.boolValue(item, StringConstants::conditionProperty()))
+ return;
+
+ RulePtr rule = Rule::create();
+
+ // read artifacts
+ bool hasArtifactChildren = false;
+ for (Item * const 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(state, rule, child);
+ }
+
+ rule->name = evaluator.stringValue(item, StringConstants::nameProperty());
+ rule->prepareScript.initialize(state.topLevelProject().scriptFunctionValue(
+ item, StringConstants::prepareProperty()));
+ rule->outputArtifactsScript.initialize(state.topLevelProject().scriptFunctionValue(
+ item, StringConstants::outputArtifactsProperty()));
+ rule->outputFileTags = evaluator.fileTagsValue(item, StringConstants::outputFileTagsProperty());
+ 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());
+ }
+ if (!hasArtifactChildren && rule->outputFileTags.empty()) {
+ throw ErrorInfo(Tr::tr("A rule needs to have Artifact items or a non-empty "
+ "outputFileTags property."), item->location());
+ }
+ rule->multiplex = evaluator.boolValue(item, StringConstants::multiplexProperty());
+ rule->alwaysRun = evaluator.boolValue(item, StringConstants::alwaysRunProperty());
+ rule->inputs = evaluator.fileTagsValue(item, StringConstants::inputsProperty());
+ rule->inputsFromDependencies
+ = evaluator.fileTagsValue(item, StringConstants::inputsFromDependenciesProperty());
+ bool requiresInputsSet = false;
+ rule->requiresInputs = evaluator.boolValue(item, StringConstants::requiresInputsProperty(),
+ &requiresInputsSet);
+ if (!requiresInputsSet)
+ rule->requiresInputs = rule->declaresInputs();
+ rule->auxiliaryInputs
+ = evaluator.fileTagsValue(item, StringConstants::auxiliaryInputsProperty());
+ rule->excludedInputs
+ = evaluator.fileTagsValue(item, StringConstants::excludedInputsProperty());
+ if (rule->excludedInputs.empty()) {
+ rule->excludedInputs = evaluator.fileTagsValue(
+ item, StringConstants::excludedAuxiliaryInputsProperty());
+ }
+ rule->explicitlyDependsOn
+ = evaluator.fileTagsValue(item, StringConstants::explicitlyDependsOnProperty());
+ rule->explicitlyDependsOnFromDependencies = evaluator.fileTagsValue(
+ item, StringConstants::explicitlyDependsOnFromDependenciesProperty());
+ rule->module = moduleContext ? moduleContext->module : projectContext->dummyModule;
+ if (!rule->multiplex && !rule->declaresInputs()) {
+ throw ErrorInfo(Tr::tr("Rule has no inputs, but is not a multiplex rule."),
+ item->location());
+ }
+ if (!rule->multiplex && !rule->requiresInputs) {
+ throw ErrorInfo(Tr::tr("Rule.requiresInputs is false for non-multiplex rule."),
+ item->location());
+ }
+ if (!rule->declaresInputs() && rule->requiresInputs) {
+ throw ErrorInfo(Tr::tr("Rule.requiresInputs is true, but the rule "
+ "does not declare any input tags."), item->location());
+ }
+ if (productContext) {
+ rule->product = productContext->product.get();
+ productContext->product->rules.push_back(rule);
+ } else {
+ projectContext->rules.push_back(rule);
+ }
+}
+
+void resolveFileTagger(LoaderState &state, Item *item, ProjectContext *projectContext,
+ ProductContext *productContext)
+{
+ Evaluator &evaluator = state.evaluator();
+ if (!evaluator.boolValue(item, StringConstants::conditionProperty()))
+ return;
+ std::vector<FileTaggerConstPtr> &fileTaggers = productContext
+ ? productContext->product->fileTaggers
+ : projectContext->fileTaggers;
+ const QStringList patterns = evaluator.stringListValue(item,
+ StringConstants::patternsProperty());
+ if (patterns.empty())
+ throw ErrorInfo(Tr::tr("FileTagger.patterns must be a non-empty list."), item->location());
+
+ const FileTags fileTags = evaluator.fileTagsValue(item, StringConstants::fileTagsProperty());
+ if (fileTags.empty())
+ throw ErrorInfo(Tr::tr("FileTagger.fileTags must not be empty."), item->location());
+
+ for (const QString &pattern : patterns) {
+ if (pattern.isEmpty())
+ throw ErrorInfo(Tr::tr("A FileTagger pattern must not be empty."), item->location());
+ }
+
+ const int priority = evaluator.intValue(item, StringConstants::priorityProperty());
+ fileTaggers.push_back(FileTagger::create(patterns, fileTags, priority));
+}
+
+void resolveJobLimit(LoaderState &state, Item *item, ProjectContext *projectContext,
+ ProductContext *productContext, ModuleContext *moduleContext)
+{
+ Evaluator &evaluator = state.evaluator();
+ if (!evaluator.boolValue(item, StringConstants::conditionProperty()))
+ return;
+ const QString jobPool = evaluator.stringValue(item, StringConstants::jobPoolProperty());
+ if (jobPool.isEmpty())
+ throw ErrorInfo(Tr::tr("A JobLimit item needs to have a non-empty '%1' property.")
+ .arg(StringConstants::jobPoolProperty()), item->location());
+ bool jobCountWasSet;
+ const int jobCount = evaluator.intValue(item, StringConstants::jobCountProperty(), -1,
+ &jobCountWasSet);
+ if (!jobCountWasSet) {
+ throw ErrorInfo(Tr::tr("A JobLimit item needs to have a '%1' property.")
+ .arg(StringConstants::jobCountProperty()), item->location());
+ }
+ if (jobCount < 0) {
+ throw ErrorInfo(Tr::tr("A JobLimit item must have a non-negative '%1' property.")
+ .arg(StringConstants::jobCountProperty()), item->location());
+ }
+ JobLimits &jobLimits = moduleContext
+ ? moduleContext->jobLimits
+ : productContext ? productContext->product->jobLimits
+ : projectContext->jobLimits;
+ JobLimit jobLimit(jobPool, jobCount);
+ const int oldLimit = jobLimits.getLimit(jobPool);
+ if (oldLimit == -1 || oldLimit > jobCount)
+ jobLimits.setJobLimit(jobLimit);
+}
+
+const FileTag unknownFileTag()
+{
+ static const FileTag tag("unknown-file-tag");
+ return tag;
+}
+
+bool ProductContext::dependenciesResolvingPending() const
+{
+ return (!dependenciesContext || !dependenciesContext->dependenciesResolved)
+ && !product && !delayedError.hasError();
+}
+
+std::pair<ProductDependency, ProductContext *> ProductContext::pendingDependency() const
+{
+ return dependenciesContext ? dependenciesContext->pendingDependency()
+ : std::make_pair(ProductDependency::None, nullptr);
+}
+
+TimingData &TimingData::operator+=(const TimingData &other)
+{
+ dependenciesResolving += other.dependenciesResolving;
+ moduleProviders += other.moduleProviders;
+ moduleInstantiation += other.moduleInstantiation;
+ propertyMerging += other.propertyMerging;
+ groupsSetup += other.groupsSetup;
+ groupsResolving += other.groupsResolving;
+ preparingProducts += other.preparingProducts;
+ resolvingProducts += other.resolvingProducts;
+ probes += other.probes;
+ propertyEvaluation += other.propertyEvaluation;
+ propertyChecking += other.propertyChecking;
+ return *this;
+}
+
+DependenciesContext::~DependenciesContext() = default;
+
+ItemReaderCache::AstCacheEntry &ItemReaderCache::retrieveOrSetupCacheEntry(
+ const QString &filePath, const std::function<void (AstCacheEntry &)> &setup)
+{
+ const auto astCacheGuard = m_astCache.lock();
+ AstCacheEntry &entry = astCacheGuard.get()[filePath];
+ if (!entry.ast) {
+ setup(entry);
+ m_filesRead << filePath;
+ }
+ return entry;
+}
+
+const QStringList &ItemReaderCache::retrieveOrSetDirectoryEntries(
+ const QString &dir, const std::function<QStringList ()> &findOnDisk)
+{
+ const auto directoryEntriesGuard = m_directoryEntries.lock();
+ auto &entries = directoryEntriesGuard.get()[dir];
+ if (!entries)
+ entries = findOnDisk();
+ return *entries;
+}
+
+bool ItemReaderCache::AstCacheEntry::addProcessingThread()
+{
+ return m_processingThreads.lock().get().insert(std::this_thread::get_id()).second;
+}
+
+void ItemReaderCache::AstCacheEntry::removeProcessingThread()
+{
+ m_processingThreads.lock().get().remove(std::this_thread::get_id());
+}
+
+class DependencyParametersMerger
+{
+public:
+ DependencyParametersMerger(std::vector<Item::Module::ParametersWithPriority> &&candidates)
+ : m_candidates(std::move(candidates)) { }
+ QVariantMap merge();
+
+private:
+ void merge(QVariantMap &current, const QVariantMap &next, int nextPrio);
+
+ const std::vector<Item::Module::ParametersWithPriority> m_candidates;
+
+ struct Conflict {
+ Conflict(QStringList path, QVariant val1, QVariant val2, int prio)
+ : path(std::move(path)), val1(std::move(val1)), val2(std::move(val2)), priority(prio) {}
+ QStringList path;
+ QVariant val1;
+ QVariant val2;
+ int priority;
+ };
+ std::vector<Conflict> m_conflicts;
+ QVariantMap m_currentValue;
+ int m_currentPrio = INT_MIN;
+ QStringList m_path;
+};
+
+QVariantMap mergeDependencyParameters(std::vector<Item::Module::ParametersWithPriority> &&candidates)
+{
+ return DependencyParametersMerger(std::move(candidates)).merge();
+}
+
+QVariantMap mergeDependencyParameters(const QVariantMap &m1, const QVariantMap &m2)
+{
+ return mergeDependencyParameters({std::make_pair(m1, 0), std::make_pair(m2, 0)});
+}
+
+QVariantMap DependencyParametersMerger::merge()
+{
+ for (const auto &next : m_candidates) {
+ merge(m_currentValue, next.first, next.second);
+ m_currentPrio = next.second;
+ }
+
+ if (!m_conflicts.empty()) {
+ ErrorInfo error(Tr::tr("Conflicting parameter values encountered:"));
+ for (const Conflict &conflict : m_conflicts) {
+ // TODO: Location would be nice ...
+ error.append(Tr::tr(" Parameter '%1' cannot be both '%2' and '%3'.")
+ .arg(conflict.path.join(QLatin1Char('.')),
+ conflict.val1.toString(), conflict.val2.toString()));
+ }
+ throw error;
+ }
+
+ return m_currentValue;
+}
+
+void DependencyParametersMerger::merge(QVariantMap &current, const QVariantMap &next, int nextPrio)
+{
+ for (auto it = next.begin(); it != next.end(); ++it) {
+ m_path << it.key();
+ const QVariant &newValue = it.value();
+ QVariant &currentValue = current[it.key()];
+ if (newValue.userType() == QMetaType::QVariantMap) {
+ QVariantMap mdst = currentValue.toMap();
+ merge(mdst, it.value().toMap(), nextPrio);
+ currentValue = mdst;
+ } else {
+ if (m_currentPrio == nextPrio) {
+ if (currentValue.isValid() && !qVariantsEqual(currentValue, newValue))
+ m_conflicts.emplace_back(m_path, currentValue, newValue, m_currentPrio);
+ } else {
+ removeIf(m_conflicts, [this](const Conflict &conflict) {
+ return m_path == conflict.path;
+ });
+ }
+ currentValue = newValue;
+ }
+ m_path.removeLast();
+ }
+}
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/loaderutils.h b/src/lib/corelib/loader/loaderutils.h
new file mode 100644
index 000000000..4a4aadfb8
--- /dev/null
+++ b/src/lib/corelib/loader/loaderutils.h
@@ -0,0 +1,464 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <language/filetags.h>
+#include <language/forward_decls.h>
+#include <language/item.h>
+#include <language/moduleproviderinfo.h>
+#include <language/propertydeclaration.h>
+#include <language/qualifiedid.h>
+#include <parser/qmljsengine_p.h>
+#include <tools/codelocation.h>
+#include <tools/filetime.h>
+#include <tools/mutexdata.h>
+#include <tools/joblimits.h>
+#include <tools/pimpl.h>
+#include <tools/set.h>
+#include <tools/version.h>
+
+#include <QHash>
+#include <QStringList>
+#include <QVariant>
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <shared_mutex>
+#include <thread>
+#include <utility>
+#include <vector>
+
+namespace qbs {
+class SetupProjectParameters;
+namespace Internal {
+class Evaluator;
+class ItemPool;
+class ItemReader;
+class Logger;
+class ProductContext;
+class ProgressObserver;
+class ProjectContext;
+class ScriptEngine;
+
+using ModulePropertiesPerGroup = std::unordered_map<const Item *, QualifiedIdSet>;
+using FileLocations = QHash<std::pair<QString, QString>, CodeLocation>;
+
+enum class Deferral { Allowed, NotAllowed };
+enum class ProductDependency { None, Single, Bulk };
+
+class CancelException { };
+
+template<typename DataType, typename MutexType = std::shared_mutex>
+struct GuardedData {
+ DataType data;
+ mutable MutexType mutex;
+};
+
+class TimingData
+{
+public:
+ TimingData &operator+=(const TimingData &other);
+
+ qint64 dependenciesResolving = 0;
+ qint64 moduleProviders = 0;
+ qint64 moduleInstantiation = 0;
+ qint64 propertyMerging = 0;
+ qint64 groupsSetup = 0;
+ qint64 groupsResolving = 0;
+ qint64 preparingProducts = 0;
+ qint64 resolvingProducts = 0;
+ qint64 schedulingProducts = 0;
+ qint64 probes = 0;
+ qint64 propertyEvaluation = 0;
+ qint64 propertyChecking = 0;
+};
+
+class ItemReaderCache
+{
+public:
+ class AstCacheEntry
+ {
+ public:
+ QString code;
+ QbsQmlJS::Engine engine;
+ QbsQmlJS::AST::UiProgram *ast = nullptr;
+
+ bool addProcessingThread();
+ void removeProcessingThread();
+
+ private:
+ MutexData<Set<std::thread::id>, std::recursive_mutex> m_processingThreads;
+ };
+
+ const Set<QString> &filesRead() const { return m_filesRead; }
+ AstCacheEntry &retrieveOrSetupCacheEntry(const QString &filePath,
+ const std::function<void(AstCacheEntry &)> &setup);
+ const QStringList &retrieveOrSetDirectoryEntries(
+ const QString &dir, const std::function<QStringList()> &findOnDisk);
+
+private:
+ Set<QString> m_filesRead;
+ MutexData<std::unordered_map<QString, std::optional<QStringList>>, std::mutex> m_directoryEntries; // TODO: Merge with module dir entries cache?
+ MutexData<std::unordered_map<QString, AstCacheEntry>, std::mutex> m_astCache;
+};
+
+class DependenciesContext
+{
+public:
+ virtual ~DependenciesContext();
+ virtual std::pair<ProductDependency, ProductContext *> pendingDependency() const = 0;
+
+ bool dependenciesResolved = false;
+};
+
+class ProductContext
+{
+public:
+ QString uniqueName() const;
+ QString displayName() const;
+ void handleError(const ErrorInfo &error);
+ bool dependenciesResolvingPending() const;
+ std::pair<ProductDependency, ProductContext *> pendingDependency() const;
+
+ QString name;
+ QString buildDirectory;
+ Item *item = nullptr;
+ Item *scope = nullptr;
+ ProjectContext *project = nullptr;
+ std::unique_ptr<ProductContext> shadowProduct;
+ Item *mergedExportItem = nullptr;
+ std::vector<ProbeConstPtr> probes;
+ ModulePropertiesPerGroup modulePropertiesSetInGroups;
+ ErrorInfo delayedError;
+ QString profileName;
+ QString multiplexConfigurationId;
+ QVariantMap profileModuleProperties; // Tree-ified module properties from profile.
+ QVariantMap moduleProperties; // Tree-ified module properties from profile + overridden values.
+ std::optional<QVariantMap> providerConfig;
+ std::optional<QVariantMap> providerQbsModule;
+ QVariantMap defaultParameters; // In Export item.
+ QStringList searchPaths;
+ ResolvedProductPtr product;
+ TimingData timingData;
+ std::unique_ptr<DependenciesContext> dependenciesContext;
+
+ // This is needed because complex cyclic dependencies that involve Depends.productTypes
+ // may only be detected after a product has already been fully resolved.
+ std::vector<std::pair<FileTags, CodeLocation>> bulkDependencies;
+
+ // The keys are module prototypes, the values specify whether the module's
+ // condition is true for this product.
+ std::unordered_map<Item *, bool> modulePrototypeEnabledInfo;
+
+ int dependsItemCount = -1;
+};
+
+class TopLevelProjectContext
+{
+public:
+ TopLevelProjectContext() = default;
+ TopLevelProjectContext(const TopLevelProjectContext &) = delete;
+ TopLevelProjectContext &operator=(const TopLevelProjectContext &) = delete;
+ ~TopLevelProjectContext();
+
+ bool checkItemCondition(Item *item, Evaluator &evaluator);
+ QString sourceCodeForEvaluation(const JSSourceValueConstPtr &value);
+ ScriptFunctionPtr scriptFunctionValue(Item *item, const QString &name);
+ QString sourceCodeAsFunction(const JSSourceValueConstPtr &value,
+ const PropertyDeclaration &decl);
+
+ void setCanceled() { m_canceled = true; }
+ void checkCancelation();
+ bool isCanceled() const { return m_canceled; }
+
+ int productCount() const { return m_productsByName.size(); }
+
+ void addProductToHandle(const ProductContext &product) { m_productsToHandle.data << &product; }
+ void removeProductToHandle(const ProductContext &product);
+ bool isProductQueuedForHandling(const ProductContext &product) const;
+ int productsToHandleCount() const { return m_productsToHandle.data.size(); }
+
+ void addDisabledItem(Item *item);
+ bool isDisabledItem(const Item *item) const;
+
+ void setProgressObserver(ProgressObserver *observer);
+ ProgressObserver *progressObserver() const;
+
+ void addProject(ProjectContext *project) { m_projects.push_back(project); }
+ const std::vector<ProjectContext *> &projects() const { return m_projects; }
+
+ using QueuedErrors = MutexData<std::vector<ErrorInfo>, std::mutex>;
+ void addQueuedError(const ErrorInfo &error);
+ QueuedErrors::UniqueConstGuard queuedErrors() const { return m_queuedErrors.lock(); }
+
+ void setProfileConfigs(const QVariantMap &profileConfigs) { m_profileConfigs = profileConfigs; }
+ void addProfileConfig(const QString &profileName, const QVariantMap &profileConfig);
+ const QVariantMap &profileConfigs() const { return m_profileConfigs; }
+ std::optional<QVariantMap> profileConfig(const QString &profileName) const;
+
+ void addProduct(ProductContext &product);
+ void addProductByType(ProductContext &product, const FileTags &tags);
+ ProductContext *productWithNameAndConstraint(
+ const QString &name, const std::function<bool(ProductContext &)> &constraint);
+ std::vector<ProductContext *> productsWithNameAndConstraint(
+ const QString &name, const std::function<bool(ProductContext &)> &constraint);
+ std::vector<ProductContext *> productsWithTypeAndConstraint(
+ const FileTags &tags, const std::function<bool(ProductContext &)> &constraint);
+ std::vector<std::pair<ProductContext *, CodeLocation>>
+ finishedProductsWithBulkDependency(const FileTag &tag) const;
+ void registerBulkDependencies(ProductContext &product);
+
+ void addProjectNameUsedInOverrides(const QString &name);
+ const Set<QString> &projectNamesUsedInOverrides() const;
+
+ void addProductNameUsedInOverrides(const QString &name);
+ const Set<QString> &productNamesUsedInOverrides() const;
+
+ void setBuildDirectory(const QString &buildDir) { m_buildDirectory = buildDir; }
+ const QString &buildDirectory() const { return m_buildDirectory; }
+
+ void addMultiplexConfiguration(const QString &id, const QVariantMap &config);
+ QVariantMap multiplexConfiguration(const QString &id) const;
+
+ void setLastResolveTime(const FileTime &time) { m_lastResolveTime = time; }
+ const FileTime &lastResolveTime() const { return m_lastResolveTime; }
+
+ Set<QString> buildSystemFiles() const { return m_itemReaderCache.filesRead(); }
+
+ std::lock_guard<std::mutex> moduleProvidersCacheLock();
+ void setModuleProvidersCache(const ModuleProvidersCache &cache);
+ const ModuleProvidersCache &moduleProvidersCache() const { return m_moduleProvidersCache; }
+ ModuleProviderInfo *moduleProvider(const ModuleProvidersCacheKey &key);
+ ModuleProviderInfo &addModuleProvider(const ModuleProvidersCacheKey &key,
+ const ModuleProviderInfo &provider);
+
+ void addParameterDeclarations(const Item *moduleProto,
+ const Item::PropertyDeclarationMap &decls);
+ Item::PropertyDeclarationMap parameterDeclarations(Item *moduleProto) const;
+
+ void setParameters(const Item *moduleProto, const QVariantMap &parameters);
+ QVariantMap parameters(Item *moduleProto) const;
+
+ void addCodeLink(const QString &sourceFile, const CodeRange &sourceRange,
+ const CodeLocation &target);
+ CodeLinks codeLinks() const { return m_codeLinks.lock().get(); }
+
+ // An empty string means no matching module directory was found.
+ QString findModuleDirectory(const QualifiedId &module, const QString &searchPath,
+ const std::function<QString()> &findOnDisk);
+
+ QStringList getModuleFilesForDirectory(const QString &dir,
+ const std::function<QStringList()> &findOnDisk);
+ void removeModuleFileFromDirectoryCache(const QString &filePath);
+
+ void addUnknownProfilePropertyError(const Item *moduleProto, const ErrorInfo &error);
+ const std::vector<ErrorInfo> &unknownProfilePropertyErrors(const Item *moduleProto) const;
+
+ Item *getModulePrototype(const QString &filePath, const QString &profile,
+ const std::function<Item *()> &produce);
+
+ void addLocalProfile(const QString &name, const QVariantMap &values,
+ const CodeLocation &location);
+ const QVariantMap localProfiles() { return m_localProfiles; }
+ void checkForLocalProfileAsTopLevelProfile(const QString &topLevelProfile);
+
+ using ProbeFilter = std::function<bool(const ProbeConstPtr &)>;
+ std::lock_guard<std::mutex> probesCacheLock();
+ void setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes);
+ void setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes);
+ void addNewlyResolvedProbe(const ProbeConstPtr &probe);
+ void addProjectLevelProbe(const ProbeConstPtr &probe);
+ const std::vector<ProbeConstPtr> projectLevelProbes() const;
+ ProbeConstPtr findOldProjectProbe(const QString &id, const ProbeFilter &filter) const;
+ ProbeConstPtr findOldProductProbe(const QString &productName, const ProbeFilter &filter) const;
+ ProbeConstPtr findCurrentProbe(const CodeLocation &location, const ProbeFilter &filter) const;
+ void incrementProbesCount() { ++m_probesInfo.probesEncountered; }
+ void incrementReusedCurrentProbesCount() { ++m_probesInfo.probesCachedCurrent; }
+ void incrementReusedOldProbesCount() { ++m_probesInfo.probesCachedOld; }
+ void incrementRunProbesCount() { ++m_probesInfo.probesRun; }
+ int probesEncounteredCount() const { return m_probesInfo.probesEncountered; }
+ int probesRunCount() const { return m_probesInfo.probesRun; }
+ int reusedOldProbesCount() const { return m_probesInfo.probesCachedOld; }
+ int reusedCurrentProbesCount() const { return m_probesInfo.probesCachedCurrent; }
+
+ TimingData &timingData() { return m_timingData; }
+ ItemReaderCache &itemReaderCache() { return m_itemReaderCache; }
+
+ void incProductDeferrals() { ++m_productDeferrals; }
+ int productDeferrals() const { return m_productDeferrals; }
+
+ void collectDataFromEngine(const ScriptEngine &engine);
+
+ ItemPool &createItemPool();
+
+private:
+ const ResolvedFileContextPtr &resolvedFileContext(const FileContextConstPtr &ctx);
+
+ std::vector<ProjectContext *> m_projects;
+ GuardedData<Set<const ProductContext *>> m_productsToHandle;
+ std::multimap<QString, ProductContext *> m_productsByName;
+ MutexData<std::unordered_map<QStringView, QString>, std::mutex> m_sourceCode;
+ std::unordered_map<QString, QVariantMap> m_multiplexConfigsById;
+ MutexData<QHash<CodeLocation, ScriptFunctionPtr>, std::mutex> m_scriptFunctionMap;
+ MutexData<std::unordered_map<std::pair<QStringView, QStringList>, QString>,
+ std::mutex> m_scriptFunctions;
+ std::unordered_map<FileContextConstPtr, ResolvedFileContextPtr> m_fileContextMap;
+ Set<QString> m_projectNamesUsedInOverrides;
+ Set<QString> m_productNamesUsedInOverrides;
+ MutexData<Set<const Item *>> m_disabledItems;
+ QueuedErrors m_queuedErrors;
+ QString m_buildDirectory;
+ QVariantMap m_profileConfigs;
+ ProgressObserver *m_progressObserver = nullptr;
+ TimingData m_timingData;
+ ModuleProvidersCache m_moduleProvidersCache;
+ std::mutex m_moduleProvidersCacheMutex;
+ QVariantMap m_localProfiles;
+ ItemReaderCache m_itemReaderCache;
+ QHash<FileTag, std::vector<std::pair<ProductContext *, CodeLocation>>> m_reverseBulkDependencies;
+
+ // For fast look-up when resolving Depends.productTypes.
+ // The contract is that it contains fully handled, error-free, enabled products.
+ MutexData<std::multimap<FileTag, ProductContext *>> m_productsByType;
+
+ // The keys are module prototypes.
+ MutexData<std::unordered_map<const Item *,
+ Item::PropertyDeclarationMap>> m_parameterDeclarations;
+ MutexData<std::unordered_map<const Item *, QVariantMap>> m_parameters;
+ MutexData<std::unordered_map<const Item *,
+ std::vector<ErrorInfo>>> m_unknownProfilePropertyErrors;
+
+ // The keys are search path + module name, the values are directories.
+ MutexData<QHash<std::pair<QString, QualifiedId>, std::optional<QString>>,
+ std::mutex> m_modulePathCache;
+
+ // The keys are file paths, the values are module prototype items accompanied by a profile.
+ MutexData<std::unordered_map<QString, std::vector<std::pair<Item *, QString>>>,
+ std::mutex> m_modulePrototypes;
+
+ MutexData<std::map<QString, std::optional<QStringList>>,
+ std::mutex> m_moduleFilesPerDirectory;
+ MutexData<CodeLinks> m_codeLinks;
+
+ struct {
+ QHash<QString, std::vector<ProbeConstPtr>> oldProjectProbes;
+ QHash<QString, std::vector<ProbeConstPtr>> oldProductProbes;
+ QHash<CodeLocation, std::vector<ProbeConstPtr>> currentProbes;
+ std::vector<ProbeConstPtr> projectLevelProbes;
+
+ quint64 probesEncountered = 0;
+ quint64 probesRun = 0;
+ quint64 probesCachedCurrent = 0;
+ quint64 probesCachedOld = 0;
+ } m_probesInfo;
+ std::mutex m_probesMutex;
+
+ std::vector<std::unique_ptr<ItemPool>> m_itemPools;
+
+ FileTime m_lastResolveTime;
+
+ std::atomic_bool m_canceled = false;
+ int m_productDeferrals = 0;
+};
+
+class ProjectContext
+{
+public:
+ QString name;
+ Item *item = nullptr;
+ Item *scope = nullptr;
+ TopLevelProjectContext *topLevelProject = nullptr;
+ ProjectContext *parent = nullptr;
+ std::vector<ProjectContext *> children;
+ std::vector<ProductContext> products;
+ std::vector<QStringList> searchPathsStack;
+ ResolvedProjectPtr project;
+ std::vector<FileTaggerConstPtr> fileTaggers;
+ std::vector<RulePtr> rules;
+ JobLimits jobLimits;
+ ResolvedModulePtr dummyModule;
+};
+
+class ModuleContext
+{
+public:
+ ResolvedModulePtr module;
+ JobLimits jobLimits;
+};
+
+class LoaderState
+{
+public:
+ LoaderState(const SetupProjectParameters &parameters, TopLevelProjectContext &topLevelProject,
+ ItemPool &itemPool, ScriptEngine &engine, Logger logger);
+ ~LoaderState();
+
+ Evaluator &evaluator();
+ ItemPool &itemPool();
+ ItemReader &itemReader();
+ Logger &logger();
+ const SetupProjectParameters &parameters() const;
+ TopLevelProjectContext &topLevelProject();
+
+private:
+ class Private;
+ Pimpl<Private> d;
+};
+
+// List must be sorted by priority in ascending order.
+[[nodiscard]] QVariantMap mergeDependencyParameters(
+ std::vector<Item::Module::ParametersWithPriority> &&candidates);
+[[nodiscard]] QVariantMap mergeDependencyParameters(const QVariantMap &m1, const QVariantMap &m2);
+
+QString fullProductDisplayName(const QString &name, const QString &multiplexId);
+void adjustParametersScopes(Item *item, Item *scope);
+void resolveRule(LoaderState &state, Item *item, ProjectContext *projectContext,
+ ProductContext *productContext, ModuleContext *moduleContext);
+void resolveJobLimit(LoaderState &state, Item *item, ProjectContext *projectContext,
+ ProductContext *productContext, ModuleContext *moduleContext);
+void resolveFileTagger(LoaderState &state, Item *item, ProjectContext *projectContext,
+ ProductContext *productContext);
+const FileTag unknownFileTag();
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/loader/localprofiles.cpp b/src/lib/corelib/loader/localprofiles.cpp
new file mode 100644
index 000000000..e72128fff
--- /dev/null
+++ b/src/lib/corelib/loader/localprofiles.cpp
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "localprofiles.h"
+
+#include "loaderutils.h"
+
+#include <language/evaluator.h>
+#include <language/item.h>
+#include <language/qualifiedid.h>
+#include <language/scriptengine.h>
+#include <language/value.h>
+#include <logging/translator.h>
+#include <tools/profile.h>
+#include <tools/scripttools.h>
+#include <tools/stringconstants.h>
+
+namespace qbs::Internal {
+class LocalProfiles
+{
+public:
+ LocalProfiles(LoaderState &loaderState) : m_loaderState(loaderState) {}
+
+ void collectProfiles(Item *productOrProject, Item *projectScope);
+
+private:
+ void handleProfile(Item *profileItem);
+ void evaluateProfileValues(const QualifiedId &namePrefix, Item *item, Item *profileItem,
+ QVariantMap &values);
+ LoaderState &m_loaderState;
+};
+
+void collectProfilesFromItems(Item *productOrProject, Item *projectScope, LoaderState &loaderState)
+{
+ LocalProfiles(loaderState).collectProfiles(productOrProject, projectScope);
+}
+
+void LocalProfiles::handleProfile(Item *profileItem)
+{
+ QVariantMap values;
+ evaluateProfileValues(QualifiedId(), profileItem, profileItem, values);
+ const bool condition = values.take(StringConstants::conditionProperty()).toBool();
+ if (!condition)
+ return;
+ const QString profileName = values.take(StringConstants::nameProperty()).toString();
+ if (profileName.isEmpty())
+ throw ErrorInfo(Tr::tr("Every Profile item must have a name"), profileItem->location());
+ if (profileName == Profile::fallbackName()) {
+ throw ErrorInfo(Tr::tr("Reserved name '%1' cannot be used for an actual profile.")
+ .arg(profileName), profileItem->location());
+ }
+ m_loaderState.topLevelProject().addLocalProfile(profileName, values, profileItem->location());
+}
+
+void LocalProfiles::evaluateProfileValues(const QualifiedId &namePrefix, Item *item,
+ Item *profileItem, QVariantMap &values)
+{
+ const Item::PropertyMap &props = item->properties();
+ for (auto it = props.begin(); it != props.end(); ++it) {
+ QualifiedId name = namePrefix;
+ name << it.key();
+ switch (it.value()->type()) {
+ case Value::ItemValueType:
+ evaluateProfileValues(name, std::static_pointer_cast<ItemValue>(it.value())->item(),
+ profileItem, values);
+ break;
+ case Value::VariantValueType:
+ values.insert(name.join(QLatin1Char('.')),
+ std::static_pointer_cast<VariantValue>(it.value())->value());
+ break;
+ case Value::JSSourceValueType:
+ if (item != profileItem)
+ item->setScope(profileItem);
+ const ScopedJsValue sv(m_loaderState.evaluator().engine()->context(),
+ m_loaderState.evaluator().value(item, it.key()));
+ values.insert(name.join(QLatin1Char('.')),
+ getJsVariant(m_loaderState.evaluator().engine()->context(), sv));
+ break;
+ }
+ }
+}
+
+void LocalProfiles::collectProfiles(Item *productOrProject, Item *projectScope)
+{
+ Item * scope = productOrProject->type() == ItemType::Project ? projectScope : nullptr;
+ for (auto it = productOrProject->children().begin();
+ it != productOrProject->children().end();) {
+ Item * const childItem = *it;
+ if (childItem->type() == ItemType::Profile) {
+ if (!scope) {
+ const ItemValuePtr itemValue = ItemValue::create(productOrProject);
+ scope = Item::create(&m_loaderState.itemPool(), ItemType::Scope);
+ scope->setProperty(StringConstants::productVar(), itemValue);
+ scope->setFile(productOrProject->file());
+ scope->setScope(projectScope);
+ }
+ childItem->setScope(scope);
+ try {
+ handleProfile(childItem);
+ } catch (const ErrorInfo &e) {
+ handlePropertyError(e, m_loaderState.parameters(), m_loaderState.logger());
+ }
+ it = productOrProject->children().erase(it); // TODO: delete item and scope
+ } else {
+ if (childItem->type() == ItemType::Product)
+ collectProfiles(childItem, projectScope);
+ ++it;
+ }
+ }
+}
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/localprofiles.h b/src/lib/corelib/loader/localprofiles.h
new file mode 100644
index 000000000..7d9eee88e
--- /dev/null
+++ b/src/lib/corelib/loader/localprofiles.h
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+namespace qbs::Internal {
+class Item;
+class LoaderState;
+
+void collectProfilesFromItems(Item *productOrProject, Item *projectScope, LoaderState &loaderState);
+
+} // namespace qbs::Internal
+
diff --git a/src/lib/corelib/loader/moduleinstantiator.cpp b/src/lib/corelib/loader/moduleinstantiator.cpp
new file mode 100644
index 000000000..1c0359217
--- /dev/null
+++ b/src/lib/corelib/loader/moduleinstantiator.cpp
@@ -0,0 +1,340 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "moduleinstantiator.h"
+
+#include "loaderutils.h"
+#include "modulepropertymerger.h"
+
+#include <language/item.h>
+#include <language/itempool.h>
+#include <language/qualifiedid.h>
+#include <language/value.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/profiling.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+
+#include <utility>
+
+namespace qbs::Internal {
+
+static std::pair<const Item *, Item *>
+getOrSetModuleInstanceItem(Item *container, Item *moduleItem, const QualifiedId &moduleName,
+ const QString &id, bool replace, LoaderState &loaderState);
+
+class ModuleInstantiator
+{
+public:
+ ModuleInstantiator(const InstantiationContext &context, LoaderState &loaderState)
+ : context(context), loaderState(loaderState) {}
+
+ void instantiate();
+
+private:
+ void overrideProperties();
+ void setupScope();
+ void exchangePlaceholderItem(Item *loadingItem, Item *moduleItemForItemValues);
+
+ const InstantiationContext &context;
+ LoaderState &loaderState;
+};
+
+void ModuleInstantiator::instantiate()
+{
+ AccumulatingTimer timer(loaderState.parameters().logElapsedTime()
+ ? &context.product.timingData.moduleInstantiation : nullptr);
+
+ // This part needs to be done only once per module and product, and only if the module
+ // was successfully loaded.
+ if (context.module && !context.alreadyLoaded) {
+ context.module->setType(ItemType::ModuleInstance);
+ overrideProperties();
+ setupScope();
+ }
+
+ // This strange-looking code deals with the fact that our syntax cannot properly handle
+ // dependencies on several multiplexed variants of the same product.
+ // See getOrSetModuleInstanceItem() below for details.
+ Item * const moduleItemForItemValues
+ = context.moduleWithSameName ? context.moduleWithSameName
+ : context.module;
+
+ // Now attach the module instance as an item value to the loading item, potentially
+ // evicting a previously attached placeholder item and merging its values into the instance.
+ // Note that we potentially do this twice, once for the actual loading item and once
+ // for the product item, if the two are different. The reason is this:
+ // For convenience, in the product item we allow attaching to properties from indirectly
+ // loaded modules. For instance:
+ // Product {
+ // Depends { name: "Qt.core" }
+ // cpp.cxxLanguageVersion: "c++20" // Works even though there is no Depends item for cpp
+ // }
+ // It's debatable whether that's a good feature, but it has been working (accidentally?)
+ // for a long time, and removing it now would break a lot of existing projects.
+ exchangePlaceholderItem(context.loadingItem, moduleItemForItemValues);
+
+ if (!context.alreadyLoaded && context.product.item
+ && context.product.item != context.loadingItem) {
+ exchangePlaceholderItem(context.product.item, moduleItemForItemValues);
+ }
+}
+
+void ModuleInstantiator::exchangePlaceholderItem(Item *loadingItem, Item *moduleItemForItemValues)
+{
+ // If we have a module item, set an item value pointing to it as a property on the loading item.
+ // Evict a possibly existing placeholder item, and return it to us, so we can merge its values
+ // into the instance.
+ const auto &[oldItem, newItem] = getOrSetModuleInstanceItem(
+ loadingItem, moduleItemForItemValues, context.moduleName, context.id, true, loaderState);
+
+ // The new item always exists, even if we don't have a module item. In that case, the
+ // function created a placeholder item for us, which we then have to turn into a
+ // non-present module.
+ QBS_CHECK(newItem);
+ if (!moduleItemForItemValues) {
+ createNonPresentModule(loaderState.itemPool(), context.moduleName.toString(),
+ QLatin1String("not found"), newItem);
+ return;
+ }
+
+ if (!moduleItemForItemValues->isPresentModule())
+ return;
+
+ // This will yield false negatives for the case where there is an invalid property attached
+ // for a module that is actually found by pkg-config via the fallback provider.
+ // However, this is extremely rare compared to the case where the presence of the fallback
+ // module simply indicates "not present".
+ if (moduleItemForItemValues->isFallbackModule())
+ return;
+
+ // If the old and the new items are the same, it means the existing item value already
+ // pointed to a module instance (rather than a placeholder).
+ // This can happen in two cases:
+ // a) Multiple identical Depends items on the same level (easily possible with inheritance).
+ // b) Dependencies to multiplexed variants of the same product
+ // (see getOrSetModuleInstanceItem() below for details).
+ if (oldItem == newItem) {
+ QBS_CHECK(oldItem->type() == ItemType::ModuleInstance);
+ QBS_CHECK(context.alreadyLoaded || context.exportingProduct);
+ return;
+ }
+
+ // In all other cases, our request to set the module instance item must have been honored.
+ QBS_CHECK(newItem == moduleItemForItemValues);
+
+ // If there was no placeholder item, we don't have to merge any values and are done.
+ if (!oldItem)
+ return;
+
+ // If an item was replaced, then it must have been a placeholder.
+ QBS_CHECK(oldItem->type() == ItemType::ModuleInstancePlaceholder);
+
+ // Prevent setting read-only properties.
+ for (auto it = oldItem->properties().cbegin(); it != oldItem->properties().cend(); ++it) {
+ const PropertyDeclaration &pd = moduleItemForItemValues->propertyDeclaration(it.key());
+ if (pd.flags().testFlag(PropertyDeclaration::ReadOnlyFlag)) {
+ throw ErrorInfo(Tr::tr("Cannot set read-only property '%1'.").arg(pd.name()),
+ it.value()->location());
+ }
+ }
+
+ // Now merge the locally attached values into the actual module instance.
+ mergeFromLocalInstance(context.product, loadingItem, context.loadingName, oldItem,
+ moduleItemForItemValues, loaderState);
+
+ // TODO: We'd like to delete the placeholder item here, because it's not
+ // being referenced anymore and there's a lot of them. However, this
+ // is not supported by ItemPool. Investigate the use of std::pmr.
+}
+
+Item *retrieveModuleInstanceItem(Item *containerItem, const QualifiedId &name,
+ LoaderState &loaderState)
+{
+ return getOrSetModuleInstanceItem(containerItem, nullptr, name, {}, false, loaderState).second;
+}
+
+Item *retrieveQbsItem(Item *containerItem, LoaderState &loaderState)
+{
+ return retrieveModuleInstanceItem(containerItem, StringConstants::qbsModule(), loaderState);
+}
+
+void ModuleInstantiator::overrideProperties()
+{
+ // Users can override module properties on the command line with the
+ // modules.<module-name>.<property-name>:<value> syntax.
+ // For simplicity and backwards compatibility, qbs properties can also be given without
+ // the "modules." prefix, i.e. just qbs.<property-name>:<value>.
+ // In addition, users can override module properties just for certain products
+ // using the products.<product-name>.<module-name>.<property-name>:<value> syntax.
+ // Such product-specific overrides have higher precedence.
+ const QString fullName = context.moduleName.toString();
+ const QString generalOverrideKey = QStringLiteral("modules.") + fullName;
+ const QString perProductOverrideKey = StringConstants::productsOverridePrefix()
+ + context.product.name + QLatin1Char('.') + fullName;
+ const SetupProjectParameters &parameters = loaderState.parameters();
+ Logger &logger = loaderState.logger();
+ context.module->overrideProperties(parameters.overriddenValuesTree(), generalOverrideKey,
+ parameters, logger);
+ if (fullName == StringConstants::qbsModule()) {
+ context.module->overrideProperties(parameters.overriddenValuesTree(), fullName, parameters,
+ logger);
+ }
+ context.module->overrideProperties(parameters.overriddenValuesTree(), perProductOverrideKey,
+ parameters, logger);
+}
+
+void ModuleInstantiator::setupScope()
+{
+ Item * const scope = Item::create(&loaderState.itemPool(), ItemType::Scope);
+ QBS_CHECK(context.module->file());
+ scope->setFile(context.module->file());
+ QBS_CHECK(context.product.project->scope);
+ context.product.project->scope->copyProperty(StringConstants::projectVar(), scope);
+ if (context.product.scope)
+ context.product.scope->copyProperty(StringConstants::productVar(), scope);
+ else
+ QBS_CHECK(context.moduleName.toString() == StringConstants::qbsModule()); // Dummy product.
+
+ if (!context.module->id().isEmpty())
+ scope->setProperty(context.module->id(), ItemValue::create(context.module));
+ for (Item * const child : context.module->children()) {
+ child->setScope(scope);
+ if (!child->id().isEmpty())
+ scope->setProperty(child->id(), ItemValue::create(child));
+ }
+ context.module->setScope(scope);
+
+ if (context.exportingProduct) {
+ QBS_CHECK(context.exportingProduct->type() == ItemType::Product);
+
+ const auto exportingProductItemValue = ItemValue::create(context.exportingProduct);
+ scope->setProperty(QStringLiteral("exportingProduct"), exportingProductItemValue);
+
+ const auto importingProductItemValue = ItemValue::create(context.product.item);
+ scope->setProperty(QStringLiteral("importingProduct"), importingProductItemValue);
+
+ // FIXME: This looks wrong. Introduce exportingProject variable?
+ scope->setProperty(StringConstants::projectVar(),
+ ItemValue::create(context.exportingProduct->parent()));
+
+ PropertyDeclaration pd(StringConstants::qbsSourceDirPropertyInternal(),
+ PropertyDeclaration::String, QString(),
+ PropertyDeclaration::PropertyNotAvailableInConfig);
+ context.module->setPropertyDeclaration(pd.name(), pd);
+ context.module->setProperty(pd.name(), context.exportingProduct->property(
+ StringConstants::sourceDirectoryProperty()));
+ }
+}
+
+void instantiateModule(const InstantiationContext &context, LoaderState &loaderState)
+{
+ ModuleInstantiator(context, loaderState).instantiate();
+}
+
+// This important function deals with retrieving and setting (pseudo-)module instances from/on
+// items.
+// Use case 1: Suppose we resolve the dependency cpp in module Qt.core, which also contains
+// property bindings for cpp such as "cpp.defines: [...]".
+// The "cpp" part of this binding is represented by an ItemValue whose
+// item is of type ModuleInstancePlaceholder, originally set up by ItemReaderASTVisitor.
+// This function will be called with the actual cpp module item and will
+// replace the placeholder item in the item value. It will also return
+// the placeholder item for subsequent merging of its properties with the
+// ones of the actual module (in ModulePropertyMerger::mergeFromLocalInstance()).
+// If there were no cpp property bindings defined in Qt.core, then we'd still
+// have to replace the placeholder item, because references to "cpp" on the
+// right-hand-side of other properties must refer to the module item.
+// This is the common use of this function as employed by resolveProdsucDependencies().
+// Note that if a product has dependencies on more than one variant of a multiplexed
+// product, these dependencies are competing for the item value property name,
+// i.e. this case is not properly supported by the syntax. You must therefore not
+// export properties from multiplexed products that will be different between the
+// variants. In this function, the condition manifests itself by a module instance
+// being encountered instead of a module instance placeholder, in which case
+// nothing is done at all.
+// Use case 2: We inject a fake qbs module into a project item, so qbs properties set in profiles
+// can be accessed in the project level. Doing this is discouraged, and the
+// functionality is kept mostly for backwards compatibility. The moduleItem
+// parameter is null in this case, and the item will be created by the function itself.
+// Use case 3: A temporary qbs module is attached to a product during low-level setup, namely
+// in product multiplexing and setting qbs.profile.
+// Use case 4: Module propagation to the the Group level.
+// In all cases, the first returned item is the existing one, and the second returned item
+// is the new one. Depending on the use case, they might be null and might also be the same item.
+std::pair<const Item *, Item *> getOrSetModuleInstanceItem(
+ Item *container, Item *moduleItem, const QualifiedId &moduleName, const QString &id,
+ bool replace, LoaderState &loaderState)
+{
+ Item *instance = container;
+ const QualifiedId itemValueName
+ = !id.isEmpty() ? QualifiedId::fromString(id) : moduleName;
+ for (int i = 0; i < itemValueName.size(); ++i) {
+ const QString &moduleNameSegment = itemValueName.at(i);
+ const ValuePtr v = instance->ownProperty(itemValueName.at(i));
+ if (v && v->type() == Value::ItemValueType) {
+ ItemValue * const itemValue = std::static_pointer_cast<ItemValue>(v).get();
+ instance = itemValue->item();
+ if (i == itemValueName.size() - 1) {
+ if (replace && instance != moduleItem
+ && instance->type() == ItemType::ModuleInstancePlaceholder) {
+ if (!moduleItem) {
+ moduleItem = Item::create(&loaderState.itemPool(),
+ ItemType::ModuleInstancePlaceholder);
+ }
+ itemValue->setItem(moduleItem);
+ }
+ return {instance, itemValue->item()};
+ }
+ } else {
+ Item *newItem = i < itemValueName.size() - 1
+ ? Item::create(&loaderState.itemPool(), ItemType::ModulePrefix)
+ : moduleItem
+ ? moduleItem
+ : Item::create(&loaderState.itemPool(),
+ ItemType::ModuleInstancePlaceholder);
+ instance->setProperty(moduleNameSegment, ItemValue::create(newItem));
+ instance = newItem;
+ }
+ }
+ return {nullptr, instance};
+}
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/moduleinstantiator.h b/src/lib/corelib/loader/moduleinstantiator.h
new file mode 100644
index 000000000..5e9ad2f44
--- /dev/null
+++ b/src/lib/corelib/loader/moduleinstantiator.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+namespace qbs::Internal {
+class Item;
+class LoaderState;
+class ProductContext;
+class QualifiedId;
+
+class InstantiationContext {
+public:
+ ProductContext &product;
+ Item * const loadingItem;
+ const QString &loadingName;
+ Item * const module;
+ Item * const moduleWithSameName;
+ Item * const exportingProduct;
+ const QualifiedId &moduleName;
+ const QString &id;
+ const bool alreadyLoaded;
+};
+
+// This function is responsible for setting up a proper module instance from a bunch of items:
+// - Set the item type to ItemType::ModuleInstance (from Module or Export).
+// - Apply possible command-line overrides for module properties.
+// - Replace a possible module instance placeholder in the loading item with the actual instance
+// and merge their values employing the ModulePropertyMerger.
+// - Setting up the module instance scope.
+void instantiateModule(const InstantiationContext &context, LoaderState &loaderState);
+
+// Helper functions for retrieving/setting module instance items for special purposes.
+// Note that these will also create the respective item value if it does not exist yet.
+Item *retrieveModuleInstanceItem(Item *containerItem, const QualifiedId &name,
+ LoaderState &loaderState);
+Item *retrieveQbsItem(Item *containerItem, LoaderState &loaderState);
+
+} // namespace qbs::Internal
+
diff --git a/src/lib/corelib/loader/moduleloader.cpp b/src/lib/corelib/loader/moduleloader.cpp
new file mode 100644
index 000000000..d26849655
--- /dev/null
+++ b/src/lib/corelib/loader/moduleloader.cpp
@@ -0,0 +1,410 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "itemreader.h"
+#include "loaderutils.h"
+#include "moduleproviderloader.h"
+
+#include <api/languageinfo.h>
+#include <language/evaluator.h>
+#include <language/scriptengine.h>
+#include <language/value.h>
+#include <logging/categories.h>
+#include <logging/translator.h>
+#include <tools/codelocation.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/hostosinfo.h>
+#include <tools/profiling.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+
+#include <QDirIterator>
+#include <QHash>
+
+#include <unordered_map>
+#include <utility>
+
+namespace qbs::Internal {
+
+class ModuleLoader
+{
+public:
+ ModuleLoader(
+ LoaderState &loaderState,
+ ProductContext &product,
+ const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName)
+ : m_loaderState(loaderState)
+ , m_product(product)
+ , m_dependsItemLocation(dependsItemLocation)
+ , m_moduleName(moduleName)
+ {}
+
+ Item *load();
+
+private:
+ std::pair<Item *, bool> loadModuleFile(const QString &moduleName, const QString &filePath);
+ Item *getModulePrototype(const QString &moduleName, const QString &filePath);
+ Item *createAndInitModuleItem(const QString &moduleName, const QString &filePath);
+ bool evaluateModuleCondition(Item *module, const QString &fullModuleName);
+ void checkForUnknownProfileProperties(const Item *module);
+ QString findModuleDirectory(const QString &searchPath);
+ QStringList findModuleDirectories();
+ QStringList getModuleFilePaths(const QString &dir);
+
+ LoaderState &m_loaderState;
+ ProductContext &m_product;
+ const CodeLocation &m_dependsItemLocation;
+ const QualifiedId &m_moduleName;
+};
+
+struct PrioritizedItem
+{
+ PrioritizedItem(Item *item, int priority, int searchPathIndex)
+ : item(item), priority(priority), searchPathIndex(searchPathIndex) { }
+
+ Item * const item;
+ int priority = 0;
+ const int searchPathIndex;
+};
+
+static Item *chooseModuleCandidate(const std::vector<PrioritizedItem> &candidates,
+ const QString &moduleName)
+{
+ // TODO: This should also consider the version requirement.
+
+ auto maxIt = std::max_element(
+ candidates.begin(), candidates.end(),
+ [] (const PrioritizedItem &a, const PrioritizedItem &b) {
+ if (a.priority < b.priority)
+ return true;
+ if (a.priority > b.priority)
+ return false;
+ return a.searchPathIndex > b.searchPathIndex;
+ });
+
+ size_t nmax = std::count_if(
+ candidates.begin(), candidates.end(),
+ [maxIt] (const PrioritizedItem &i) {
+ return i.priority == maxIt->priority && i.searchPathIndex == maxIt->searchPathIndex;
+ });
+
+ if (nmax > 1) {
+ ErrorInfo e(Tr::tr("There is more than one equally prioritized candidate for module '%1'.")
+ .arg(moduleName));
+ for (size_t i = 0; i < candidates.size(); ++i) {
+ const auto candidate = candidates.at(i);
+ if (candidate.priority == maxIt->priority) {
+ //: The %1 denotes the number of the candidate.
+ e.append(Tr::tr("candidate %1").arg(i + 1), candidates.at(i).item->location());
+ }
+ }
+ throw e;
+ }
+
+ return maxIt->item;
+}
+
+Item *searchAndLoadModuleFile(
+ LoaderState &loaderState,
+ ProductContext &product,
+ const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName)
+{
+ return ModuleLoader(loaderState, product, dependsItemLocation, moduleName).load();
+}
+
+Item *ModuleLoader::load()
+{
+ SearchPathsManager searchPathsManager(m_loaderState.itemReader());
+
+ QStringList existingPaths = findModuleDirectories();
+ if (existingPaths.isEmpty()) { // no suitable names found, try to use providers
+ AccumulatingTimer providersTimer(m_loaderState.parameters().logElapsedTime()
+ ? &m_product.timingData.moduleProviders : nullptr);
+ auto result = ModuleProviderLoader(m_loaderState)
+ .executeModuleProviders(m_product, m_dependsItemLocation, m_moduleName);
+ if (result.searchPaths) {
+ qCDebug(lcModuleLoader) << "Re-checking for module" << m_moduleName.toString()
+ << "with newly added search paths from module provider";
+ m_loaderState.itemReader().pushExtraSearchPaths(*result.searchPaths);
+ existingPaths = findModuleDirectories();
+ }
+ }
+
+ const QString fullName = m_moduleName.toString();
+ bool triedToLoadModule = false;
+ std::vector<PrioritizedItem> candidates;
+ candidates.reserve(size_t(existingPaths.size()));
+ for (int i = 0; i < existingPaths.size(); ++i) {
+ const QStringList &moduleFileNames = getModuleFilePaths(existingPaths.at(i));
+ for (const QString &filePath : moduleFileNames) {
+ const auto [module, triedToLoad] = loadModuleFile(fullName, filePath);
+ if (module)
+ candidates.emplace_back(module, 0, i);
+ if (!triedToLoad)
+ m_loaderState.topLevelProject().removeModuleFileFromDirectoryCache(filePath);
+ triedToLoadModule = triedToLoadModule || triedToLoad;
+ }
+ }
+
+ if (candidates.empty())
+ return nullptr;
+
+ Item *moduleItem = nullptr;
+ if (candidates.size() == 1) {
+ moduleItem = candidates.at(0).item;
+ } else {
+ for (auto &candidate : candidates) {
+ ModuleItemLocker lock(*candidate.item);
+ candidate.priority = m_loaderState.evaluator()
+ .intValue(candidate.item, StringConstants::priorityProperty(),
+ candidate.priority);
+ }
+ moduleItem = chooseModuleCandidate(candidates, fullName);
+ }
+
+ checkForUnknownProfileProperties(moduleItem);
+ return moduleItem;
+}
+
+std::pair<Item *, bool> ModuleLoader::loadModuleFile(const QString &moduleName,
+ const QString &filePath)
+{
+ qCDebug(lcModuleLoader) << "loadModuleFile" << moduleName << "from" << filePath;
+
+ Item * const module = getModulePrototype(moduleName, filePath);
+ if (!module)
+ return {nullptr, false};
+
+ const auto it = m_product.modulePrototypeEnabledInfo.find(module);
+ if (it != m_product.modulePrototypeEnabledInfo.end()) {
+ qCDebug(lcModuleLoader) << "prototype cache hit (level 2)";
+ return {it->second ? module : nullptr, true};
+ }
+
+ if (!evaluateModuleCondition(module, moduleName)) {
+ qCDebug(lcModuleLoader) << "condition of module" << moduleName << "is false";
+ m_product.modulePrototypeEnabledInfo.insert({module, false});
+ return {nullptr, true};
+ }
+
+ m_product.modulePrototypeEnabledInfo.insert({module, true});
+ return {module, true};
+}
+
+Item * ModuleLoader::getModulePrototype(const QString &moduleName, const QString &filePath)
+{
+ bool fromCache = true;
+ Item * const module = m_loaderState.topLevelProject().getModulePrototype(
+ filePath, m_product.profileName, [&] {
+ fromCache = false;
+ return createAndInitModuleItem(moduleName, filePath);
+ });
+
+ if (fromCache)
+ qCDebug(lcModuleLoader) << "prototype cache hit (level 1)";
+ return module;
+}
+
+Item *ModuleLoader::createAndInitModuleItem(const QString &moduleName, const QString &filePath)
+{
+ Item * const module = m_loaderState.itemReader().setupItemFromFile(filePath, {});
+ if (module->type() != ItemType::Module) {
+ qCDebug(lcModuleLoader).nospace()
+ << "Alleged module " << moduleName << " has type '"
+ << module->typeName() << "', so it's not a module after all.";
+ return nullptr;
+ }
+
+ // Not technically needed, but we want to keep the invariant in item.cpp.
+ ModuleItemLocker locker(*module);
+
+ module->setProperty(StringConstants::nameProperty(), VariantValue::create(moduleName));
+ if (moduleName == StringConstants::qbsModule()) {
+ module->setProperty(QStringLiteral("hostPlatform"),
+ VariantValue::create(HostOsInfo::hostOSIdentifier()));
+ module->setProperty(QStringLiteral("hostArchitecture"),
+ VariantValue::create(HostOsInfo::hostOSArchitecture()));
+ module->setProperty(QStringLiteral("libexecPath"),
+ VariantValue::create(m_loaderState.parameters().libexecPath()));
+
+ const Version qbsVersion = LanguageInfo::qbsVersion();
+ module->setProperty(QStringLiteral("versionMajor"),
+ VariantValue::create(qbsVersion.majorVersion()));
+ module->setProperty(QStringLiteral("versionMinor"),
+ VariantValue::create(qbsVersion.minorVersion()));
+ module->setProperty(QStringLiteral("versionPatch"),
+ VariantValue::create(qbsVersion.patchLevel()));
+ } else {
+ Item::PropertyDeclarationMap decls;
+ const auto &moduleChildren = module->children();
+ for (Item *param : moduleChildren) {
+ if (param->type() == ItemType::Parameter) {
+ const auto &paramDecls = param->propertyDeclarations();
+ for (auto it = paramDecls.begin(); it != paramDecls.end(); ++it)
+ decls.insert(it.key(), it.value());
+ } else if (param->type() == ItemType::Parameters) {
+ adjustParametersScopes(param, param);
+ Evaluator &evaluator = m_loaderState.evaluator();
+ QVariantMap parameters = getJsVariant(evaluator.engine()->context(),
+ evaluator.scriptValue(param)).toMap();
+ m_loaderState.topLevelProject().setParameters(module, parameters);
+ }
+ }
+ m_loaderState.topLevelProject().addParameterDeclarations(module, decls);
+ }
+
+ // Module properties that are defined in the profile are used as default values.
+ // This is the reason we need to have different items per profile.
+ const QVariantMap profileModuleProperties
+ = m_product.profileModuleProperties.value(moduleName).toMap();
+ for (auto it = profileModuleProperties.cbegin(); it != profileModuleProperties.cend(); ++it) {
+ if (Q_UNLIKELY(!module->hasProperty(it.key()))) {
+ m_loaderState.topLevelProject().addUnknownProfilePropertyError(
+ module, {Tr::tr("Unknown property: %1.%2").arg(moduleName, it.key())});
+ continue;
+ }
+ const PropertyDeclaration decl = module->propertyDeclaration(it.key());
+ VariantValuePtr v = VariantValue::create(
+ PropertyDeclaration::convertToPropertyType(it.value(), decl.type(),
+ QStringList(moduleName), it.key()));
+ v->markAsSetByProfile();
+ module->setProperty(it.key(), v);
+ }
+
+ return module;
+}
+
+bool ModuleLoader::evaluateModuleCondition(Item *module, const QString &fullModuleName)
+{
+ ModuleItemLocker locker(*module);
+
+ // Temporarily make the product's qbs module instance available, so the condition
+ // can use qbs.targetOS etc.
+ class TempQbsModuleProvider {
+ public:
+ TempQbsModuleProvider(const ProductContext &product,
+ Item *module, const QString &moduleName)
+ : m_module(module), m_needsQbsItem(moduleName != StringConstants::qbsModule())
+ {
+ if (m_needsQbsItem) {
+ m_prevQbsItemValue = module->property(StringConstants::qbsModule());
+ module->setProperty(StringConstants::qbsModule(),
+ product.item->property(StringConstants::qbsModule()));
+ }
+ }
+ ~TempQbsModuleProvider()
+ {
+ if (!m_needsQbsItem)
+ return;
+ if (m_prevQbsItemValue)
+ m_module->setProperty(StringConstants::qbsModule(), m_prevQbsItemValue);
+ else
+ m_module->removeProperty(StringConstants::qbsModule());
+ }
+ private:
+ Item * const m_module;
+ ValuePtr m_prevQbsItemValue;
+ const bool m_needsQbsItem;
+ };
+
+ const TempQbsModuleProvider tempQbs(m_product, module, fullModuleName);
+ return m_loaderState.evaluator().boolValue(module, StringConstants::conditionProperty());
+}
+
+void ModuleLoader::checkForUnknownProfileProperties(const Item *module)
+{
+ const std::vector<ErrorInfo> &errors
+ = m_loaderState.topLevelProject().unknownProfilePropertyErrors(module);
+ if (errors.empty())
+ return;
+
+ ErrorInfo error(Tr::tr("Loading module '%1' for product '%2' failed due to invalid values "
+ "in profile '%3':")
+ .arg(m_moduleName.toString(), m_product.displayName(), m_product.profileName));
+ for (const ErrorInfo &e : errors)
+ error.append(e.toString());
+ handlePropertyError(error, m_loaderState.parameters(), m_loaderState.logger());
+}
+
+QString ModuleLoader::findModuleDirectory(const QString &searchPath)
+{
+ // isFileCaseCorrect is a very expensive call on macOS, so we cache the value for the
+ // modules and search paths we've already processed
+ return m_loaderState.topLevelProject().findModuleDirectory(m_moduleName, searchPath,
+ [&] {
+ QString dirPath = searchPath + QStringLiteral("/modules");
+ for (const QString &moduleNamePart : m_moduleName) {
+ dirPath = FileInfo::resolvePath(dirPath, moduleNamePart);
+ if (!FileInfo::exists(dirPath) || !FileInfo::isFileCaseCorrect(dirPath))
+ return QString();
+ }
+ return dirPath;
+ });
+}
+
+QStringList ModuleLoader::findModuleDirectories()
+{
+ const QStringList &searchPaths = m_loaderState.itemReader().allSearchPaths();
+ QStringList result;
+ result.reserve(searchPaths.size());
+ for (const auto &path: searchPaths) {
+ const QString dirPath = findModuleDirectory(path);
+ if (!dirPath.isEmpty())
+ result.append(dirPath);
+ }
+ return result;
+}
+
+QStringList ModuleLoader::getModuleFilePaths(const QString &dir)
+{
+ return m_loaderState.topLevelProject().getModuleFilesForDirectory(dir, [&] {
+ QStringList moduleFiles;
+ QDirIterator dirIter(dir, StringConstants::qbsFileWildcards());
+ while (dirIter.hasNext())
+ moduleFiles += dirIter.next();
+ return moduleFiles;
+ });
+}
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/moduleloader.h b/src/lib/corelib/loader/moduleloader.h
new file mode 100644
index 000000000..b07f01836
--- /dev/null
+++ b/src/lib/corelib/loader/moduleloader.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+namespace qbs {
+class CodeLocation;
+namespace Internal {
+class Item;
+class LoaderState;
+class ProductContext;
+class QualifiedId;
+
+Item *searchAndLoadModuleFile(
+ LoaderState &loaderState,
+ ProductContext &product,
+ const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName);
+
+} // namespace Internal
+} // namespace qbs
+
diff --git a/src/lib/corelib/loader/modulepropertymerger.cpp b/src/lib/corelib/loader/modulepropertymerger.cpp
new file mode 100644
index 000000000..c9dd56ccf
--- /dev/null
+++ b/src/lib/corelib/loader/modulepropertymerger.cpp
@@ -0,0 +1,323 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "modulepropertymerger.h"
+
+#include "loaderutils.h"
+
+#include <language/evaluator.h>
+#include <language/item.h>
+#include <language/value.h>
+#include <logging/translator.h>
+#include <tools/profiling.h>
+#include <tools/set.h>
+#include <tools/setupprojectparameters.h>
+
+#include <unordered_set>
+
+namespace qbs::Internal {
+class ModulePropertyMerger
+{
+public:
+ ModulePropertyMerger(ProductContext &product, LoaderState &loaderState)
+ : m_product(product), m_loaderState(loaderState) {}
+
+ void mergeFromLocalInstance(Item *loadingItem, const QString &loadingName,
+ const Item *localInstance, Item *globalInstance);
+ void doFinalMerge();
+
+private:
+ int compareValuePriorities(const ValueConstPtr &v1, const ValueConstPtr &v2);
+ ValuePtr mergeListValues(const ValuePtr &currentHead, const ValuePtr &newElem);
+ void mergePropertyFromLocalInstance(Item *loadingItem, const QString &loadingName,
+ Item *globalInstance, const QString &name,
+ const ValuePtr &value);
+ bool doFinalMerge(Item *moduleItem);
+ bool doFinalMerge(const PropertyDeclaration &propertyDecl, ValuePtr &propertyValue);
+
+ ProductContext & m_product;
+ LoaderState &m_loaderState;
+};
+
+void mergeFromLocalInstance(ProductContext &product, Item *loadingItem, const QString &loadingName,
+ const Item *localInstance, Item *globalInstance,
+ LoaderState &loaderState)
+{
+ ModulePropertyMerger(product, loaderState).mergeFromLocalInstance(
+ loadingItem, loadingName, localInstance, globalInstance);
+}
+
+void doFinalMerge(ProductContext &product, LoaderState &loaderState)
+{
+ ModulePropertyMerger(product, loaderState).doFinalMerge();
+}
+
+void ModulePropertyMerger::mergeFromLocalInstance(Item *loadingItem, const QString &loadingName,
+ const Item *localInstance, Item *globalInstance)
+{
+ AccumulatingTimer t(m_loaderState.parameters().logElapsedTime()
+ ? &m_product.timingData.propertyMerging : nullptr);
+
+ for (auto it = localInstance->properties().constBegin();
+ it != localInstance->properties().constEnd(); ++it) {
+ mergePropertyFromLocalInstance(loadingItem, loadingName, globalInstance, it.key(),
+ it.value());
+ }
+}
+
+void ModulePropertyMerger::doFinalMerge()
+{
+ AccumulatingTimer t(m_loaderState.parameters().logElapsedTime()
+ ? &m_product.timingData.propertyMerging : nullptr);
+
+ std::unordered_set<const Item *> itemsToInvalidate;
+ for (const Item::Module &module : m_product.item->modules()) {
+ if (doFinalMerge(module.item))
+ itemsToInvalidate.insert(module.item);
+ }
+
+ // For each module item, if it requires invalidation, we also add modules that depend on
+ // that module item, all the way up to the Product item.
+ std::unordered_set<const Item *> visitedItems;
+ const auto collectDependentItems =
+ [&itemsToInvalidate, &visitedItems](const Item *item, const auto &collect) -> bool
+ {
+ const bool alreadyInSet = itemsToInvalidate.count(item);
+ if (!visitedItems.insert(item).second) // item handled already
+ return alreadyInSet;
+ bool addItem = false;
+ for (const Item::Module &m : item->modules()) {
+ if (collect(m.item, collect))
+ addItem = true;
+ }
+ if (addItem && !alreadyInSet)
+ itemsToInvalidate.insert(item);
+ return addItem || alreadyInSet;
+ };
+ collectDependentItems(m_product.item, collectDependentItems);
+ for (const Item * const item : itemsToInvalidate)
+ m_loaderState.evaluator().clearCache(item);
+}
+
+int ModulePropertyMerger::compareValuePriorities(const ValueConstPtr &v1, const ValueConstPtr &v2)
+{
+ QBS_CHECK(v1);
+ QBS_CHECK(v2);
+ QBS_CHECK(v1->scope() != v2->scope());
+ QBS_CHECK(v1->type() == Value::JSSourceValueType || v2->type() == Value::JSSourceValueType);
+
+ const int prio1 = v1->priority(m_product.item);
+ const int prio2 = v2->priority(m_product.item);
+ if (prio1 != prio2)
+ return prio1 - prio2;
+ const int prioDiff = v1->scopeName().compare(v2->scopeName()); // Sic! See 8ff1dd0044
+ QBS_CHECK(prioDiff != 0);
+ return prioDiff;
+}
+
+ValuePtr ModulePropertyMerger::mergeListValues(const ValuePtr &currentHead, const ValuePtr &newElem)
+{
+ QBS_CHECK(newElem);
+ QBS_CHECK(!newElem->next());
+
+ if (!currentHead)
+ return !newElem->expired(m_product.item) ? newElem : newElem->next();
+
+ QBS_CHECK(!currentHead->expired(m_product.item));
+
+ if (newElem->expired(m_product.item))
+ return currentHead;
+
+ if (compareValuePriorities(currentHead, newElem) < 0) {
+ newElem->setNext(currentHead);
+ return newElem;
+ }
+ currentHead->setNext(mergeListValues(currentHead->next(), newElem));
+ return currentHead;
+}
+
+void ModulePropertyMerger::mergePropertyFromLocalInstance(
+ Item *loadingItem, const QString &loadingName, Item *globalInstance,
+ const QString &name, const ValuePtr &value)
+{
+ if (loadingItem->type() == ItemType::Project) {
+ throw ErrorInfo(Tr::tr("Module properties cannot be set in Project items."),
+ value->location());
+ }
+
+ const PropertyDeclaration decl = globalInstance->propertyDeclaration(name);
+ if (!decl.isValid()) {
+ if (value->type() == Value::ItemValueType || value->createdByPropertiesBlock())
+ return;
+ throw ErrorInfo(Tr::tr("Property '%1' is not declared.")
+ .arg(name), value->location());
+ }
+ if (const ErrorInfo error = decl.checkForDeprecation(
+ m_loaderState.parameters().deprecationWarningMode(), value->location(),
+ m_loaderState.logger()); error.hasError()) {
+ handlePropertyError(error, m_loaderState.parameters(), m_loaderState.logger());
+ return;
+ }
+ if (value->setInternally()) { // E.g. qbs.architecture after multiplexing.
+ globalInstance->setProperty(decl.name(), value);
+ return;
+ }
+ QBS_CHECK(value->type() != Value::ItemValueType);
+ const ValuePtr globalVal = globalInstance->ownProperty(decl.name());
+ value->setScope(loadingItem, loadingName);
+ QBS_CHECK(globalVal);
+
+ // Values set internally cannot be overridden by JS values.
+ // The same goes for values set on the command line.
+ // Note that in both cases, there is no merging for list properties: The override is absolute.
+ if (globalVal->setInternally() || globalVal->setByCommandLine())
+ return;
+
+ QBS_CHECK(value->type() == Value::JSSourceValueType);
+
+ if (decl.isScalar()) {
+ QBS_CHECK(!globalVal->expired(m_product.item));
+ QBS_CHECK(!value->expired(m_product.item));
+ if (compareValuePriorities(globalVal, value) < 0) {
+ value->setCandidates(globalVal->candidates());
+ globalVal->setCandidates({});
+ value->addCandidate(globalVal);
+ globalInstance->setProperty(decl.name(), value);
+ } else {
+ globalVal->addCandidate(value);
+ }
+ } else {
+ if (const ValuePtr &newChainStart = mergeListValues(globalVal, value);
+ newChainStart != globalVal) {
+ globalInstance->setProperty(decl.name(), newChainStart);
+ }
+ }
+}
+
+bool ModulePropertyMerger::doFinalMerge(Item *moduleItem)
+{
+ if (!moduleItem->isPresentModule())
+ return false;
+ bool mustInvalidateCache = false;
+ for (auto it = moduleItem->properties().begin(); it != moduleItem->properties().end(); ++it) {
+ if (doFinalMerge(moduleItem->propertyDeclaration(it.key()), it.value()))
+ mustInvalidateCache = true;
+ }
+ return mustInvalidateCache;
+}
+
+bool ModulePropertyMerger::doFinalMerge(const PropertyDeclaration &propertyDecl,
+ ValuePtr &propertyValue)
+{
+ if (propertyValue->type() == Value::VariantValueType) {
+ QBS_CHECK(!propertyValue->next());
+ return false;
+ }
+ if (!propertyDecl.isValid())
+ return false; // Caught later by dedicated checker.
+ propertyValue->resetPriority();
+ if (propertyDecl.isScalar()) {
+ if (propertyValue->candidates().empty())
+ return false;
+ std::pair<int, std::vector<ValuePtr>> candidatesWithHighestPrio;
+ candidatesWithHighestPrio.first = propertyValue->priority(m_product.item);
+ candidatesWithHighestPrio.second.push_back(propertyValue);
+ for (const ValuePtr &v : propertyValue->candidates()) {
+ const int prio = v->priority(m_product.item);
+ if (prio < candidatesWithHighestPrio.first)
+ continue;
+ if (prio > candidatesWithHighestPrio.first) {
+ candidatesWithHighestPrio.first = prio;
+ candidatesWithHighestPrio.second = {v};
+ continue;
+ }
+ candidatesWithHighestPrio.second.push_back(v);
+ }
+ ValuePtr chosenValue = candidatesWithHighestPrio.second.front();
+ if (int(candidatesWithHighestPrio.second.size()) > 1) {
+ ErrorInfo error(Tr::tr("Conflicting scalar values for property '%1'.")
+ .arg(propertyDecl.name()));
+ error.append({}, chosenValue->location());
+ QBS_CHECK(chosenValue->type() == Value::JSSourceValueType);
+ QStringView sourcCode = static_cast<JSSourceValue *>(
+ chosenValue.get())->sourceCode();
+ for (int i = 1; i < int(candidatesWithHighestPrio.second.size()); ++i) {
+ const ValuePtr &v = candidatesWithHighestPrio.second.at(i);
+ QBS_CHECK(v->type() == Value::JSSourceValueType);
+
+ // Note that this is a bit silly: The source code could still evaluate to
+ // different values in the end.
+ if (static_cast<JSSourceValue *>(v.get())->sourceCode() != sourcCode)
+ error.append({}, v->location());
+ }
+ if (error.items().size() > 2)
+ m_loaderState.logger().printWarning(error);
+ }
+
+ if (propertyValue == chosenValue)
+ return false;
+ std::vector<ValuePtr> candidates = propertyValue->candidates();
+ candidates.erase(std::find(candidates.begin(), candidates.end(), chosenValue));
+ chosenValue->setCandidates(candidates);
+ chosenValue->addCandidate(propertyValue);
+ propertyValue->setCandidates({});
+ propertyValue = chosenValue;
+ return true;
+ }
+ if (!propertyValue->next())
+ return false;
+ std::vector<ValuePtr> singleValuesBefore;
+ for (ValuePtr current = propertyValue; current;) {
+ singleValuesBefore.push_back(current);
+ const ValuePtr next = current->next();
+ if (next)
+ current->setNext({});
+ current = next;
+ }
+ ValuePtr newValue;
+ for (const ValuePtr &v : singleValuesBefore)
+ newValue = mergeListValues(newValue, v);
+ std::vector<ValuePtr> singleValuesAfter;
+ for (ValuePtr current = propertyValue; current; current = current->next())
+ singleValuesAfter.push_back(current);
+ propertyValue = newValue;
+ return singleValuesBefore != singleValuesAfter;
+}
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/modulepropertymerger.h b/src/lib/corelib/loader/modulepropertymerger.h
new file mode 100644
index 000000000..a6643216f
--- /dev/null
+++ b/src/lib/corelib/loader/modulepropertymerger.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QtGlobal>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+namespace qbs::Internal {
+class Item;
+class LoaderState;
+class ProductContext;
+
+// This module comprises functions for collecting values attached to module properties
+// in different contexts.
+// For example, in the Qt.core module you will find a property binding such as this:
+// cpp.defines: "QT_CORE_LIB"
+// while in the Qt.widgets module, it will look like this:
+// cpp.defines: "QT_WIDGETS_LIB"
+// A product with a dependency on both these modules will end up with a value of
+// ["QT_WIDGETS_LIB", "QT_CORE_LIB"], plus potentially other defines set elsewhere.
+// Each of these values is assigned a priority that roughly corresponds to the "level" at which
+// the module containing the property binding resides in the dependency hierarchy.
+// For list properties, the priorities determine the order of the respecive values in the
+// final array, for scalar values they determine which one survives. Different scalar values
+// with the same priority trigger a warning message.
+// Since the right-hand side of a binding can refer to properties of the surrounding context,
+// each such value gets its own scope.
+
+// This function is called when a module is loaded via a Depends item.
+// loadingItem is the product or module containing the Depends item.
+// loadingName is the name of that module. It is used as a tie-breaker for list property values
+// with equal priority.
+// localInstance is the module instance placeholder in the ItemValue of a property binding,
+// i.e. the "cpp" in "cpp.defines".
+// globalInstance is the actual module into which the properties from localInstance get merged.
+void mergeFromLocalInstance(ProductContext &product, Item *loadingItem,
+ const QString &loadingName, const Item *localInstance,
+ Item *globalInstance, LoaderState &loaderState);
+
+// This function is called after all dependencies have been resolved. It uses its global
+// knowledge of module priorities to potentially adjust the order of list values or
+// favor different scalar values. It can also remove previously merged-in values again;
+// this can happen if a module fails to load after it already merged some values, or
+// if it fails validation in the end.
+void doFinalMerge(ProductContext &product, LoaderState &loaderState);
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/moduleproviderloader.cpp b/src/lib/corelib/loader/moduleproviderloader.cpp
new file mode 100644
index 000000000..d0789facd
--- /dev/null
+++ b/src/lib/corelib/loader/moduleproviderloader.cpp
@@ -0,0 +1,416 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "moduleproviderloader.h"
+
+#include "itemreader.h"
+#include "probesresolver.h"
+
+#include <language/builtindeclarations.h>
+#include <language/evaluator.h>
+#include <language/item.h>
+#include <language/scriptengine.h>
+#include <language/value.h>
+#include <logging/categories.h>
+#include <logging/translator.h>
+#include <tools/fileinfo.h>
+#include <tools/jsliterals.h>
+#include <tools/scripttools.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stlutils.h>
+#include <tools/stringconstants.h>
+
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+
+namespace qbs {
+namespace Internal {
+
+static QString getConfigHash(const QVariantMap& config)
+{
+ QJsonDocument doc;
+ doc.setObject(QJsonObject::fromVariantMap(config));
+ return QString::fromLatin1(
+ QCryptographicHash::hash(doc.toJson(), QCryptographicHash::Sha1).toHex().left(16));
+}
+
+ModuleProviderLoader::ModuleProviderLoader(LoaderState &loaderState)
+ : m_loaderState(loaderState) {}
+
+ModuleProviderLoader::ModuleProviderResult ModuleProviderLoader::executeModuleProviders(
+ ProductContext &productContext,
+ const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName)
+{
+ ModuleProviderLoader::ModuleProviderResult result;
+ try {
+ std::vector<Provider> providersToRun;
+ qCDebug(lcModuleLoader) << "Module" << moduleName.toString()
+ << "not found, checking for module providers";
+ const auto providerNames = getModuleProviders(productContext.item);
+ if (providerNames) {
+ providersToRun = transformed<std::vector<Provider>>(*providerNames, [](const auto &name) {
+ return Provider{name, ModuleProviderLookup::Named}; });
+ } else {
+ for (QualifiedId providerName = moduleName; !providerName.empty();
+ providerName.pop_back()) {
+ providersToRun.push_back({providerName, ModuleProviderLookup::Scoped});
+ }
+ }
+ result = executeModuleProvidersHelper(
+ productContext, dependsItemLocation, moduleName, providersToRun);
+ } catch (const ErrorInfo &error) {
+ auto ei = error;
+ ei.prepend(
+ Tr::tr("Error executing provider for module '%1':").arg(moduleName.toString()),
+ dependsItemLocation);
+ productContext.handleError(ei);
+ }
+ return result;
+}
+
+ModuleProviderLoader::ModuleProviderResult ModuleProviderLoader::executeModuleProvidersHelper(
+ ProductContext &product,
+ const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName,
+ const std::vector<Provider> &providers)
+{
+ if (providers.empty())
+ return {};
+ QStringList allSearchPaths;
+ ModuleProviderResult result;
+ setupModuleProviderConfig(product);
+ const auto qbsModule = evaluateQbsModule(product);
+ for (const auto &[name, lookupType] : providers) {
+ const auto &[info, fromCache] = findOrCreateProviderInfo(
+ product, dependsItemLocation, moduleName, name, lookupType, qbsModule);
+ if (info.providerFile.isEmpty()) {
+ if (lookupType == ModuleProviderLookup::Named)
+ throw ErrorInfo(Tr::tr("Unknown provider '%1'").arg(name.toString()));
+ continue;
+ }
+ if (fromCache)
+ qCDebug(lcModuleLoader) << "Re-using provider" << name.toString() << "from cache";
+
+ result.providerFound = true;
+ if (info.searchPaths.empty()) {
+ qCDebug(lcModuleLoader)
+ << "Module provider did run, but did not set up any modules.";
+ continue;
+ }
+ qCDebug(lcModuleLoader) << "Module provider added" << info.searchPaths.size()
+ << "new search path(s)";
+
+ allSearchPaths << info.searchPaths;
+ }
+ if (allSearchPaths.isEmpty())
+ return result;
+
+ result.searchPaths = std::move(allSearchPaths);
+
+ return result;
+}
+
+std::pair<const ModuleProviderInfo &, bool /*fromCache*/>
+ModuleProviderLoader::findOrCreateProviderInfo(
+ ProductContext &product, const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName, const QualifiedId &name, ModuleProviderLookup lookupType,
+ const QVariantMap &qbsModule)
+{
+ QBS_CHECK(product.providerConfig);
+ const QVariantMap config = product.providerConfig->value(name.toString()).toMap();
+ std::lock_guard lock(m_loaderState.topLevelProject().moduleProvidersCacheLock());
+ ModuleProvidersCacheKey cacheKey{name.toString(), {}, config, qbsModule, int(lookupType)};
+ // TODO: get rid of non-eager providers and eliminate following if-logic
+ // first, try to find eager provider (stored with an empty module name)
+ if (ModuleProviderInfo *provider = m_loaderState.topLevelProject().moduleProvider(cacheKey))
+ return {*provider, true};
+ // second, try to find non-eager provider for a specific module name
+ std::get<1>(cacheKey) = moduleName.toString(); // override moduleName
+ if (ModuleProviderInfo *provider = m_loaderState.topLevelProject().moduleProvider(cacheKey))
+ return {*provider, true};
+ bool isEager = false;
+ ModuleProviderInfo info;
+ info.name = name;
+ info.config = config;
+ info.providerFile = findModuleProviderFile(name, lookupType);
+ if (!info.providerFile.isEmpty()) {
+ qCDebug(lcModuleLoader) << "Running provider" << name << "at" << info.providerFile;
+ std::tie(info.searchPaths, isEager) = evaluateModuleProvider(
+ product,
+ dependsItemLocation,
+ moduleName,
+ name,
+ info.providerFile,
+ config,
+ qbsModule);
+ info.transientOutput = m_loaderState.parameters().dryRun();
+ }
+ std::get<1>(cacheKey) = isEager ? QString() : moduleName.toString();
+ return {m_loaderState.topLevelProject().addModuleProvider(cacheKey, info), false};
+}
+
+void ModuleProviderLoader::setupModuleProviderConfig(ProductContext &product)
+{
+ if (product.providerConfig)
+ return;
+ QVariantMap providerConfig;
+ const ItemValueConstPtr configItemValue =
+ product.item->itemProperty(StringConstants::moduleProviders(), m_loaderState.itemPool());
+ if (configItemValue) {
+ const std::function<void(const Item *, QualifiedId)> collectMap
+ = [this, &providerConfig, &collectMap](const Item *item, const QualifiedId &name) {
+ const Item::PropertyMap &props = item->properties();
+ for (auto it = props.begin(); it != props.end(); ++it) {
+ QVariant value;
+ switch (it.value()->type()) {
+ case Value::ItemValueType: {
+ const auto childItem = static_cast<ItemValue *>(it.value().get())->item();
+ childItem->setScope(item->scope());
+ collectMap(childItem, QualifiedId(name) << it.key());
+ continue;
+ }
+ case Value::JSSourceValueType: {
+ it.value()->setScope(item->scope(), {});
+ const ScopedJsValue sv(m_loaderState.evaluator().engine()->context(),
+ m_loaderState.evaluator().value(item, it.key()));
+ value = getJsVariant(m_loaderState.evaluator().engine()->context(), sv);
+ break;
+ }
+ case Value::VariantValueType:
+ value = static_cast<VariantValue *>(it.value().get())->value();
+ break;
+ }
+ QVariantMap m = providerConfig.value(name.toString()).toMap();
+ m.insert(it.key(), value);
+ providerConfig.insert(name.toString(), m);
+ }
+ };
+ configItemValue->item()->setScope(product.item);
+ collectMap(configItemValue->item(), QualifiedId());
+ }
+ for (auto it = product.moduleProperties.begin(); it != product.moduleProperties.end(); ++it) {
+ if (!it.key().startsWith(QStringLiteral("moduleProviders.")))
+ continue;
+ const QString provider = it.key().mid(QStringLiteral("moduleProviders.").size());
+ const QVariantMap providerConfigFromBuildConfig = it.value().toMap();
+ if (providerConfigFromBuildConfig.empty())
+ continue;
+ QVariantMap currentMapForProvider = providerConfig.value(provider).toMap();
+ for (auto propIt = providerConfigFromBuildConfig.begin();
+ propIt != providerConfigFromBuildConfig.end(); ++propIt) {
+ currentMapForProvider.insert(propIt.key(), propIt.value());
+ }
+ providerConfig.insert(provider, currentMapForProvider);
+ }
+ product.providerConfig = providerConfig;
+}
+
+std::optional<std::vector<QualifiedId>> ModuleProviderLoader::getModuleProviders(Item *item)
+{
+ while (item) {
+ const auto providers = m_loaderState.evaluator().optionalStringListValue(
+ item, StringConstants::qbsModuleProviders());
+ if (providers) {
+ return transformed<std::vector<QualifiedId>>(*providers, [](const auto &provider) {
+ return QualifiedId::fromString(provider); });
+ }
+ item = item->parent();
+ }
+ return std::nullopt;
+}
+
+QString ModuleProviderLoader::findModuleProviderFile(
+ const QualifiedId &name, ModuleProviderLookup lookupType)
+{
+ for (const QString &path : m_loaderState.itemReader().allSearchPaths()) {
+ QString fullPath = FileInfo::resolvePath(path, QStringLiteral("module-providers"));
+ switch (lookupType) {
+ case ModuleProviderLookup::Named: {
+ const auto result =
+ FileInfo::resolvePath(fullPath, name.toString() + QStringLiteral(".qbs"));
+ if (FileInfo::exists(result)) {
+ fullPath = result;
+ break;
+ }
+ [[fallthrough]];
+ }
+ case ModuleProviderLookup::Scoped:
+ for (const QString &component : name)
+ fullPath = FileInfo::resolvePath(fullPath, component);
+ fullPath = FileInfo::resolvePath(fullPath, QStringLiteral("provider.qbs"));
+ break;
+ }
+ if (!FileInfo::exists(fullPath)) {
+ qCDebug(lcModuleLoader) << "No module provider found at" << fullPath;
+ continue;
+ }
+ return fullPath;
+ }
+ return {};
+}
+
+QVariantMap ModuleProviderLoader::evaluateQbsModule(ProductContext &product) const
+{
+ if (product.providerQbsModule)
+ return *product.providerQbsModule;
+ const QString properties[] = {
+ QStringLiteral("sysroot"),
+ QStringLiteral("toolchain"),
+ };
+ const auto qbsItemValue = std::static_pointer_cast<ItemValue>(
+ product.item->property(StringConstants::qbsModule()));
+ QVariantMap result;
+ for (const auto &property : properties) {
+ const ScopedJsValue val(m_loaderState.evaluator().engine()->context(),
+ m_loaderState.evaluator().value(qbsItemValue->item(), property));
+ auto value = getJsVariant(m_loaderState.evaluator().engine()->context(), val);
+ if (!value.isValid())
+ continue;
+
+ // The xcode module sets qbs.sysroot; the resulting value is bogus before the probes
+ // have run.
+ if (property == QLatin1String("sysroot") && !FileInfo::isAbsolute(value.toString()))
+ continue;
+
+ result[property] = std::move(value);
+ }
+ return *(product.providerQbsModule = result);
+}
+
+Item *ModuleProviderLoader::createProviderScope(
+ const ProductContext &product, const QVariantMap &qbsModule)
+{
+ const auto qbsItemValue = std::static_pointer_cast<ItemValue>(
+ product.item->property(StringConstants::qbsModule()));
+
+ Item *fakeQbsModule = Item::create(&m_loaderState.itemPool(), ItemType::Scope);
+
+ for (auto it = qbsModule.begin(), end = qbsModule.end(); it != end; ++it) {
+ fakeQbsModule->setProperty(it.key(), VariantValue::create(it.value()));
+ }
+
+ Item *scope = Item::create(&m_loaderState.itemPool(), ItemType::Scope);
+ scope->setFile(qbsItemValue->item()->file());
+ scope->setProperty(StringConstants::qbsModule(), ItemValue::create(fakeQbsModule));
+ return scope;
+}
+
+ModuleProviderLoader::EvaluationResult ModuleProviderLoader::evaluateModuleProvider(
+ ProductContext &product,
+ const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName,
+ const QualifiedId &name,
+ const QString &providerFile,
+ const QVariantMap &moduleConfig,
+ const QVariantMap &qbsModule)
+{
+ qCDebug(lcModuleLoader) << "Instantiating module provider at" << providerFile;
+ const QString projectBuildDir = product.project->item->variantProperty(
+ StringConstants::buildDirectoryProperty())->value().toString();
+ const QString searchPathBaseDir = ModuleProviderInfo::outputDirPath(projectBuildDir, name);
+
+ // include qbs module into hash
+ auto jsConfig = moduleConfig;
+ jsConfig[StringConstants::qbsModule()] = qbsModule;
+
+ QString outputBaseDir = searchPathBaseDir + QLatin1Char('/') + getConfigHash(jsConfig);
+ Item * const providerItem = m_loaderState.itemReader().setupItemFromFile(
+ providerFile, dependsItemLocation);
+ if (providerItem->type() != ItemType::ModuleProvider) {
+ throw ErrorInfo(Tr::tr("File '%1' declares an item of type '%2', "
+ "but '%3' was expected.")
+ .arg(providerFile, providerItem->typeName(),
+ BuiltinDeclarations::instance().nameForType(ItemType::ModuleProvider)));
+ }
+
+ Item * const scope = createProviderScope(product, qbsModule);
+ for (auto it = providerItem->properties().begin(); it != providerItem->properties().end(); ++it)
+ it.value()->setScope(scope, {});
+ providerItem->setProperty(
+ StringConstants::nameProperty(),
+ VariantValue::create(name.toString()));
+ providerItem->setProperty(
+ QStringLiteral("outputBaseDir"),
+ VariantValue::create(outputBaseDir));
+ providerItem->overrideProperties(moduleConfig, name,
+ m_loaderState.parameters(), m_loaderState.logger());
+
+ const bool isEager = m_loaderState.evaluator().boolValue(
+ providerItem, StringConstants::isEagerProperty());
+ if (!isEager) {
+ providerItem->setProperty(
+ StringConstants::moduleNameProperty(),
+ VariantValue::create(moduleName.toString()));
+ }
+
+ ProbesResolver(m_loaderState).resolveProbes(product, providerItem);
+
+ EvalContextSwitcher contextSwitcher(m_loaderState.evaluator().engine(),
+ EvalContext::ModuleProvider);
+
+ checkAllowedValues(providerItem);
+ auto searchPaths = m_loaderState.evaluator().stringListValue(
+ providerItem, QStringLiteral("relativeSearchPaths"));
+ auto prependBaseDir = [&outputBaseDir](const auto &path) {
+ return outputBaseDir + QLatin1Char('/') + path;
+ };
+ std::transform(searchPaths.begin(), searchPaths.end(), searchPaths.begin(), prependBaseDir);
+ return {searchPaths, isEager};
+}
+
+void ModuleProviderLoader::checkAllowedValues(Item *providerItem)
+{
+ for (const auto &propertyDeclaration : providerItem->propertyDeclarations()) {
+ if (!propertyDeclaration.shouldCheckAllowedValues())
+ continue;
+ const auto &name = propertyDeclaration.name();
+ if (name == QStringLiteral("relativeSearchPaths"))
+ continue;
+ const auto value = m_loaderState.evaluator().variantValue(providerItem, name);
+ const auto propertyValue = providerItem->property(name);
+ propertyDeclaration.checkAllowedValues(
+ value, propertyValue->location(), name, m_loaderState);
+ }
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/loader/moduleproviderloader.h b/src/lib/corelib/loader/moduleproviderloader.h
new file mode 100644
index 000000000..471cfbb02
--- /dev/null
+++ b/src/lib/corelib/loader/moduleproviderloader.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 MODULEPROVIDERLOADER_H
+#define MODULEPROVIDERLOADER_H
+
+#include "loaderutils.h"
+
+#include <language/forward_decls.h>
+#include <language/moduleproviderinfo.h>
+
+#include <QtCore/qvariant.h>
+
+#include <optional>
+#include <utility>
+#include <vector>
+
+namespace qbs::Internal {
+class Item;
+class LoaderState;
+class ProductContext;
+
+class ModuleProviderLoader
+{
+public:
+ explicit ModuleProviderLoader(LoaderState &loaderState);
+
+ struct ModuleProviderResult {
+ bool providerFound = false;
+ std::optional<QStringList> searchPaths;
+ };
+ ModuleProviderResult executeModuleProviders(
+ ProductContext &productContext,
+ const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName);
+
+private:
+ enum class ModuleProviderLookup { Scoped, Named };
+ struct Provider {
+ QualifiedId name;
+ ModuleProviderLookup lookup;
+ };
+ ModuleProviderResult executeModuleProvidersHelper(
+ ProductContext &product,
+ const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName,
+ const std::vector<Provider> &providers);
+ std::pair<const ModuleProviderInfo &, bool>
+ findOrCreateProviderInfo(ProductContext &product, const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName, const QualifiedId &name,
+ ModuleProviderLookup lookupType, const QVariantMap &qbsModule);
+ void setupModuleProviderConfig(ProductContext &product);
+
+ std::optional<std::vector<QualifiedId>> getModuleProviders(Item *item);
+
+ QString findModuleProviderFile(const QualifiedId &name, ModuleProviderLookup lookupType);
+ QVariantMap evaluateQbsModule(ProductContext &product) const;
+ Item *createProviderScope(const ProductContext &product, const QVariantMap &qbsModule);
+ using EvaluationResult = std::pair<QStringList, bool /*isEager*/>;
+ EvaluationResult evaluateModuleProvider(
+ ProductContext &product,
+ const CodeLocation &dependsItemLocation,
+ const QualifiedId &moduleName,
+ const QualifiedId &name,
+ const QString &providerFile,
+ const QVariantMap &moduleConfig,
+ const QVariantMap &qbsModule);
+ void checkAllowedValues(Item *providerItem);
+
+ LoaderState &m_loaderState;
+};
+
+} // namespace qbs::Internal
+
+#endif // MODULEPROVIDERLOADER_H
diff --git a/src/lib/corelib/loader/probesresolver.cpp b/src/lib/corelib/loader/probesresolver.cpp
new file mode 100644
index 000000000..269e9c4b6
--- /dev/null
+++ b/src/lib/corelib/loader/probesresolver.cpp
@@ -0,0 +1,293 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2022 Raphaël Cotty <raphael.cotty@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "probesresolver.h"
+
+#include "itemreader.h"
+#include "loaderutils.h"
+
+#include <api/languageinfo.h>
+#include <language/evaluator.h>
+#include <language/filecontext.h>
+#include <language/item.h>
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <language/value.h>
+#include <logging/categories.h>
+#include <logging/logger.h>
+#include <logging/translator.h>
+#include <tools/profiling.h>
+#include <tools/scripttools.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+
+#include <quickjs.h>
+
+namespace qbs {
+namespace Internal {
+
+static QString probeGlobalId(Item *probe)
+{
+ QString id;
+
+ for (Item *obj = probe; obj; obj = obj->prototype()) {
+ if (!obj->id().isEmpty()) {
+ id = obj->id();
+ break;
+ }
+ }
+
+ if (id.isEmpty())
+ return {};
+
+ QBS_CHECK(probe->file());
+ return id + QLatin1Char('_') + probe->file()->filePath();
+}
+
+ProbesResolver::ProbesResolver(LoaderState &loaderState) : m_loaderState(loaderState) {}
+
+void ProbesResolver::resolveProbes(ProductContext &productContext, Item *item)
+{
+ AccumulatingTimer probesTimer(m_loaderState.parameters().logElapsedTime()
+ ? &productContext.timingData.probes : nullptr);
+
+ EvalContextSwitcher evalContextSwitcher(m_loaderState.evaluator().engine(),
+ EvalContext::ProbeExecution);
+ for (Item * const child : item->children()) {
+ if (child->type() == ItemType::Probe)
+ resolveProbe(productContext, item, child);
+ }
+}
+
+void ProbesResolver::resolveProbe(ProductContext &productContext, Item *parent,
+ Item *probe)
+{
+ qCDebug(lcModuleLoader) << "Resolving Probe at " << probe->location().toString();
+ const QString &probeId = probeGlobalId(probe);
+ if (Q_UNLIKELY(probeId.isEmpty()))
+ throw ErrorInfo(Tr::tr("Probe.id must be set."), probe->location());
+ const bool isProjectLevelProbe
+ = parent->type() == ItemType::Project
+ || productContext.name.startsWith(StringConstants::shadowProductPrefix());
+ const JSSourceValueConstPtr configureScript
+ = probe->sourceProperty(StringConstants::configureProperty());
+ QBS_CHECK(configureScript);
+ if (Q_UNLIKELY(configureScript->sourceCode() == StringConstants::undefinedValue()))
+ throw ErrorInfo(Tr::tr("Probe.configure must be set."), probe->location());
+ using ProbeProperty = std::pair<QString, ScopedJsValue>;
+ std::vector<ProbeProperty> probeBindings;
+ Evaluator &evaluator = m_loaderState.evaluator();
+ ScriptEngine * const engine = evaluator.engine();
+ JSContext * const ctx = engine->context();
+ QVariantMap initialProperties;
+ for (Item *obj = probe; obj; obj = obj->prototype()) {
+ const Item::PropertyMap &props = obj->properties();
+ for (auto it = props.cbegin(); it != props.cend(); ++it) {
+ const QString &name = it.key();
+ if (name == StringConstants::configureProperty())
+ continue;
+ const JSValue value = evaluator.value(probe, name);
+ probeBindings.emplace_back(name, ScopedJsValue(ctx, value));
+ if (name != StringConstants::conditionProperty())
+ initialProperties.insert(name, getJsVariant(ctx, value));
+ }
+ }
+ const bool condition = evaluator.boolValue(probe, StringConstants::conditionProperty());
+ const QString &sourceCode = configureScript->sourceCode().toString();
+ ProbeConstPtr resolvedProbe;
+ std::lock_guard lock(m_loaderState.topLevelProject().probesCacheLock());
+ m_loaderState.topLevelProject().incrementProbesCount();
+ if (isProjectLevelProbe) {
+ resolvedProbe = findOldProjectProbe(probeId, condition, initialProperties, sourceCode);
+ } else {
+ resolvedProbe = findOldProductProbe(productContext.uniqueName(), condition,
+ initialProperties, sourceCode);
+ }
+ if (!resolvedProbe) {
+ resolvedProbe = findCurrentProbe(probe->location(), condition, initialProperties);
+ if (resolvedProbe) {
+ qCDebug(lcModuleLoader) << "probe results cached from current run";
+ m_loaderState.topLevelProject().incrementReusedCurrentProbesCount();
+ }
+ } else {
+ qCDebug(lcModuleLoader) << "probe results cached from earlier run";
+ m_loaderState.topLevelProject().incrementReusedOldProbesCount();
+ }
+ ScopedJsValue configureScope(ctx, JS_UNDEFINED);
+ std::vector<QString> importedFilesUsedInConfigure;
+ if (!condition) {
+ qCDebug(lcModuleLoader) << "Probe disabled; skipping";
+ } else if (!resolvedProbe) {
+ m_loaderState.topLevelProject().incrementRunProbesCount();
+ qCDebug(lcModuleLoader) << "configure script needs to run";
+ const Evaluator::FileContextScopes fileCtxScopes
+ = evaluator.fileContextScopes(configureScript->file());
+ configureScope.setValue(engine->newObject());
+ for (const ProbeProperty &b : probeBindings)
+ setJsProperty(ctx, configureScope, b.first, JS_DupValue(ctx, b.second));
+ engine->clearRequestedProperties();
+ ScopedJsValue sv(ctx, engine->evaluate(JsValueOwner::Caller,
+ configureScript->sourceCodeForEvaluation(), {}, 1,
+ {fileCtxScopes.fileScope, fileCtxScopes.importScope, configureScope}));
+ if (JsException ex = engine->checkAndClearException(configureScript->location()))
+ throw ex.toErrorInfo();
+ importedFilesUsedInConfigure = engine->importedFilesUsedInScript();
+ } else {
+ importedFilesUsedInConfigure = resolvedProbe->importedFilesUsed();
+ }
+ QVariantMap properties;
+ VariantValuePtr storedValue;
+ QMap<QString, VariantValuePtr> storedValues;
+ for (const ProbeProperty &b : probeBindings) {
+ QVariant newValue;
+ if (resolvedProbe) {
+ newValue = resolvedProbe->properties().value(b.first);
+ } else {
+ if (condition) {
+ JSValue v = getJsProperty(ctx, configureScope, b.first);
+ const JSValue saved = v;
+ ScopedJsValue valueMgr(ctx, saved);
+ const PropertyDeclaration decl = probe->propertyDeclaration(b.first);
+ evaluator.convertToPropertyType(decl, probe->location(), v);
+
+ // If the value was converted from scalar to array as per our convenience
+ // functionality, then the original value is now the only element of a
+ // newly allocated array and thus gets deleted via that array.
+ // The array itself is owned by the script engine, so we must stay out of
+ // memory management here.
+ if (v != saved)
+ valueMgr.setValue(JS_UNDEFINED);
+
+ if (JsException ex = engine->checkAndClearException({}))
+ throw ex.toErrorInfo();
+ newValue = getJsVariant(ctx, v);
+ // Special case, string and path lists are represented as js arrays but we don't
+ // want to make them const as we do for object lists. Converting to QStringList
+ // allows to distinguish between these two cases in ScriptEngine::asJsValue
+ if (newValue.userType() == QMetaType::QVariantList
+ && (decl.type() == PropertyDeclaration::StringList
+ || decl.type() == PropertyDeclaration::PathList)) {
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ newValue.convert(QMetaType(QMetaType::QStringList));
+#else
+ newValue.convert(QMetaType::QStringList);
+#endif
+ }
+ } else {
+ newValue = initialProperties.value(b.first);
+ }
+ }
+ if (!qVariantsEqual(newValue, getJsVariant(ctx, b.second))) {
+ if (!resolvedProbe)
+ storedValue = VariantValue::createStored(newValue);
+ else
+ storedValue = resolvedProbe->values().value(b.first);
+
+ probe->setProperty(b.first, storedValue);
+ }
+ if (!resolvedProbe) {
+ properties.insert(b.first, newValue);
+ storedValues[b.first] = storedValue;
+ }
+ }
+ if (!resolvedProbe) {
+ resolvedProbe = Probe::create(probeId, probe->location(), condition,
+ sourceCode, properties, initialProperties, storedValues,
+ importedFilesUsedInConfigure);
+ m_loaderState.topLevelProject().addNewlyResolvedProbe(resolvedProbe);
+ }
+ if (isProjectLevelProbe)
+ m_loaderState.topLevelProject().addProjectLevelProbe(resolvedProbe);
+ else
+ productContext.probes << resolvedProbe;
+}
+
+ProbeConstPtr ProbesResolver::findOldProjectProbe(
+ const QString &globalId,
+ bool condition,
+ const QVariantMap &initialProperties,
+ const QString &sourceCode) const
+{
+ if (m_loaderState.parameters().forceProbeExecution())
+ return {};
+ return m_loaderState.topLevelProject().findOldProjectProbe(globalId,
+ [&](const ProbeConstPtr &oldProbe) {
+ return probeMatches(oldProbe, condition, initialProperties, sourceCode, CompareScript::Yes);
+ });
+}
+
+ProbeConstPtr ProbesResolver::findOldProductProbe(
+ const QString &productName,
+ bool condition,
+ const QVariantMap &initialProperties,
+ const QString &sourceCode) const
+{
+ if (m_loaderState.parameters().forceProbeExecution())
+ return {};
+ return m_loaderState.topLevelProject().findOldProductProbe(productName,
+ [&](const ProbeConstPtr &oldProbe) {
+ return probeMatches(oldProbe, condition, initialProperties, sourceCode, CompareScript::Yes);
+ });
+}
+
+ProbeConstPtr ProbesResolver::findCurrentProbe(
+ const CodeLocation &location,
+ bool condition,
+ const QVariantMap &initialProperties) const
+{
+ return m_loaderState.topLevelProject().findCurrentProbe(location,
+ [&](const ProbeConstPtr &probe) {
+ return probeMatches(probe, condition, initialProperties, QString(), CompareScript::No);
+ });
+}
+
+bool ProbesResolver::probeMatches(const ProbeConstPtr &probe, bool condition,
+ const QVariantMap &initialProperties, const QString &configureScript,
+ CompareScript compareScript) const
+{
+ return probe->condition() == condition
+ && qVariantMapsEqual(probe->initialProperties(), initialProperties)
+ && (compareScript == CompareScript::No
+ || (probe->configureScript() == configureScript
+ && !probe->needsReconfigure(m_loaderState.topLevelProject().lastResolveTime())));
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/loader/probesresolver.h b/src/lib/corelib/loader/probesresolver.h
new file mode 100644
index 000000000..4c49861d3
--- /dev/null
+++ b/src/lib/corelib/loader/probesresolver.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Copyright (C) 2022 Raphaël Cotty <raphael.cotty@gmail.com>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 PROBESRESOLVER_H
+#define PROBESRESOLVER_H
+
+#include <language/forward_decls.h>
+#include <tools/codelocation.h>
+
+#include <QString>
+
+namespace qbs::Internal {
+class Item;
+class LoaderState;
+class ProductContext;
+
+class ProbesResolver
+{
+public:
+ explicit ProbesResolver(LoaderState &loaderState);
+ void resolveProbes(ProductContext &productContext, Item *item);
+
+private:
+ ProbeConstPtr findOldProjectProbe(const QString &globalId, bool condition,
+ const QVariantMap &initialProperties,
+ const QString &sourceCode) const;
+ ProbeConstPtr findOldProductProbe(const QString &productName, bool condition,
+ const QVariantMap &initialProperties,
+ const QString &sourceCode) const;
+ ProbeConstPtr findCurrentProbe(const CodeLocation &location, bool condition,
+ const QVariantMap &initialProperties) const;
+ enum class CompareScript { No, Yes };
+ bool probeMatches(const ProbeConstPtr &probe, bool condition,
+ const QVariantMap &initialProperties, const QString &configureScript,
+ CompareScript compareScript) const;
+ void resolveProbe(ProductContext &productContext, Item *parent, Item *probe);
+
+ LoaderState &m_loaderState;
+};
+
+} // namespace qbs::Internal
+
+#endif // PROBESRESOLVER_H
diff --git a/src/lib/corelib/loader/productitemmultiplexer.cpp b/src/lib/corelib/loader/productitemmultiplexer.cpp
new file mode 100644
index 000000000..0adb3d7fd
--- /dev/null
+++ b/src/lib/corelib/loader/productitemmultiplexer.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "productitemmultiplexer.h"
+
+#include "loaderutils.h"
+#include "moduleinstantiator.h"
+
+#include <language/evaluator.h>
+#include <language/item.h>
+#include <language/scriptengine.h>
+#include <language/value.h>
+#include <logging/translator.h>
+#include <tools/scripttools.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+
+#include <QJsonDocument>
+
+#include <vector>
+
+// This module deals with product multiplexing over the various defined axes.
+// For instance, a product with qbs.architectures: ["x86", "arm"] will get multiplexed into
+// two products with qbs.architecture: "x86" and qbs.architecture: "arm", respectively.
+namespace qbs::Internal {
+namespace {
+using MultiplexRow = std::vector<VariantValuePtr>;
+using MultiplexTable = std::vector<MultiplexRow>;
+class MultiplexInfo
+{
+public:
+ std::vector<QString> properties;
+ MultiplexTable table;
+ bool aggregate = false;
+ VariantValuePtr multiplexedType;
+
+ QString toIdString(size_t row, LoaderState &loaderState) const;
+};
+} // namespace
+
+class ProductItemMultiplexer
+{
+public:
+ ProductItemMultiplexer(const QString &productName, Item *productItem, Item *tempQbsModuleItem,
+ const std::function<void()> &dropTempQbsModule, LoaderState &loaderState)
+ : m_loaderState(loaderState), m_productName(productName), m_productItem(productItem),
+ m_tempQbsModuleItem(tempQbsModuleItem), m_dropTempQbsModule(dropTempQbsModule) {}
+
+ QList<Item *> multiplex();
+private:
+ MultiplexInfo extractMultiplexInfo();
+ MultiplexTable combine(const MultiplexTable &table, const MultiplexRow &values);
+
+ LoaderState &m_loaderState;
+ const QString &m_productName;
+ Item * const m_productItem;
+ Item * const m_tempQbsModuleItem;
+ const std::function<void()> &m_dropTempQbsModule;
+};
+
+QList<Item *> ProductItemMultiplexer::multiplex()
+{
+ const auto multiplexInfo = extractMultiplexInfo();
+ m_dropTempQbsModule();
+ if (multiplexInfo.table.size() > 1)
+ m_productItem->setProperty(StringConstants::multiplexedProperty(), VariantValue::trueValue());
+ VariantValuePtr productNameValue = VariantValue::create(m_productName);
+ Item *aggregator = multiplexInfo.aggregate ? m_productItem->clone(m_loaderState.itemPool())
+ : nullptr;
+ QList<Item *> additionalProductItems;
+ std::vector<VariantValuePtr> multiplexConfigurationIdValues;
+ for (size_t row = 0; row < multiplexInfo.table.size(); ++row) {
+ Item *item = m_productItem;
+ const auto &mprow = multiplexInfo.table.at(row);
+ QBS_CHECK(mprow.size() == multiplexInfo.properties.size());
+ if (row > 0) {
+ item = m_productItem->clone(m_loaderState.itemPool());
+ additionalProductItems.push_back(item);
+ }
+ const QString multiplexConfigurationId = multiplexInfo.toIdString(row, m_loaderState);
+ const VariantValuePtr multiplexConfigurationIdValue
+ = VariantValue::create(multiplexConfigurationId);
+ if (multiplexInfo.table.size() > 1 || aggregator) {
+ multiplexConfigurationIdValues.push_back(multiplexConfigurationIdValue);
+ item->setProperty(StringConstants::multiplexConfigurationIdProperty(),
+ multiplexConfigurationIdValue);
+ }
+ if (multiplexInfo.multiplexedType)
+ item->setProperty(StringConstants::typeProperty(), multiplexInfo.multiplexedType);
+ for (size_t column = 0; column < mprow.size(); ++column) {
+ Item * const qbsItem = retrieveQbsItem(item, m_loaderState);
+ const QString &propertyName = multiplexInfo.properties.at(column);
+ const VariantValuePtr &mpvalue = mprow.at(column);
+ qbsItem->setProperty(propertyName, mpvalue);
+ }
+ }
+
+ if (aggregator) {
+ additionalProductItems << aggregator;
+
+ // Add dependencies to all multiplexed instances.
+ for (const auto &v : multiplexConfigurationIdValues) {
+ Item *dependsItem = Item::create(&m_loaderState.itemPool(), ItemType::Depends);
+ dependsItem->setProperty(StringConstants::nameProperty(), productNameValue);
+ dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(), v);
+ dependsItem->setProperty(StringConstants::profilesProperty(),
+ VariantValue::create(QStringList()));
+ dependsItem->setFile(aggregator->file());
+ dependsItem->setupForBuiltinType(m_loaderState.parameters().deprecationWarningMode(),
+ m_loaderState.logger());
+ Item::addChild(aggregator, dependsItem);
+ }
+ }
+
+ return additionalProductItems;
+}
+
+MultiplexInfo ProductItemMultiplexer::extractMultiplexInfo()
+{
+ static const QString mpmKey = QStringLiteral("multiplexMap");
+
+ Evaluator &evaluator = m_loaderState.evaluator();
+ JSContext * const ctx = evaluator.engine()->context();
+ const ScopedJsValue multiplexMap(ctx, evaluator.value(m_tempQbsModuleItem, mpmKey));
+ const QStringList multiplexByQbsProperties = evaluator.stringListValue(
+ m_productItem, StringConstants::multiplexByQbsPropertiesProperty());
+
+ MultiplexInfo multiplexInfo;
+ multiplexInfo.aggregate = evaluator.boolValue(
+ m_productItem, StringConstants::aggregateProperty());
+
+ const QString multiplexedType = evaluator.stringValue(
+ m_productItem, StringConstants::multiplexedTypeProperty());
+ if (!multiplexedType.isEmpty())
+ multiplexInfo.multiplexedType = VariantValue::create(multiplexedType);
+
+ Set<QString> uniqueMultiplexByQbsProperties;
+ for (const QString &key : multiplexByQbsProperties) {
+ const QString mappedKey = getJsStringProperty(ctx, multiplexMap, key);
+ if (mappedKey.isEmpty())
+ throw ErrorInfo(Tr::tr("There is no entry for '%1' in 'qbs.multiplexMap'.").arg(key));
+
+ if (!uniqueMultiplexByQbsProperties.insert(mappedKey).second)
+ continue;
+
+ const ScopedJsValue arr(ctx, evaluator.value(m_tempQbsModuleItem, key));
+ if (JS_IsUndefined(arr))
+ continue;
+ if (!JS_IsArray(ctx, arr))
+ throw ErrorInfo(Tr::tr("Property '%1' must be an array.").arg(key));
+
+ const quint32 arrlen = getJsIntProperty(ctx, arr, StringConstants::lengthProperty());
+ if (arrlen == 0)
+ continue;
+
+ MultiplexRow mprow;
+ mprow.reserve(arrlen);
+ QVariantList entriesForKey;
+ for (quint32 i = 0; i < arrlen; ++i) {
+ const ScopedJsValue sv(ctx, JS_GetPropertyUint32(ctx, arr, i));
+ const QVariant value = getJsVariant(ctx, sv);
+ if (entriesForKey.contains(value))
+ continue;
+ entriesForKey << value;
+ mprow.push_back(VariantValue::create(value));
+ }
+ multiplexInfo.table = combine(multiplexInfo.table, mprow);
+ multiplexInfo.properties.push_back(mappedKey);
+ }
+ return multiplexInfo;
+}
+
+MultiplexTable ProductItemMultiplexer::combine(const MultiplexTable &table,
+ const MultiplexRow &values)
+{
+ MultiplexTable result;
+ if (table.empty()) {
+ result.resize(values.size());
+ for (size_t i = 0; i < values.size(); ++i) {
+ MultiplexRow row;
+ row.resize(1);
+ row[0] = values.at(i);
+ result[i] = row;
+ }
+ } else {
+ for (const auto &row : table) {
+ for (const auto &value : values) {
+ MultiplexRow newRow = row;
+ newRow.push_back(value);
+ result.push_back(newRow);
+ }
+ }
+ }
+ return result;
+}
+
+QString MultiplexInfo::toIdString(size_t row, LoaderState &loaderState) const
+{
+ const auto &mprow = table.at(row);
+ QVariantMap multiplexConfiguration;
+ for (size_t column = 0; column < mprow.size(); ++column) {
+ const QString &propertyName = properties.at(column);
+ const VariantValuePtr &mpvalue = mprow.at(column);
+ multiplexConfiguration.insert(propertyName, mpvalue->value());
+ }
+ QString id = QString::fromUtf8(QJsonDocument::fromVariant(multiplexConfiguration)
+ .toJson(QJsonDocument::Compact)
+ .toBase64());
+
+ // Cache for later use in multiplexIdToVariantMap()
+ loaderState.topLevelProject().addMultiplexConfiguration(id, multiplexConfiguration);
+
+ return id;
+}
+
+QList<Item *> multiplex(const QString &productName, Item *productItem, Item *tempQbsModuleItem,
+ const std::function<void ()> &dropTempQbsModule, LoaderState &loaderState)
+{
+ return ProductItemMultiplexer(productName, productItem, tempQbsModuleItem, dropTempQbsModule,
+ loaderState).multiplex();
+}
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/productitemmultiplexer.h b/src/lib/corelib/loader/productitemmultiplexer.h
new file mode 100644
index 000000000..e02e21793
--- /dev/null
+++ b/src/lib/corelib/loader/productitemmultiplexer.h
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QList>
+
+#include <functional>
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+namespace qbs::Internal {
+class Item;
+class LoaderState;
+
+// Checks whether the product item is to be multiplexed and returns the list of additional
+// product items. In the normal, non-multiplex case, this list is empty.
+QList<Item *> multiplex(
+ const QString &productName,
+ Item *productItem,
+ Item *tempQbsModuleItem,
+ const std::function<void()> &dropTempQbsModule,
+ LoaderState &loaderState
+ );
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/productresolver.cpp b/src/lib/corelib/loader/productresolver.cpp
new file mode 100644
index 000000000..a026a76d6
--- /dev/null
+++ b/src/lib/corelib/loader/productresolver.cpp
@@ -0,0 +1,1618 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "productresolver.h"
+
+#include "dependenciesresolver.h"
+#include "groupshandler.h"
+#include "loaderutils.h"
+#include "modulepropertymerger.h"
+#include "probesresolver.h"
+
+#include <jsextensions/jsextensions.h>
+#include <jsextensions/moduleproperties.h>
+#include <language/artifactproperties.h>
+#include <language/builtindeclarations.h>
+#include <language/evaluator.h>
+#include <language/filecontext.h>
+#include <language/item.h>
+#include <language/language.h>
+#include <language/propertymapinternal.h>
+#include <language/scriptengine.h>
+#include <language/value.h>
+#include <logging/categories.h>
+#include <logging/translator.h>
+#include <tools/fileinfo.h>
+#include <tools/jsliterals.h>
+#include <tools/profiling.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+
+#include <algorithm>
+
+namespace qbs::Internal {
+
+class PropertiesEvaluator
+{
+public:
+ PropertiesEvaluator(ProductContext &product, LoaderState &loaderState)
+ : m_product(product), m_loaderState(loaderState) {}
+
+ QVariantMap evaluateProperties(Item *item, bool lookupPrototype, bool checkErrors);
+ QVariantMap evaluateProperties(const Item *item, const Item *propertiesContainer,
+ const QVariantMap &tmplt, bool lookupPrototype,
+ bool checkErrors);
+ void evaluateProperty(const Item *item, const QString &propName, const ValuePtr &propValue,
+ QVariantMap &result, bool checkErrors);
+
+private:
+ ProductContext &m_product;
+ LoaderState &m_loaderState;
+};
+
+// Dependency resolving, Probe execution.
+// Run for real products and shadow products.
+class ProductResolverStage1
+{
+public:
+ ProductResolverStage1(ProductContext &product, Deferral deferral, LoaderState &loaderState)
+ : m_product(product), m_loaderState(loaderState), m_deferral(deferral) {}
+ void start();
+
+private:
+ void resolveProbes();
+ void resolveProbes(Item *item);
+ void runModuleProbes(const Item::Module &module);
+ void updateModulePresentState(const Item::Module &module);
+ bool validateModule(const Item::Module &module);
+ void handleModuleSetupError(const Item::Module &module, const ErrorInfo &error);
+ void checkPropertyDeclarations();
+ void mergeDependencyParameters();
+ void checkDependencyParameterDeclarations(const Item *productItem,
+ const QString &productName) const;
+ void collectCodeLinks();
+
+ ProductContext &m_product;
+ LoaderState &m_loaderState;
+ const Deferral m_deferral;
+};
+
+// Setting up ResolvedProduct, incuding property evaluation and handling Product child items.
+// Run only for real products.
+class ProductResolverStage2
+{
+public:
+ ProductResolverStage2(ProductContext &product, LoaderState &loaderState)
+ : m_product(product), m_loaderState(loaderState) {}
+ void start();
+
+private:
+ void resolveProductFully();
+ void createProductConfig();
+ void resolveGroup(Item *item);
+ void resolveGroupFully(Item *item, bool isEnabled);
+ QVariantMap resolveAdditionalModuleProperties(const Item *group,
+ const QVariantMap &currentValues);
+ SourceArtifactPtr createSourceArtifact(const QString &fileName, const GroupPtr &group,
+ bool wildcard, const CodeLocation &filesLocation,
+ ErrorInfo *errorInfo);
+ void resolveExport(Item *exportItem);
+ std::unique_ptr<ExportedItem> resolveExportChild(const Item *item,
+ const ExportedModule &module);
+ void setupExportedProperties(const Item *item, const QString &namePrefix,
+ std::vector<ExportedProperty> &properties);
+ QVariantMap evaluateModuleValues(Item *item, bool lookupPrototype = true);
+
+ void resolveScanner(Item *item, ModuleContext &moduleContext);
+ void resolveModules();
+ void resolveModule(const QualifiedId &moduleName, Item *item, bool isProduct,
+ const QVariantMap &parameters, JobLimits &jobLimits);
+ void applyFileTaggers();
+ void finalizeArtifactProperties();
+ void collectProductDependencies();
+
+ ProductContext &m_product;
+ LoaderState &m_loaderState;
+ GroupConstPtr m_currentGroup;
+ FileLocations m_sourceArtifactLocations;
+ PropertiesEvaluator m_propertiesEvaluator{m_product, m_loaderState};
+
+ using ArtifactPropertiesInfo = std::pair<ArtifactPropertiesPtr, std::vector<CodeLocation>>;
+ QHash<QStringList, ArtifactPropertiesInfo> m_artifactPropertiesPerFilter;
+};
+
+class ExportsResolver
+{
+public:
+ ExportsResolver(ProductContext &product, LoaderState &loaderState)
+ : m_product(product), m_loaderState(loaderState) {}
+ void start();
+
+private:
+ void resolveShadowProduct();
+ void collectPropertiesForExportItem(Item *productModuleInstance);
+ void collectPropertiesForExportItem(const QualifiedId &moduleName, const ValuePtr &value,
+ Item *moduleInstance, QVariantMap &moduleProps);
+ void collectPropertiesForModuleInExportItem(const Item::Module &module);
+ void adaptExportedPropertyValues();
+ void collectExportedProductDependencies();
+
+ ProductContext &m_product;
+ LoaderState &m_loaderState;
+ PropertiesEvaluator m_propertiesEvaluator{m_product, m_loaderState};
+};
+
+void resolveProduct(ProductContext &product, Deferral deferral, LoaderState &loaderState)
+{
+ try {
+ ProductResolverStage1(product, deferral, loaderState).start();
+ } catch (const ErrorInfo &err) {
+ if (err.isCancelException()) {
+ loaderState.topLevelProject().setCanceled();
+ return;
+ }
+ product.handleError(err);
+ }
+
+ if (product.dependenciesResolvingPending())
+ return;
+
+ if (product.name.startsWith(StringConstants::shadowProductPrefix()))
+ return;
+
+ // TODO: The weird double-forwarded error handling can hopefully be simplified now.
+ try {
+ ProductResolverStage2(product, loaderState).start();
+ } catch (const ErrorInfo &err) {
+ if (err.isCancelException()) {
+ loaderState.topLevelProject().setCanceled();
+ return;
+ }
+ loaderState.topLevelProject().addQueuedError(err);
+ }
+}
+
+void setupExports(ProductContext &product, LoaderState &loaderState)
+{
+ ExportsResolver(product, loaderState).start();
+}
+
+void ProductResolverStage1::start()
+{
+ TopLevelProjectContext &topLevelProject = m_loaderState.topLevelProject();
+ topLevelProject.checkCancelation();
+
+ if (m_product.delayedError.hasError())
+ return;
+
+ resolveDependencies(m_product, m_deferral, m_loaderState);
+ QBS_CHECK(m_product.dependenciesContext);
+ if (!m_product.dependenciesContext->dependenciesResolved)
+ return;
+ if (m_product.delayedError.hasError())
+ return;
+
+ // Run probes for modules and product.
+ resolveProbes();
+
+ // After the probes have run, we can switch on the evaluator cache.
+ Evaluator &evaluator = m_loaderState.evaluator();
+ FileTags fileTags = evaluator.fileTagsValue(m_product.item, StringConstants::typeProperty());
+ EvalCacheEnabler cacheEnabler(&evaluator, evaluator.stringValue(
+ m_product.item,
+ StringConstants::sourceDirectoryProperty()));
+
+ // Run module validation scripts.
+ for (const Item::Module &module : m_product.item->modules()) {
+ if (!validateModule(module))
+ return;
+ fileTags += evaluator.fileTagsValue(
+ module.item, StringConstants::additionalProductTypesProperty());
+ }
+
+ // Disable modules that have been pulled in only by now-disabled modules.
+ // Note that this has to happen in the reverse order compared to the other loops,
+ // with the leaves checked last.
+ for (auto it = m_product.item->modules().rbegin(); it != m_product.item->modules().rend(); ++it)
+ updateModulePresentState(*it);
+
+ // Now do the canonical module property values merge. Note that this will remove
+ // previously attached values from modules that failed validation.
+ // Evaluator cache entries that could potentially change due to this will be purged.
+ doFinalMerge(m_product, m_loaderState);
+
+ const bool enabled = topLevelProject.checkItemCondition(m_product.item, evaluator);
+
+ collectCodeLinks();
+ mergeDependencyParameters();
+ checkDependencyParameterDeclarations(m_product.item, m_product.name);
+
+ setupGroups(m_product, m_loaderState);
+
+ // Collect the full list of fileTags, including the values contributed by modules.
+ if (!m_product.delayedError.hasError() && enabled
+ && !m_product.name.startsWith(StringConstants::shadowProductPrefix())) {
+ topLevelProject.addProductByType(m_product, fileTags);
+ m_product.item->setProperty(StringConstants::typeProperty(),
+ VariantValue::create(sorted(fileTags.toStringList())));
+ }
+
+ checkPropertyDeclarations();
+}
+
+void ProductResolverStage1::resolveProbes()
+{
+ for (const Item::Module &module : m_product.item->modules()) {
+ runModuleProbes(module);
+ if (m_product.delayedError.hasError())
+ return;
+ }
+ resolveProbes(m_product.item);
+}
+
+void ProductResolverStage1::resolveProbes(Item *item)
+{
+ ProbesResolver(m_loaderState).resolveProbes(m_product, item);
+}
+
+void ProductResolverStage1::runModuleProbes(const Item::Module &module)
+{
+ if (!module.item->isPresentModule())
+ return;
+ if (module.product && m_loaderState.topLevelProject().isDisabledItem(module.product->item)) {
+ createNonPresentModule(m_loaderState.itemPool(), module.name.toString(),
+ QLatin1String("module's exporting product is disabled"),
+ module.item);
+ return;
+ }
+ try {
+ resolveProbes(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_loaderState.evaluator().stringValue(module.item,
+ StringConstants::versionProperty()));
+ 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(module, error);
+ }
+}
+
+void ProductResolverStage1::updateModulePresentState(const Item::Module &module)
+{
+ if (!module.item->isPresentModule())
+ return;
+ if (module.name.first() == StringConstants::qbsModule())
+ return;
+ bool hasPresentLoadingItem = false;
+ for (const Item::Module::LoadContext &loadContext : module.loadContexts) {
+ const Item * const loadingItem = loadContext.loadingItem();
+ if (loadingItem == m_product.item) {
+ hasPresentLoadingItem = true;
+ break;
+ }
+ if (!loadingItem->isPresentModule())
+ continue;
+ if (loadingItem->prototype() && loadingItem->prototype()->type() == ItemType::Export) {
+ QBS_CHECK(loadingItem->prototype()->parent()->type() == ItemType::Product);
+ if (m_loaderState.topLevelProject().isDisabledItem(loadingItem->prototype()->parent()))
+ continue;
+ }
+ hasPresentLoadingItem = true;
+ break;
+ }
+ if (!hasPresentLoadingItem) {
+ createNonPresentModule(m_loaderState.itemPool(), module.name.toString(),
+ QLatin1String("imported only by disabled module(s)"),
+ module.item);
+ }
+}
+
+bool ProductResolverStage1::validateModule(const Item::Module &module)
+{
+ if (!module.item->isPresentModule())
+ return true;
+ try {
+ m_loaderState.evaluator().boolValue(module.item, StringConstants::validateProperty());
+ for (const auto &dep : module.item->modules()) {
+ if (dep.required && !dep.item->isPresentModule()) {
+ throw ErrorInfo(Tr::tr("Module '%1' depends on module '%2', which was not "
+ "loaded successfully")
+ .arg(module.name.toString(), dep.name.toString()));
+ }
+ }
+ } catch (const ErrorInfo &error) {
+ handleModuleSetupError(module, error);
+ if (m_product.delayedError.hasError())
+ return false;
+ }
+ return true;
+}
+
+void ProductResolverStage1::handleModuleSetupError(const Item::Module &module,
+ const ErrorInfo &error)
+{
+ if (module.required) {
+ m_product.handleError(error);
+ } else {
+ qCDebug(lcModuleLoader()) << "non-required module" << module.name.toString()
+ << "found, but not usable in product" << m_product.name
+ << error.toString();
+ createNonPresentModule(m_loaderState.itemPool(), module.name.toString(),
+ QStringLiteral("failed validation"), module.item);
+ }
+}
+
+void ProductResolverStage1::checkPropertyDeclarations()
+{
+ AccumulatingTimer timer(m_loaderState.parameters().logElapsedTime()
+ ? &m_product.timingData.propertyChecking : nullptr);
+ qbs::Internal::checkPropertyDeclarations(m_product.item, m_loaderState);
+}
+
+void ProductResolverStage1::mergeDependencyParameters()
+{
+ if (m_loaderState.topLevelProject().isDisabledItem(m_product.item))
+ return;
+
+ for (Item::Module &module : m_product.item->modules()) {
+ if (module.name.toString() == StringConstants::qbsModule())
+ continue;
+ if (!module.item->isPresentModule())
+ continue;
+
+ using PP = Item::Module::ParametersWithPriority;
+ std::vector<PP> priorityList;
+ const QVariantMap defaultParameters = module.product
+ ? module.product->defaultParameters
+ : m_loaderState.topLevelProject().parameters(module.item->prototype());
+ priorityList.emplace_back(defaultParameters, INT_MIN);
+ for (const Item::Module::LoadContext &context : module.loadContexts) {
+ const QVariantMap &parameters = context.parameters.first;
+
+ // Empty parameter maps and inactive loading modules do not contribute to the
+ // final parameter map.
+ if (parameters.isEmpty())
+ continue;
+ if (context.loadingItem()->type() == ItemType::ModuleInstance
+ && !context.loadingItem()->isPresentModule()) {
+ continue;
+ }
+
+ // Build a list sorted by priority.
+ static const auto cmp = [](const PP &elem, int prio) { return elem.second < prio; };
+ const auto it = std::lower_bound(priorityList.begin(), priorityList.end(),
+ context.parameters.second, cmp);
+ priorityList.insert(it, context.parameters);
+ }
+
+ module.parameters = qbs::Internal::mergeDependencyParameters(std::move(priorityList));
+ }
+}
+
+class DependencyParameterDeclarationCheck
+{
+public:
+ DependencyParameterDeclarationCheck(const QString &productName, const Item *productItem,
+ const TopLevelProjectContext &topLevelProject)
+ : m_productName(productName), m_productItem(productItem), m_topLevelProject(topLevelProject)
+ {}
+
+ void operator()(const QVariantMap &parameters) const { check(parameters, QualifiedId()); }
+
+private:
+ void check(const QVariantMap &parameters, const QualifiedId &moduleName) const
+ {
+ for (auto it = parameters.begin(); it != parameters.end(); ++it) {
+ if (it.value().userType() == QMetaType::QVariantMap) {
+ check(it.value().toMap(), QualifiedId(moduleName) << it.key());
+ } else {
+ const auto &deps = m_productItem->modules();
+ auto m = std::find_if(deps.begin(), deps.end(),
+ [&moduleName] (const Item::Module &module) {
+ return module.name == moduleName;
+ });
+
+ if (m == deps.end()) {
+ const QualifiedId fullName = QualifiedId(moduleName) << it.key();
+ throw ErrorInfo(Tr::tr("Cannot set parameter '%1', "
+ "because '%2' does not have a dependency on '%3'.")
+ .arg(fullName.toString(), m_productName, moduleName.toString()),
+ m_productItem->location());
+ }
+
+ const auto decls = m_topLevelProject.parameterDeclarations(
+ m->item->rootPrototype());
+ if (!decls.contains(it.key())) {
+ const QualifiedId fullName = QualifiedId(moduleName) << it.key();
+ throw ErrorInfo(Tr::tr("Parameter '%1' is not declared.")
+ .arg(fullName.toString()), m_productItem->location());
+ }
+ }
+ }
+ }
+
+ bool moduleExists(const QualifiedId &name) const
+ {
+ const auto &deps = m_productItem->modules();
+ return any_of(deps, [&name](const Item::Module &module) {
+ return module.name == name;
+ });
+ }
+
+ const QString &m_productName;
+ const Item * const m_productItem;
+ const TopLevelProjectContext &m_topLevelProject;
+};
+
+void ProductResolverStage1::checkDependencyParameterDeclarations(const Item *productItem,
+ const QString &productName) const
+{
+ DependencyParameterDeclarationCheck dpdc(productName, productItem,
+ m_loaderState.topLevelProject());
+ for (const Item::Module &dep : productItem->modules()) {
+ if (!dep.parameters.empty())
+ dpdc(dep.parameters);
+ }
+}
+
+void ProductResolverStage1::collectCodeLinks()
+{
+ for (const Item::Module &module : m_product.item->modules()) {
+ if (module.name.first() == StringConstants::qbsModule())
+ continue;
+ for (const Item::Module::LoadContext &context : module.loadContexts) {
+ m_loaderState.topLevelProject().addCodeLink(
+ context.dependsItem->location().filePath(),
+ context.dependsItem->codeRange(),
+ module.product ? module.product->item->location()
+ : module.item->location());
+ }
+ }
+}
+
+
+void ProductResolverStage2::start()
+{
+ m_loaderState.evaluator().clearPropertyDependencies();
+
+ ResolvedProductPtr product = ResolvedProduct::create();
+ product->enabled = m_product.project->project->enabled;
+ product->moduleProperties = PropertyMapInternal::create();
+ product->project = m_product.project->project;
+ m_product.product = product;
+ product->location = m_product.item->location();
+ const auto errorFromDelayedError = [&] {
+ if (m_product.delayedError.hasError()) {
+ ErrorInfo errorInfo;
+
+ // First item is "main error", gets prepended again in the catch clause.
+ const QList<ErrorItem> &items = m_product.delayedError.items();
+ for (int i = 1; i < items.size(); ++i)
+ errorInfo.append(items.at(i));
+
+ m_product.delayedError.clear();
+ return errorInfo;
+ }
+ return ErrorInfo();
+ };
+
+ // Even if we previously encountered an error, try to continue for as long as possible
+ // to provide IDEs with useful data (e.g. the list of files).
+ // If we encounter a follow-up error, suppress it and report the original one instead.
+ try {
+ resolveProductFully();
+ if (const ErrorInfo error = errorFromDelayedError(); error.hasError())
+ throw error;
+ } catch (ErrorInfo e) {
+ if (const ErrorInfo error = errorFromDelayedError(); error.hasError())
+ e = error;
+ QString mainErrorString = !product->name.isEmpty()
+ ? Tr::tr("Error while handling product '%1':").arg(product->name)
+ : Tr::tr("Error while handling product:");
+ ErrorInfo fullError(mainErrorString, product->location);
+ appendError(fullError, e);
+ if (!product->enabled) {
+ qCDebug(lcProjectResolver) << fullError.toString();
+ return;
+ }
+ if (m_loaderState.parameters().productErrorMode() == ErrorHandlingMode::Strict)
+ throw fullError;
+ m_loaderState.logger().printWarning(fullError);
+ m_loaderState.logger().printWarning(
+ ErrorInfo(Tr::tr("Product '%1' had errors and was disabled.")
+ .arg(product->name), product->location));
+ product->enabled = false;
+ }
+}
+
+void ProductResolverStage2::resolveProductFully()
+{
+ Item * const item = m_product.item;
+ const ResolvedProductPtr product = m_product.product;
+ Evaluator &evaluator = m_loaderState.evaluator();
+ product->name = evaluator.stringValue(item, StringConstants::nameProperty());
+
+ // product->buildDirectory() isn't valid yet, because the productProperties map is not ready.
+ m_product.buildDirectory = evaluator.stringValue(
+ item, StringConstants::buildDirectoryProperty());
+ product->multiplexConfigurationId = evaluator.stringValue(
+ item, StringConstants::multiplexConfigurationIdProperty());
+ qCDebug(lcProjectResolver) << "resolveProduct" << product->uniqueName();
+ product->enabled = product->enabled
+ && evaluator.boolValue(item, StringConstants::conditionProperty());
+ const VariantValuePtr type = item->variantProperty(StringConstants::typeProperty());
+ if (type)
+ product->fileTags = FileTags::fromStringList(type->value().toStringList());
+ product->targetName = evaluator.stringValue(item, StringConstants::targetNameProperty());
+ product->sourceDirectory = evaluator.stringValue(
+ item, StringConstants::sourceDirectoryProperty());
+ product->destinationDirectory = evaluator.stringValue(
+ item, StringConstants::destinationDirProperty());
+
+ if (product->destinationDirectory.isEmpty()) {
+ product->destinationDirectory = m_product.buildDirectory;
+ } else {
+ product->destinationDirectory = FileInfo::resolvePath(
+ product->topLevelProject()->buildDirectory,
+ product->destinationDirectory);
+ }
+ product->probes = m_product.probes;
+ createProductConfig();
+ product->productProperties.insert(StringConstants::destinationDirProperty(),
+ product->destinationDirectory);
+ ModuleProperties::init(evaluator.engine(), evaluator.scriptValue(item), product.get());
+
+ QList<Item *> subItems = item->children();
+ const ValuePtr filesProperty = item->property(StringConstants::filesProperty());
+ if (filesProperty) {
+ Item *fakeGroup = Item::create(&m_loaderState.itemPool(), ItemType::Group);
+ fakeGroup->setFile(item->file());
+ fakeGroup->setLocation(item->location());
+ fakeGroup->setScope(item);
+ fakeGroup->setProperty(StringConstants::nameProperty(), VariantValue::create(product->name));
+ fakeGroup->setProperty(StringConstants::filesProperty(), filesProperty);
+ fakeGroup->setProperty(StringConstants::excludeFilesProperty(),
+ item->property(StringConstants::excludeFilesProperty()));
+ fakeGroup->setProperty(StringConstants::overrideTagsProperty(),
+ VariantValue::falseValue());
+ fakeGroup->setupForBuiltinType(m_loaderState.parameters().deprecationWarningMode(),
+ m_loaderState.logger());
+ subItems.prepend(fakeGroup);
+ }
+
+ for (Item * const child : std::as_const(subItems)) {
+ switch (child->type()) {
+ case ItemType::Rule:
+ resolveRule(m_loaderState, child, m_product.project, &m_product, nullptr);
+ break;
+ case ItemType::FileTagger:
+ resolveFileTagger(m_loaderState, child, nullptr, &m_product);
+ break;
+ case ItemType::JobLimit:
+ resolveJobLimit(m_loaderState, child, nullptr, &m_product, nullptr);
+ break;
+ case ItemType::Group:
+ resolveGroup(child);
+ break;
+ case ItemType::Export:
+ resolveExport(child);
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (const ProjectContext *p = m_product.project; p; p = p->parent) {
+ JobLimits tempLimits = p->jobLimits;
+ product->jobLimits = tempLimits.update(product->jobLimits);
+ }
+
+ resolveModules();
+ applyFileTaggers();
+ finalizeArtifactProperties();
+
+ for (const RulePtr &rule : m_product.project->rules) {
+ RulePtr clonedRule = rule->clone();
+ clonedRule->product = product.get();
+ product->rules.push_back(clonedRule);
+ }
+
+ collectProductDependencies();
+}
+
+void ProductResolverStage2::createProductConfig()
+{
+ EvalCacheEnabler cachingEnabler(&m_loaderState.evaluator(),
+ m_product.product->sourceDirectory);
+ m_product.product->moduleProperties->setValue(evaluateModuleValues(m_product.item));
+ m_product.product->productProperties = m_propertiesEvaluator.evaluateProperties(
+ m_product.item, m_product.item, QVariantMap(), true, true);
+}
+
+void ProductResolverStage2::resolveGroup(Item *item)
+{
+ const bool parentEnabled = m_currentGroup ? m_currentGroup->enabled
+ : m_product.product->enabled;
+ const bool isEnabled = parentEnabled
+ && m_loaderState.evaluator().boolValue(item, StringConstants::conditionProperty());
+ try {
+ resolveGroupFully(item, isEnabled);
+ } catch (const ErrorInfo &error) {
+ if (!isEnabled) {
+ qCDebug(lcProjectResolver) << "error resolving group at" << item->location()
+ << error.toString();
+ return;
+ }
+ if (m_loaderState.parameters().productErrorMode() == ErrorHandlingMode::Strict)
+ throw;
+ m_loaderState.logger().printWarning(error);
+ }
+}
+
+void ProductResolverStage2::resolveGroupFully(Item *item, bool isEnabled)
+{
+ AccumulatingTimer groupTimer(m_loaderState.parameters().logElapsedTime()
+ ? &m_product.timingData.groupsResolving
+ : nullptr);
+
+ const auto getGroupPropertyMap = [&](const ArtifactProperties *existingProps) {
+ PropertyMapPtr moduleProperties;
+ bool newPropertyMapRequired = false;
+ if (existingProps)
+ moduleProperties = existingProps->propertyMap();
+ if (!moduleProperties) {
+ newPropertyMapRequired = true;
+ moduleProperties = m_currentGroup
+ ? m_currentGroup->properties
+ : m_product.product->moduleProperties;
+ }
+ const QVariantMap newModuleProperties = resolveAdditionalModuleProperties(
+ item, moduleProperties->value());
+ if (!newModuleProperties.empty()) {
+ if (newPropertyMapRequired)
+ moduleProperties = PropertyMapInternal::create();
+ moduleProperties->setValue(newModuleProperties);
+ }
+ return moduleProperties;
+ };
+
+ Evaluator &evaluator = m_loaderState.evaluator();
+ QStringList files = evaluator.stringListValue(item, StringConstants::filesProperty());
+ bool fileTagsSet;
+ const FileTags fileTags = evaluator.fileTagsValue(item, StringConstants::fileTagsProperty(),
+ &fileTagsSet);
+ const QStringList fileTagsFilter
+ = evaluator.stringListValue(item, StringConstants::fileTagsFilterProperty());
+ if (!fileTagsFilter.empty()) {
+ if (Q_UNLIKELY(!files.empty()))
+ throw ErrorInfo(Tr::tr("Group.files and Group.fileTagsFilters are exclusive."),
+ item->location());
+
+ if (!isEnabled)
+ return;
+
+ ArtifactPropertiesInfo &apinfo = m_artifactPropertiesPerFilter[fileTagsFilter];
+ if (apinfo.first) {
+ const auto it = std::find_if(apinfo.second.cbegin(), apinfo.second.cend(),
+ [item](const CodeLocation &loc) {
+ return item->location().filePath() == loc.filePath();
+ });
+ if (it != apinfo.second.cend()) {
+ ErrorInfo error(Tr::tr("Conflicting fileTagsFilter in Group items."));
+ error.append(Tr::tr("First item"), *it);
+ error.append(Tr::tr("Second item"), item->location());
+ throw error;
+ }
+ } else {
+ apinfo.first = ArtifactProperties::create();
+ apinfo.first->setFileTagsFilter(FileTags::fromStringList(fileTagsFilter));
+ m_product.product->artifactProperties.push_back(apinfo.first);
+ }
+ apinfo.second.push_back(item->location());
+ apinfo.first->setPropertyMapInternal(getGroupPropertyMap(apinfo.first.get()));
+ apinfo.first->addExtraFileTags(fileTags);
+ return;
+ }
+ QStringList patterns;
+ for (int i = files.size(); --i >= 0;) {
+ if (FileInfo::isPattern(files[i]))
+ patterns.push_back(files.takeAt(i));
+ }
+ GroupPtr group = ResolvedGroup::create();
+ bool prefixWasSet = false;
+ group->prefix = evaluator.stringValue(item, StringConstants::prefixProperty(), QString(),
+ &prefixWasSet);
+ if (!prefixWasSet && m_currentGroup)
+ group->prefix = m_currentGroup->prefix;
+ if (!group->prefix.isEmpty()) {
+ for (auto it = files.rbegin(), end = files.rend(); it != end; ++it)
+ it->prepend(group->prefix);
+ }
+ group->location = item->location();
+ group->enabled = isEnabled;
+ group->properties = getGroupPropertyMap(nullptr);
+ group->fileTags = fileTags;
+ group->overrideTags = evaluator.boolValue(item, StringConstants::overrideTagsProperty());
+ if (group->overrideTags && fileTagsSet) {
+ if (group->fileTags.empty() )
+ group->fileTags.insert(unknownFileTag());
+ } else if (m_currentGroup) {
+ group->fileTags.unite(m_currentGroup->fileTags);
+ }
+
+ const CodeLocation filesLocation = item->property(StringConstants::filesProperty())->location();
+ const VariantValueConstPtr moduleProp = item->variantProperty(
+ StringConstants::modulePropertyInternal());
+ if (moduleProp)
+ group->targetOfModule = moduleProp->value().toString();
+ ErrorInfo fileError;
+ if (!patterns.empty()) {
+ group->wildcards = std::make_unique<SourceWildCards>();
+ SourceWildCards *wildcards = group->wildcards.get();
+ wildcards->excludePatterns = evaluator.stringListValue(
+ item, StringConstants::excludeFilesProperty());
+ wildcards->patterns = patterns;
+ wildcards->prefix = group->prefix;
+ wildcards->baseDir = FileInfo::path(item->file()->filePath());
+ wildcards->buildDir = m_product.project->project->topLevelProject()->buildDirectory;
+ wildcards->expandPatterns();
+ for (const QString &fileName : wildcards->expandedFiles)
+ createSourceArtifact(fileName, group, true, filesLocation, &fileError);
+ }
+
+ for (const QString &fileName : std::as_const(files))
+ createSourceArtifact(fileName, group, false, filesLocation, &fileError);
+ if (fileError.hasError()) {
+ if (group->enabled) {
+ if (m_loaderState.parameters().productErrorMode() == ErrorHandlingMode::Strict)
+ throw ErrorInfo(fileError);
+ m_loaderState.logger().printWarning(fileError);
+ } else {
+ qCDebug(lcProjectResolver) << "error for disabled group:" << fileError.toString();
+ }
+ }
+ group->name = evaluator.stringValue(item, StringConstants::nameProperty());
+ if (group->name.isEmpty())
+ group->name = Tr::tr("Group %1").arg(m_product.product->groups.size());
+ m_product.product->groups.push_back(group);
+
+ class GroupContextSwitcher {
+ public:
+ GroupContextSwitcher(ProductResolverStage2 &resolver, const GroupConstPtr &newGroup)
+ : m_resolver(resolver), m_oldGroup(resolver.m_currentGroup) {
+ resolver.m_currentGroup = newGroup;
+ }
+ ~GroupContextSwitcher() { m_resolver.m_currentGroup = m_oldGroup; }
+ private:
+ ProductResolverStage2 &m_resolver;
+ const GroupConstPtr m_oldGroup;
+ };
+ GroupContextSwitcher groupSwitcher(*this, group);
+ for (Item * const childItem : item->children())
+ resolveGroup(childItem);
+}
+
+SourceArtifactPtr ProductResolverStage2::createSourceArtifact(
+ const QString &fileName, const GroupPtr &group, bool wildcard,
+ const CodeLocation &filesLocation, ErrorInfo *errorInfo)
+{
+ const QString &baseDir = FileInfo::path(group->location.filePath());
+ const QString absFilePath = QDir::cleanPath(FileInfo::resolvePath(baseDir, fileName));
+ if (!wildcard && !FileInfo(absFilePath).exists()) {
+ if (errorInfo)
+ errorInfo->append(Tr::tr("File '%1' does not exist.").arg(absFilePath), filesLocation);
+ m_product.product->missingSourceFiles << absFilePath;
+ return {};
+ }
+ if (group->enabled) {
+ CodeLocation &loc = m_sourceArtifactLocations[
+ std::make_pair(group->targetOfModule, 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 {};
+ }
+ loc = filesLocation;
+ }
+ SourceArtifactPtr artifact = SourceArtifactInternal::create();
+ artifact->absoluteFilePath = absFilePath;
+ artifact->fileTags = group->fileTags;
+ artifact->overrideFileTags = group->overrideTags;
+ artifact->properties = group->properties;
+ artifact->targetOfModule = group->targetOfModule;
+ artifact->fromWildcard = wildcard;
+ group->files.push_back(artifact);
+ return artifact;
+}
+
+static QualifiedIdSet propertiesToEvaluate(std::deque<QualifiedId> initialProps,
+ const PropertyDependencies &deps)
+{
+ std::deque<QualifiedId> remainingProps = std::move(initialProps);
+ QualifiedIdSet allProperties;
+ while (!remainingProps.empty()) {
+ const QualifiedId prop = remainingProps.front();
+ remainingProps.pop_front();
+ const auto insertResult = allProperties.insert(prop);
+ if (!insertResult.second)
+ continue;
+ transform(deps.value(prop), remainingProps, [](const QualifiedId &id) { return id; });
+ }
+ return allProperties;
+}
+
+QVariantMap ProductResolverStage2::resolveAdditionalModuleProperties(
+ const Item *group, const QVariantMap &currentValues)
+{
+ // Step 1: Retrieve the properties directly set in the group
+ const ModulePropertiesPerGroup &mp = m_product.modulePropertiesSetInGroups;
+ const auto it = mp.find(group);
+ if (it == mp.end())
+ return {};
+ const QualifiedIdSet &propsSetInGroup = it->second;
+
+ // Step 2: Gather all properties that depend on these properties.
+ const QualifiedIdSet &propsToEval = propertiesToEvaluate(
+ rangeTo<std::deque<QualifiedId>>(propsSetInGroup),
+ m_loaderState.evaluator().propertyDependencies());
+
+ // Step 3: Evaluate all these properties and replace their values in the map
+ QVariantMap modulesMap = currentValues;
+ QHash<QString, QStringList> propsPerModule;
+ for (auto fullPropName : propsToEval) {
+ const QString moduleName
+ = QualifiedId(fullPropName.mid(0, fullPropName.size() - 1)).toString();
+ propsPerModule[moduleName] << fullPropName.last();
+ }
+ EvalCacheEnabler cachingEnabler(&m_loaderState.evaluator(),
+ m_product.product->sourceDirectory);
+ for (const Item::Module &module : group->modules()) {
+ const QString &fullModName = module.name.toString();
+ const QStringList propsForModule = propsPerModule.take(fullModName);
+ if (propsForModule.empty())
+ continue;
+ QVariantMap reusableValues = modulesMap.value(fullModName).toMap();
+ for (const QString &prop : std::as_const(propsForModule))
+ reusableValues.remove(prop);
+ modulesMap.insert(fullModName, m_propertiesEvaluator.evaluateProperties(
+ module.item, module.item, reusableValues, true, true));
+ }
+ return modulesMap;
+}
+
+static QString getLineAtLocation(const CodeLocation &loc, const QString &content)
+{
+ int pos = 0;
+ int currentLine = 1;
+ while (currentLine < loc.line()) {
+ while (content.at(pos++) != QLatin1Char('\n'))
+ ;
+ ++currentLine;
+ }
+ const int eolPos = content.indexOf(QLatin1Char('\n'), pos);
+ return content.mid(pos, eolPos - pos);
+}
+
+static bool usesImport(const ExportedProperty &prop, const QRegularExpression &regex)
+{
+ return prop.sourceCode.indexOf(regex) != -1;
+}
+
+static bool usesImport(const ExportedItem &item, const QRegularExpression &regex)
+{
+ return any_of(item.properties,
+ [regex](const ExportedProperty &p) { return usesImport(p, regex); })
+ || any_of(item.children,
+ [regex](const ExportedItemPtr &child) { return usesImport(*child, regex); });
+}
+
+static bool usesImport(const ExportedModule &module, const QString &name)
+{
+ // Imports are used in three ways:
+ // (1) var f = new TextFile(...);
+ // (2) var path = FileInfo.joinPaths(...)
+ // (3) var obj = DataCollection;
+ const QString pattern = QStringLiteral("\\b%1\\b");
+
+ const QRegularExpression regex(pattern.arg(name)); // std::regex is much slower
+ return any_of(module.m_properties,
+ [regex](const ExportedProperty &p) { return usesImport(p, regex); })
+ || any_of(module.children,
+ [regex](const ExportedItemPtr &child) { return usesImport(*child, regex); });
+}
+
+void ProductResolverStage2::resolveExport(Item *exportItem)
+{
+ ExportedModule &exportedModule = m_product.product->exportedModule;
+ setupExportedProperties(exportItem, QString(), exportedModule.m_properties);
+ static const auto cmpFunc = [](const ExportedProperty &p1, const ExportedProperty &p2) {
+ return p1.fullName < p2.fullName;
+ };
+ std::sort(exportedModule.m_properties.begin(), exportedModule.m_properties.end(), cmpFunc);
+
+ transform(exportItem->children(), exportedModule.children,
+ [&exportedModule, this](const auto &child) {
+ return resolveExportChild(child, exportedModule); });
+
+ for (const JsImport &jsImport : exportItem->file()->jsImports()) {
+ if (usesImport(exportedModule, jsImport.scopeName)) {
+ exportedModule.importStatements << getLineAtLocation(jsImport.location,
+ exportItem->file()->content());
+ }
+ }
+ const auto builtInImports = JsExtensions::extensionNames();
+ for (const QString &builtinImport: builtInImports) {
+ if (usesImport(exportedModule, builtinImport))
+ exportedModule.importStatements << QStringLiteral("import qbs.") + builtinImport;
+ }
+ exportedModule.importStatements.sort();
+}
+
+// TODO: This probably wouldn't be necessary if we had item serialization.
+std::unique_ptr<ExportedItem> ProductResolverStage2::resolveExportChild(
+ const Item *item, const ExportedModule &module)
+{
+ std::unique_ptr<ExportedItem> exportedItem(new ExportedItem);
+
+ // This is the type of the built-in base item. It may turn out that we need to support
+ // derived items under Export. In that case, we probably need a new Item member holding
+ // the original type name.
+ exportedItem->name = item->typeName();
+
+ transform(item->children(), exportedItem->children, [&module, this](const auto &child) {
+ return resolveExportChild(child, module); });
+
+ setupExportedProperties(item, QString(), exportedItem->properties);
+ return exportedItem;
+}
+
+void ProductResolverStage2::setupExportedProperties(const Item *item, const QString &namePrefix,
+ std::vector<ExportedProperty> &properties)
+{
+ const auto &props = item->properties();
+ for (auto it = props.cbegin(); it != props.cend(); ++it) {
+ const QString qualifiedName = namePrefix.isEmpty()
+ ? it.key() : namePrefix + QLatin1Char('.') + it.key();
+ if ((item->type() == ItemType::Export || item->type() == ItemType::Properties)
+ && qualifiedName == StringConstants::prefixMappingProperty()) {
+ continue;
+ }
+ const ValuePtr &v = it.value();
+ if (v->type() == Value::ItemValueType) {
+ setupExportedProperties(std::static_pointer_cast<ItemValue>(v)->item(),
+ qualifiedName, properties);
+ continue;
+ }
+ ExportedProperty exportedProperty;
+ exportedProperty.fullName = qualifiedName;
+ exportedProperty.type = item->propertyDeclaration(it.key()).type();
+ if (v->type() == Value::VariantValueType) {
+ exportedProperty.sourceCode = toJSLiteral(
+ std::static_pointer_cast<VariantValue>(v)->value());
+ } else {
+ QBS_CHECK(v->type() == Value::JSSourceValueType);
+ const JSSourceValue * const sv = static_cast<JSSourceValue *>(v.get());
+ exportedProperty.sourceCode = sv->sourceCode().toString();
+ }
+ const ItemDeclaration itemDecl
+ = BuiltinDeclarations::instance().declarationsForType(item->type());
+ PropertyDeclaration propertyDecl;
+ const auto itemProperties = itemDecl.properties();
+ for (const PropertyDeclaration &decl : itemProperties) {
+ if (decl.name() == it.key()) {
+ propertyDecl = decl;
+ exportedProperty.isBuiltin = true;
+ break;
+ }
+ }
+
+ // Do not add built-in properties that were left at their default value.
+ if (!exportedProperty.isBuiltin
+ || m_loaderState.evaluator().isNonDefaultValue(item, it.key())) {
+ properties.push_back(exportedProperty);
+ }
+ }
+
+ // Order the list of properties, so the output won't look so random.
+ static const auto less = [](const ExportedProperty &p1, const ExportedProperty &p2) -> bool {
+ const int p1ComponentCount = p1.fullName.count(QLatin1Char('.'));
+ const int p2ComponentCount = p2.fullName.count(QLatin1Char('.'));
+ if (p1.isBuiltin && !p2.isBuiltin)
+ return true;
+ if (!p1.isBuiltin && p2.isBuiltin)
+ return false;
+ if (p1ComponentCount < p2ComponentCount)
+ return true;
+ if (p1ComponentCount > p2ComponentCount)
+ return false;
+ return p1.fullName < p2.fullName;
+ };
+ std::sort(properties.begin(), properties.end(), less);
+}
+
+QVariantMap ProductResolverStage2::evaluateModuleValues(Item *item, bool lookupPrototype)
+{
+ QVariantMap moduleValues;
+ for (const Item::Module &module : item->modules()) {
+ if (!module.item->isPresentModule())
+ continue;
+ const QString fullName = module.name.toString();
+ moduleValues[fullName] = m_propertiesEvaluator.evaluateProperties(
+ module.item, lookupPrototype, true);
+ }
+ return moduleValues;
+}
+
+void ProductResolverStage2::resolveScanner(Item *item, ModuleContext &moduleContext)
+{
+ Evaluator &evaluator = m_loaderState.evaluator();
+ if (!evaluator.boolValue(item, StringConstants::conditionProperty())) {
+ qCDebug(lcProjectResolver) << "scanner condition is false";
+ return;
+ }
+
+ ResolvedScannerPtr scanner = ResolvedScanner::create();
+ scanner->module = moduleContext.module;
+ scanner->inputs = evaluator.fileTagsValue(item, StringConstants::inputsProperty());
+ scanner->recursive = evaluator.boolValue(item, StringConstants::recursiveProperty());
+ scanner->searchPathsScript.initialize(m_loaderState.topLevelProject().scriptFunctionValue(
+ item, StringConstants::searchPathsProperty()));
+ scanner->scanScript.initialize(m_loaderState.topLevelProject().scriptFunctionValue(
+ item, StringConstants::scanProperty()));
+ m_product.product->scanners.push_back(scanner);
+}
+
+void ProductResolverStage2::resolveModules()
+{
+ JobLimits jobLimits;
+ for (const Item::Module &m : m_product.item->modules())
+ resolveModule(m.name, m.item, m.product, m.parameters, jobLimits);
+ for (int i = 0; i < jobLimits.count(); ++i) {
+ const JobLimit &moduleJobLimit = jobLimits.jobLimitAt(i);
+ if (m_product.product->jobLimits.getLimit(moduleJobLimit.pool()) == -1)
+ m_product.product->jobLimits.setJobLimit(moduleJobLimit);
+ }
+}
+
+void ProductResolverStage2::resolveModule(const QualifiedId &moduleName, Item *item, bool isProduct,
+ const QVariantMap &parameters, JobLimits &jobLimits)
+{
+ if (!item->isPresentModule())
+ return;
+
+ ModuleContext moduleContext;
+ moduleContext.module = ResolvedModule::create();
+
+ const ResolvedModulePtr &module = moduleContext.module;
+ module->name = moduleName.toString();
+ module->isProduct = isProduct;
+ module->product = m_product.product.get();
+ module->setupBuildEnvironmentScript.initialize(m_loaderState.topLevelProject()
+ .scriptFunctionValue(item, StringConstants::setupBuildEnvironmentProperty()));
+ module->setupRunEnvironmentScript.initialize(m_loaderState.topLevelProject()
+ .scriptFunctionValue(item, StringConstants::setupRunEnvironmentProperty()));
+
+ for (const Item::Module &m : item->modules()) {
+ if (m.item->isPresentModule())
+ module->moduleDependencies += m.name.toString();
+ }
+
+ m_product.product->modules.push_back(module);
+ if (!parameters.empty())
+ m_product.product->moduleParameters[module] = parameters;
+
+ for (Item *child : item->children()) {
+ switch (child->type()) {
+ case ItemType::Rule:
+ resolveRule(m_loaderState, child, nullptr, &m_product, &moduleContext);
+ break;
+ case ItemType::FileTagger:
+ resolveFileTagger(m_loaderState, child, nullptr, &m_product);
+ break;
+ case ItemType::JobLimit:
+ resolveJobLimit(m_loaderState, child, nullptr, nullptr, &moduleContext);
+ break;
+ case ItemType::Scanner:
+ resolveScanner(child, moduleContext);
+ break;
+ default:
+ break;
+ }
+ }
+ for (int i = 0; i < moduleContext.jobLimits.count(); ++i) {
+ const JobLimit &newJobLimit = moduleContext.jobLimits.jobLimitAt(i);
+ const int oldLimit = jobLimits.getLimit(newJobLimit.pool());
+ if (oldLimit == -1 || oldLimit > newJobLimit.limit())
+ jobLimits.setJobLimit(newJobLimit);
+ }
+}
+
+void ProductResolverStage2::applyFileTaggers()
+{
+ m_product.product->fileTaggers << m_product.project->fileTaggers;
+ m_product.product->fileTaggers = sorted(m_product.product->fileTaggers,
+ [] (const FileTaggerConstPtr &a, const FileTaggerConstPtr &b) {
+ return a->priority() > b->priority();
+ });
+ for (const SourceArtifactPtr &artifact : m_product.product->allEnabledFiles()) {
+ if (!artifact->overrideFileTags || artifact->fileTags.empty()) {
+ const QString fileName = FileInfo::fileName(artifact->absoluteFilePath);
+ const FileTags fileTags = m_product.product->fileTagsForFileName(fileName);
+ artifact->fileTags.unite(fileTags);
+ if (artifact->fileTags.empty())
+ artifact->fileTags.insert(unknownFileTag());
+ qCDebug(lcProjectResolver) << "adding file tags" << artifact->fileTags
+ << "to" << fileName;
+ }
+ }
+}
+
+void ProductResolverStage2::finalizeArtifactProperties()
+{
+ for (const SourceArtifactPtr &artifact : m_product.product->allEnabledFiles()) {
+ for (const auto &artifactProperties : m_product.product->artifactProperties) {
+ if (!artifact->isTargetOfModule()
+ && artifact->fileTags.intersects(artifactProperties->fileTagsFilter())) {
+ // FIXME: Should be merged, not overwritten.
+ artifact->properties = artifactProperties->propertyMap();
+ }
+ }
+
+ // Let a positive value of qbs.install imply the file tag "installable".
+ if (artifact->properties->qbsPropertyValue(StringConstants::installProperty()).toBool())
+ artifact->fileTags += "installable";
+ }
+}
+
+void ProductResolverStage2::collectProductDependencies()
+{
+ const ResolvedProductPtr &product = m_product.product;
+ if (!product)
+ return;
+ for (const Item::Module &module : m_product.item->modules()) {
+ if (!module.product)
+ continue;
+ const ResolvedProductPtr &dep = module.product->product;
+ QBS_CHECK(dep);
+ QBS_CHECK(dep != product);
+ product->dependencies << dep;
+ product->dependencyParameters.insert(dep, module.parameters); // TODO: Streamline this with normal module dependencies?
+ }
+
+ // TODO: We might want to keep the topological sorting and get rid of "module module dependencies".
+ std::sort(product->dependencies.begin(),product->dependencies.end(),
+ [](const ResolvedProductPtr &p1, const ResolvedProductPtr &p2) {
+ return p1->fullDisplayName() < p2->fullDisplayName();
+ });
+}
+
+void ExportsResolver::start()
+{
+ resolveShadowProduct();
+ collectExportedProductDependencies();
+}
+
+void ExportsResolver::resolveShadowProduct()
+{
+ if (!m_product.product->enabled)
+ return;
+ if (!m_product.shadowProduct)
+ return;
+ for (const auto &m : m_product.shadowProduct->item->modules()) {
+ if (m.name.toString() != m_product.product->name)
+ continue;
+ collectPropertiesForExportItem(m.item);
+ for (const auto &dep : m.item->modules())
+ collectPropertiesForModuleInExportItem(dep);
+ break;
+ }
+ try {
+ adaptExportedPropertyValues();
+ } catch (const ErrorInfo &) {}
+}
+
+class TempScopeSetter
+{
+public:
+ TempScopeSetter(const ValuePtr &value, Item *newScope) : m_value(value), m_oldScope(value->scope())
+ {
+ value->setScope(newScope, {});
+ }
+ ~TempScopeSetter() { if (m_value) m_value->setScope(m_oldScope, {}); }
+
+ TempScopeSetter(const TempScopeSetter &) = delete;
+ TempScopeSetter &operator=(const TempScopeSetter &) = delete;
+ TempScopeSetter &operator=(TempScopeSetter &&) = delete;
+
+ TempScopeSetter(TempScopeSetter &&other) noexcept
+ : m_value(std::move(other.m_value)), m_oldScope(other.m_oldScope)
+ {
+ other.m_value.reset();
+ other.m_oldScope = nullptr;
+ }
+
+private:
+ ValuePtr m_value;
+ Item *m_oldScope;
+};
+
+void ExportsResolver::collectPropertiesForExportItem(
+ const QualifiedId &moduleName, const ValuePtr &value, Item *moduleInstance,
+ QVariantMap &moduleProps)
+{
+ QBS_CHECK(value->type() == Value::ItemValueType);
+ Item * const itemValueItem = std::static_pointer_cast<ItemValue>(value)->item();
+ if (itemValueItem->propertyDeclarations().isEmpty()) {
+ for (const Item::Module &module : moduleInstance->modules()) {
+ if (module.name == moduleName) {
+ itemValueItem->setPropertyDeclarations(module.item->propertyDeclarations());
+ break;
+ }
+ }
+ }
+ if (itemValueItem->type() == ItemType::ModuleInstancePlaceholder) {
+ struct EvalPreparer {
+ EvalPreparer(Item *valueItem, const QualifiedId &moduleName)
+ : valueItem(valueItem),
+ hadName(!!valueItem->variantProperty(StringConstants::nameProperty()))
+ {
+ if (!hadName) {
+ // Evaluator expects a name here.
+ valueItem->setProperty(StringConstants::nameProperty(),
+ VariantValue::create(moduleName.toString()));
+ }
+ }
+ ~EvalPreparer()
+ {
+ if (!hadName)
+ valueItem->removeProperty(StringConstants::nameProperty());
+ }
+ Item * const valueItem;
+ const bool hadName;
+ };
+ EvalPreparer ep(itemValueItem, moduleName);
+ std::vector<TempScopeSetter> tss;
+ for (const ValuePtr &v : itemValueItem->properties())
+ tss.emplace_back(v, moduleInstance);
+ moduleProps.insert(moduleName.toString(), m_propertiesEvaluator.evaluateProperties(
+ itemValueItem, false, false));
+ return;
+ }
+ QBS_CHECK(itemValueItem->type() == ItemType::ModulePrefix);
+ const Item::PropertyMap &props = itemValueItem->properties();
+ for (auto it = props.begin(); it != props.end(); ++it) {
+ QualifiedId fullModuleName = moduleName;
+ fullModuleName << it.key();
+ collectPropertiesForExportItem(fullModuleName, it.value(), moduleInstance, moduleProps);
+ }
+}
+
+void ExportsResolver::collectPropertiesForExportItem(Item *productModuleInstance)
+{
+ if (!productModuleInstance->isPresentModule())
+ return;
+ Item * const exportItem = productModuleInstance->prototype();
+ QBS_CHECK(exportItem);
+ QBS_CHECK(exportItem->type() == ItemType::Export);
+ const ItemDeclaration::Properties exportDecls = BuiltinDeclarations::instance()
+ .declarationsForType(ItemType::Export).properties();
+ ExportedModule &exportedModule = m_product.product->exportedModule;
+ const auto &props = exportItem->properties();
+ for (auto it = props.begin(); it != props.end(); ++it) {
+ const auto match
+ = [it](const PropertyDeclaration &decl) { return decl.name() == it.key(); };
+ if (it.key() != StringConstants::prefixMappingProperty() &&
+ std::find_if(exportDecls.begin(), exportDecls.end(), match) != exportDecls.end()) {
+ continue;
+ }
+ if (it.value()->type() == Value::ItemValueType) {
+ collectPropertiesForExportItem(it.key(), it.value(), productModuleInstance,
+ exportedModule.modulePropertyValues);
+ } else {
+ TempScopeSetter tss(it.value(), productModuleInstance);
+ m_propertiesEvaluator.evaluateProperty(
+ exportItem, it.key(), it.value(), exportedModule.propertyValues, false);
+ }
+ }
+}
+
+// Collects module properties assigned to in other (higher-level) modules.
+void ExportsResolver::collectPropertiesForModuleInExportItem(const Item::Module &module)
+{
+ if (!module.item->isPresentModule())
+ return;
+ ExportedModule &exportedModule = m_product.product->exportedModule;
+ if (module.product || module.name.first() == StringConstants::qbsModule())
+ return;
+ const auto checkName = [module](const ExportedModuleDependency &d) {
+ return module.name.toString() == d.name;
+ };
+ if (any_of(exportedModule.moduleDependencies, checkName))
+ return;
+
+ Item *modulePrototype = module.item->prototype();
+ while (modulePrototype && modulePrototype->type() != ItemType::Module)
+ modulePrototype = modulePrototype->prototype();
+ if (!modulePrototype) // Can happen for broken products in relaxed mode.
+ return;
+ ModuleItemLocker locker(*modulePrototype);
+ const Item::PropertyMap &props = modulePrototype->properties();
+ ExportedModuleDependency dep;
+ dep.name = module.name.toString();
+ for (auto it = props.begin(); it != props.end(); ++it) {
+ if (it.value()->type() == Value::ItemValueType)
+ collectPropertiesForExportItem(it.key(), it.value(), module.item, dep.moduleProperties);
+ }
+ exportedModule.moduleDependencies.push_back(dep);
+
+ for (const auto &dep : module.item->modules())
+ collectPropertiesForModuleInExportItem(dep);
+}
+
+void ExportsResolver::adaptExportedPropertyValues()
+{
+ QBS_CHECK(m_product.shadowProduct);
+ ExportedModule &m = m_product.product->exportedModule;
+ const QVariantList prefixList = m.propertyValues.take(
+ StringConstants::prefixMappingProperty()).toList();
+ const QString shadowProductName = m_loaderState.evaluator().stringValue(
+ m_product.shadowProduct->item, StringConstants::nameProperty());
+ const QString shadowProductBuildDir = m_loaderState.evaluator().stringValue(
+ m_product.shadowProduct->item, StringConstants::buildDirectoryProperty());
+ QVariantMap prefixMap;
+ for (const QVariant &v : prefixList) {
+ const QVariantMap o = v.toMap();
+ prefixMap.insert(o.value(QStringLiteral("prefix")).toString(),
+ o.value(QStringLiteral("replacement")).toString());
+ }
+ const auto valueRefersToImportingProduct
+ = [shadowProductName, shadowProductBuildDir](const QString &value) {
+ return value.toLower().contains(shadowProductName.toLower())
+ || value.contains(shadowProductBuildDir);
+ };
+ static const auto stringMapper = [](const QVariantMap &mappings, const QString &value)
+ -> QString {
+ for (auto it = mappings.cbegin(); it != mappings.cend(); ++it) {
+ if (value.startsWith(it.key()))
+ return it.value().toString() + value.mid(it.key().size());
+ }
+ return value;
+ };
+ const auto stringListMapper = [&valueRefersToImportingProduct](
+ const QVariantMap &mappings, const QStringList &value) -> QStringList {
+ QStringList result;
+ result.reserve(value.size());
+ for (const QString &s : value) {
+ if (!valueRefersToImportingProduct(s))
+ result.push_back(stringMapper(mappings, s));
+ }
+ return result;
+ };
+ const std::function<QVariant(const QVariantMap &, const QVariant &)> mapper
+ = [&stringListMapper, &mapper](
+ const QVariantMap &mappings, const QVariant &value) -> QVariant {
+ switch (static_cast<QMetaType::Type>(value.userType())) {
+ case QMetaType::QString:
+ return stringMapper(mappings, value.toString());
+ case QMetaType::QStringList:
+ return stringListMapper(mappings, value.toStringList());
+ case QMetaType::QVariantMap: {
+ QVariantMap m = value.toMap();
+ for (auto it = m.begin(); it != m.end(); ++it)
+ it.value() = mapper(mappings, it.value());
+ return m;
+ }
+ default:
+ return value;
+ }
+ };
+ for (auto it = m.propertyValues.begin(); it != m.propertyValues.end(); ++it)
+ it.value() = mapper(prefixMap, it.value());
+ for (auto it = m.modulePropertyValues.begin(); it != m.modulePropertyValues.end(); ++it)
+ it.value() = mapper(prefixMap, it.value());
+ for (ExportedModuleDependency &dep : m.moduleDependencies) {
+ for (auto it = dep.moduleProperties.begin(); it != dep.moduleProperties.end(); ++it)
+ it.value() = mapper(prefixMap, it.value());
+ }
+}
+
+void ExportsResolver::collectExportedProductDependencies()
+{
+ if (!m_product.shadowProduct)
+ return;
+ const ResolvedProductPtr exportingProduct = m_product.product;
+ if (!exportingProduct || !exportingProduct->enabled)
+ return;
+ Item * const importingProductItem = m_product.shadowProduct->item;
+
+ std::vector<std::pair<ResolvedProductPtr, QVariantMap>> directDeps;
+ for (const Item::Module &m : importingProductItem->modules()) {
+ if (m.name.toString() != exportingProduct->name)
+ continue;
+ for (const Item::Module &dep : m.item->modules()) {
+ if (dep.product)
+ directDeps.emplace_back(dep.product->product, m.parameters);
+ }
+ }
+ for (const auto &dep : directDeps) {
+ if (!contains(exportingProduct->exportedModule.productDependencies,
+ dep.first->uniqueName())) {
+ exportingProduct->exportedModule.productDependencies.push_back(
+ dep.first->uniqueName());
+ }
+ if (!dep.second.isEmpty()) {
+ exportingProduct->exportedModule.dependencyParameters.insert(dep.first,
+ dep.second);
+ }
+ }
+ auto &productDeps = exportingProduct->exportedModule.productDependencies;
+ std::sort(productDeps.begin(), productDeps.end());
+}
+
+QVariantMap PropertiesEvaluator::evaluateProperties(
+ const Item *item, const Item *propertiesContainer, const QVariantMap &tmplt,
+ bool lookupPrototype, bool checkErrors)
+{
+ AccumulatingTimer propEvalTimer(m_loaderState.parameters().logElapsedTime()
+ ? &m_product.timingData.propertyEvaluation
+ : nullptr);
+ QVariantMap result = tmplt;
+ for (auto it = propertiesContainer->properties().begin();
+ it != propertiesContainer->properties().end(); ++it) {
+ evaluateProperty(item, it.key(), it.value(), result, checkErrors);
+ }
+ return lookupPrototype && propertiesContainer->prototype()
+ && propertiesContainer->prototype()->type() != ItemType::Module
+ ? evaluateProperties(item, propertiesContainer->prototype(), result, true, checkErrors)
+ : result;
+}
+
+QVariantMap PropertiesEvaluator::evaluateProperties(Item *item, bool lookupPrototype,
+ bool checkErrors)
+{
+ const QVariantMap tmplt;
+ return evaluateProperties(item, item, tmplt, lookupPrototype, checkErrors);
+}
+
+void PropertiesEvaluator::evaluateProperty(
+ const Item *item, const QString &propName, const ValuePtr &propValue, QVariantMap &result,
+ bool checkErrors)
+{
+ JSContext * const ctx = m_loaderState.evaluator().engine()->context();
+ switch (propValue->type()) {
+ case Value::ItemValueType:
+ {
+ // Ignore items. Those point to module instances
+ // and are handled in evaluateModuleValues().
+ break;
+ }
+ case Value::JSSourceValueType:
+ {
+ if (result.contains(propName))
+ break;
+ const PropertyDeclaration pd = item->propertyDeclaration(propName);
+ if (pd.flags().testFlag(PropertyDeclaration::PropertyNotAvailableInConfig)) {
+ break;
+ }
+ const ScopedJsValue scriptValue(ctx, m_loaderState.evaluator().property(item, propName));
+ if (JsException ex = m_loaderState.evaluator().engine()->checkAndClearException(
+ propValue->location())) {
+ if (checkErrors)
+ throw ex.toErrorInfo();
+ }
+
+ // NOTE: Loses type information if scriptValue.isUndefined == true,
+ // as such QScriptValues become invalid QVariants.
+ QVariant v;
+ if (JS_IsFunction(ctx, scriptValue)) {
+ v = getJsString(ctx, scriptValue);
+ } else {
+ v = getJsVariant(ctx, scriptValue);
+ QVariantMap m = v.toMap();
+ if (m.contains(StringConstants::importScopeNamePropertyInternal())) {
+ QVariantMap tmp = m;
+ const ScopedJsValue proto(ctx, JS_GetPrototype(ctx, scriptValue));
+ m = getJsVariant(ctx, proto).toMap();
+ for (auto it = tmp.begin(); it != tmp.end(); ++it)
+ m.insert(it.key(), it.value());
+ v = m;
+ }
+ }
+
+ if (pd.type() == PropertyDeclaration::Path && v.isValid()) {
+ v = v.toString();
+ } else if (pd.type() == PropertyDeclaration::PathList
+ || pd.type() == PropertyDeclaration::StringList) {
+ v = v.toStringList();
+ } else if (pd.type() == PropertyDeclaration::VariantList) {
+ v = v.toList();
+ }
+
+ // Enforce proper type for undefined values (note that path degrades to string).
+ if (!v.isValid())
+ v = pd.typedNullValue();
+
+ pd.checkAllowedValues(v, propValue->location(), propName, m_loaderState);
+
+ result[propName] = v;
+ break;
+ }
+ case Value::VariantValueType:
+ {
+ if (result.contains(propName))
+ break;
+ VariantValuePtr vvp = std::static_pointer_cast<VariantValue>(propValue);
+ QVariant v = vvp->value();
+
+ const PropertyDeclaration pd = item->propertyDeclaration(propName);
+ if (v.isNull() && !pd.isScalar()) // QTBUG-51237
+ v = QStringList();
+
+ pd.checkAllowedValues(v, propValue->location(), propName, m_loaderState);
+ result[propName] = v;
+ break;
+ }
+ }
+}
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/productresolver.h b/src/lib/corelib/loader/productresolver.h
new file mode 100644
index 000000000..34c49b2bb
--- /dev/null
+++ b/src/lib/corelib/loader/productresolver.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+namespace qbs::Internal {
+enum class Deferral;
+class LoaderState;
+class ProductContext;
+
+// Responsibilities:
+// - Resolving dependencies to modules and other products (via DependenciesResolver).
+// - Module validation.
+// - Running probes (via ProbesResolver) in Product and Module items.
+// - Evaluating product and module properties.
+// - Handling all Product child items, such as FileTaggers, Rules and so on.
+void resolveProduct(ProductContext &product, Deferral deferral, LoaderState &loaderState);
+
+void setupExports(ProductContext &product, LoaderState &loaderState);
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/productscollector.cpp b/src/lib/corelib/loader/productscollector.cpp
new file mode 100644
index 000000000..042dbd160
--- /dev/null
+++ b/src/lib/corelib/loader/productscollector.cpp
@@ -0,0 +1,754 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "productscollector.h"
+
+#include "dependenciesresolver.h"
+#include "itemreader.h"
+#include "loaderutils.h"
+#include "localprofiles.h"
+#include "productitemmultiplexer.h"
+#include "probesresolver.h"
+
+#include <language/builtindeclarations.h>
+#include <language/evaluator.h>
+#include <language/filecontext.h>
+#include <language/item.h>
+#include <language/language.h>
+#include <language/value.h>
+#include <logging/categories.h>
+#include <logging/translator.h>
+#include <tools/fileinfo.h>
+#include <tools/profile.h>
+#include <tools/profiling.h>
+#include <language/scriptengine.h>
+#include <tools/set.h>
+#include <tools/settings.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+
+#include <QDir>
+#include <QDirIterator>
+
+namespace qbs::Internal {
+
+class ProductsCollector::Private
+{
+public:
+ Private(LoaderState &loaderState) : loaderState(loaderState) {}
+
+ void handleProject(Item *projectItem, ProjectContext *parentProject,
+ const Set<QString> &referencedFilePaths);
+ QList<Item *> multiplexProductItem(ProductContext &dummyContext, Item *productItem);
+ void prepareProduct(ProjectContext &projectContext, Item *productItem,
+ ProductContext *mainProduct = nullptr);
+ void handleSubProject(ProjectContext &projectContext, Item *projectItem,
+ const Set<QString> &referencedFilePaths);
+ void copyProperties(const Item *sourceProject, Item *targetProject);
+ QList<Item *> loadReferencedFile(
+ const QString &relativePath, const CodeLocation &referencingLocation,
+ const Set<QString> &referencedFilePaths, ProductContext &dummyContext);
+ bool mergeExportItems(ProductContext &productContext);
+ bool checkExportItemCondition(Item *exportItem, const ProductContext &product);
+ void initProductProperties(const ProductContext &product);
+ void checkProjectNamesInOverrides();
+ void collectProductsByNameAndItem();
+ void checkProductNamesInOverrides();
+ void mergeProperty(Item *dst, const QString &name, const ValuePtr &value);
+
+ LoaderState &loaderState;
+ Settings settings{loaderState.parameters().settingsDirectory()};
+ Set<QString> disabledProjects;
+ Version qbsVersion;
+ Item *tempScopeItem = nullptr;
+
+private:
+ // TODO: Put this in loaderutils
+ class TempBaseModuleAttacher {
+ public:
+ TempBaseModuleAttacher(Private *d, ProductContext &product);
+ ~TempBaseModuleAttacher() { drop(); }
+ void drop();
+ Item *tempBaseModuleItem() const { return m_tempBaseModule; }
+
+ private:
+ Item * const m_productItem;
+ ValuePtr m_origQbsValue;
+ Item *m_tempBaseModule = nullptr;
+ };
+};
+
+ProductsCollector::ProductsCollector(LoaderState &loaderState)
+ : d(makePimpl<Private>(loaderState)) {}
+
+ProductsCollector::~ProductsCollector() = default;
+
+void ProductsCollector::run(Item *rootProject)
+{
+ d->handleProject(rootProject, nullptr, {rootProject->file()->filePath()});
+ d->checkProjectNamesInOverrides();
+ d->collectProductsByNameAndItem();
+ d->checkProductNamesInOverrides();
+ d->loaderState.topLevelProject().checkForLocalProfileAsTopLevelProfile(
+ d->loaderState.parameters().topLevelProfile());
+}
+
+void ProductsCollector::Private::handleProject(Item *projectItem, ProjectContext *parentProject,
+ const Set<QString> &referencedFilePaths)
+{
+ const SetupProjectParameters &parameters = loaderState.parameters();
+ Evaluator &evaluator = loaderState.evaluator();
+ Logger &logger = loaderState.logger();
+ ItemReader &itemReader = loaderState.itemReader();
+ TopLevelProjectContext &topLevelProject = loaderState.topLevelProject();
+
+ auto p = std::make_unique<ProjectContext>();
+ auto &projectContext = *p;
+ projectContext.item = projectItem;
+ projectContext.parent = parentProject;
+ projectContext.topLevelProject = &topLevelProject;
+ ItemValuePtr itemValue = ItemValue::create(projectItem);
+ projectContext.scope = Item::create(&loaderState.itemPool(), ItemType::Scope);
+ projectContext.scope->setFile(projectItem->file());
+ projectContext.scope->setProperty(StringConstants::projectVar(), itemValue);
+ ProductContext dummyProduct;
+ dummyProduct.project = &projectContext;
+ dummyProduct.moduleProperties = parameters.finalBuildConfigurationTree();
+ dummyProduct.profileModuleProperties = dummyProduct.moduleProperties;
+ dummyProduct.profileName = parameters.topLevelProfile();
+ loadBaseModule(dummyProduct, projectItem, loaderState);
+
+ projectItem->overrideProperties(parameters.overriddenValuesTree(),
+ StringConstants::projectPrefix(), parameters, logger);
+ projectContext.name = evaluator.stringValue(projectItem, StringConstants::nameProperty());
+ if (projectContext.name.isEmpty()) {
+ projectContext.name = FileInfo::baseName(projectItem->location().filePath());
+ projectItem->setProperty(StringConstants::nameProperty(),
+ VariantValue::create(projectContext.name));
+ }
+ if (parentProject)
+ parentProject->children.push_back(p.get());
+ topLevelProject.addProject(p.release());
+ projectItem->overrideProperties(parameters.overriddenValuesTree(),
+ StringConstants::projectsOverridePrefix() + projectContext.name,
+ parameters, logger);
+ if (!topLevelProject.checkItemCondition(projectItem, evaluator)) {
+ disabledProjects.insert(projectContext.name);
+ return;
+ }
+ SearchPathsManager searchPathsManager(itemReader, itemReader.readExtraSearchPaths(projectItem)
+ << projectItem->file()->dirPath());
+ projectContext.searchPathsStack = itemReader.extraSearchPathsStack();
+
+ const QString minVersionStr
+ = evaluator.stringValue(projectItem, StringConstants::minimumQbsVersionProperty(),
+ QStringLiteral("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 (!qbsVersion.isValid())
+ qbsVersion = Version::fromString(QLatin1String(QBS_VERSION));
+ if (qbsVersion < minVersion) {
+ throw ErrorInfo(Tr::tr("The project requires at least qbs version %1, but "
+ "this is qbs version %2.").arg(minVersion.toString(),
+ qbsVersion.toString()));
+ }
+
+ for (Item * const child : projectItem->children())
+ child->setScope(projectContext.scope);
+
+ ProbesResolver(loaderState).resolveProbes(dummyProduct, projectItem);
+
+ collectProfilesFromItems(projectItem, projectContext.scope, loaderState);
+
+ QList<Item *> multiplexedProducts;
+ for (Item * const child : projectItem->children()) {
+ if (child->type() == ItemType::Product)
+ multiplexedProducts << multiplexProductItem(dummyProduct, child);
+ }
+ for (Item * const additionalProductItem : std::as_const(multiplexedProducts))
+ Item::addChild(projectItem, additionalProductItem);
+
+ const QList<Item *> originalChildren = projectItem->children();
+ for (Item * const child : originalChildren) {
+ 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(child, &projectContext, referencedFilePaths);
+ break;
+ default:
+ break;
+ }
+ }
+
+ const QStringList refs = evaluator.stringListValue(
+ projectItem, StringConstants::referencesProperty());
+ const CodeLocation referencingLocation
+ = projectItem->property(StringConstants::referencesProperty())->location();
+ QList<Item *> additionalProjectChildren;
+ for (const QString &filePath : refs) {
+ try {
+ additionalProjectChildren << loadReferencedFile(
+ filePath, referencingLocation, referencedFilePaths, dummyProduct);
+ } catch (const ErrorInfo &error) {
+ if (parameters.productErrorMode() == ErrorHandlingMode::Strict)
+ throw;
+ logger.printWarning(error);
+ }
+ }
+ for (Item * const subItem : std::as_const(additionalProjectChildren)) {
+ Item::addChild(projectContext.item, subItem);
+ switch (subItem->type()) {
+ case ItemType::Product:
+ prepareProduct(projectContext, subItem);
+ break;
+ case ItemType::Project:
+ copyProperties(projectItem, subItem);
+ handleProject(subItem, &projectContext,
+ Set<QString>(referencedFilePaths) << subItem->file()->filePath());
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+QList<Item *> ProductsCollector::Private::multiplexProductItem(ProductContext &dummyContext,
+ Item *productItem)
+{
+ // Overriding the product item properties must be done here already, because multiplexing
+ // properties might depend on product properties.
+ const QString &nameKey = StringConstants::nameProperty();
+ QString productName = loaderState.evaluator().stringValue(productItem, nameKey);
+ if (productName.isEmpty()) {
+ productName = FileInfo::completeBaseName(productItem->file()->filePath());
+ productItem->setProperty(nameKey, VariantValue::create(productName));
+ }
+ productItem->overrideProperties(loaderState.parameters().overriddenValuesTree(),
+ StringConstants::productsOverridePrefix() + productName,
+ loaderState.parameters(), loaderState.logger());
+ dummyContext.item = productItem;
+ TempBaseModuleAttacher tbma(this, dummyContext);
+ return multiplex(productName, productItem, tbma.tempBaseModuleItem(),
+ [&] { tbma.drop(); }, loaderState);
+}
+
+void ProductsCollector::Private::prepareProduct(ProjectContext &projectContext, Item *productItem,
+ ProductContext *mainProduct)
+{
+ const SetupProjectParameters &parameters = loaderState.parameters();
+ Evaluator &evaluator = loaderState.evaluator();
+ TopLevelProjectContext &topLevelProject = loaderState.topLevelProject();
+
+ AccumulatingTimer timer(parameters.logElapsedTime()
+ ? &topLevelProject.timingData().preparingProducts : nullptr);
+ topLevelProject.checkCancelation();
+ qCDebug(lcModuleLoader) << "prepareProduct" << productItem->file()->filePath();
+
+ if (mainProduct)
+ mainProduct->shadowProduct = std::make_unique<ProductContext>();
+ else
+ projectContext.products.emplace_back();
+ ProductContext &productContext = mainProduct
+ ? *mainProduct->shadowProduct : projectContext.products.back();
+ productContext.item = productItem;
+ productContext.project = &projectContext;
+
+ // Retrieve name, profile and multiplex id.
+ productContext.name = evaluator.stringValue(productItem, StringConstants::nameProperty());
+ QBS_CHECK(!productContext.name.isEmpty());
+ const ItemValueConstPtr qbsItemValue = productItem->itemProperty(StringConstants::qbsModule(),
+ loaderState.itemPool());
+ if (qbsItemValue && qbsItemValue->item()->hasProperty(StringConstants::profileProperty())) {
+ TempBaseModuleAttacher tbma(this, productContext);
+ productContext.profileName = evaluator.stringValue(
+ tbma.tempBaseModuleItem(), StringConstants::profileProperty(), QString());
+ } else {
+ productContext.profileName = parameters.topLevelProfile();
+ }
+ productContext.multiplexConfigurationId = evaluator.stringValue(
+ productItem, StringConstants::multiplexConfigurationIdProperty());
+ QBS_CHECK(!productContext.profileName.isEmpty());
+
+ // Set up full module property map based on the profile.
+ std::optional<QVariantMap> flatConfig
+ = topLevelProject.profileConfig(productContext.profileName);
+ if (!flatConfig) {
+ const Profile profile(productContext.profileName, &settings,
+ loaderState.topLevelProject().localProfiles());
+ if (!profile.exists()) {
+ ErrorInfo error(Tr::tr("Profile '%1' does not exist.").arg(profile.name()),
+ productItem->location());
+ productContext.handleError(error);
+ return;
+ }
+ flatConfig = SetupProjectParameters::expandedBuildConfiguration(
+ profile, parameters.configurationName());
+ topLevelProject.addProfileConfig(productContext.profileName, *flatConfig);
+ }
+ productContext.profileModuleProperties = SetupProjectParameters::finalBuildConfigurationTree(
+ *flatConfig, {});
+ productContext.moduleProperties = SetupProjectParameters::finalBuildConfigurationTree(
+ *flatConfig, parameters.overriddenValues());
+ initProductProperties(productContext);
+
+ // Set up product scope. This is mainly for using the "product" and "project"
+ // variables in some contexts.
+ ItemValuePtr itemValue = ItemValue::create(productItem);
+ productContext.scope = Item::create(&loaderState.itemPool(), ItemType::Scope);
+ productContext.scope->setProperty(StringConstants::productVar(), itemValue);
+ productContext.scope->setFile(productItem->file());
+ productContext.scope->setScope(productContext.project->scope);
+
+ // If there are any child items with an id, set up a scope for them. This is mostly
+ // relevant for Probe items. While we might get away with using the file's id scope
+ // in the absence of multiplexing, having a proper per-product scope seems cleaner.
+ QBS_CHECK(productItem->scope());
+ QBS_CHECK(productItem->scope() == productContext.project->scope);
+ for (Item * const child : productItem->children()) {
+ if (child->id().isEmpty())
+ continue;
+ if (productItem->scope() == productContext.project->scope) {
+ productItem->setScope(Item::create(&loaderState.itemPool(), ItemType::Scope));
+ productItem->scope()->setScope(productContext.project->scope);
+ }
+ const ItemValuePtr childValue = ItemValue::create(child);
+ productItem->scope()->setProperty(child->id(), childValue);
+ productContext.scope->setProperty(child->id(), childValue);
+ }
+
+ const bool hasExportItems = mergeExportItems(productContext);
+
+ setScopeForDescendants(productItem, productContext.scope);
+
+ if (!hasExportItems)
+ return;
+
+ // This "shadow product" exists only to pull in a dependency on the actual product
+ // and nothing else, thus providing us with the pure environment that we need to
+ // evaluate the product's exported properties in isolation in the project resolver.
+ Item * const importer = Item::create(&loaderState.itemPool(), ItemType::Product);
+ importer->setProperty(QStringLiteral("name"),
+ VariantValue::create(StringConstants::shadowProductPrefix()
+ + productContext.name));
+ importer->setFile(productItem->file());
+ importer->setLocation(productItem->location());
+ importer->setScope(projectContext.scope);
+ importer->setupForBuiltinType(parameters.deprecationWarningMode(), loaderState.logger());
+ Item * const dependsItem = Item::create(&loaderState.itemPool(), ItemType::Depends);
+ dependsItem->setProperty(QStringLiteral("name"), VariantValue::create(productContext.name));
+ dependsItem->setProperty(QStringLiteral("required"), VariantValue::create(false));
+ dependsItem->setFile(importer->file());
+ dependsItem->setLocation(importer->location());
+ dependsItem->setupForBuiltinType(parameters.deprecationWarningMode(), loaderState.logger());
+ dependsItem->setProperty(StringConstants::multiplexConfigurationIdsProperty(),
+ VariantValue::create(productContext.multiplexConfigurationId));
+ Item::addChild(importer, dependsItem);
+ prepareProduct(projectContext, importer, &productContext);
+}
+
+void ProductsCollector::Private::handleSubProject(
+ ProjectContext &projectContext, Item *projectItem, const Set<QString> &referencedFilePaths)
+{
+ const SetupProjectParameters &parameters = loaderState.parameters();
+ Evaluator &evaluator = loaderState.evaluator();
+ Logger &logger = loaderState.logger();
+ ItemReader &itemReader = loaderState.itemReader();
+ TopLevelProjectContext &topLevelProject = loaderState.topLevelProject();
+
+ qCDebug(lcModuleLoader) << "handleSubProject" << projectItem->file()->filePath();
+
+ Item * const propertiesItem = projectItem->child(ItemType::PropertiesInSubProject);
+ if (!topLevelProject.checkItemCondition(projectItem, evaluator))
+ return;
+ if (propertiesItem) {
+ propertiesItem->setScope(projectItem);
+ if (!topLevelProject.checkItemCondition(propertiesItem, evaluator))
+ return;
+ }
+
+ Item *loadedItem = nullptr;
+ QString subProjectFilePath;
+ try {
+ const QString projectFileDirPath = FileInfo::path(projectItem->file()->filePath());
+ const QString relativeFilePath
+ = evaluator.stringValue(projectItem, StringConstants::filePathProperty());
+ 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 = itemReader.setupItemFromFile(subProjectFilePath, projectItem->location());
+ } catch (const ErrorInfo &error) {
+ if (parameters.productErrorMode() == ErrorHandlingMode::Strict)
+ throw;
+ logger.printWarning(error);
+ return;
+ }
+
+ loadedItem = itemReader.wrapInProjectIfNecessary(loadedItem);
+ const bool inheritProperties = evaluator.boolValue(
+ projectItem, StringConstants::inheritPropertiesProperty());
+
+ if (inheritProperties)
+ copyProperties(projectItem->parent(), loadedItem);
+ if (propertiesItem) {
+ const Item::PropertyMap &overriddenProperties = propertiesItem->properties();
+ for (auto it = overriddenProperties.begin(); it != overriddenProperties.end(); ++it)
+ loadedItem->setProperty(it.key(), it.value());
+ }
+
+ Item::addChild(projectItem, loadedItem);
+ projectItem->setScope(projectContext.scope);
+ handleProject(loadedItem, &projectContext, Set<QString>(referencedFilePaths) << subProjectFilePath);
+}
+
+void ProductsCollector::Private::copyProperties(const Item *sourceProject, Item *targetProject)
+{
+ if (!sourceProject)
+ return;
+ const QList<PropertyDeclaration> builtinProjectProperties
+ = BuiltinDeclarations::instance().declarationsForType(ItemType::Project).properties();
+ Set<QString> builtinProjectPropertyNames;
+ for (const PropertyDeclaration &p : builtinProjectProperties)
+ builtinProjectPropertyNames << p.name();
+
+ for (auto it = sourceProject->propertyDeclarations().begin();
+ it != sourceProject->propertyDeclarations().end(); ++it) {
+
+ // We must not inherit built-in properties such as "name",
+ // but there are exceptions.
+ if (it.key() == StringConstants::qbsSearchPathsProperty()
+ || it.key() == StringConstants::profileProperty()
+ || it.key() == StringConstants::buildDirectoryProperty()
+ || it.key() == StringConstants::sourceDirectoryProperty()
+ || it.key() == StringConstants::minimumQbsVersionProperty()) {
+ const JSSourceValueConstPtr &v = targetProject->sourceProperty(it.key());
+ QBS_ASSERT(v, continue);
+ if (v->sourceCode() == StringConstants::undefinedValue())
+ sourceProject->copyProperty(it.key(), targetProject);
+ continue;
+ }
+
+ if (builtinProjectPropertyNames.contains(it.key()))
+ continue;
+
+ if (targetProject->hasOwnProperty(it.key()))
+ continue; // Ignore stuff the target project already has.
+
+ targetProject->setPropertyDeclaration(it.key(), it.value());
+ sourceProject->copyProperty(it.key(), targetProject);
+ }
+}
+
+QList<Item *> ProductsCollector::Private::loadReferencedFile(
+ const QString &relativePath, const CodeLocation &referencingLocation,
+ const Set<QString> &referencedFilePaths, ProductContext &dummyContext)
+{
+ QString absReferencePath = FileInfo::resolvePath(FileInfo::path(referencingLocation.filePath()),
+ relativePath);
+ if (FileInfo(absReferencePath).isDir()) {
+ QString qbsFilePath;
+
+ QDirIterator dit(absReferencePath, StringConstants::qbsFileWildcards());
+ 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 = loaderState.itemReader().setupItemFromFile(
+ absReferencePath, referencingLocation);
+ 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<Item *> loadedItems;
+ loadedItems << subItem;
+ if (subItem->type() == ItemType::Product) {
+ collectProfilesFromItems(subItem, dummyContext.project->scope, loaderState);
+ loadedItems << multiplexProductItem(dummyContext, subItem);
+ }
+ return loadedItems;
+}
+
+void ProductsCollector::Private::mergeProperty(Item *dst, const QString &name,
+ const ValuePtr &value)
+{
+ if (value->type() == Value::ItemValueType) {
+ const ItemValueConstPtr itemValue = std::static_pointer_cast<ItemValue>(value);
+ const Item * const valueItem = itemValue->item();
+ Item * const subItem = dst->itemProperty(name, itemValue, loaderState.itemPool())->item();
+ for (auto it = valueItem->properties().begin(); it != valueItem->properties().end(); ++it)
+ mergeProperty(subItem, it.key(), it.value());
+ return;
+ }
+
+ // If the property already exists, set up the base value.
+ if (value->type() == Value::JSSourceValueType) {
+ const auto jsValue = static_cast<JSSourceValue *>(value.get());
+ if (jsValue->isBuiltinDefaultValue())
+ return;
+ const ValuePtr baseValue = dst->property(name);
+ if (baseValue) {
+ QBS_CHECK(baseValue->type() == Value::JSSourceValueType);
+ const JSSourceValuePtr jsBaseValue = std::static_pointer_cast<JSSourceValue>(
+ baseValue->clone(loaderState.itemPool()));
+ jsValue->setBaseValue(jsBaseValue);
+ std::vector<JSSourceValue::Alternative> alternatives = jsValue->alternatives();
+ jsValue->clearAlternatives();
+ for (JSSourceValue::Alternative &a : alternatives) {
+ a.value->setBaseValue(jsBaseValue);
+ jsValue->addAlternative(a);
+ }
+ }
+ }
+ dst->setProperty(name, value);
+}
+
+bool ProductsCollector::Private::mergeExportItems(ProductContext &productContext)
+{
+ const SetupProjectParameters &parameters = loaderState.parameters();
+ Evaluator &evaluator = loaderState.evaluator();
+ Logger &logger = loaderState.logger();
+ TopLevelProjectContext &topLevelProject = loaderState.topLevelProject();
+
+ std::vector<Item *> exportItems;
+ QList<Item *> children = productContext.item->children();
+
+ const auto isExport = [](Item *item) { return item->type() == ItemType::Export; };
+ std::copy_if(children.cbegin(), children.cend(), std::back_inserter(exportItems), isExport);
+ qbs::Internal::removeIf(children, isExport);
+
+ // 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.empty())
+ productContext.item->setChildren(children);
+
+ Item *merged = Item::create(&loaderState.itemPool(), ItemType::Export);
+ const QString &nameKey = StringConstants::nameProperty();
+ const ValuePtr nameValue = VariantValue::create(productContext.name);
+ merged->setProperty(nameKey, nameValue);
+ Set<FileContextConstPtr> filesWithExportItem;
+ QVariantMap defaultParameters;
+ for (Item * const exportItem : exportItems) {
+ topLevelProject.checkCancelation();
+ if (Q_UNLIKELY(filesWithExportItem.contains(exportItem->file())))
+ throw ErrorInfo(Tr::tr("Multiple Export items in one product are prohibited."),
+ exportItem->location());
+ exportItem->setProperty(nameKey, nameValue);
+ if (!checkExportItemCondition(exportItem, productContext))
+ continue;
+ filesWithExportItem += exportItem->file();
+ for (Item * const child : exportItem->children()) {
+ if (child->type() == ItemType::Parameters) {
+ adjustParametersScopes(child, child);
+ defaultParameters = mergeDependencyParameters(defaultParameters,
+ getJsVariant(evaluator.engine()->context(),
+ evaluator.scriptValue(child)).toMap());
+ } else {
+ 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, parameters, logger);
+ }
+ merged->setPropertyDeclaration(newDecl.name(), newDecl);
+ }
+ for (QMap<QString, ValuePtr>::const_iterator it = exportItem->properties().constBegin();
+ it != exportItem->properties().constEnd(); ++it) {
+ mergeProperty(merged, it.key(), it.value());
+ }
+ }
+ merged->setFile(exportItems.empty()
+ ? productContext.item->file() : exportItems.back()->file());
+ merged->setLocation(exportItems.empty()
+ ? productContext.item->location() : exportItems.back()->location());
+ Item::addChild(productContext.item, merged);
+ merged->setupForBuiltinType(parameters.deprecationWarningMode(), logger);
+
+ productContext.mergedExportItem = merged;
+ productContext.defaultParameters = defaultParameters;
+ return !exportItems.empty();
+
+}
+
+// TODO: This seems dubious. Can we merge the conditions instead?
+bool ProductsCollector::Private::checkExportItemCondition(Item *exportItem,
+ const ProductContext &product)
+{
+ class ScopeHandler {
+ public:
+ ScopeHandler(Item *exportItem, const ProductContext &productContext, Item **cachedScopeItem,
+ ItemPool &itemPool) : m_exportItem(exportItem), m_itemPool(itemPool)
+ {
+ if (!*cachedScopeItem)
+ *cachedScopeItem = Item::create(&m_itemPool, ItemType::Scope);
+ Item * const scope = *cachedScopeItem;
+ QBS_CHECK(productContext.item->file());
+ scope->setFile(productContext.item->file());
+ scope->setScope(productContext.item);
+ productContext.project->scope->copyProperty(StringConstants::projectVar(), scope);
+ productContext.scope->copyProperty(StringConstants::productVar(), scope);
+ QBS_CHECK(!exportItem->scope());
+ exportItem->setScope(scope);
+ }
+ ~ScopeHandler() { m_exportItem->setScope(nullptr); }
+
+ private:
+ Item * const m_exportItem;
+ ItemPool &m_itemPool;
+ } scopeHandler(exportItem, product, &tempScopeItem, loaderState.itemPool());
+ return loaderState.topLevelProject().checkItemCondition(exportItem, loaderState.evaluator());
+}
+
+void ProductsCollector::Private::initProductProperties(const ProductContext &product)
+{
+ QString buildDir = ResolvedProduct::deriveBuildDirectoryName(product.name,
+ product.multiplexConfigurationId);
+ buildDir = FileInfo::resolvePath(product.project->topLevelProject->buildDirectory(), buildDir);
+ product.item->setProperty(StringConstants::buildDirectoryProperty(),
+ VariantValue::create(buildDir));
+ const QString sourceDir = QFileInfo(product.item->file()->filePath()).absolutePath();
+ product.item->setProperty(StringConstants::sourceDirectoryProperty(),
+ VariantValue::create(sourceDir));
+}
+
+void ProductsCollector::Private::checkProjectNamesInOverrides()
+{
+ for (const QString &projectNameInOverride
+ : loaderState.topLevelProject().projectNamesUsedInOverrides()) {
+ if (disabledProjects.contains(projectNameInOverride))
+ continue;
+ if (!any_of(loaderState.topLevelProject().projects(),
+ [&projectNameInOverride](const ProjectContext *p) {
+ return p->name == projectNameInOverride; })) {
+ handlePropertyError(Tr::tr("Unknown project '%1' in property override.")
+ .arg(projectNameInOverride),
+ loaderState.parameters(), loaderState.logger());
+ }
+ }
+}
+
+void ProductsCollector::Private::collectProductsByNameAndItem()
+{
+ TopLevelProjectContext &topLevelProject = loaderState.topLevelProject();
+ for (ProjectContext * const project : topLevelProject.projects()) {
+ for (ProductContext &product : project->products)
+ topLevelProject.addProduct(product);
+ }
+}
+
+void ProductsCollector::Private::checkProductNamesInOverrides()
+{
+ TopLevelProjectContext &topLevelProject = loaderState.topLevelProject();
+ for (const QString &productNameInOverride : topLevelProject.productNamesUsedInOverrides()) {
+ if (!topLevelProject.productWithNameAndConstraint(
+ productNameInOverride, [&productNameInOverride](const ProductContext &product) {
+ // In an override string such as "a.b.c:d, we cannot tell whether we have a product
+ // "a" and a module "b.c" or a product "a.b" and a module "c", so we need to take
+ // care not to emit false positives here.
+ return product.name == productNameInOverride
+ || product.name.startsWith(productNameInOverride + StringConstants::dot());
+ })) {
+ handlePropertyError(Tr::tr("Unknown product '%1' in property override.")
+ .arg(productNameInOverride),
+ loaderState.parameters(), loaderState.logger());
+ }
+ }
+}
+
+ProductsCollector::Private::TempBaseModuleAttacher::TempBaseModuleAttacher(
+ ProductsCollector::Private *d, ProductContext &product)
+ : m_productItem(product.item)
+{
+ const ValuePtr qbsValue = m_productItem->property(StringConstants::qbsModule());
+
+ // Cloning is necessary because the original value will get "instantiated" now.
+ if (qbsValue)
+ m_origQbsValue = qbsValue->clone(d->loaderState.itemPool());
+
+ m_tempBaseModule = loadBaseModule(product, m_productItem, d->loaderState);
+}
+
+void ProductsCollector::Private::TempBaseModuleAttacher::drop()
+{
+ if (!m_tempBaseModule)
+ return;
+
+ // "Unload" the qbs module again.
+ if (m_origQbsValue)
+ m_productItem->setProperty(StringConstants::qbsModule(), m_origQbsValue);
+ else
+ m_productItem->removeProperty(StringConstants::qbsModule());
+ m_productItem->removeModules();
+ m_tempBaseModule = nullptr;
+}
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/productscollector.h b/src/lib/corelib/loader/productscollector.h
new file mode 100644
index 000000000..246361b37
--- /dev/null
+++ b/src/lib/corelib/loader/productscollector.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <tools/pimpl.h>
+
+namespace qbs::Internal {
+class Item;
+class LoaderState;
+
+// Traverses the root project item and fills the TopLevelProjectContext with all the
+// product and sub-project information, including those coming from referenced files.
+class ProductsCollector
+{
+public:
+ ProductsCollector(LoaderState &loaderState);
+ ~ProductsCollector();
+
+ void run(Item *rootProject);
+
+private:
+ class Private;
+ Pimpl<Private> d;
+};
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/productsresolver.cpp b/src/lib/corelib/loader/productsresolver.cpp
new file mode 100644
index 000000000..997a4dc57
--- /dev/null
+++ b/src/lib/corelib/loader/productsresolver.cpp
@@ -0,0 +1,599 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "productsresolver.h"
+
+#include "itemreader.h"
+#include "loaderutils.h"
+#include "productresolver.h"
+
+#include <language/language.h>
+#include <language/scriptengine.h>
+#include <logging/categories.h>
+#include <logging/translator.h>
+#include <tools/profiling.h>
+#include <tools/progressobserver.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stringconstants.h>
+
+#include <algorithm>
+#include <condition_variable>
+#include <future>
+#include <mutex>
+#include <queue>
+#include <system_error>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+namespace qbs::Internal {
+namespace {
+struct ThreadInfo {
+ ThreadInfo(std::future<void> &&future, LoaderState &loaderState)
+ : future(std::move(future)), loaderState(loaderState)
+ {}
+ std::future<void> future;
+ LoaderState &loaderState;
+ bool done = false;
+};
+
+struct ProductWithLoaderState {
+ ProductWithLoaderState(ProductContext &product, LoaderState *loaderState)
+ : product(&product), loaderState(loaderState) {}
+ ProductContext * const product;
+ LoaderState *loaderState;
+};
+
+class ThreadsLocker {
+public:
+ ThreadsLocker(std::launch mode, std::mutex &mutex) {
+ if (mode == std::launch::async)
+ lock = std::make_unique<std::unique_lock<std::mutex>>(mutex);
+ }
+ std::unique_ptr<std::unique_lock<std::mutex>> lock;
+};
+} // namespace
+
+class ProductsResolver
+{
+public:
+ ProductsResolver(LoaderState &loaderState) : m_loaderState(loaderState) {}
+ void resolve();
+
+private:
+ void initialize();
+ void initializeProductQueue();
+ void initializeLoaderStatePool();
+ void runScheduler();
+ void scheduleNext();
+ bool tryToReserveLoaderState(ProductWithLoaderState &product, Deferral deferral);
+ std::optional<std::pair<ProductContext *, Deferral>>
+ unblockProductWaitingForLoaderState(LoaderState &loaderState);
+ void startJob(const ProductWithLoaderState &product, Deferral deferral);
+ void checkForCancelation();
+ void handleFinishedThreads();
+ void queueProductForScheduling(const ProductWithLoaderState &product, Deferral deferral);
+ void waitForSingleDependency(const ProductWithLoaderState &product, ProductContext &dependency);
+ void waitForBulkDependency(const ProductWithLoaderState &product);
+ void unblockProductsWaitingForDependency(ProductContext &finishedProduct);
+ void postProcess();
+ void checkForMissedBulkDependencies(const ProductContext &product);
+
+ static int dependsItemCount(const Item *item);
+ static int dependsItemCount(ProductContext &product);
+
+ LoaderState &m_loaderState;
+ std::queue<std::pair<ProductWithLoaderState, int>> m_productsToSchedule;
+ std::vector<ProductContext *> m_finishedProducts;
+ std::unordered_map<ProductContext *,
+ std::vector<ProductWithLoaderState>> m_waitingForSingleDependency;
+ std::vector<ProductWithLoaderState> m_waitingForBulkDependency;
+ std::unordered_map<LoaderState *, std::queue<std::pair<ProductContext *, Deferral>>> m_waitingForLoaderState;
+ std::unordered_map<ProductContext *, ThreadInfo> m_runningThreads;
+ std::mutex m_threadsMutex;
+ std::condition_variable m_threadsNotifier;
+ std::vector<std::unique_ptr<ScriptEngine>> m_enginePool;
+ std::vector<std::unique_ptr<LoaderState>> m_loaderStatePool;
+ std::vector<LoaderState *> m_availableLoaderStates;
+ std::mutex m_cancelingMutex;
+ std::launch m_asyncMode = std::launch::async;
+ int m_maxJobCount = m_loaderState.parameters().maxJobCount();
+ bool m_canceling = false;
+};
+
+void resolveProducts(LoaderState &loaderState)
+{
+ ProductsResolver(loaderState).resolve();
+}
+
+void ProductsResolver::resolve()
+{
+ initialize();
+ try {
+ runScheduler();
+ } catch (const ErrorInfo &e) {
+ for (auto &thread : m_runningThreads)
+ thread.second.future.wait();
+ throw e;
+ }
+ postProcess();
+}
+
+void ProductsResolver::initialize()
+{
+ initializeProductQueue();
+ initializeLoaderStatePool();
+}
+
+void ProductsResolver::initializeProductQueue()
+{
+ TopLevelProjectContext &topLevelProject = m_loaderState.topLevelProject();
+ std::vector<ProductContext *> sortedProducts;
+ for (ProjectContext * const projectContext : topLevelProject.projects()) {
+ for (ProductContext &product : projectContext->products) {
+ topLevelProject.addProductToHandle(product);
+ const auto it = std::lower_bound(sortedProducts.begin(), sortedProducts.end(), product,
+ [&product](ProductContext *p1, const ProductContext &) {
+ return dependsItemCount(*p1) < dependsItemCount(product);
+ });
+ sortedProducts.insert(it, &product);
+ }
+ }
+
+ for (ProductContext * const product : sortedProducts) {
+ queueProductForScheduling(ProductWithLoaderState(*product, nullptr), Deferral::Allowed);
+ if (product->shadowProduct) {
+ topLevelProject.addProductToHandle(*product->shadowProduct);
+ queueProductForScheduling(ProductWithLoaderState(*product->shadowProduct, nullptr),
+ Deferral::Allowed);
+ }
+ }
+}
+
+void ProductsResolver::initializeLoaderStatePool()
+{
+ TopLevelProjectContext &topLevelProject = m_loaderState.topLevelProject();
+
+ // Adapt max job count: It makes no sense to have it be higher than the number of products
+ // or what can actually be run concurrently. In both cases, we would simply waste resources.
+ const int maxConcurrency = std::thread::hardware_concurrency();
+ if (maxConcurrency > 0 && maxConcurrency < m_maxJobCount)
+ m_maxJobCount = maxConcurrency;
+ if (m_maxJobCount > topLevelProject.productsToHandleCount())
+ m_maxJobCount = topLevelProject.productsToHandleCount();
+
+ // The number of engines and loader states we need to allocate here is one less than the
+ // total number of concurrent jobs, as we already have one loader state that we can re-use.
+ if (m_maxJobCount > 1)
+ m_enginePool.reserve(m_maxJobCount - 1);
+ m_loaderStatePool.reserve(m_enginePool.size());
+ m_availableLoaderStates.reserve(m_enginePool.size() + 1);
+ m_availableLoaderStates.push_back(&m_loaderState);
+ for (std::size_t i = 0; i < m_enginePool.capacity(); ++i) {
+ ScriptEngine &engine = *m_enginePool.emplace_back(
+ ScriptEngine::create(m_loaderState.logger(), EvalContext::PropertyEvaluation));
+ ItemPool &itemPool = topLevelProject.createItemPool();
+ engine.setEnvironment(m_loaderState.parameters().adjustedEnvironment());
+ auto loaderState = std::make_unique<LoaderState>(
+ m_loaderState.parameters(), topLevelProject, itemPool, engine,
+ m_loaderState.logger());
+ m_loaderStatePool.push_back(std::move(loaderState));
+ m_availableLoaderStates.push_back(m_loaderStatePool.back().get());
+ if (topLevelProject.progressObserver())
+ topLevelProject.progressObserver()->addScriptEngine(m_enginePool.back().get());
+ }
+ qCDebug(lcLoaderScheduling) << "using" << m_availableLoaderStates.size() << "loader states";
+ if (int(m_availableLoaderStates.size()) == 1)
+ m_asyncMode = std::launch::deferred;
+}
+
+void ProductsResolver::runScheduler()
+{
+ AccumulatingTimer timer(m_loaderState.parameters().logElapsedTime()
+ ? &m_loaderState.topLevelProject().timingData().resolvingProducts
+ : nullptr);
+
+ ThreadsLocker threadsLock(m_asyncMode, m_threadsMutex);
+ while (true) {
+ scheduleNext();
+ if (m_runningThreads.empty())
+ break;
+ if (m_asyncMode == std::launch::async) {
+ qCDebug(lcLoaderScheduling()) << "scheduling paused, waiting for threads to finish";
+ m_threadsNotifier.wait(*threadsLock.lock);
+ }
+ checkForCancelation();
+ handleFinishedThreads();
+ }
+
+ QBS_CHECK(m_productsToSchedule.empty());
+ QBS_CHECK(m_loaderState.topLevelProject().productsToHandleCount() == 0);
+ QBS_CHECK(m_runningThreads.empty());
+ QBS_CHECK(m_waitingForSingleDependency.empty());
+ QBS_CHECK(m_waitingForBulkDependency.empty());
+}
+
+void ProductsResolver::scheduleNext()
+{
+ TopLevelProjectContext &topLevelProject = m_loaderState.topLevelProject();
+ AccumulatingTimer timer(m_loaderState.parameters().logElapsedTime()
+ ? &topLevelProject.timingData().schedulingProducts : nullptr);
+ while (m_maxJobCount > int(m_runningThreads.size()) && !m_productsToSchedule.empty()) {
+ auto [product, toHandleCountOnInsert] = m_productsToSchedule.front();
+ m_productsToSchedule.pop();
+
+ qCDebug(lcLoaderScheduling) << "potentially scheduling product"
+ << product.product->displayName()
+ << "unhandled product count on queue insertion"
+ << toHandleCountOnInsert << "current unhandled product count"
+ << topLevelProject.productsToHandleCount();
+
+ // If the number of unfinished products has shrunk since the last time we tried handling
+ // this product, there has been forward progress and we can allow a deferral.
+ const Deferral deferral = toHandleCountOnInsert == -1
+ || toHandleCountOnInsert > topLevelProject.productsToHandleCount()
+ ? Deferral::Allowed : Deferral::NotAllowed;
+
+ if (!tryToReserveLoaderState(product, deferral))
+ continue;
+
+ startJob(product, deferral);
+ }
+
+ // There are jobs running, so forward progress is still possible.
+ if (!m_runningThreads.empty())
+ return;
+
+ QBS_CHECK(m_productsToSchedule.empty());
+
+ // If we end up here, nothing was scheduled in the loop above, which means that either ...
+ // a) ... we are done or
+ // b) ... we finally need to schedule our bulk dependencies or
+ // c) ... we need to schedule products waiting for an unhandled dependency.
+ // In the latter case, the project has at least one dependency cycle, and the
+ // DependencyResolver will emit an error.
+
+ // a)
+ if (m_waitingForBulkDependency.empty() && m_waitingForSingleDependency.empty())
+ return;
+
+ // b)
+ for (const ProductWithLoaderState &product : m_waitingForBulkDependency)
+ queueProductForScheduling(product, Deferral::NotAllowed);
+ if (!m_productsToSchedule.empty()) {
+ m_waitingForBulkDependency.clear();
+ scheduleNext();
+ return;
+ }
+
+ // c)
+ for (const auto &e : m_waitingForSingleDependency) {
+ for (const ProductWithLoaderState &p : e.second)
+ queueProductForScheduling(p, Deferral::NotAllowed);
+ }
+ QBS_CHECK(!m_productsToSchedule.empty());
+ scheduleNext();
+}
+
+bool ProductsResolver::tryToReserveLoaderState(ProductWithLoaderState &product, Deferral deferral)
+{
+ QBS_CHECK(!m_availableLoaderStates.empty());
+ if (!product.loaderState) {
+ product.loaderState = m_availableLoaderStates.back();
+ m_availableLoaderStates.pop_back();
+ return true;
+ }
+ if (const auto it = std::find(m_availableLoaderStates.begin(), m_availableLoaderStates.end(),
+ product.loaderState); it != m_availableLoaderStates.end()) {
+ m_availableLoaderStates.erase(it);
+ return true;
+ }
+ qCDebug(lcLoaderScheduling) << "loader state" << product.loaderState << " for product"
+ << product.product->displayName()
+ << "not available, adding product to wait queue";
+ m_waitingForLoaderState[product.loaderState].push({product.product, deferral});
+ return false;
+}
+
+std::optional<std::pair<ProductContext *, Deferral>>
+ProductsResolver::unblockProductWaitingForLoaderState(LoaderState &loaderState)
+{
+ auto &waitingForLoaderState = m_waitingForLoaderState[&loaderState];
+ if (waitingForLoaderState.empty())
+ return {};
+ const auto product = waitingForLoaderState.front();
+ waitingForLoaderState.pop();
+ qCDebug(lcLoaderScheduling) << "loader state" << &loaderState << "now available for product"
+ << product.first->displayName();
+ return product;
+}
+
+void ProductsResolver::startJob(const ProductWithLoaderState &product, Deferral deferral)
+{
+ QBS_CHECK(product.loaderState);
+ qCDebug(lcLoaderScheduling) << "scheduling product" << product.product->displayName()
+ << "with loader state" << product.loaderState
+ << "and deferral mode" << int(deferral);
+ try {
+ const auto it = m_runningThreads.emplace(product.product, ThreadInfo(std::async(m_asyncMode,
+ [this, product, deferral] {
+ product.loaderState->itemReader().setExtraSearchPathsStack(
+ product.product->project->searchPathsStack);
+ resolveProduct(*product.product, deferral, *product.loaderState);
+
+ // The search paths stack can change during dependency resolution
+ // (due to module providers); check that we've rolled back all the changes
+ QBS_CHECK(product.loaderState->itemReader().extraSearchPathsStack()
+ == product.product->project->searchPathsStack);
+
+ std::lock_guard cancelingLock(m_cancelingMutex);
+ if (m_canceling)
+ return;
+ ThreadsLocker threadsLock(m_asyncMode, m_threadsMutex);
+ if (const auto it = m_runningThreads.find(product.product);
+ it != m_runningThreads.end()) {
+ it->second.done = true;
+ qCDebug(lcLoaderScheduling) << "thread for product"
+ << product.product->displayName()
+ << "finished, waking up scheduler";
+ m_threadsNotifier.notify_one();
+ }
+ }), *product.loaderState));
+
+ // With just one worker thread, the notify/wait overhead would be excessive, so
+ // we run the task synchronously.
+ if (m_asyncMode == std::launch::deferred) {
+ qCDebug(lcLoaderScheduling) << "blocking on product thread immediately";
+ it.first->second.future.wait();
+ }
+ } catch (const std::system_error &e) {
+ if (e.code() != std::errc::resource_unavailable_try_again)
+ throw e;
+ qCWarning(lcLoaderScheduling) << "failed to create thread";
+ if (m_maxJobCount >= 2) {
+ m_maxJobCount /= 2;
+ qCWarning(lcLoaderScheduling) << "throttling down to" << m_maxJobCount << "jobs";
+ }
+ queueProductForScheduling(product, deferral);
+ m_availableLoaderStates.push_back(product.loaderState);
+ }
+}
+
+void ProductsResolver::checkForCancelation()
+{
+ if (m_loaderState.topLevelProject().isCanceled()) {
+ m_cancelingMutex.lock();
+ m_canceling = true;
+ m_cancelingMutex.unlock();
+ for (auto &thread : m_runningThreads)
+ thread.second.future.wait();
+ throw CancelException();
+ }
+}
+
+void ProductsResolver::handleFinishedThreads()
+{
+ TopLevelProjectContext &topLevelProject = m_loaderState.topLevelProject();
+ AccumulatingTimer timer(m_loaderState.parameters().logElapsedTime()
+ ? &topLevelProject.timingData().schedulingProducts : nullptr);
+
+ std::vector<std::pair<ProductWithLoaderState, Deferral>> productsToScheduleDirectly;
+ for (auto it = m_runningThreads.begin(); it != m_runningThreads.end();) {
+ ThreadInfo &ti = it->second;
+ if (!ti.done) {
+ ++it;
+ continue;
+ }
+ ti.future.wait();
+ ProductContext &product = *it->first;
+ LoaderState &loaderState = ti.loaderState;
+ it = m_runningThreads.erase(it);
+
+ qCDebug(lcLoaderScheduling) << "handling finished thread for product"
+ << product.displayName()
+ << "current unhandled product count is"
+ << topLevelProject.productsToHandleCount();
+
+ // If there are products waiting for the loader state used in the finished thread,
+ // we can start a job for one of them right away (but not in the loop,
+ // because startJob() modifies the thread list we are currently iterating over).
+ if (const auto productInfo = unblockProductWaitingForLoaderState(loaderState)) {
+ productsToScheduleDirectly.emplace_back(
+ ProductWithLoaderState(*productInfo->first, &loaderState),
+ productInfo->second);
+ } else {
+ qCDebug(lcLoaderScheduling) << "making loader state" << &loaderState
+ << "available again";
+ m_availableLoaderStates.push_back(&loaderState);
+ }
+
+ // If we encountered a dependency to an in-progress product or to a bulk dependency,
+ // we defer handling this product.
+ if (product.dependenciesResolvingPending()) {
+ qCDebug(lcLoaderScheduling) << "dependencies resolving not finished for product"
+ << product.displayName();
+ const auto pending = product.pendingDependency();
+ switch (pending.first) {
+ case ProductDependency::Single:
+ waitForSingleDependency(ProductWithLoaderState(product, &loaderState),
+ *pending.second);
+ break;
+ case ProductDependency::Bulk:
+ waitForBulkDependency(ProductWithLoaderState(product, &loaderState));
+ break;
+ case ProductDependency::None:
+ // This can happen if the dependency has finished in between the check in
+ // DependencyResolver and the one here.
+ QBS_CHECK(pending.second);
+ queueProductForScheduling(ProductWithLoaderState(product, &loaderState),
+ Deferral::Allowed);
+ break;
+ }
+ topLevelProject.incProductDeferrals();
+ } else {
+ qCDebug(lcLoaderScheduling) << "product" << product.displayName() << "finished";
+ topLevelProject.removeProductToHandle(product);
+ if (!product.name.startsWith(StringConstants::shadowProductPrefix()))
+ m_finishedProducts.push_back(&product);
+ topLevelProject.timingData() += product.timingData;
+ checkForMissedBulkDependencies(product);
+ topLevelProject.registerBulkDependencies(product);
+ unblockProductsWaitingForDependency(product);
+ }
+ }
+
+ for (const auto &productInfo : productsToScheduleDirectly)
+ startJob(productInfo.first, productInfo.second);
+}
+
+void ProductsResolver::queueProductForScheduling(const ProductWithLoaderState &product,
+ Deferral deferral)
+{
+ qCDebug(lcLoaderScheduling) << "queueing product" << product.product->displayName()
+ << "with deferral mode" << int(deferral);
+ m_productsToSchedule.emplace(product, deferral == Deferral::Allowed
+ ? -1 : m_loaderState.topLevelProject().productsToHandleCount());
+}
+
+void ProductsResolver::waitForSingleDependency(const ProductWithLoaderState &product,
+ ProductContext &dependency)
+{
+ qCDebug(lcLoaderScheduling) << "product" << product.product->displayName()
+ << "now waiting for single dependency"
+ << dependency.displayName();
+ m_waitingForSingleDependency[&dependency].push_back(product);
+}
+
+void ProductsResolver::waitForBulkDependency(const ProductWithLoaderState &product)
+{
+ qCDebug(lcLoaderScheduling) << "product" << product.product->displayName()
+ << "now waiting for bulk dependency";
+ m_waitingForBulkDependency.push_back(product);
+}
+
+void ProductsResolver::unblockProductsWaitingForDependency(ProductContext &finishedProduct)
+{
+ const auto it = m_waitingForSingleDependency.find(&finishedProduct);
+ if (it == m_waitingForSingleDependency.end())
+ return;
+
+ qCDebug(lcLoaderScheduling) << "unblocking all products waiting for now-finished product" <<
+ finishedProduct.displayName();
+ for (const ProductWithLoaderState &p : it->second) {
+ qCDebug(lcLoaderScheduling) << " unblocking product" << p.product->displayName();
+ queueProductForScheduling(p, Deferral::Allowed);
+ }
+ m_waitingForSingleDependency.erase(it);
+}
+
+void ProductsResolver::postProcess()
+{
+ for (ProductContext * const product : m_finishedProducts) {
+ if (product->product)
+ product->product->project->products.push_back(product->product);
+
+ // This has to be done in post-processing, because we need both product and shadow product
+ // to be ready, and contrary to what one might assume, there is no proper ordering
+ // between them regarding dependency resolving.
+ setupExports(*product, m_loaderState);
+ }
+
+ for (const auto &engine : m_enginePool)
+ m_loaderState.topLevelProject().collectDataFromEngine(*engine);
+
+ QBS_CHECK(!m_loaderState.topLevelProject().projects().empty());
+ const auto project = std::dynamic_pointer_cast<TopLevelProject>(
+ m_loaderState.topLevelProject().projects().front()->project);
+ QBS_CHECK(project);
+ for (LoaderState * const loaderState : m_availableLoaderStates)
+ project->warningsEncountered << loaderState->logger().warnings();
+}
+
+void ProductsResolver::checkForMissedBulkDependencies(const ProductContext &product)
+{
+ if (!product.product || !product.product->enabled || !product.bulkDependencies.empty())
+ return;
+ for (const FileTag &tag : product.product->fileTags) {
+ for (const auto &[p, location]
+ : m_loaderState.topLevelProject().finishedProductsWithBulkDependency(tag)) {
+ if (!p->product->enabled)
+ continue;
+ if (p->name == product.name)
+ continue;
+ ErrorInfo e;
+ e.append(Tr::tr("Cyclic dependencies detected:"));
+ e.append(Tr::tr("First product is '%1'.")
+ .arg(product.displayName()), product.item->location());
+ e.append(Tr::tr("Second product is '%1'.")
+ .arg(p->displayName()), p->item->location());
+ e.append(Tr::tr("Dependency from %1 to %2 was established via Depends.productTypes.")
+ .arg(p->displayName(), product.displayName()), location);
+ if (m_loaderState.parameters().productErrorMode() == ErrorHandlingMode::Strict)
+ throw e;
+ m_loaderState.logger().printWarning(e);
+ m_loaderState.logger().printWarning(
+ ErrorInfo(Tr::tr("Product '%1' had errors and was disabled.")
+ .arg(product.displayName()), product.item->location()));
+ m_loaderState.logger().printWarning(
+ ErrorInfo(Tr::tr("Product '%1' had errors and was disabled.")
+ .arg(p->displayName()), p->item->location()));
+ product.product->enabled = false;
+ p->product->enabled = false;
+ }
+ }
+}
+
+int ProductsResolver::dependsItemCount(const Item *item)
+{
+ int count = 0;
+ for (const Item * const child : item->children()) {
+ if (child->type() == ItemType::Depends)
+ ++count;
+ }
+ return count;
+}
+
+int ProductsResolver::dependsItemCount(ProductContext &product)
+{
+ if (product.dependsItemCount == -1)
+ product.dependsItemCount = dependsItemCount(product.item);
+ return product.dependsItemCount;
+}
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/productsresolver.h b/src/lib/corelib/loader/productsresolver.h
new file mode 100644
index 000000000..b8b4b7516
--- /dev/null
+++ b/src/lib/corelib/loader/productsresolver.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+namespace qbs::Internal {
+class LoaderState;
+
+void resolveProducts(LoaderState &loaderState);
+
+} // namespace qbs::Internal
diff --git a/src/lib/corelib/loader/projectresolver.cpp b/src/lib/corelib/loader/projectresolver.cpp
new file mode 100644
index 000000000..be99d547d
--- /dev/null
+++ b/src/lib/corelib/loader/projectresolver.cpp
@@ -0,0 +1,562 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "itemreader.h"
+#include "loaderutils.h"
+#include "productscollector.h"
+#include "productsresolver.h"
+
+#include <jsextensions/jsextensions.h>
+#include <jsextensions/moduleproperties.h>
+#include <language/artifactproperties.h>
+#include <language/builtindeclarations.h>
+#include <language/evaluator.h>
+#include <language/filecontext.h>
+#include <language/filetags.h>
+#include <language/item.h>
+#include <language/itempool.h>
+#include <language/language.h>
+#include <language/propertymapinternal.h>
+#include <language/resolvedfilecontext.h>
+#include <language/scriptengine.h>
+#include <language/value.h>
+#include <logging/categories.h>
+#include <logging/translator.h>
+#include <tools/error.h>
+#include <tools/fileinfo.h>
+#include <tools/joblimits.h>
+#include <tools/jsliterals.h>
+#include <tools/profile.h>
+#include <tools/profiling.h>
+#include <tools/progressobserver.h>
+#include <tools/scripttools.h>
+#include <tools/qbsassert.h>
+#include <tools/qttools.h>
+#include <tools/set.h>
+#include <tools/settings.h>
+#include <tools/setupprojectparameters.h>
+#include <tools/stlutils.h>
+#include <tools/stringconstants.h>
+
+#include <quickjs.h>
+
+#include <QtCore/qdir.h>
+#include <QtCore/qregularexpression.h>
+
+#include <algorithm>
+#include <memory>
+#include <queue>
+#include <utility>
+#include <vector>
+
+namespace qbs {
+namespace Internal {
+
+static SetupProjectParameters finalizedProjectParameters(const SetupProjectParameters &in,
+ Logger &logger)
+{
+ SetupProjectParameters params = in;
+ if (params.topLevelProfile().isEmpty()) {
+ Settings settings(params.settingsDirectory());
+ QString profileName = settings.defaultProfile();
+ if (profileName.isEmpty()) {
+ logger.qbsDebug() << Tr::tr("No profile specified and no default profile exists. "
+ "Using default property values.");
+ profileName = Profile::fallbackName();
+ }
+ params.setTopLevelProfile(profileName);
+ params.expandBuildConfiguration();
+ }
+ params.finalizeProjectFilePath();
+
+ QBS_CHECK(QFileInfo(params.projectFilePath()).isAbsolute());
+ QBS_CHECK(FileInfo::isAbsolute(params.buildRoot()));
+
+ return params;
+}
+
+class ProjectResolver::Private
+{
+public:
+ Private(const SetupProjectParameters &parameters, ScriptEngine *engine, Logger &&logger)
+ : setupParams(finalizedProjectParameters(parameters, logger)), engine(engine),
+ logger(std::move(logger)) {}
+
+ TopLevelProjectPtr resolveTopLevelProject();
+ void resolveProject(ProjectContext *projectContext);
+ void resolveProjectFully(ProjectContext *projectContext);
+ void resolveSubProject(Item *item, ProjectContext *projectContext);
+ void checkOverriddenValues();
+ void collectNameFromOverride(const QString &overrideString);
+ void loadTopLevelProjectItem();
+ void buildProjectTree();
+
+ void printProfilingInfo();
+
+ const SetupProjectParameters setupParams;
+ ScriptEngine * const engine;
+ mutable Logger logger;
+ ItemPool itemPool;
+ TopLevelProjectContext topLevelProject;
+ LoaderState state{setupParams, topLevelProject, itemPool, *engine, logger};
+ Item *rootProjectItem = nullptr;
+};
+
+ProjectResolver::ProjectResolver(const SetupProjectParameters &parameters, ScriptEngine *engine,
+ Logger logger)
+ : d(makePimpl<Private>(parameters, engine, std::move(logger)))
+{
+ d->logger.storeWarnings();
+}
+
+ProjectResolver::~ProjectResolver() = default;
+
+void ProjectResolver::setProgressObserver(ProgressObserver *observer)
+{
+ d->state.topLevelProject().setProgressObserver(observer);
+}
+
+void ProjectResolver::setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes)
+{
+ d->state.topLevelProject().setOldProjectProbes(oldProbes);
+}
+
+void ProjectResolver::setOldProductProbes(
+ const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes)
+{
+ d->state.topLevelProject().setOldProductProbes(oldProbes);
+}
+
+void ProjectResolver::setLastResolveTime(const FileTime &time)
+{
+ d->state.topLevelProject().setLastResolveTime(time);
+}
+
+void ProjectResolver::setStoredProfiles(const QVariantMap &profiles)
+{
+ d->state.topLevelProject().setProfileConfigs(profiles);
+}
+
+void ProjectResolver::setStoredModuleProviderInfo(const StoredModuleProviderInfo &providerInfo)
+{
+ d->state.topLevelProject().setModuleProvidersCache(providerInfo.providers);
+}
+
+static void checkForDuplicateProductNames(const TopLevelProjectConstPtr &project)
+{
+ const std::vector<ResolvedProductPtr> allProducts = project->allProducts();
+ for (size_t i = 0; i < allProducts.size(); ++i) {
+ const ResolvedProductConstPtr product1 = allProducts.at(i);
+ const QString productName = product1->uniqueName();
+ for (size_t j = i + 1; j < allProducts.size(); ++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()
+{
+ qCDebug(lcProjectResolver) << "resolving" << d->setupParams.projectFilePath();
+
+ d->engine->setEnvironment(d->setupParams.adjustedEnvironment());
+ d->engine->checkAndClearException({});
+ d->engine->clearImportsCache();
+ d->engine->clearRequestedProperties();
+ d->engine->enableProfiling(d->setupParams.logElapsedTime());
+ d->logger.clearWarnings();
+ EvalContextSwitcher evalContextSwitcher(d->engine, EvalContext::PropertyEvaluation);
+
+ // 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 (ProgressObserver * const observer = d->state.topLevelProject().progressObserver()) {
+ observer->initialize(Tr::tr("Resolving project for configuration %1")
+ .arg(TopLevelProject::deriveId(d->setupParams.finalBuildConfigurationTree())), 1);
+ observer->addScriptEngine(d->engine);
+ }
+
+ const FileTime resolveTime = FileTime::currentTime();
+ TopLevelProjectPtr tlp;
+ try {
+ d->checkOverriddenValues();
+ d->loadTopLevelProjectItem();
+ d->buildProjectTree();
+ tlp = d->resolveTopLevelProject();
+ } catch (const CancelException &) {
+ throw ErrorInfo(Tr::tr("Project resolving canceled for configuration '%1'.")
+ .arg(TopLevelProject::deriveId(
+ d->setupParams.finalBuildConfigurationTree())));
+ }
+ tlp->lastStartResolveTime = resolveTime;
+ tlp->lastEndResolveTime = FileTime::currentTime();
+
+ // E.g. if the top-level project is disabled.
+ if (ProgressObserver * const observer = d->state.topLevelProject().progressObserver()) {
+ observer->setFinished();
+ d->printProfilingInfo();
+ }
+ return tlp;
+}
+
+static void makeSubProjectNamesUniqe(const ResolvedProjectPtr &parentProject)
+{
+ Set<QString> subProjectNames;
+ Set<ResolvedProjectPtr> projectsInNeedOfNameChange;
+ for (const ResolvedProjectPtr &p : std::as_const(parentProject->subProjects)) {
+ if (!subProjectNames.insert(p->name).second)
+ projectsInNeedOfNameChange << p;
+ makeSubProjectNamesUniqe(p);
+ }
+ while (!projectsInNeedOfNameChange.empty()) {
+ auto it = projectsInNeedOfNameChange.begin();
+ while (it != projectsInNeedOfNameChange.end()) {
+ const ResolvedProjectPtr p = *it;
+ p->name += QLatin1Char('_');
+ if (subProjectNames.insert(p->name).second) {
+ it = projectsInNeedOfNameChange.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+}
+
+TopLevelProjectPtr ProjectResolver::Private::resolveTopLevelProject()
+{
+ if (ProgressObserver * const observer = state.topLevelProject().progressObserver())
+ observer->setMaximum(state.topLevelProject().productCount());
+ TopLevelProjectPtr project = TopLevelProject::create();
+ project->buildDirectory = TopLevelProject::deriveBuildDirectory(
+ setupParams.buildRoot(),
+ TopLevelProject::deriveId(setupParams.finalBuildConfigurationTree()));
+ if (!state.topLevelProject().projects().empty()) {
+ ProjectContext * const projectContext = state.topLevelProject().projects().front();
+ QBS_CHECK(projectContext->item == rootProjectItem);
+ projectContext->project = project;
+ resolveProject(projectContext);
+ }
+ resolveProducts(state);
+ ErrorInfo accumulatedErrors;
+ {
+ const auto queuedErrors = state.topLevelProject().queuedErrors();
+ for (const ErrorInfo &e : queuedErrors.get())
+ appendError(accumulatedErrors, e);
+ }
+ if (accumulatedErrors.hasError())
+ throw accumulatedErrors;
+
+ project->buildSystemFiles.unite(state.topLevelProject().buildSystemFiles());
+ project->profileConfigs = state.topLevelProject().profileConfigs();
+ project->codeLinks = state.topLevelProject().codeLinks();
+ const QVariantMap &profiles = state.topLevelProject().localProfiles();
+ for (auto it = profiles.begin(); it != profiles.end(); ++it)
+ project->profileConfigs.remove(it.key());
+ project->probes = state.topLevelProject().projectLevelProbes();
+ project->moduleProviderInfo.providers = state.topLevelProject().moduleProvidersCache();
+ project->setBuildConfiguration(setupParams.finalBuildConfigurationTree());
+ project->overriddenValues = setupParams.overriddenValues();
+ state.topLevelProject().collectDataFromEngine(*engine);
+ makeSubProjectNamesUniqe(project);
+ checkForDuplicateProductNames(project);
+ project->warningsEncountered << logger.warnings();
+
+ return project;
+}
+
+void ProjectResolver::Private::resolveProject(ProjectContext *projectContext)
+{
+ state.topLevelProject().checkCancelation();
+
+ if (projectContext->parent) {
+ projectContext->project = ResolvedProject::create();
+ projectContext->parent->project->subProjects.push_back(projectContext->project);
+ projectContext->project->parentProject = projectContext->parent->project;
+ projectContext->project->enabled = projectContext->parent->project->enabled;
+ }
+ projectContext->project->location = projectContext->item->location();
+
+ try {
+ resolveProjectFully(projectContext);
+ } catch (const ErrorInfo &error) {
+ if (!projectContext->project->enabled) {
+ qCDebug(lcProjectResolver) << "error resolving project"
+ << projectContext->project->location << error.toString();
+ return;
+ }
+ if (setupParams.productErrorMode() == ErrorHandlingMode::Strict)
+ throw;
+ logger.printWarning(error);
+ }
+}
+
+void ProjectResolver::Private::resolveProjectFully(ProjectContext *projectContext)
+{
+ Item * const item = projectContext->item;
+ projectContext->project->enabled = projectContext->project->enabled
+ && state.evaluator().boolValue(item, StringConstants::conditionProperty());
+ projectContext->project->name = state.evaluator().stringValue(
+ item, StringConstants::nameProperty());
+ if (projectContext->project->name.isEmpty())
+ projectContext->project->name = FileInfo::baseName(item->location().filePath()); // FIXME: Must also be changed in item?
+ QVariantMap projectProperties;
+ if (!projectContext->project->enabled) {
+ projectProperties.insert(StringConstants::profileProperty(),
+ state.evaluator().stringValue(
+ item, StringConstants::profileProperty()));
+ 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);
+ const ScopedJsValue sv(engine->context(), state.evaluator().value(item, it.key()));
+ projectProperties.insert(it.key(), getJsVariant(engine->context(), sv));
+ }
+ projectContext->project->setProjectProperties(projectProperties);
+
+ for (Item * const child : item->children()) {
+ state.topLevelProject().checkCancelation();
+ try {
+ switch (child->type()) {
+ case ItemType::SubProject:
+ resolveSubProject(child, projectContext);
+ break;
+ case ItemType::FileTagger:
+ resolveFileTagger(state, child, projectContext, nullptr);
+ break;
+ case ItemType::JobLimit:
+ resolveJobLimit(state, child, projectContext, nullptr, nullptr);
+ break;
+ case ItemType::Rule:
+ resolveRule(state, child, projectContext, nullptr, nullptr);
+ break;
+ default:
+ break;
+ }
+ } catch (const ErrorInfo &e) {
+ state.topLevelProject().addQueuedError(e);
+ }
+ }
+
+ for (ProjectContext * const childContext : projectContext->children)
+ resolveProject(childContext);
+}
+
+void ProjectResolver::Private::resolveSubProject(Item *item, ProjectContext *projectContext)
+{
+ // If we added a Project child item in ProductsCollector, then the sub-project will be
+ // resolved normally via resolveProject(). Otherwise, it was not loaded, for instance
+ // because its Properties condition was false, and special handling applies.
+ if (item->child(ItemType::Project))
+ return;
+
+ ResolvedProjectPtr project = ResolvedProject::create();
+ project->enabled = false;
+ project->parentProject = projectContext->project;
+ projectContext->project->subProjects << project;
+ if (Item * const propertiesItem = item->child(ItemType::PropertiesInSubProject)) {
+ project->name = state.evaluator().stringValue(propertiesItem,
+ StringConstants::nameProperty());
+ }
+}
+
+void ProjectResolver::Private::checkOverriddenValues()
+{
+ static const auto matchesPrefix = [](const QString &key) {
+ static const QStringList prefixes({StringConstants::projectPrefix(),
+ QStringLiteral("projects"),
+ QStringLiteral("products"), QStringLiteral("modules"),
+ StringConstants::moduleProviders(),
+ StringConstants::qbsModule()});
+ return any_of(prefixes, [&key](const QString &prefix) {
+ return key.startsWith(prefix + QLatin1Char('.')); });
+ };
+ const QVariantMap &overriddenValues = state.parameters().overriddenValues();
+ for (auto it = overriddenValues.begin(); it != overriddenValues.end(); ++it) {
+ if (matchesPrefix(it.key())) {
+ collectNameFromOverride(it.key());
+ continue;
+ }
+
+ ErrorInfo e(Tr::tr("Property override key '%1' not understood.").arg(it.key()));
+ e.append(Tr::tr("Please use one of the following:"));
+ e.append(QLatin1Char('\t') + Tr::tr("projects.<project-name>.<property-name>:value"));
+ e.append(QLatin1Char('\t') + Tr::tr("products.<product-name>.<property-name>:value"));
+ e.append(QLatin1Char('\t') + Tr::tr("modules.<module-name>.<property-name>:value"));
+ e.append(QLatin1Char('\t') + Tr::tr("products.<product-name>.<module-name>."
+ "<property-name>:value"));
+ e.append(QLatin1Char('\t') + Tr::tr("moduleProviders.<provider-name>."
+ "<property-name>:value"));
+ handlePropertyError(e, state.parameters(), state.logger());
+ }
+}
+
+void ProjectResolver::Private::collectNameFromOverride(const QString &overrideString)
+{
+ const auto extract = [&overrideString](const QString &prefix) {
+ if (!overrideString.startsWith(prefix))
+ return QString();
+ const int startPos = prefix.length();
+ const int endPos = overrideString.lastIndexOf(StringConstants::dot());
+ if (endPos == -1)
+ return QString();
+ return overrideString.mid(startPos, endPos - startPos);
+ };
+ const QString &projectName = extract(StringConstants::projectsOverridePrefix());
+ if (!projectName.isEmpty()) {
+ state.topLevelProject().addProjectNameUsedInOverrides(projectName);
+ return;
+ }
+ const QString &productName = extract(StringConstants::productsOverridePrefix());
+ if (!productName.isEmpty()) {
+ state.topLevelProject().addProductNameUsedInOverrides(productName.left(
+ productName.indexOf(StringConstants::dot())));
+ return;
+ }
+}
+
+void ProjectResolver::Private::loadTopLevelProjectItem()
+{
+ const QStringList topLevelSearchPaths
+ = state.parameters().finalBuildConfigurationTree()
+ .value(StringConstants::projectPrefix()).toMap()
+ .value(StringConstants::qbsSearchPathsProperty()).toStringList();
+ SearchPathsManager searchPathsManager(state.itemReader(), topLevelSearchPaths);
+ Item * const root = state.itemReader().setupItemFromFile(
+ state.parameters().projectFilePath(), {});
+ if (!root)
+ return;
+
+ switch (root->type()) {
+ case ItemType::Product:
+ rootProjectItem = state.itemReader().wrapInProjectIfNecessary(root);
+ break;
+ case ItemType::Project:
+ rootProjectItem = root;
+ break;
+ default:
+ throw ErrorInfo(Tr::tr("The top-level item must be of type 'Project' or 'Product', but it"
+ " is of type '%1'.").arg(root->typeName()), root->location());
+ }
+}
+
+void ProjectResolver::Private::buildProjectTree()
+{
+ state.topLevelProject().setBuildDirectory(TopLevelProject::deriveBuildDirectory(
+ state.parameters().buildRoot(),
+ TopLevelProject::deriveId(state.parameters().finalBuildConfigurationTree())));
+ rootProjectItem->setProperty(StringConstants::sourceDirectoryProperty(),
+ VariantValue::create(QFileInfo(rootProjectItem->file()->filePath())
+ .absolutePath()));
+ rootProjectItem->setProperty(StringConstants::buildDirectoryProperty(),
+ VariantValue::create(state.topLevelProject().buildDirectory()));
+ rootProjectItem->setProperty(StringConstants::profileProperty(),
+ VariantValue::create(state.parameters().topLevelProfile()));
+ ProductsCollector(state).run(rootProjectItem);
+
+ AccumulatingTimer timer(state.parameters().logElapsedTime()
+ ? &state.topLevelProject().timingData().propertyChecking : nullptr);
+ checkPropertyDeclarations(rootProjectItem, state);
+}
+
+void ProjectResolver::Private::printProfilingInfo()
+{
+ if (!setupParams.logElapsedTime())
+ return;
+ const auto print = [this](int indent, const QString &pattern, qint64 time) {
+ logger.qbsLog(LoggerInfo, true) << QByteArray(indent, ' ')
+ << pattern.arg(elapsedTimeString(time));
+ };
+ print(2, Tr::tr("Project file loading and parsing took %1."), state.itemReader().elapsedTime());
+ print(2, Tr::tr("Preparing products took %1."),
+ state.topLevelProject().timingData().preparingProducts);
+ print(2, Tr::tr("Setting up Groups took %1."),
+ state.topLevelProject().timingData().groupsSetup);
+ print(2, Tr::tr("Scheduling products took %1."),
+ state.topLevelProject().timingData().schedulingProducts);
+ print(2, Tr::tr("Resolving products took %1."),
+ state.topLevelProject().timingData().resolvingProducts);
+ print(4, Tr::tr("Property evaluation took %1."),
+ state.topLevelProject().timingData().propertyEvaluation);
+ print(4, Tr::tr("Resolving groups (without module property evaluation) took %1."),
+ state.topLevelProject().timingData().groupsResolving);
+ print(4, Tr::tr("Setting up product dependencies took %1."),
+ state.topLevelProject().timingData().dependenciesResolving);
+ print(6, Tr::tr("Running module providers took %1."),
+ state.topLevelProject().timingData().moduleProviders);
+ print(6, Tr::tr("Instantiating modules took %1."),
+ state.topLevelProject().timingData().moduleInstantiation);
+ print(6, Tr::tr("Merging module property values took %1."),
+ state.topLevelProject().timingData().propertyMerging);
+ logger.qbsLog(LoggerInfo, true) << QByteArray(4, ' ') << "There were "
+ << state.topLevelProject().productDeferrals()
+ << " product deferrals with a total of "
+ << state.topLevelProject().productCount() << " products.";
+ print(2, Tr::tr("Running Probes took %1."), state.topLevelProject().timingData().probes);
+ state.logger().qbsLog(LoggerInfo, true)
+ << " "
+ << Tr::tr("%1 probes encountered, %2 configure scripts executed, "
+ "%3 re-used from current run, %4 re-used from earlier run.")
+ .arg(state.topLevelProject().probesEncounteredCount())
+ .arg(state.topLevelProject().probesRunCount())
+ .arg(state.topLevelProject().reusedCurrentProbesCount())
+ .arg(state.topLevelProject().reusedOldProbesCount());
+ print(2, Tr::tr("Property checking took %1."),
+ state.topLevelProject().timingData().propertyChecking);
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/loader/projectresolver.h b/src/lib/corelib/loader/projectresolver.h
new file mode 100644
index 000000000..22ad82518
--- /dev/null
+++ b/src/lib/corelib/loader/projectresolver.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 PROJECTRESOLVER_H
+#define PROJECTRESOLVER_H
+
+#include <language/forward_decls.h>
+#include <logging/logger.h>
+#include <tools/pimpl.h>
+#include <tools/qbs_export.h>
+
+#include <QHash>
+#include <QVariant>
+
+#include <vector>
+
+namespace qbs {
+class SetupProjectParameters;
+namespace Internal {
+class FileTime;
+class Logger;
+class ProgressObserver;
+class ScriptEngine;
+class StoredModuleProviderInfo;
+
+class QBS_AUTOTEST_EXPORT ProjectResolver
+{
+public:
+ ProjectResolver(const SetupProjectParameters &parameters, ScriptEngine *engine, Logger logger);
+ ~ProjectResolver();
+
+ void setProgressObserver(ProgressObserver *observer);
+ void setOldProjectProbes(const std::vector<ProbeConstPtr> &oldProbes);
+ void setOldProductProbes(const QHash<QString, std::vector<ProbeConstPtr>> &oldProbes);
+ void setLastResolveTime(const FileTime &time);
+ void setStoredProfiles(const QVariantMap &profiles);
+ void setStoredModuleProviderInfo(const StoredModuleProviderInfo &providerInfo);
+ TopLevelProjectPtr resolve();
+
+private:
+ class Private;
+ Pimpl<Private> d;
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // PROJECTRESOLVER_H
diff --git a/src/lib/corelib/logging/categories.cpp b/src/lib/corelib/logging/categories.cpp
index 0f844f5b4..5738dc21d 100644
--- a/src/lib/corelib/logging/categories.cpp
+++ b/src/lib/corelib/logging/categories.cpp
@@ -50,6 +50,7 @@ Q_LOGGING_CATEGORY(lcModuleLoader, "qbs.moduleloader", QtCriticalMsg)
Q_LOGGING_CATEGORY(lcPluginManager, "qbs.pluginmanager", QtCriticalMsg)
Q_LOGGING_CATEGORY(lcProjectResolver, "qbs.projectresolver", QtCriticalMsg)
Q_LOGGING_CATEGORY(lcUpToDateCheck, "qbs.uptodate", QtCriticalMsg)
+Q_LOGGING_CATEGORY(lcLoaderScheduling, "qbs.loader.scheduling", QtCriticalMsg)
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/logging/categories.h b/src/lib/corelib/logging/categories.h
index 40c69845e..c8873c30c 100644
--- a/src/lib/corelib/logging/categories.h
+++ b/src/lib/corelib/logging/categories.h
@@ -53,6 +53,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcModuleLoader)
Q_DECLARE_LOGGING_CATEGORY(lcPluginManager)
Q_DECLARE_LOGGING_CATEGORY(lcProjectResolver)
Q_DECLARE_LOGGING_CATEGORY(lcUpToDateCheck)
+Q_DECLARE_LOGGING_CATEGORY(lcLoaderScheduling)
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/logging/ilogsink.cpp b/src/lib/corelib/logging/ilogsink.cpp
index 4eb930cbf..20cf52bcd 100644
--- a/src/lib/corelib/logging/ilogsink.cpp
+++ b/src/lib/corelib/logging/ilogsink.cpp
@@ -82,14 +82,11 @@ public:
std::mutex mutex;
};
-ILogSink::ILogSink() : d(new ILogSinkPrivate)
+ILogSink::ILogSink() : d(std::make_unique<ILogSinkPrivate>())
{
}
-ILogSink::~ILogSink()
-{
- delete d;
-}
+ILogSink::~ILogSink() = default;
void ILogSink::setLogLevel(LoggerLevel level)
{
diff --git a/src/lib/corelib/logging/ilogsink.h b/src/lib/corelib/logging/ilogsink.h
index 4b25aa7b8..0a055abc1 100644
--- a/src/lib/corelib/logging/ilogsink.h
+++ b/src/lib/corelib/logging/ilogsink.h
@@ -43,6 +43,8 @@
#include <QtCore/qstring.h>
+#include <memory>
+
namespace qbs {
class ErrorInfo;
@@ -83,7 +85,7 @@ private:
const QString &tag) = 0;
class ILogSinkPrivate;
- ILogSinkPrivate * const d;
+ const std::unique_ptr<ILogSinkPrivate> d;
};
} // namespace qbs
diff --git a/src/lib/corelib/logging/logger.cpp b/src/lib/corelib/logging/logger.cpp
index b962afa92..65d0cc27f 100644
--- a/src/lib/corelib/logging/logger.cpp
+++ b/src/lib/corelib/logging/logger.cpp
@@ -79,6 +79,9 @@ LogWriter::~LogWriter()
const LogWriter &LogWriter::operator=(const LogWriter &other)
{
+ if (this == &other) // Self assignment guard.
+ return *this;
+
m_logSink = other.m_logSink;
m_level = other.m_level;
m_message = other.m_message;
@@ -163,7 +166,7 @@ LogWriter operator<<(LogWriter w, const Internal::Set<QString> &strSet)
LogWriter operator<<(LogWriter w, const QVariant &variant)
{
QString str = QLatin1String(variant.typeName()) + QLatin1Char('(');
- if (variant.type() == QVariant::List) {
+ if (variant.userType() == QMetaType::QVariantList) {
bool firstLoop = true;
const auto list = variant.toList();
for (const QVariant &item : list) {
@@ -183,17 +186,20 @@ LogWriter operator<<(LogWriter w, const QVariant &variant)
LogWriter operator<<(LogWriter w, int n)
{
- return w << QString::number(n);
+ w.write(QString::number(n));
+ return w;
}
LogWriter operator<<(LogWriter w, qint64 n)
{
- return w << QString::number(n);
+ w.write(QString::number(n));
+ return w;
}
LogWriter operator<<(LogWriter w, bool b)
{
- return w << QString::fromLatin1(b ? "true" : "false");
+ w.write(QString::fromLatin1(b ? "true" : "false"));
+ return w;
}
LogWriter operator<<(LogWriter w, const MessageTag &tag)
@@ -225,7 +231,7 @@ void Logger::printWarning(const ErrorInfo &warning)
LogWriter Logger::qbsLog(LoggerLevel level, bool force) const
{
- return LogWriter(m_logSink, level, force);
+ return {m_logSink, level, force};
}
} // namespace Internal
diff --git a/src/lib/corelib/logging/logger.h b/src/lib/corelib/logging/logger.h
index 419a371c2..389dc7e05 100644
--- a/src/lib/corelib/logging/logger.h
+++ b/src/lib/corelib/logging/logger.h
@@ -90,7 +90,7 @@ private:
class QBS_EXPORT MessageTag
{
public:
- explicit MessageTag(const QString &tag) : m_tag(tag) {}
+ explicit MessageTag(QString tag) : m_tag(std::move(tag)) {}
const QString &tag() const { return m_tag; }
diff --git a/src/lib/corelib/logging/logging.pri b/src/lib/corelib/logging/logging.pri
deleted file mode 100644
index 3a4379a9a..000000000
--- a/src/lib/corelib/logging/logging.pri
+++ /dev/null
@@ -1,18 +0,0 @@
-include(../../../install_prefix.pri)
-
-HEADERS += \
- $$PWD/categories.h \
- $$PWD/logger.h \
- $$PWD/translator.h \
- $$PWD/ilogsink.h
-
-SOURCES += \
- $$PWD/categories.cpp \
- $$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/parser/parser.pri b/src/lib/corelib/parser/parser.pri
deleted file mode 100644
index e6a8a5345..000000000
--- a/src/lib/corelib/parser/parser.pri
+++ /dev/null
@@ -1,21 +0,0 @@
-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
index e72e79f87..42ef8ea29 100644
--- a/src/lib/corelib/parser/qmlerror.cpp
+++ b/src/lib/corelib/parser/qmlerror.cpp
@@ -39,6 +39,8 @@
#include "qmlerror.h"
+#include <tools/qttools.h>
+
#include <QtCore/qdebug.h>
#include <QtCore/qfile.h>
#include <QtCore/qstringlist.h>
@@ -93,16 +95,12 @@ QmlErrorPrivate::QmlErrorPrivate()
/*!
Creates an empty error object.
*/
-QmlError::QmlError()
-: d(nullptr)
-{
-}
+QmlError::QmlError() = default;
/*!
Creates a copy of \a other.
*/
QmlError::QmlError(const QmlError &other)
-: d(nullptr)
{
*this = other;
}
@@ -112,11 +110,13 @@ QmlError::QmlError(const QmlError &other)
*/
QmlError &QmlError::operator=(const QmlError &other)
{
+ if (this == &other) // Self assignment guard.
+ return *this;
+
if (!other.d) {
- delete d;
d = nullptr;
} else {
- if (!d) d = new QmlErrorPrivate;
+ if (!d) d = qbs::Internal::makePimpl<QmlErrorPrivate>();
d->url = other.d->url;
d->description = other.d->description;
d->line = other.d->line;
@@ -128,10 +128,7 @@ QmlError &QmlError::operator=(const QmlError &other)
/*!
\internal
*/
-QmlError::~QmlError()
-{
- delete d; d = nullptr;
-}
+QmlError::~QmlError() = default;
/*!
Returns true if this error is valid, otherwise false.
@@ -147,7 +144,7 @@ bool QmlError::isValid() const
QUrl QmlError::url() const
{
if (d) return d->url;
- else return {};
+ return {};
}
/*!
@@ -155,7 +152,7 @@ QUrl QmlError::url() const
*/
void QmlError::setUrl(const QUrl &url)
{
- if (!d) d = new QmlErrorPrivate;
+ if (!d) d = qbs::Internal::makePimpl<QmlErrorPrivate>();
d->url = url;
}
@@ -165,7 +162,7 @@ void QmlError::setUrl(const QUrl &url)
QString QmlError::description() const
{
if (d) return d->description;
- else return {};
+ return {};
}
/*!
@@ -173,7 +170,7 @@ QString QmlError::description() const
*/
void QmlError::setDescription(const QString &description)
{
- if (!d) d = new QmlErrorPrivate;
+ if (!d) d = qbs::Internal::makePimpl<QmlErrorPrivate>();
d->description = description;
}
@@ -183,7 +180,7 @@ void QmlError::setDescription(const QString &description)
int QmlError::line() const
{
if (d) return d->line;
- else return -1;
+ return -1;
}
/*!
@@ -191,7 +188,7 @@ int QmlError::line() const
*/
void QmlError::setLine(int line)
{
- if (!d) d = new QmlErrorPrivate;
+ if (!d) qbs::Internal::makePimpl<QmlErrorPrivate>();
d->line = line;
}
@@ -201,7 +198,7 @@ void QmlError::setLine(int line)
int QmlError::column() const
{
if (d) return d->column;
- else return -1;
+ return -1;
}
/*!
@@ -209,7 +206,7 @@ int QmlError::column() const
*/
void QmlError::setColumn(int column)
{
- if (!d) d = new QmlErrorPrivate;
+ if (!d) qbs::Internal::makePimpl<QmlErrorPrivate>();
d->column = column;
}
@@ -259,9 +256,7 @@ QDebug operator<<(QDebug debug, const QmlError &error)
if (f.open(QIODevice::ReadOnly)) {
QByteArray data = f.readAll();
QTextStream stream(data, QIODevice::ReadOnly);
-#ifndef QT_NO_TEXTCODEC
- stream.setCodec("UTF-8");
-#endif
+ qbs::setupDefaultCodec(stream);
const QString code = stream.readAll();
const QStringList lines = code.split(QLatin1Char('\n'));
@@ -271,7 +266,7 @@ QDebug operator<<(QDebug debug, const QmlError &error)
if (error.column() > 0) {
int column = std::max(0, error.column() - 1);
- column = std::min(column, line.length());
+ column = std::min<int>(column, line.length());
QByteArray ind;
ind.reserve(column);
diff --git a/src/lib/corelib/parser/qmlerror.h b/src/lib/corelib/parser/qmlerror.h
index cfac506bb..4f7bf8a07 100644
--- a/src/lib/corelib/parser/qmlerror.h
+++ b/src/lib/corelib/parser/qmlerror.h
@@ -40,7 +40,7 @@
#ifndef QQMLERROR_H
#define QQMLERROR_H
-
+#include <tools/pimpl.h>
#include <QtCore/qurl.h>
#include <QtCore/qstring.h>
@@ -73,7 +73,7 @@ public:
QString toString() const;
private:
- QmlErrorPrivate *d;
+ qbs::Internal::Pimpl<QmlErrorPrivate> d;
};
} // namespace QbsQmlJS
diff --git a/src/lib/corelib/parser/qmljs.g b/src/lib/corelib/parser/qmljs.g
index 956c65809..b9d533ada 100644
--- a/src/lib/corelib/parser/qmljs.g
+++ b/src/lib/corelib/parser/qmljs.g
@@ -189,6 +189,23 @@
**
****************************************************************************/
+
+//
+// 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
@@ -196,17 +213,16 @@
#include "qmljsgrammar_p.h"
#include "qmljsast_p.h"
#include "qmljsengine_p.h"
+#include <tools/qbs_export.h>
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
-QT_QML_BEGIN_NAMESPACE
-
-namespace QmlJS {
+namespace QbsQmlJS {
class Engine;
-class QML_PARSER_EXPORT Parser: protected $table
+class QBS_AUTOTEST_EXPORT Parser: protected $table
{
public:
union Value {
@@ -270,7 +286,7 @@ public:
AST::Statement *statement() const
{
if (! program)
- return 0;
+ return nullptr;
return program->statementCast();
}
@@ -278,7 +294,7 @@ public:
AST::ExpressionNode *expression() const
{
if (! program)
- return 0;
+ return nullptr;
return program->expressionCast();
}
@@ -286,7 +302,7 @@ public:
AST::UiObjectMember *uiObjectMember() const
{
if (! program)
- return 0;
+ return nullptr;
return program->uiObjectMemberCast();
}
@@ -348,13 +364,13 @@ protected:
enum { TOKEN_BUFFER_SIZE = 3 };
struct SavedToken {
- int token;
- double dval;
+ int token = 0;
+ double dval = 0.0;
AST::SourceLocation loc;
QStringRef spell;
};
- double yylval;
+ double yylval = 0.0;
QStringRef yytokenspell;
AST::SourceLocation yylloc;
AST::SourceLocation yyprevlloc;
@@ -366,7 +382,7 @@ protected:
QList<DiagnosticMessage> diagnostic_messages;
};
-} // end of namespace QmlJS
+} // end of namespace QbsQmlJS
:/
@@ -458,7 +474,7 @@ AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr)
return currentId->finish();
}
- return 0;
+ return nullptr;
}
bool Parser::parse(int startToken)
@@ -3003,9 +3019,5 @@ QT_QML_END_NAMESPACE
./
/:
-QT_QML_END_NAMESPACE
-
-
-
#endif // QMLJSPARSER_P_H
:/
diff --git a/src/lib/corelib/parser/qmljsast_p.h b/src/lib/corelib/parser/qmljsast_p.h
index dcee233da..e2ba68508 100644
--- a/src/lib/corelib/parser/qmljsast_p.h
+++ b/src/lib/corelib/parser/qmljsast_p.h
@@ -57,6 +57,10 @@
#include <QtCore/qstring.h>
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QtCore5Compat/qstringref.h>
+#endif
+
namespace QbsQmlJS {
#define QMLJS_DECLARE_AST_NODE(name) \
diff --git a/src/lib/corelib/parser/qmljsastvisitor_p.h b/src/lib/corelib/parser/qmljsastvisitor_p.h
index bec174c65..4b7911aa4 100644
--- a/src/lib/corelib/parser/qmljsastvisitor_p.h
+++ b/src/lib/corelib/parser/qmljsastvisitor_p.h
@@ -58,7 +58,7 @@
namespace QbsQmlJS {
namespace AST {
-class QBS_AUTOTEST_EXPORT Visitor
+class QML_PARSER_EXPORT Visitor
{
public:
Visitor();
diff --git a/src/lib/corelib/parser/qmljsengine_p.cpp b/src/lib/corelib/parser/qmljsengine_p.cpp
index 92ac6452a..67da09825 100644
--- a/src/lib/corelib/parser/qmljsengine_p.cpp
+++ b/src/lib/corelib/parser/qmljsengine_p.cpp
@@ -50,9 +50,9 @@ static int toDigit(char c)
{
if ((c >= '0') && (c <= '9'))
return c - '0';
- else if ((c >= 'a') && (c <= 'z'))
+ if ((c >= 'a') && (c <= 'z'))
return 10 + c - 'a';
- else if ((c >= 'A') && (c <= 'Z'))
+ if ((c >= 'A') && (c <= 'Z'))
return 10 + c - 'A';
return -1;
}
@@ -149,7 +149,7 @@ QStringRef Engine::newStringRef(const QString &text)
{
const int pos = _extraCode.length();
_extraCode += text;
- return _extraCode.midRef(pos, text.length());
+ return QStringRef(&_extraCode).mid(pos, text.length());
}
QStringRef Engine::newStringRef(const QChar *chars, int size)
diff --git a/src/lib/corelib/parser/qmljsengine_p.h b/src/lib/corelib/parser/qmljsengine_p.h
index 19e76a699..9c603ee5c 100644
--- a/src/lib/corelib/parser/qmljsengine_p.h
+++ b/src/lib/corelib/parser/qmljsengine_p.h
@@ -58,7 +58,12 @@
#include <QtCore/qstring.h>
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QtCore5Compat/qstringref.h>
+#endif
+
#include <set>
+#include <utility>
namespace QbsQmlJS {
@@ -74,8 +79,8 @@ public:
DiagnosticMessage()
: kind(Error) {}
- DiagnosticMessage(Kind kind, const AST::SourceLocation &loc, const QString &message)
- : kind(kind), loc(loc), message(message) {}
+ DiagnosticMessage(Kind kind, const AST::SourceLocation &loc, QString message)
+ : kind(kind), loc(loc), message(std::move(message)) {}
bool isWarning() const
{ return kind == Warning; }
@@ -88,7 +93,7 @@ public:
QString message;
};
-class QBS_AUTOTEST_EXPORT Engine
+class QML_PARSER_EXPORT Engine
{
Lexer *_lexer{nullptr};
Directives *_directives{nullptr};
@@ -114,7 +119,10 @@ public:
MemoryPool *pool();
- inline QStringRef midRef(int position, int size) { return _code.midRef(position, size); }
+ inline QStringRef midRef(int position, int size)
+ {
+ return QStringRef(&_code).mid(position, size);
+ }
QStringRef newStringRef(const QString &s);
QStringRef newStringRef(const QChar *chars, int size);
diff --git a/src/lib/corelib/parser/qmljsglobal_p.h b/src/lib/corelib/parser/qmljsglobal_p.h
index c3d198ea5..02e0b38e4 100644
--- a/src/lib/corelib/parser/qmljsglobal_p.h
+++ b/src/lib/corelib/parser/qmljsglobal_p.h
@@ -41,31 +41,14 @@
#include <QtCore/qglobal.h>
-// Force QML_PARSER_EXPORT to be always empty.
-#ifndef QT_CREATOR
-# define QT_CREATOR
-#endif
-#ifdef QML_BUILD_STATIC_LIB
-# undef QML_BUILD_STATIC_LIB
-#endif
-#define QML_BUILD_STATIC_LIB 1
-
-#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
+#ifdef QBS_STATIC_LIB
+#define QML_PARSER_EXPORT
+#else
+#ifdef QBS_LIBRARY
+#define QML_PARSER_EXPORT Q_DECL_EXPORT
+#else
+#define QML_PARSER_EXPORT Q_DECL_IMPORT
+#endif // QBS_LIBRARY
+#endif // QBS_STATIC_LIB
#endif // QMLJSGLOBAL_P_H
diff --git a/src/lib/corelib/parser/qmljsgrammar.cpp b/src/lib/corelib/parser/qmljsgrammar.cpp
index 07a193f6f..75b4a488a 100644
--- a/src/lib/corelib/parser/qmljsgrammar.cpp
+++ b/src/lib/corelib/parser/qmljsgrammar.cpp
@@ -43,969 +43,975 @@
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};
+ "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};
+ 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};
+ 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};
+ 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};
+ 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,
+ 360, 1257, 2491, 2491, 2393, 982, 78, 84, 145, -102,
+ 81, 64, 68, 210, -102, 306, 54, -102, -102, 620,
+ 59, 128, 268, 244, -102, -102, -102, 521, 205, 1257,
+ -102, -102, -102, 442, -102, 2197, 1819, 1257, 1257, 1257,
+ -102, 682, 1257, -102, -102, -102, 1257, 1257, -102, -102,
+ -102, -102, -102, 1257, -102, 1257, 1257, -102, 1257, 1257,
+ 95, 188, -102, -102, 1257, 1257, 1257, -102, -102, -102,
+ 191, 1257, 313, 1257, 1257, 1257, 1257, 521, 1257, 1257,
+ 1257, 1257, 1257, 1257, 196, 1257, 1257, 1257, 151, 146,
+ 116, 167, 174, 182, 186, 189, 521, 429, 521, 1257,
+ 45, 1257, 41, 2099, 1257, 1257, -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,
+ 125, 1257, -102, -102, 58, 46, -102, 1257, -102, -102,
+ 1257, -102, -102, -102, -102, -102, -102, -102, -102, -102,
+ -102, -102, -102, -102, 1257, 17, 1257, 1257, 75, 71,
+ 1257, -102, 2099, 1257, 1257, -102, 154, -102, 51, -102,
+ -102, 55, -102, 288, 72, 57, -102, 297, -102, 62,
+ 2491, -102, -102, -102, -102, -102, 232, -102, -102, 60,
+ -102, -102, -102, -102, -102, -102, 2491, -102, -102, 458,
+ -102, 470, 89, 2393, 49, 442, 69, 47, 2687, 65,
+ 1257, -102, 61, 43, 1257, 40, -102, 39, 82, -102,
+ -102, 442, -102, -102, -102, -102, -102, -102, 102, -102,
+ -102, -102, -102, 98, -102, -102, -102, -102, -102, -102,
+ 76, 63, 1257, 92, 93, -102, -102, 1348, -102, 88,
+ 66, 48, -102, 304, 80, 77, 560, 187, 86, 400,
+ 271, 442, 1257, 302, 1257, 1257, 1257, 1257, 407, 1257,
+ 1257, 1257, 1257, 1257, 253, 257, 272, 281, 278, 362,
+ 358, 337, 1257, 48, 1257, 73, 1257, -102, 620, 1257,
+ -102, 1257, 56, 44, 1257, 42, 2393, -102, 1257, 117,
+ 2393, -102, 1257, 38, 1257, 1257, 187, 74, 1257, -102,
+ 70, 118, 22, -102, -102, 1257, -102, 442, 1257, -102,
+ 67, 1257, 79, 2393, -102, 1257, 112, 2393, -102, -25,
+ 442, -48, -20, 2491, -41, -102, 2393, -102, 1257, 113,
+ 2393, 2, 2393, -102, 3, 11, -34, -102, -102, 2393,
+ -30, 504, 19, 450, 114, 1257, 2393, 20, -16, 414,
+ -1, -31, 772, -3, -4, -102, 1443, -102, 87, -35,
+ -5, 1257, 0, 30, 1257, 33, 1257, 14, 15, 1257,
+ -102, 2295, 1, -102, -102, -102, -102, -102, -102, 1257,
+ -102, -102, -102, -102, 226, -102, 1257, -6, -102, 2393,
+ -102, 96, -102, -102, 2393, -102, 1257, 101, 36, -102,
+ 53, -102, 29, 91, 1257, -102, 24, 23, -102, -24,
+ -102, 2393, -102, 100, 2393, -102, 250, -102, -102, 90,
+ 2393, 16, -102, 10, 12, -102, 442, -12, 4, -102,
+ -102, -102, -102, 1257, 111, 2393, -102, 1257, 105, 2393,
+ -102, 6, -102, 214, -102, -102, 1257, -102, -102, 354,
+ -102, -102, -102, 85, 1537, -102, -102, 1631, -102, -102,
+ 1725, -102, -102, -102, -102, -102, -102, 135, -102, -102,
+ -102, -102, -102, -102, -102, -102, -102, 2491, -102, -102,
+ -102, 159, -10, 890, 202, -11, 18, -102, -102, 222,
+ -102, 199, -102, -102, -102, 359, 211, -102, 1910, -102,
+ -102, -102, -102, -102, -102, -102, -102, -102, 183, -13,
+ 349, 175, -2, 344, 282, -102, -17, -102, 800, 124,
+ -102, -18, 890, -102, -102, 1074, -102, -102, -102, 1166,
+ -102, -102, 208, -102, 1910, -102, 369, -18, -102, -102,
+ 204, 341, 9, 1910, -102, 203, -102, 177, -102, -8,
+ -51, 309, 206, 442, -102, 83, -102, -102, -102, 2001,
+ 800, 369, 2589, 1819, 34, -102, 436, 25, 493, 108,
+ 1257, 2393, 26, 7, 409, 28, 8, 598, 32, 31,
+ -102, 1443, -102, 87, 5, 21, 1257, 52, 37, 1257,
+ 50, 1257, 35, 13, 134, -102, -102, 27, -102, -102,
+ 710, -102, 266, -38, 890, -102, -102, 115, 300, -102,
+ 176, -102, 122, -102, -102, 442, -102, -102, 126, -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};
+ -107, -3, -98, 4, 7, 280, 3, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -35,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, 78,
+ -107, -107, -107, 20, -107, -107, -22, 15, 86, 72,
+ -107, 193, 175, -107, -107, -107, 174, 170, -107, -107,
+ -107, -107, -107, 118, -107, 144, 145, -107, 127, 156,
+ -107, -107, -107, -107, 162, 165, 166, -107, -107, -107,
+ -107, 149, -107, 137, 136, 135, 134, -107, 181, 180,
+ 179, 173, 53, 117, -107, 81, 95, 90, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, 103,
+ -107, 101, -107, 194, 92, 58, -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, 32, -107, -107, -107, -107, -107, 16, -107, -107,
+ 49, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, -107, -107, 70, -107, 109, 34, -107, -107,
+ 45, -107, 216, 60, 100, -107, -107, -107, -107, -107,
+ -107, -107, -107, 44, -107, -107, -107, 47, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, 48, -107, -107, 35,
+ -107, 39, -107, 56, -107, 79, -107, -107, 83, -107,
+ 76, -107, -107, -107, 89, 69, -107, -107, -107, -107,
+ -107, -2, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, 19, -107, -107, -107, -107, 105, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -13, 195, -107, 218, 212, 215, 228, -107, 114,
+ 87, 121, 124, 107, -107, -107, -107, -107, -107, -107,
+ -107, -107, 198, -107, 221, -107, 236, -107, -107, 237,
+ -107, 128, -107, -107, 184, -107, 36, -107, 24, -107,
+ 102, -107, 192, -107, 234, 238, -107, -107, 224, -107,
+ -107, -107, -107, -107, -107, 191, -107, 93, 99, -107,
+ -107, 108, -107, 88, -107, 84, -107, 91, -107, -107,
+ 141, -107, -107, -23, -107, -107, 59, -107, 57, -107,
+ 55, -107, 54, -107, -107, -107, -107, -107, -107, 46,
+ -107, 31, -107, 30, -107, 169, 37, -107, -107, 68,
+ -107, -107, 85, -107, -107, -107, 73, -107, -107, -107,
+ -107, 66, -107, 52, 98, -107, 104, -107, -107, 43,
+ -107, 40, -107, -107, -107, -107, -107, -107, -107, 42,
+ -107, -107, -107, -107, -107, -107, 106, -107, -107, 62,
+ -107, -107, -107, -107, 63, -107, 65, -107, -107, -107,
+ -107, -107, -30, -107, 67, -107, -19, -107, -107, -107,
+ -107, 94, -107, -107, 97, -107, -107, -107, -107, -107,
+ 75, -50, -107, -107, 25, -107, 28, -107, 29, -107,
+ -107, -107, -107, 41, -107, 64, -107, 38, -107, 51,
+ -107, -107, -107, -107, -107, -107, 33, -107, -107, 122,
+ -107, -107, -107, -107, 61, -107, -107, 163, -107, -107,
+ 50, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -107, -107, -107, -107, -107, -107, -107, 82, -107, -107,
+ -107, -107, -107, -12, -107, -107, -107, -107, -107, -107,
+ -107, -15, -107, -107, -107, -5, -107, -107, 297, -107,
+ -107, -107, -107, -107, -107, -107, -107, -107, -107, -107,
+ -9, -26, -107, -11, -107, -107, -107, -107, 183, -107,
+ -107, -107, 288, -107, -107, 283, -107, -107, -107, 390,
+ -107, -107, -107, -107, 321, -107, -107, -8, -107, -107,
+ 11, 18, -107, 305, -107, -107, -107, 1, -107, -107,
+ -107, 14, -4, 0, -107, -107, -107, -107, -107, 337,
+ 217, -107, 22, 279, 9, -107, 8, -107, 6, -107,
+ 153, 17, -107, -107, 5, -107, -107, 96, -107, -107,
+ -107, 13, -107, -107, -107, -107, 23, -107, 10, 77,
+ -107, 71, -107, -107, -107, -107, -107, 12, -107, -107,
+ 21, -107, -107, 26, 148, -107, -107, -107, 27, -107,
+ -107, -107, -107, -107, -107, -10, -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,
+ 572, 416, -124, -103, -121, 348, 346, -126, 399, 575,
+ 389, 340, 342, 343, 345, 545, 563, 538, 352, 391,
+ 257, 401, 533, 466, 531, 403, -132, -113, -124, 424,
+ 431, 430, -132, -113, 424, 346, 444, 440, -103, -121,
+ -129, 416, 71, 257, 348, 440, 446, 565, 448, 457,
+ 453, 457, 620, 335, 408, 560, 511, -129, 424, -126,
+ 545, 164, 141, 440, 141, 99, 147, 545, 414, 420,
+ 440, 453, 409, 440, 272, 453, 346, 164, 170, 416,
+ 187, 318, 71, 348, 644, 257, 292, 325, 296, 272,
+ 0, 183, 0, 477, -102, 190, 443, 0, 0, 252,
+ 141, 427, 306, 0, 0, 304, 149, 434, 141, 141,
+ 444, 179, 99, 141, 292, 236, 0, 189, 457, 141,
+ 141, 141, 0, 0, 101, 141, 327, 294, 331, 0,
+ 172, 0, 542, 141, 0, 0, 314, 62, 301, 333,
+ 315, 478, 542, 141, 577, 576, 428, 101, 63, 173,
+ 249, 248, 254, 251, 256, 255, 58, 413, 412, 247,
+ 246, 418, 141, 242, 241, 459, 0, 59, 620, 249,
+ 248, 455, 337, 350, 172, 249, 248, 310, 166, 328,
+ 543, 142, 167, 634, 633, 58, 58, 640, 639, 312,
+ 615, 58, 85, 173, 86, 488, 59, 59, 172, 85,
+ 64, 86, 59, 64, 512, 87, 512, 85, 0, 86,
+ 177, 85, 87, 86, 85, 554, 86, 173, 518, 406,
+ 87, 85, 469, 86, 87, 103, 0, 87, 512, 579,
+ 85, 529, 86, 512, 87, 512, 0, 637, 636, 567,
+ 141, 172, 0, 87, 104, 65, 105, 0, 65, 0,
+ 514, 66, 514, 0, 66, 545, 64, 0, 141, 0,
+ 173, 513, 406, 513, 568, 566, 0, 635, 0, 555,
+ 553, 0, 519, 517, 514, 470, 468, 514, 85, 514,
+ 86, 514, 85, 172, 86, 513, 0, 0, 513, 536,
+ 513, 87, 513, 235, 234, 87, 85, 85, 86, 86,
+ 0, 65, 173, 85, 174, 86, 85, 66, 86, 87,
+ 87, 438, 437, 630, 580, 0, 87, 34, 0, 87,
+ 274, 275, 274, 275, 73, 74, 34, 631, 629, 34,
+ 0, 73, 74, 0, 0, 0, 0, 0, 34, 0,
+ 0, 0, 0, 537, 535, 0, 0, 276, 277, 276,
+ 277, 75, 76, 0, 48, 50, 49, 628, 75, 76,
+ 279, 280, 0, 48, 50, 49, 48, 50, 49, 281,
+ 34, 0, 282, 34, 283, 48, 50, 49, 34, 0,
+ 45, 279, 280, 34, 172, 279, 280, 0, 34, 45,
+ 281, 0, 45, 282, 281, 283, 0, 282, 0, 283,
+ 0, 45, -90, 173, 0, 174, 0, 48, 50, 49,
+ 48, 50, 49, 0, 0, 48, 50, 49, 0, 0,
+ 48, 50, 49, 279, 280, 48, 50, 49, 0, 0,
+ 279, 280, 281, 45, 0, 282, 45, 283, 34, 281,
+ 0, 45, 282, 34, 283, -345, 45, 0, 0, 0,
+ -345, 45, 78, 79, 6, 5, 4, 1, 3, 2,
+ 80, 81, 0, 0, 82, 34, 83, 0, 0, 0,
+ 0, 34, 0, 0, 0, 48, 50, 49, 0, 34,
+ 48, 50, 49, 0, 0, 0, 0, 34, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 240, 239, 34,
+ 0, 45, 48, 50, 49, 0, 45, 0, 48, 50,
+ 49, 245, 244, 0, 0, 0, 48, 50, 49, 240,
+ 239, 0, 34, 0, 48, 50, 49, 0, 45, 0,
+ 0, 245, 244, 34, 45, 0, 48, 50, 49, 0,
+ 0, 0, 45, 0, 78, 79, 0, 0, 0, 0,
+ 45, 0, 80, 81, 245, 244, 82, 0, 83, 48,
+ 50, 49, 45, 151, 0, 240, 239, 0, 0, 0,
+ 48, 50, 49, 152, 0, 0, 0, 153, 0, 0,
+ 0, 0, 0, 0, 0, 45, 154, 0, 155, 0,
+ 0, 308, 0, 0, 0, 0, 45, 0, 0, 156,
+ 0, 157, 62, 0, 0, 0, 0, 0, 0, 158,
+ 30, 31, 159, 63, 0, 0, 0, 0, 160, 0,
+ 33, 0, 0, 151, 161, 0, 0, 34, 0, 0,
+ 0, 35, 36, 152, 37, 0, 0, 153, 0, 0,
+ 162, 41, 0, 0, 0, 44, 154, 0, 155, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 156,
+ 0, 157, 62, 51, 48, 50, 49, 0, 52, 158,
+ 0, 0, 159, 63, 0, 0, 0, 0, 160, 43,
+ 54, 32, 0, 0, 161, 40, 0, 0, 0, 0,
+ 45, 0, 0, 0, 30, 31, 0, 0, 0, 0,
+ 162, 0, 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, 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, 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,
+ 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, 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, 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, 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, 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,
+ -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, 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, 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, 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, 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, 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, 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, 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};
+ 16, 528, 320, 532, 534, 638, 552, 497, 498, 499,
+ 516, 559, 619, 461, 515, 574, 302, 472, 487, 250,
+ 182, 243, 253, 238, 358, 573, 436, 309, 616, 578,
+ 571, 618, 439, 562, 627, 182, 150, 641, 307, 250,
+ 561, 458, 632, 447, 454, 243, 238, 450, 402, 445,
+ 238, 449, 237, 460, 243, 148, 353, 351, 250, 186,
+ 349, 347, 188, 176, 410, 415, 456, 425, 417, 436,
+ 433, 143, 467, 169, 439, 429, 302, 358, 344, 302,
+ 392, 400, 390, 182, 171, 358, 237, 336, 163, 482,
+ 334, 439, 436, 338, 339, 140, 358, 146, 404, 358,
+ 485, 60, 302, 0, 311, 0, 94, 0, 320, 404,
+ 0, 302, 0, 0, 0, 0, 0, 0, 60, 60,
+ 60, 452, 490, 0, 0, 60, 60, 451, 180, 60,
+ 405, 302, 165, 88, 60, 60, 489, 461, 60, 146,
+ 285, 405, 90, 60, 178, 146, 60, 89, 451, 60,
+ 407, 60, 60, 266, 452, 60, 186, 60, 270, 102,
+ 288, 501, 60, 100, 642, 60, 60, 284, 494, 60,
+ 95, 330, 60, 168, 286, 60, 432, 287, 61, 435,
+ 332, 329, 60, 60, 60, 60, 0, 302, 98, 97,
+ 96, 77, 60, 60, 451, 452, 501, 60, 480, 540,
+ 303, 60, 479, 180, 60, 72, 404, 70, 471, 106,
+ 60, 0, 67, 60, 60, 68, 69, 60, 60, 180,
+ 493, 60, 60, 60, 492, 491, 93, 60, 60, 60,
+ 501, 106, 92, 91, 84, 108, 0, 617, 405, 298,
+ 60, 341, 0, 60, 270, 270, 60, 0, 270, 0,
+ 0, 270, 273, 0, 0, 313, 305, 108, 175, 293,
+ 60, 326, 0, 60, 0, 270, 60, 289, 270, 60,
+ 290, 270, 298, 278, 270, 0, 60, 270, 0, 0,
+ 295, 270, 60, 291, 298, 298, 298, 270, 548, 270,
+ 270, 270, 556, 501, 319, 540, 501, 614, 0, 316,
+ 509, 501, 0, 509, 544, 0, 297, 300, 317, 0,
+ 0, 0, 500, 510, 0, 500, 510, 0, 472, 520,
+ 521, 522, 523, 527, 524, 525, 564, 520, 521, 522,
+ 523, 527, 524, 525, 556, 0, 0, 0, 0, 0,
+ 0, 557, 558, 520, 521, 522, 523, 527, 524, 525,
+ 581, 0, 0, 0, 0, 0, 0, 584, 585, 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, 551, 0, 0, 0, 0,
+ 0, 0, 0, 501, 0, 0, 0, 0, 0, 0,
+ 509, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 500, 510, 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, 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,
+ 8, 36, 7, 7, 7, 36, 7, 7, 7, 60,
+ 7, 36, 60, 33, 55, 33, 7, 34, 16, 8,
+ 36, 55, 24, 17, 37, 55, 7, 7, 7, 5,
+ 7, 55, 7, 7, 5, 7, 20, 33, 7, 7,
+ 7, 36, 1, 36, 36, 33, 36, 29, 60, 36,
+ 36, 36, 90, 31, 60, 66, 66, 7, 5, 7,
+ 33, 2, 8, 33, 8, 48, 8, 33, 7, 33,
+ 33, 36, 7, 33, 1, 36, 7, 2, 7, 36,
+ 8, 7, 1, 36, 0, 36, 48, 17, 8, 1,
+ -1, 36, -1, 8, 7, 33, 6, -1, -1, 36,
+ 8, 10, 60, -1, -1, 61, 60, 7, 8, 8,
+ 20, 60, 48, 8, 48, 55, -1, 60, 36, 8,
+ 8, 8, -1, -1, 79, 8, 8, 79, 61, -1,
+ 15, -1, 8, 8, -1, -1, 50, 42, 61, 60,
+ 54, 56, 8, 8, 61, 62, 55, 79, 53, 34,
+ 61, 62, 60, 77, 61, 62, 40, 61, 62, 61,
+ 62, 60, 8, 61, 62, 60, -1, 51, 90, 61,
+ 62, 60, 60, 60, 15, 61, 62, 60, 50, 61,
+ 56, 56, 54, 61, 62, 40, 40, 61, 62, 2,
+ 56, 40, 25, 34, 27, 60, 51, 51, 15, 25,
+ 12, 27, 51, 12, 29, 38, 29, 25, -1, 27,
+ 56, 25, 38, 27, 25, 7, 27, 34, 7, 36,
+ 38, 25, 8, 27, 38, 15, -1, 38, 29, 7,
+ 25, 29, 27, 29, 38, 29, -1, 61, 62, 36,
+ 8, 15, -1, 38, 34, 57, 36, -1, 57, -1,
+ 75, 63, 75, -1, 63, 33, 12, -1, 8, -1,
+ 34, 86, 36, 86, 61, 62, -1, 91, -1, 61,
+ 62, -1, 61, 62, 75, 61, 62, 75, 25, 75,
+ 27, 75, 25, 15, 27, 86, -1, -1, 86, 7,
+ 86, 38, 86, 61, 62, 38, 25, 25, 27, 27,
+ -1, 57, 34, 25, 36, 27, 25, 63, 27, 38,
+ 38, 61, 62, 47, 92, -1, 38, 29, -1, 38,
+ 18, 19, 18, 19, 18, 19, 29, 61, 62, 29,
+ -1, 18, 19, -1, -1, -1, -1, -1, 29, -1,
+ -1, -1, -1, 61, 62, -1, -1, 45, 46, 45,
+ 46, 45, 46, -1, 66, 67, 68, 91, 45, 46,
+ 23, 24, -1, 66, 67, 68, 66, 67, 68, 32,
+ 29, -1, 35, 29, 37, 66, 67, 68, 29, -1,
+ 92, 23, 24, 29, 15, 23, 24, -1, 29, 92,
+ 32, -1, 92, 35, 32, 37, -1, 35, -1, 37,
+ -1, 92, 33, 34, -1, 36, -1, 66, 67, 68,
+ 66, 67, 68, -1, -1, 66, 67, 68, -1, -1,
+ 66, 67, 68, 23, 24, 66, 67, 68, -1, -1,
+ 23, 24, 32, 92, -1, 35, 92, 37, 29, 32,
+ -1, 92, 35, 29, 37, 36, 92, -1, -1, -1,
+ 36, 92, 23, 24, 94, 95, 96, 97, 98, 99,
+ 31, 32, -1, -1, 35, 29, 37, -1, -1, -1,
+ -1, 29, -1, -1, -1, 66, 67, 68, -1, 29,
+ 66, 67, 68, -1, -1, -1, -1, 29, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 61, 62, 29,
+ -1, 92, 66, 67, 68, -1, 92, -1, 66, 67,
+ 68, 61, 62, -1, -1, -1, 66, 67, 68, 61,
+ 62, -1, 29, -1, 66, 67, 68, -1, 92, -1,
+ -1, 61, 62, 29, 92, -1, 66, 67, 68, -1,
+ -1, -1, 92, -1, 23, 24, -1, -1, -1, -1,
+ 92, -1, 31, 32, 61, 62, 35, -1, 37, 66,
+ 67, 68, 92, 3, -1, 61, 62, -1, -1, -1,
+ 66, 67, 68, 13, -1, -1, -1, 17, -1, -1,
+ -1, -1, -1, -1, -1, 92, 26, -1, 28, -1,
+ -1, 31, -1, -1, -1, -1, 92, -1, -1, 39,
+ -1, 41, 42, -1, -1, -1, -1, -1, -1, 49,
+ 12, 13, 52, 53, -1, -1, -1, -1, 58, -1,
+ 22, -1, -1, 3, 64, -1, -1, 29, -1, -1,
+ -1, 33, 34, 13, 36, -1, -1, 17, -1, -1,
+ 80, 43, -1, -1, -1, 47, 26, -1, 28, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 39,
+ -1, 41, 42, 65, 66, 67, 68, -1, 70, 49,
+ -1, -1, 52, 53, -1, -1, -1, -1, 58, 81,
+ 82, 83, -1, -1, 64, 87, -1, -1, -1, -1,
+ 92, -1, -1, -1, 12, 13, -1, -1, -1, -1,
+ 80, -1, -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, -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, 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, -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, 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,
+ 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, 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};
+ 3, 13, 15, 29, 15, 15, 15, 105, 4, 2,
+ 15, 19, 9, 15, 29, 15, 3, 39, 3, 2,
+ 15, 15, 3, 15, 2, 29, 3, 3, 19, 15,
+ 29, 19, 22, 15, 13, 15, 71, 11, 2, 2,
+ 29, 3, 15, 15, 3, 15, 15, 22, 2, 99,
+ 15, 22, 4, 2, 15, 39, 2, 2, 2, 15,
+ 3, 2, 15, 3, 2, 2, 2, 97, 3, 3,
+ 3, 39, 39, 39, 22, 94, 3, 2, 101, 3,
+ 40, 39, 39, 15, 39, 2, 4, 3, 39, 39,
+ 2, 22, 3, 2, 15, 3, 2, 39, 13, 2,
+ 39, 48, 3, -1, 2, -1, 53, -1, 15, 13,
+ -1, 3, -1, -1, -1, -1, -1, -1, 48, 48,
+ 48, 50, 50, -1, -1, 48, 48, 50, 50, 48,
+ 45, 3, 62, 52, 48, 48, 50, 15, 48, 39,
+ 53, 45, 52, 48, 44, 39, 48, 52, 50, 48,
+ 44, 48, 48, 48, 50, 48, 15, 48, 53, 58,
+ 53, 13, 48, 60, 16, 48, 48, 53, 50, 48,
+ 53, 72, 48, 64, 53, 48, 82, 53, 51, 82,
+ 72, 88, 48, 48, 48, 48, -1, 3, 54, 54,
+ 54, 54, 48, 48, 50, 50, 13, 48, 35, 16,
+ 72, 48, 39, 50, 48, 56, 13, 51, 86, 15,
+ 48, -1, 50, 48, 48, 50, 50, 48, 48, 50,
+ 50, 48, 48, 48, 50, 50, 53, 48, 48, 48,
+ 13, 15, 53, 53, 53, 41, -1, 20, 45, 48,
+ 48, 100, -1, 48, 53, 53, 48, -1, 53, -1,
+ -1, 53, 57, -1, -1, 63, 72, 41, 42, 61,
+ 48, 70, -1, 48, -1, 53, 48, 55, 53, 48,
+ 55, 53, 48, 55, 53, -1, 48, 53, -1, -1,
+ 59, 53, 48, 55, 48, 48, 48, 53, 5, 53,
+ 53, 53, 13, 13, 70, 16, 13, 18, -1, 65,
+ 20, 13, -1, 20, 16, -1, 70, 70, 70, -1,
+ -1, -1, 32, 33, -1, 32, 33, -1, 39, 22,
+ 23, 24, 25, 26, 27, 28, 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, 5, -1, -1, -1, -1,
+ -1, -1, -1, 13, -1, -1, -1, -1, -1, -1,
+ 20, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 32, 33, -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, -1, -1, -1, -1, -1, -1
+};
-} // namespace QbsQmlJS
+} // end of namespace QbsQmlJS
diff --git a/src/lib/corelib/parser/qmljsgrammar_p.h b/src/lib/corelib/parser/qmljsgrammar_p.h
index a3525408b..aa41562de 100644
--- a/src/lib/corelib/parser/qmljsgrammar_p.h
+++ b/src/lib/corelib/parser/qmljsgrammar_p.h
@@ -52,156 +52,155 @@
#ifndef QMLJSGRAMMAR_P_H
#define QMLJSGRAMMAR_P_H
-#include "qmljsglobal_p.h"
#include <QtCore/qglobal.h>
namespace QbsQmlJS {
-class QML_PARSER_EXPORT QmlJSGrammar
+class 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];
- }
+ 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 = 2789,
+ GOTO_CHECK_OFFSET = 2789
+ };
+
+ 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];
+ }
};
diff --git a/src/lib/corelib/parser/qmljslexer.cpp b/src/lib/corelib/parser/qmljslexer.cpp
index 9a09ec348..e148652ad 100644
--- a/src/lib/corelib/parser/qmljslexer.cpp
+++ b/src/lib/corelib/parser/qmljslexer.cpp
@@ -46,7 +46,11 @@
#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
-Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+Q_CORE_EXPORT double qstrntod(const char *s00, int len, char const **se, bool *ok);
+#else
+Q_CORE_EXPORT double qstrntod(const char *s00, qsizetype len, char const **se, bool *ok);
+#endif
QT_END_NAMESPACE
namespace QbsQmlJS {
@@ -65,10 +69,9 @@ static unsigned char convertHex(ushort c)
{
if (c >= '0' && c <= '9')
return (c - '0');
- else if (c >= 'a' && c <= 'f')
+ if (c >= 'a' && c <= 'f')
return (c - 'a' + 10);
- else
- return (c - 'A' + 10);
+ return (c - 'A' + 10);
}
static QChar convertHex(QChar c1, QChar c2)
@@ -78,7 +81,7 @@ static QChar convertHex(QChar c1, QChar c2)
static QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4)
{
- return {uchar((convertHex(c3.unicode()) << 4) + convertHex(c4.unicode())),
+ return QChar{uchar((convertHex(c3.unicode()) << 4) + convertHex(c4.unicode())),
uchar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()))};
}
@@ -178,7 +181,7 @@ int Lexer::lex()
_tokenSpell = QStringRef();
_tokenKind = scanToken();
- _tokenLength = _codePtr - _tokenStartPtr - 1;
+ _tokenLength = int(_codePtr - _tokenStartPtr - 1);
_delimited = false;
_restrictedKeyword = false;
@@ -237,10 +240,10 @@ int Lexer::lex()
bool Lexer::isUnicodeEscapeSequence(const QChar *chars)
{
- if (isHexDigit(chars[0]) && isHexDigit(chars[1]) && isHexDigit(chars[2]) && isHexDigit(chars[3]))
- return true;
-
- return false;
+ return isHexDigit(chars[0])
+ && isHexDigit(chars[1])
+ && isHexDigit(chars[2])
+ && isHexDigit(chars[3]);
}
QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok)
@@ -293,10 +296,9 @@ again:
_tokenLine = _currentLineNumber;
_tokenStartPtr = _codePtr - 1; // ### TODO: insert it before the optional \r sequence.
return T_SEMICOLON;
- } else {
- _terminator = true;
- syncProhibitAutomaticSemicolon();
}
+ _terminator = true;
+ syncProhibitAutomaticSemicolon();
}
scanChar();
@@ -348,7 +350,8 @@ again:
return T_GT_GT_GT_EQ;
}
return T_GT_GT_GT;
- } else if (_char == QLatin1Char('=')) {
+ }
+ if (_char == QLatin1Char('=')) {
scanChar();
return T_GT_GT_EQ;
}
@@ -397,7 +400,8 @@ again:
scanChar();
if (_engine) {
- _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 4,
+ _engine->addComment(tokenOffset() + 2,
+ int(_codePtr - _tokenStartPtr - 1 - 4),
tokenStartLine(), tokenStartColumn() + 2);
}
@@ -412,7 +416,7 @@ again:
scanChar();
}
if (_engine) {
- _engine->addComment(tokenOffset() + 2, _codePtr - _tokenStartPtr - 1 - 2,
+ _engine->addComment(tokenOffset() + 2, int(_codePtr - _tokenStartPtr - 1 - 2),
tokenStartLine(), tokenStartColumn() + 2);
}
goto again;
@@ -452,15 +456,13 @@ again:
}
}
- chars.append('\0');
-
const char *begin = chars.constData();
const char *end = nullptr;
bool ok = false;
- _tokenValue = qstrtod(begin, &end, &ok);
+ _tokenValue = qstrntod(begin, chars.size(), &end, &ok);
- if (end - begin != chars.size() - 1) {
+ if (end - begin != chars.size()) {
_errorCode = IllegalExponentIndicator;
_errorMessage = QCoreApplication::translate("QmlParser", "Illegal syntax for exponential number");
return T_ERROR;
@@ -553,8 +555,10 @@ again:
while (!_char.isNull()) {
if (_char == QLatin1Char('\n') || _char == QLatin1Char('\\')) {
break;
- } else if (_char == quote) {
- _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode);
+ }
+ if (_char == quote) {
+ _tokenSpell = _engine->midRef(
+ int(startCode - _code.unicode() - 1), int(_codePtr - startCode));
scanChar();
return T_STRING_LITERAL;
@@ -706,7 +710,7 @@ again:
if (! identifierWithEscapeChars) {
identifierWithEscapeChars = true;
_tokenText.resize(0);
- _tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1);
+ _tokenText.insert(0, _tokenStartPtr, int(_codePtr - _tokenStartPtr - 1));
_validTokenText = true;
}
@@ -719,7 +723,7 @@ again:
return T_ERROR;
}
} else {
- _tokenLength = _codePtr - _tokenStartPtr - 1;
+ _tokenLength = int(_codePtr - _tokenStartPtr - 1);
int kind = T_IDENTIFIER;
@@ -727,10 +731,12 @@ again:
kind = classify(_tokenStartPtr, _tokenLength, _qmlMode);
if (_engine) {
- if (kind == T_IDENTIFIER && identifierWithEscapeChars)
+ if (kind == T_IDENTIFIER && identifierWithEscapeChars) {
_tokenSpell = _engine->newStringRef(_tokenText);
- else
- _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength);
+ } else {
+ _tokenSpell = _engine->midRef(
+ int(_tokenStartPtr - _code.unicode()), _tokenLength);
+ }
}
return kind;
@@ -842,15 +848,13 @@ int Lexer::scanNumber(QChar ch)
return T_NUMERIC_LITERAL;
}
- chars.append('\0');
-
const char *begin = chars.constData();
const char *end = nullptr;
bool ok = false;
- _tokenValue = qstrtod(begin, &end, &ok);
+ _tokenValue = qstrntod(begin, chars.size(), &end, &ok);
- if (end - begin != chars.size() - 1) {
+ if (end - begin != chars.size()) {
_errorCode = IllegalExponentIndicator;
_errorMessage = QCoreApplication::translate("QmlParser", "Illegal syntax for exponential number");
return T_ERROR;
@@ -891,7 +895,7 @@ bool Lexer::scanRegExp(RegExpBodyPrefix prefix)
scanChar();
}
- _tokenLength = _codePtr - _tokenStartPtr - 1;
+ _tokenLength = int(_codePtr - _tokenStartPtr - 1);
return true;
case '\\':
@@ -916,7 +920,7 @@ bool Lexer::scanRegExp(RegExpBodyPrefix prefix)
while (! _char.isNull() && ! isLineTerminator()) {
if (_char == QLatin1Char(']'))
break;
- else if (_char == QLatin1Char('\\')) {
+ if (_char == QLatin1Char('\\')) {
// regular expression backslash sequence
_tokenText += _char;
scanChar();
@@ -995,7 +999,7 @@ int Lexer::tokenEndLine() const
int Lexer::tokenEndColumn() const
{
- return _codePtr - _lastLinePtr;
+ return int(_codePtr - _lastLinePtr);
}
QString Lexer::tokenText() const
@@ -1067,7 +1071,7 @@ bool Lexer::scanDirectives(Directives *directives)
const int lineNumber = tokenStartLine();
- if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_RESERVED_WORD))
+ if (_tokenKind != T_IDENTIFIER && _tokenKind != T_RESERVED_WORD)
return false; // expected a valid QML/JS directive
const QString directiveName = tokenText();
@@ -1079,7 +1083,7 @@ bool Lexer::scanDirectives(Directives *directives)
// it must be a pragma or an import directive.
if (directiveName == QLatin1String("pragma")) {
// .pragma library
- if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("library")))
+ if (lex() != T_IDENTIFIER || tokenText() != QLatin1String("library"))
return false; // expected `library
// we found a .pragma library directive
@@ -1122,7 +1126,7 @@ bool Lexer::scanDirectives(Directives *directives)
//
// recognize the mandatory `as' followed by the module name
//
- if (! (lex() == T_RESERVED_WORD && tokenText() == QLatin1String("as")))
+ if (lex() != T_RESERVED_WORD || tokenText() != QLatin1String("as"))
return false; // expected `as'
if (lex() != T_IDENTIFIER)
diff --git a/src/lib/corelib/parser/qmljslexer_p.h b/src/lib/corelib/parser/qmljslexer_p.h
index cf41fb255..c9801c0f5 100644
--- a/src/lib/corelib/parser/qmljslexer_p.h
+++ b/src/lib/corelib/parser/qmljslexer_p.h
@@ -56,6 +56,10 @@
#include <tools/qbs_export.h>
#include <QtCore/qstring.h>
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+#include <QtCore5Compat/qstringref.h>
+#endif
+
namespace QbsQmlJS {
class Engine;
@@ -82,7 +86,7 @@ public:
}
};
-class QBS_AUTOTEST_EXPORT Lexer: public QmlJSGrammar
+class QML_PARSER_EXPORT Lexer : public QmlJSGrammar
{
public:
enum {
diff --git a/src/lib/corelib/parser/qmljsparser.cpp b/src/lib/corelib/parser/qmljsparser.cpp
index 3c8ae51e0..d2c87c7cb 100644
--- a/src/lib/corelib/parser/qmljsparser.cpp
+++ b/src/lib/corelib/parser/qmljsparser.cpp
@@ -328,7 +328,7 @@ case 26: {
} break;
case 27: {
- const auto node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0);
+ const auto node = new (pool) AST::UiObjectInitializer(nullptr);
node->lbraceToken = loc(1);
node->rbraceToken = loc(2);
sym(1).Node = node;
@@ -614,7 +614,7 @@ case 80: {
} break;
case 81: {
- const auto node = new (pool) AST::ArrayLiteral((AST::Elision *) 0);
+ const auto node = new (pool) AST::ArrayLiteral(static_cast<AST::Elision *>(nullptr));
node->lbracketToken = loc(1);
node->rbracketToken = loc(2);
sym(1).Node = node;
@@ -635,8 +635,7 @@ case 83: {
} break;
case 84: {
- const auto node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
- (AST::Elision *) 0);
+ const auto node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), nullptr);
node->lbracketToken = loc(1);
node->commaToken = loc(3);
node->rbracketToken = loc(4);
@@ -700,7 +699,7 @@ case 89: {
} break;
case 90: {
- sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression);
+ sym(1).Node = new (pool) AST::ElementList(nullptr, sym(1).Expression);
} break;
case 91: {
@@ -708,8 +707,7 @@ case 91: {
} break;
case 92: {
- const auto node = new (pool) AST::ElementList(sym(1).ElementList,
- (AST::Elision *) 0, sym(3).Expression);
+ const auto node = new (pool) AST::ElementList(sym(1).ElementList, nullptr, sym(3).Expression);
node->commaToken = loc(2);
sym(1).Node = node;
} break;
diff --git a/src/lib/corelib/parser/qmljsparser_p.h b/src/lib/corelib/parser/qmljsparser_p.h
index c761bb25b..9744a7eb6 100644
--- a/src/lib/corelib/parser/qmljsparser_p.h
+++ b/src/lib/corelib/parser/qmljsparser_p.h
@@ -70,7 +70,7 @@ namespace QbsQmlJS {
class Engine;
-class QBS_AUTOTEST_EXPORT Parser: protected QmlJSGrammar
+class QML_PARSER_EXPORT Parser : protected QmlJSGrammar
{
public:
union Value {
diff --git a/src/lib/corelib/tools/applecodesignutils.cpp b/src/lib/corelib/tools/applecodesignutils.cpp
index feae266bf..de74e9206 100644
--- a/src/lib/corelib/tools/applecodesignutils.cpp
+++ b/src/lib/corelib/tools/applecodesignutils.cpp
@@ -95,12 +95,12 @@ QVariantMap certificateInfo(const QByteArray &data)
map.insert(QString::fromUtf8(attr), cert.subjectInfo(attr).front());
return map;
};
-
+ const auto sha1 = QString::fromLatin1(cert.digest(QCryptographicHash::Sha1).toHex().toUpper());
return {
- {QStringLiteral("SHA1"), cert.digest(QCryptographicHash::Sha1).toHex().toUpper()},
+ {QStringLiteral("SHA1"), sha1},
{QStringLiteral("subjectInfo"), subjectInfo(cert)},
- {QStringLiteral("validBefore"), cert.effectiveDate()},
- {QStringLiteral("validAfter"), cert.expiryDate()}
+ {QStringLiteral("validAfter"), cert.effectiveDate()},
+ {QStringLiteral("validBefore"), cert.expiryDate()}
};
}
diff --git a/src/lib/corelib/tools/architectures.cpp b/src/lib/corelib/tools/architectures.cpp
index cf9fec27b..f139509e4 100644
--- a/src/lib/corelib/tools/architectures.cpp
+++ b/src/lib/corelib/tools/architectures.cpp
@@ -55,7 +55,7 @@ QString canonicalTargetArchitecture(const QString &architecture,
const QString &system,
const QString &abi)
{
- const QString arch = canonicalArchitecture(architecture);
+ QString arch = canonicalArchitecture(architecture);
const bool isApple = (vendor == QStringLiteral("apple")
|| system == QStringLiteral("darwin")
|| system == QStringLiteral("macosx")
diff --git a/src/lib/corelib/tools/buildoptions.cpp b/src/lib/corelib/tools/buildoptions.cpp
index e4e9ba17f..cc3ef7557 100644
--- a/src/lib/corelib/tools/buildoptions.cpp
+++ b/src/lib/corelib/tools/buildoptions.cpp
@@ -413,8 +413,8 @@ template<> JobLimits fromJson(const QJsonValue &limitsData)
{
JobLimits limits;
const QJsonArray &limitsArray = limitsData.toArray();
- for (const QJsonValue &v : limitsArray) {
- const QJsonObject limitData = v.toObject();
+ for (const auto &value : limitsArray) {
+ const QJsonObject limitData = value.toObject();
QString pool;
int limit = 0;
setValueFromJson(pool, limitData, "pool");
diff --git a/src/lib/corelib/tools/buildoptions.h b/src/lib/corelib/tools/buildoptions.h
index bd0fb22cb..59b87237d 100644
--- a/src/lib/corelib/tools/buildoptions.h
+++ b/src/lib/corelib/tools/buildoptions.h
@@ -45,10 +45,10 @@
#include "joblimits.h"
#include <QtCore/qshareddata.h>
+#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
class QJsonObject;
-class QStringList;
QT_END_NAMESPACE
namespace qbs {
diff --git a/src/lib/corelib/tools/clangclinfo.cpp b/src/lib/corelib/tools/clangclinfo.cpp
new file mode 100644
index 000000000..fd907ebf1
--- /dev/null
+++ b/src/lib/corelib/tools/clangclinfo.cpp
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** 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 "clangclinfo.h"
+
+#include "hostosinfo.h"
+#include "msvcinfo.h"
+#include "stlutils.h"
+
+namespace qbs {
+namespace Internal {
+
+static std::vector<MSVCInstallInfo> compatibleMsvcs(Logger &logger)
+{
+ auto msvcs = MSVCInstallInfo::installedMSVCs(logger);
+ auto filter = [](const MSVCInstallInfo &info)
+ {
+ const auto versions = info.version.split(QLatin1Char('.'));
+ if (versions.empty())
+ return true;
+ bool ok = false;
+ const int major = versions.at(0).toInt(&ok);
+ return !ok || major < 15; // support MSVC2017 and above
+ };
+ Internal::removeIf(msvcs, filter);
+ for (const auto &msvc: msvcs) {
+ auto vcvarsallPath = msvc.findVcvarsallBat();
+ if (vcvarsallPath.isEmpty())
+ continue;
+ }
+ return msvcs;
+}
+
+static QString findCompatibleVcsarsallBat(const std::vector<MSVCInstallInfo> &msvcs)
+{
+ for (const auto &msvc: msvcs) {
+ auto vcvarsallPath = msvc.findVcvarsallBat();
+ if (!vcvarsallPath.isEmpty())
+ return vcvarsallPath;
+ }
+ return {};
+}
+
+static QString wow6432Key()
+{
+#ifdef Q_OS_WIN64
+ return QStringLiteral("\\Wow6432Node");
+#else
+ return {};
+#endif
+}
+
+static QString getToolchainInstallPath(const QFileInfo &compiler)
+{
+ return compiler.path(); // 1 level up
+}
+
+QVariantMap ClangClInfo::toVariantMap() const
+{
+ return {
+ {QStringLiteral("toolchainInstallPath"), toolchainInstallPath},
+ {QStringLiteral("vcvarsallPath"), vcvarsallPath},
+ };
+}
+
+ClangClInfo ClangClInfo::fromCompilerFilePath(const QString &path, Logger &logger)
+{
+ const auto compilerName = QStringLiteral("clang-cl");
+ const auto vcvarsallPath = findCompatibleVcsarsallBat(compatibleMsvcs(logger));
+ if (vcvarsallPath.isEmpty()) {
+ logger.qbsWarning()
+ << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.")
+ .arg(compilerName);
+ return {};
+ }
+
+ return {getToolchainInstallPath(QFileInfo(path)), vcvarsallPath};
+}
+
+std::vector<ClangClInfo> ClangClInfo::installedCompilers(
+ const std::vector<QString> &extraPaths, Logger &logger)
+{
+ std::vector<QString> compilerPaths;
+ compilerPaths.reserve(extraPaths.size());
+ std::copy_if(extraPaths.begin(), extraPaths.end(),
+ std::back_inserter(compilerPaths),
+ [](const QString &path){ return !path.isEmpty(); });
+ const auto compilerName = HostOsInfo::appendExecutableSuffix(QStringLiteral("clang-cl"));
+
+ const QSettings registry(
+ QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\LLVM\\LLVM").arg(wow6432Key()),
+ QSettings::NativeFormat);
+ const auto key = QStringLiteral(".");
+ if (registry.contains(key)) {
+ const auto compilerPath = QDir::fromNativeSeparators(registry.value(key).toString())
+ + QStringLiteral("/bin/") + compilerName;
+ if (QFileInfo::exists(compilerPath) && !contains(compilerPaths, compilerPath))
+ compilerPaths.push_back(compilerPath);
+ }
+
+ // this branch can be useful in case user had two LLVM installations (e.g. 32bit & 64bit)
+ // but uninstalled one - in that case, registry will be empty
+ static const char * const envVarCandidates[] = {"ProgramFiles", "ProgramFiles(x86)"};
+ for (const auto &envVar : envVarCandidates) {
+ const auto value
+ = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv(envVar)));
+ const auto compilerPath = value + QStringLiteral("/LLVM/bin/") + compilerName;
+ if (QFileInfo::exists(compilerPath) && !contains(compilerPaths, compilerPath))
+ compilerPaths.push_back(compilerPath);
+ }
+
+ const auto msvcs = compatibleMsvcs(logger);
+ const auto vcvarsallPath = findCompatibleVcsarsallBat(msvcs);
+ if (vcvarsallPath.isEmpty()) {
+ logger.qbsWarning()
+ << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.")
+ .arg(compilerName);
+ return {};
+ }
+
+ std::vector<ClangClInfo> result;
+ result.reserve(compilerPaths.size() + msvcs.size());
+ transform(compilerPaths, result, [&vcvarsallPath](const auto &path) {
+ return ClangClInfo{getToolchainInstallPath(QFileInfo(path)), vcvarsallPath}; });
+
+ // If we didn't find custom LLVM installation, try to find if it's installed with Visual Studio
+ for (const auto &msvc : msvcs) {
+ const auto compilerPath = QStringLiteral("%1/VC/Tools/Llvm/bin/%2")
+ .arg(msvc.installDir, compilerName);
+ if (QFileInfo::exists(compilerPath)) {
+ const auto vcvarsallPath = msvc.findVcvarsallBat();
+ if (vcvarsallPath.isEmpty()) {
+ logger.qbsWarning()
+ << Tr::tr("Found LLVM in %1, but vcvarsall.bat is missing.")
+ .arg(msvc.installDir);
+ }
+
+ result.push_back({getToolchainInstallPath(QFileInfo(compilerPath)), vcvarsallPath});
+ }
+ }
+
+ return result;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/clangclinfo.h b/src/lib/corelib/tools/clangclinfo.h
new file mode 100644
index 000000000..76ae169f2
--- /dev/null
+++ b/src/lib/corelib/tools/clangclinfo.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** 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_CLANGCLINFO_H
+#define QBS_CLANGCLINFO_H
+
+#include "logging/logger.h"
+
+#include <QtCore/qstring.h>
+
+#include <vector>
+
+namespace qbs {
+namespace Internal {
+
+class ClangClInfo
+{
+public:
+ QString toolchainInstallPath;
+ QString vcvarsallPath;
+
+ bool isEmpty() const { return toolchainInstallPath.isEmpty() && vcvarsallPath.isEmpty(); }
+
+ QBS_EXPORT QVariantMap toVariantMap() const;
+
+ QBS_EXPORT static ClangClInfo fromCompilerFilePath(const QString &path, Logger &logger);
+ QBS_EXPORT static std::vector<ClangClInfo> installedCompilers(
+ const std::vector<QString> &extraPaths, qbs::Internal::Logger &logger);
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_CLANGCLINFO_H
diff --git a/src/lib/corelib/tools/cleanoptions.cpp b/src/lib/corelib/tools/cleanoptions.cpp
index cef77468e..affc9b3f7 100644
--- a/src/lib/corelib/tools/cleanoptions.cpp
+++ b/src/lib/corelib/tools/cleanoptions.cpp
@@ -58,7 +58,7 @@ public:
bool logElapsedTime;
};
-}
+} // namespace Internal
/*!
* \class CleanOptions
diff --git a/src/lib/corelib/tools/codelocation.cpp b/src/lib/corelib/tools/codelocation.cpp
index 542408795..f04041e14 100644
--- a/src/lib/corelib/tools/codelocation.cpp
+++ b/src/lib/corelib/tools/codelocation.cpp
@@ -47,7 +47,7 @@
#include <QtCore/qdir.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonvalue.h>
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h>
@@ -87,8 +87,9 @@ CodeLocation::CodeLocation(const QString &aFilePath, int aLine, int aColumn, boo
}
CodeLocation::CodeLocation(const CodeLocation &other) = default;
-
+CodeLocation::CodeLocation(CodeLocation &&other) noexcept = default;
CodeLocation &CodeLocation::operator=(const CodeLocation &other) = default;
+CodeLocation &CodeLocation::operator=(CodeLocation &&other) noexcept = default;
CodeLocation::~CodeLocation() = default;
@@ -118,9 +119,9 @@ QString CodeLocation::toString() const
if (isValid()) {
str = QDir::toNativeSeparators(filePath());
QString lineAndColumn;
- if (line() > 0 && !str.contains(QRegExp(QStringLiteral(":[0-9]+$"))))
+ if (line() > 0 && !str.contains(QRegularExpression(QStringLiteral(":[0-9]+$"))))
lineAndColumn += QLatin1Char(':') + QString::number(line());
- if (column() > 0 && !str.contains(QRegExp(QStringLiteral(":[0-9]+:[0-9]+$"))))
+ if (column() > 0 && !str.contains(QRegularExpression(QStringLiteral(":[0-9]+:[0-9]+$"))))
lineAndColumn += QLatin1Char(':') + QString::number(column());
str += lineAndColumn;
}
@@ -181,4 +182,44 @@ bool operator<(const CodeLocation &cl1, const CodeLocation &cl2)
return cl1.toString() < cl2.toString();
}
+void CodePosition::load(Internal::PersistentPool &pool) { pool.load(m_line, m_column); }
+void CodePosition::store(Internal::PersistentPool &pool) const { pool.store(m_line, m_column); }
+
+bool operator==(const CodePosition &pos1, const CodePosition &pos2)
+{
+ return pos1.line() == pos2.line() && pos1.column() == pos2.column();
+}
+bool operator!=(const CodePosition &pos1, const CodePosition &pos2) { return !(pos1 == pos2); }
+
+bool operator<(const CodePosition &pos1, const CodePosition &pos2)
+{
+ const int lineDiff = pos1.line() - pos2.line();
+ if (lineDiff < 0)
+ return true;
+ if (lineDiff > 0)
+ return false;
+ return pos1.column() < pos2.column();
+}
+bool operator>(const CodePosition &pos1, const CodePosition &pos2) { return pos2 < pos1; }
+bool operator<=(const CodePosition &pos1, const CodePosition &pos2) { return !(pos1 > pos2); }
+bool operator>=(const CodePosition &pos1, const CodePosition &pos2) { return !(pos1 < pos2); }
+
+CodeRange::CodeRange(const CodePosition &start, const CodePosition &end)
+ : m_start(start), m_end(end) {}
+
+void CodeRange::load(Internal::PersistentPool &pool) { pool.load(m_start, m_end); }
+void CodeRange::store(Internal::PersistentPool &pool) const { pool.store(m_start, m_end); }
+
+bool CodeRange::contains(const CodePosition &pos) const
+{
+ return start() <= pos && end() > pos;
+}
+
+bool operator==(const CodeRange &r1, const CodeRange &r2)
+{
+ return r1.start() == r2.start() && r1.end() == r2.end();
+}
+bool operator!=(const CodeRange &r1, const CodeRange &r2) { return !(r1 == r2); }
+bool operator<(const CodeRange &r1, const CodeRange &r2) { return r1.start() < r2.start(); }
+
} // namespace qbs
diff --git a/src/lib/corelib/tools/codelocation.h b/src/lib/corelib/tools/codelocation.h
index 3e84ce2d1..afcd2e075 100644
--- a/src/lib/corelib/tools/codelocation.h
+++ b/src/lib/corelib/tools/codelocation.h
@@ -62,7 +62,9 @@ public:
explicit CodeLocation(const QString &aFilePath, int aLine = -1, int aColumn = -1,
bool checkPath = true);
CodeLocation(const CodeLocation &other);
+ CodeLocation(CodeLocation &&other) noexcept;
CodeLocation &operator=(const CodeLocation &other);
+ CodeLocation &operator=(CodeLocation &&other) noexcept;
~CodeLocation();
QString filePath() const;
@@ -84,10 +86,78 @@ private:
QBS_EXPORT bool operator==(const CodeLocation &cl1, const CodeLocation &cl2);
QBS_EXPORT bool operator!=(const CodeLocation &cl1, const CodeLocation &cl2);
QBS_EXPORT bool operator<(const CodeLocation &cl1, const CodeLocation &cl2);
+inline auto qHash(const CodeLocation &cl) { return qHash(cl.toString()); }
+QDebug operator<<(QDebug debug, const CodeLocation &location);
-inline uint qHash(const CodeLocation &cl) { return qHash(cl.toString()); }
+class QBS_EXPORT CodePosition
+{
+public:
+ CodePosition(int line, int column) : m_line(line), m_column(column) {}
-QDebug operator<<(QDebug debug, const CodeLocation &location);
+ CodePosition() = default;
+ CodePosition(const CodePosition &other) = default;
+ CodePosition(CodePosition &&other) = default;
+ CodePosition &operator=(const CodePosition &other) = default;
+ CodePosition &operator=(CodePosition &&other) = default;
+
+ int line() const { return m_line; }
+ void setLine(int newLine) { m_line = newLine; }
+
+ int column() const { return m_column; }
+ void setColumn(int newColumn) { m_column = newColumn; }
+
+ void load(Internal::PersistentPool &pool);
+ void store(Internal::PersistentPool &pool) const;
+
+private:
+ int m_line = 0;
+ int m_column = 0;
+};
+
+QBS_EXPORT bool operator==(const CodePosition &pos1, const CodePosition &pos2);
+QBS_EXPORT bool operator!=(const CodePosition &pos1, const CodePosition &pos2);
+QBS_EXPORT bool operator<(const CodePosition &pos1, const CodePosition &pos2);
+QBS_EXPORT bool operator>(const CodePosition &pos1, const CodePosition &pos2);
+QBS_EXPORT bool operator<=(const CodePosition &pos1, const CodePosition &pos2);
+QBS_EXPORT bool operator>=(const CodePosition &pos1, const CodePosition &pos2);
+inline auto qHash(const CodePosition &pos)
+{
+ return QT_PREPEND_NAMESPACE(qHash)(pos.line()) ^ QT_PREPEND_NAMESPACE(qHash)(pos.column());
+}
+
+class QBS_EXPORT CodeRange
+{
+public:
+ CodeRange(const CodePosition &start, const CodePosition &end);
+
+ CodeRange() = default;
+ CodeRange(const CodeRange &other) = default;
+ CodeRange(CodeRange &&other) = default;
+ CodeRange &operator=(const CodeRange &other) = default;
+ CodeRange &operator=(CodeRange &&other) = default;
+
+ const CodePosition &start() const & { return m_start; }
+ const CodePosition &end() const & { return m_end; }
+ CodePosition start() && { return std::move(m_start); }
+ CodePosition end() && { return std::move(m_end); }
+
+ bool contains(const CodePosition &pos) const;
+
+ void load(Internal::PersistentPool &pool);
+ void store(Internal::PersistentPool &pool) const;
+
+private:
+ CodePosition m_start;
+ CodePosition m_end;
+};
+
+QBS_EXPORT bool operator==(const CodeRange &r1, const CodeRange &r2);
+QBS_EXPORT bool operator!=(const CodeRange &r1, const CodeRange &r2);
+QBS_EXPORT bool operator<(const CodeRange &r1, const CodeRange &r2);
+inline auto qHash(const CodeRange &range) { return qHash(range.start()) ^ qHash(range.end()); }
+
+using CodeLinksInFile = QHash<CodeRange, QList<CodeLocation>>;
+using CodeLinks = QHash<QString, CodeLinksInFile>;
} // namespace qbs
diff --git a/src/lib/corelib/tools/commandechomode.h b/src/lib/corelib/tools/commandechomode.h
index 88d8377ad..e7b315563 100644
--- a/src/lib/corelib/tools/commandechomode.h
+++ b/src/lib/corelib/tools/commandechomode.h
@@ -43,11 +43,7 @@
#include "qbs_export.h"
#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-class QString;
-class QStringList;
-QT_END_NAMESPACE
+#include <QtCore/qstringlist.h>
namespace qbs {
diff --git a/src/lib/corelib/tools/deprecationwarningmode.cpp b/src/lib/corelib/tools/deprecationwarningmode.cpp
new file mode 100644
index 000000000..e140e3e49
--- /dev/null
+++ b/src/lib/corelib/tools/deprecationwarningmode.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "deprecationwarningmode.h"
+
+/*!
+ * \enum DeprecationWarningMode
+ * This enum type specifies how \QBS should behave on encountering deprecated items or properties.
+ * \value DeprecationWarningMode::Error Project resolving will stop with an error message.
+ * \value DeprecationWarningMode::On A warning will be printed.
+ * \value DeprecationWarningMode::BeforeRemoval A warning will be printed if and only if this is
+ * the last \QBS version before the removal version. This is the default behavior.
+ * \note If the removal version's minor version number is zero, the behavior is
+ * the same as for ErrorHandlingMode::On.
+ * \value DeprecationWarningMode::Off No warnings will be emitted for deprecated constructs.
+ */
+
+namespace qbs {
+
+DeprecationWarningMode defaultDeprecationWarningMode()
+{
+ return DeprecationWarningMode::BeforeRemoval;
+}
+
+QString deprecationWarningModeName(DeprecationWarningMode mode)
+{
+ switch (mode) {
+ case DeprecationWarningMode::Error:
+ return QStringLiteral("error");
+ case DeprecationWarningMode::On:
+ return QStringLiteral("on");
+ case DeprecationWarningMode::BeforeRemoval:
+ return QStringLiteral("before-removal");
+ case DeprecationWarningMode::Off:
+ return QStringLiteral("off");
+ default:
+ break;
+ }
+ return {};
+}
+
+DeprecationWarningMode deprecationWarningModeFromName(const QString &name)
+{
+ DeprecationWarningMode mode = defaultDeprecationWarningMode();
+ for (int i = 0; i <= int(DeprecationWarningMode::Sentinel); ++i) {
+ if (deprecationWarningModeName(static_cast<DeprecationWarningMode>(i)) == name) {
+ mode = static_cast<DeprecationWarningMode>(i);
+ break;
+ }
+ }
+ return mode;
+}
+
+QStringList allDeprecationWarningModeStrings()
+{
+ QStringList result;
+ for (int i = 0; i <= int(DeprecationWarningMode::Sentinel); ++i)
+ result << deprecationWarningModeName(static_cast<DeprecationWarningMode>(i));
+ return result;
+}
+
+} // namespace qbs
diff --git a/src/lib/corelib/tools/deprecationwarningmode.h b/src/lib/corelib/tools/deprecationwarningmode.h
new file mode 100644
index 000000000..bb2a14155
--- /dev/null
+++ b/src/lib/corelib/tools/deprecationwarningmode.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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_DEPRECATIONWARNINGMODE_H
+#define QBS_DEPRECATIONWARNINGMODE_H
+
+#include "qbs_export.h"
+
+#include <QtCore/qstringlist.h>
+
+namespace qbs {
+
+enum class DeprecationWarningMode { Error, On, BeforeRemoval, Off, Sentinel = Off };
+
+QBS_EXPORT DeprecationWarningMode defaultDeprecationWarningMode();
+QBS_EXPORT QString deprecationWarningModeName(DeprecationWarningMode mode);
+QBS_EXPORT DeprecationWarningMode deprecationWarningModeFromName(const QString &name);
+QBS_EXPORT QStringList allDeprecationWarningModeStrings();
+
+} // namespace qbs
+
+#endif // QBS_DEPRECATIONWARNINGMODE_H
+
diff --git a/src/lib/corelib/tools/error.cpp b/src/lib/corelib/tools/error.cpp
index ff7ce46cf..f1b90b71e 100644
--- a/src/lib/corelib/tools/error.cpp
+++ b/src/lib/corelib/tools/error.cpp
@@ -40,8 +40,11 @@
#include "error.h"
#include "persistence.h"
-#include "qttools.h"
#include "stringconstants.h"
+#include "setupprojectparameters.h"
+#include "logging/logger.h"
+
+#include <tools/stlutils.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsonobject.h>
@@ -205,15 +208,21 @@ ErrorInfo::ErrorInfo(const QString &description, const QStringList &backtrace)
{
append(description);
for (const QString &traceLine : backtrace) {
- static const std::regex regexp("^(.+) at (.+):(\\-?[0-9]+)$");
+ if (traceLine.contains(QStringLiteral("<eval>")))
+ continue;
+ static const std::regex regexpWithFunc("^(.+) at [^(]*\\((.+):(\\-?[0-9]+)\\)$");
+ static const std::regex regexpWithoutFunc("^(.+) at (.+):(\\-?[0-9]+)$");
std::smatch match;
const std::string tl = traceLine.toStdString();
- if (std::regex_match(tl, match, regexp)) {
- const QString message = QString::fromStdString(match[1]),
- file = QString::fromStdString(match[2]),
- line = QString::fromStdString(match[3]);
+ bool hasMatch = std::regex_match(tl, match, regexpWithFunc);
+ if (!hasMatch)
+ hasMatch = std::regex_match(tl, match, regexpWithoutFunc);
+ if (hasMatch) {
+ const QString message = QString::fromStdString(match[1]).trimmed();
+ const QString file = QString::fromStdString(match[2]);
+ const QString line = QString::fromStdString(match[3]);
const CodeLocation location(file, line.toInt());
- appendBacktrace(message, location);
+ appendBacktrace(message, CodeLocation(file, line.toInt()));
}
}
}
@@ -267,7 +276,7 @@ void ErrorInfo::clear()
QString ErrorInfo::toString() const
{
QStringList lines;
- for (const ErrorItem &e : qAsConst(d->items)) {
+ for (const ErrorItem &e : std::as_const(d->items)) {
if (e.isBacktraceItem()) {
QString line;
if (!e.description().isEmpty())
@@ -304,8 +313,15 @@ bool ErrorInfo::isInternalError() const
bool ErrorInfo::hasLocation() const
{
- return std::any_of(d->items.cbegin(), d->items.cend(),
- [](const ErrorItem &ei) { return ei.codeLocation().isValid(); });
+ return Internal::any_of(d->items, [](const ErrorItem &ei) {
+ return ei.codeLocation().isValid(); });
+}
+
+bool ErrorInfo::isCancelException() const
+{
+ return Internal::any_of(d->items, [](const ErrorItem &ei) {
+ return ei.description() == QLatin1String("interrupted");
+ });
}
void ErrorInfo::load(Internal::PersistentPool &pool)
@@ -325,4 +341,12 @@ void appendError(ErrorInfo &dst, const ErrorInfo &src)
dst.append(item);
}
+void handlePropertyError(
+ const ErrorInfo &error, const SetupProjectParameters &params, Internal::Logger &logger)
+{
+ if (params.propertyCheckingMode() == ErrorHandlingMode::Strict)
+ throw error;
+ logger.printWarning(error);
+}
+
} // namespace qbs
diff --git a/src/lib/corelib/tools/error.h b/src/lib/corelib/tools/error.h
index 36cf5e0ea..ba600a558 100644
--- a/src/lib/corelib/tools/error.h
+++ b/src/lib/corelib/tools/error.h
@@ -45,18 +45,21 @@
#include <QtCore/qhash.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qshareddata.h>
+#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
class QJsonObject;
-template <class T> class QList;
-class QString;
-class QStringList;
QT_END_NAMESPACE
namespace qbs {
-namespace Internal { class PersistentPool; }
+namespace Internal {
+class PersistentPool;
+class Logger;
+} // namespace Internal
class CodeLocation;
+class SetupProjectParameters;
+
class QBS_EXPORT ErrorItem
{
friend class ErrorInfo;
@@ -110,6 +113,7 @@ public:
QJsonObject toJson() const;
bool isInternalError() const;
bool hasLocation() const;
+ bool isCancelException() const;
void load(Internal::PersistentPool &pool);
void store(Internal::PersistentPool &pool) const;
@@ -120,7 +124,10 @@ private:
};
void appendError(ErrorInfo &dst, const ErrorInfo &src);
-inline uint qHash(const ErrorInfo &e) { return qHash(e.toString()); }
+void handlePropertyError(
+ const ErrorInfo &error, const SetupProjectParameters &params, Internal::Logger &logger);
+
+inline auto qHash(const ErrorInfo &e) { return qHash(e.toString()); }
inline bool operator==(const ErrorInfo &e1, const ErrorInfo &e2) {
return e1.toString() == e2.toString();
}
diff --git a/src/lib/corelib/tools/executablefinder.cpp b/src/lib/corelib/tools/executablefinder.cpp
index 0dc06dd86..b5d9c0151 100644
--- a/src/lib/corelib/tools/executablefinder.cpp
+++ b/src/lib/corelib/tools/executablefinder.cpp
@@ -76,7 +76,7 @@ QString ExecutableFinder::findExecutable(const QString &path, const QString &wor
//if (FileInfo::fileName(filePath) == filePath)
if (!FileInfo::isAbsolute(filePath))
return findInPath(filePath, workingDirPath);
- else if (HostOsInfo::isWindowsHost())
+ if (HostOsInfo::isWindowsHost())
return findBySuffix(filePath);
return filePath;
}
@@ -99,7 +99,7 @@ QString ExecutableFinder::findBySuffix(const QString &filePath) const
bool ExecutableFinder::candidateCheck(const QString &directory, const QString &program,
QString &fullProgramPath) const
{
- for (const QString &suffix : qAsConst(m_executableSuffixes)) {
+ for (const QString &suffix : std::as_const(m_executableSuffixes)) {
QString candidate = directory + program + suffix;
qCDebug(lcExec) << "candidate:" << candidate;
QFileInfo fi(candidate);
@@ -120,10 +120,10 @@ QString ExecutableFinder::findInPath(const QString &filePath, const QString &wor
fullProgramPath = filePath;
qCDebug(lcExec) << "looking for executable in PATH" << fullProgramPath;
QStringList pathEnv = m_environment.value(StringConstants::pathEnvVar())
- .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+ .split(HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts);
if (HostOsInfo::isWindowsHost())
pathEnv.prepend(StringConstants::dot());
- for (QString directory : qAsConst(pathEnv)) {
+ for (QString directory : std::as_const(pathEnv)) {
if (directory == StringConstants::dot())
directory = workingDirPath;
if (!directory.isEmpty()) {
diff --git a/src/lib/corelib/tools/fileinfo.cpp b/src/lib/corelib/tools/fileinfo.cpp
index 1918117d6..f56762c04 100644
--- a/src/lib/corelib/tools/fileinfo.cpp
+++ b/src/lib/corelib/tools/fileinfo.cpp
@@ -41,13 +41,14 @@
#include <logging/translator.h>
#include <tools/qbsassert.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#if defined(Q_OS_UNIX)
#include <cerrno>
@@ -133,16 +134,16 @@ void FileInfo::splitIntoDirectoryAndFileName(const QString &filePath, QString *d
*fileName = filePath.mid(idx + 1);
}
-void FileInfo::splitIntoDirectoryAndFileName(const QString &filePath, QStringRef *dirPath, QStringRef *fileName)
+void FileInfo::splitIntoDirectoryAndFileName(const QString &filePath, QStringView *dirPath, QStringView *fileName)
{
int idx = filePath.lastIndexOf(QLatin1Char('/'));
if (idx < 0) {
- dirPath->clear();
- *fileName = QStringRef(&filePath);
+ *dirPath = QStringView();
+ *fileName = QStringView(filePath);
return;
}
- *dirPath = filePath.leftRef(idx);
- *fileName = filePath.midRef(idx + 1);
+ *dirPath = QStringView(filePath).left(idx);
+ *fileName = QStringView(filePath).mid(idx + 1);
}
bool FileInfo::exists(const QString &fp)
@@ -180,20 +181,12 @@ bool FileInfo::isAbsolute(const QString &path, HostOsInfo::HostOs hostOs)
return false;
}
-bool FileInfo::isPattern(const QString &str)
-{
- return isPattern(QStringRef(&str));
-}
-
-bool FileInfo::isPattern(const QStringRef &str)
+bool FileInfo::isPattern(QStringView str)
{
- for (const QChar &ch : str) {
- if (ch == QLatin1Char('*') || ch == QLatin1Char('?')
- || ch == QLatin1Char(']') || ch == QLatin1Char('[')) {
- return true;
- }
- }
- return false;
+ return Internal::any_of(str, [](const auto &ch) {
+ return (ch == QLatin1Char('*') || ch == QLatin1Char('?') || ch == QLatin1Char(']')
+ || ch == QLatin1Char('['));
+ });
}
/**
@@ -242,17 +235,6 @@ QString FileInfo::resolvePath(const QString &base, const QString &rel, HostOsInf
return r;
}
-bool FileInfo::globMatches(const QRegExp &regexp, const QString &fileName)
-{
- const QString pattern = regexp.pattern();
- // May be it's simple wildcard, i.e. "*.cpp"?
- if (pattern.startsWith(QLatin1Char('*')) && !isPattern(pattern.midRef(1))) {
- // Yes, it's rather simple to just check the extension
- return fileName.endsWith(pattern.midRef(1));
- }
- return regexp.exactMatch(fileName);
-}
-
#ifdef Q_OS_WIN
static QString prependLongPathPrefix(const QString &absolutePath)
{
@@ -353,9 +335,70 @@ QString applicationDirPath()
#elif defined(Q_OS_UNIX)
+namespace GetFileTimes {
+
+enum TimeType { LastModified, LastStatusChanged };
+
+// SFINAE magic inspired by
+// https://github.com/qt/qtbase/blob/5.13/src/corelib/io/qfilesystemengine_unix.cpp
+template<
+ TimeType type,
+ typename T,
+ std::enable_if_t<(&T::st_mtim, &T::st_ctim, true), int> = 0>
+static inline FileTime get(const T &stat)
+{
+ if constexpr (type == LastModified)
+ return stat.st_mtim;
+ else if constexpr (type == LastStatusChanged)
+ return stat.st_ctim;
+}
+
+// we are interested in real members, not compatibility macros
+# if defined(st_mtimespec)
+# undef st_mtimespec
+# endif
+
+# if defined(st_ctimespec)
+# undef st_ctimespec
+# endif
+
+template<
+ TimeType type,
+ typename T,
+ std::enable_if_t<(&T::st_mtimespec, &T::st_ctimespec, true), int> = 0>
+static inline FileTime get(const T &stat)
+{
+ if constexpr (type == LastModified)
+ return stat.st_mtimespec;
+ else if constexpr (type == LastStatusChanged)
+ return stat.st_ctimespec;
+}
+
+# if defined(st_mtimensec)
+# undef st_mtimensec
+# endif
+
+# if defined(st_ctimensec)
+# undef st_ctimensec
+# endif
+
+template<
+ TimeType type,
+ typename T,
+ std::enable_if_t<(&T::st_mtimensec, &T::st_ctimensec, true), int> = 0>
+static inline FileTime get(const T &stat)
+{
+ if constexpr (type == LastModified)
+ return FileTime::InternalType{stat.st_mtime, stat.st_mtimensec};
+ else if constexpr (type == LastStatusChanged)
+ return FileTime::InternalType{stat.st_ctime, stat.st_ctimensec};
+}
+
+} // namespace GetFileTimes
+
FileInfo::FileInfo(const QString &fileName)
{
- if (stat(fileName.toLocal8Bit(), &m_stat) == -1) {
+ if (stat(fileName.toLocal8Bit().constData(), &m_stat) == -1) {
m_stat.st_mtime = 0;
m_stat.st_mode = 0;
}
@@ -368,24 +411,12 @@ bool FileInfo::exists() const
FileTime FileInfo::lastModified() const
{
-#if APPLE_STAT_TIMESPEC
- return m_stat.st_mtimespec;
-#elif HAS_CLOCK_GETTIME
- return m_stat.st_mtim;
-#else
- return m_stat.st_mtime;
-#endif
+ return GetFileTimes::get<GetFileTimes::LastModified>(m_stat);
}
FileTime FileInfo::lastStatusChange() const
{
-#if APPLE_STAT_TIMESPEC
- return m_stat.st_ctimespec;
-#elif HAS_CLOCK_GETTIME
- return m_stat.st_ctim;
-#else
- return m_stat.st_ctime;
-#endif
+ return GetFileTimes::get<GetFileTimes::LastStatusChanged>(m_stat);
}
bool FileInfo::isDir() const
@@ -450,7 +481,7 @@ static QByteArray storedLinkTarget(const QString &filePath)
const QByteArray nativeFilePath = QFile::encodeName(filePath);
ssize_t len;
while (true) {
- struct stat sb;
+ struct stat sb{};
if (lstat(nativeFilePath.constData(), &sb)) {
qWarning("storedLinkTarget: lstat for %s failed with error code %d",
nativeFilePath.constData(), errno);
diff --git a/src/lib/corelib/tools/fileinfo.h b/src/lib/corelib/tools/fileinfo.h
index 9813b69a7..3ad585f74 100644
--- a/src/lib/corelib/tools/fileinfo.h
+++ b/src/lib/corelib/tools/fileinfo.h
@@ -72,14 +72,12 @@ public:
static QString completeSuffix(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 void splitIntoDirectoryAndFileName(const QString &filePath, QStringView *dirPath, QStringView *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 bool isPattern(QStringView str);
static QString resolvePath(const QString &base, const QString &rel,
HostOsInfo::HostOs hostOs = HostOsInfo::hostOs());
- static bool globMatches(const QRegExp &pattern, const QString &subject);
static bool isFileCaseCorrect(const QString &filePath);
// Symlink-correct check.
@@ -101,10 +99,10 @@ private:
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);
+bool QBS_AUTOTEST_EXPORT removeDirectoryWithContents(const QString &path, QString *errorMessage);
+bool QBS_AUTOTEST_EXPORT copyFileRecursion(
+ const QString &sourcePath, const QString &targetPath, bool preserveSymLinks,
+ bool copyDirectoryContents, QString *errorMessage);
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/tools/filesaver.cpp b/src/lib/corelib/tools/filesaver.cpp
index 5a0a68c1f..3459ac558 100644
--- a/src/lib/corelib/tools/filesaver.cpp
+++ b/src/lib/corelib/tools/filesaver.cpp
@@ -107,14 +107,9 @@ bool FileSaver::commit()
return true;
}
-bool FileSaver::write(const std::vector<char> &data)
+bool FileSaver::write(std::string_view data)
{
- return fwrite(data, device());
-}
-
-bool FileSaver::write(const std::string &data)
-{
- return fwrite(data, device());
+ return Internal::fwrite(data.data(), data.size(), device());
}
} // namespace Internal
diff --git a/src/lib/corelib/tools/filesaver.h b/src/lib/corelib/tools/filesaver.h
index 8b4c01669..06f11eba0 100644
--- a/src/lib/corelib/tools/filesaver.h
+++ b/src/lib/corelib/tools/filesaver.h
@@ -60,8 +60,7 @@ public:
std::ostream *device();
bool open();
bool commit();
- bool write(const std::vector<char> &data);
- bool write(const std::string &data);
+ bool write(std::string_view data);
private:
std::string m_oldFileContents;
diff --git a/src/lib/corelib/tools/filetime.cpp b/src/lib/corelib/tools/filetime.cpp
index d115075a7..4ccb5731e 100644
--- a/src/lib/corelib/tools/filetime.cpp
+++ b/src/lib/corelib/tools/filetime.cpp
@@ -50,78 +50,29 @@
namespace qbs {
namespace Internal {
-#ifdef APPLE_CUSTOM_CLOCK_GETTIME
-#include <sys/time.h>
-
-#ifndef CLOCK_REALTIME
-#define CLOCK_REALTIME 0
-#endif
-
-// clk_id isn't used, only the CLOCK_REALTIME case is implemented.
-int clock_gettime(int /*clk_id*/, struct timespec *t)
-{
- struct timeval tv;
- // Resolution of gettimeofday is 1000nsecs = 1 microsecond.
- int ret = gettimeofday(&tv, NULL);
- t->tv_sec = tv.tv_sec;
- t->tv_nsec = tv.tv_usec * 1000;
- return ret;
-}
-#endif
+#if defined(Q_OS_WIN)
FileTime::FileTime()
{
-#ifdef Q_OS_WIN
static_assert(sizeof(FileTime::InternalType) == sizeof(FILETIME),
"FileTime::InternalType has wrong size.");
m_fileTime = 0;
-#elif HAS_CLOCK_GETTIME
- m_fileTime = {0, 0};
-#else
- m_fileTime = 0;
-#endif
}
FileTime::FileTime(const FileTime::InternalType &ft) : m_fileTime(ft)
{
-#if HAS_CLOCK_GETTIME
- if (m_fileTime.tv_sec == 0)
- m_fileTime.tv_nsec = 0; // stat() sets only the first member to 0 for non-existing files.
-#endif
}
int FileTime::compare(const FileTime &other) const
{
-#ifdef Q_OS_WIN
auto const t1 = reinterpret_cast<const FILETIME *>(&m_fileTime);
auto const t2 = reinterpret_cast<const FILETIME *>(&other.m_fileTime);
return CompareFileTime(t1, t2);
-#elif HAS_CLOCK_GETTIME
- if (m_fileTime.tv_sec < other.m_fileTime.tv_sec)
- return -1;
- if (m_fileTime.tv_sec > other.m_fileTime.tv_sec)
- return 1;
- if (m_fileTime.tv_nsec < other.m_fileTime.tv_nsec)
- return -1;
- if (m_fileTime.tv_nsec > other.m_fileTime.tv_nsec)
- return 1;
- return 0;
-#else
- if (m_fileTime < other.m_fileTime)
- return -1;
- if (m_fileTime > other.m_fileTime)
- return 1;
- return 0;
-#endif
}
void FileTime::clear()
{
-#if HAS_CLOCK_GETTIME
- m_fileTime = { 0, 0 };
-#else
m_fileTime = 0;
-#endif
}
bool FileTime::isValid() const
@@ -131,32 +82,16 @@ bool FileTime::isValid() const
FileTime FileTime::currentTime()
{
-#ifdef Q_OS_WIN
FileTime result;
SYSTEMTIME st;
GetSystemTime(&st);
auto const ft = reinterpret_cast<FILETIME *>(&result.m_fileTime);
SystemTimeToFileTime(&st, ft);
return result;
-#elif defined APPLE_CUSTOM_CLOCK_GETTIME
- InternalType t;
- // Explicitly use our custom version, so that we don't get an additional unresolved symbol on a
- // system that actually provides one, but isn't used due to the minimium deployment target
- // being lower.
- qbs::Internal::clock_gettime(CLOCK_REALTIME, &t);
- return t;
-#elif HAS_CLOCK_GETTIME
- InternalType t;
- clock_gettime(CLOCK_REALTIME, &t);
- return t;
-#else
- return time(nullptr);
-#endif
}
FileTime FileTime::oldestTime()
{
-#ifdef Q_OS_WIN
SYSTEMTIME st = {
1601,
1,
@@ -171,25 +106,15 @@ FileTime FileTime::oldestTime()
auto const ft = reinterpret_cast<FILETIME *>(&result.m_fileTime);
SystemTimeToFileTime(&st, ft);
return result;
-#elif HAS_CLOCK_GETTIME
- return FileTime({1, 0});
-#else
- return 1;
-#endif
}
double FileTime::asDouble() const
{
-#if HAS_CLOCK_GETTIME
- return static_cast<double>(m_fileTime.tv_sec);
-#else
return static_cast<double>(m_fileTime);
-#endif
}
QString FileTime::toString() const
{
-#ifdef Q_OS_WIN
auto const ft = reinterpret_cast<const FILETIME *>(&m_fileTime);
SYSTEMTIME stUTC, stLocal;
FileTimeToSystemTime(ft, &stUTC);
@@ -198,16 +123,93 @@ QString FileTime::toString() const
.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;
-#else
+}
+
+#else // defined(Q_OS_WIN)
+
+// based on https://github.com/qt/qtbase/blob/5.13/src/corelib/kernel/qelapsedtimer_unix.cpp
+// for details why it is implemented this way, see Qt source code
+# if !defined(CLOCK_REALTIME)
+# define CLOCK_REALTIME 0
+static inline void qbs_clock_gettime(int, struct timespec *ts)
+{
+ // support clock_gettime with gettimeofday
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec * 1000;
+}
+
+# ifdef _POSIX_MONOTONIC_CLOCK
+# undef _POSIX_MONOTONIC_CLOCK
+# define _POSIX_MONOTONIC_CLOCK -1
+# endif
+# else
+static inline void qbs_clock_gettime(clockid_t clock, struct timespec *ts)
+{
+ clock_gettime(clock, ts);
+}
+# endif
+
+FileTime::FileTime()
+{
+ m_fileTime = {0, 0};
+}
+
+FileTime::FileTime(const FileTime::InternalType &ft) : m_fileTime(ft)
+{
+ if (m_fileTime.tv_sec == 0)
+ m_fileTime.tv_nsec = 0; // stat() sets only the first member to 0 for non-existing files.
+}
+
+int FileTime::compare(const FileTime &other) const
+{
+ if (m_fileTime.tv_sec < other.m_fileTime.tv_sec)
+ return -1;
+ if (m_fileTime.tv_sec > other.m_fileTime.tv_sec)
+ return 1;
+ if (m_fileTime.tv_nsec < other.m_fileTime.tv_nsec)
+ return -1;
+ if (m_fileTime.tv_nsec > other.m_fileTime.tv_nsec)
+ return 1;
+ return 0;
+}
+
+void FileTime::clear()
+{
+ m_fileTime = { 0, 0 };
+}
+
+bool FileTime::isValid() const
+{
+ return *this != FileTime();
+}
+
+FileTime FileTime::currentTime()
+{
+ InternalType t;
+ qbs_clock_gettime(CLOCK_REALTIME, &t);
+ return t;
+}
+
+FileTime FileTime::oldestTime()
+{
+ return FileTime({1, 0});
+}
+
+double FileTime::asDouble() const
+{
+ return static_cast<double>(m_fileTime.tv_sec);
+}
+
+QString FileTime::toString() const
+{
QDateTime dt;
-#if HAS_CLOCK_GETTIME
- dt.setMSecsSinceEpoch(m_fileTime.tv_sec * 1000 + m_fileTime.tv_nsec / 1000000);
-#else
- dt.setTime_t(m_fileTime);
-#endif
+ dt.setMSecsSinceEpoch(m_fileTime.tv_sec * 1000 + m_fileTime.tv_nsec / 1000000);
return dt.toString(Qt::ISODateWithMs);
-#endif
}
+#endif // defined(Q_OS_WIN)
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/tools/filetime.h b/src/lib/corelib/tools/filetime.h
index f9a15f794..a0d4d2d7d 100644
--- a/src/lib/corelib/tools/filetime.h
+++ b/src/lib/corelib/tools/filetime.h
@@ -45,28 +45,10 @@
#include <QtCore/qdatastream.h>
#include <QtCore/qdebug.h>
-#if defined(Q_OS_UNIX) && !defined(__APPLE__)
+#if defined(Q_OS_UNIX)
#include <time.h>
-#define HAS_CLOCK_GETTIME (_POSIX_C_SOURCE >= 199309L)
#endif // Q_OS_UNIX
-#ifdef __APPLE__
-
-#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
-// macOS 10.12+ ships clock_gettime.
-#else
-// We implement our own clock_gettime.
-#define APPLE_CUSTOM_CLOCK_GETTIME 1
-#endif // __MAC_OS_X_VERSION_MIN_REQUIRED
-
-// Either way we have a clock_gettime in the end.
-#define HAS_CLOCK_GETTIME 1
-
-// Apple stat struct has slightly different names for time fields.
-#define APPLE_STAT_TIMESPEC 1
-
-#endif // __APPLE__
-
namespace qbs {
namespace Internal {
@@ -74,11 +56,7 @@ class QBS_AUTOTEST_EXPORT FileTime
{
public:
#if defined(Q_OS_UNIX)
-#if HAS_CLOCK_GETTIME
using InternalType = timespec;
-#else
- using InternalType = time_t;
-#endif // HAS_CLOCK_GETTIME
#elif defined(Q_OS_WIN)
using InternalType = quint64;
#else
@@ -107,7 +85,7 @@ public:
template<PersistentPool::OpType opType> void completeSerializationOp(PersistentPool &pool)
{
-#if HAS_CLOCK_GETTIME
+#if !defined(Q_OS_WIN)
pool.serializationOp<opType>(m_fileTime.tv_sec, m_fileTime.tv_nsec);
#else
pool.serializationOp<opType>(m_fileTime);
diff --git a/src/lib/corelib/tools/hostosinfo.h b/src/lib/corelib/tools/hostosinfo.h
index d7f718c19..32817f731 100644
--- a/src/lib/corelib/tools/hostosinfo.h
+++ b/src/lib/corelib/tools/hostosinfo.h
@@ -67,16 +67,16 @@
namespace qbs {
namespace Internal {
-class QBS_EXPORT HostOsInfo // Exported for use by command-line tools.
+class HostOsInfo
{
public:
// Add more as needed.
enum HostOs { HostOsWindows, HostOsLinux, HostOsMacos, HostOsOtherUnix, HostOsOther };
- static inline std::string hostOSIdentifier();
- static inline std::string hostOSArchitecture();
- static inline std::vector<std::string> hostOSIdentifiers();
- static inline std::vector<std::string> canonicalOSIdentifiers(const std::string &os);
+ static inline QString hostOSIdentifier();
+ static inline QString hostOSArchitecture();
+ static inline std::vector<QString> hostOSIdentifiers();
+ static inline std::vector<QString> canonicalOSIdentifiers(const QString &os);
static inline HostOs hostOs();
static inline Version hostOsVersion() {
@@ -98,6 +98,28 @@ public:
return v;
}
+ static QString hostOsBuildVersion() {
+ QString v;
+ if (HostOsInfo::isWindowsHost()) {
+ QSettings settings(QStringLiteral("HKEY_LOCAL_MACHINE\\Software\\"
+ "Microsoft\\Windows NT\\CurrentVersion"),
+ QSettings::NativeFormat);
+ v = settings.value(QStringLiteral("CurrentBuildNumber")).toString();
+ } else if (HostOsInfo::isMacosHost()) {
+ QSettings serverSettings(QStringLiteral(
+ "/System/Library/CoreServices/ServerVersion.plist"),
+ QSettings::NativeFormat);
+ v = serverSettings.value(QStringLiteral("ProductBuildVersion")).toString();
+ if (v.isNull()) {
+ QSettings systemSettings(QStringLiteral(
+ "/System/Library/CoreServices/SystemVersion.plist"),
+ QSettings::NativeFormat);
+ v = systemSettings.value(QStringLiteral("ProductBuildVersion")).toString();
+ }
+ }
+ return v;
+ }
+
static bool isWindowsHost() { return hostOs() == HostOsWindows; }
static bool isLinuxHost() { return hostOs() == HostOsLinux; }
static bool isMacosHost() { return hostOs() == HostOsMacos; }
@@ -112,6 +134,13 @@ public:
return finalName;
}
+ static QString stripExecutableSuffix(const QString &executable)
+ {
+ constexpr QLatin1String suffix(QBS_HOST_EXE_SUFFIX, sizeof(QBS_HOST_EXE_SUFFIX) - 1);
+ return !suffix.isEmpty() && executable.endsWith(suffix)
+ ? executable.chopped(suffix.size()) : executable;
+ }
+
static QString dynamicLibraryName(const QString &libraryBaseName)
{
return QLatin1String(QBS_HOST_DYNAMICLIB_PREFIX) + libraryBaseName
@@ -148,69 +177,68 @@ public:
}
};
-std::string HostOsInfo::hostOSIdentifier()
+QString HostOsInfo::hostOSIdentifier()
{
#if defined(__APPLE__)
- return "macos";
+ return QStringLiteral("macos");
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
- return "windows";
+ return QStringLiteral("windows");
#elif defined(_AIX)
- return "aix";
+ return QStringLiteral("aix");
#elif defined(hpux) || defined(__hpux)
- return "hpux";
+ return QStringLiteral("hpux");
#elif defined(__sun) || defined(sun)
- return "solaris";
+ return QStringLiteral("solaris");
#elif defined(__linux__) || defined(__linux)
- return "linux";
+ return QStringLiteral("linux");
#elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
- return "freebsd";
+ return QStringLiteral("freebsd");
#elif defined(__NetBSD__)
- return "netbsd";
+ return QStringLiteral("netbsd");
#elif defined(__OpenBSD__)
- return "openbsd";
+ return QStringLiteral("openbsd");
#elif defined(__GNU__)
- return "hurd";
+ return QStringLiteral("hurd");
#elif defined(__HAIKU__)
- return "haiku";
+ return QStringLiteral("haiku");
#else
#warning "Qbs has not been ported to this OS - see http://qbs.io/"
- return "";
+ return {};
#endif
}
-std::string HostOsInfo::hostOSArchitecture()
+QString HostOsInfo::hostOSArchitecture()
{
const auto cpuArch = QSysInfo::currentCpuArchitecture();
if (cpuArch == QLatin1String("i386"))
- return "x86";
- return cpuArch.toStdString();
+ return QStringLiteral("x86");
+ return cpuArch;
}
-std::vector<std::string> HostOsInfo::hostOSIdentifiers()
+std::vector<QString> HostOsInfo::hostOSIdentifiers()
{
return canonicalOSIdentifiers(hostOSIdentifier());
}
-std::vector<std::string> HostOsInfo::canonicalOSIdentifiers(const std::string &name)
+std::vector<QString> HostOsInfo::canonicalOSIdentifiers(const QString &name)
{
- std::vector<std::string> list { name };
- if (contains(std::vector<std::string> {"ios-simulator"}, name))
- list << canonicalOSIdentifiers("ios");
- if (contains(std::vector<std::string> {"tvos-simulator"}, name))
- list << canonicalOSIdentifiers("tvos");
- if (contains(std::vector<std::string> {"watchos-simulator"}, name))
- list << canonicalOSIdentifiers("watchos");
- if (contains(std::vector<std::string> {"macos", "ios", "tvos", "watchos"}, name))
- list << canonicalOSIdentifiers("darwin");
- if (contains(std::vector<std::string> {"darwin", "freebsd", "netbsd", "openbsd"}, name))
- list << canonicalOSIdentifiers("bsd");
- if (contains(std::vector<std::string> {"android"}, name))
- list << canonicalOSIdentifiers("linux");
+ std::vector<QString> list { name };
+ if (contains({u"ios-simulator"}, name))
+ list << canonicalOSIdentifiers(QStringLiteral("ios"));
+ if (contains({u"tvos-simulator"}, name))
+ list << canonicalOSIdentifiers(QStringLiteral("tvos"));
+ if (contains({u"watchos-simulator"}, name))
+ list << canonicalOSIdentifiers(QStringLiteral("watchos"));
+ if (contains({u"macos", u"ios", u"tvos", u"watchos"}, name))
+ list << canonicalOSIdentifiers(QStringLiteral("darwin"));
+ if (contains({u"darwin", u"freebsd", u"netbsd", u"openbsd"}, name))
+ list << canonicalOSIdentifiers(QStringLiteral("bsd"));
+ if (contains({u"android"}, name))
+ list << canonicalOSIdentifiers(QStringLiteral("linux"));
// Note: recognized non-Unix platforms include: windows, haiku, vxworks
- if (contains(std::vector<std::string> {
- "bsd", "aix", "hpux", "solaris", "linux", "hurd", "qnx", "integrity"}, name))
- list << canonicalOSIdentifiers("unix");
+ if (contains({u"bsd", u"aix", u"hpux", u"solaris", u"linux", u"hurd", u"qnx", u"integrity"}, name))
+ list << canonicalOSIdentifiers(QStringLiteral("unix"));
return list;
}
diff --git a/src/lib/corelib/tools/id.cpp b/src/lib/corelib/tools/id.cpp
index 6dd1147f2..cca755512 100644
--- a/src/lib/corelib/tools/id.cpp
+++ b/src/lib/corelib/tools/id.cpp
@@ -75,7 +75,7 @@ public:
: n(length), str(s)
{
if (!n)
- length = n = qstrlen(s);
+ length = n = int(qstrlen(s));
h = 0;
while (length--) {
h = (h << 4) + *s++;
@@ -95,7 +95,7 @@ static bool operator==(const StringHolder &sh1, const StringHolder &sh2)
}
-static uint qHash(const StringHolder &sh)
+QHashValueType qHash(const StringHolder &sh)
{
return sh.h;
}
@@ -269,11 +269,10 @@ Id Id::withPrefix(const char *prefix) const
bool Id::operator==(const char *name) const
{
- const char *string = getStringFromId(m_id);
- if (string && name)
- return strcmp(string, name) == 0;
- else
- return false;
+ const auto string = getStringFromId(m_id);
+ if (!string.isNull() && name)
+ return strcmp(string.data(), name) == 0;
+ return false;
}
bool Id::alphabeticallyBefore(Id other) const
diff --git a/src/lib/corelib/tools/id.h b/src/lib/corelib/tools/id.h
index aa327833f..845ed60df 100644
--- a/src/lib/corelib/tools/id.h
+++ b/src/lib/corelib/tools/id.h
@@ -41,6 +41,7 @@
#define QBS_TOOLS_ID_H
#include "qbs_export.h"
+#include <tools/porting.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qstring.h>
@@ -84,7 +85,7 @@ private:
int m_id;
};
-inline uint qHash(const Id &id) { return id.uniqueIdentifier(); }
+inline QHashValueType qHash(const Id &id) { return id.uniqueIdentifier(); }
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/tools/installoptions.cpp b/src/lib/corelib/tools/installoptions.cpp
index 5e112e6de..563f04a7b 100644
--- a/src/lib/corelib/tools/installoptions.cpp
+++ b/src/lib/corelib/tools/installoptions.cpp
@@ -68,7 +68,7 @@ public:
QString effectiveInstallRoot(const InstallOptions &options, const TopLevelProject *project)
{
- const QString installRoot = options.installRoot();
+ QString installRoot = options.installRoot();
if (!installRoot.isEmpty())
return installRoot;
diff --git a/src/lib/corelib/tools/iosutils.h b/src/lib/corelib/tools/iosutils.h
index 1a5faf3c3..69957a82e 100644
--- a/src/lib/corelib/tools/iosutils.h
+++ b/src/lib/corelib/tools/iosutils.h
@@ -45,7 +45,7 @@
#include <ostream>
#if defined(_WIN32) && defined(_MSC_VER)
-#include <codecvt>
+#include <windows.h>
#include <locale>
#define QBS_RENAME_IMPL ::_wrename
#define QBS_UNLINK_IMPL ::_wunlink
@@ -82,8 +82,10 @@ static inline bool fwrite(const char *s, std::ostream *stream)
static inline qbs_filesystem_path_string_type utf8_to_native_path(const std::string &str)
{
#if defined(_WIN32) && defined(_MSC_VER)
- std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
- return converter.from_bytes(str);
+ const int size = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
+ std::wstring result(size, 0);
+ MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), result.data(), size);
+ return result;
#else
return str;
#endif
diff --git a/src/lib/corelib/tools/jsliterals.cpp b/src/lib/corelib/tools/jsliterals.cpp
index 74328006c..647bdc640 100644
--- a/src/lib/corelib/tools/jsliterals.cpp
+++ b/src/lib/corelib/tools/jsliterals.cpp
@@ -40,8 +40,9 @@
#include "jsliterals.h"
#include <tools/stringconstants.h>
+#include <tools/qttools.h>
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
namespace qbs {
@@ -53,7 +54,7 @@ QString toJSLiteral(const bool b)
QString toJSLiteral(const QString &str)
{
QString js = str;
- js.replace(QRegExp(QLatin1String("([\\\\\"])")), QLatin1String("\\\\1"));
+ js.replace(QRegularExpression(QLatin1String("([\\\\\"])")), QLatin1String("\\\\1"));
js.prepend(QLatin1Char('"'));
js.append(QLatin1Char('"'));
return js;
@@ -75,7 +76,7 @@ QString toJSLiteral(const QVariant &val)
{
if (!val.isValid())
return Internal::StringConstants::undefinedValue();
- if (val.type() == QVariant::List || val.type() == QVariant::StringList) {
+ if (val.userType() == QMetaType::QVariantList || val.userType() == QMetaType::QStringList) {
QString res;
const auto list = val.toList();
for (const QVariant &child : list) {
@@ -86,7 +87,7 @@ QString toJSLiteral(const QVariant &val)
res.append(QLatin1Char(']'));
return res;
}
- if (val.type() == QVariant::Map) {
+ if (val.userType() == QMetaType::QVariantMap) {
const QVariantMap &vm = val.toMap();
QString str = QStringLiteral("{");
for (QVariantMap::const_iterator it = vm.begin(); it != vm.end(); ++it) {
@@ -97,9 +98,9 @@ QString toJSLiteral(const QVariant &val)
str += QLatin1Char('}');
return str;
}
- if (val.type() == QVariant::Bool)
+ if (val.userType() == QMetaType::Bool)
return toJSLiteral(val.toBool());
- if (val.canConvert(QVariant::String))
+ if (qVariantCanConvert(val, QMetaType::QString))
return toJSLiteral(val.toString());
return QStringLiteral("Unconvertible type %1").arg(QLatin1String(val.typeName()));
}
diff --git a/src/lib/corelib/tools/jsonhelper.h b/src/lib/corelib/tools/jsonhelper.h
index d87802c0a..cb911c45a 100644
--- a/src/lib/corelib/tools/jsonhelper.h
+++ b/src/lib/corelib/tools/jsonhelper.h
@@ -40,6 +40,8 @@
#ifndef QBS_JSON_HELPER_H
#define QBS_JSON_HELPER_H
+#include <tools/stlutils.h>
+
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonvalue.h>
@@ -60,10 +62,7 @@ template<> inline QString fromJson(const QJsonValue &v) { return v.toString(); }
template<> inline QStringList fromJson(const QJsonValue &v)
{
const QJsonArray &jsonList = v.toArray();
- QStringList stringList;
- std::transform(jsonList.begin(), jsonList.end(), std::back_inserter(stringList),
- [](const QVariant &v) { return v.toString(); });
- return stringList;
+ return transformed<QStringList>(jsonList, [](const auto &v) { return v.toString(); });
}
template<> inline QVariantMap fromJson(const QJsonValue &v) { return v.toObject().toVariantMap(); }
template<> inline QProcessEnvironment fromJson(const QJsonValue &v)
@@ -78,9 +77,9 @@ template<> inline QProcessEnvironment fromJson(const QJsonValue &v)
template<typename T> inline void setValueFromJson(T &targetValue, const QJsonObject &data,
const char *jsonProperty)
{
- const QJsonValue v = data.value(QLatin1String(jsonProperty));
- if (!v.isNull())
- targetValue = fromJson<T>(v);
+ const auto it = data.find(QLatin1String(jsonProperty));
+ if (it != data.end())
+ targetValue = fromJson<T>(*it);
}
} // namespace Internal
diff --git a/src/lib/corelib/tools/launcherinterface.cpp b/src/lib/corelib/tools/launcherinterface.cpp
index f4e80c5e2..cd877c154 100644
--- a/src/lib/corelib/tools/launcherinterface.cpp
+++ b/src/lib/corelib/tools/launcherinterface.cpp
@@ -61,11 +61,23 @@ namespace Internal {
class LauncherProcess : public QProcess
{
public:
- LauncherProcess(QObject *parent) : QProcess(parent) { }
+ LauncherProcess(QObject *parent) : QProcess(parent)
+ {
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) && defined(Q_OS_UNIX)
+ setChildProcessModifier([this] { setupChildProcess_impl(); });
+#endif
+ }
private:
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
void setupChildProcess() override
{
+ setupChildProcess_impl();
+ }
+#endif
+
+ void setupChildProcess_impl()
+ {
#ifdef Q_OS_UNIX
const auto pid = static_cast<pid_t>(processId());
setpgid(pid, pid);
@@ -108,9 +120,7 @@ void LauncherInterface::doStart()
return;
}
m_process = new LauncherProcess(this);
- connect(m_process,
- static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
- this, &LauncherInterface::handleProcessError);
+ connect(m_process, &QProcess::errorOccurred, this, &LauncherInterface::handleProcessError);
connect(m_process,
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this, &LauncherInterface::handleProcessFinished);
@@ -130,8 +140,7 @@ void LauncherInterface::doStop()
if (!m_process)
return;
m_process->disconnect();
- if (m_socket->isReady())
- m_socket->shutdown();
+ m_socket->shutdown();
m_process->waitForFinished(3000);
m_process->deleteLater();
m_process = nullptr;
diff --git a/src/lib/corelib/tools/launcherpackets.h b/src/lib/corelib/tools/launcherpackets.h
index 4306718fd..b3eac4320 100644
--- a/src/lib/corelib/tools/launcherpackets.h
+++ b/src/lib/corelib/tools/launcherpackets.h
@@ -145,7 +145,7 @@ class ProcessErrorPacket : public LauncherPacket
public:
ProcessErrorPacket(quintptr token);
- QProcess::ProcessError error;
+ QProcess::ProcessError error = QProcess::UnknownError;
QString errorString;
private:
diff --git a/src/lib/corelib/tools/launchersocket.cpp b/src/lib/corelib/tools/launchersocket.cpp
index 4373b10b8..7d4788ee3 100644
--- a/src/lib/corelib/tools/launchersocket.cpp
+++ b/src/lib/corelib/tools/launchersocket.cpp
@@ -68,21 +68,21 @@ void LauncherSocket::sendData(const QByteArray &data)
void LauncherSocket::shutdown()
{
- QBS_ASSERT(m_socket, return);
- m_socket->disconnect();
- m_socket->write(ShutdownPacket().serialize());
- m_socket->waitForBytesWritten(1000);
- m_socket->deleteLater();
- m_socket = nullptr;
+ const auto socket = m_socket.exchange(nullptr);
+ if (!socket)
+ return;
+ socket->disconnect();
+ socket->write(ShutdownPacket().serialize());
+ socket->waitForBytesWritten(1000);
+ socket->deleteLater();
}
void LauncherSocket::setSocket(QLocalSocket *socket)
{
QBS_ASSERT(!m_socket, return);
- m_socket = socket;
+ m_socket.store(socket);
m_packetParser.setDevice(m_socket);
- connect(m_socket,
- static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
+ connect(m_socket, &QLocalSocket::errorOccurred,
this, &LauncherSocket::handleSocketError);
connect(m_socket, &QLocalSocket::readyRead,
this, &LauncherSocket::handleSocketDataAvailable);
@@ -93,8 +93,9 @@ void LauncherSocket::setSocket(QLocalSocket *socket)
void LauncherSocket::handleSocketError()
{
- if (m_socket->error() != QLocalSocket::PeerClosedError)
- handleError(Tr::tr("Socket error: %1").arg(m_socket->errorString()));
+ auto socket = m_socket.load();
+ if (socket->error() != QLocalSocket::PeerClosedError)
+ handleError(Tr::tr("Socket error: %1").arg(socket->errorString()));
}
void LauncherSocket::handleSocketDataAvailable()
@@ -127,18 +128,19 @@ void LauncherSocket::handleSocketDisconnected()
void LauncherSocket::handleError(const QString &error)
{
- m_socket->disconnect();
- m_socket->deleteLater();
- m_socket = nullptr;
+ const auto socket = m_socket.exchange(nullptr);
+ socket->disconnect();
+ socket->deleteLater();
emit errorOccurred(error);
}
void LauncherSocket::handleRequests()
{
- QBS_ASSERT(isReady(), return);
+ const auto socket = m_socket.load();
+ QBS_ASSERT(socket, return);
std::lock_guard<std::mutex> locker(m_requestsMutex);
- for (const QByteArray &request : qAsConst(m_requests))
- m_socket->write(request);
+ for (const QByteArray &request : std::as_const(m_requests))
+ socket->write(request);
m_requests.clear();
}
diff --git a/src/lib/corelib/tools/launchersocket.h b/src/lib/corelib/tools/launchersocket.h
index a9a1af800..2eb2c3f63 100644
--- a/src/lib/corelib/tools/launchersocket.h
+++ b/src/lib/corelib/tools/launchersocket.h
@@ -60,7 +60,7 @@ class LauncherSocket : public QObject
Q_OBJECT
friend class LauncherInterface;
public:
- bool isReady() const { return m_socket; }
+ bool isReady() const { return m_socket.load(); }
void sendData(const QByteArray &data);
signals:
@@ -81,7 +81,7 @@ private:
void handleError(const QString &error);
void handleRequests();
- QLocalSocket *m_socket = nullptr;
+ std::atomic<QLocalSocket *> m_socket{nullptr};
PacketParser m_packetParser;
std::vector<QByteArray> m_requests;
std::mutex m_requestsMutex;
diff --git a/src/lib/corelib/tools/msvcinfo.cpp b/src/lib/corelib/tools/msvcinfo.cpp
index cffec85b2..77b83023a 100644
--- a/src/lib/corelib/tools/msvcinfo.cpp
+++ b/src/lib/corelib/tools/msvcinfo.cpp
@@ -38,14 +38,21 @@
****************************************************************************/
#include "msvcinfo.h"
+#include "visualstudioversioninfo.h"
+#include <logging/logger.h>
#include <tools/error.h>
#include <tools/profile.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qdir.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
#include <QtCore/qprocess.h>
+#include <QtCore/qsettings.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qtemporaryfile.h>
@@ -54,6 +61,7 @@
#endif
#include <algorithm>
+#include <memory>
#include <mutex>
using namespace qbs;
@@ -129,16 +137,18 @@ public:
#ifdef Q_OS_WIN
static QStringList parseCommandLine(const QString &commandLine)
{
- QStringList list;
- const auto buf = new wchar_t[commandLine.size() + 1];
- buf[commandLine.toWCharArray(buf)] = 0;
+ const auto buf = std::make_unique<wchar_t[]>(size_t(commandLine.size()) + 1);
+ buf[size_t(commandLine.toWCharArray(buf.get()))] = 0;
int argCount = 0;
- LPWSTR *args = CommandLineToArgvW(buf, &argCount);
+ const auto argsDeleter = [](LPWSTR *p){ LocalFree(p); };
+ const auto args = std::unique_ptr<LPWSTR[], decltype(argsDeleter)>(
+ CommandLineToArgvW(buf.get(), &argCount), argsDeleter);
if (!args)
throw ErrorInfo(mkStr("Could not parse command line arguments: ") + commandLine);
+ QStringList list;
+ list.reserve(argCount);
for (int i = 0; i < argCount; ++i)
- list.push_back(QString::fromWCharArray(args[i]));
- delete[] buf;
+ list.push_back(QString::fromWCharArray(args[size_t(i)]));
return list;
}
#endif
@@ -163,9 +173,10 @@ static QVariantMap getMsvcDefines(const QString &compilerFilePath,
QStringList out = QString::fromLocal8Bit(runProcess(compilerFilePath, QStringList()
<< QStringLiteral("/nologo")
<< backendSwitch
- << QString::fromWCharArray(_wgetenv(L"COMSPEC"))
+ << qEnvironmentVariable("COMSPEC")
<< QStringLiteral("/c")
<< languageSwitch
+ << QStringLiteral("/Zs")
<< QStringLiteral("NUL"),
compilerEnv, true, commands)).split(QLatin1Char('\n'));
@@ -206,30 +217,32 @@ static QVariantMap getMsvcDefines(const QString &compilerFilePath,
static QVariantMap getClangClDefines(
const QString &compilerFilePath,
const QProcessEnvironment &compilerEnv,
- MSVC::CompilerLanguage language)
+ MSVC::CompilerLanguage language,
+ const QString &arch)
{
#ifdef Q_OS_WIN
QFileInfo clInfo(compilerFilePath);
- QFileInfo clangInfo(clInfo.absolutePath() + QLatin1String("/clang.exe"));
+ QFileInfo clangInfo(clInfo.absolutePath() + QLatin1String("/clang-cl.exe"));
if (!clangInfo.exists())
throw ErrorInfo(QStringLiteral("%1 does not exist").arg(clangInfo.absoluteFilePath()));
- QString languageSwitch;
- switch (language) {
- case MSVC::CLanguage:
- languageSwitch = QStringLiteral("c");
- break;
- case MSVC::CPlusPlusLanguage:
- languageSwitch = QStringLiteral("c++");
- break;
- }
QStringList args = {
- QStringLiteral("-dM"),
- QStringLiteral("-E"),
- QStringLiteral("-x"),
- languageSwitch,
- QStringLiteral("NUL"),
+ QStringLiteral("/d1PP"), // dump macros
+ QStringLiteral("/E") // preprocess to stdout
};
+
+ if (language == MSVC::CLanguage)
+ args.append(QStringLiteral("/TC"));
+ else if (language == MSVC::CPlusPlusLanguage)
+ args.append(QStringLiteral("/TP"));
+
+ if (arch == QLatin1String("x86"))
+ args.append(QStringLiteral("-m32"));
+ else if (arch == QLatin1String("x86_64"))
+ args.append(QStringLiteral("-m64"));
+
+ args.append(QStringLiteral("NUL")); // filename
+
const auto lines = QString::fromLocal8Bit(
runProcess(
clangInfo.absoluteFilePath(),
@@ -239,10 +252,8 @@ static QVariantMap getClangClDefines(
QVariantMap result;
for (const auto &line: lines) {
static const auto defineString = QLatin1String("#define ");
- if (!line.startsWith(defineString)) {
- throw ErrorInfo(QStringLiteral("Unexpected compiler frontend output: ")
- + lines.join(QLatin1Char('\n')));
- }
+ if (!line.startsWith(defineString))
+ continue;
QStringView view(line.data() + defineString.size());
const auto it = std::find(view.begin(), view.end(), QLatin1Char(' '));
if (it == view.end()) {
@@ -253,15 +264,240 @@ static QVariantMap getClangClDefines(
QStringView value(it + 1, view.end());
result.insert(key.toString(), value.isEmpty() ? QVariant() : QVariant(value.toString()));
}
+ if (result.isEmpty()) {
+ throw ErrorInfo(QStringLiteral("Cannot determine macroses from compiler frontend output: ")
+ + lines.join(QLatin1Char('\n')));
+ }
return result;
#else
Q_UNUSED(compilerFilePath);
Q_UNUSED(compilerEnv);
Q_UNUSED(language);
+ Q_UNUSED(arch);
+ return {};
+#endif
+}
+
+static QString formatVswhereOutput(const QString &out, const QString &err)
+{
+ QString ret;
+ if (!out.isEmpty()) {
+ ret.append(Tr::tr("stdout")).append(QLatin1String(":\n"));
+ const auto lines = out.split(QLatin1Char('\n'));
+ for (const QString &line : lines)
+ ret.append(QLatin1Char('\t')).append(line).append(QLatin1Char('\n'));
+ }
+ if (!err.isEmpty()) {
+ ret.append(Tr::tr("stderr")).append(QLatin1String(":\n"));
+ const auto lines = err.split(QLatin1Char('\n'));
+ for (const QString &line : lines)
+ ret.append(QLatin1Char('\t')).append(line).append(QLatin1Char('\n'));
+ }
+ return ret;
+}
+
+static QString wow6432Key()
+{
+#ifdef Q_OS_WIN64
+ return QStringLiteral("\\Wow6432Node");
+#else
return {};
#endif
}
+static QString vswhereFilePath()
+{
+ static const std::vector<const char *> envVarCandidates{"ProgramFiles", "ProgramFiles(x86)"};
+ for (const char * const envVar : envVarCandidates) {
+ const QString value = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv(envVar)));
+ QString cmd = value
+ + QStringLiteral("/Microsoft Visual Studio/Installer/vswhere.exe");
+ if (QFileInfo(cmd).exists())
+ return cmd;
+ }
+ return {};
+}
+
+enum class ProductType { VisualStudio, BuildTools };
+static std::vector<MSVCInstallInfo> retrieveInstancesFromVSWhere(
+ ProductType productType, Logger &logger)
+{
+ std::vector<MSVCInstallInfo> result;
+ const QString cmd = vswhereFilePath();
+ if (cmd.isEmpty())
+ return result;
+ QProcess vsWhere;
+ QStringList args = productType == ProductType::VisualStudio
+ ? QStringList({QStringLiteral("-all"), QStringLiteral("-legacy"),
+ QStringLiteral("-prerelease")})
+ : QStringList({QStringLiteral("-products"),
+ QStringLiteral("Microsoft.VisualStudio.Product.BuildTools")});
+ args << QStringLiteral("-format") << QStringLiteral("json") << QStringLiteral("-utf8");
+ vsWhere.start(cmd, args);
+ if (!vsWhere.waitForStarted(-1))
+ return result;
+ if (!vsWhere.waitForFinished(-1)) {
+ logger.qbsWarning() << Tr::tr("The vswhere tool failed to run").append(QLatin1String(": "))
+ .append(vsWhere.errorString());
+ return result;
+ }
+ if (vsWhere.exitCode() != 0) {
+ const QString stdOut = QString::fromLocal8Bit(vsWhere.readAllStandardOutput());
+ const QString stdErr = QString::fromLocal8Bit(vsWhere.readAllStandardError());
+ logger.qbsWarning() << Tr::tr("The vswhere tool failed to run").append(QLatin1String(".\n"))
+ .append(formatVswhereOutput(stdOut, stdErr));
+ return result;
+ }
+ QJsonParseError parseError{};
+ QJsonDocument jsonOutput = QJsonDocument::fromJson(vsWhere.readAllStandardOutput(),
+ &parseError);
+ if (parseError.error != QJsonParseError::NoError) {
+ logger.qbsWarning() << Tr::tr("The vswhere tool produced invalid JSON output: %1")
+ .arg(parseError.errorString());
+ return result;
+ }
+ const auto jsonArray = jsonOutput.array();
+ for (const auto &value : jsonArray) {
+ const QJsonObject o = value.toObject();
+ MSVCInstallInfo info;
+ info.version = o.value(QStringLiteral("installationVersion")).toString();
+ if (productType == ProductType::BuildTools) {
+ // For build tools, the version is e.g. "15.8.28010.2036", rather than "15.0".
+ const int dotIndex = info.version.indexOf(QLatin1Char('.'));
+ if (dotIndex != -1)
+ info.version = info.version.left(dotIndex);
+ }
+ info.installDir = o.value(QStringLiteral("installationPath")).toString();
+ if (!info.version.isEmpty() && !info.installDir.isEmpty())
+ result.push_back(info);
+ }
+ return result;
+}
+
+static std::vector<MSVCInstallInfo> installedMSVCsFromVsWhere(Logger &logger)
+{
+ const std::vector<MSVCInstallInfo> vsInstallations
+ = retrieveInstancesFromVSWhere(ProductType::VisualStudio, logger);
+ const std::vector<MSVCInstallInfo> buildToolInstallations
+ = retrieveInstancesFromVSWhere(ProductType::BuildTools, logger);
+ std::vector<MSVCInstallInfo> all;
+ std::copy(vsInstallations.begin(), vsInstallations.end(), std::back_inserter(all));
+ std::copy(buildToolInstallations.begin(), buildToolInstallations.end(),
+ std::back_inserter(all));
+ return all;
+}
+
+static std::vector<MSVCInstallInfo> installedMSVCsFromRegistry()
+{
+ std::vector<MSVCInstallInfo> result;
+
+ // Detect Visual Studio
+ const QSettings vsRegistry(
+ QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key()
+ + QStringLiteral("\\Microsoft\\VisualStudio\\SxS\\VS7"),
+ QSettings::NativeFormat);
+ const auto vsNames = vsRegistry.childKeys();
+ for (const QString &vsName : vsNames) {
+ MSVCInstallInfo entry;
+ entry.version = vsName;
+ entry.installDir = vsRegistry.value(vsName).toString();
+ result.push_back(entry);
+ }
+
+ // Detect Visual C++ Build Tools
+ QSettings vcbtRegistry(
+ QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key()
+ + QStringLiteral("\\Microsoft\\VisualCppBuildTools"),
+ QSettings::NativeFormat);
+ const QStringList &vcbtRegistryChildGroups = vcbtRegistry.childGroups();
+ for (const QString &childGroup : vcbtRegistryChildGroups) {
+ vcbtRegistry.beginGroup(childGroup);
+ bool ok;
+ int installed = vcbtRegistry.value(QStringLiteral("Installed")).toInt(&ok);
+ if (ok && installed) {
+ MSVCInstallInfo entry;
+ entry.version = childGroup;
+ const QSettings vsRegistry(
+ QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE") + wow6432Key()
+ + QStringLiteral("\\Microsoft\\VisualStudio\\") + childGroup
+ + QStringLiteral("\\Setup\\VC"),
+ QSettings::NativeFormat);
+ entry.installDir = vsRegistry.value(QStringLiteral("ProductDir")).toString();
+ result.push_back(entry);
+ }
+ vcbtRegistry.endGroup();
+ }
+
+ return result;
+}
+
+/*
+ Returns the list of compilers present in all MSVC installations
+ (Visual Studios or Build Tools) without the architecture, e.g.
+ [VC\Tools\MSVC\14.16.27023, VC\Tools\MSVC\14.14.26428, ...]
+*/
+static std::vector<MSVC> installedCompilersHelper(Logger &logger)
+{
+ std::vector<MSVC> msvcs;
+ std::vector<MSVCInstallInfo> installInfos = installedMSVCsFromVsWhere(logger);
+ if (installInfos.empty())
+ installInfos = installedMSVCsFromRegistry();
+ for (const MSVCInstallInfo &installInfo : installInfos) {
+ MSVC msvc;
+ msvc.internalVsVersion = Version::fromString(installInfo.version, true);
+ if (!msvc.internalVsVersion.isValid())
+ continue;
+
+ QDir vsInstallDir(installInfo.installDir);
+ msvc.vsInstallPath = vsInstallDir.absolutePath();
+ if (vsInstallDir.dirName() != QStringLiteral("VC")
+ && !vsInstallDir.cd(QStringLiteral("VC"))) {
+ continue;
+ }
+
+ msvc.version = QString::number(Internal::VisualStudioVersionInfo(
+ msvc.internalVsVersion).marketingVersion());
+ if (msvc.version.isEmpty()) {
+ logger.qbsWarning()
+ << Tr::tr("Unknown MSVC version %1 found.").arg(installInfo.version);
+ continue;
+ }
+
+ if (msvc.internalVsVersion.majorVersion() < 15) {
+ QDir vcInstallDir = vsInstallDir;
+ if (!vcInstallDir.cd(QStringLiteral("bin")))
+ continue;
+ msvc.vcInstallPath = vcInstallDir.absolutePath();
+ msvcs.push_back(msvc);
+ } else {
+ QDir vcInstallDir = vsInstallDir;
+ vcInstallDir.cd(QStringLiteral("Tools/MSVC"));
+ const auto vcVersionStrs = vcInstallDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ std::vector<Version> vcVersions;
+ vcVersions.reserve(vcVersionStrs.size());
+ for (const QString &vcVersionStr : vcVersionStrs) {
+ const Version vcVersion = Version::fromString(vcVersionStr);
+ if (!vcVersion.isValid())
+ continue;
+ vcVersions.push_back(vcVersion);
+ }
+ // sort the versions so the new one comes first
+ std::sort(vcVersions.begin(), vcVersions.end(), std::greater<>());
+
+ for (const Version &vcVersion : vcVersions) {
+ QDir specificVcInstallDir = vcInstallDir;
+ if (!specificVcInstallDir.cd(vcVersion.toString())
+ || !specificVcInstallDir.cd(QStringLiteral("bin"))) {
+ continue;
+ }
+ msvc.vcInstallPath = specificVcInstallDir.absolutePath();
+ msvcs.push_back(msvc);
+ }
+ }
+ }
+ return msvcs;
+}
+
void MSVC::init()
{
determineCompilerVersion();
@@ -274,12 +510,55 @@ void MSVC::init()
QString MSVC::architectureFromClPath(const QString &clPath)
{
const auto parentDir = QFileInfo(clPath).absolutePath();
- const auto parentDirName = QFileInfo(parentDir).fileName().toLower();
+ auto parentDirName = QFileInfo(parentDir).fileName().toLower();
+ // can be the case when cl.exe is present within the Windows SDK installation... but can it?
if (parentDirName == QLatin1String("bin"))
return QStringLiteral("x86");
return parentDirName;
}
+QString MSVC::vcVariablesVersionFromBinPath(const QString &binPath)
+{
+ const auto binDirName = QFileInfo(binPath).fileName().toLower();
+ // the case when cl.exe is present within the Windows SDK installation
+ if (binDirName == QLatin1String("bin"))
+ return {};
+ // binPath is something like
+ // Microsoft Visual Studio 14.0/VC/bin/amd64_x86
+ // or
+ // Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.28.29910/bin/Hostx64/x64
+ QDir dir(binPath);
+ dir.cdUp();
+ // older Visual Studios do not support multiple compiler versions
+ if (dir.dirName().toLower() == QLatin1String("bin"))
+ return {};
+ dir.cdUp();
+ dir.cdUp();
+ return dir.dirName();
+}
+
+QString MSVC::canonicalArchitecture(const QString &arch)
+{
+ if (arch == QLatin1String("x64") || arch == QLatin1String("amd64"))
+ return QStringLiteral("x86_64");
+ return arch;
+}
+
+std::pair<QString, QString> MSVC::getHostTargetArchPair(const QString &arch)
+{
+ QString hostArch;
+ QString targetArch;
+ const int index = arch.indexOf(QLatin1Char('_'));
+ if (index != -1) {
+ hostArch = arch.mid(0, index);
+ targetArch = arch.mid(index);
+ } else {
+ hostArch = arch;
+ targetArch = arch;
+ }
+ return {canonicalArchitecture(hostArch), canonicalArchitecture(targetArch)};
+}
+
QString MSVC::binPathForArchitecture(const QString &arch) const
{
QString archSubDir;
@@ -301,10 +580,106 @@ QVariantMap MSVC::compilerDefines(const QString &compilerFilePath,
{
const auto compilerName = QFileInfo(compilerFilePath).fileName().toLower();
if (compilerName == QLatin1String("clang-cl.exe"))
- return getClangClDefines(compilerFilePath, environment, language);
+ return getClangClDefines(compilerFilePath, environment, language, architecture);
return getMsvcDefines(compilerFilePath, environment, language);
}
+std::vector<MSVCArchInfo> MSVC::findSupportedArchitectures(const MSVC &msvc)
+{
+ std::vector<MSVCArchInfo> result;
+ auto addResult = [&result](const MSVCArchInfo &ai) {
+ if (QFile::exists(ai.binPath + QLatin1String("/cl.exe")))
+ result.push_back(ai);
+ };
+ 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);
+ addResult(ai);
+ }
+ } else {
+ QDir vcInstallDir(msvc.vcInstallPath);
+ const auto hostArchs = vcInstallDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QString &hostArch : hostArchs) {
+ QDir subdir = vcInstallDir;
+ if (!subdir.cd(hostArch))
+ continue;
+ const QString shortHostArch = hostArch.mid(4).toLower();
+ const auto archs = subdir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
+ for (const QString &arch : archs) {
+ MSVCArchInfo ai;
+ ai.binPath = subdir.absoluteFilePath(arch);
+ if (shortHostArch == arch)
+ ai.arch = arch;
+ else
+ ai.arch = shortHostArch + QLatin1Char('_') + arch;
+ addResult(ai);
+ }
+ }
+ }
+ return result;
+}
+
+QVariantMap MSVC::toVariantMap() const
+{
+ return {
+ {QStringLiteral("version"), version},
+ {QStringLiteral("internalVsVersion"), internalVsVersion.toString()},
+ {QStringLiteral("vsInstallPath"), vsInstallPath},
+ {QStringLiteral("vcInstallPath"), vcInstallPath},
+ {QStringLiteral("binPath"), binPath},
+ {QStringLiteral("architecture"), architecture},
+ };
+}
+
+/*!
+ \internal
+ Returns the list of all compilers present in all MSVC installations
+ separated by host/target arch, e.g.
+ [
+ VC\Tools\MSVC\14.16.27023\bin\Hostx64\x64,
+ VC\Tools\MSVC\14.16.27023\bin\Hostx64\x86,
+ VC\Tools\MSVC\14.16.27023\bin\Hostx64\arm,
+ VC\Tools\MSVC\14.16.27023\bin\Hostx64\arm64,
+ VC\Tools\MSVC\14.16.27023\bin\Hostx86\x64,
+ ...
+ ]
+ \note that MSVC.architecture can be either "x64" or "amd64" (depending on the MSVC version)
+ in case of 64-bit platform (but we use the "x86_64" name...)
+*/
+std::vector<MSVC> MSVC::installedCompilers(Logger &logger)
+{
+ std::vector<MSVC> msvcs;
+ const auto instMsvcs = installedCompilersHelper(logger);
+ for (const MSVC &msvc : instMsvcs) {
+ if (msvc.internalVsVersion.majorVersion() < 15) {
+ // Check existence of various install scripts
+ const QString vcvars32bat = msvc.vcInstallPath + QLatin1String("/vcvars32.bat");
+ if (!QFileInfo(vcvars32bat).isFile())
+ continue;
+ }
+
+ const auto ais = findSupportedArchitectures(msvc);
+ transform(ais, msvcs, [&msvc](const auto &ai) {
+ MSVC specificMSVC = msvc;
+ specificMSVC.architecture = ai.arch;
+ specificMSVC.binPath = ai.binPath;
+ return specificMSVC;
+ });
+ }
+ return msvcs;
+}
+
void MSVC::determineCompilerVersion()
{
QString cppFilePath;
@@ -332,3 +707,24 @@ void MSVC::determineCompilerVersion()
compilerVersion = Version(versionStr.mid(0, 2).toInt(), versionStr.mid(2, 2).toInt(),
versionStr.mid(4).toInt());
}
+
+QString MSVCInstallInfo::findVcvarsallBat() const
+{
+ static const auto vcvarsall2017 = QStringLiteral("VC/Auxiliary/Build/vcvarsall.bat");
+ // 2015, 2013 and 2012
+ static const auto vcvarsallOld = QStringLiteral("VC/vcvarsall.bat");
+ QDir dir(installDir);
+ if (dir.exists(vcvarsall2017))
+ return dir.absoluteFilePath(vcvarsall2017);
+ if (dir.exists(vcvarsallOld))
+ return dir.absoluteFilePath(vcvarsallOld);
+ return {};
+}
+
+std::vector<MSVCInstallInfo> MSVCInstallInfo::installedMSVCs(Logger &logger)
+{
+ auto installInfos = installedMSVCsFromVsWhere(logger);
+ if (installInfos.empty())
+ return installedMSVCsFromRegistry();
+ return installInfos;
+}
diff --git a/src/lib/corelib/tools/msvcinfo.h b/src/lib/corelib/tools/msvcinfo.h
index 171afeb29..efaf0b6c2 100644
--- a/src/lib/corelib/tools/msvcinfo.h
+++ b/src/lib/corelib/tools/msvcinfo.h
@@ -53,6 +53,14 @@
namespace qbs {
namespace Internal {
+class Logger;
+
+struct MSVCArchInfo
+{
+ QString arch;
+ QString binPath;
+};
+
/**
* Represents one MSVC installation for one specific target architecture.
* There are potentially multiple MSVCs in one Visual Studio installation.
@@ -73,12 +81,14 @@ public:
QString binPath;
QString pathPrefix;
QString architecture;
+ QString sdkVersion;
QProcessEnvironment environment;
MSVC() = default;
- MSVC(const QString &clPath, QString arch):
- architecture(std::move(arch))
+ MSVC(const QString &clPath, QString arch, QString sdkVersion = {}):
+ architecture(std::move(arch)),
+ sdkVersion(std::move(sdkVersion))
{
QDir parentDir = QFileInfo(clPath).dir();
binPath = parentDir.absolutePath();
@@ -90,11 +100,20 @@ public:
QBS_EXPORT void init();
QBS_EXPORT static QString architectureFromClPath(const QString &clPath);
+ QBS_EXPORT static QString vcVariablesVersionFromBinPath(const QString &binPath);
+ QBS_EXPORT static QString canonicalArchitecture(const QString &arch);
+ QBS_EXPORT static std::pair<QString, QString> getHostTargetArchPair(const QString &arch);
QBS_EXPORT QString binPathForArchitecture(const QString &arch) const;
QBS_EXPORT QString clPathForArchitecture(const QString &arch) const;
QBS_EXPORT QVariantMap compilerDefines(const QString &compilerFilePath,
CompilerLanguage language) const;
+ QBS_EXPORT static std::vector<MSVCArchInfo> findSupportedArchitectures(const MSVC &msvc);
+
+ QBS_EXPORT QVariantMap toVariantMap() const;
+
+ QBS_EXPORT static std::vector<MSVC> installedCompilers(Logger &logger);
+
private:
void determineCompilerVersion();
};
@@ -110,6 +129,16 @@ public:
}
};
+struct QBS_EXPORT MSVCInstallInfo
+{
+ QString version;
+ QString installDir;
+
+ QString findVcvarsallBat() const;
+
+ static std::vector<MSVCInstallInfo> installedMSVCs(Logger &logger);
+};
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/tools/mutexdata.h b/src/lib/corelib/tools/mutexdata.h
new file mode 100644
index 000000000..160f6dd26
--- /dev/null
+++ b/src/lib/corelib/tools/mutexdata.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <shared_mutex>
+
+namespace qbs {
+namespace Internal {
+
+// adapted version of https://github.com/dragazo/rustex/blob/master/rustex.h
+
+// a data with a mutually exclusive access
+template<typename DataType, typename MutexType = std::shared_mutex>
+class MutexData
+{
+public:
+ template<typename T, template<typename> typename LockType>
+ class ReferenceGuard
+ {
+ friend class MutexData;
+ template<typename U>
+ ReferenceGuard(U &&data) noexcept
+ : m_ptr(std::addressof(data.m_data))
+ , m_lock(data.m_mutex)
+ {}
+ public:
+ ReferenceGuard(const ReferenceGuard &) = delete;
+ ReferenceGuard(ReferenceGuard &&) = default;
+ ReferenceGuard &operator=(const ReferenceGuard &) = delete;
+ ReferenceGuard &operator=(ReferenceGuard &&) = default;
+
+ T &get() const noexcept { return *m_ptr; }
+ T &data() const noexcept { return *m_ptr; }
+ operator T &() const noexcept { return *m_ptr; }
+
+ private:
+ T *m_ptr;
+ LockType<MutexType> m_lock;
+ };
+
+ using UniqueMutableGuard = ReferenceGuard<DataType, std::unique_lock>;
+ using UniqueConstGuard = ReferenceGuard<const DataType, std::unique_lock>;
+ using SharedGuard = ReferenceGuard<const DataType, std::shared_lock>;
+
+ template<
+ typename ...Args,
+ std::enable_if_t<std::is_constructible_v<DataType, Args...>, int> = 0
+ >
+ explicit MutexData(Args &&...args) : m_data(std::forward<Args>(args)...) {}
+
+ [[nodiscard]] UniqueMutableGuard lock() noexcept { return UniqueMutableGuard{*this}; }
+ [[nodiscard]] UniqueConstGuard lock() const noexcept { return UniqueConstGuard{*this}; }
+
+ template<
+ typename U = MutexType,
+ std::enable_if_t<std::is_same_v<U, std::shared_mutex>, int> = 0
+ >
+ [[nodiscard]] SharedGuard lock_shared() const noexcept
+ { return SharedGuard{*this}; }
+
+private:
+ DataType m_data;
+ mutable MutexType m_mutex;
+};
+
+} // namespace qbs
+} // namespace Internal
diff --git a/src/lib/corelib/tools/pathutils.h b/src/lib/corelib/tools/pathutils.h
index a2fad9aa4..55dbc1c6d 100644
--- a/src/lib/corelib/tools/pathutils.h
+++ b/src/lib/corelib/tools/pathutils.h
@@ -46,7 +46,7 @@
namespace qbs {
namespace Internal {
-class QBS_EXPORT PathUtils
+class PathUtils
{
public:
static QString toNativeSeparators(const QString &s,
diff --git a/src/lib/corelib/tools/persistence.cpp b/src/lib/corelib/tools/persistence.cpp
index c30d16518..0e545377a 100644
--- a/src/lib/corelib/tools/persistence.cpp
+++ b/src/lib/corelib/tools/persistence.cpp
@@ -48,7 +48,7 @@
namespace qbs {
namespace Internal {
-static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-127";
+static const char QBS_PERSISTENCE_MAGIC[] = "QBSPERSISTENCE-133";
NoBuildGraphError::NoBuildGraphError(const QString &filePath)
: ErrorInfo(Tr::tr("Build graph not found for configuration '%1'. Expected location was '%2'.")
@@ -140,7 +140,12 @@ void PersistentPool::finalizeWriteStream()
void PersistentPool::storeVariant(const QVariant &variant)
{
- const auto type = static_cast<quint32>(variant.type());
+ if (variant.isNull()) {
+ m_stream << quint32(QMetaType::User);
+ m_stream << variant;
+ return;
+ }
+ const auto type = static_cast<quint32>(variant.userType());
m_stream << type;
switch (type) {
case QMetaType::QString:
@@ -206,7 +211,7 @@ void PersistentPool::doLoadValue(QStringList &l)
void PersistentPool::doLoadValue(QProcessEnvironment &env)
{
- const QStringList keys = load<QStringList>();
+ const auto keys = load<QStringList>();
for (const QString &key : keys)
env.insert(key, load<QString>());
}
@@ -218,7 +223,7 @@ void PersistentPool::doStoreValue(const QString &s)
void PersistentPool::doStoreValue(const QStringList &l)
{
- m_stream << l.size();
+ m_stream << int(l.size());
for (const QString &s : l)
store(s);
}
@@ -231,8 +236,5 @@ void PersistentPool::doStoreValue(const QProcessEnvironment &env)
store(env.value(key));
}
-const PersistentPool::PersistentObjectId PersistentPool::ValueNotFoundId;
-const PersistentPool::PersistentObjectId PersistentPool::EmptyValueId;
-
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/tools/persistence.h b/src/lib/corelib/tools/persistence.h
index e00310321..86365c993 100644
--- a/src/lib/corelib/tools/persistence.h
+++ b/src/lib/corelib/tools/persistence.h
@@ -48,8 +48,9 @@
#include <QtCore/qdatastream.h>
#include <QtCore/qflags.h>
#include <QtCore/qprocess.h>
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
#include <memory>
@@ -81,16 +82,14 @@ public:
QVariantMap projectConfig;
};
- template<typename T, typename ...Types> void store(const T &value, const Types &...args)
+ template<typename ...Types> void store(const Types &...args)
{
- PPHelper<T>::store(value, this);
- store(args...);
+ (... , PPHelper<Types>::store(args, this));
}
- template<typename T, typename ...Types> void load(T &value, Types &...args)
+ template<typename ...Types> void load(Types &...args)
{
- PPHelper<T>::load(value, this);
- load(args...);
+ (... , PPHelper<Types>::load(args, this));
}
template<typename T> T load() {
T tmp;
@@ -99,30 +98,17 @@ public:
}
enum OpType { Store, Load };
- template<OpType type, typename T, typename ...Types> struct OpTypeHelper { };
- template<typename T, typename ...Types> struct OpTypeHelper<Store, T, Types...>
- {
- static void serializationOp(PersistentPool *pool, const T &value, const Types &...args)
- {
- pool->store(value, args...);
- }
- };
- template<typename T, typename ...Types> struct OpTypeHelper<Load, T, Types...>
+ template<OpType type, typename ...Types> void serializationOp(const Types &...args)
{
- static void serializationOp(PersistentPool *pool, T &value, Types &...args)
- {
- pool->load(value, args...);
- }
- };
- template<OpType type, typename T, typename ...Types> void serializationOp(const T &value,
- const Types &...args)
- {
- OpTypeHelper<type, T, Types...>::serializationOp(this, value, args...);
+ static_assert(type == Store);
+ store(args...);
}
- template<OpType type, typename T, typename ...Types> void serializationOp(T &value,
- Types &...args)
+ template<OpType type, typename ...Types> void serializationOp(Types &...args)
{
- OpTypeHelper<type, T, Types...>::serializationOp(this, value, args...);
+ if constexpr(type == Store)
+ store(args...);
+ else
+ load(args...);
}
void load(const QString &filePath);
@@ -159,12 +145,9 @@ private:
template<typename T> QHash<T, PersistentObjectId> &idMap();
template<typename T> PersistentObjectId &lastStoredId();
- // Recursion termination
- void store() {}
- void load() {}
-
- static const PersistentObjectId ValueNotFoundId = -1;
- static const PersistentObjectId EmptyValueId = -2;
+ static const inline PersistentObjectId ValueNotFoundId = -1;
+ static const inline PersistentObjectId EmptyValueId = -2;
+ static const inline PersistentObjectId NullValueId = -3;
std::unique_ptr<QIODevice> m_file;
QDataStream m_stream;
@@ -289,8 +272,13 @@ template<typename T> inline T PersistentPool::idLoadValue()
{
int id;
m_stream >> id;
- if (id == EmptyValueId)
+ if (id == NullValueId)
+ return T();
+ if (id == EmptyValueId) {
+ if constexpr (std::is_same_v<T, QString>)
+ return QString(0, QChar());
return T();
+ }
QBS_CHECK(id >= 0);
if (id >= static_cast<int>(idStorage<T>().size())) {
T value;
@@ -305,6 +293,12 @@ template<typename T> inline T PersistentPool::idLoadValue()
template<typename T>
void PersistentPool::idStoreValue(const T &value)
{
+ if constexpr (std::is_same_v<T, QString>) {
+ if (value.isNull()) {
+ m_stream << NullValueId;
+ return;
+ }
+ }
if (value.isEmpty()) {
m_stream << EmptyValueId;
return;
@@ -340,8 +334,8 @@ struct PPHelper
/***** Specializations of Helper class *****/
template<typename T>
-struct PPHelper<T, std::enable_if_t<std::is_member_function_pointer<
- decltype(&T::template completeSerializationOp<PersistentPool::Load>)>::value>>
+struct PPHelper<T, std::enable_if_t<std::is_member_function_pointer_v<
+ decltype(&T::template completeSerializationOp<PersistentPool::Load>)>>>
{
static void store(const T &value, PersistentPool *pool)
{
@@ -353,7 +347,7 @@ struct PPHelper<T, std::enable_if_t<std::is_member_function_pointer<
}
};
-template<typename T> struct PPHelper<T, std::enable_if_t<std::is_integral<T>::value>>
+template<typename T> struct PPHelper<T, std::enable_if_t<std::is_integral_v<T>>>
{
static void store(const T &value, PersistentPool *pool) { pool->m_stream << value; }
static void load(T &value, PersistentPool *pool) { pool->m_stream >> value; }
@@ -370,7 +364,7 @@ template<> struct PPHelper<long>
}
};
-template<typename T> struct PPHelper<T, std::enable_if_t<std::is_enum<T>::value>>
+template<typename T> struct PPHelper<T, std::enable_if_t<std::is_enum_v<T>>>
{
using U = std::underlying_type_t<T>;
static void store(const T &value, PersistentPool *pool)
@@ -413,8 +407,8 @@ template<typename T> struct PPHelper<T *>
static void load(T* &value, PersistentPool *pool) { value = pool->idLoad<T>(); }
};
-template<typename T> struct PPHelper<T, std::enable_if_t<std::is_same<T, QString>::value
- || std::is_same<T, QStringList>::value || std::is_same<T, QProcessEnvironment>::value>>
+template<typename T> struct PPHelper<T, std::enable_if_t<std::is_same_v<T, QString>
+ || std::is_same_v<T, QStringList> || std::is_same_v<T, QProcessEnvironment>>>
{
static void store(const T &v, PersistentPool *pool) { pool->idStoreValue(v); }
static void load(T &v, PersistentPool *pool) { v = pool->idLoadValue<T>(); }
@@ -426,10 +420,16 @@ template<> struct PPHelper<QVariant>
static void load(QVariant &v, PersistentPool *pool) { v = pool->loadVariant(); }
};
-template<> struct PPHelper<QRegExp>
+template<> struct PPHelper<QRegularExpression>
{
- static void store(const QRegExp &re, PersistentPool *pool) { pool->store(re.pattern()); }
- static void load(QRegExp &re, PersistentPool *pool) { re.setPattern(pool->load<QString>()); }
+ static void store(const QRegularExpression &re, PersistentPool *pool)
+ {
+ pool->store(re.pattern());
+ }
+ static void load(QRegularExpression &re, PersistentPool *pool)
+ {
+ re.setPattern(pool->load<QString>());
+ }
};
template<typename T, typename U> struct PPHelper<std::pair<T, U>>
@@ -446,6 +446,33 @@ template<typename T, typename U> struct PPHelper<std::pair<T, U>>
}
};
+template<typename... Args> struct PPHelper<std::tuple<Args...>>
+{
+ template<std::size_t... Ns>
+ static void storeHelper(
+ std::index_sequence<Ns...>, const std::tuple<Args...> &tuple, PersistentPool *pool)
+ {
+ (pool->store(std::get<Ns>(tuple)), ...);
+ }
+
+ static void store(const std::tuple<Args...> &tuple, PersistentPool *pool)
+ {
+ storeHelper(std::make_index_sequence<sizeof...(Args)>(), tuple, pool);
+ }
+
+ template<std::size_t... Ns>
+ static void loadHelper(
+ std::index_sequence<Ns...>, std::tuple<Args...> &tuple, PersistentPool *pool)
+ {
+ (pool->load(std::get<Ns>(tuple)), ...);
+ }
+
+ static void load(std::tuple<Args...> &tuple, PersistentPool * pool)
+ {
+ loadHelper(std::make_index_sequence<sizeof...(Args)>(), tuple, pool);
+ }
+};
+
template<typename T> struct PPHelper<QFlags<T>>
{
using Int = typename QFlags<T>::Int;
@@ -461,6 +488,7 @@ template<typename T> struct PPHelper<QFlags<T>>
template<typename T> struct IsSimpleContainer : std::false_type { };
template<typename T> struct IsSimpleContainer<QList<T>> : std::true_type { };
+template<> struct IsSimpleContainer<QStringList> : std::false_type { };
template<typename T> struct IsSimpleContainer<std::vector<T>> : std::true_type { };
template<typename T> struct PPHelper<T, std::enable_if_t<IsSimpleContainer<T>::value>>
@@ -490,7 +518,7 @@ struct PPHelper<T, std::enable_if_t<IsKeyValueContainer<T>::value>>
{
static void store(const T &container, PersistentPool *pool)
{
- pool->store(container.size());
+ pool->store(int(container.size()));
for (auto it = container.cbegin(); it != container.cend(); ++it) {
pool->store(it.key());
pool->store(it.value());
diff --git a/src/lib/corelib/tools/pimpl.h b/src/lib/corelib/tools/pimpl.h
new file mode 100644
index 000000000..ab672aabe
--- /dev/null
+++ b/src/lib/corelib/tools/pimpl.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 PIMPL_H
+#define PIMPL_H
+
+#include <tools/propagate_const.h>
+#include <memory>
+
+namespace qbs {
+namespace Internal {
+
+template<typename T>
+using Pimpl = KDToolBox::propagate_const<std::unique_ptr<T>>;
+
+template<typename T, typename... Args>
+Pimpl<T> makePimpl(Args&&... args)
+{
+ return Pimpl<T>(std::make_unique<T>(std::forward<Args>(args)...));
+}
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // PIMPL_H
diff --git a/src/lib/corelib/tools/porting.h b/src/lib/corelib/tools/porting.h
new file mode 100644
index 000000000..37582e8d3
--- /dev/null
+++ b/src/lib/corelib/tools/porting.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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_PORTING_H
+#define QBS_TOOLS_PORTING_H
+
+#include <QtCore/qglobal.h>
+
+namespace qbs {
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+using QHashValueType = uint;
+#else
+using QHashValueType = size_t;
+#endif
+
+}
+
+#endif
diff --git a/src/lib/corelib/tools/preferences.cpp b/src/lib/corelib/tools/preferences.cpp
index 4db271758..fe669e163 100644
--- a/src/lib/corelib/tools/preferences.cpp
+++ b/src/lib/corelib/tools/preferences.cpp
@@ -63,7 +63,6 @@ Preferences::Preferences(Settings *settings, QVariantMap profileContents)
{
}
-
/*!
* \brief Returns true <=> colored output should be used for printing messages.
* This is only relevant for command-line frontends.
diff --git a/src/lib/corelib/tools/processresult.h b/src/lib/corelib/tools/processresult.h
index 92408aa31..3870a3ae5 100644
--- a/src/lib/corelib/tools/processresult.h
+++ b/src/lib/corelib/tools/processresult.h
@@ -44,11 +44,10 @@
#include <QtCore/qshareddata.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qprocess.h>
+#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
class QJsonObject;
-class QString;
-class QStringList;
QT_END_NAMESPACE
namespace qbs {
diff --git a/src/lib/corelib/tools/processutils.cpp b/src/lib/corelib/tools/processutils.cpp
index b27592f88..02608cc9f 100644
--- a/src/lib/corelib/tools/processutils.cpp
+++ b/src/lib/corelib/tools/processutils.cpp
@@ -91,7 +91,7 @@ QString processNameByPid(qint64 pid)
char exePath[64];
char buf[PATH_MAX];
memset(buf, 0, sizeof(buf));
- sprintf(exePath, "/proc/%lld/exe", pid);
+ std::sprintf(exePath, "/proc/%lld/exe", pid);
if (readlink(exePath, buf, sizeof(buf)) < 0)
return {};
return FileInfo::fileName(QString::fromUtf8(buf));
diff --git a/src/lib/corelib/tools/profile.cpp b/src/lib/corelib/tools/profile.cpp
index 7e594fd2d..0fe0ba19d 100644
--- a/src/lib/corelib/tools/profile.cpp
+++ b/src/lib/corelib/tools/profile.cpp
@@ -63,13 +63,13 @@ namespace qbs {
/*!
* \brief Creates an object giving access to the settings for profile \c name.
*/
-Profile::Profile(const QString &name, Settings *settings, const QVariantMap &profiles)
- : m_name(name),
+Profile::Profile(QString name, Settings *settings, QVariantMap profiles)
+ : m_name(std::move(name)),
m_settings(settings),
- m_values(profiles.value(name).toMap()),
- m_profiles(profiles)
+ m_values(profiles.value(m_name).toMap()),
+ m_profiles(std::move(profiles))
{
- QBS_ASSERT(name == cleanName(name), return);
+ QBS_ASSERT(m_name == cleanName(m_name), return);
}
bool Profile::exists() const
@@ -213,7 +213,7 @@ QVariant Profile::possiblyInheritedValue(const QString &key, const QVariant &def
QStringList profileChain) const
{
extendAndCheckProfileChain(profileChain);
- const QVariant v = localValue(key);
+ QVariant v = localValue(key);
if (v.isValid())
return v;
const QString baseProfileName = baseProfile();
diff --git a/src/lib/corelib/tools/profile.h b/src/lib/corelib/tools/profile.h
index aa8b7ef10..b7fc7ff67 100644
--- a/src/lib/corelib/tools/profile.h
+++ b/src/lib/corelib/tools/profile.h
@@ -42,12 +42,9 @@
#include "qbs_export.h"
#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
-QT_BEGIN_NAMESPACE
-class QStringList;
-QT_END_NAMESPACE
-
namespace qbs {
class ErrorInfo;
class Settings;
@@ -55,7 +52,7 @@ class Settings;
class QBS_EXPORT Profile
{
public:
- Profile(const QString &name, Settings *settings, const QVariantMap &profiles = QVariantMap());
+ Profile(QString name, Settings *settings, QVariantMap profiles = QVariantMap());
bool exists() const;
QVariant value(const QString &key, const QVariant &defaultValue = QVariant(),
diff --git a/src/lib/corelib/tools/profiling.cpp b/src/lib/corelib/tools/profiling.cpp
index 7e3559b54..b93d3fa17 100644
--- a/src/lib/corelib/tools/profiling.cpp
+++ b/src/lib/corelib/tools/profiling.cpp
@@ -61,7 +61,7 @@ TimedActivityLogger::TimedActivityLogger(const Logger &logger, const QString &ac
{
if (!enabled)
return;
- d = new TimedActivityLoggerPrivate;
+ d = std::make_unique<TimedActivityLoggerPrivate>();
d->logger = logger;
d->activity = activity;
d->logger.qbsLog(LoggerInfo, true) << Tr::tr("Starting activity '%2'.").arg(activity);
@@ -72,11 +72,10 @@ void TimedActivityLogger::finishActivity()
{
if (!d)
return;
- const QString timeString = elapsedTimeString(d->timer.elapsed());
+ const QString timeString = elapsedTimeString(d->timer.nsecsElapsed());
d->logger.qbsLog(LoggerInfo, true)
<< Tr::tr("Activity '%2' took %3.").arg(d->activity, timeString);
- delete d;
- d = nullptr;
+ d.reset();
}
TimedActivityLogger::~TimedActivityLogger()
@@ -99,13 +98,13 @@ void AccumulatingTimer::stop()
{
if (!m_timer.isValid())
return;
- *m_elapsedTime += m_timer.elapsed();
+ *m_elapsedTime += m_timer.nsecsElapsed();
m_timer.invalidate();
}
-QString elapsedTimeString(qint64 elapsedTimeInMs)
+QString elapsedTimeString(qint64 elapsedTimeInNs)
{
- qint64 ms = elapsedTimeInMs;
+ qint64 ms = elapsedTimeInNs / (1000ll * 1000ll);
qint64 s = ms/1000;
ms -= s*1000;
qint64 m = s/60;
diff --git a/src/lib/corelib/tools/profiling.h b/src/lib/corelib/tools/profiling.h
index 89f862ff9..3220c2a82 100644
--- a/src/lib/corelib/tools/profiling.h
+++ b/src/lib/corelib/tools/profiling.h
@@ -42,6 +42,8 @@
#include <QtCore/qelapsedtimer.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QString;
QT_END_NAMESPACE
@@ -50,7 +52,7 @@ namespace qbs {
namespace Internal {
class Logger;
-QString elapsedTimeString(qint64 elapsedTimeInMs);
+QString elapsedTimeString(qint64 elapsedTimeInNs);
class TimedActivityLogger
{
@@ -61,7 +63,7 @@ public:
private:
class TimedActivityLoggerPrivate;
- TimedActivityLoggerPrivate *d;
+ std::unique_ptr<TimedActivityLoggerPrivate> d;
};
class AccumulatingTimer
diff --git a/src/lib/corelib/tools/progressobserver.h b/src/lib/corelib/tools/progressobserver.h
index fc49d9eed..73a61de37 100644
--- a/src/lib/corelib/tools/progressobserver.h
+++ b/src/lib/corelib/tools/progressobserver.h
@@ -41,12 +41,15 @@
#include <QtCore/qglobal.h>
+#include <vector>
+
QT_BEGIN_NAMESPACE
class QString;
QT_END_NAMESPACE
namespace qbs {
namespace Internal {
+class ScriptEngine;
class ProgressObserver
{
@@ -64,6 +67,14 @@ public:
// Call this to ensure that the progress bar always goes to 100%.
void setFinished();
+
+ void addScriptEngine(ScriptEngine *engine) { m_scriptEngines.push_back(engine); }
+
+protected:
+ const std::vector<ScriptEngine *> &scriptEngines() const { return m_scriptEngines; }
+
+private:
+ std::vector<ScriptEngine *> m_scriptEngines;
};
} // namespace Internal
diff --git a/src/lib/corelib/tools/projectgeneratormanager.h b/src/lib/corelib/tools/projectgeneratormanager.h
index 469d650b9..1efe593fc 100644
--- a/src/lib/corelib/tools/projectgeneratormanager.h
+++ b/src/lib/corelib/tools/projectgeneratormanager.h
@@ -45,10 +45,7 @@
#include <QtCore/qmap.h>
#include <QtCore/qstring.h>
-
-QT_BEGIN_NAMESPACE
-class QStringList;
-QT_END_NAMESPACE
+#include <QtCore/qstringlist.h>
namespace qbs {
class ProjectGenerator;
diff --git a/src/lib/corelib/tools/propagate_const.h b/src/lib/corelib/tools/propagate_const.h
new file mode 100644
index 000000000..65e35fc8c
--- /dev/null
+++ b/src/lib/corelib/tools/propagate_const.h
@@ -0,0 +1,418 @@
+/****************************************************************************
+** MIT License
+**
+** Copyright (C) 2022-2023 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
+** Author: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
+**
+** This file is part of KDToolBox (https://github.com/KDAB/KDToolBox).
+**
+** 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 (including the next paragraph)
+** 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 KDTOOLBOX_PROPAGATE_CONST
+#define KDTOOLBOX_PROPAGATE_CONST
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+// Our SFINAE is too noisy and clang-format gets confused.
+// clang-format off
+
+namespace KDToolBox
+{
+
+template <typename T>
+class propagate_const;
+
+namespace detail {
+// Type traits to detect propagate_const specializations
+template <typename T>
+struct is_propagate_const : std::false_type
+{
+};
+
+template <typename T>
+struct is_propagate_const<propagate_const<T>> : std::true_type
+{
+};
+
+// Using SFINAE in a base class to constrain the conversion operators.
+// Otherwise, if we make them function templates and apply SFINAE directly on
+// them, we would not be able to do certain conversions (cf.
+// [over.ics.user/3]).
+template <typename T>
+using propagate_const_element_type = std::remove_reference_t<decltype(*std::declval<T &>())>;
+
+// Const conversion.
+// NON-STANDARD: checks that `const T` is convertible, not `T`.
+// See https://wg21.link/lwg3812
+template <typename T,
+ bool = std::disjunction_v<
+ std::is_pointer<T>,
+ std::is_convertible<const T, const propagate_const_element_type<T> *>
+ >
+ >
+struct propagate_const_const_conversion_operator_base
+{
+};
+
+template <typename T>
+struct propagate_const_const_conversion_operator_base<T, true>
+{
+ constexpr operator const propagate_const_element_type<T> *() const;
+};
+
+// Non-const conversion
+template <typename T,
+ bool = std::disjunction_v<
+ std::is_pointer<T>,
+ std::is_convertible<T, propagate_const_element_type<T> *>
+ >
+ >
+struct propagate_const_non_const_conversion_operator_base
+{
+};
+
+template <typename T>
+struct propagate_const_non_const_conversion_operator_base<T, true>
+{
+ constexpr operator propagate_const_element_type<T> *();
+};
+
+template <typename T>
+struct propagate_const_base
+ : propagate_const_const_conversion_operator_base<T>,
+ propagate_const_non_const_conversion_operator_base<T>
+{};
+
+} // namespace detail
+
+/*
+ TODO: This code could benefit from a C++20 overhaul:
+ * concepts
+ * three-way comparisons
+ * explicit(bool)
+
+ However we can't depend on C++20 yet...
+*/
+
+template <typename T>
+class propagate_const : public detail::propagate_const_base<T>
+{
+public:
+ using element_type = detail::propagate_const_element_type<T>;
+
+ // Special member functions
+ propagate_const() = default;
+ propagate_const(propagate_const &&) = default;
+ propagate_const &operator=(propagate_const &&) = default;
+ propagate_const(const propagate_const &) = delete;
+ propagate_const &operator=(const propagate_const &) = delete;
+ ~propagate_const() = default;
+
+ // Constructor from values
+
+ template <
+ typename U,
+ std::enable_if_t< // This constructor is enabled if:
+ std::conjunction_v<
+ std::is_constructible<T, U>, // 1) we can build a T from a U,
+ std::negation<detail::is_propagate_const<std::decay_t<U>>>, // 2) we are not making a
+ // converting constructor,
+ std::is_convertible<U, T> // 3) and the conversion from U to T is implicit;
+ >,
+ bool> = true>
+ /* implicit */ // then, this constructor is implicit too.
+ propagate_const(U &&other)
+ : m_t(std::forward<U>(other))
+ {
+ }
+
+ template <
+ typename U,
+ std::enable_if_t< // This constructor is enabled if:
+ std::conjunction_v<
+ std::is_constructible<T, U>, // 1) we can build a T from a U,
+ std::negation<detail::is_propagate_const<std::decay_t<U>>>, // 2) we are not making a
+ // converting constructor,
+ std::negation<std::is_convertible<U, T>> // 3) and the conversion from U to T is
+ // explicit;
+ >,
+ bool> = true>
+ explicit // then, this constructor is explicit.
+ propagate_const(U &&other)
+ : m_t(std::forward<U>(other))
+ {
+ }
+
+ // Constructors from other propagate_const (converting constructors)
+ template <typename U,
+ std::enable_if_t< // This constructor is enabled if:
+ std::conjunction_v<std::is_constructible<T, U>, // 1) we can do the conversion,
+ std::is_convertible<U, T> // 2) and the conversion is implicit;
+ >,
+ bool> = true>
+ /* implicit */ // then, this constructor is implicit.
+ constexpr propagate_const(propagate_const<U> &&other)
+ : m_t(std::move(get_underlying(other)))
+ {
+ }
+
+ template <typename U,
+ std::enable_if_t< // This constructor is enabled if:
+ std::conjunction_v<std::is_constructible<T, U>, // 1) we can do the conversion,
+ std::negation<std::is_convertible<U, T>> // 2) and the
+ // conversion is
+ // explicit;
+ >,
+ bool> = true>
+ explicit // then, this constructor is explicit.
+ constexpr propagate_const(propagate_const<U> &&other)
+ : m_t(std::move(get_underlying(other)))
+ {
+ }
+
+ // Converting assignment
+ template <typename U,
+ std::enable_if_t< // This assignment operator is enabled if
+ std::is_convertible_v<U, T>, // the conversion from U to T is implicit
+ bool> = true>
+ constexpr propagate_const &operator=(propagate_const<U> &&other)
+ {
+ m_t = std::move(get_underlying(other));
+ return *this;
+ }
+
+ template <typename U,
+ std::enable_if_t< // This assignment operator is enabled if:
+ std::conjunction_v<
+ std::is_convertible<U, T>, // 1) the conversion from U to T is implicit,
+ std::negation<detail::is_propagate_const<std::decay_t<U>>> // 2) and U is not a
+ // propagate_const
+ >,
+ bool> = true>
+ constexpr propagate_const &operator=(U &&other)
+ {
+ m_t = std::forward<U>(other);
+ return *this;
+ }
+
+ // Swap
+ constexpr void swap(propagate_const &other) noexcept(std::is_nothrow_swappable_v<T>)
+ {
+ using std::swap;
+ swap(m_t, other.m_t);
+ }
+
+ // Const observers
+ constexpr explicit operator bool() const { return static_cast<bool>(m_t); }
+
+ constexpr const element_type *get() const { return get_impl(m_t); }
+
+ constexpr const element_type &operator*() const { return *get(); }
+
+ constexpr const element_type *operator->() const { return get(); }
+
+ // Non-const observers
+ constexpr element_type *get() { return get_impl(m_t); }
+
+ constexpr element_type &operator*() { return *get(); }
+
+ constexpr element_type *operator->() { return get(); }
+
+ // Non-member utilities: extract the contained object
+ template <typename U>
+ friend constexpr auto &get_underlying(propagate_const<U> &p);
+
+ template <typename U>
+ friend constexpr const auto &get_underlying(const propagate_const<U> &p);
+
+private:
+ // Implementation of get() that works with raw pointers and smart
+ // pointers. Similar to std::to_address, but to_address is C++20,
+ // and propagate_const spec does not match it.
+ template <typename U>
+ static constexpr element_type *get_impl(U *u)
+ {
+ return u;
+ }
+
+ template <typename U>
+ static constexpr element_type *get_impl(U &u)
+ {
+ return u.get();
+ }
+
+ template <typename U>
+ static constexpr const element_type *get_impl(const U *u)
+ {
+ return u;
+ }
+
+ template <typename U>
+ static constexpr const element_type *get_impl(const U &u)
+ {
+ return u.get();
+ }
+
+ T m_t;
+};
+
+// Swap
+template <typename T,
+ std::enable_if_t<std::is_swappable_v<T>, bool> = true>
+constexpr void swap(propagate_const<T> &lhs, propagate_const<T> &rhs)
+ noexcept(noexcept(lhs.swap(rhs)))
+{
+ lhs.swap(rhs);
+}
+
+// Implementation of get_underlying
+template <typename T>
+constexpr auto &get_underlying(propagate_const<T> &p)
+{
+ return p.m_t;
+}
+
+template <typename T>
+constexpr const auto &get_underlying(const propagate_const<T> &p)
+{
+ return p.m_t;
+}
+
+// Implementation of the conversion operators
+template <typename T>
+constexpr detail::propagate_const_const_conversion_operator_base<T, true>
+ ::operator const detail::propagate_const_element_type<T> *() const
+{
+ return static_cast<const propagate_const<T> *>(this)->get();
+}
+
+template <typename T>
+constexpr detail::propagate_const_non_const_conversion_operator_base<T, true>
+ ::operator detail::propagate_const_element_type<T> *()
+{
+ return static_cast<propagate_const<T> *>(this)->get();
+}
+
+// Comparisons. As per spec, they're free function templates.
+
+// Comparisons against nullptr.
+template <typename T>
+constexpr bool operator==(const propagate_const<T> &p, std::nullptr_t)
+{
+ return get_underlying(p) == nullptr;
+}
+
+template <typename T>
+constexpr bool operator!=(const propagate_const<T> &p, std::nullptr_t)
+{
+ return get_underlying(p) != nullptr;
+}
+
+template <typename T>
+constexpr bool operator==(std::nullptr_t, const propagate_const<T> &p)
+{
+ return nullptr == get_underlying(p);
+}
+
+template <typename T>
+constexpr bool operator!=(std::nullptr_t, const propagate_const<T> &p)
+{
+ return nullptr == get_underlying(p);
+}
+
+// Comparisons between propagate_const
+#define DEFINE_PROPAGATE_CONST_COMPARISON_OP(op) \
+template <typename T, typename U> \
+ constexpr bool operator op (const propagate_const<T> &lhs, const propagate_const<U> &rhs) \
+{ \
+ return get_underlying(lhs) op get_underlying(rhs); \
+} \
+
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(==)
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(!=)
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(<)
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(<=)
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(>)
+ DEFINE_PROPAGATE_CONST_COMPARISON_OP(>=)
+
+#undef DEFINE_PROPAGATE_CONST_COMPARISON_OP
+
+// Comparisons against other (smart) pointers
+#define DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(op) \
+ template <typename T, typename U> \
+ constexpr bool operator op (const propagate_const<T> &lhs, const U &rhs) \
+{ \
+ return get_underlying(lhs) op rhs; \
+} \
+ template <typename T, typename U> \
+ constexpr bool operator op (const T &lhs, const propagate_const<U> &rhs) \
+{ \
+ return lhs op get_underlying(rhs); \
+} \
+
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(==)
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(!=)
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(<)
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(<=)
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(>)
+ DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP(>=)
+
+#undef DEFINE_PROPAGATE_CONST_MIXED_COMPARISON_OP
+
+} // namespace KDToolBox
+
+// std::hash specialization
+namespace std
+{
+template <typename T>
+struct hash<KDToolBox::propagate_const<T>>
+{
+ constexpr size_t operator()(const KDToolBox::propagate_const<T> &t) const
+ noexcept(noexcept(hash<T>{}(get_underlying(t))))
+ {
+ return hash<T>{}(get_underlying(t));
+ }
+};
+
+#define DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(COMP) \
+template <typename T> \
+ struct COMP<KDToolBox::propagate_const<T>> \
+{ \
+ constexpr bool operator()(const KDToolBox::propagate_const<T> &lhs, \
+ const KDToolBox::propagate_const<T> &rhs) \
+ { \
+ return COMP<T>{}(get_underlying(lhs), get_underlying(rhs)); \
+ } \
+}; \
+
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(equal_to)
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(not_equal_to)
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(less)
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(greater)
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(less_equal)
+ DEFINE_COMP_OBJECT_SPECIALIZATION_FOR_PROPAGATE_CONST(greater_equal)
+
+#undef DEFINE_COMP_OBJECT_SPECIALIZATION
+
+} // namespace std
+
+#endif // KDTOOLBOX_PROPAGATE_CONST
diff --git a/src/lib/corelib/tools/qbs_export.h b/src/lib/corelib/tools/qbs_export.h
index 164aa4184..2cfe1cd82 100644
--- a/src/lib/corelib/tools/qbs_export.h
+++ b/src/lib/corelib/tools/qbs_export.h
@@ -53,14 +53,14 @@
#else
# ifdef QBS_LIBRARY
# define QBS_EXPORT QBS_DECL_EXPORT
-# ifdef QBS_ENABLE_UNIT_TESTS
+# ifdef QBS_WITH_TESTS
# define QBS_AUTOTEST_EXPORT QBS_DECL_EXPORT
# else
# define QBS_AUTOTEST_EXPORT
# endif
# else
# define QBS_EXPORT QBS_DECL_IMPORT
-# ifdef QBS_ENABLE_UNIT_TESTS
+# ifdef QBS_WITH_TESTS
# define QBS_AUTOTEST_EXPORT QBS_DECL_IMPORT
# else
# define QBS_AUTOTEST_EXPORT
diff --git a/src/lib/corelib/tools/qbsassert.h b/src/lib/corelib/tools/qbsassert.h
index e8dfcacf8..65352fdb3 100644
--- a/src/lib/corelib/tools/qbsassert.h
+++ b/src/lib/corelib/tools/qbsassert.h
@@ -59,6 +59,11 @@ QBS_EXPORT void writeAssertLocation(const char *condition, const char *file, int
// 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_GUARD(cond) \
+ (Q_LIKELY(cond) \
+ ? true \
+ : (::qbs::Internal::writeAssertLocation(#cond, __FILE__, __LINE__), false))
+
#define QBS_CHECK(cond)\
do {\
if (Q_LIKELY(cond)) {} else {\
diff --git a/src/lib/corelib/tools/qbspluginmanager.cpp b/src/lib/corelib/tools/qbspluginmanager.cpp
index d4e92e22a..0816d9d25 100644
--- a/src/lib/corelib/tools/qbspluginmanager.cpp
+++ b/src/lib/corelib/tools/qbspluginmanager.cpp
@@ -79,7 +79,7 @@ QbsPluginManager::~QbsPluginManager()
{
unloadStaticPlugins();
- for (QLibrary * const lib : qAsConst(d->libs)) {
+ for (QLibrary * const lib : std::as_const(d->libs)) {
auto unload = reinterpret_cast<QbsPluginUnloadFunction>(lib->resolve("QbsPluginUnload"));
if (unload)
unload();
diff --git a/src/lib/corelib/tools/qbsprocess.cpp b/src/lib/corelib/tools/qbsprocess.cpp
index 52ce3f25a..3fdc05afe 100644
--- a/src/lib/corelib/tools/qbsprocess.cpp
+++ b/src/lib/corelib/tools/qbsprocess.cpp
@@ -65,7 +65,7 @@ void QbsProcess::start(const QString &command, const QStringList &arguments)
{
if (m_socketError) {
m_error = QProcess::FailedToStart;
- emit error(m_error);
+ emit errorOccurred(m_error);
return;
}
m_command = command;
@@ -95,7 +95,7 @@ void QbsProcess::cancel()
m_errorString = Tr::tr("Process canceled before it was started.");
m_error = QProcess::FailedToStart;
m_state = QProcess::NotRunning;
- emit error(m_error);
+ emit errorOccurred(m_error);
break;
case QProcess::Running:
sendPacket(StopProcessPacket(token()));
@@ -120,7 +120,7 @@ void QbsProcess::sendPacket(const LauncherPacket &packet)
QByteArray QbsProcess::readAndClear(QByteArray &data)
{
- const QByteArray tmp = data;
+ QByteArray tmp = data;
data.clear();
return tmp;
}
@@ -155,7 +155,7 @@ void QbsProcess::handleSocketError(const QString &message)
if (m_state != QProcess::NotRunning) {
m_state = QProcess::NotRunning;
m_error = QProcess::FailedToStart;
- emit error(m_error);
+ emit errorOccurred(m_error);
}
}
@@ -166,7 +166,7 @@ void QbsProcess::handleErrorPacket(const QByteArray &packetData)
m_error = packet.error;
m_errorString = packet.errorString;
m_state = QProcess::NotRunning;
- emit error(m_error);
+ emit errorOccurred(m_error);
}
void QbsProcess::handleFinishedPacket(const QByteArray &packetData)
diff --git a/src/lib/corelib/tools/qbsprocess.h b/src/lib/corelib/tools/qbsprocess.h
index 2181818f0..3314e3d3c 100644
--- a/src/lib/corelib/tools/qbsprocess.h
+++ b/src/lib/corelib/tools/qbsprocess.h
@@ -69,7 +69,7 @@ public:
QString errorString() const { return m_errorString; }
signals:
- void error(QProcess::ProcessError error);
+ void errorOccurred(QProcess::ProcessError error);
void finished(int exitCode);
private:
diff --git a/src/lib/corelib/tools/qttools.cpp b/src/lib/corelib/tools/qttools.cpp
index ffd336d56..40e0d0778 100644
--- a/src/lib/corelib/tools/qttools.cpp
+++ b/src/lib/corelib/tools/qttools.cpp
@@ -38,20 +38,42 @@
****************************************************************************/
#include "qttools.h"
+#include "porting.h"
#include <QtCore/qprocess.h>
+namespace std {
+
+size_t hash<QVariant>::operator()(const QVariant &v) const noexcept
+{
+ switch (v.userType()) {
+ case QMetaType::UnknownType: return 0;
+ case QMetaType::Bool: return std::hash<bool>()(v.toBool());
+ case QMetaType::Int: return std::hash<int>()(v.toInt());
+ case QMetaType::UInt: return std::hash<uint>()(v.toUInt());
+ case QMetaType::QString: return std::hash<QString>()(v.toString());
+ case QMetaType::QStringList: return std::hash<QStringList>()(v.toStringList());
+ case QMetaType::QVariantList: return std::hash<QVariantList>()(v.toList());
+ case QMetaType::QVariantMap: return std::hash<QVariantMap>()(v.toMap());
+ case QMetaType::QVariantHash: return std::hash<QVariantHash>()(v.toHash());
+ default:
+ QBS_ASSERT("Unsupported variant type" && false, return 0);
+ }
+}
+
+} // namespace std
+
QT_BEGIN_NAMESPACE
-uint qHash(const QStringList &list)
+qbs::QHashValueType qHash(const QStringList &list)
{
- uint s = 0;
+ qbs::QHashValueType s = 0;
for (const QString &n : list)
s ^= qHash(n) + 0x9e3779b9 + (s << 6) + (s >> 2);
return s;
}
-uint qHash(const QProcessEnvironment &env)
+qbs::QHashValueType qHash(const QProcessEnvironment &env)
{
return qHash(env.toStringList());
}
diff --git a/src/lib/corelib/tools/qttools.h b/src/lib/corelib/tools/qttools.h
index c3b4d3a9f..029948be4 100644
--- a/src/lib/corelib/tools/qttools.h
+++ b/src/lib/corelib/tools/qttools.h
@@ -40,8 +40,14 @@
#ifndef QBSQTTOOLS_H
#define QBSQTTOOLS_H
+#include <tools/qbsassert.h>
+#include <tools/porting.h>
+#include <tools/stlutils.h>
+
#include <QtCore/qhash.h>
#include <QtCore/qstringlist.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qvariant.h>
#include <functional>
@@ -50,12 +56,6 @@ class QProcessEnvironment;
QT_END_NAMESPACE
namespace std {
-#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
-template<> struct hash<QString> {
- std::size_t operator()(const QString &s) const { return qHash(s); }
-};
-#endif
-
template<typename T1, typename T2> struct hash<std::pair<T1, T2>>
{
size_t operator()(const pair<T1, T2> &x) const
@@ -63,11 +63,92 @@ template<typename T1, typename T2> struct hash<std::pair<T1, T2>>
return std::hash<T1>()(x.first) ^ std::hash<T2>()(x.second);
}
};
+
+template <typename... Ts>
+struct hash<std::tuple<Ts...>>
+{
+private:
+ template<std::size_t... Ns>
+ static size_t helper(std::index_sequence<Ns...>, const std::tuple<Ts...> &tuple) noexcept
+ {
+ size_t seed = 0;
+ (qbs::Internal::hashCombineHelper(seed, std::get<Ns>(tuple)), ...);
+ return seed;
+ }
+
+public:
+ size_t operator()(const std::tuple<Ts...> & tuple) const noexcept
+ {
+ return helper(std::make_index_sequence<sizeof...(Ts)>(), tuple);
+ }
+};
+
+
+template<> struct hash<QStringList>
+{
+ std::size_t operator()(const QStringList &s) const noexcept
+ {
+ return qbs::Internal::hashRange(s);
+ }
+};
+
+template<> struct hash<QVariant>
+{
+ size_t operator()(const QVariant &v) const noexcept;
+};
+
+template<> struct hash<QVariantList>
+{
+ size_t operator()(const QVariantList &v) const noexcept
+ {
+ return qbs::Internal::hashRange(v);
+ }
+};
+
+template<> struct hash<QVariantMap>
+{
+ size_t operator()(const QVariantMap &v) const noexcept
+ {
+ return qbs::Internal::hashRange(v);
+ }
+};
+
+template<> struct hash<QVariantHash>
+{
+ size_t operator()(const QVariantHash &v) const noexcept
+ {
+ return qbs::Internal::hashRange(v);
+ }
+};
+
} // namespace std
QT_BEGIN_NAMESPACE
-uint qHash(const QStringList &list);
-uint qHash(const QProcessEnvironment &env);
+
+qbs::QHashValueType qHash(const QStringList &list);
+qbs::QHashValueType qHash(const QProcessEnvironment &env);
+
+template<typename... Args>
+qbs::QHashValueType qHash(const std::tuple<Args...> &tuple)
+{
+ return std::hash<std::tuple<Args...>>()(tuple) % std::numeric_limits<uint>::max();
+}
+
+inline qbs::QHashValueType qHash(const QVariant &v)
+{
+ return std::hash<QVariant>()(v) % std::numeric_limits<uint>::max();
+}
+
+inline qbs::QHashValueType qHash(const QVariantMap &v)
+{
+ return std::hash<QVariantMap>()(v) % std::numeric_limits<uint>::max();
+}
+
+inline qbs::QHashValueType qHash(const QVariantHash &v)
+{
+ return std::hash<QVariantHash>()(v) % std::numeric_limits<uint>::max();
+}
+
QT_END_NAMESPACE
namespace qbs {
@@ -75,14 +156,94 @@ namespace qbs {
template <class T>
QSet<T> toSet(const QList<T> &list)
{
-#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
- return list.toSet();
-#else
return QSet<T>(list.begin(), list.end());
+}
+
+template<class T>
+QList<T> toList(const QSet<T> &set)
+{
+ return QList<T>(set.begin(), set.end());
+}
+
+template<typename K, typename V>
+QHash<K, V> &unite(QHash<K, V> &h, const QHash<K, V> &other)
+{
+ h.insert(other);
+ return h;
+}
+
+inline void setupDefaultCodec(QTextStream &stream)
+{
+#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
+ stream.setCodec("UTF-8");
+#else
+ Q_UNUSED(stream);
#endif
}
-} // namespace qbs
+inline bool qVariantCanConvert(const QVariant &variant, int typeId)
+{
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ return variant.canConvert(QMetaType(typeId));
+#else
+ return variant.canConvert(typeId); // deprecated in Qt6
+#endif
+}
+inline bool qVariantConvert(QVariant &variant, int typeId)
+{
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ return variant.convert(QMetaType(typeId));
+#else
+ return variant.convert(typeId); // deprecated in Qt6
+#endif
+}
+
+inline QMetaType::Type qVariantType(const QVariant &v)
+{
+ return static_cast<QMetaType::Type>(
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ v.metaType().id()
+#else
+ v.type()
+#endif
+ );
+}
+
+template<typename T>
+inline QVariant typedNullVariant()
+{
+ const auto metaType = QMetaType::fromType<T>();
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ return QVariant(metaType, nullptr);
+#else
+ return QVariant(static_cast<QVariant::Type>(metaType.id()));
+#endif
+}
+
+inline bool qVariantsEqual(const QVariant &v1, const QVariant &v2)
+{
+ return v1.isNull() == v2.isNull() && v1 == v2;
+}
+
+inline bool qVariantMapsEqual(const QVariantMap &m1, const QVariantMap &m2)
+{
+ if (m1.size() != m2.size())
+ return false;
+ if (m1.isSharedWith(m2))
+ return true;
+
+ auto it1 = m1.cbegin();
+ auto it2 = m2.cbegin();
+ while (it1 != m1.cend()) {
+ if (it1.key() != it2.key() || !qVariantsEqual(it1.value(), it2.value()))
+ return false;
+ ++it2;
+ ++it1;
+ }
+ return true;
+}
+
+} // namespace qbs
#endif // QBSQTTOOLS_H
diff --git a/src/lib/corelib/tools/scripttools.cpp b/src/lib/corelib/tools/scripttools.cpp
index adf930cf0..e89e4a0ad 100644
--- a/src/lib/corelib/tools/scripttools.cpp
+++ b/src/lib/corelib/tools/scripttools.cpp
@@ -39,9 +39,14 @@
#include "scripttools.h"
-#include <QtCore/qdatastream.h>
+#include <language/scriptengine.h>
+#include <tools/error.h>
-#include <QtScript/qscriptengine.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
namespace qbs {
namespace Internal {
@@ -62,20 +67,262 @@ QVariant getConfigProperty(const QVariantMap &cfg, const QStringList &name)
{
if (name.length() == 1)
return cfg.value(name.front());
- else
- return getConfigProperty(cfg.value(name.front()).toMap(), name.mid(1));
+ return getConfigProperty(cfg.value(name.front()).toMap(), name.mid(1));
}
-TemporaryGlobalObjectSetter::TemporaryGlobalObjectSetter(const QScriptValue &object)
+TemporaryGlobalObjectSetter::TemporaryGlobalObjectSetter(
+ ScriptEngine *engine, const JSValue &object)
+ : m_engine(engine), m_oldGlobalObject(engine->globalObject())
{
- QScriptEngine *engine = object.engine();
- m_oldGlobalObject = engine->globalObject();
engine->setGlobalObject(object);
}
TemporaryGlobalObjectSetter::~TemporaryGlobalObjectSetter()
{
- m_oldGlobalObject.engine()->setGlobalObject(m_oldGlobalObject);
+ m_engine->setGlobalObject(m_oldGlobalObject);
+}
+
+JsException::JsException(JsException &&other) noexcept
+ : m_ctx(other.m_ctx), m_exception(other.m_exception),
+ m_fallbackLocation(std::move(other.m_fallbackLocation))
+{
+ other.m_exception = JS_NULL;
+}
+
+JsException::~JsException() { JS_FreeValue(m_ctx, m_exception); }
+
+QString JsException::message() const
+{
+ if (JS_IsError(m_ctx, m_exception))
+ return getJsStringProperty(m_ctx, m_exception, QStringLiteral("message"));
+ const QVariant v = getJsVariant(m_ctx, m_exception);
+ switch (static_cast<QMetaType::Type>(v.userType())) {
+ case QMetaType::QVariantMap:
+ return QString::fromUtf8(QJsonDocument(QJsonObject::fromVariantMap(v.toMap()))
+ .toJson(QJsonDocument::Indented));
+ case QMetaType::QStringList:
+ return QString::fromUtf8(QJsonDocument(QJsonArray::fromStringList(v.toStringList()))
+ .toJson(QJsonDocument::Indented));
+ case QMetaType::QVariantList:
+ return QString::fromUtf8(QJsonDocument(QJsonArray::fromVariantList(v.toList()))
+ .toJson(QJsonDocument::Indented));
+ default:
+ return v.toString();
+ }
+}
+
+const QStringList JsException::stackTrace() const
+{
+ return getJsStringProperty(m_ctx, m_exception, QLatin1String("stack"))
+ .split(QLatin1Char('\n'), Qt::SkipEmptyParts);
+}
+
+ErrorInfo JsException::toErrorInfo() const
+{
+ const QString msg = message();
+ ErrorInfo e(msg, stackTrace());
+ if (e.hasLocation() || !m_fallbackLocation.isValid())
+ return e;
+ return {msg, m_fallbackLocation};
+}
+
+void defineJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, JSValue val)
+{
+ JS_DefinePropertyValueStr(ctx, obj, prop.toUtf8().constData(), val, 0);
+}
+
+JSValue getJsProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ return JS_GetPropertyStr(ctx, obj, prop.toUtf8().constData());
+}
+
+void setJsProperty(JSContext *ctx, JSValue obj, const QString &prop, JSValue val)
+{
+ if (JS_SetPropertyStr(ctx, obj, prop.toUtf8().constData(), val) <= 0)
+ qDebug() << "Oje!";
+}
+
+void setJsProperty(JSContext *ctx, JSValue obj, const QString &prop, const QString &val)
+{
+ setJsProperty(ctx, obj, prop, makeJsString(ctx, val));
+}
+
+void handleJsProperties(JSContext *ctx, JSValue obj,
+ const std::function<void (const JSAtom &,
+ const JSPropertyDescriptor &)> &handler)
+{
+ struct PropsHolder {
+ PropsHolder(JSContext *ctx) : ctx(ctx) {}
+ ~PropsHolder() {
+ for (uint32_t i = 0; i < count; ++i)
+ JS_FreeAtom(ctx, props[i].atom);
+ js_free(ctx, props);
+ }
+ JSContext * const ctx;
+ JSPropertyEnum *props = nullptr;
+ uint32_t count = 0;
+ } propsHolder(ctx);
+ JS_GetOwnPropertyNames(ctx, &propsHolder.props, &propsHolder.count, obj, JS_GPN_STRING_MASK);
+ for (uint32_t i = 0; i < propsHolder.count; ++i) {
+ JSPropertyDescriptor desc{0, JS_UNDEFINED, JS_UNDEFINED, JS_UNDEFINED};
+ JS_GetOwnProperty(ctx, &desc, obj, propsHolder.props[i].atom);
+ const ScopedJsValueList valueHolder(ctx, {desc.value, desc.getter, desc.setter});
+ handler(propsHolder.props[i].atom, desc);
+ }
+}
+
+QString getJsString(JSContext *ctx, JSValueConst val)
+{
+ size_t strLen;
+ const char * const str = JS_ToCStringLen(ctx, &strLen, val);
+ QString s = QString::fromUtf8(str, strLen);
+ JS_FreeCString(ctx, str);
+ return s;
+}
+
+QString getJsStringProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ const ScopedJsValue sv(ctx, getJsProperty(ctx, obj, prop));
+ return getJsString(ctx, sv);
+}
+
+int getJsIntProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ return JS_VALUE_GET_INT(getJsProperty(ctx, obj, prop));
+}
+
+bool getJsBoolProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ return JS_VALUE_GET_BOOL(getJsProperty(ctx, obj, prop));
+}
+
+JSValue makeJsArrayBuffer(JSContext *ctx, const QByteArray &s)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(s);
+}
+
+JSValue makeJsString(JSContext *ctx, const QString &s)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(s);
+}
+
+JSValue makeJsStringList(JSContext *ctx, const QStringList &l)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(l);
+}
+
+JSValue throwError(JSContext *ctx, const QString &message)
+{
+ return JS_Throw(ctx, makeJsString(ctx, message));
+}
+
+QStringList getJsStringList(JSContext *ctx, JSValue val)
+{
+ if (JS_IsString(val))
+ return {getJsString(ctx, val)};
+ if (!JS_IsArray(ctx, val))
+ return {};
+ const int size = getJsIntProperty(ctx, val, QLatin1String("length"));
+ QStringList l;
+ for (int i = 0; i < size; ++i) {
+ const ScopedJsValue elem(ctx, JS_GetPropertyUint32(ctx, val, i));
+ l << getJsString(ctx, elem);
+ }
+ return l;
+}
+
+JSValue makeJsVariant(JSContext *ctx, const QVariant &v, quintptr id)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(v, id);
+}
+
+JSValue makeJsVariantList(JSContext *ctx, const QVariantList &l, quintptr id)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(l, id);
+}
+
+JSValue makeJsVariantMap(JSContext *ctx, const QVariantMap &m, quintptr id)
+{
+ return ScriptEngine::engineForContext(ctx)->asJsValue(m, id);
+}
+
+static QVariant getJsVariantImpl(JSContext *ctx, JSValue val, QList<JSValue> path)
+{
+ if (JS_IsString(val))
+ return getJsString(ctx, val);
+ if (JS_IsBool(val))
+ return bool(JS_VALUE_GET_BOOL(val));
+ if (JS_IsArrayBuffer(val)) {
+ size_t size = 0;
+ const auto data = JS_GetArrayBuffer(ctx, &size, val);
+ if (!data || !size)
+ return QByteArray();
+ return QByteArray(reinterpret_cast<const char *>(data), size);
+ }
+ if (JS_IsArray(ctx, val)) {
+ if (path.contains(val))
+ return {};
+ path << val;
+ QVariantList l;
+ const int size = getJsIntProperty(ctx, val, QLatin1String("length"));
+ for (int i = 0; i < size; ++i) {
+ const ScopedJsValue sv(ctx, JS_GetPropertyUint32(ctx, val, i));
+ l << getJsVariantImpl(ctx, sv, path);
+ }
+ return l;
+ }
+ if (JS_IsDate(val)) {
+ ScopedJsValue toString(ctx, getJsProperty(ctx, val, QLatin1String("toISOString")));
+ if (!JS_IsFunction(ctx, toString))
+ return {};
+ ScopedJsValue dateString(ctx, JS_Call(ctx, toString, val, 0, nullptr));
+ if (!JS_IsString(dateString))
+ return {};
+ return QDateTime::fromString(getJsString(ctx, dateString), Qt::ISODateWithMs);
+ }
+ if (JS_IsObject(val)) {
+ if (path.contains(val))
+ return {};
+ path << val;
+ QVariantMap map;
+ handleJsProperties(ctx, val, [ctx, &map, path](const JSAtom &prop, const JSPropertyDescriptor &desc) {
+ map.insert(getJsString(ctx, prop), getJsVariantImpl(ctx, desc.value, path));
+ });
+ return map;
+ }
+ const auto tag = JS_VALUE_GET_TAG(val);
+ if (tag == JS_TAG_INT)
+ return JS_VALUE_GET_INT(val);
+ else if (JS_TAG_IS_FLOAT64(tag))
+ return JS_VALUE_GET_FLOAT64(val);
+ return {};
+}
+
+QVariant getJsVariant(JSContext *ctx, JSValue val)
+{
+ return getJsVariantImpl(ctx, val, {});
+}
+
+QStringList getJsStringListProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ JSValue array = getJsProperty(ctx, obj, prop);
+ QStringList list = getJsStringList(ctx, array);
+ JS_FreeValue(ctx, array);
+ return list;
+}
+
+QVariant getJsVariantProperty(JSContext *ctx, JSValue obj, const QString &prop)
+{
+ const JSValue vv = getJsProperty(ctx, obj, prop);
+ QVariant v = getJsVariant(ctx, vv);
+ JS_FreeValue(ctx, vv);
+ return v;
+}
+
+QString getJsString(JSContext *ctx, JSAtom atom)
+{
+ const ScopedJsValue atomString(ctx, JS_AtomToString(ctx, atom));
+ return getJsString(ctx, atomString);
}
} // namespace Internal
diff --git a/src/lib/corelib/tools/scripttools.h b/src/lib/corelib/tools/scripttools.h
index 4a258b98f..ac7ed9928 100644
--- a/src/lib/corelib/tools/scripttools.h
+++ b/src/lib/corelib/tools/scripttools.h
@@ -40,57 +40,172 @@
#ifndef QBS_SCRIPTTOOLS_H
#define QBS_SCRIPTTOOLS_H
-#include <tools/qbs_export.h>
+#include "codelocation.h"
+#include "porting.h"
+#include "qbs_export.h"
+#include <quickjs.h>
+
+#include <QtCore/qhash.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptprogram.h>
-#include <QtScript/qscriptvalue.h>
+#include <utility>
+#include <vector>
namespace qbs {
+class ErrorInfo;
namespace Internal {
+class ScriptEngine;
-template <typename C>
-QScriptValue toScriptValue(QScriptEngine *scriptEngine, const C &container)
-{
- QScriptValue v = scriptEngine->newArray(container.size());
- int i = 0;
- for (const typename C::value_type &item : container)
- v.setProperty(i++, scriptEngine->toScriptValue(item));
- return v;
-}
+using JSValueList = std::vector<JSValue>;
-void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value);
-QVariant QBS_AUTOTEST_EXPORT getConfigProperty(const QVariantMap &cfg, const QStringList &name);
+void defineJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, JSValue val);
+JSValue getJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+void setJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, JSValue val);
+void setJsProperty(JSContext *ctx, JSValueConst obj, const QString &prop, const QString &val);
+QString getJsStringProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+QStringList getJsStringListProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+int getJsIntProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+bool getJsBoolProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+QVariant getJsVariantProperty(JSContext *ctx, JSValueConst obj, const QString &prop);
+QString getJsString(JSContext *ctx, JSValueConst val);
+QString getJsString(JSContext *ctx, JSAtom atom);
+QBS_AUTOTEST_EXPORT QVariant getJsVariant(JSContext *ctx, JSValueConst val);
+JSValue makeJsArrayBuffer(JSContext *ctx, const QByteArray &s);
+JSValue makeJsString(JSContext *ctx, const QString &s);
+JSValue makeJsStringList(JSContext *ctx, const QStringList &l);
+JSValue makeJsVariant(JSContext *ctx, const QVariant &v, quintptr id = 0);
+JSValue makeJsVariantList(JSContext *ctx, const QVariantList &l, quintptr id = 0);
+JSValue makeJsVariantMap(JSContext *ctx, const QVariantMap &m, quintptr id = 0);
+QStringList getJsStringList(JSContext *ctx, JSValueConst val);
+JSValue throwError(JSContext *ctx, const QString &message);
+using PropertyHandler = std::function<void(const JSAtom &, const JSPropertyDescriptor &)>;
+void handleJsProperties(JSContext *ctx, JSValueConst obj, const PropertyHandler &handler);
+inline quintptr jsObjectId(const JSValue &val) { return quintptr(JS_VALUE_GET_OBJ(val)); }
template <class T>
-void attachPointerTo(QScriptValue &scriptValue, T *ptr)
+void attachPointerTo(JSValue &scriptValue, T *ptr)
{
- QVariant v;
- v.setValue<quintptr>(reinterpret_cast<quintptr>(ptr));
- scriptValue.setData(scriptValue.engine()->newVariant(v));
+ JS_SetOpaque(scriptValue, const_cast<std::remove_const_t<T> *>(ptr));
}
template <class T>
-T *attachedPointer(const QScriptValue &scriptValue)
+T *attachedPointer(const JSValue &scriptValue, JSClassID id)
{
- const auto ptr = scriptValue.data().toVariant().value<quintptr>();
- return reinterpret_cast<T *>(ptr);
+ return reinterpret_cast<T *>(JS_GetOpaque(scriptValue, id));
}
class TemporaryGlobalObjectSetter
{
public:
- TemporaryGlobalObjectSetter(const QScriptValue &object);
+ TemporaryGlobalObjectSetter(ScriptEngine *engine, const JSValue &object);
~TemporaryGlobalObjectSetter();
private:
- QScriptValue m_oldGlobalObject;
+ ScriptEngine * const m_engine;
+ const JSValue m_oldGlobalObject;
+};
+
+class ScopedJsValue
+{
+public:
+ ScopedJsValue(JSContext *ctx, JSValue v) : m_context(ctx), m_value(v) {}
+ void setValue(JSValue v) { JS_FreeValue(m_context, m_value); m_value = v; }
+ ~ScopedJsValue() { JS_FreeValue(m_context, m_value); }
+ operator JSValue() const { return m_value; }
+ JSValue release() { const JSValue v = m_value; m_value = JS_UNDEFINED; return v; }
+ void reset(JSValue v) { JS_FreeValue(m_context, m_value); m_value = v; }
+
+ ScopedJsValue(const ScopedJsValue &) = delete;
+ ScopedJsValue &operator=(const ScopedJsValue &) = delete;
+
+ ScopedJsValue(ScopedJsValue && other) : m_context(other.m_context), m_value(other.m_value)
+ {
+ other.m_value = JS_UNDEFINED;
+ }
+
+private:
+ JSContext * const m_context;
+ JSValue m_value;
};
+class ScopedJsValueList
+{
+public:
+ ScopedJsValueList(JSContext *ctx, const JSValueList &l) : m_context(ctx), m_list(l) {}
+ ~ScopedJsValueList() { for (const JSValue v : m_list) JS_FreeValue(m_context, v); }
+ operator JSValueList() const { return m_list; }
+
+ ScopedJsValueList(const ScopedJsValueList &) = delete;
+ ScopedJsValueList &operator=(const ScopedJsValueList &) = delete;
+
+ ScopedJsValueList(ScopedJsValueList && other) noexcept
+ : m_context(other.m_context), m_list(std::move(other.m_list))
+ {
+ other.m_list.clear();
+ }
+
+private:
+ JSContext * const m_context;
+ JSValueList m_list;
+};
+
+class ScopedJsAtom
+{
+public:
+ ScopedJsAtom(JSContext *ctx, JSAtom a) : m_context(ctx), m_atom(a) {}
+ ScopedJsAtom(JSContext *ctx, const QByteArray &s)
+ : ScopedJsAtom(ctx, JS_NewAtom(ctx, s.constData())) {}
+ ScopedJsAtom(JSContext *ctx, const QString &s) : ScopedJsAtom(ctx, s.toUtf8()) {}
+ ~ScopedJsAtom() { JS_FreeAtom(m_context, m_atom); }
+ operator JSAtom() const { return m_atom; }
+
+ ScopedJsAtom(const ScopedJsAtom &) = delete;
+ ScopedJsAtom&operator=(const ScopedJsAtom &) = delete;
+
+ ScopedJsAtom(ScopedJsAtom &&other) : m_context(other.m_context), m_atom(other.m_atom)
+ {
+ other.m_atom = 0;
+ }
+
+private:
+ JSContext * const m_context;
+ JSAtom m_atom;
+};
+
+class QBS_AUTOTEST_EXPORT JsException
+{
+public:
+ JsException(JSContext *ctx, JSValue ex, const CodeLocation &fallbackLocation)
+ : m_ctx(ctx), m_exception(ex), m_fallbackLocation(fallbackLocation) {}
+ JsException(JsException && other) noexcept;
+ ~JsException();
+ JsException(const JsException &) = delete;
+ JsException &operator=(const JsException &) = delete;
+
+ operator bool() const { return !JS_IsNull(m_exception); }
+ QString message() const;
+ const QStringList stackTrace() const;
+ ErrorInfo toErrorInfo() const;
+private:
+ JSContext *m_ctx;
+ JSValue m_exception;
+ CodeLocation m_fallbackLocation;
+};
+
+void setConfigProperty(QVariantMap &cfg, const QStringList &name, const QVariant &value);
+QVariant QBS_AUTOTEST_EXPORT getConfigProperty(const QVariantMap &cfg, const QStringList &name);
+
} // namespace Internal
} // namespace qbs
+// Only to be used for objects!
+#ifndef JS_NAN_BOXING
+inline bool operator==(JSValue v1, JSValue v2) { return v1.u.ptr == v2.u.ptr; }
+inline bool operator!=(JSValue v1, JSValue v2) { return !(v1 == v2); }
+inline bool operator<(JSValue v1, JSValue v2) { return v1.u.ptr < v2.u.ptr; }
+inline qbs::QHashValueType qHash(const JSValue &v) { return QT_PREPEND_NAMESPACE(qHash)(v.u.ptr); }
+#endif
+
#endif // QBS_SCRIPTTOOLS_H
diff --git a/src/lib/corelib/tools/set.h b/src/lib/corelib/tools/set.h
index d2af77d73..461175615 100644
--- a/src/lib/corelib/tools/set.h
+++ b/src/lib/corelib/tools/set.h
@@ -42,6 +42,7 @@
#include <tools/dynamictypecheck.h>
#include <tools/persistence.h>
+#include <tools/stlutils.h>
#ifdef QT_CORE_LIB
#include <QtCore/qstringlist.h>
@@ -100,6 +101,8 @@ public:
Set() = default;
Set(const std::initializer_list<T> &list);
+ template<typename InputIterator>
+ Set(InputIterator first, InputIterator last);
Set &unite(const Set &other);
Set &operator+=(const Set &other) { return unite(other); }
@@ -112,8 +115,8 @@ public:
Set &operator&=(const Set &other) { return intersect(other); }
Set &operator&=(const T &v) { return intersect(Set{ v }); }
- iterator find(const T &v) { return std::find(m_data.begin(), m_data.end(), v); }
- const_iterator find(const T &v) const { return std::find(m_data.cbegin(), m_data.cend(), v); }
+ iterator find(const T &v) { return binaryFind(m_data.begin(), m_data.end(), v); }
+ const_iterator find(const T &v) const { return binaryFind(m_data.cbegin(), m_data.cend(), v); }
std::pair<iterator, bool> insert(const T &v);
Set &operator+=(const T &v) { insert(v); return *this; }
Set &operator|=(const T &v) { return operator+=(v); }
@@ -143,15 +146,8 @@ public:
QStringList toStringList() const;
QString toString(const T& value) const { return value.toString(); }
QString toString() const;
-
- static Set<T> fromList(const QList<T> &list);
- QList<T> toList() const;
#endif
- static Set<T> fromStdVector(const std::vector<T> &vector);
- static Set<T> fromStdSet(const std::set<T> &set);
- std::set<T> toStdSet() const;
-
template<typename U> static Set<T> filtered(const Set<U> &s);
bool operator==(const Set &other) const { return m_data == other.m_data; }
@@ -177,6 +173,15 @@ template<typename T> Set<T>::Set(const std::initializer_list<T> &list) : m_data(
m_data.erase(last, m_data.end());
}
+template<typename T>
+template<typename InputIterator>
+Set<T>::Set(InputIterator first, InputIterator last)
+{
+ reserveIfForwardIterator(&m_data, first, last);
+ std::copy(first, last, std::back_inserter(m_data));
+ sort();
+}
+
template<typename T> Set<T> &Set<T>::intersect(const Set<T> &other)
{
auto it = begin();
@@ -287,11 +292,7 @@ template<typename T> void Set<T>::store(PersistentPool &pool) const
#ifdef QT_CORE_LIB
template<typename T> QStringList Set<T>::toStringList() const
{
- QStringList sl;
- sl.reserve(int(size()));
- std::transform(cbegin(), cend(), std::back_inserter(sl),
- [this](const T &e) { return toString(e); });
- return sl;
+ return transformed<QStringList>(*this, [this](const T &e) { return toString(e); });
}
template<typename T> QString Set<T>::toString() const
@@ -301,45 +302,8 @@ template<typename T> QString Set<T>::toString() const
template<> inline QString Set<QString>::toString(const QString &value) const { return value; }
-template<typename T> Set<T> Set<T>::fromList(const QList<T> &list)
-{
- Set<T> s;
- std::copy(list.cbegin(), list.cend(), std::back_inserter(s.m_data));
- s.sort();
- return s;
-}
-
-template<typename T> QList<T> Set<T>::toList() const
-{
- QList<T> list;
- std::copy(m_data.cbegin(), m_data.cend(), std::back_inserter(list));
- return list;
-}
#endif
-template<typename T> Set<T> Set<T>::fromStdVector(const std::vector<T> &vector)
-{
- Set<T> s;
- std::copy(vector.cbegin(), vector.cend(), std::back_inserter(s.m_data));
- s.sort();
- return s;
-}
-
-template<typename T> Set<T> Set<T>::fromStdSet(const std::set<T> &set)
-{
- Set<T> s;
- std::copy(set.cbegin(), set.cend(), std::back_inserter(s.m_data));
- return s;
-}
-
-template<typename T> std::set<T> Set<T>::toStdSet() const
-{
- std::set<T> set;
- for (auto it = cbegin(); it != cend(); ++it)
- set.insert(*it);
- return set;
-}
-
template<typename T>
typename Set<T>::iterator Set<T>::asMutableIterator(typename Set<T>::const_iterator cit)
{
@@ -349,8 +313,8 @@ typename Set<T>::iterator Set<T>::asMutableIterator(typename Set<T>::const_itera
template<typename T> template<typename U> Set<T> Set<T>::filtered(const Set<U> &s)
{
- static_assert(std::is_pointer<T>::value, "Set::filtered() assumes pointer types");
- static_assert(std::is_pointer<U>::value, "Set::filtered() assumes pointer types");
+ static_assert(std::is_pointer_v<T>, "Set::filtered() assumes pointer types");
+ static_assert(std::is_pointer_v<U>, "Set::filtered() assumes pointer types");
Set<T> filteredSet;
for (auto &u : s) {
if (hasDynamicType<std::remove_pointer_t<T>>(u))
diff --git a/src/lib/corelib/tools/settings.cpp b/src/lib/corelib/tools/settings.cpp
index 7312cd622..4a3d9d727 100644
--- a/src/lib/corelib/tools/settings.cpp
+++ b/src/lib/corelib/tools/settings.cpp
@@ -91,8 +91,8 @@ Settings::Settings(const QString &baseDir) : Settings(baseDir, systemSettingsBas
Settings::Settings(const QString &baseDir, const QString &systemBaseDir)
: m_settings(SettingsCreator(baseDir).getQSettings()),
- m_systemSettings(new QSettings(systemBaseDir + QStringLiteral("/qbs.conf"),
- QSettings::IniFormat)),
+ m_systemSettings(std::make_unique<QSettings>(systemBaseDir + QStringLiteral("/qbs.conf"),
+ QSettings::IniFormat)),
m_baseDir(baseDir)
{
// Actual qbs settings are stored transparently within a group, because QSettings
@@ -100,11 +100,7 @@ Settings::Settings(const QString &baseDir, const QString &systemBaseDir)
m_settings->beginGroup(QStringLiteral("org/qt-project/qbs"));
}
-Settings::~Settings()
-{
- delete m_settings;
- delete m_systemSettings;
-}
+Settings::~Settings() = default;
QVariant Settings::value(const QString &key, Scopes scopes, const QVariant &defaultValue) const
{
@@ -121,9 +117,9 @@ QVariant Settings::value(const QString &key, Scopes scopes, const QVariant &defa
}
if (!systemValue.isValid())
return userValue;
- if (static_cast<QMetaType::Type>(userValue.type()) == QMetaType::QStringList)
+ if (static_cast<QMetaType::Type>(userValue.userType()) == QMetaType::QStringList)
return userValue.toStringList() + systemValue.toStringList();
- if (static_cast<QMetaType::Type>(userValue.type()) == QMetaType::QVariantList)
+ if (static_cast<QMetaType::Type>(userValue.userType()) == QMetaType::QVariantList)
return userValue.toList() + systemValue.toList();
return userValue;
}
@@ -139,13 +135,22 @@ QStringList Settings::allKeys(Scopes scopes) const
return keys;
}
-QStringList Settings::directChildren(const QString &parentGroup, Scope scope) const
+QStringList Settings::directChildren(const QString &parentGroup, Scopes scopes) const
{
- QSettings * const settings = settingsForScope(scope);
- settings->beginGroup(internalRepresentation(parentGroup));
- QStringList children = settings->childGroups();
- children << settings->childKeys();
- settings->endGroup();
+ auto helper = [this, &parentGroup](const Scope scope) {
+ QSettings * const settings = settingsForScope(scope);
+ settings->beginGroup(internalRepresentation(parentGroup));
+ QStringList children = settings->childGroups();
+ children << settings->childKeys();
+ settings->endGroup();
+ return children;
+ };
+
+ QStringList children;
+ if (scopes & UserScope)
+ children += helper(UserScope);
+ if (scopes & SystemScope)
+ children += helper(SystemScope);
fixupKeys(children);
return children;
}
@@ -241,7 +246,7 @@ void Settings::fixupKeys(QStringList &keys) const
QSettings *Settings::settingsForScope(Settings::Scope scope) const
{
- return scope == UserScope ? m_settings : m_systemSettings;
+ return scope == UserScope ? m_settings.get() : m_systemSettings.get();
}
QSettings *Settings::targetForWriting() const
diff --git a/src/lib/corelib/tools/settings.h b/src/lib/corelib/tools/settings.h
index d4de08490..7c9c52c9e 100644
--- a/src/lib/corelib/tools/settings.h
+++ b/src/lib/corelib/tools/settings.h
@@ -43,11 +43,13 @@
#include "qbs_export.h"
#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
#include <QtCore/qvariant.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QSettings;
-class QStringList;
QT_END_NAMESPACE
namespace qbs {
@@ -68,7 +70,7 @@ public:
QVariant value(const QString &key, Scopes scopes,
const QVariant &defaultValue = QVariant()) const;
QStringList allKeys(Scopes scopes) const;
- QStringList directChildren(const QString &parentGroup, Scope scope) const; // Keys and groups.
+ QStringList directChildren(const QString &parentGroup, Scopes scopes) const; // Keys and groups.
QStringList allKeysWithPrefix(const QString &group, Scopes scopes) const;
void setValue(const QString &key, const QVariant &value);
void remove(const QString &key);
@@ -94,8 +96,8 @@ private:
QSettings *targetForWriting() const;
void checkForWriteError();
- QSettings * const m_settings;
- QSettings * const m_systemSettings;
+ const std::unique_ptr<QSettings> m_settings;
+ const std::unique_ptr<QSettings> m_systemSettings;
const QString m_baseDir;
Scope m_scopeForWriting = UserScope;
};
diff --git a/src/lib/corelib/tools/settingscreator.cpp b/src/lib/corelib/tools/settingscreator.cpp
index b7c727cd0..2cef0e5ce 100644
--- a/src/lib/corelib/tools/settingscreator.cpp
+++ b/src/lib/corelib/tools/settingscreator.cpp
@@ -53,23 +53,35 @@
namespace qbs {
namespace Internal {
+namespace {
+QString getBaseDir(QString baseDir) {
+ if (!baseDir.isEmpty())
+ return baseDir;
+
+ const char key[] = "QBS_SETTINGS_DIR";
+ if (qEnvironmentVariableIsSet(key))
+ return QLatin1String(qgetenv(key));
+
+ return {};
+}
+} // namespace
+
static QSettings::Format format()
{
return HostOsInfo::isWindowsHost() ? QSettings::IniFormat : QSettings::NativeFormat;
}
-
SettingsCreator::SettingsCreator(QString baseDir)
- : m_settingsBaseDir(std::move(baseDir))
+ : m_settingsBaseDir(getBaseDir(std::move(baseDir)))
, m_qbsVersion(Version::fromString(QLatin1String(QBS_VERSION)))
{
}
-QSettings *SettingsCreator::getQSettings()
+std::unique_ptr<QSettings> SettingsCreator::getQSettings()
{
createQSettings();
migrate();
- return m_settings.release();
+ return std::move(m_settings);
}
void SettingsCreator::migrate()
@@ -84,46 +96,15 @@ void SettingsCreator::migrate()
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()
+ if (QFileInfo::exists(oldSettingsFilePath)
&& (!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 = std::make_unique<QSettings>(m_newSettingsFilePath, format());
- const auto allKeys = m_settings->allKeys();
- for (const QString &key : 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;
- for (const QString &oldString : oldList) {
- QString newString = oldString;
- newList << newString.replace(oldProfilesDir, newProfilesDir);
- }
- if (newList != oldList)
- m_settings->setValue(key, newList);
- }
- }
}
void SettingsCreator::createQSettings()
diff --git a/src/lib/corelib/tools/settingscreator.h b/src/lib/corelib/tools/settingscreator.h
index 39da80a7f..ab491105c 100644
--- a/src/lib/corelib/tools/settingscreator.h
+++ b/src/lib/corelib/tools/settingscreator.h
@@ -58,7 +58,7 @@ class SettingsCreator
public:
SettingsCreator(QString baseDir);
- QSettings *getQSettings();
+ std::unique_ptr<QSettings> getQSettings();
private:
void migrate();
diff --git a/src/lib/corelib/tools/settingsmodel.cpp b/src/lib/corelib/tools/settingsmodel.cpp
index 7283e959c..4fa0e14da 100644
--- a/src/lib/corelib/tools/settingsmodel.cpp
+++ b/src/lib/corelib/tools/settingsmodel.cpp
@@ -42,6 +42,7 @@
#include <tools/profile.h>
#include <tools/qttools.h>
#include <tools/settings.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
#include <QtCore/qlist.h>
@@ -77,7 +78,7 @@ QString Node::uniqueChildName() const
bool unique;
do {
unique = true;
- for (const Node *childNode : qAsConst(children)) {
+ for (const Node *childNode : std::as_const(children)) {
if (childNode->name == newName) {
unique = false;
newName += QLatin1Char('_');
@@ -90,11 +91,9 @@ QString Node::uniqueChildName() const
bool Node::hasDirectChildWithName(const QString &name) const
{
- for (const Node * const child : qAsConst(children)) {
- if (child->name == name)
- return true;
- }
- return false;
+ return Internal::any_of(children, [&name](const auto &child){
+ return child->name == name;
+ });
}
} // namespace Internal
@@ -123,17 +122,15 @@ public:
};
SettingsModel::SettingsModel(const QString &settingsDir, Settings::Scope scope, QObject *parent)
- : QAbstractItemModel(parent), d(new SettingsModelPrivate)
+ : QAbstractItemModel(parent),
+ d(std::make_unique<SettingsModelPrivate>())
{
d->settings = std::make_unique<qbs::Settings>(settingsDir);
d->settings->setScopeForWriting(scope);
d->readSettings();
}
-SettingsModel::~SettingsModel()
-{
- delete d;
-}
+SettingsModel::~SettingsModel() = default;
void SettingsModel::reload()
{
@@ -284,10 +281,10 @@ bool SettingsModel::setData(const QModelIndex &index, const QVariant &value, int
const QString valueString = value.toString();
QString *toChange = nullptr;
if (index.column() == keyColumn() && !valueString.isEmpty()
- && !node->parent->hasDirectChildWithName(valueString)
- && !(node->parent->parent == &d->rootNode
- && node->parent->name == Internal::StringConstants::profilesSettingsKey()
- && valueString == Profile::fallbackName())) {
+ && !node->parent->hasDirectChildWithName(valueString)
+ && (node->parent->parent != &d->rootNode
+ || node->parent->name != Internal::StringConstants::profilesSettingsKey()
+ || valueString != Profile::fallbackName())) {
toChange = &node->name;
} else if (index.column() == valueColumn() && valueString != node->value) {
toChange = &node->value;
@@ -332,7 +329,7 @@ void SettingsModel::SettingsModelPrivate::readSettings()
addNodeFromSettings(&rootNode, topLevelKey);
for (QVariantMap::ConstIterator it = additionalProperties.constBegin();
it != additionalProperties.constEnd(); ++it) {
- const QStringList nameAsList = it.key().split(QLatin1Char('.'), QString::SkipEmptyParts);
+ const QStringList nameAsList = it.key().split(QLatin1Char('.'), Qt::SkipEmptyParts);
addNode(&rootNode, nameAsList.front(), nameAsList.mid(1), it.value());
}
dirty = false;
@@ -364,7 +361,7 @@ void SettingsModel::SettingsModelPrivate::addNode(qbs::Internal::Node *parentNod
const QString &currentNamePart, const QStringList &restOfName, const QVariant &value)
{
Node *currentNode = nullptr;
- for (Node * const n : qAsConst(parentNode->children)) {
+ for (Node * const n : std::as_const(parentNode->children)) {
if (n->name == currentNamePart) {
currentNode = n;
break;
@@ -388,7 +385,7 @@ void SettingsModel::SettingsModelPrivate::doSave(const Node *node, const QString
}
const QString newPrefix = prefix + node->name + QLatin1Char('.');
- for (const Node * const child : qAsConst(node->children))
+ for (const Node * const child : std::as_const(node->children))
doSave(child, newPrefix);
}
diff --git a/src/lib/corelib/tools/settingsmodel.h b/src/lib/corelib/tools/settingsmodel.h
index 1bd59737c..63651d300 100644
--- a/src/lib/corelib/tools/settingsmodel.h
+++ b/src/lib/corelib/tools/settingsmodel.h
@@ -46,6 +46,8 @@
#include <QtCore/qabstractitemmodel.h>
#include <QtCore/qvariant.h>
+#include <memory>
+
namespace qbs {
class QBS_EXPORT SettingsModel : public QAbstractItemModel
@@ -81,7 +83,7 @@ public:
private:
class SettingsModelPrivate;
- SettingsModelPrivate * const d;
+ const std::unique_ptr<SettingsModelPrivate> d;
};
} // namespace qbs
diff --git a/src/lib/corelib/tools/settingsrepresentation.cpp b/src/lib/corelib/tools/settingsrepresentation.cpp
index 256c60c0e..6dfd81beb 100644
--- a/src/lib/corelib/tools/settingsrepresentation.cpp
+++ b/src/lib/corelib/tools/settingsrepresentation.cpp
@@ -41,8 +41,8 @@
#include "jsliterals.h"
-#include <QtScript/qscriptengine.h>
-#include <QtScript/qscriptvalue.h>
+#include <language/scriptengine.h>
+#include <logging/logger.h>
namespace qbs {
@@ -54,11 +54,19 @@ QString settingsValueToRepresentation(const QVariant &value)
static QVariant variantFromString(const QString &str, bool &ok)
{
// ### use Qt5's JSON reader at some point.
- QScriptEngine engine;
- QScriptValue sv = engine.evaluate(QLatin1String("(function(){return ")
- + str + QLatin1String(";})()"));
- ok = !sv.isError();
- return sv.toVariant();
+ class DummyLogSink : public ILogSink {
+ void doPrintMessage(LoggerLevel, const QString &, const QString &) override { }
+ } logSink;
+ Internal::Logger logger(&logSink);
+
+ const auto engine = Internal::ScriptEngine::create(logger, {});
+ Internal::ScopedJsValue sv(
+ engine->context(),
+ engine->evaluate(Internal::JsValueOwner::Caller,
+ QLatin1String("(function(){return ") + str
+ + QLatin1String(";})()")));
+ ok = !engine->checkAndClearException({});
+ return Internal::getJsVariant(engine->context(), sv);
}
QVariant representationToSettingsValue(const QString &representation)
@@ -67,8 +75,8 @@ QVariant representationToSettingsValue(const QString &representation)
QVariant variant = variantFromString(representation, ok);
// We have no floating-point properties, so this is most likely intended to be a string.
- if (static_cast<QMetaType::Type>(variant.type()) == QMetaType::Float
- || static_cast<QMetaType::Type>(variant.type()) == QMetaType::Double) {
+ if (static_cast<QMetaType::Type>(variant.userType()) == QMetaType::Float
+ || static_cast<QMetaType::Type>(variant.userType()) == QMetaType::Double) {
variant = variantFromString(QLatin1Char('"') + representation + QLatin1Char('"'), ok);
}
diff --git a/src/lib/corelib/tools/setupprojectparameters.cpp b/src/lib/corelib/tools/setupprojectparameters.cpp
index 996f6b273..e9212c165 100644
--- a/src/lib/corelib/tools/setupprojectparameters.cpp
+++ b/src/lib/corelib/tools/setupprojectparameters.cpp
@@ -38,6 +38,8 @@
****************************************************************************/
#include "setupprojectparameters.h"
+#include "buildoptions.h"
+
#include <logging/logger.h>
#include <logging/translator.h>
#include <tools/buildgraphlocker.h>
@@ -45,8 +47,10 @@
#include <tools/jsonhelper.h>
#include <tools/profile.h>
#include <tools/qbsassert.h>
+#include <tools/qttools.h>
#include <tools/scripttools.h>
#include <tools/settings.h>
+#include <tools/stringconstants.h>
#include <QtCore/qdir.h>
#include <QtCore/qfileinfo.h>
@@ -89,21 +93,24 @@ public:
mutable QVariantMap buildConfigurationTree;
mutable QVariantMap overriddenValuesTree;
mutable QVariantMap finalBuildConfigTree;
+ int maxJobCount = 0;
bool overrideBuildGraphData;
bool dryRun;
bool logElapsedTime;
bool forceProbeExecution;
bool waitLockBuildGraph;
- bool fallbackProviderEnabled = true;
SetupProjectParameters::RestoreBehavior restoreBehavior;
ErrorHandlingMode propertyCheckingMode;
ErrorHandlingMode productErrorMode;
+ DeprecationWarningMode deprecationWarningMode = defaultDeprecationWarningMode();
QProcessEnvironment environment;
};
} // namespace Internal
-SetupProjectParameters::SetupProjectParameters() : d(new Internal::SetupProjectParametersPrivate)
+using namespace Internal;
+
+SetupProjectParameters::SetupProjectParameters() : d(new SetupProjectParametersPrivate)
{
}
@@ -125,6 +132,11 @@ template<> ErrorHandlingMode fromJson(const QJsonValue &v)
return ErrorHandlingMode::Strict;
}
+template<> DeprecationWarningMode fromJson(const QJsonValue &v)
+{
+ return deprecationWarningModeFromName(v.toString());
+}
+
template<> SetupProjectParameters::RestoreBehavior fromJson(const QJsonValue &v)
{
const QString value = v.toString();
@@ -145,15 +157,18 @@ SetupProjectParameters SetupProjectParameters::fromJson(const QJsonObject &data)
setValueFromJson(params.d->projectFilePath, data, "project-file-path");
setValueFromJson(params.d->buildRoot, data, "build-root");
setValueFromJson(params.d->settingsBaseDir, data, "settings-directory");
+ setValueFromJson(params.d->maxJobCount, data, "max-job-count");
+ if (params.maxJobCount() <= 0)
+ params.setMaxJobCount(BuildOptions::defaultMaxJobCount());
setValueFromJson(params.d->overriddenValues, data, "overridden-properties");
setValueFromJson(params.d->dryRun, data, "dry-run");
setValueFromJson(params.d->logElapsedTime, data, "log-time");
setValueFromJson(params.d->forceProbeExecution, data, "force-probe-execution");
setValueFromJson(params.d->waitLockBuildGraph, data, "wait-lock-build-graph");
- setValueFromJson(params.d->fallbackProviderEnabled, data, "fallback-provider-enabled");
setValueFromJson(params.d->environment, data, "environment");
setValueFromJson(params.d->restoreBehavior, data, "restore-behavior");
setValueFromJson(params.d->propertyCheckingMode, data, "error-handling-mode");
+ setValueFromJson(params.d->deprecationWarningMode, data, "deprecation-warning-mode");
params.d->productErrorMode = params.d->propertyCheckingMode;
return params;
}
@@ -219,6 +234,44 @@ void SetupProjectParameters::setProjectFilePath(const QString &projectFilePath)
d->projectFilePath = canonicalProjectFilePath;
}
+void SetupProjectParameters::finalizeProjectFilePath()
+{
+ QString filePath = projectFilePath();
+ if (filePath.isEmpty())
+ filePath = QDir::currentPath();
+ const QFileInfo projectFileInfo(filePath);
+ if (!projectFileInfo.exists())
+ throw ErrorInfo(Tr::tr("Project file '%1' cannot be found.").arg(filePath));
+ if (projectFileInfo.isRelative())
+ filePath = projectFileInfo.absoluteFilePath();
+ if (projectFileInfo.isFile()) {
+ setProjectFilePath(filePath);
+ return;
+ }
+ if (!projectFileInfo.isDir())
+ throw ErrorInfo(Tr::tr("Project file '%1' has invalid type.").arg(filePath));
+
+ const QStringList &actualFileNames
+ = QDir(filePath).entryList(StringConstants::qbsFileWildcards(), QDir::Files);
+ if (actualFileNames.empty()) {
+ QString error;
+ if (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(filePath);
+ throw ErrorInfo(error);
+ }
+ if (actualFileNames.size() > 1) {
+ throw ErrorInfo(Tr::tr("More than one project file found in directory '%1'.")
+ .arg(filePath));
+ }
+ filePath.append(QLatin1Char('/')).append(actualFileNames.front());
+
+ filePath = QDir::current().filePath(filePath);
+ filePath = QDir::cleanPath(filePath);
+ setProjectFilePath(filePath);
+}
+
/*!
* \brief Returns the base path of where to put the build artifacts and store the build graph.
*/
@@ -243,7 +296,7 @@ void SetupProjectParameters::setBuildRoot(const QString &buildRoot)
// Calling mkpath() may be necessary to get the canonical build root, but if we do it,
// it must be reverted immediately afterwards as not to create directories needlessly,
// e.g in the case of a dry run build.
- Internal::DirectoryManager dirManager(buildRoot, Internal::Logger());
+ DirectoryManager dirManager(buildRoot, Logger());
// We don't do error checking here, as this is not a convenient place to report an error.
// If creation of the build directory is not possible, we will get sensible error messages
@@ -325,6 +378,27 @@ void SetupProjectParameters::setSettingsDirectory(const QString &settingsBaseDir
}
/*!
+ * \brief Returns the maximum number of threads to employ when resolving the project.
+ * If the value is not valid (i.e. <= 0), a sensible one will be derived from the number of
+ * available processor cores.
+ * The default is 0.
+ * \sa BuildOptions::defaultMaxJobCount
+ */
+int SetupProjectParameters::maxJobCount() const
+{
+ return d->maxJobCount;
+}
+
+/*!
+ * \brief Controls how many threads to employ when resolving the project.
+ * A value <= 0 leaves the decision to qbs.
+ */
+void SetupProjectParameters::setMaxJobCount(int jobCount)
+{
+ d->maxJobCount = jobCount;
+}
+
+/*!
* Returns the overridden values of the build configuration.
*/
QVariantMap SetupProjectParameters::overriddenValues() const
@@ -349,12 +423,12 @@ static void provideValuesTree(const QVariantMap &values, QVariantMap *valueTree)
valueTree->clear();
for (QVariantMap::const_iterator it = values.constBegin(); it != values.constEnd(); ++it) {
- const QString name = it.key();
+ 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());
+ setConfigProperty(*valueTree, nameElements, it.value());
}
}
@@ -395,7 +469,7 @@ static QVariantMap expandedBuildConfigurationInternal(const Profile &profile,
if (err.hasError())
throw err;
if (profileKeys.empty())
- throw ErrorInfo(Internal::Tr::tr("Unknown or empty profile '%1'.").arg(profile.name()));
+ throw ErrorInfo(Tr::tr("Unknown or empty profile '%1'.").arg(profile.name()));
for (const QString &profileKey : profileKeys) {
buildConfig.insert(profileKey, profile.value(profileKey, QVariant(), &err));
if (err.hasError())
@@ -405,7 +479,7 @@ static QVariantMap expandedBuildConfigurationInternal(const Profile &profile,
// (2) Build configuration name.
if (configurationName.isEmpty())
- throw ErrorInfo(Internal::Tr::tr("No build configuration name set."));
+ throw ErrorInfo(Tr::tr("No build configuration name set."));
buildConfig.insert(QStringLiteral("qbs.configurationName"), configurationName);
return buildConfig;
}
@@ -442,7 +516,7 @@ ErrorInfo SetupProjectParameters::expandBuildConfiguration()
QVariantMap expandedConfig = expandedBuildConfiguration(profile, configurationName(), &err);
if (err.hasError())
return err;
- if (d->buildConfiguration != expandedConfig) {
+ if (!qVariantMapsEqual(d->buildConfiguration, expandedConfig)) {
d->buildConfigurationTree.clear();
d->buildConfiguration = expandedConfig;
}
@@ -547,22 +621,6 @@ void SetupProjectParameters::setWaitLockBuildGraph(bool wait)
}
/*!
- * \brief Returns true if qbs should fall back to pkg-config if a dependency is not found.
- */
-bool SetupProjectParameters::fallbackProviderEnabled() const
-{
- return d->fallbackProviderEnabled;
-}
-
-/*!
- * Controls whether to fall back to pkg-config if a dependency is not found.
- */
-void SetupProjectParameters::setFallbackProviderEnabled(bool enable)
-{
- d->fallbackProviderEnabled = enable;
-}
-
-/*!
* \brief Gets the environment used while resolving the project.
*/
QProcessEnvironment SetupProjectParameters::environment() const
@@ -688,4 +746,20 @@ void SetupProjectParameters::setProductErrorMode(ErrorHandlingMode mode)
d->productErrorMode = mode;
}
+/*!
+ * \brief Indicates how deprecated constructs are handled.
+ */
+DeprecationWarningMode SetupProjectParameters::deprecationWarningMode() const
+{
+ return d->deprecationWarningMode;
+}
+
+/*!
+ * \brief Specifies the behavior on encountering deprecated constructs.
+ */
+void SetupProjectParameters::setDeprecationWarningMode(DeprecationWarningMode mode)
+{
+ d->deprecationWarningMode = mode;
+}
+
} // namespace qbs
diff --git a/src/lib/corelib/tools/setupprojectparameters.h b/src/lib/corelib/tools/setupprojectparameters.h
index a4d090ec5..5cd9700a9 100644
--- a/src/lib/corelib/tools/setupprojectparameters.h
+++ b/src/lib/corelib/tools/setupprojectparameters.h
@@ -41,13 +41,14 @@
#include "qbs_export.h"
+#include <tools/deprecationwarningmode.h>
#include <tools/error.h>
#include <QtCore/qshareddata.h>
+#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
class QProcessEnvironment;
-class QStringList;
using QVariantMap = QMap<QString, QVariant>;
QT_END_NAMESPACE
@@ -81,6 +82,7 @@ public:
QString projectFilePath() const;
void setProjectFilePath(const QString &projectFilePath);
+ void finalizeProjectFilePath();
QString buildRoot() const;
void setBuildRoot(const QString &buildRoot);
@@ -97,6 +99,9 @@ public:
QString settingsDirectory() const;
void setSettingsDirectory(const QString &settingsBaseDir);
+ int maxJobCount() const;
+ void setMaxJobCount(int jobCount);
+
QVariantMap overriddenValues() const;
void setOverriddenValues(const QVariantMap &values);
QVariantMap overriddenValuesTree() const;
@@ -127,9 +132,6 @@ public:
bool waitLockBuildGraph() const;
void setWaitLockBuildGraph(bool wait);
- bool fallbackProviderEnabled() const;
- void setFallbackProviderEnabled(bool enable);
-
QProcessEnvironment environment() const;
void setEnvironment(const QProcessEnvironment &env);
QProcessEnvironment adjustedEnvironment() const;
@@ -144,6 +146,9 @@ public:
ErrorHandlingMode productErrorMode() const;
void setProductErrorMode(ErrorHandlingMode mode);
+ DeprecationWarningMode deprecationWarningMode() const;
+ void setDeprecationWarningMode(DeprecationWarningMode mode);
+
private:
QSharedDataPointer<Internal::SetupProjectParametersPrivate> d;
};
diff --git a/src/lib/corelib/tools/shellutils.cpp b/src/lib/corelib/tools/shellutils.cpp
index dae98f337..5fff254f6 100644
--- a/src/lib/corelib/tools/shellutils.cpp
+++ b/src/lib/corelib/tools/shellutils.cpp
@@ -39,9 +39,12 @@
****************************************************************************/
#include "shellutils.h"
+
#include "pathutils.h"
+#include "qttools.h"
+
#include <QtCore/qfile.h>
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qtextstream.h>
namespace qbs {
@@ -53,8 +56,8 @@ QString shellInterpreter(const QString &filePath) {
QTextStream ts(&file);
const QString shebang = ts.readLine();
if (shebang.startsWith(QLatin1String("#!"))) {
- return (shebang.mid(2).split(QRegExp(QStringLiteral("\\s")),
- QString::SkipEmptyParts) << QString()).front();
+ return (shebang.mid(2).split(QRegularExpression(QStringLiteral("\\s")),
+ Qt::SkipEmptyParts) << QString()).front();
}
}
@@ -66,9 +69,7 @@ QString shellInterpreter(const QString &filePath) {
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;
+ return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
}
inline static bool hasSpecialChars(const QString &arg, const uchar (&iqm)[16])
@@ -124,9 +125,9 @@ static QString shellQuoteWin(const QString &arg)
// 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\\\""));
+ ret.replace(QRegularExpression(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"));
+ ret.replace(QRegularExpression(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
diff --git a/src/lib/corelib/tools/shellutils.h b/src/lib/corelib/tools/shellutils.h
index 6f1d82afb..f4ad35044 100644
--- a/src/lib/corelib/tools/shellutils.h
+++ b/src/lib/corelib/tools/shellutils.h
@@ -77,7 +77,7 @@ public:
private:
struct Argument
{
- Argument(const QString &value = QString()) : value(value) { }
+ Argument(QString value = QString()) : value(std::move(value)) { }
QString value;
bool isFilePath = false;
bool shouldQuote = true;
diff --git a/src/lib/corelib/tools/stlutils.h b/src/lib/corelib/tools/stlutils.h
index ad00070cf..306f37157 100644
--- a/src/lib/corelib/tools/stlutils.h
+++ b/src/lib/corelib/tools/stlutils.h
@@ -46,19 +46,58 @@
namespace qbs {
namespace Internal {
-template <class C>
-C sorted(const C &container)
+template <typename C>
+auto sorted(C &&container)
{
- C result = container;
+ using R = std::remove_cv_t<std::remove_reference_t<C>>;
+ R result(std::forward<C>(container));
std::sort(std::begin(result), std::end(result));
return result;
}
-template <class C>
-bool contains(const C &container, const typename C::value_type &v)
+template <typename C, typename Pred>
+auto sorted(C &&container, Pred &&pred)
{
- const auto &end = container.cend();
- return std::find(container.cbegin(), end, v) != end;
+ using R = std::remove_cv_t<std::remove_reference_t<C>>;
+ R result(std::forward<C>(container));
+ std::sort(std::begin(result), std::end(result), std::forward<Pred>(pred));
+ return result;
+}
+
+template <typename To, typename From, typename Op>
+To transformed(const From &from, Op op)
+{
+ To to;
+ to.reserve(int(from.size()));
+ std::transform(std::cbegin(from), std::cend(from), std::back_inserter(to), std::move(op));
+ return to;
+}
+
+template <typename C, typename Op>
+void transform(C &&container, Op op)
+{
+ std::transform(std::begin(container), std::end(container), std::begin(container),
+ std::move(op));
+}
+
+template <typename To, typename From, typename Op>
+void transform(const From &from, To &&to, Op op)
+{
+ std::transform(std::cbegin(from), std::cend(from), std::back_inserter(to), std::move(op));
+}
+
+template <class C, class T>
+bool contains(const C &container, const T &v)
+{
+ const auto &end = std::cend(container);
+ return std::find(std::cbegin(container), end, v) != end;
+}
+
+template <class T, size_t N, class U>
+bool contains(const T (&container)[N], const U &v)
+{
+ const auto &end = std::cend(container);
+ return std::find(std::cbegin(container), end, v) != end;
}
template <class C>
@@ -68,6 +107,17 @@ bool containsKey(const C &container, const typename C::key_type &v)
return container.find(v) != end;
}
+template <class C>
+typename C::mapped_type mapValue(
+ const C &container,
+ const typename C::key_type &key,
+ const typename C::mapped_type &value = typename C::mapped_type())
+{
+ const auto end = container.cend();
+ const auto it = container.find(key);
+ return it != end ? it->second : value;
+}
+
template <typename C>
bool removeOne(C &container, const typename C::value_type &v)
{
@@ -86,6 +136,13 @@ void removeAll(C &container, const typename C::value_type &v)
std::end(container));
}
+template <typename C, typename Pred>
+void removeIf(C &container, const Pred &pred)
+{
+ container.erase(std::remove_if(std::begin(container), std::end(container), pred),
+ std::end(container));
+}
+
template <class Container, class UnaryPredicate>
bool any_of(const Container &container, const UnaryPredicate &predicate)
{
@@ -93,11 +150,32 @@ bool any_of(const Container &container, const UnaryPredicate &predicate)
}
template <class Container, class UnaryPredicate>
+bool all_of(const Container &container, const UnaryPredicate &predicate)
+{
+ return std::all_of(std::begin(container), std::end(container), predicate);
+}
+
+template <class Container, class UnaryPredicate>
bool none_of(const Container &container, const UnaryPredicate &predicate)
{
return std::none_of(std::begin(container), std::end(container), predicate);
}
+template <class It, class T, class Compare>
+It binaryFind(It begin, It end, const T &value, Compare comp)
+{
+ const auto it = std::lower_bound(begin, end, value, comp);
+ if (it == end || comp(value, *it))
+ return end;
+ return it;
+}
+
+template <class It, class T>
+It binaryFind(It begin, It end, const T &value)
+{
+ return binaryFind(begin, end, value, std::less<T>());
+}
+
template <class C>
C &operator<<(C &container, const typename C::value_type &v)
{
@@ -112,6 +190,79 @@ C &operator<<(C &container, const C &other)
return container;
}
+// based on http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0814r0.pdf
+template<typename T>
+void hashCombineHelper(size_t &seed, const T &val)
+{
+ seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+}
+
+template<typename... Types>
+size_t hashCombine(const Types &... args)
+{
+ size_t seed = 0;
+ (hashCombineHelper(seed, args), ...); // create hash value with seed over all args return seed;
+ return seed;
+}
+
+template<typename It>
+size_t hashRange(It first, It last)
+{
+ size_t seed = 0;
+ for (; first != last; ++first)
+ hashCombineHelper(seed, *first);
+
+ return seed;
+}
+
+template<typename R>
+size_t hashRange(R &&range)
+{
+ return hashRange(std::begin(range), std::end(range));
+}
+
+// based on qcontainertools_impl.h
+template <typename Iterator>
+using IfIsForwardIterator_t = typename std::enable_if_t<
+ std::is_convertible_v<
+ typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>,
+ bool>;
+
+template <typename Iterator>
+using IfIsNotForwardIterator = typename std::enable_if_t<
+ !std::is_convertible_v<
+ typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>,
+ bool>;
+
+template <typename Container,
+ typename InputIterator,
+ IfIsNotForwardIterator<InputIterator> = true>
+void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
+{
+}
+
+template <typename Container,
+ typename ForwardIterator,
+ IfIsForwardIterator_t<ForwardIterator> = true>
+void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l)
+{
+ c->reserve(static_cast<typename Container::size_type>(std::distance(f, l)));
+}
+
+// similar to ranges::to proposal
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1206r1.pdf
+template <class C, class R>
+C rangeTo(R &&r)
+{
+ return C(std::begin(r), std::end(r));
+}
+
+template<class Enum>
+constexpr std::underlying_type_t<Enum> toUnderlying(Enum e) noexcept
+{
+ return static_cast<std::underlying_type_t<Enum>>(e);
+}
+
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/tools/stringconstants.h b/src/lib/corelib/tools/stringconstants.h
index 79cbcd125..799a140d9 100644
--- a/src/lib/corelib/tools/stringconstants.h
+++ b/src/lib/corelib/tools/stringconstants.h
@@ -110,6 +110,7 @@ public:
QBS_STRING_CONSTANT(installDirProperty, "installDir")
QBS_STRING_CONSTANT(installSourceBaseProperty, "installSourceBase")
QBS_STRING_CONSTANT(isEnabledKey, "is-enabled")
+ QBS_STRING_CONSTANT(isEagerProperty, "isEager")
QBS_STRING_CONSTANT(jobCountProperty, "jobCount")
QBS_STRING_CONSTANT(jobPoolProperty, "jobPool")
QBS_STRING_CONSTANT(lengthProperty, "length")
@@ -143,6 +144,7 @@ public:
static const QString &profilesProperty() { return profiles(); }
QBS_STRING_CONSTANT(productTypesProperty, "productTypes")
QBS_STRING_CONSTANT(productsKey, "products")
+ QBS_STRING_CONSTANT(qbsModuleProviders, "qbsModuleProviders")
QBS_STRING_CONSTANT(qbsSearchPathsProperty, "qbsSearchPaths")
QBS_STRING_CONSTANT(referencesProperty, "references")
QBS_STRING_CONSTANT(recursiveProperty, "recursive")
@@ -153,6 +155,7 @@ public:
QBS_STRING_CONSTANT(searchPathsProperty, "searchPaths")
QBS_STRING_CONSTANT(setupBuildEnvironmentProperty, "setupBuildEnvironment")
QBS_STRING_CONSTANT(setupRunEnvironmentProperty, "setupRunEnvironment")
+ QBS_STRING_CONSTANT(shadowProductPrefix, "__shadow__")
QBS_STRING_CONSTANT(sourceCodeProperty, "sourceCode")
QBS_STRING_CONSTANT(sourceDirectoryProperty, "sourceDirectory")
QBS_STRING_CONSTANT(submodulesProperty, "submodules")
@@ -166,6 +169,7 @@ public:
QBS_STRING_CONSTANT(importScopeNamePropertyInternal, "_qbs_importScopeName")
QBS_STRING_CONSTANT(modulePropertyInternal, "__module")
+ QBS_STRING_CONSTANT(dataPropertyInternal, "_qbs_data")
QBS_STRING_CONSTANT(qbsSourceDirPropertyInternal, "_qbs_sourceDir")
static const char *qbsProcEnvVarInternal() { return "_qbs_procenv"; }
diff --git a/src/lib/corelib/tools/toolchains.cpp b/src/lib/corelib/tools/toolchains.cpp
index 0d793f8aa..6263fb199 100644
--- a/src/lib/corelib/tools/toolchains.cpp
+++ b/src/lib/corelib/tools/toolchains.cpp
@@ -49,9 +49,11 @@ namespace qbs {
namespace Internal {
static const QString clangToolchain() { return QStringLiteral("clang"); }
+static const QString clangClToolchain() { return QStringLiteral("clang-cl"); }
static const QString gccToolchain() { return QStringLiteral("gcc"); }
static const QString llvmToolchain() { return QStringLiteral("llvm"); }
static const QString mingwToolchain() { return QStringLiteral("mingw"); }
+static const QString msvcToolchain() { return QStringLiteral("msvc"); }
}
using namespace Internal;
@@ -64,7 +66,8 @@ QStringList canonicalToolchain(const QStringList &toolchain)
llvmToolchain(),
mingwToolchain(),
gccToolchain(),
- QStringLiteral("msvc")
+ clangClToolchain(),
+ msvcToolchain()
};
// Canonicalize each toolchain in the toolchain list,
@@ -110,6 +113,8 @@ QStringList canonicalToolchain(const QString &name)
else if (toolchainName == llvmToolchain() ||
toolchainName == mingwToolchain()) {
toolchains << canonicalToolchain(QStringLiteral("gcc"));
+ } else if (toolchainName == clangClToolchain()) {
+ toolchains << canonicalToolchain(msvcToolchain());
}
return toolchains;
}
diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri
deleted file mode 100644
index 89d752671..000000000
--- a/src/lib/corelib/tools/tools.pri
+++ /dev/null
@@ -1,144 +0,0 @@
-include(../../../install_prefix.pri)
-
-INCLUDEPATH += $$PWD/../.. # for plugins
-
-QBS_SYSTEM_SETTINGS_DIR = $$(QBS_SYSTEM_SETTINGS_DIR)
-!isEmpty(QBS_SYSTEM_SETTINGS_DIR) {
- DEFINES += QBS_SYSTEM_SETTINGS_DIR=\\\"$$QBS_SYSTEM_SETTINGS_DIR\\\"
-}
-
-HEADERS += \
- $$PWD/architectures.h \
- $$PWD/buildgraphlocker.h \
- $$PWD/codelocation.h \
- $$PWD/commandechomode.h \
- $$PWD/dynamictypecheck.h \
- $$PWD/error.h \
- $$PWD/executablefinder.h \
- $$PWD/fileinfo.h \
- $$PWD/filesaver.h \
- $$PWD/filetime.h \
- $$PWD/generateoptions.h \
- $$PWD/id.h \
- $$PWD/iosutils.h \
- $$PWD/joblimits.h \
- $$PWD/jsliterals.h \
- $$PWD/jsonhelper.h \
- $$PWD/launcherinterface.h \
- $$PWD/launcherpackets.h \
- $$PWD/launchersocket.h \
- $$PWD/msvcinfo.h \
- $$PWD/persistence.h \
- $$PWD/scannerpluginmanager.h \
- $$PWD/scripttools.h \
- $$PWD/set.h \
- $$PWD/settings.h \
- $$PWD/settingsmodel.h \
- $$PWD/settingsrepresentation.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/qbspluginmanager.h \
- $$PWD/qbsprocess.h \
- $$PWD/shellutils.h \
- $$PWD/stlutils.h \
- $$PWD/stringutils.h \
- $$PWD/toolchains.h \
- $$PWD/hostosinfo.h \
- $$PWD/buildoptions.h \
- $$PWD/installoptions.h \
- $$PWD/cleanoptions.h \
- $$PWD/setupprojectparameters.h \
- $$PWD/weakpointer.h \
- $$PWD/qbs_export.h \
- $$PWD/qbsassert.h \
- $$PWD/qttools.h \
- $$PWD/settingscreator.h \
- $$PWD/stringconstants.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/filetime.cpp \
- $$PWD/generateoptions.cpp \
- $$PWD/id.cpp \
- $$PWD/joblimits.cpp \
- $$PWD/jsliterals.cpp \
- $$PWD/launcherinterface.cpp \
- $$PWD/launcherpackets.cpp \
- $$PWD/launchersocket.cpp \
- $$PWD/msvcinfo.cpp \
- $$PWD/persistence.cpp \
- $$PWD/scannerpluginmanager.cpp \
- $$PWD/scripttools.cpp \
- $$PWD/settings.cpp \
- $$PWD/settingsmodel.cpp \
- $$PWD/settingsrepresentation.cpp \
- $$PWD/preferences.cpp \
- $$PWD/processresult.cpp \
- $$PWD/processutils.cpp \
- $$PWD/profile.cpp \
- $$PWD/profiling.cpp \
- $$PWD/progressobserver.cpp \
- $$PWD/projectgeneratormanager.cpp \
- $$PWD/qbspluginmanager.cpp \
- $$PWD/qbsprocess.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
-}
-
-!qbs_no_dev_install {
- tools_headers.files = \
- $$PWD/architectures.h \
- $$PWD/buildoptions.h \
- $$PWD/cleanoptions.h \
- $$PWD/codelocation.h \
- $$PWD/commandechomode.h \
- $$PWD/error.h \
- $$PWD/generateoptions.h \
- $$PWD/installoptions.h \
- $$PWD/joblimits.h \
- $$PWD/preferences.h \
- $$PWD/processresult.h \
- $$PWD/profile.h \
- $$PWD/projectgeneratormanager.h \
- $$PWD/qbs_export.h \
- $$PWD/settings.h \
- $$PWD/settingsmodel.h \
- $$PWD/settingsrepresentation.h \
- $$PWD/setupprojectparameters.h \
- $$PWD/toolchains.h \
- $$PWD/version.h
- tools_headers.path = $${QBS_INSTALL_PREFIX}/include/qbs/tools
- INSTALLS += tools_headers
-}
diff --git a/src/lib/corelib/tools/version.cpp b/src/lib/corelib/tools/version.cpp
index f653256b3..719bc386f 100644
--- a/src/lib/corelib/tools/version.cpp
+++ b/src/lib/corelib/tools/version.cpp
@@ -39,7 +39,7 @@
#include "version.h"
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qstring.h>
namespace qbs {
@@ -51,13 +51,14 @@ Version Version::fromString(const QString &versionString, bool buildNumberAllowe
pattern += QStringLiteral("(?:\\.(\\d+))?"); // Followed by a dot and a number up to two times.
if (buildNumberAllowed)
pattern += QStringLiteral("(?:[-.](\\d+))?"); // And possibly a dash or dot followed by the build number.
- QRegExp rex(pattern);
- if (!rex.exactMatch(versionString))
+ const QRegularExpression rex(QRegularExpression::anchoredPattern(pattern));
+ const QRegularExpressionMatch match = rex.match(versionString);
+ if (!match.hasMatch())
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;
+ const int majorNr = match.captured(1).toInt();
+ const int minorNr = match.lastCapturedIndex() >= 2 ? match.captured(2).toInt() : 0;
+ const int patchNr = match.lastCapturedIndex() >= 3 ? match.captured(3).toInt() : 0;
+ const int buildNr = match.lastCapturedIndex() >= 4 ? match.captured(4).toInt() : 0;
return Version{majorNr, minorNr, patchNr, buildNr};
}
diff --git a/src/lib/corelib/tools/version.h b/src/lib/corelib/tools/version.h
index 63ad3f88c..7b2d23ebb 100644
--- a/src/lib/corelib/tools/version.h
+++ b/src/lib/corelib/tools/version.h
@@ -51,7 +51,7 @@ QT_END_NAMESPACE
namespace qbs {
-class QBS_EXPORT Version
+class Version
{
public:
constexpr explicit Version(int majorVersion = 0, int minorVersion = 0, int patchLevel = 0,
@@ -73,9 +73,9 @@ public:
constexpr int buildNumber() const { return m_build; }
constexpr void setBuildNumber(int nr) { m_build = nr; }
- static Version fromString(const QString &versionString, bool buildNumberAllowed = false);
- QString toString(const QChar &separator = QLatin1Char('.'),
- const QChar &buildSeparator = QLatin1Char('-')) const;
+ static QBS_EXPORT Version fromString(const QString &versionString, bool buildNumberAllowed = false);
+ QString QBS_EXPORT toString(const QChar &separator = QLatin1Char('.'),
+ const QChar &buildSeparator = QLatin1Char('-')) const;
private:
int m_major;
diff --git a/src/lib/corelib/tools/visualstudioversioninfo.cpp b/src/lib/corelib/tools/visualstudioversioninfo.cpp
index 02e5ef495..9ea86aaed 100644
--- a/src/lib/corelib/tools/visualstudioversioninfo.cpp
+++ b/src/lib/corelib/tools/visualstudioversioninfo.cpp
@@ -58,8 +58,8 @@ VisualStudioVersionInfo::VisualStudioVersionInfo(const Version &version)
std::set<VisualStudioVersionInfo> VisualStudioVersionInfo::knownVersions()
{
static const std::set<VisualStudioVersionInfo> known = {
- Version(16), Version(15), Version(14), Version(12), Version(11), Version(10), Version(9),
- Version(8), Version(7, 1), Version(7), Version(6)
+ Version(17), Version(16), Version(15), Version(14), Version(12), Version(11),
+ Version(10), Version(9), Version(8), Version(7, 1), Version(7), Version(6)
};
return known;
}
@@ -125,6 +125,8 @@ int VisualStudioVersionInfo::marketingVersion() const
return 2017;
case 16:
return 2019;
+ case 17:
+ return 2022;
default:
qWarning() << QStringLiteral("unrecognized Visual Studio version: ")
<< m_version.toString();
@@ -171,17 +173,18 @@ QString VisualStudioVersionInfo::toolsVersion() const
QString VisualStudioVersionInfo::platformToolsetVersion() const
{
static std::pair<int, QString> table[] = {
+ {17, QStringLiteral("v143")}, // VS 2022
{16, QStringLiteral("v142")}, // VS 2019
{15, QStringLiteral("v141")} // VS 2017
};
- for (auto p : table) {
+ for (const auto &p : table) {
if (p.first == m_version.majorVersion())
return p.second;
}
return QStringLiteral("v%1").arg(m_version.majorVersion() * 10);
}
-quint32 qHash(const VisualStudioVersionInfo &info)
+QHashValueType qHash(const VisualStudioVersionInfo &info)
{
return qHash(info.version().toString());
}
diff --git a/src/lib/corelib/tools/visualstudioversioninfo.h b/src/lib/corelib/tools/visualstudioversioninfo.h
index d4b226623..92eecb388 100644
--- a/src/lib/corelib/tools/visualstudioversioninfo.h
+++ b/src/lib/corelib/tools/visualstudioversioninfo.h
@@ -43,6 +43,7 @@
#include "qbs_export.h"
+#include <tools/porting.h>
#include <tools/version.h>
#include <QtCore/qstring.h>
@@ -78,7 +79,7 @@ private:
Version m_version;
};
-quint32 qHash(const VisualStudioVersionInfo &info);
+QHashValueType qHash(const VisualStudioVersionInfo &info);
} // namespace Internal
} // namespace qbs
diff --git a/src/lib/corelib/tools/vsenvironmentdetector.cpp b/src/lib/corelib/tools/vsenvironmentdetector.cpp
index f8f98e7b7..5bcbd93b6 100644
--- a/src/lib/corelib/tools/vsenvironmentdetector.cpp
+++ b/src/lib/corelib/tools/vsenvironmentdetector.cpp
@@ -64,7 +64,7 @@ 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<ushort*>(str));
+ return QString::fromUtf16(reinterpret_cast<char16_t*>(str));
#endif
return {};
}
@@ -124,14 +124,12 @@ QString VsEnvironmentDetector::findVcVarsAllBat(const MSVC &msvc,
QString fullPath = dir.absoluteFilePath(path);
if (dir.exists(path))
return fullPath;
- else
- searchedPaths.push_back(fullPath);
+ searchedPaths.push_back(fullPath);
path = QStringLiteral("Auxiliary/Build/") + vcvarsallbat;
fullPath = dir.absoluteFilePath(path);
if (dir.exists(path))
return fullPath;
- else
- searchedPaths.push_back(fullPath);
+ searchedPaths.push_back(fullPath);
return {};
}
@@ -193,13 +191,13 @@ bool VsEnvironmentDetector::startDetection(const std::vector<MSVC *> &compatible
static void batClearVars(QTextStream &s, const QStringList &varnames)
{
for (const QString &varname : varnames)
- s << "set " << varname << '=' << endl;
+ s << "set " << varname << '=' << Qt::endl;
}
static void batPrintVars(QTextStream &s, const QStringList &varnames)
{
for (const QString &varname : varnames)
- s << "echo " << varname << "=%" << varname << '%' << endl;
+ s << "echo " << varname << "=%" << varname << '%' << Qt::endl;
}
static QString vcArchitecture(const MSVC *msvc)
@@ -232,14 +230,22 @@ void VsEnvironmentDetector::writeBatchFile(QIODevice *device, const QString &vcv
<< QStringLiteral("INCLUDE") << QStringLiteral("LIB") << QStringLiteral("WindowsSdkDir")
<< QStringLiteral("WindowsSDKVersion") << QStringLiteral("VSINSTALLDIR");
QTextStream s(device);
+ using Qt::endl;
s << "@echo off" << endl;
+ // Avoid execution of powershell (in vsdevcmd.bat), which is not in the cleared PATH
+ s << "set VSCMD_SKIP_SENDTELEMETRY=1" << 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;
+ s << "call \"" << vcvarsallbat << "\" " << vcArchitecture(msvc);
+ if (!msvc->sdkVersion.isEmpty())
+ s << " " << msvc->sdkVersion;
+ const auto vcVarsVer = MSVC::vcVariablesVersionFromBinPath(msvc->binPath);
+ if (!vcVarsVer.isEmpty())
+ s << " -vcvars_ver=" << vcVarsVer;
+ s << " || exit /b 1" << endl;
batPrintVars(s, varnames);
s << "endlocal" << endl;
}
@@ -272,8 +278,6 @@ void VsEnvironmentDetector::parseBatOutput(const QByteArray &output, std::vector
value.remove(m_windowsSystemDirPath);
if (value.endsWith(QLatin1Char(';')))
value.chop(1);
- if (value.endsWith(QLatin1Char('\\')))
- value.chop(1);
targetEnv->insert(name, value);
}
}
diff --git a/src/lib/corelib/tools/vsenvironmentdetector.h b/src/lib/corelib/tools/vsenvironmentdetector.h
index 7fa152cb6..39bea07d6 100644
--- a/src/lib/corelib/tools/vsenvironmentdetector.h
+++ b/src/lib/corelib/tools/vsenvironmentdetector.h
@@ -57,7 +57,7 @@ class MSVC;
class QBS_EXPORT VsEnvironmentDetector
{
public:
- explicit VsEnvironmentDetector(QString vcvarsallPath = QString());
+ explicit VsEnvironmentDetector(QString vcvarsallPath = {});
bool start(MSVC *msvc);
bool start(std::vector<MSVC *> msvcs);
@@ -71,6 +71,7 @@ private:
const QString m_windowsSystemDirPath;
const QString m_vcvarsallPath;
+ const QString m_vcVariablesVersion;
QString m_errorString;
};
diff --git a/src/lib/corelib/use_corelib.pri b/src/lib/corelib/use_corelib.pri
deleted file mode 100644
index c674ee664..000000000
--- a/src/lib/corelib/use_corelib.pri
+++ /dev/null
@@ -1,47 +0,0 @@
-include(../../../qbs_version.pri)
-include(../../library_dirname.pri)
-
-isEmpty(QBSLIBDIR) {
- QBSLIBDIR = $$OUT_PWD/../../../$${QBS_LIBRARY_DIRNAME}
-}
-
-unix {
- LIBS += -L$$QBSLIBDIR -lqbscore
-}
-
-isEmpty(QBS_RPATH): QBS_RPATH = ../$$QBS_LIBRARY_DIRNAME
-!qbs_disable_rpath {
- linux-*: QMAKE_LFLAGS += -Wl,-z,origin \'-Wl,-rpath,\$\$ORIGIN/$${QBS_RPATH}\'
- macx: QMAKE_LFLAGS += -Wl,-rpath,@loader_path/$${QBS_RPATH}
-}
-
-!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(static, static|shared) {
- DEFINES += QBS_STATIC_LIB
-}
-qbs_enable_project_file_updates:DEFINES += QBS_ENABLE_PROJECT_FILE_UPDATES
-qbs_enable_unit_tests:DEFINES += QBS_ENABLE_UNIT_TESTS
diff --git a/src/lib/corelib/use_installed_corelib.pri b/src/lib/corelib/use_installed_corelib.pri
deleted file mode 100644
index 4ff72414d..000000000
--- a/src/lib/corelib/use_installed_corelib.pri
+++ /dev/null
@@ -1,38 +0,0 @@
-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}
-
-CONFIG(static, static|shared) {
- DEFINES += QBS_STATIC_LIB
-}
-qbs_enable_project_file_updates:DEFINES += QBS_ENABLE_PROJECT_FILE_UPDATES
-qbs_enable_unit_tests:DEFINES += QBS_ENABLE_UNIT_TESTS
diff --git a/src/lib/library.pri b/src/lib/library.pri
deleted file mode 100644
index 11427b097..000000000
--- a/src/lib/library.pri
+++ /dev/null
@@ -1,49 +0,0 @@
-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
-qbs_enable_unit_tests:DEFINES += QBS_ENABLE_UNIT_TESTS
-INCLUDEPATH += $${PWD}/../
-contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols
-win32:CONFIG(debug, debug|release):TARGET = $${TARGET}d
-CONFIG(debug, debug|release):DEFINES += QT_STRICT_ITERATORS
-CONFIG += c++14
-CONFIG += create_prl
-
-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
index dd21cc081..8638ab8dc 100644
--- a/src/lib/libs.qbs
+++ b/src/lib/libs.qbs
@@ -1,8 +1,7 @@
-import qbs
-
Project {
references: [
"corelib/corelib.qbs",
- "scriptengine/scriptengine.qbs",
+ "msbuild/msbuild.qbs",
+ "pkgconfig/pkgconfig.qbs",
]
}
diff --git a/src/lib/msbuild/CMakeLists.txt b/src/lib/msbuild/CMakeLists.txt
new file mode 100644
index 000000000..c908ed2d3
--- /dev/null
+++ b/src/lib/msbuild/CMakeLists.txt
@@ -0,0 +1,72 @@
+set(SOLUTION_SOURCES
+ ivisualstudiosolutionproject.cpp
+ ivisualstudiosolutionproject.h
+ visualstudiosolutionfileproject.cpp
+ visualstudiosolutionfileproject.h
+ visualstudiosolutionfolderproject.cpp
+ visualstudiosolutionfolderproject.h
+ visualstudiosolution.cpp
+ visualstudiosolution.h
+ visualstudiosolutionglobalsection.cpp
+ visualstudiosolutionglobalsection.h
+ )
+list_transform_prepend(SOLUTION_SOURCES solution/)
+
+set(MSBUILD_SOURCES
+ imsbuildgroup.cpp
+ imsbuildgroup.h
+ imsbuildnode.cpp
+ imsbuildnode.h
+ imsbuildnodevisitor.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
+ msbuildpropertybase.cpp
+ msbuildpropertybase.h
+ msbuildpropertygroup.cpp
+ msbuildpropertygroup.h
+ )
+list_transform_prepend(MSBUILD_SOURCES msbuild/)
+
+set(MSBUILD_ITEMS_SOURCES
+ msbuildclcompile.cpp
+ msbuildclcompile.h
+ msbuildclinclude.cpp
+ msbuildclinclude.h
+ msbuildfileitem.cpp
+ msbuildfileitem.h
+ msbuildfilter.cpp
+ msbuildfilter.h
+ msbuildlink.cpp
+ msbuildlink.h
+ msbuildnone.cpp
+ msbuildnone.h
+ )
+list_transform_prepend(MSBUILD_ITEMS_SOURCES msbuild/items/)
+
+set(IO_SOURCES
+ msbuildprojectwriter.cpp
+ msbuildprojectwriter.h
+ visualstudiosolutionwriter.cpp
+ visualstudiosolutionwriter.h
+ )
+list_transform_prepend(IO_SOURCES io/)
+
+add_qbs_library(qbsmsbuild
+ STATIC
+ DEPENDS qbscore
+ SOURCES ${SOLUTION_SOURCES} ${MSBUILD_SOURCES} ${MSBUILD_ITEMS_SOURCES} ${IO_SOURCES}
+ )
diff --git a/src/lib/msbuild/io/msbuildprojectwriter.cpp b/src/lib/msbuild/io/msbuildprojectwriter.cpp
new file mode 100644
index 000000000..986458a7f
--- /dev/null
+++ b/src/lib/msbuild/io/msbuildprojectwriter.cpp
@@ -0,0 +1,235 @@
+/****************************************************************************
+**
+** 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 <QtCore/qxmlstream.h>
+
+#include <memory>
+
+namespace qbs {
+
+static const QString kMSBuildSchemaURI =
+ QStringLiteral("http://schemas.microsoft.com/developer/msbuild/2003");
+
+class MSBuildProjectWriterPrivate : public IMSBuildNodeVisitor
+{
+public:
+ std::ostream *device = nullptr;
+ QByteArray buffer;
+ std::unique_ptr<QXmlStreamWriter> 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(std::ostream *device)
+ : d(std::make_unique<MSBuildProjectWriterPrivate>())
+{
+ d->device = device;
+ d->writer = std::make_unique<QXmlStreamWriter>(&d->buffer);
+ d->writer->setAutoFormatting(true);
+}
+
+MSBuildProjectWriter::~MSBuildProjectWriter() = default;
+
+bool MSBuildProjectWriter::write(const MSBuildProject *project)
+{
+ d->buffer.clear();
+ d->writer->writeStartDocument();
+ project->accept(d.get());
+ d->writer->writeEndDocument();
+ if (d->writer->hasError())
+ return false;
+ d->device->write(&*std::begin(d->buffer), d->buffer.size());
+ return d->device->good();
+}
+
+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().userType() == QMetaType::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().userType() == QMetaType::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/msbuild/io/msbuildprojectwriter.h b/src/lib/msbuild/io/msbuildprojectwriter.h
new file mode 100644
index 000000000..9feb347c5
--- /dev/null
+++ b/src/lib/msbuild/io/msbuildprojectwriter.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 MSBUILDPROJECTWRITER_H
+#define MSBUILDPROJECTWRITER_H
+
+#include <ostream>
+
+#include <QtCore/qglobal.h>
+
+#include <memory>
+
+namespace qbs {
+
+class MSBuildProject;
+class MSBuildProjectWriterPrivate;
+
+class MSBuildProjectWriter
+{
+ Q_DISABLE_COPY(MSBuildProjectWriter)
+public:
+ explicit MSBuildProjectWriter(std::ostream *device);
+ ~MSBuildProjectWriter();
+
+ bool write(const MSBuildProject *project);
+
+private:
+ const std::unique_ptr<MSBuildProjectWriterPrivate> d;
+};
+
+} // namespace qbs
+
+#endif // MSBUILDPROJECTWRITER_H
diff --git a/src/lib/msbuild/io/visualstudiosolutionwriter.cpp b/src/lib/msbuild/io/visualstudiosolutionwriter.cpp
new file mode 100644
index 000000000..874fbc71f
--- /dev/null
+++ b/src/lib/msbuild/io/visualstudiosolutionwriter.cpp
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** 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 <tools/hostosinfo.h>
+#include <tools/pathutils.h>
+#include <tools/stlutils.h>
+#include <tools/visualstudioversioninfo.h>
+
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/quuid.h>
+
+#include <vector>
+
+namespace qbs {
+
+using namespace Internal;
+
+class VisualStudioSolutionWriterPrivate
+{
+public:
+ std::ostream *device = nullptr;
+ std::string baseDir;
+};
+
+VisualStudioSolutionWriter::VisualStudioSolutionWriter(std::ostream *device)
+ : d(new VisualStudioSolutionWriterPrivate)
+{
+ d->device = device;
+}
+
+VisualStudioSolutionWriter::~VisualStudioSolutionWriter() = default;
+
+std::string VisualStudioSolutionWriter::projectBaseDirectory() const
+{
+ return d->baseDir;
+}
+
+void VisualStudioSolutionWriter::setProjectBaseDirectory(const std::string &dir)
+{
+ d->baseDir = dir;
+}
+
+bool VisualStudioSolutionWriter::write(const VisualStudioSolution *solution)
+{
+ auto &out = *d->device;
+ out << u8"Microsoft Visual Studio Solution File, Format Version "
+ << solution->versionInfo().solutionVersion().toStdString()
+ << u8"\n# Visual Studio "
+ << solution->versionInfo().version().majorVersion()
+ << u8"\n";
+
+ const auto fileProjects = solution->fileProjects();
+ for (const auto &project : fileProjects) {
+ auto projectFilePath = project->filePath().toStdString();
+
+ // Try to make the project file path relative to the
+ // solution file path if we're writing to a file device
+ if (!d->baseDir.empty()) {
+ const QDir solutionDir(QString::fromStdString(d->baseDir));
+ projectFilePath = Internal::PathUtils::toNativeSeparators(
+ solutionDir.relativeFilePath(QString::fromStdString(projectFilePath)),
+ Internal::HostOsInfo::HostOsWindows).toStdString();
+ }
+
+ out << u8"Project(\""
+ << project->projectTypeGuid().toString().toStdString()
+ << u8"\") = \""
+ << QFileInfo(QString::fromStdString(projectFilePath)).baseName().toStdString()
+ << u8"\", \""
+ << projectFilePath
+ << u8"\", \""
+ << project->guid().toString().toStdString()
+ << u8"\"\n";
+
+ out << u8"EndProject\n";
+ }
+
+ const auto folderProjects = solution->folderProjects();
+ for (const auto &project : folderProjects) {
+ out << u8"Project(\""
+ << project->projectTypeGuid().toString().toStdString()
+ << u8"\") = \""
+ << project->name().toStdString()
+ << u8"\", \""
+ << project->name().toStdString()
+ << u8"\", \""
+ << project->guid().toString().toStdString()
+ << u8"\"\n";
+
+ out << u8"EndProject\n";
+ }
+
+ out << u8"Global\n";
+
+ const auto globalSections = solution->globalSections();
+ for (const auto &globalSection : globalSections) {
+ out << u8"\tGlobalSection("
+ << globalSection->name().toStdString()
+ << u8") = "
+ << (globalSection->isPost() ? u8"postSolution" : u8"preSolution")
+ << u8"\n";
+ for (const auto &property : globalSection->properties())
+ out << u8"\t\t"
+ << property.first.toStdString()
+ << u8" = "
+ << property.second.toStdString()
+ << u8"\n";
+
+ out << u8"\tEndGlobalSection\n";
+ }
+
+ out << u8"EndGlobal\n";
+
+ return out.good();
+}
+
+} // namespace qbs
diff --git a/src/plugins/generator/visualstudio/io/visualstudiosolutionwriter.h b/src/lib/msbuild/io/visualstudiosolutionwriter.h
index 62ab45ead..62ab45ead 100644
--- a/src/plugins/generator/visualstudio/io/visualstudiosolutionwriter.h
+++ b/src/lib/msbuild/io/visualstudiosolutionwriter.h
diff --git a/src/lib/msbuild/msbuild.qbs b/src/lib/msbuild/msbuild.qbs
new file mode 100644
index 000000000..505b977be
--- /dev/null
+++ b/src/lib/msbuild/msbuild.qbs
@@ -0,0 +1,86 @@
+QbsStaticLibrary {
+ Depends { name: "cpp" }
+ Depends { name: "qbscore" }
+ name: "qbsmsbuild"
+ cpp.visibility: "default"
+ cpp.includePaths: base.concat([
+ ".",
+ "../corelib", // for some header-only functions in tools
+ ])
+
+ 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",
+ "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",
+ "msbuildpropertybase.cpp",
+ "msbuildpropertybase.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/plugins/generator/visualstudio/msbuild/imsbuildgroup.cpp b/src/lib/msbuild/msbuild/imsbuildgroup.cpp
index 81078ecb9..81078ecb9 100644
--- a/src/plugins/generator/visualstudio/msbuild/imsbuildgroup.cpp
+++ b/src/lib/msbuild/msbuild/imsbuildgroup.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/imsbuildgroup.h b/src/lib/msbuild/msbuild/imsbuildgroup.h
index 3d9115643..3d9115643 100644
--- a/src/plugins/generator/visualstudio/msbuild/imsbuildgroup.h
+++ b/src/lib/msbuild/msbuild/imsbuildgroup.h
diff --git a/src/plugins/generator/visualstudio/msbuild/imsbuildnode.cpp b/src/lib/msbuild/msbuild/imsbuildnode.cpp
index f563f7b07..f563f7b07 100644
--- a/src/plugins/generator/visualstudio/msbuild/imsbuildnode.cpp
+++ b/src/lib/msbuild/msbuild/imsbuildnode.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/imsbuildnode.h b/src/lib/msbuild/msbuild/imsbuildnode.h
index 67fb10081..67fb10081 100644
--- a/src/plugins/generator/visualstudio/msbuild/imsbuildnode.h
+++ b/src/lib/msbuild/msbuild/imsbuildnode.h
diff --git a/src/plugins/generator/visualstudio/msbuild/imsbuildnodevisitor.h b/src/lib/msbuild/msbuild/imsbuildnodevisitor.h
index 84239d648..84239d648 100644
--- a/src/plugins/generator/visualstudio/msbuild/imsbuildnodevisitor.h
+++ b/src/lib/msbuild/msbuild/imsbuildnodevisitor.h
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildclcompile.cpp b/src/lib/msbuild/msbuild/items/msbuildclcompile.cpp
index d8a587454..d8a587454 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildclcompile.cpp
+++ b/src/lib/msbuild/msbuild/items/msbuildclcompile.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildclcompile.h b/src/lib/msbuild/msbuild/items/msbuildclcompile.h
index 8c326a036..8c326a036 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildclcompile.h
+++ b/src/lib/msbuild/msbuild/items/msbuildclcompile.h
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildclinclude.cpp b/src/lib/msbuild/msbuild/items/msbuildclinclude.cpp
index d9c61bece..d9c61bece 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildclinclude.cpp
+++ b/src/lib/msbuild/msbuild/items/msbuildclinclude.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildclinclude.h b/src/lib/msbuild/msbuild/items/msbuildclinclude.h
index 77d8e0ff2..77d8e0ff2 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildclinclude.h
+++ b/src/lib/msbuild/msbuild/items/msbuildclinclude.h
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildfileitem.cpp b/src/lib/msbuild/msbuild/items/msbuildfileitem.cpp
index 4cbb01d04..4cbb01d04 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildfileitem.cpp
+++ b/src/lib/msbuild/msbuild/items/msbuildfileitem.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildfileitem.h b/src/lib/msbuild/msbuild/items/msbuildfileitem.h
index f34ac119a..f34ac119a 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildfileitem.h
+++ b/src/lib/msbuild/msbuild/items/msbuildfileitem.h
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildfilter.cpp b/src/lib/msbuild/msbuild/items/msbuildfilter.cpp
index 0b9c2c86a..0b9c2c86a 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildfilter.cpp
+++ b/src/lib/msbuild/msbuild/items/msbuildfilter.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildfilter.h b/src/lib/msbuild/msbuild/items/msbuildfilter.h
index fb977f1cc..fb977f1cc 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildfilter.h
+++ b/src/lib/msbuild/msbuild/items/msbuildfilter.h
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildlink.cpp b/src/lib/msbuild/msbuild/items/msbuildlink.cpp
index cae1a63a3..cae1a63a3 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildlink.cpp
+++ b/src/lib/msbuild/msbuild/items/msbuildlink.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildlink.h b/src/lib/msbuild/msbuild/items/msbuildlink.h
index 3d7a3ab44..3d7a3ab44 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildlink.h
+++ b/src/lib/msbuild/msbuild/items/msbuildlink.h
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildnone.cpp b/src/lib/msbuild/msbuild/items/msbuildnone.cpp
index a590c6e9f..a590c6e9f 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildnone.cpp
+++ b/src/lib/msbuild/msbuild/items/msbuildnone.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/items/msbuildnone.h b/src/lib/msbuild/msbuild/items/msbuildnone.h
index 3779da04f..3779da04f 100644
--- a/src/plugins/generator/visualstudio/msbuild/items/msbuildnone.h
+++ b/src/lib/msbuild/msbuild/items/msbuildnone.h
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuildimport.cpp b/src/lib/msbuild/msbuild/msbuildimport.cpp
index 000af4496..000af4496 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuildimport.cpp
+++ b/src/lib/msbuild/msbuild/msbuildimport.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuildimport.h b/src/lib/msbuild/msbuild/msbuildimport.h
index 65f9bc056..65f9bc056 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuildimport.h
+++ b/src/lib/msbuild/msbuild/msbuildimport.h
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuildimportgroup.cpp b/src/lib/msbuild/msbuild/msbuildimportgroup.cpp
index d84d81783..d84d81783 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuildimportgroup.cpp
+++ b/src/lib/msbuild/msbuild/msbuildimportgroup.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuildimportgroup.h b/src/lib/msbuild/msbuild/msbuildimportgroup.h
index a21cb5a54..a21cb5a54 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuildimportgroup.h
+++ b/src/lib/msbuild/msbuild/msbuildimportgroup.h
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuilditem.cpp b/src/lib/msbuild/msbuild/msbuilditem.cpp
index ed181a453..ed181a453 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuilditem.cpp
+++ b/src/lib/msbuild/msbuild/msbuilditem.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuilditem.h b/src/lib/msbuild/msbuild/msbuilditem.h
index d01a357a2..d01a357a2 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuilditem.h
+++ b/src/lib/msbuild/msbuild/msbuilditem.h
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuilditemdefinitiongroup.cpp b/src/lib/msbuild/msbuild/msbuilditemdefinitiongroup.cpp
index 1479577c5..1479577c5 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuilditemdefinitiongroup.cpp
+++ b/src/lib/msbuild/msbuild/msbuilditemdefinitiongroup.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuilditemdefinitiongroup.h b/src/lib/msbuild/msbuild/msbuilditemdefinitiongroup.h
index 3c324699d..3c324699d 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuilditemdefinitiongroup.h
+++ b/src/lib/msbuild/msbuild/msbuilditemdefinitiongroup.h
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuilditemgroup.cpp b/src/lib/msbuild/msbuild/msbuilditemgroup.cpp
index 48f919709..48f919709 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuilditemgroup.cpp
+++ b/src/lib/msbuild/msbuild/msbuilditemgroup.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuilditemgroup.h b/src/lib/msbuild/msbuild/msbuilditemgroup.h
index bbbdb0e69..bbbdb0e69 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuilditemgroup.h
+++ b/src/lib/msbuild/msbuild/msbuilditemgroup.h
diff --git a/src/lib/msbuild/msbuild/msbuilditemmetadata.cpp b/src/lib/msbuild/msbuild/msbuilditemmetadata.cpp
new file mode 100644
index 000000000..2f315287c
--- /dev/null
+++ b/src/lib/msbuild/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)
+ : MSBuildPropertyBase(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/msbuild/msbuild/msbuilditemmetadata.h b/src/lib/msbuild/msbuild/msbuilditemmetadata.h
new file mode 100644
index 000000000..cfde125bb
--- /dev/null
+++ b/src/lib/msbuild/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 "imsbuildnode.h"
+#include "msbuildpropertybase.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 MSBuildPropertyBase, public IMSBuildNode
+{
+ Q_OBJECT
+ Q_DISABLE_COPY(MSBuildItemMetadata)
+public:
+ explicit MSBuildItemMetadata(MSBuildItem *parent = nullptr);
+ MSBuildItemMetadata(const QString &name, const QVariant &value = QVariant(),
+ MSBuildItem *parent = nullptr);
+
+ void accept(IMSBuildNodeVisitor *visitor) const override;
+};
+
+} // namespace qbs
+
+#endif // MSBUILDITEMMETADATA_H
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuildproject.cpp b/src/lib/msbuild/msbuild/msbuildproject.cpp
index 11b5b81ea..11b5b81ea 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuildproject.cpp
+++ b/src/lib/msbuild/msbuild/msbuildproject.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuildproject.h b/src/lib/msbuild/msbuild/msbuildproject.h
index 5281f615d..5281f615d 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuildproject.h
+++ b/src/lib/msbuild/msbuild/msbuildproject.h
diff --git a/src/lib/msbuild/msbuild/msbuildproperty.cpp b/src/lib/msbuild/msbuild/msbuildproperty.cpp
new file mode 100644
index 000000000..fd6942d0c
--- /dev/null
+++ b/src/lib/msbuild/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)
+ : MSBuildPropertyBase(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/msbuild/msbuild/msbuildproperty.h b/src/lib/msbuild/msbuild/msbuildproperty.h
new file mode 100644
index 000000000..4fd36b275
--- /dev/null
+++ b/src/lib/msbuild/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 "imsbuildnode.h"
+#include "msbuildpropertybase.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 MSBuildPropertyBase, public IMSBuildNode
+{
+ Q_OBJECT
+public:
+ explicit MSBuildProperty(MSBuildPropertyGroup *parent = nullptr);
+ MSBuildProperty(const QString &name, const QVariant &value = QVariant(),
+ MSBuildPropertyGroup *parent = nullptr);
+
+ void accept(IMSBuildNodeVisitor *visitor) const override;
+};
+
+} // namespace qbs
+
+#endif // MSBUILDPROPERTY_H
diff --git a/src/lib/msbuild/msbuild/msbuildpropertybase.cpp b/src/lib/msbuild/msbuild/msbuildpropertybase.cpp
new file mode 100644
index 000000000..3a8affd3f
--- /dev/null
+++ b/src/lib/msbuild/msbuild/msbuildpropertybase.cpp
@@ -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.
+**
+****************************************************************************/
+
+#include "msbuildpropertybase.h"
+
+namespace qbs {
+
+class MSBuildPropertyBasePrivate
+{
+public:
+ QString condition;
+ QString name;
+ QVariant value;
+};
+
+MSBuildPropertyBase::MSBuildPropertyBase(QObject *parent)
+ : QObject(parent)
+ , d(new MSBuildPropertyBasePrivate)
+{
+}
+
+MSBuildPropertyBase::~MSBuildPropertyBase() = default;
+
+QString MSBuildPropertyBase::condition() const
+{
+ return d->condition;
+}
+
+void MSBuildPropertyBase::setCondition(const QString &condition)
+{
+ d->condition = condition;
+}
+
+QString MSBuildPropertyBase::name() const
+{
+ return d->name;
+}
+
+void MSBuildPropertyBase::setName(const QString &name)
+{
+ d->name = name;
+}
+
+QVariant MSBuildPropertyBase::value() const
+{
+ return d->value;
+}
+
+void MSBuildPropertyBase::setValue(const QVariant &value)
+{
+ d->value = value;
+}
+
+} // namespace qbs
diff --git a/src/lib/msbuild/msbuild/msbuildpropertybase.h b/src/lib/msbuild/msbuild/msbuildpropertybase.h
new file mode 100644
index 000000000..b4d25f827
--- /dev/null
+++ b/src/lib/msbuild/msbuild/msbuildpropertybase.h
@@ -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.
+**
+****************************************************************************/
+
+#ifndef MSBUILDPROPERTYBASE_H
+#define MSBUILDPROPERTYBASE_H
+
+#include <QtCore/qobject.h>
+#include <QtCore/qvariant.h>
+
+#include <memory>
+
+namespace qbs {
+
+class MSBuildPropertyBasePrivate;
+
+class MSBuildPropertyBase : public QObject
+{
+ Q_OBJECT
+protected:
+ explicit MSBuildPropertyBase(QObject *parent = nullptr);
+
+public:
+ ~MSBuildPropertyBase() override;
+
+ 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:
+ std::unique_ptr<MSBuildPropertyBasePrivate> d;
+};
+
+} // namespace qbs
+
+#endif // MSBUILDPROPERTYBASE_H
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuildpropertygroup.cpp b/src/lib/msbuild/msbuild/msbuildpropertygroup.cpp
index 5acb73dad..5acb73dad 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuildpropertygroup.cpp
+++ b/src/lib/msbuild/msbuild/msbuildpropertygroup.cpp
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuildpropertygroup.h b/src/lib/msbuild/msbuild/msbuildpropertygroup.h
index 60bdb008e..60bdb008e 100644
--- a/src/plugins/generator/visualstudio/msbuild/msbuildpropertygroup.h
+++ b/src/lib/msbuild/msbuild/msbuildpropertygroup.h
diff --git a/src/plugins/generator/visualstudio/solution/ivisualstudiosolutionproject.cpp b/src/lib/msbuild/solution/ivisualstudiosolutionproject.cpp
index 4623b5d4d..4623b5d4d 100644
--- a/src/plugins/generator/visualstudio/solution/ivisualstudiosolutionproject.cpp
+++ b/src/lib/msbuild/solution/ivisualstudiosolutionproject.cpp
diff --git a/src/plugins/generator/visualstudio/solution/ivisualstudiosolutionproject.h b/src/lib/msbuild/solution/ivisualstudiosolutionproject.h
index 6f8574aed..6f8574aed 100644
--- a/src/plugins/generator/visualstudio/solution/ivisualstudiosolutionproject.h
+++ b/src/lib/msbuild/solution/ivisualstudiosolutionproject.h
diff --git a/src/lib/msbuild/solution/visualstudiosolution.cpp b/src/lib/msbuild/solution/visualstudiosolution.cpp
new file mode 100644
index 000000000..0f6cd4f23
--- /dev/null
+++ b/src/lib/msbuild/solution/visualstudiosolution.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** 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 <tools/visualstudioversioninfo.h>
+
+#include <QtCore/qmap.h>
+
+namespace qbs {
+
+class VisualStudioSolutionPrivate
+{
+public:
+ VisualStudioSolutionPrivate(const Internal::VisualStudioVersionInfo &versionInfo)
+ : versionInfo(versionInfo) { }
+ const Internal::VisualStudioVersionInfo versionInfo;
+ QList<IVisualStudioSolutionProject *> projects;
+ QMap<VisualStudioSolutionFileProject *, QList<VisualStudioSolutionFileProject *>> dependencies;
+ QList<VisualStudioSolutionGlobalSection *> globalSections;
+};
+
+VisualStudioSolution::VisualStudioSolution(const Internal::VisualStudioVersionInfo &versionInfo,
+ QObject *parent)
+ : QObject(parent)
+ , d(new VisualStudioSolutionPrivate(versionInfo))
+{
+}
+
+VisualStudioSolution::~VisualStudioSolution() = default;
+
+Internal::VisualStudioVersionInfo VisualStudioSolution::versionInfo() const
+{
+ return d->versionInfo;
+}
+
+QList<IVisualStudioSolutionProject *> VisualStudioSolution::projects() const
+{
+ return d->projects;
+}
+
+QList<VisualStudioSolutionFileProject *> VisualStudioSolution::fileProjects() const
+{
+ QList<VisualStudioSolutionFileProject *> list;
+ for (const auto &project : std::as_const(d->projects))
+ if (auto fileProject = qobject_cast<VisualStudioSolutionFileProject *>(project))
+ list.push_back(fileProject);
+ return list;
+}
+
+QList<VisualStudioSolutionFolderProject *> VisualStudioSolution::folderProjects() const
+{
+ QList<VisualStudioSolutionFolderProject *> list;
+ for (const auto &project : std::as_const(d->projects))
+ if (auto folderProject = qobject_cast<VisualStudioSolutionFolderProject *>(project))
+ list.push_back(folderProject);
+ return list;
+}
+
+void VisualStudioSolution::appendProject(IVisualStudioSolutionProject *project)
+{
+ d->projects.push_back(project);
+}
+
+QList<VisualStudioSolutionFileProject *> VisualStudioSolution::dependencies(
+ VisualStudioSolutionFileProject *project) const
+{
+ return d->dependencies.value(project);
+}
+
+void VisualStudioSolution::addDependency(VisualStudioSolutionFileProject *project,
+ VisualStudioSolutionFileProject *dependency)
+{
+ d->dependencies[project].push_back(dependency);
+}
+
+QList<VisualStudioSolutionGlobalSection *> VisualStudioSolution::globalSections() const
+{
+ return d->globalSections;
+}
+
+void VisualStudioSolution::appendGlobalSection(VisualStudioSolutionGlobalSection *globalSection)
+{
+ d->globalSections.push_back(globalSection);
+}
+
+} // namespace qbs
diff --git a/src/plugins/generator/visualstudio/solution/visualstudiosolution.h b/src/lib/msbuild/solution/visualstudiosolution.h
index ba304c00c..ba304c00c 100644
--- a/src/plugins/generator/visualstudio/solution/visualstudiosolution.h
+++ b/src/lib/msbuild/solution/visualstudiosolution.h
diff --git a/src/lib/msbuild/solution/visualstudiosolutionfileproject.cpp b/src/lib/msbuild/solution/visualstudiosolutionfileproject.cpp
new file mode 100644
index 000000000..e1b83d801
--- /dev/null
+++ b/src/lib/msbuild/solution/visualstudiosolutionfileproject.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 "visualstudiosolutionfileproject.h"
+
+#include <QtCore/qfileinfo.h>
+
+namespace qbs {
+
+class VisualStudioSolutionFileProjectPrivate
+{
+public:
+ QString filePath;
+};
+
+VisualStudioSolutionFileProject::VisualStudioSolutionFileProject(const QString &filePath,
+ QObject *parent)
+ : IVisualStudioSolutionProject(parent)
+ , d(new VisualStudioSolutionFileProjectPrivate)
+{
+ setFilePath(filePath);
+}
+
+VisualStudioSolutionFileProject::~VisualStudioSolutionFileProject() = default;
+
+QString VisualStudioSolutionFileProject::name() const
+{
+ auto projectName = IVisualStudioSolutionProject::name();
+ if (projectName.isEmpty())
+ return QFileInfo(filePath()).baseName();
+ return projectName;
+}
+
+QUuid VisualStudioSolutionFileProject::projectTypeGuid() const
+{
+ return QUuid::fromString(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/plugins/generator/visualstudio/solution/visualstudiosolutionfileproject.h b/src/lib/msbuild/solution/visualstudiosolutionfileproject.h
index 2039146b6..2039146b6 100644
--- a/src/plugins/generator/visualstudio/solution/visualstudiosolutionfileproject.h
+++ b/src/lib/msbuild/solution/visualstudiosolutionfileproject.h
diff --git a/src/lib/msbuild/solution/visualstudiosolutionfolderproject.cpp b/src/lib/msbuild/solution/visualstudiosolutionfolderproject.cpp
new file mode 100644
index 000000000..8b0997040
--- /dev/null
+++ b/src/lib/msbuild/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 <QtCore/qfileinfo.h>
+
+namespace qbs {
+
+VisualStudioSolutionFolderProject::VisualStudioSolutionFolderProject(QObject *parent)
+ : IVisualStudioSolutionProject(parent)
+{
+}
+
+QUuid VisualStudioSolutionFolderProject::projectTypeGuid() const
+{
+ return QUuid::fromString(QStringLiteral("{2150E333-8FDC-42A3-9474-1A3956D46DE8}"));
+}
+
+} // namespace qbs
diff --git a/src/plugins/generator/visualstudio/solution/visualstudiosolutionfolderproject.h b/src/lib/msbuild/solution/visualstudiosolutionfolderproject.h
index a7fd180cc..a7fd180cc 100644
--- a/src/plugins/generator/visualstudio/solution/visualstudiosolutionfolderproject.h
+++ b/src/lib/msbuild/solution/visualstudiosolutionfolderproject.h
diff --git a/src/plugins/generator/visualstudio/solution/visualstudiosolutionglobalsection.cpp b/src/lib/msbuild/solution/visualstudiosolutionglobalsection.cpp
index 5cbb0cd61..5cbb0cd61 100644
--- a/src/plugins/generator/visualstudio/solution/visualstudiosolutionglobalsection.cpp
+++ b/src/lib/msbuild/solution/visualstudiosolutionglobalsection.cpp
diff --git a/src/plugins/generator/visualstudio/solution/visualstudiosolutionglobalsection.h b/src/lib/msbuild/solution/visualstudiosolutionglobalsection.h
index 6d383e7d4..6d383e7d4 100644
--- a/src/plugins/generator/visualstudio/solution/visualstudiosolutionglobalsection.h
+++ b/src/lib/msbuild/solution/visualstudiosolutionglobalsection.h
diff --git a/src/lib/pkgconfig/CMakeLists.txt b/src/lib/pkgconfig/CMakeLists.txt
new file mode 100644
index 000000000..c82edac16
--- /dev/null
+++ b/src/lib/pkgconfig/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(SOURCES
+ pcpackage.cpp
+ pcpackage.h
+ pcparser.cpp
+ pcparser.h
+ pkgconfig.cpp
+ pkgconfig.h
+)
+list_transform_prepend(SOLUTION_SOURCES solution/)
+
+if(APPLE OR MINGW)
+ set(HAS_STD_FILESYSTEM "0")
+else()
+ set(HAS_STD_FILESYSTEM "1")
+endif()
+
+set(QBS_PKGCONFIG_PUBLIC_DEPENDS "")
+if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
+ set(QBS_PKGCONFIG_PUBLIC_DEPENDS "stdc++fs")
+endif()
+
+add_qbs_library(qbspkgconfig
+ STATIC
+ DEFINES
+ "PKG_CONFIG_PC_PATH=\"${CMAKE_INSTALL_PREFIX}/${QBS_LIBDIR_NAME}/pkgconfig:${CMAKE_INSTALL_PREFIX}/share/pkgconfig:/usr/${QBS_LIBDIR_NAME}/pkgconfig/:/usr/share/pkgconfig/\""
+ "PKG_CONFIG_SYSTEM_LIBRARY_PATH=\"/usr/${QBS_LIBDIR_NAME}\""
+ "HAS_STD_FILESYSTEM=${HAS_STD_FILESYSTEM}"
+ PUBLIC_DEFINES
+ "QBS_PC_WITH_QT_SUPPORT=1"
+ PUBLIC_DEPENDS Qt${QT_VERSION_MAJOR}::Core ${QBS_PKGCONFIG_PUBLIC_DEPENDS} qbsvariant
+ SOURCES ${SOURCES}
+)
diff --git a/src/lib/pkgconfig/pcpackage.cpp b/src/lib/pkgconfig/pcpackage.cpp
new file mode 100644
index 000000000..9aaefcfde
--- /dev/null
+++ b/src/lib/pkgconfig/pcpackage.cpp
@@ -0,0 +1,243 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "pcpackage.h"
+
+#include <algorithm>
+
+namespace qbs {
+
+using Internal::startsWith;
+
+using ComparisonType = PcPackage::RequiredVersion::ComparisonType;
+
+std::string_view PcPackage::Flag::typeToString(Type t)
+{
+ switch (t) {
+ case Type::LibraryName: return "LibraryName";
+ case Type::StaticLibraryName: return "StaticLibraryName";
+ case Type::LibraryPath: return "LibraryPath";
+ case Type::Framework: return "Framework";
+ case Type::FrameworkPath: return "FrameworkPath";
+ case Type::LinkerFlag: return "LinkerFlag";
+ case Type::IncludePath: return "IncludePath";
+ case Type::SystemIncludePath: return "SystemIncludePath";
+ case Type::DirAfterIncludePath: return "DirAfterIncludePath";
+ case Type::Define: return "Define";
+ case Type::CompilerFlag: return "CompilerFlag";
+ }
+ return {};
+}
+
+std::optional<PcPackage::Flag::Type> PcPackage::Flag::typeFromString(std::string_view s)
+{
+ if (s == "LibraryName")
+ return Type::LibraryName;
+ if (s == "StaticLibraryName")
+ return Type::StaticLibraryName;
+ if (s == "LibraryPath")
+ return Type::LibraryPath;
+ if (s == "Framework")
+ return Type::Framework;
+ if (s == "FrameworkPath")
+ return Type::FrameworkPath;
+ if (s == "LinkerFlag")
+ return Type::LinkerFlag;
+ if (s == "IncludePath")
+ return Type::IncludePath;
+ if (s == "SystemIncludePath")
+ return Type::SystemIncludePath;
+ if (s == "DirAfterIncludePath")
+ return Type::DirAfterIncludePath;
+ if (s == "Define")
+ return Type::Define;
+ if (s == "CompilerFlag")
+ return Type::CompilerFlag;
+ return std::nullopt;
+}
+
+std::string_view PcPackage::RequiredVersion::comparisonToString(ComparisonType t)
+{
+ switch (t) {
+ case ComparisonType::LessThan: return "LessThan";
+ case ComparisonType::GreaterThan: return "GreaterThan";
+ case ComparisonType::LessThanEqual: return "LessThanEqual";
+ case ComparisonType::GreaterThanEqual: return "GreaterThanEqual";
+ case ComparisonType::Equal: return "Equal";
+ case ComparisonType::NotEqual: return "NotEqual";
+ case ComparisonType::AlwaysMatch: return "AlwaysMatch";
+ }
+ return {};
+}
+
+std::optional<ComparisonType> PcPackage::RequiredVersion::comparisonFromString(std::string_view s)
+{
+ if (s == "LessThan")
+ return ComparisonType::LessThan;
+ if (s == "GreaterThan")
+ return ComparisonType::GreaterThan;
+ if (s == "LessThanEqual")
+ return ComparisonType::LessThanEqual;
+ if (s == "GreaterThanEqual")
+ return ComparisonType::GreaterThanEqual;
+ if (s == "Equal")
+ return ComparisonType::Equal;
+ if (s == "NotEqual")
+ return ComparisonType::NotEqual;
+ if (s == "AlwaysMatch")
+ return ComparisonType::AlwaysMatch;
+ return std::nullopt;
+}
+
+// see https://github.com/pkgconf/pkgconf/blob/pkgconf-2.1.0/libpkgconf/tuple.c#L194
+bool PcPackage::shouldRewriteSysroot(std::string_view sysroot, std::string_view value)
+{
+ if (sysroot.empty() || value.empty())
+ return false;
+
+ if (value.front() != '/')
+ return false;
+
+ if (sysroot == "/")
+ return false;
+
+ if (startsWith(value, sysroot))
+ return false;
+
+ return true;
+}
+
+// TODO: pkg-config only prepends sysroot to flags; while pkgconf does this as a part of the
+// variable substitution and thus this affects all variables, not only flags
+PcPackage PcPackage::prependSysroot(std::string_view sysroot) &&
+{
+ PcPackage package(std::move(*this));
+
+ const auto doAppend = [](std::vector<Flag> flags, std::string_view sysroot)
+ {
+ if (sysroot.empty())
+ return flags;
+ for (auto &flag : flags) {
+ if (!shouldRewriteSysroot(sysroot, flag.value))
+ continue;
+ if (flag.type == Flag::Type::IncludePath
+ || flag.type == Flag::Type::SystemIncludePath
+ || flag.type == Flag::Type::DirAfterIncludePath
+ || flag.type == Flag::Type::LibraryPath) {
+ flag.value = std::string(sysroot) + std::move(flag.value);
+ }
+ }
+ return flags;
+ };
+
+ package.libs = doAppend(std::move(package.libs), sysroot);
+ package.libsPrivate = doAppend(std::move(package.libsPrivate), sysroot);
+ package.cflags = doAppend(std::move(package.cflags), sysroot);
+ return package;
+}
+
+PcPackage PcPackage::removeSystemLibraryPaths(
+ const std::unordered_set<std::string> &libraryPaths) &&
+{
+ PcPackage package(std::move(*this));
+ if (libraryPaths.empty())
+ return package;
+
+ const auto doRemove = [&libraryPaths](std::vector<Flag> flags)
+ {
+ const auto predicate = [&libraryPaths](const Flag &flag)
+ {
+ return flag.type == Flag::Type::LibraryPath && libraryPaths.count(flag.value);
+ };
+ flags.erase(std::remove_if(flags.begin(), flags.end(), predicate), flags.end());
+ return flags;
+ };
+ package.libs = doRemove(package.libs);
+ package.libsPrivate = doRemove(package.libsPrivate);
+ return package;
+}
+
+namespace Internal {
+
+std::string_view fileName(std::string_view filePath)
+{
+ const auto pos = filePath.rfind('/');
+ if (pos == std::string_view::npos) {
+#if defined(WIN32)
+ if (filePath.size() >= 2 && filePath[1] == ':')
+ return filePath.substr(2);
+#endif
+ return filePath;
+ }
+ return filePath.substr(pos + 1);
+}
+
+std::string_view completeBaseName(std::string_view filePath)
+{
+ filePath = fileName(filePath);
+ const auto pos = filePath.rfind('.');
+ return pos == std::string_view::npos
+ ? filePath
+ : filePath.substr(0, pos);
+}
+
+std::string_view parentPath(std::string_view path)
+{
+ if (path.empty())
+ return {};
+ auto pos = path.rfind('/');
+ if (pos == std::string_view::npos) {
+#if defined(WIN32)
+ if (path.size() >= 2 && path[1] == ':')
+ return path.substr(0, 2);
+#endif
+ return ".";
+ }
+ if (pos == 0)
+ return "/";
+#if defined(WIN32)
+ if (pos == 2 && path[1] == ':')
+ return path.substr(0, pos + 1);
+#endif
+ return path.substr(0, pos);
+};
+
+} // namespace Internal
+
+} // namespace qbs
diff --git a/src/lib/pkgconfig/pcpackage.h b/src/lib/pkgconfig/pcpackage.h
new file mode 100644
index 000000000..794e2fc40
--- /dev/null
+++ b/src/lib/pkgconfig/pcpackage.h
@@ -0,0 +1,218 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 PC_PACKAGE_H
+#define PC_PACKAGE_H
+
+#include <variant.h>
+
+#include <map>
+#include <optional>
+#include <stdexcept>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+namespace qbs {
+
+class PcPackage
+{
+public:
+ struct Flag
+ {
+ enum class Type {
+ LibraryName = (1 << 0),
+ StaticLibraryName = (1 << 1),
+ LibraryPath = (1 << 2),
+ Framework = (1 << 3),
+ FrameworkPath = (1 << 4),
+ LinkerFlag = (1 << 5), // this is a lie, this is DriverLinkerFlags
+ IncludePath = (1 << 6),
+ SystemIncludePath = (1 << 7),
+ DirAfterIncludePath = (1 << 8),
+ Define = (1 << 9),
+ CompilerFlag = (1 << 10),
+ };
+ Type type{Type::CompilerFlag};
+ std::string value;
+
+ static std::string_view typeToString(Type t);
+ static std::optional<Type> typeFromString(std::string_view s);
+ };
+
+ struct RequiredVersion
+ {
+ enum class ComparisonType {
+ LessThan,
+ GreaterThan,
+ LessThanEqual,
+ GreaterThanEqual,
+ Equal,
+ NotEqual,
+ AlwaysMatch
+ };
+
+ std::string name;
+ ComparisonType comparison{ComparisonType::GreaterThanEqual};
+ std::string version;
+
+ static std::string_view comparisonToString(ComparisonType t);
+ static std::optional<ComparisonType> comparisonFromString(std::string_view s);
+ };
+
+ std::string filePath;
+ std::string baseFileName;
+ std::string name;
+ std::string version;
+ std::string description;
+ std::string url;
+
+ std::vector<Flag> libs;
+ std::vector<Flag> libsPrivate;
+ std::vector<Flag> cflags;
+
+ std::vector<RequiredVersion> requiresPublic;
+ std::vector<RequiredVersion> requiresPrivate;
+ std::vector<RequiredVersion> conflicts;
+ std::optional<std::string> oldPrefix;
+
+ using VariablesMap = std::map<std::string, std::string, std::less<>>;
+ VariablesMap variables;
+
+ bool uninstalled{false};
+
+ static bool shouldRewriteSysroot(std::string_view sysroot, std::string_view value);
+ PcPackage prependSysroot(std::string_view sysroot) &&;
+ PcPackage removeSystemLibraryPaths(const std::unordered_set<std::string> &libraryPaths) &&;
+};
+
+class PcBrokenPackage
+{
+public:
+ std::string filePath;
+ std::string baseFileName;
+ std::string errorText;
+};
+
+class PcPackageVariant: public Variant::variant<PcPackage, PcBrokenPackage>
+{
+public:
+ using Base = Variant::variant<PcPackage, PcBrokenPackage>;
+ using Base::Base;
+
+ bool isValid() const noexcept { return index() == 0; }
+ bool isBroken() const noexcept { return index() == 1; }
+
+ const PcPackage &asPackage() const { return Variant::get<PcPackage>(*this); }
+ PcPackage &asPackage() { return Variant::get<PcPackage>(*this); }
+
+ const PcBrokenPackage &asBrokenPackage() const { return Variant::get<PcBrokenPackage>(*this); }
+ PcBrokenPackage &asBrokenPackage() { return Variant::get<PcBrokenPackage>(*this); }
+
+ template<typename F>
+ decltype(auto) visit(F &&f) const
+ {
+ return Variant::visit(std::forward<F>(f), static_cast<const Base &>(*this));
+ }
+
+ template<typename F>
+ decltype(auto) visit(F &&f)
+ {
+ return Variant::visit(std::forward<F>(f), static_cast<Base &>(*this));
+ }
+
+ const std::string &getBaseFileName() const
+ {
+ return visit([](auto &&value) noexcept -> const std::string & {
+ return value.baseFileName;
+ });
+ }
+};
+
+class PcException: public std::runtime_error
+{
+public:
+ explicit PcException(const std::string &message) : std::runtime_error(message) {}
+};
+
+inline bool operator==(const PcPackage::Flag &lhs, const PcPackage::Flag &rhs)
+{
+ return lhs.type == rhs.type && lhs.value == rhs.value;
+}
+
+inline bool operator!=(const PcPackage::Flag &lhs, const PcPackage::Flag &rhs)
+{
+ return !(lhs == rhs);
+}
+
+namespace Internal {
+
+// fast versions (no allocations) of the QFileInfo methods
+std::string_view fileName(std::string_view filePath);
+std::string_view completeBaseName(std::string_view filePath);
+std::string_view parentPath(std::string_view path);
+
+inline bool startsWith(std::string_view haystack, std::string_view needle)
+{
+ return haystack.size() >= needle.size() && haystack.compare(0, needle.size(), needle) == 0;
+}
+
+inline bool endsWith(std::string_view haystack, std::string_view needle)
+{
+ return haystack.size() >= needle.size()
+ && haystack.compare(haystack.size() - needle.size(), needle.size(), needle) == 0;
+}
+
+} // Internal
+
+} // namespace qbs
+
+namespace std {
+template<> struct hash<qbs::PcPackage::Flag>
+{
+ size_t operator()(const qbs::PcPackage::Flag &v) const noexcept
+ {
+ return hash<std::string>()(v.value) + size_t(v.type);
+ }
+};
+} // namespace std
+
+#endif // PC_PACKAGE_H
diff --git a/src/lib/pkgconfig/pcparser.cpp b/src/lib/pkgconfig/pcparser.cpp
new file mode 100644
index 000000000..eb07aa5ef
--- /dev/null
+++ b/src/lib/pkgconfig/pcparser.cpp
@@ -0,0 +1,788 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "pcparser.h"
+
+#include "pkgconfig.h"
+
+#if HAS_STD_FILESYSTEM
+# if __has_include(<filesystem>)
+# include <filesystem>
+# else
+# include <experimental/filesystem>
+// We need the alias from std::experimental::filesystem to std::filesystem
+namespace std {
+ namespace filesystem = experimental::filesystem;
+}
+# endif
+#else
+#include <QFileInfo>
+#endif
+
+#include <algorithm>
+#include <cctype>
+#include <fstream>
+#include <stdexcept>
+
+namespace qbs {
+
+using Internal::completeBaseName;
+using Internal::parentPath;
+using Internal::startsWith;
+using Internal::endsWith;
+
+namespace {
+
+// workaround for a missing ctor before c++20
+template<typename It>
+std::string_view makeStringView(It begin, It end)
+{
+ if (begin == end)
+ return {};
+ return std::string_view(&*begin, std::distance(begin, end));
+}
+
+bool readOneLine(std::ifstream &file, std::string &line)
+{
+ bool quoted = false;
+ bool comment = false;
+ int n_read = 0;
+
+ line = {};
+
+ while (true) {
+ char c;
+ file.get(c);
+ const bool ok = file.good();
+
+ if (!ok) {
+ if (quoted)
+ line += '\\';
+
+ return n_read > 0;
+ }
+ n_read++;
+
+ if (c == '\r') {
+ n_read--;
+ continue;
+ }
+
+ if (quoted) {
+ quoted = false;
+
+ switch (c) {
+ case '#':
+ line += '#';
+ break;
+ case '\n':
+ break;
+ default:
+ line += '\\';
+ line += c;
+ break;
+ }
+ } else {
+ switch (c) {
+ case '#':
+ comment = true;
+ break;
+ case '\\':
+ if (!comment)
+ quoted = true;
+ break;
+ case '\n':
+ return n_read > 0;
+ default:
+ if (!comment)
+ line += c;
+ break;
+ }
+ }
+ }
+
+ return n_read > 0;
+}
+
+std::string_view trimmed(std::string_view str)
+{
+ const auto predicate = [](int c){ return std::isspace(c); };
+ const auto left = std::find_if_not(str.begin(), str.end(), predicate);
+ const auto right = std::find_if_not(str.rbegin(), str.rend(), predicate).base();
+ if (right <= left)
+ return {};
+ return makeStringView(left, right);
+}
+
+// based on https://opensource.apple.com/source/distcc/distcc-31.0.81/popt/poptparse.c.auto.html
+std::optional<std::vector<std::string>> splitCommand(std::string_view s)
+{
+ std::vector<std::string> result;
+ std::string arg;
+
+ char quote = '\0';
+
+ for (auto it = s.begin(), end = s.end(); it != end; ++it) {
+ if (quote == *it) {
+ quote = '\0';
+ } else if (quote != '\0') {
+ if (*it == '\\') {
+ ++it;
+ if (it == s.end())
+ return std::nullopt;
+
+ if (*it != quote)
+ arg += '\\';
+ }
+ arg += *it;
+ } else if (isspace(*it)) {
+ if (!arg.empty()) {
+ result.push_back(arg);
+ arg.clear();
+ }
+ } else {
+ switch (*it) {
+ case '"':
+ case '\'':
+ quote = *it;
+ break;
+ case '\\':
+ ++it;
+ if (it == s.end())
+ return std::nullopt;
+ [[fallthrough]];
+ default:
+ arg += *it;
+ break;
+ }
+ }
+ }
+
+ if (!arg.empty())
+ result.push_back(arg);
+
+ return result;
+}
+
+[[noreturn]] void raizeUnknownComparisonException(const PcPackage &pkg, std::string_view verName, std::string_view comp)
+{
+ std::string message;
+ message += "Unknown version comparison operator '";
+ message += comp;
+ message += "' after package name '";
+ message += verName;
+ message += "' in file '";
+ message += pkg.filePath;
+ message += "'";
+ throw PcException(message);
+}
+
+[[noreturn]] void raiseDuplicateFieldException(std::string_view fieldName, std::string_view path)
+{
+ std::string message;
+ message += fieldName;
+ message += " field occurs twice in '";
+ message += path;
+ message += "'";
+ throw PcException(message);
+}
+
+[[noreturn]] void raizeEmptyPackageNameException(const PcPackage &pkg)
+{
+ std::string message;
+ message += "Empty package name in Requires or Conflicts in file '";
+ message += pkg.filePath;
+ message += "'";
+ throw PcException(message);
+}
+
+[[noreturn]] void raizeNoVersionException(const PcPackage &pkg, std::string_view verName)
+{
+ std::string message;
+ message += "Comparison operator but no version after package name '";
+ message += verName;
+ message += "' in file '";
+ message += pkg.filePath;
+ message += "'";
+ throw PcException(message);
+}
+
+[[noreturn]] void raizeDuplicateVariableException(const PcPackage &pkg, std::string_view variable)
+{
+ std::string message;
+ message += "Duplicate definition of variable '";
+ message += variable;
+ message += "' in '";
+ message += pkg.filePath;
+ throw PcException(message);
+}
+
+[[noreturn]] void raizeUndefinedVariableException(const PcPackage &pkg, std::string_view variable)
+{
+ std::string message;
+ message += "Variable '";
+ message += variable;
+ message += "' not defined in '";
+ message += pkg.filePath;
+ throw PcException(message);
+}
+
+bool isModuleSeparator(char c) { return c == ',' || std::isspace(c); }
+bool isModuleOperator(char c) { return c == '<' || c == '>' || c == '!' || c == '='; }
+
+// A module list is a list of modules with optional version specification,
+// separated by commas and/or spaces. Commas are treated just like whitespace,
+// in order to allow stuff like: Requires: @FRIBIDI_PC@, glib, gmodule
+// where @FRIBIDI_PC@ gets substituted to nothing or to 'fribidi'
+
+std::vector<std::string> splitModuleList(std::string_view str)
+{
+ enum class State {
+ // put numbers to help interpret lame debug spew ;-)
+ OutsideModule = 0,
+ InModuleName = 1,
+ BeforeOperator = 2,
+ InOperator = 3,
+ AfterOperator = 4,
+ InModuleVersion = 5
+ };
+
+ std::vector<std::string> result;
+ State state = State::OutsideModule;
+ State last_state = State::OutsideModule;
+
+ auto start = str.begin();
+ const auto end = str.end();
+ auto p = start;
+
+ while (p != end) {
+
+ switch (state) {
+ case State::OutsideModule:
+ if (!isModuleSeparator(*p))
+ state = State::InModuleName;
+ break;
+
+ case State::InModuleName:
+ if (std::isspace(*p)) {
+ // Need to look ahead to determine next state
+ auto s = p;
+ while (s != end && std::isspace (*s))
+ ++s;
+
+ state = State::OutsideModule;
+ if (s != end && isModuleOperator(*s))
+ state = State::BeforeOperator;
+ }
+ else if (isModuleSeparator(*p))
+ state = State::OutsideModule; // comma precludes any operators
+ break;
+
+ case State::BeforeOperator:
+ // We know an operator is coming up here due to lookahead from
+ // IN_MODULE_NAME
+ if (std::isspace(*p))
+ ; // no change
+ else if (isModuleOperator(*p))
+ state = State::InOperator;
+ break;
+
+ case State::InOperator:
+ if (!isModuleOperator(*p))
+ state = State::AfterOperator;
+ break;
+
+ case State::AfterOperator:
+ if (!std::isspace(*p))
+ state = State::InModuleVersion;
+ break;
+
+ case State::InModuleVersion:
+ if (isModuleSeparator(*p))
+ state = State::OutsideModule;
+ break;
+
+ default:
+ break;
+ }
+
+ if (state == State::OutsideModule && last_state != State::OutsideModule) {
+ // We left a module
+ while (start != end && isModuleSeparator(*start))
+ ++start;
+
+ std::string module(&*start, p - start);
+ result.push_back(module);
+
+ // reset start
+ start = p;
+ }
+
+ last_state = state;
+ ++p;
+ }
+
+ if (p != start) {
+ // get the last module
+ while (start != end && isModuleSeparator(*start))
+ ++start;
+ std::string module(&*start, p - start);
+ result.push_back(module);
+ }
+
+ return result;
+}
+
+PcPackage::RequiredVersion::ComparisonType comparisonFromString(
+ const PcPackage &pkg, std::string_view verName, std::string_view comp)
+{
+ using ComparisonType = PcPackage::RequiredVersion::ComparisonType;
+ if (comp.empty())
+ return ComparisonType::AlwaysMatch;
+ if (comp == "=")
+ return ComparisonType::Equal;
+ if (comp == ">=")
+ return ComparisonType::GreaterThanEqual;
+ if (comp == "<=")
+ return ComparisonType::LessThanEqual;
+ if (comp == ">")
+ return ComparisonType::GreaterThan;
+ if (comp == "<")
+ return ComparisonType::LessThan;
+ if (comp == "!=")
+ return ComparisonType::NotEqual;
+
+ raizeUnknownComparisonException(pkg, verName, comp);
+}
+
+} // namespace
+
+PcParser::PcParser(const PkgConfig &pkgConfig)
+ : m_pkgConfig(pkgConfig)
+{
+
+}
+
+PcPackageVariant PcParser::parsePackageFile(const std::string &path)
+try
+{
+ PcPackage package;
+
+ if (path.empty())
+ return package;
+
+ std::ifstream file(path);
+
+ if (!file.is_open())
+ throw PcException(std::string("Can't open file ") + path);
+
+ package.baseFileName = std::string{completeBaseName(path)};
+#if HAS_STD_FILESYSTEM
+ const auto fsPath = std::filesystem::path(path);
+ package.filePath = fsPath.generic_string();
+ package.variables["pcfiledir"] = fsPath.parent_path().generic_string();
+#else
+ QFileInfo fileInfo(QString::fromStdString(path));
+ package.filePath = fileInfo.absoluteFilePath().toStdString();
+ package.variables["pcfiledir"] = fileInfo.absolutePath().toStdString();
+#endif
+
+ std::string line;
+ while (readOneLine(file, line))
+ parseLine(package, line);
+ return package;
+} catch(const PcException &ex) {
+ return PcBrokenPackage{path, std::string{completeBaseName(path)}, ex.what()};
+}
+
+std::string PcParser::trimAndSubstitute(const PcPackage &pkg, std::string_view str) const
+{
+ str = trimmed(str);
+
+ std::string result;
+
+ while (!str.empty()) {
+ if (startsWith(str, "$$")) {
+ // escaped $
+ result += '$';
+ str.remove_prefix(2); // cut "$$"
+ } else if (startsWith(str, "${")) {
+ // variable
+ str.remove_prefix(2); // cut "${"
+ const auto it = std::find(str.begin(), str.end(), '}');
+ // funny, original pkg-config simply reads all available memory here
+ if (it == str.end())
+ throw PcException("Missing closing '}'");
+
+ const std::string_view varname = str.substr(0, std::distance(str.begin(), it));
+
+ // past brace
+ str.remove_prefix(varname.size());
+ str.remove_prefix(1);
+
+ const auto varval = m_pkgConfig.packageGetVariable(pkg, varname);
+
+ if (!varval)
+ raizeUndefinedVariableException(pkg, varname);
+
+ result += *varval;
+ } else {
+ result += str.front();
+ str.remove_prefix(1);
+ }
+ }
+
+ return result;
+}
+
+std::string PcParser::evaluateVariable(
+ PcPackage &pkg, std::string_view tag, std::string_view str) const
+{
+ static constexpr std::string_view prefixVariable = "prefix";
+ if (m_pkgConfig.options().definePrefix) {
+ if (tag == prefixVariable) {
+ std::string_view prefix = pkg.filePath;
+ prefix = parentPath(prefix);
+ if (completeBaseName(prefix) == "pkgconfig") {
+ prefix = parentPath(prefix);
+ prefix = parentPath(prefix);
+ }
+ pkg.oldPrefix = std::string(str);
+ if (!prefix.empty())
+ str = prefix;
+ return std::string(str);
+ } else if (pkg.oldPrefix
+ && str.size() > pkg.oldPrefix->size()
+ && str.substr(0, pkg.oldPrefix->size()) == *pkg.oldPrefix
+ && str[pkg.oldPrefix->size()] == '/') {
+ auto result = pkg.variables["prefix"];
+ result += str.substr(pkg.oldPrefix->size());
+ return trimAndSubstitute(pkg, result);
+ }
+ }
+
+ return trimAndSubstitute(pkg, str);
+}
+
+void PcParser::parseStringField(
+ PcPackage &pkg,
+ std::string &field,
+ std::string_view fieldName,
+ std::string_view str)
+{
+ if (!field.empty())
+ raiseDuplicateFieldException(fieldName, pkg.filePath);
+
+ field = trimAndSubstitute(pkg, str);
+}
+
+void PcParser::parseLibs(
+ PcPackage &pkg,
+ std::vector<PcPackage::Flag> &libs,
+ std::string_view fieldName,
+ std::string_view str)
+{
+ // Strip out -l and -L flags, put them in a separate list.
+
+ if (!libs.empty())
+ raiseDuplicateFieldException(fieldName, pkg.filePath);
+
+ const auto trimmed = trimAndSubstitute(pkg, str);
+ if (trimmed.empty())
+ return;
+
+ const auto argv = splitCommand(trimmed);
+ if (!argv)
+ throw PcException("Couldn't parse Libs field into an argument vector");
+
+ libs = doParseLibs(*argv);
+}
+
+std::vector<PcPackage::Flag> PcParser::doParseLibs(const std::vector<std::string> &argv)
+{
+ std::vector<PcPackage::Flag> libs;
+ libs.reserve(argv.size());
+
+ for (auto it = argv.begin(), end = argv.end(); it != end; ++it) {
+ PcPackage::Flag flag;
+ const auto escapedArgument = trimmed(*it);
+ std::string_view arg(escapedArgument);
+
+ // -lib: is used by the C# compiler for libs; it's not an -l flag.
+ if (startsWith(arg, "-l") && !startsWith(arg, "-lib:")) {
+ arg.remove_prefix(2);
+ arg = trimmed(arg);
+
+ flag.type = PcPackage::Flag::Type::LibraryName;
+ flag.value += arg;
+ } else if (startsWith(arg, "-L")) {
+ arg.remove_prefix(2);
+ arg = trimmed(arg);
+
+ flag.type = PcPackage::Flag::Type::LibraryPath;
+ flag.value += arg;
+ } else if ((arg == "-framework" /*|| arg == "-Wl,-framework"*/) && it + 1 != end) {
+ // macOS has a -framework Foo which is really one option,
+ // so we join those to avoid having -framework Foo
+ // -framework Bar being changed into -framework Foo Bar
+ // later
+ const auto framework = trimmed(*(it + 1));
+ flag.type = PcPackage::Flag::Type::Framework;
+ flag.value += framework;
+ ++it;
+ } else if (startsWith(arg, "-F")) {
+ arg.remove_prefix(2);
+ arg = trimmed(arg);
+
+ flag.type = PcPackage::Flag::Type::FrameworkPath;
+ flag.value += arg;
+
+ } else if (!startsWith(arg, "-") && (endsWith(arg, ".a") || endsWith(arg, ".lib"))) {
+ flag.type = PcPackage::Flag::Type::StaticLibraryName;
+ flag.value += arg;
+ } else if (!arg.empty()) {
+ flag.type = PcPackage::Flag::Type::LinkerFlag;
+ flag.value += arg;
+ } else {
+ continue;
+ }
+ libs.push_back(flag);
+ }
+ return libs;
+}
+
+void PcParser::parseCFlags(PcPackage &pkg, std::string_view str)
+{
+ // Strip out -I, -D, -isystem and idirafter flags, put them in a separate lists.
+
+ if (!pkg.cflags.empty())
+ raiseDuplicateFieldException("Cflags", pkg.filePath);
+
+ const auto command = trimAndSubstitute(pkg, str);
+ if (command.empty())
+ return;
+
+ const auto argv = splitCommand(command);
+ if (!argv)
+ throw PcException("Couldn't parse Cflags field into an argument vector");
+
+ std::vector<PcPackage::Flag> cflags;
+ cflags.reserve(argv->size());
+
+ for (auto it = argv->begin(), end = argv->end(); it != end; ++it) {
+ PcPackage::Flag flag;
+ const auto escapedArgument = trimmed(*it);
+ std::string_view arg(escapedArgument);
+
+ if (startsWith(arg, "-I")) {
+ arg.remove_prefix(2);
+ arg = trimmed(arg);
+
+ flag.type = PcPackage::Flag::Type::IncludePath;
+ flag.value += arg;
+ } else if (startsWith(arg, "-D")) {
+ arg.remove_prefix(2);
+ arg = trimmed(arg);
+
+ flag.type = PcPackage::Flag::Type::Define;
+ flag.value += arg;
+ } else if (arg == "-isystem" && it + 1 != end) {
+ flag.type = PcPackage::Flag::Type::SystemIncludePath;
+ flag.value = trimmed(*(it + 1));
+ ++it;
+ } else if (arg == "-idirafter" && it + 1 != end) {
+ flag.type = PcPackage::Flag::Type::DirAfterIncludePath;
+ flag.value = trimmed(*(it + 1));
+ ++it;
+ } else if (!arg.empty()) {
+ flag.type = PcPackage::Flag::Type::CompilerFlag;
+ flag.value += arg;
+ } else {
+ continue;
+ }
+ cflags.push_back(flag);
+ }
+ pkg.cflags = std::move(cflags);
+}
+
+std::vector<PcPackage::RequiredVersion> PcParser::parseModuleList(PcPackage &pkg, std::string_view str)
+{
+ using ComparisonType = PcPackage::RequiredVersion::ComparisonType;
+
+ std::vector<PcPackage::RequiredVersion> result;
+ auto split = splitModuleList(str);
+
+ for (auto &module: split) {
+ PcPackage::RequiredVersion ver;
+
+ auto p = module.begin();
+ const auto end = module.end();
+
+ ver.comparison = ComparisonType::AlwaysMatch;
+
+ auto start = p;
+
+ while (p != end && !std::isspace(*p))
+ ++p;
+
+ const auto name = makeStringView(start, p);
+
+ if (name.empty())
+ raizeEmptyPackageNameException(pkg);
+
+ ver.name = std::string(name);
+
+ while (p != end && std::isspace(*p))
+ ++p;
+
+ start = p;
+
+ while (p != end && !std::isspace(*p))
+ ++p;
+
+ const auto comp = makeStringView(start, p);
+ ver.comparison = comparisonFromString(pkg, ver.name, comp);
+
+ while (p != end && std::isspace(*p))
+ ++p;
+
+ start = p;
+
+ while (p != end && !std::isspace(*p))
+ ++p;
+
+ const auto version = makeStringView(start, p);
+
+ while (p != end && std::isspace(*p))
+ ++p;
+
+ if (ver.comparison != ComparisonType::AlwaysMatch && version.empty())
+ raizeNoVersionException(pkg, ver.name);
+
+ ver.version = std::string(version);
+
+ result.push_back(ver);
+ }
+
+ return result;
+}
+
+void PcParser::parseVersionsField(
+ PcPackage &pkg,
+ std::vector<PcPackage::RequiredVersion> &modules,
+ std::string_view fieldName,
+ std::string_view str)
+{
+ if (!modules.empty())
+ raiseDuplicateFieldException(fieldName, pkg.filePath);
+
+ const auto trimmed = trimAndSubstitute(pkg, str);
+ modules = parseModuleList(pkg, trimmed.c_str());
+}
+
+void PcParser::parseLine(PcPackage &pkg, std::string_view str)
+{
+ str = trimmed(str);
+ if (str.empty())
+ return;
+
+ auto getFirstWord = [](std::string_view s) {
+ size_t pos = 0;
+ for (; pos < s.size(); ++pos) {
+ auto p = s.data() + pos;
+ if ((*p < 'A' || *p > 'Z') && (*p < 'a' || *p > 'z') && (*p < '0' || *p > '9')
+ && *p != '_' && *p != '.') {
+ break;
+ }
+ }
+ return s.substr(0, pos);
+ };
+
+ const auto tag = getFirstWord(str);
+
+ str.remove_prefix(tag.size()); // cut tag
+ str = trimmed(str);
+
+ if (str.empty())
+ return;
+
+ if (str.front() == ':') {
+ // keyword
+ str.remove_prefix(1); // cut ':'
+ str = trimmed(str);
+
+ if (tag == "Name")
+ parseStringField(pkg, pkg.name, tag, str);
+ else if (tag == "Description")
+ parseStringField(pkg, pkg.description, tag, str);
+ else if (tag == "Version")
+ parseStringField(pkg, pkg.version, tag, str);
+ else if (tag == "Requires.private")
+ parseVersionsField(pkg, pkg.requiresPrivate, tag, str);
+ else if (tag == "Requires")
+ parseVersionsField(pkg, pkg.requiresPublic, tag, str);
+ else if (tag == "Libs.private")
+ parseLibs(pkg, pkg.libsPrivate, "Libs.private", str);
+ else if (tag == "Libs")
+ parseLibs(pkg, pkg.libs, "Libs", str);
+ else if (tag == "Cflags" || tag == "CFlags")
+ parseCFlags(pkg, str);
+ else if (tag == "Conflicts")
+ parseVersionsField(pkg, pkg.conflicts, tag, str);
+ else if (tag == "URL")
+ parseStringField(pkg, pkg.url, tag, str);
+ else {
+ // we don't error out on unknown keywords because they may
+ // represent additions to the .pc file format from future
+ // versions of pkg-config.
+ return;
+ }
+ } else if (str.front() == '=') {
+ // variable
+
+ str.remove_prefix(1); // cut '='
+ str = trimmed(str);
+
+ const auto value = evaluateVariable(pkg, tag, str);
+ if (!pkg.variables.insert({std::string(tag), value}).second)
+ raizeDuplicateVariableException(pkg, tag);
+ }
+}
+
+} // namespace qbs
diff --git a/src/lib/pkgconfig/pcparser.h b/src/lib/pkgconfig/pcparser.h
new file mode 100644
index 000000000..a8a5e5cd4
--- /dev/null
+++ b/src/lib/pkgconfig/pcparser.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 PC_PARSER_H
+#define PC_PARSER_H
+
+#include "pcpackage.h"
+
+namespace qbs {
+
+class PkgConfig;
+
+class PcParser
+{
+public:
+ explicit PcParser(const PkgConfig &pkgConfig);
+
+ PcPackageVariant parsePackageFile(const std::string &path);
+
+private:
+ std::string trimAndSubstitute(const PcPackage &pkg, std::string_view str) const;
+ std::string evaluateVariable(PcPackage &pkg, std::string_view tag, std::string_view str) const;
+ void parseStringField(
+ PcPackage &pkg,
+ std::string &field,
+ std::string_view fieldName,
+ std::string_view str);
+ void parseLibs(
+ PcPackage &pkg,
+ std::vector<PcPackage::Flag> &libs,
+ std::string_view fieldName,
+ std::string_view str);
+ std::vector<PcPackage::Flag> doParseLibs(const std::vector<std::string> &argv);
+ void parseCFlags(PcPackage &pkg, std::string_view str);
+ std::vector<PcPackage::RequiredVersion> parseModuleList(PcPackage &pkg, std::string_view str);
+ void parseVersionsField(
+ PcPackage &pkg,
+ std::vector<PcPackage::RequiredVersion> &modules,
+ std::string_view fieldName,
+ std::string_view str);
+ void parseLine(PcPackage &pkg, std::string_view str);
+
+private:
+ const PkgConfig &m_pkgConfig;
+};
+
+} // namespace qbs
+
+#endif // PC_PARSER_H
diff --git a/src/lib/pkgconfig/pkgconfig.cpp b/src/lib/pkgconfig/pkgconfig.cpp
new file mode 100644
index 000000000..6b3bde46c
--- /dev/null
+++ b/src/lib/pkgconfig/pkgconfig.cpp
@@ -0,0 +1,337 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 "pkgconfig.h"
+#include "pcparser.h"
+
+#if HAS_STD_FILESYSTEM
+# if __has_include(<filesystem>)
+# include <filesystem>
+# else
+# include <experimental/filesystem>
+// We need the alias from std::experimental::filesystem to std::filesystem
+namespace std {
+ namespace filesystem = experimental::filesystem;
+}
+# endif
+#else
+# include <QtCore/QDir>
+# include <QtCore/QFileInfo>
+#endif
+
+#include <algorithm>
+#include <iostream>
+
+namespace qbs {
+
+namespace {
+
+std::string varToEnvVar(std::string_view pkg, std::string_view var)
+{
+ auto result = std::string("PKG_CONFIG_");
+ result += pkg;
+ result += '_';
+ result += var;
+
+ for (char &p : result) {
+ int c = std::toupper(p);
+
+ if (!std::isalnum(c))
+ c = '_';
+
+ p = char(c);
+ }
+
+ return result;
+}
+
+std::vector<std::string> split(std::string_view str, const char delim)
+{
+ std::vector<std::string> result;
+ size_t prev = 0;
+ size_t pos = 0;
+ do {
+ pos = str.find(delim, prev);
+ if (pos == std::string::npos) pos = str.length();
+ std::string token(str.substr(prev, pos - prev));
+ if (!token.empty())
+ result.push_back(token);
+ prev = pos + 1;
+ } while (pos < str.length() && prev < str.length());
+ return result;
+}
+
+constexpr inline char listSeparator() noexcept
+{
+#if defined(WIN32)
+ return ';';
+#else
+ return ':';
+#endif
+}
+
+[[noreturn]] void raizeUnknownPackageException(std::string_view package)
+{
+ std::string message;
+ message += "Can't find package '";
+ message += package;
+ message += "'";
+ throw PcException(message);
+}
+
+template <class C>
+C &operator<<(C &container, const C &other)
+{
+ container.insert(container.end(), other.cbegin(), other.cend());
+ return container;
+}
+
+} // namespace
+
+PkgConfig::PkgConfig()
+ : PkgConfig(Options())
+{
+}
+
+PkgConfig::PkgConfig(Options options)
+ : m_options(std::move(options))
+{
+ if (m_options.libDirs.empty())
+ m_options.libDirs = split(PKG_CONFIG_PC_PATH, listSeparator());
+
+ if (m_options.topBuildDir.empty())
+ m_options.topBuildDir = "$(top_builddir)"; // pkg-config sets this for automake =)
+
+ if (m_options.systemLibraryPaths.empty())
+ m_options.systemLibraryPaths = split(PKG_CONFIG_SYSTEM_LIBRARY_PATH, ':');
+
+ // this is weird on Windows, but that's what pkg-config does
+ if (m_options.sysroot.empty())
+ m_options.globalVariables["pc_sysrootdir"] = "/";
+ else
+ m_options.globalVariables["pc_sysrootdir"] = m_options.sysroot;
+ m_options.globalVariables["pc_top_builddir"] = m_options.topBuildDir;
+
+ m_packages = findPackages();
+}
+
+const PcPackageVariant &PkgConfig::getPackage(std::string_view baseFileName) const
+{
+ // heterogeneous comparator so we can search the package using string_view
+ const auto lessThan = [](const PcPackageVariant &package, const std::string_view &name)
+ {
+ return package.visit([name](auto &&value) noexcept {
+ return value.baseFileName < name;
+ });
+ };
+
+ const auto testPackage = [baseFileName](const PcPackageVariant &package) {
+ return package.visit([baseFileName](auto &&value) noexcept {
+ return baseFileName != value.baseFileName;
+ });
+ };
+
+ const auto it = std::lower_bound(m_packages.begin(), m_packages.end(), baseFileName, lessThan);
+ if (it == m_packages.end() || testPackage(*it))
+ raizeUnknownPackageException(baseFileName);
+ return *it;
+}
+
+std::optional<std::string_view> PkgConfig::packageGetVariable(
+ const PcPackage &pkg, std::string_view var) const
+{
+ std::optional<std::string_view> result;
+
+ if (var.empty())
+ return result;
+
+ const auto &globals = m_options.globalVariables;
+ if (auto it = globals.find(var); it != globals.end())
+ result = it->second;
+
+ // Allow overriding specific variables using an environment variable of the
+ // form PKG_CONFIG_$PACKAGENAME_$VARIABLE
+ if (!pkg.baseFileName.empty()) {
+ const std::string envVariable = varToEnvVar(pkg.baseFileName, var);
+ const auto it = m_options.systemVariables.find(envVariable);
+ if (it != m_options.systemVariables.end())
+ result = it->second;
+ }
+
+ if (!result) {
+ if (const auto it = pkg.variables.find(var); it != pkg.variables.end())
+ result = it->second;
+ }
+
+ return result;
+}
+
+#if HAS_STD_FILESYSTEM
+std::vector<std::string> getPcFilePaths(const std::vector<std::string> &searchPaths)
+{
+ std::vector<std::filesystem::path> paths;
+
+ for (const auto &searchPath : searchPaths) {
+ if (!std::filesystem::exists(std::filesystem::directory_entry(searchPath).status()))
+ continue;
+ const auto dir = std::filesystem::directory_iterator(searchPath);
+ std::copy_if(
+ std::filesystem::begin(dir),
+ std::filesystem::end(dir),
+ std::back_inserter(paths),
+ [](const auto &entry) { return entry.path().extension() == ".pc"; }
+ );
+ }
+ std::vector<std::string> result;
+ std::transform(
+ std::begin(paths),
+ std::end(paths),
+ std::back_inserter(result),
+ [](const auto &path) { return path.generic_string(); }
+ );
+ return result;
+}
+#else
+std::vector<std::string> getPcFilePaths(const std::vector<std::string> &searchPaths)
+{
+ std::vector<std::string> result;
+ for (const auto &path : searchPaths) {
+ QDir dir(QString::fromStdString(path));
+ const auto paths = dir.entryList({QStringLiteral("*.pc")});
+ std::transform(
+ std::begin(paths),
+ std::end(paths),
+ std::back_inserter(result),
+ [&dir](const auto &path) { return dir.filePath(path).toStdString(); }
+ );
+ }
+ return result;
+}
+#endif
+
+PcBrokenPackage makeMissingDependency(
+ const PcPackage &package, const PcPackage::RequiredVersion &depVersion)
+{
+ std::string message;
+ message += "Package ";
+ message += package.name;
+ message += " requires package ";
+ message += depVersion.name;
+ message += " but it is not found";
+ return PcBrokenPackage{
+ package.filePath, package.baseFileName, std::move(message)};
+}
+
+PcBrokenPackage makeBrokenDependency(
+ const PcPackage &package, const PcPackage::RequiredVersion &depVersion)
+{
+ std::string message;
+ message += "Package ";
+ message += package.name;
+ message += " requires package ";
+ message += depVersion.name;
+ message += " but it is broken";
+ return PcBrokenPackage{
+ package.filePath, package.baseFileName, std::move(message)};
+}
+
+PcBrokenPackage makeVersionMismatchDependency(
+ const PcPackage &package,
+ const PcPackage &depPackage,
+ const PcPackage::RequiredVersion &depVersion)
+{
+ std::string message;
+ message += "Package ";
+ message += package.name;
+ message += " requires version ";
+ message += PcPackage::RequiredVersion::comparisonToString(
+ depVersion.comparison);
+ message += depVersion.version;
+ message += " but ";
+ message += depPackage.version;
+ message += " is present";
+ return PcBrokenPackage{
+ package.filePath, package.baseFileName, std::move(message)};
+}
+
+PkgConfig::Packages PkgConfig::findPackages() const
+{
+ Packages result;
+ PcParser parser(*this);
+
+ const auto systemLibraryPaths = !m_options.allowSystemLibraryPaths ?
+ std::unordered_set<std::string>(
+ m_options.systemLibraryPaths.begin(),
+ m_options.systemLibraryPaths.end()) : std::unordered_set<std::string>();
+
+ auto allSearchPaths = m_options.extraPaths;
+ allSearchPaths.insert(
+ allSearchPaths.end(), m_options.libDirs.begin(), m_options.libDirs.end());
+ const auto pcFilePaths = getPcFilePaths(allSearchPaths);
+
+ for (const auto &pcFilePath : pcFilePaths) {
+ if (m_options.disableUninstalled) {
+ if (pcFilePath.find("-uninstalled.pc") != std::string::npos)
+ continue;
+ }
+
+ auto pkg = parser.parsePackageFile(pcFilePath);
+ pkg.visit([&](auto &value) {
+ using T = std::decay_t<decltype(value)>;
+ if constexpr (std::is_same_v<T, PcPackage>) { // NOLINT
+ value = std::move(value)
+ // Weird, but pkg-config removes libs first and only then appends
+ // sysroot. Looks like sysroot has to be used with
+ // allowSystemLibraryPaths: true
+ .removeSystemLibraryPaths(systemLibraryPaths)
+ .prependSysroot(m_options.sysroot);
+ }
+ });
+ result.emplace_back(std::move(pkg));
+ }
+
+ const auto lessThanPackage = [](const PcPackageVariant &lhs, const PcPackageVariant &rhs)
+ {
+ return lhs.getBaseFileName() < rhs.getBaseFileName();
+ };
+ std::sort(result.begin(), result.end(), lessThanPackage);
+ return result;
+}
+
+} // namespace qbs
diff --git a/src/lib/pkgconfig/pkgconfig.h b/src/lib/pkgconfig/pkgconfig.h
new file mode 100644
index 000000000..fd4cc3253
--- /dev/null
+++ b/src/lib/pkgconfig/pkgconfig.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 PKGCONFIG_H
+#define PKGCONFIG_H
+
+#include "pcpackage.h"
+
+namespace qbs {
+
+class PkgConfig
+{
+public:
+ struct Options {
+ using VariablesMap = PcPackage::VariablesMap;
+
+ std::vector<std::string> libDirs; // PKG_CONFIG_LIBDIR
+ std::vector<std::string> extraPaths; // PKG_CONFIG_PATH
+ std::string sysroot; // PKG_CONFIG_SYSROOT_DIR
+ std::string topBuildDir; // PKG_CONFIG_TOP_BUILD_DIR
+ bool allowSystemLibraryPaths{false}; // PKG_CONFIG_ALLOW_SYSTEM_LIBS
+ std::vector<std::string> systemLibraryPaths; // PKG_CONFIG_SYSTEM_LIBRARY_PATH
+ bool disableUninstalled{true}; // PKG_CONFIG_DISABLE_UNINSTALLED
+ bool staticMode{false};
+ bool definePrefix{false};
+ VariablesMap globalVariables;
+ VariablesMap systemVariables;
+ };
+
+ using Packages = std::vector<PcPackageVariant>;
+
+ explicit PkgConfig();
+ explicit PkgConfig(Options options);
+
+ const Options &options() const { return m_options; }
+ const Packages &packages() const { return m_packages; }
+ const PcPackageVariant &getPackage(std::string_view baseFileName) const;
+
+ std::optional<std::string_view> packageGetVariable(
+ const PcPackage &pkg, std::string_view var) const;
+
+private:
+ Packages findPackages() const;
+
+private:
+ Options m_options;
+
+ Packages m_packages;
+};
+
+} // namespace qbs
+
+#endif // PKGCONFIG_H
diff --git a/src/lib/pkgconfig/pkgconfig.qbs b/src/lib/pkgconfig/pkgconfig.qbs
new file mode 100644
index 000000000..a32eb775b
--- /dev/null
+++ b/src/lib/pkgconfig/pkgconfig.qbs
@@ -0,0 +1,68 @@
+import qbs.FileInfo
+import qbs.Utilities
+
+QbsStaticLibrary {
+ Depends { name: "cpp" }
+ Depends { name: "qbsbuildconfig" }
+ Depends { name: "qbsvariant" }
+
+ property stringList pcPaths: {
+ var result = [];
+ result.push(FileInfo.joinPaths(qbs.installPrefix, qbsbuildconfig.libDirName, "pkgconfig"));
+ result.push(FileInfo.joinPaths(qbs.installPrefix, "share", "pkgconfig"));
+ if (qbs.hostOS.contains("unix")) {
+ result.push("/usr/lib/pkgconfig/")
+ result.push("/usr/share/pkgconfig/")
+ }
+ return result
+ }
+ readonly property stringList pcPathsString: pcPaths.join(qbs.pathListSeparator)
+
+ property bool withQtSupport: true
+
+ readonly property stringList publicDefines: {
+ var result = [];
+ if (withQtSupport)
+ result.push("QBS_PC_WITH_QT_SUPPORT=1")
+ else
+ result.push("QBS_PC_WITH_QT_SUPPORT=0")
+ return result;
+ }
+
+ name: "qbspkgconfig"
+
+ files: [
+ "pcpackage.cpp",
+ "pcpackage.h",
+ "pcparser.cpp",
+ "pcparser.h",
+ "pkgconfig.cpp",
+ "pkgconfig.h",
+ ]
+
+ cpp.defines: {
+ var result = [
+ "PKG_CONFIG_PC_PATH=\"" + pcPathsString + "\"",
+ "PKG_CONFIG_SYSTEM_LIBRARY_PATH=\"/usr/" + qbsbuildconfig.libDirName + "\"",
+ ]
+ if ((qbs.targetOS.contains("darwin")
+ && Utilities.versionCompare(cpp.minimumMacosVersion, "10.15") < 0)
+ || qbs.toolchain.contains("mingw"))
+ result.push("HAS_STD_FILESYSTEM=0")
+ else
+ result.push("HAS_STD_FILESYSTEM=1")
+ result = result.concat(publicDefines);
+ return result
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ Depends { name: "qbsvariant" }
+ cpp.defines: exportingProduct.publicDefines
+ cpp.staticLibraries: {
+ if (qbs.toolchainType === "gcc" && cpp.compilerVersionMajor < 9)
+ return ["stdc++fs"];
+ return [];
+ }
+ }
+}
diff --git a/src/lib/scriptengine/include/QtScript/qtscriptglobal.h b/src/lib/scriptengine/include/QtScript/qtscriptglobal.h
deleted file mode 100644
index 8b1e09bb1..000000000
--- a/src/lib/scriptengine/include/QtScript/qtscriptglobal.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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_QTSCRIPTGLOBAL_H
-#define QBS_QTSCRIPTGLOBAL_H
-
-#define Q_SCRIPT_EXPORT
-#define Q_SCRIPTTOOLS_EXPORT
-
-#endif // include guard
diff --git a/src/lib/scriptengine/scriptengine.pro b/src/lib/scriptengine/scriptengine.pro
deleted file mode 100644
index c5ad5f07a..000000000
--- a/src/lib/scriptengine/scriptengine.pro
+++ /dev/null
@@ -1,92 +0,0 @@
-TARGET = qbsscriptengine
-include(../library.pri)
-INSTALLS =
-
-QT = core-private
-
-DEFINES += QT_BUILD_SCRIPT_LIB
-
-DEFINES += JSC=QTJSC jscyyparse=qtjscyyparse jscyylex=qtjscyylex jscyyerror=qtjscyyerror WTF=QTWTF
-DEFINES += QT_NO_USING_NAMESPACE
-
-CONFIG += building-libs
-CONFIG += staticlib
-
-GENERATED_SOURCES_DIR = generated
-
-CONFIG += QTDIR_build
-include(../../shared/qtscript/src/3rdparty/javascriptcore/WebKit.pri)
-
-# Disable a few warnings on Windows.
-# These are in addition to the ones disabled in WebKit.pri
-msvc: QMAKE_CXXFLAGS += -wd4396 -wd4099
-else: QMAKE_CXXFLAGS += -Wno-deprecated
-
-# We cannot afford -O0 for QtScript even in debug builds.
-QMAKE_CXXFLAGS_DEBUG += -O2
-
-darwin {
- DEFINES += ENABLE_JSC_MULTIPLE_THREADS=0
- contains(QT_CONFIG, coreservices) {
- LIBS_PRIVATE += -framework CoreServices
- } else {
- LIBS_PRIVATE += -framework CoreFoundation
- }
-}
-win32 {
- LIBS += -lwinmm
-}
-
-# Suppress 'LEAK' messages (see QTBUG-18201)
-DEFINES += LOG_DISABLED=1
-
-JAVASCRIPTCORE_JIT = no
-include(../../shared/qtscript/src/3rdparty/javascriptcore/JavaScriptCore/JavaScriptCore.pri)
-
-# This line copied from WebCore.pro
-DEFINES += WTF_USE_JAVASCRIPTCORE_BINDINGS=1 WTF_CHANGES=1
-
-CONFIG(release, debug|release): DEFINES += NDEBUG
-
-# Avoid JSC C API functions being exported.
-DEFINES += JS_NO_EXPORT
-
-!build_pass {
- qtPrepareTool(QMAKE_SYNCQT, syncqt, , system)
- QMAKE_SYNCQT += \
- -minimal -version $$[QT_VERSION] \
- -outdir $$system_quote($$system_path($$OUT_PWD)) \
- $$system_quote($$system_path($$clean_path($$PWD/../../shared/qtscript)))
- !system($$QMAKE_SYNCQT): error("Failed to execute syncqt for the bundled QtScript module.")
-}
-
-INCLUDEPATH += \
- $$PWD/include \
- $$OUT_PWD/include \
- $$OUT_PWD/include/QtScript/$$[QT_VERSION]/QtScript \
- $$PWD/../../shared/qtscript/src/script \
- $$PWD/../../shared/qtscript/src/script/api
-
-include(../../shared/qtscript/src/script/api/api.pri)
-include(../../shared/qtscript/src/script/parser/parser.pri)
-
-BRIDGESRCDIR = ../../shared/qtscript/src/script/bridge
-SOURCES += \
- $$BRIDGESRCDIR/qscriptactivationobject.cpp \
- $$BRIDGESRCDIR/qscriptclassobject.cpp \
- $$BRIDGESRCDIR/qscriptfunction.cpp \
- $$BRIDGESRCDIR/qscriptglobalobject.cpp \
- $$BRIDGESRCDIR/qscriptobject.cpp \
- $$BRIDGESRCDIR/qscriptqobject.cpp \
- $$BRIDGESRCDIR/qscriptstaticscopeobject.cpp \
- $$BRIDGESRCDIR/qscriptvariant.cpp
-
-HEADERS += \
- $$BRIDGESRCDIR/qscriptactivationobject_p.h \
- $$BRIDGESRCDIR/qscriptclassobject_p.h \
- $$BRIDGESRCDIR/qscriptfunction_p.h \
- $$BRIDGESRCDIR/qscriptglobalobject_p.h \
- $$BRIDGESRCDIR/qscriptobject_p.h \
- $$BRIDGESRCDIR/qscriptqobject_p.h \
- $$BRIDGESRCDIR/qscriptstaticscopeobject_p.h \
- $$BRIDGESRCDIR/qscriptvariant_p.h
diff --git a/src/lib/scriptengine/scriptengine.qbs b/src/lib/scriptengine/scriptengine.qbs
deleted file mode 100644
index 4c03d5e36..000000000
--- a/src/lib/scriptengine/scriptengine.qbs
+++ /dev/null
@@ -1,433 +0,0 @@
-import qbs
-import qbs.File
-import qbs.FileInfo
-import qbs.Process
-
-Project {
- QbsLibrary {
- condition: qbsbuildconfig.useBundledQtScript || !Qt.script.present
- Depends {
- name: "Qt.script"
- condition: !qbsbuildconfig.useBundledQtScript
- required: false
- }
- Depends { name: "QtScriptFwdHeaders" }
- Depends { name: "cpp" }
- Depends { name: "Qt"; submodules: ["core-private"] }
- type: ["staticlibrary"]
- name: "qbsscriptengine"
-
- generatePkgConfigFile: false
- generateQbsModule: false
-
- property bool useSystemMalloc: !qbs.targetOS.contains("macos")
- && !qbs.targetOS.contains("unix")
- property string qtscriptPath: "../../shared/qtscript/src/"
-
- cpp.includePaths: {
- var result = base.concat(
- ".",
- "include"
- );
-
- var jscBaseDir = qtscriptPath + "3rdparty/javascriptcore";
- result.push(jscBaseDir);
- jscBaseDir += "/JavaScriptCore";
- result.push(jscBaseDir);
-
- var jscSubDirs = [
- "assembler",
- "bytecode",
- "bytecompiler",
- "debugger",
- "interpreter",
- "jit",
- "parser",
- "pcre",
- "profiler",
- "runtime",
- "wrec",
- "wtf",
- "wtf/unicode",
- "yarr",
- "API",
- "ForwardingHeaders",
- "generated"];
- result = result.concat(jscSubDirs.map(function(s) { return jscBaseDir + '/' + s; }));
-
- result.push(qtscriptPath + "script");
- result.push(qtscriptPath + "script/api");
- result.push(qtscriptPath + "script/parser");
- result = result.concat(QtScriptFwdHeaders.publicIncludePaths,
- QtScriptFwdHeaders.privateIncludePaths);
- return result;
- }
- cpp.defines: {
- var result = base.concat([
- "QT_BUILD_SCRIPT_LIB", "QT_NO_USING_NAMESPACE",
- "JSC=QTJSC", "jscyyparse=qtjscyyparse", "jscyylex=qtjscyylex",
- "jscyyerror=qtjscyyerror",
- "WTF=QTWTF",
- "LOG_DISABLED=1",
- "WTF_USE_JAVASCRIPTCORE_BINDINGS=1", "WTF_CHANGES=1",
- "JS_NO_EXPORT"]);
- if (qbs.buildVariant != "debug")
- result.push("NDEBUG");
- if (qbs.targetOS.contains("macos"))
- result.push("ENABLE_JSC_MULTIPLE_THREADS=0");
-
- // JavaScriptCore
- result.push("BUILDING_QT__", "BUILDING_JavaScriptCore", "BUILDING_WTF",
- "ENABLE_JIT=0", "ENABLE_YARR_JIT=0", "ENABLE_YARR=0");
- if (qbs.targetOS.contains("windows")) {
- // Prevent definition of min, max macros in windows.h
- result.push("NOMINMAX");
- // Enables rand_s
- result.push("_CRT_RAND_S");
- }
-
- // WebKit
- result.push("WTF_USE_ACCELERATED_COMPOSITING");
- if (useSystemMalloc)
- result.push("USE_SYSTEM_MALLOC");
- return result;
- }
- cpp.cxxFlags: {
- var result = base;
- if (qbs.toolchain.contains("gcc")) {
- result.push("-fno-strict-aliasing",
- "-Wall", "-Wreturn-type", "-Wcast-align", "-Wchar-subscripts",
- "-Wformat-security", "-Wreturn-type", "-Wno-unused-parameter",
- "-Wno-sign-compare", "-Wno-switch", "-Wno-switch-enum", "-Wundef",
- "-Wmissing-noreturn", "-Winit-self", "-Wno-deprecated",
- "-Wno-suggest-attribute=noreturn", "-Wno-nonnull-compare");
- } else if (qbs.toolchain.contains("msvc")) {
- result.push("-wd4291", "-wd4344", "-wd4396", "-wd4503", "-wd4800", "-wd4819",
- "-wd4996");
- }
- if (qbs.targetOS.contains("unix"))
- result.push("-pthread");
-
- return result;
- }
- cpp.warningLevel: "none"
- cpp.optimization: "fast" // We cannot afford -O0 for QtScript even in debug builds.
- Properties {
- condition: qbs.targetOS.contains("unix")
- cpp.dynamicLibraries: base.concat(["pthread"])
- }
-
- Group {
- name: "pcre"
- prefix: qtscriptPath + "3rdparty/javascriptcore/JavaScriptCore/pcre/"
- files: [
- "pcre_compile.cpp",
- "pcre_exec.cpp",
- "pcre_tables.cpp",
- "pcre_ucp_searchfuncs.cpp",
- "pcre_xclass.cpp",
- ]
- }
-
- Group {
- name: "system malloc replacement"
- prefix: qtscriptPath + "3rdparty/javascriptcore/JavaScriptCore/"
- condition: !useSystemMalloc
- files: [
- "wtf/TCSystemAlloc.cpp",
- ]
- }
-
- Group {
- name: "JavaScriptCore"
- prefix: qtscriptPath + "3rdparty/javascriptcore/JavaScriptCore/"
- files: [
- "API/JSBase.cpp",
- "API/JSCallbackConstructor.cpp",
- "API/JSCallbackFunction.cpp",
- "API/JSCallbackObject.cpp",
- "API/JSClassRef.cpp",
- "API/JSContextRef.cpp",
- "API/JSObjectRef.cpp",
- "API/JSStringRef.cpp",
- "API/JSValueRef.cpp",
- "API/OpaqueJSString.cpp",
- "assembler/ARMAssembler.cpp",
- "assembler/MacroAssemblerARM.cpp",
- "bytecode/CodeBlock.cpp",
- "bytecode/JumpTable.cpp",
- "bytecode/Opcode.cpp",
- "bytecode/SamplingTool.cpp",
- "bytecode/StructureStubInfo.cpp",
- "bytecompiler/BytecodeGenerator.cpp",
- "bytecompiler/NodesCodegen.cpp",
- "debugger/DebuggerActivation.cpp",
- "debugger/DebuggerCallFrame.cpp",
- "debugger/Debugger.cpp",
- "generated/Grammar.cpp",
- "interpreter/CallFrame.cpp",
- "interpreter/Interpreter.cpp",
- "interpreter/RegisterFile.cpp",
- "parser/Lexer.cpp",
- "parser/Nodes.cpp",
- "parser/ParserArena.cpp",
- "parser/Parser.cpp",
- "profiler/Profile.cpp",
- "profiler/ProfileGenerator.cpp",
- "profiler/ProfileNode.cpp",
- "profiler/Profiler.cpp",
- "runtime/ArgList.cpp",
- "runtime/Arguments.cpp",
- "runtime/ArrayConstructor.cpp",
- "runtime/ArrayPrototype.cpp",
- "runtime/BooleanConstructor.cpp",
- "runtime/BooleanObject.cpp",
- "runtime/BooleanPrototype.cpp",
- "runtime/CallData.cpp",
- "runtime/Collector.cpp",
- "runtime/CommonIdentifiers.cpp",
- "runtime/Completion.cpp",
- "runtime/ConstructData.cpp",
- "runtime/DateConstructor.cpp",
- "runtime/DateConversion.cpp",
- "runtime/DateInstance.cpp",
- "runtime/DatePrototype.cpp",
- "runtime/ErrorConstructor.cpp",
- "runtime/Error.cpp",
- "runtime/ErrorInstance.cpp",
- "runtime/ErrorPrototype.cpp",
- "runtime/ExceptionHelpers.cpp",
- "runtime/Executable.cpp",
- "runtime/FunctionConstructor.cpp",
- "runtime/FunctionPrototype.cpp",
- "runtime/GetterSetter.cpp",
- "runtime/GlobalEvalFunction.cpp",
- "runtime/Identifier.cpp",
- "runtime/InitializeThreading.cpp",
- "runtime/InternalFunction.cpp",
- "runtime/JSActivation.cpp",
- "runtime/JSAPIValueWrapper.cpp",
- "runtime/JSArray.cpp",
- "runtime/JSByteArray.cpp",
- "runtime/JSCell.cpp",
- "runtime/JSFunction.cpp",
- "runtime/JSGlobalData.cpp",
- "runtime/JSGlobalObject.cpp",
- "runtime/JSGlobalObjectFunctions.cpp",
- "runtime/JSImmediate.cpp",
- "runtime/JSLock.cpp",
- "runtime/JSNotAnObject.cpp",
- "runtime/JSNumberCell.cpp",
- "runtime/JSObject.cpp",
- "runtime/JSONObject.cpp",
- "runtime/JSPropertyNameIterator.cpp",
- "runtime/JSStaticScopeObject.cpp",
- "runtime/JSString.cpp",
- "runtime/JSValue.cpp",
- "runtime/JSVariableObject.cpp",
- "runtime/JSWrapperObject.cpp",
- "runtime/LiteralParser.cpp",
- "runtime/Lookup.cpp",
- "runtime/MarkStackPosix.cpp",
- "runtime/MarkStackSymbian.cpp",
- "runtime/MarkStackWin.cpp",
- "runtime/MarkStack.cpp",
- "runtime/MathObject.cpp",
- "runtime/NativeErrorConstructor.cpp",
- "runtime/NativeErrorPrototype.cpp",
- "runtime/NumberConstructor.cpp",
- "runtime/NumberObject.cpp",
- "runtime/NumberPrototype.cpp",
- "runtime/ObjectConstructor.cpp",
- "runtime/ObjectPrototype.cpp",
- "runtime/Operations.cpp",
- "runtime/PropertyDescriptor.cpp",
- "runtime/PropertyNameArray.cpp",
- "runtime/PropertySlot.cpp",
- "runtime/PrototypeFunction.cpp",
- "runtime/RegExpConstructor.cpp",
- "runtime/RegExp.cpp",
- "runtime/RegExpObject.cpp",
- "runtime/RegExpPrototype.cpp",
- "runtime/ScopeChain.cpp",
- "runtime/SmallStrings.cpp",
- "runtime/StringConstructor.cpp",
- "runtime/StringObject.cpp",
- "runtime/StringPrototype.cpp",
- "runtime/StructureChain.cpp",
- "runtime/Structure.cpp",
- "runtime/TimeoutChecker.cpp",
- "runtime/UString.cpp",
- "runtime/UStringImpl.cpp",
- "wtf/Assertions.cpp",
- "wtf/ByteArray.cpp",
- "wtf/CurrentTime.cpp",
- "wtf/DateMath.cpp",
- "wtf/dtoa.cpp",
- "wtf/FastMalloc.cpp",
- "wtf/HashTable.cpp",
- "wtf/MainThread.cpp",
- "wtf/qt/MainThreadQt.cpp",
- "wtf/qt/ThreadingQt.cpp",
- "wtf/RandomNumber.cpp",
- "wtf/RefCountedLeakCounter.cpp",
- "wtf/ThreadingNone.cpp",
- "wtf/Threading.cpp",
- "wtf/TypeTraits.cpp",
- "wtf/unicode/CollatorDefault.cpp",
- "wtf/unicode/icu/CollatorICU.cpp",
- "wtf/unicode/UTF8.cpp",
- ]
- }
- Group {
- name: "api"
- prefix: qtscriptPath + "script/api/"
- files: [
- "qscriptable.cpp",
- "qscriptable.h",
- "qscriptable_p.h",
- "qscriptclass.cpp",
- "qscriptclass.h",
- "qscriptclasspropertyiterator.cpp",
- "qscriptclasspropertyiterator.h",
- "qscriptcontext.cpp",
- "qscriptcontext.h",
- "qscriptcontextinfo.cpp",
- "qscriptcontextinfo.h",
- "qscriptcontext_p.h",
- "qscriptengineagent.cpp",
- "qscriptengineagent.h",
- "qscriptengineagent_p.h",
- "qscriptengine.cpp",
- "qscriptengine.h",
- "qscriptengine_p.h",
- "qscriptextensioninterface.h",
- "qscriptextensionplugin.cpp",
- "qscriptextensionplugin.h",
- "qscriptprogram.cpp",
- "qscriptprogram.h",
- "qscriptprogram_p.h",
- "qscriptstring.cpp",
- "qscriptstring.h",
- "qscriptstring_p.h",
- "qscriptvalue.cpp",
- "qscriptvalue.h",
- "qscriptvalueiterator.cpp",
- "qscriptvalueiterator.h",
- "qscriptvalue_p.h",
- "qtscriptglobal.h",
- ]
- }
- Group {
- name: "bridge"
- prefix: qtscriptPath + "script/bridge/"
- files: [
- "qscriptactivationobject.cpp",
- "qscriptactivationobject_p.h",
- "qscriptclassobject.cpp",
- "qscriptclassobject_p.h",
- "qscriptfunction.cpp",
- "qscriptfunction_p.h",
- "qscriptglobalobject.cpp",
- "qscriptglobalobject_p.h",
- "qscriptobject.cpp",
- "qscriptobject_p.h",
- "qscriptqobject.cpp",
- "qscriptqobject_p.h",
- "qscriptstaticscopeobject.cpp",
- "qscriptstaticscopeobject_p.h",
- "qscriptvariant.cpp",
- "qscriptvariant_p.h",
- ]
- }
- Group {
- name: "parser"
- prefix: qtscriptPath + "script/parser/"
- files: [
- "qscriptast.cpp",
- "qscriptastfwd_p.h",
- "qscriptast_p.h",
- "qscriptastvisitor.cpp",
- "qscriptastvisitor_p.h",
- "qscriptgrammar.cpp",
- "qscriptgrammar_p.h",
- "qscriptlexer.cpp",
- "qscriptlexer_p.h",
- "qscriptsyntaxchecker.cpp",
- "qscriptsyntaxchecker_p.h",
- ]
- }
-
- Export {
- Depends { name: "QtScriptFwdHeaders" }
- Depends { name: "cpp" }
- property stringList includePaths: [product.sourceDirectory + "/include"]
- .concat(QtScriptFwdHeaders.publicIncludePaths)
- Properties {
- condition: qbs.targetOS.contains("unix")
- cpp.dynamicLibraries: base.concat(["pthread"])
- }
- Properties {
- condition: qbs.targetOS.contains("windows")
- cpp.dynamicLibraries: base.concat(["winmm"])
- }
- }
- }
- Product {
- type: ["hpp"]
- name: "QtScriptFwdHeaders"
- Depends { name: "Qt.core" }
- Group {
- files: [
- "../../shared/qtscript/src/script/api/*.h"
- ]
- fileTags: ["qtscriptheader"]
- }
- Rule {
- multiplex: true
- inputs: ["qtscriptheader"]
- Artifact {
- filePath: "include/QtScript/qscriptengine.h"
- fileTags: ["hpp"]
- }
- prepare: {
- var syncQtPath = FileInfo.joinPaths(product.Qt.core.binPath, "syncqt.pl");
- if (!File.exists(syncQtPath)) {
- // syncqt.pl is not in Qt's bin path. We might have a developer build.
- // As we don't provide QT_HOST_BINS/src in our Qt modules we must
- // kindly ask qmake.
- var qmake = FileInfo.joinPaths(product.Qt.core.binPath,
- "qmake" + product.cpp.executableSuffix);
- var p = new Process();
- if (p.exec(qmake, ["-query", "QT_HOST_BINS/src"]) !== 0)
- throw new Error("Error while querying qmake.");
- syncQtPath = FileInfo.joinPaths(p.readStdOut().replace(/\r?\n/, ''),
- "syncqt.pl");
- }
- var qtScriptSrcPath = FileInfo.cleanPath(
- FileInfo.path(inputs["qtscriptheader"][0].filePath) + "/../../..");
- console.info("qtScriptSrcPath: " + qtScriptSrcPath);
- var cmd = new Command("perl", [
- syncQtPath,
- "-minimal",
- "-version", product.Qt.core.version,
- "-outdir", FileInfo.cleanPath(
- FileInfo.path(output.filePath) + "/../.."),
- qtScriptSrcPath
- ]);
- cmd.description = "Create forwarding headers for the bundled QtScript module.";
- return cmd;
- }
- }
- Export {
- Depends { name: "Qt.core" }
- property stringList publicIncludePaths: [
- FileInfo.joinPaths(product.buildDirectory, "include")
- ]
- property stringList privateIncludePaths: [
- FileInfo.joinPaths(product.buildDirectory, "include",
- "QtScript", Qt.core.version, "QtScript")
- ]
- }
- }
-}
diff --git a/src/lib/scriptengine/use_scriptengine.pri b/src/lib/scriptengine/use_scriptengine.pri
deleted file mode 100644
index 6450e171f..000000000
--- a/src/lib/scriptengine/use_scriptengine.pri
+++ /dev/null
@@ -1,11 +0,0 @@
-!qbs_do_not_link_bundled_qtscript {
- include(../../library_dirname.pri)
- isEmpty(QBSLIBDIR) {
- QBSLIBDIR = $$shadowed($$PWD/../../../$${QBS_LIBRARY_DIRNAME})
- }
- LIBS += -L$$QBSLIBDIR -lqbsscriptengine$$qtPlatformTargetSuffix()
-}
-
-INCLUDEPATH += \
- $$PWD/include \
- $$shadowed($$PWD/include)
diff --git a/src/libexec/CMakeLists.txt b/src/libexec/CMakeLists.txt
new file mode 100644
index 000000000..dcf87ae88
--- /dev/null
+++ b/src/libexec/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(qbs_processlauncher)
diff --git a/src/libexec/libexec.pri b/src/libexec/libexec.pri
deleted file mode 100644
index a5cd2c599..000000000
--- a/src/libexec/libexec.pri
+++ /dev/null
@@ -1,11 +0,0 @@
-include(../install_prefix.pri)
-
-win32:LIBEXEC_BASE_DIR=bin
-else:LIBEXEC_BASE_DIR=libexec/qbs
-
-!isEmpty(QBS_LIBEXEC_DESTDIR):DESTDIR=$${QBS_LIBEXEC_DESTDIR}
-else:DESTDIR=../../../$$LIBEXEC_BASE_DIR
-
-!isEmpty(QBS_LIBEXEC_INSTALL_DIR):target.path = $${QBS_LIBEXEC_INSTALL_DIR}
-else:target.path = $${QBS_INSTALL_PREFIX}/$$LIBEXEC_BASE_DIR
-INSTALLS += target
diff --git a/src/libexec/libexec.pro b/src/libexec/libexec.pro
deleted file mode 100644
index 75b1d0844..000000000
--- a/src/libexec/libexec.pro
+++ /dev/null
@@ -1,3 +0,0 @@
-TEMPLATE = subdirs
-
-SUBDIRS += qbs_processlauncher
diff --git a/src/libexec/libexec.qbs b/src/libexec/libexec.qbs
index a43e26157..edf081487 100644
--- a/src/libexec/libexec.qbs
+++ b/src/libexec/libexec.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
references: [
"qbs_processlauncher/qbs_processlauncher.qbs",
diff --git a/src/libexec/qbs_processlauncher/CMakeLists.txt b/src/libexec/qbs_processlauncher/CMakeLists.txt
new file mode 100644
index 000000000..03ab07cbe
--- /dev/null
+++ b/src/libexec/qbs_processlauncher/CMakeLists.txt
@@ -0,0 +1,25 @@
+set(SOURCES
+ launcherlogging.cpp
+ launcherlogging.h
+ launchersockethandler.cpp
+ launchersockethandler.h
+ processlauncher-main.cpp
+ )
+
+set(PATH_TO_PROTOCOL_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/../../lib/corelib/tools")
+set(PROTOCOL_SOURCES
+ launcherpackets.cpp
+ launcherpackets.h
+ )
+list_transform_prepend(PROTOCOL_SOURCES ${PATH_TO_PROTOCOL_SOURCES}/)
+
+add_qbs_app(qbs_processlauncher
+ DESTINATION ${QBS_LIBEXEC_INSTALL_DIR}
+ DEPENDS Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Network
+ INCLUDES ${PATH_TO_PROTOCOL_SOURCES}
+ SOURCES ${SOURCES} ${PROTOCOL_SOURCES}
+ )
+set_target_properties(qbs_processlauncher PROPERTIES
+ BUILD_RPATH "${QBS_LIBEXEC_RPATH}"
+ INSTALL_RPATH "${QBS_LIBEXEC_RPATH}"
+ )
diff --git a/src/libexec/qbs_processlauncher/launchersockethandler.cpp b/src/libexec/qbs_processlauncher/launchersockethandler.cpp
index 53bd81a9a..037f061a5 100644
--- a/src/libexec/qbs_processlauncher/launchersockethandler.cpp
+++ b/src/libexec/qbs_processlauncher/launchersockethandler.cpp
@@ -121,8 +121,7 @@ void LauncherSocketHandler::start()
connect(m_socket, &QLocalSocket::disconnected,
this, &LauncherSocketHandler::handleSocketClosed);
connect(m_socket, &QLocalSocket::readyRead, this, &LauncherSocketHandler::handleSocketData);
- connect(m_socket,
- static_cast<void(QLocalSocket::*)(QLocalSocket::LocalSocketError)>(&QLocalSocket::error),
+ connect(m_socket, &QLocalSocket::errorOccurred,
this, &LauncherSocketHandler::handleSocketError);
m_socket->connectToServer(m_serverPath);
}
@@ -273,8 +272,7 @@ void LauncherSocketHandler::sendPacket(const LauncherPacket &packet)
Process *LauncherSocketHandler::setupProcess(quintptr token)
{
const auto p = new Process(token, this);
- connect(p, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error),
- this, &LauncherSocketHandler::handleProcessError);
+ connect(p, &QProcess::errorOccurred, this, &LauncherSocketHandler::handleProcessError);
connect(p, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
this, &LauncherSocketHandler::handleProcessFinished);
connect(p, &Process::failedToStop, this, &LauncherSocketHandler::handleStopFailure);
diff --git a/src/libexec/qbs_processlauncher/qbs_processlauncher.pro b/src/libexec/qbs_processlauncher/qbs_processlauncher.pro
deleted file mode 100644
index 1c98d97ae..000000000
--- a/src/libexec/qbs_processlauncher/qbs_processlauncher.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-include(../libexec.pri)
-
-TARGET = qbs_processlauncher
-CONFIG += console c++14
-CONFIG -= app_bundle
-QT = core network
-
-TOOLS_DIR = $$PWD/../../lib/corelib/tools
-
-INCLUDEPATH += $$TOOLS_DIR
-
-HEADERS += \
- launcherlogging.h \
- launchersockethandler.h \
- $$TOOLS_DIR/launcherpackets.h
-
-SOURCES += \
- launcherlogging.cpp \
- launchersockethandler.cpp \
- processlauncher-main.cpp \
- $$TOOLS_DIR/launcherpackets.cpp
diff --git a/src/libexec/qbs_processlauncher/qbs_processlauncher.qbs b/src/libexec/qbs_processlauncher/qbs_processlauncher.qbs
index 7aa7d3fc0..5be05163f 100644
--- a/src/libexec/qbs_processlauncher/qbs_processlauncher.qbs
+++ b/src/libexec/qbs_processlauncher/qbs_processlauncher.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.FileInfo
QbsProduct {
diff --git a/src/library_dirname.pri b/src/library_dirname.pri
deleted file mode 100644
index 5d0712d79..000000000
--- a/src/library_dirname.pri
+++ /dev/null
@@ -1 +0,0 @@
-isEmpty(QBS_LIBRARY_DIRNAME):QBS_LIBRARY_DIRNAME = lib
diff --git a/src/packages/archive/archive.qbs b/src/packages/archive/archive.qbs
index 468606368..d1875a1ac 100644
--- a/src/packages/archive/archive.qbs
+++ b/src/packages/archive/archive.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.FileInfo
import qbs.ModUtils
import qbs.Process
@@ -15,13 +14,15 @@ QbsProduct {
condition: qbs.targetOS.contains("unix") && project.withDocumentation
}
Depends { productTypes: ["qbsapplication", "qbsplugin"] }
+ Depends { productTypes: ["autotest"]; condition: includeTests }
Depends { name: "archiver" }
property bool includeTopLevelDir: false
+ property bool includeTests: false
builtByDefault: false
- name: "qbs archive"
+ name: "qbs_archive"
type: ["archiver.archive"]
targetName: "qbs-" + qbs.targetOS[0] + "-" + qbs.architecture + "-" + qbsversion.version
destinationDirectory: project.buildDirectory
@@ -69,7 +70,7 @@ QbsProduct {
inputFilePaths.sort();
var tf;
try {
- tf = new TextFile(outputFilePath, TextFile.ReadWrite);
+ tf = new TextFile(outputFilePath, TextFile.WriteOnly);
for (var i = 0; i < inputFilePaths.length; ++i) {
var relativePath = FileInfo.relativePath(baseDirectory, inputFilePaths[i]);
tf.writeLine(relativePath);
diff --git a/src/packages/chocolatey/chocolatey.qbs b/src/packages/chocolatey/chocolatey.qbs
index 27cc1e489..4a0c2df4a 100644
--- a/src/packages/chocolatey/chocolatey.qbs
+++ b/src/packages/chocolatey/chocolatey.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.Environment
import qbs.File
import qbs.FileInfo
@@ -14,7 +13,7 @@ Product {
id: choco
condition: qbs.targetOS.contains("windows")
names: ["choco"]
- platformPaths: { // TODO: Change to platformSearchPaths in qbs 1.15
+ platformSearchPaths: {
var chocolateyInstall = Environment.getEnv("ChocolateyInstall");
if (chocolateyInstall)
return [FileInfo.joinPaths(chocolateyInstall, "bin")];
@@ -123,7 +122,7 @@ Product {
cmd.nuspecFileName),
"--limitoutput",
"--outputdirectory", cmd.chocoOutDirectory]);
- cmd2.description = "choco pack " + inputs["chocolatey.nuspec"][0].fileName;
+ cmd2.description = "packing choco " + inputs["chocolatey.nuspec"][0].fileName;
return [cmd, cmd2];
}
}
diff --git a/src/packages/chocolatey/chocolateyinstall.ps1 b/src/packages/chocolatey/chocolateyinstall.ps1
index f55d87eb9..fb763e681 100644
--- a/src/packages/chocolatey/chocolateyinstall.ps1
+++ b/src/packages/chocolatey/chocolateyinstall.ps1
@@ -9,15 +9,11 @@ ForEach ($line in (New-Object Net.WebClient).DownloadString("$qbsBaseUrl/${check
$checksums.Add($items[1], $items[0])
}
-$qbs32 = "qbs-windows-x86-$qbsVersion.zip"
$qbs64 = "qbs-windows-x86_64-$qbsVersion.zip"
Install-ChocolateyZipPackage `
-PackageName 'qbs' `
- -Url "$qbsBaseUrl/$qbs32" `
-UnzipLocation "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" `
-Url64bit "$qbsBaseUrl/$qbs64" `
- -Checksum $checksums[$qbs32] `
- -ChecksumType $checksumType `
-Checksum64 $checksums[$qbs64] `
-ChecksumType64 $checksumType
diff --git a/src/packages/chocolatey/icon.png b/src/packages/chocolatey/icon.png
new file mode 100644
index 000000000..fb08b22f5
--- /dev/null
+++ b/src/packages/chocolatey/icon.png
Binary files differ
diff --git a/src/packages/chocolatey/qbs.nuspec b/src/packages/chocolatey/qbs.nuspec
index ed15b2716..a0d650fc4 100644
--- a/src/packages/chocolatey/qbs.nuspec
+++ b/src/packages/chocolatey/qbs.nuspec
@@ -18,6 +18,6 @@
<copyright>© 2017 The Qt Company Ltd.</copyright>
<licenseUrl>http://doc.qt.io/qt-5/licensing.html</licenseUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
- <iconUrl>https://d3hp9ud7yvwzy0.cloudfront.net/wp-content/uploads/2015/02/Qt-logo-medium.png</iconUrl>
+ <iconUrl>https://rawcdn.githack.com/qbs/qbs/v1.17.0/src/packages/chocolatey/icon.png</iconUrl>
</metadata>
</package>
diff --git a/src/packages/packages.qbs b/src/packages/packages.qbs
index 12ca8b041..d2b88ccfe 100644
--- a/src/packages/packages.qbs
+++ b/src/packages/packages.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
references: [
"archive/archive.qbs",
@@ -8,7 +6,7 @@ Project {
// Virtual product for building all possible packagings
Product {
- Depends { name: "qbs archive"; required: false }
+ Depends { name: "qbs_archive"; required: false }
Depends { name: "qbs chocolatey"; required: false }
name: "dist"
builtByDefault: false
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
new file mode 100644
index 000000000..659468aa4
--- /dev/null
+++ b/src/plugins/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(generator)
+add_subdirectory(scanner)
diff --git a/src/plugins/generator/CMakeLists.txt b/src/plugins/generator/CMakeLists.txt
new file mode 100644
index 000000000..738e428bc
--- /dev/null
+++ b/src/plugins/generator/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_subdirectory(clangcompilationdb)
+add_subdirectory(iarew)
+add_subdirectory(keiluv)
+add_subdirectory(makefilegenerator)
+add_subdirectory(visualstudio)
diff --git a/src/plugins/generator/clangcompilationdb/CMakeLists.txt b/src/plugins/generator/clangcompilationdb/CMakeLists.txt
new file mode 100644
index 000000000..237ae4650
--- /dev/null
+++ b/src/plugins/generator/clangcompilationdb/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(SOURCES
+ clangcompilationdbgenerator.cpp
+ clangcompilationdbgenerator.h
+ clangcompilationdbgeneratorplugin.cpp
+ )
+
+add_qbs_plugin(clangcompilationdbgenerator
+ DEPENDS qbscore
+ SOURCES ${SOURCES}
+ )
diff --git a/src/plugins/generator/clangcompilationdb/clangcompilationdb.pri b/src/plugins/generator/clangcompilationdb/clangcompilationdb.pri
deleted file mode 100644
index 7c9a71129..000000000
--- a/src/plugins/generator/clangcompilationdb/clangcompilationdb.pri
+++ /dev/null
@@ -1 +0,0 @@
-qbsPluginTarget = clangcompilationdbgenerator
diff --git a/src/plugins/generator/clangcompilationdb/clangcompilationdb.pro b/src/plugins/generator/clangcompilationdb/clangcompilationdb.pro
deleted file mode 100644
index 1b5342598..000000000
--- a/src/plugins/generator/clangcompilationdb/clangcompilationdb.pro
+++ /dev/null
@@ -1,11 +0,0 @@
-include(clangcompilationdb.pri)
-include(../../plugins.pri)
-
-QT = core
-
-HEADERS += \
- $$PWD/clangcompilationdbgenerator.h
-
-SOURCES += \
- $$PWD/clangcompilationdbgenerator.cpp \
- $$PWD/clangcompilationdbgeneratorplugin.cpp
diff --git a/src/plugins/generator/clangcompilationdb/clangcompilationdb.qbs b/src/plugins/generator/clangcompilationdb/clangcompilationdb.qbs
index 741d6dbf0..89f7c84b9 100644
--- a/src/plugins/generator/clangcompilationdb/clangcompilationdb.qbs
+++ b/src/plugins/generator/clangcompilationdb/clangcompilationdb.qbs
@@ -1,4 +1,3 @@
-import qbs
import "../../qbsplugin.qbs" as QbsPlugin
QbsPlugin {
diff --git a/src/plugins/generator/clangcompilationdb/clangcompilationdbgenerator.cpp b/src/plugins/generator/clangcompilationdb/clangcompilationdbgenerator.cpp
index 93be2804b..dc5450967 100644
--- a/src/plugins/generator/clangcompilationdb/clangcompilationdbgenerator.cpp
+++ b/src/plugins/generator/clangcompilationdb/clangcompilationdbgenerator.cpp
@@ -45,6 +45,7 @@
#include <tools/error.h>
#include <tools/installoptions.h>
#include <tools/shellutils.h>
+#include <tools/stlutils.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
@@ -71,12 +72,10 @@ void ClangCompilationDatabaseGenerator::generate()
for (const Project &theProject : projects) {
QJsonArray database;
const ProjectData projectData = theProject.projectData();
- const QString buildDir = projectData.buildDirectory();
+ const QString &buildDir = projectData.buildDirectory();
- const auto products = projectData.allProducts();
- for (const ProductData &productData : products) {
- const auto groups = productData.groups();
- for (const GroupData &groupData : groups) {
+ for (const ProductData &productData : projectData.allProducts()) {
+ for (const GroupData &groupData : productData.groups()) {
const auto sourceArtifacts = groupData.allSourceArtifacts();
for (const ArtifactData &sourceArtifact : sourceArtifacts) {
if (!hasValidInputFileTag(sourceArtifact.fileTags()))
@@ -116,12 +115,11 @@ QJsonObject ClangCompilationDatabaseGenerator::createEntry(const QString &filePa
const QStringList arguments = QStringList() << ruleCommand.executable()
<< ruleCommand.arguments();
- const QJsonObject object = {
+ return QJsonObject{
{ QStringLiteral("directory"), QJsonValue(workDir) },
{ QStringLiteral("arguments"), QJsonArray::fromStringList(arguments) },
{ QStringLiteral("file"), QJsonValue(filePath) }
};
- return object;
}
void ClangCompilationDatabaseGenerator::writeProjectDatabase(const QString &filePath,
@@ -132,13 +130,11 @@ void ClangCompilationDatabaseGenerator::writeProjectDatabase(const QString &file
if (!databaseFile.open(QFile::WriteOnly))
throw ErrorInfo(Tr::tr("Cannot open '%1' for writing: %2")
- .arg(filePath)
- .arg(databaseFile.errorString()));
+ .arg(filePath, databaseFile.errorString()));
if (databaseFile.write(database.toJson()) == -1)
throw ErrorInfo(Tr::tr("Error while writing '%1': %2")
- .arg(filePath)
- .arg(databaseFile.errorString()));
+ .arg(filePath, databaseFile.errorString()));
}
bool ClangCompilationDatabaseGenerator::hasValidInputFileTag(const QStringList &fileTags) const
@@ -150,11 +146,9 @@ bool ClangCompilationDatabaseGenerator::hasValidInputFileTag(const QStringList &
QStringLiteral("objcpp")
};
- for (const QString &tag : fileTags) {
- if (validFileTags.contains(tag))
- return true;
- }
- return false;
+ return Internal::any_of(fileTags, [](const auto &tag) {
+ return validFileTags.contains(tag);
+ });
}
} // namespace qbs
diff --git a/src/plugins/generator/generator.pro b/src/plugins/generator/generator.pro
deleted file mode 100644
index 4a3861321..000000000
--- a/src/plugins/generator/generator.pro
+++ /dev/null
@@ -1,6 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS += clangcompilationdb
-SUBDIRS += makefilegenerator
-SUBDIRS += visualstudio
-SUBDIRS += iarew
-SUBDIRS += keiluv
diff --git a/src/plugins/generator/iarew/CMakeLists.txt b/src/plugins/generator/iarew/CMakeLists.txt
new file mode 100644
index 000000000..32af6e282
--- /dev/null
+++ b/src/plugins/generator/iarew/CMakeLists.txt
@@ -0,0 +1,119 @@
+set(COMMON_SOURCES
+ iarewfileversionproperty.cpp
+ iarewfileversionproperty.h
+ iarewgenerator.cpp
+ iarewgenerator.h
+ iarewgeneratorplugin.cpp
+ iarewoptionpropertygroup.cpp
+ iarewoptionpropertygroup.h
+ iarewproject.cpp
+ iarewproject.h
+ iarewprojectwriter.cpp
+ iarewprojectwriter.h
+ iarewsettingspropertygroup.cpp
+ iarewsettingspropertygroup.h
+ iarewsourcefilepropertygroup.cpp
+ iarewsourcefilepropertygroup.h
+ iarewsourcefilespropertygroup.cpp
+ iarewsourcefilespropertygroup.h
+ iarewtoolchainpropertygroup.cpp
+ iarewtoolchainpropertygroup.h
+ iarewutils.cpp
+ iarewutils.h
+ iarewversioninfo.h
+ iarewworkspace.cpp
+ iarewworkspace.h
+ iarewworkspacewriter.cpp
+ iarewworkspacewriter.h
+ )
+
+set(ARCHS_ARM_SOURCES
+ armarchiversettingsgroup_v8.cpp
+ armarchiversettingsgroup_v8.h
+ armassemblersettingsgroup_v8.cpp
+ armassemblersettingsgroup_v8.h
+ armbuildconfigurationgroup_v8.cpp
+ armbuildconfigurationgroup_v8.h
+ armcompilersettingsgroup_v8.cpp
+ armcompilersettingsgroup_v8.h
+ armgeneralsettingsgroup_v8.cpp
+ armgeneralsettingsgroup_v8.h
+ armlinkersettingsgroup_v8.cpp
+ armlinkersettingsgroup_v8.h
+ )
+list_transform_prepend(ARCHS_ARM_SOURCES archs/arm/)
+
+set(ARCHS_AVR_SOURCES
+ avrarchiversettingsgroup_v7.cpp
+ avrarchiversettingsgroup_v7.h
+ avrassemblersettingsgroup_v7.cpp
+ avrassemblersettingsgroup_v7.h
+ avrbuildconfigurationgroup_v7.cpp
+ avrbuildconfigurationgroup_v7.h
+ avrcompilersettingsgroup_v7.cpp
+ avrcompilersettingsgroup_v7.h
+ avrgeneralsettingsgroup_v7.cpp
+ avrgeneralsettingsgroup_v7.h
+ avrlinkersettingsgroup_v7.cpp
+ avrlinkersettingsgroup_v7.h
+ )
+list_transform_prepend(ARCHS_AVR_SOURCES archs/avr/)
+
+set(ARCHS_MCS51_SOURCES
+ mcs51archiversettingsgroup_v10.cpp
+ mcs51archiversettingsgroup_v10.h
+ mcs51assemblersettingsgroup_v10.cpp
+ mcs51assemblersettingsgroup_v10.h
+ mcs51buildconfigurationgroup_v10.cpp
+ mcs51buildconfigurationgroup_v10.h
+ mcs51compilersettingsgroup_v10.cpp
+ mcs51compilersettingsgroup_v10.h
+ mcs51generalsettingsgroup_v10.cpp
+ mcs51generalsettingsgroup_v10.h
+ mcs51linkersettingsgroup_v10.cpp
+ mcs51linkersettingsgroup_v10.h
+ )
+list_transform_prepend(ARCHS_MCS51_SOURCES archs/mcs51/)
+
+set(ARCHS_STM8_SOURCES
+ stm8archiversettingsgroup_v3.cpp
+ stm8archiversettingsgroup_v3.h
+ stm8assemblersettingsgroup_v3.cpp
+ stm8assemblersettingsgroup_v3.h
+ stm8buildconfigurationgroup_v3.cpp
+ stm8buildconfigurationgroup_v3.h
+ stm8compilersettingsgroup_v3.cpp
+ stm8compilersettingsgroup_v3.h
+ stm8generalsettingsgroup_v3.cpp
+ stm8generalsettingsgroup_v3.h
+ stm8linkersettingsgroup_v3.cpp
+ stm8linkersettingsgroup_v3.h
+ )
+list_transform_prepend(ARCHS_STM8_SOURCES archs/stm8/)
+
+set(ARCHS_MSP430_SOURCES
+ msp430archiversettingsgroup_v7.cpp
+ msp430archiversettingsgroup_v7.h
+ msp430assemblersettingsgroup_v7.cpp
+ msp430assemblersettingsgroup_v7.h
+ msp430buildconfigurationgroup_v7.cpp
+ msp430buildconfigurationgroup_v7.h
+ msp430compilersettingsgroup_v7.cpp
+ msp430compilersettingsgroup_v7.h
+ msp430generalsettingsgroup_v7.cpp
+ msp430generalsettingsgroup_v7.h
+ msp430linkersettingsgroup_v7.cpp
+ msp430linkersettingsgroup_v7.h
+ )
+list_transform_prepend(ARCHS_MSP430_SOURCES archs/msp430/)
+
+add_qbs_plugin(iarewgenerator
+ DEPENDS qbscore qbsjson
+ SOURCES
+ ${COMMON_SOURCES}
+ ${ARCHS_ARM_SOURCES}
+ ${ARCHS_AVR_SOURCES}
+ ${ARCHS_MCS51_SOURCES}
+ ${ARCHS_STM8_SOURCES}
+ ${ARCHS_MSP430_SOURCES}
+ )
diff --git a/src/plugins/generator/iarew/archs/arm/armgeneralsettingsgroup_v8.cpp b/src/plugins/generator/iarew/archs/arm/armgeneralsettingsgroup_v8.cpp
index 911873cf4..a2395e3d7 100644
--- a/src/plugins/generator/iarew/archs/arm/armgeneralsettingsgroup_v8.cpp
+++ b/src/plugins/generator/iarew/archs/arm/armgeneralsettingsgroup_v8.cpp
@@ -105,7 +105,7 @@ struct CpuCoreEntry final
};
// Dictionary of known ARM CPU cores and its compiler options.
-static const CpuCoreEntry cpusDict[] = {
+const CpuCoreEntry cpusDict[] = {
{CpuCoreEntry::Arm7tdmi, "arm7tdmi"}, // same as 'sc100'
{CpuCoreEntry::Arm7tdmis, "arm7tdmi-s"},
{CpuCoreEntry::Arm710t, "arm710t"},
@@ -187,7 +187,7 @@ struct FpuCoreEntry final
};
// Dictionary of known ARM FPU cores and its compiler options.
-static const FpuCoreEntry fpusDict[] = {
+const FpuCoreEntry fpusDict[] = {
{FpuCoreEntry::Vfp2, FpuCoreEntry::NoFpuRegisters, "vfpv2"},
{FpuCoreEntry::Vfp3d16, FpuCoreEntry::Fpu16Registers, "vfpv3_d16"},
{FpuCoreEntry::Vfp3, FpuCoreEntry::Fpu32Registers, "vfpv3"},
@@ -499,7 +499,7 @@ void ArmGeneralSettingsGroup::buildLibraryOptionsTwoPage(
}
void ArmGeneralSettingsGroup::buildLibraryConfigPage(
- const QString baseDirectory,
+ const QString &baseDirectory,
const ProductData &qbsProduct)
{
const LibraryConfigPageOptions opts(baseDirectory, qbsProduct);
@@ -523,7 +523,7 @@ void ArmGeneralSettingsGroup::buildLibraryConfigPage(
}
void ArmGeneralSettingsGroup::buildOutputPage(
- const QString baseDirectory,
+ const QString &baseDirectory,
const ProductData &qbsProduct)
{
const OutputPageOptions opts(baseDirectory, qbsProduct);
diff --git a/src/plugins/generator/iarew/archs/arm/armgeneralsettingsgroup_v8.h b/src/plugins/generator/iarew/archs/arm/armgeneralsettingsgroup_v8.h
index 420e98008..0b1564b3c 100644
--- a/src/plugins/generator/iarew/archs/arm/armgeneralsettingsgroup_v8.h
+++ b/src/plugins/generator/iarew/archs/arm/armgeneralsettingsgroup_v8.h
@@ -49,9 +49,9 @@ private:
void buildTargetPage(const ProductData &qbsProduct);
void buildLibraryOptionsOnePage(const ProductData &qbsProduct);
void buildLibraryOptionsTwoPage(const ProductData &qbsProduct);
- void buildLibraryConfigPage(const QString baseDirectory,
+ void buildLibraryConfigPage(const QString &baseDirectory,
const ProductData &qbsProduct);
- void buildOutputPage(const QString baseDirectory,
+ void buildOutputPage(const QString &baseDirectory,
const ProductData &qbsProduct);
};
diff --git a/src/plugins/generator/iarew/archs/arm/armlinkersettingsgroup_v8.cpp b/src/plugins/generator/iarew/archs/arm/armlinkersettingsgroup_v8.cpp
index 7e29c2788..2cabac587 100644
--- a/src/plugins/generator/iarew/archs/arm/armlinkersettingsgroup_v8.cpp
+++ b/src/plugins/generator/iarew/archs/arm/armlinkersettingsgroup_v8.cpp
@@ -60,8 +60,7 @@ struct ConfigPageOptions final
// Enumerate all product linker config files
// (which are set trough 'linkerscript' tag).
- const auto qbsGroups = qbsProduct.groups();
- for (const auto &qbsGroup : qbsGroups) {
+ for (const auto &qbsGroup : qbsProduct.groups()) {
if (!qbsGroup.isEnabled())
continue;
const auto qbsArtifacts = qbsGroup.sourceArtifacts();
diff --git a/src/plugins/generator/iarew/archs/avr/avrgeneralsettingsgroup_v7.cpp b/src/plugins/generator/iarew/archs/avr/avrgeneralsettingsgroup_v7.cpp
index a4819a90d..f3fb8647d 100644
--- a/src/plugins/generator/iarew/archs/avr/avrgeneralsettingsgroup_v7.cpp
+++ b/src/plugins/generator/iarew/archs/avr/avrgeneralsettingsgroup_v7.cpp
@@ -49,7 +49,7 @@ struct TargetMcuEntry final
};
// Dictionary of known AVR MCU's and its compiler options.
-static const TargetMcuEntry mcusDict[] = {
+const TargetMcuEntry mcusDict[] = {
{"AT43USB320A", "at43usb320a"},
{"AT43USB325", "at43usb325"},
{"AT43USB326", "at43usb326"},
diff --git a/src/plugins/generator/iarew/archs/avr/avrlinkersettingsgroup_v7.cpp b/src/plugins/generator/iarew/archs/avr/avrlinkersettingsgroup_v7.cpp
index 59ba515d9..0af4f088b 100644
--- a/src/plugins/generator/iarew/archs/avr/avrlinkersettingsgroup_v7.cpp
+++ b/src/plugins/generator/iarew/archs/avr/avrlinkersettingsgroup_v7.cpp
@@ -60,8 +60,7 @@ struct ConfigPageOptions final
// Enumerate all product linker config files
// (which are set trough 'linkerscript' tag).
- const auto qbsGroups = qbsProduct.groups();
- for (const auto &qbsGroup : qbsGroups) {
+ for (const auto &qbsGroup : qbsProduct.groups()) {
const auto qbsArtifacts = qbsGroup.sourceArtifacts();
for (const auto &qbsArtifact : qbsArtifacts) {
const auto qbsTags = qbsArtifact.fileTags();
diff --git a/src/plugins/generator/iarew/archs/mcs51/mcs51generalsettingsgroup_v10.cpp b/src/plugins/generator/iarew/archs/mcs51/mcs51generalsettingsgroup_v10.cpp
index 43c160045..b3b68377f 100644
--- a/src/plugins/generator/iarew/archs/mcs51/mcs51generalsettingsgroup_v10.cpp
+++ b/src/plugins/generator/iarew/archs/mcs51/mcs51generalsettingsgroup_v10.cpp
@@ -95,17 +95,15 @@ struct TargetPageOptions final
const QString core = IarewUtils::flagValue(
flags, QStringLiteral("--core"))
.toLower();
- if (core == QLatin1String("plain")) {
+ // If the core variant is not set, then choose the
+ // default value, CorePlain (see the compiler datasheet for
+ // '--core' option).
+ if (core == QLatin1String("plain") || core.isEmpty()) {
cpuCore = TargetPageOptions::CorePlain;
} else if (core == QLatin1String("extended1")) {
cpuCore = TargetPageOptions::CoreExtended1;
} else if (core == QLatin1String("extended2")) {
cpuCore = TargetPageOptions::CoreExtended2;
- } else {
- // If the core variant is not set, then choose the
- // default values (see the compiler datasheet for
- // '--core' option).
- cpuCore = TargetPageOptions::CorePlain;
}
const QString cm = IarewUtils::flagValue(
@@ -119,7 +117,7 @@ struct TargetPageOptions final
codeModel = TargetPageOptions::CodeModelFar;
} else if (cm == QLatin1String("banked_ext2")) {
codeModel = TargetPageOptions::CodeModelBankedExtended2;
- } else {
+ } else if (cm.isEmpty()) {
// If the code model is not set, then choose the
// default values (see the compiler datasheet for
// '--code_model' option).
@@ -145,7 +143,7 @@ struct TargetPageOptions final
dataModel = TargetPageOptions::DataModelFarGeneric;
} else if (dm == QLatin1String("far")) {
dataModel = TargetPageOptions::DataModelFar;
- } else {
+ } else if (dm.isEmpty()) {
// If the data model is not set, then choose the
// default values (see the compiler datasheet for
// '--data_model' option).
@@ -172,26 +170,26 @@ struct TargetPageOptions final
const QString constPlace = IarewUtils::flagValue(
flags, QStringLiteral("--place_constants"))
.toLower();
- if (constPlace == QLatin1String("data")) {
+ // If this option is not set, then choose the
+ // default value (see the compiler datasheet for
+ // '--place_constants' option).
+ if (constPlace == QLatin1String("data") || constPlace.isEmpty()) {
constPlacement = TargetPageOptions::RamMemoryPlace;
} else if (constPlace == QLatin1String("data_rom")) {
constPlacement = TargetPageOptions::RomMemoryPlace;
} else if (constPlace == QLatin1String("code")) {
constPlacement = TargetPageOptions::CodeMemoryPlace;
- } else {
- // If this option is not set, then choose the
- // default value (see the compiler datasheet for
- // '--place_constants' option).
- constPlacement = TargetPageOptions::RamMemoryPlace;
}
-
const QString cc = IarewUtils::flagValue(
flags, QStringLiteral("--calling_convention")).toLower();
if (cc == QLatin1String("data_overlay")) {
callingConvention = TargetPageOptions::DataOverlayConvention;
} else if (cc == QLatin1String("idata_overlay")) {
callingConvention = TargetPageOptions::IDataOverlayConvention;
- } else if (cc == QLatin1String("idata_reentrant")) {
+ } else if (cc == QLatin1String("idata_reentrant") || cc.isEmpty()) {
+ // If this option is not set, then choose the
+ // default value (see the compiler datasheet for
+ // '--calling_convention' option).
callingConvention = TargetPageOptions::IDataReentrantConvention;
} else if (cc == QLatin1String("pdata_reentrant")) {
callingConvention = TargetPageOptions::PDataReentrantConvention;
@@ -199,11 +197,6 @@ struct TargetPageOptions final
callingConvention = TargetPageOptions::XDataReentrantConvention;
} else if (cc == QLatin1String("ext_stack_reentrant")) {
callingConvention = TargetPageOptions::ExtendedStackReentrantConvention;
- } else {
- // If this option is not set, then choose the
- // default value (see the compiler datasheet for
- // '--calling_convention' option).
- callingConvention = TargetPageOptions::IDataReentrantConvention;
}
}
@@ -222,8 +215,7 @@ struct TargetPageOptions final
// Enumerate all product linker config files
// (which are set trough 'linkerscript' tag).
- const auto qbsGroups = qbsProduct.groups();
- for (const auto &qbsGroup : qbsGroups) {
+ for (const auto &qbsGroup : qbsProduct.groups()) {
const auto qbsArtifacts = qbsGroup.sourceArtifacts();
for (const auto &qbsArtifact : qbsArtifacts) {
const auto qbsTags = qbsArtifact.fileTags();
@@ -238,7 +230,7 @@ struct TargetPageOptions final
}
const QString toolkitPath = IarewUtils::toolkitRootPath(qbsProduct);
- for (const QVariant &configPath : configPaths) {
+ for (const QVariant &configPath : std::as_const(configPaths)) {
const QString fullConfigPath = configPath.toString();
// We interested only in a config paths shipped inside of a toolkit.
if (!fullConfigPath.startsWith(toolkitPath, Qt::CaseInsensitive))
@@ -447,13 +439,12 @@ struct DptrPageOptions final
case VisibilityIndex:
if (part == QLatin1String("shadowed"))
dptrVisibility = DptrPageOptions::DptrShadowed;
- else if (part == QLatin1String("separate"))
- dptrVisibility = DptrPageOptions::DptrSeparate;
- else
- // If this option is not set, then choose the
+ else if (part == QLatin1String("separate") || part.isEmpty()) {
+ // If this option is not set or set to "separate", then choose the
// default value (see the compiler datasheet for
// '--dptr' option).
dptrVisibility = DptrPageOptions::DptrSeparate;
+ }
break;
case SwitchMethodIndex:
if (part == QLatin1String("inc")) {
@@ -648,30 +639,22 @@ struct LibraryOptionsPageOptions final
Qt::CaseInsensitive)) {
const QString prop = flag.split(
QLatin1Char('=')).at(0).toLower();
+ // see the compiler datasheet for the '_formatted_write' option.
if (prop == QLatin1String("-e_large_write"))
printfFormatter = LibraryOptionsPageOptions::PrintfLargeFormatter;
else if (prop == QLatin1String("-e_medium_write"))
printfFormatter = LibraryOptionsPageOptions::PrintfMediumFormatter;
else if (prop == QLatin1String("-e_small_write"))
printfFormatter = LibraryOptionsPageOptions::PrintfSmallFormatter;
- else
- // If this option is not set, then choose the
- // default value (see the compiler datasheet for
- // '_formatted_write' option).
- printfFormatter = LibraryOptionsPageOptions::PrintfMediumFormatter;
} else if (flag.endsWith(QLatin1String("_formatted_read"),
Qt::CaseInsensitive)) {
const QString prop = flag.split(QLatin1Char('='))
.at(0).toLower();
+ // see the compiler datasheet for the '_formatted_read' option.
if (prop == QLatin1String("-e_large_read"))
scanfFormatter = LibraryOptionsPageOptions::ScanfLargeFormatter;
else if (prop == QLatin1String("-e_medium_read"))
scanfFormatter = LibraryOptionsPageOptions::ScanfMediumFormatter;
- else
- // If this option is not set, then choose the
- // default value (see the compiler datasheet for
- // '_formatted_read' option).
- scanfFormatter = LibraryOptionsPageOptions::ScanfMediumFormatter;
}
}
}
diff --git a/src/plugins/generator/iarew/archs/mcs51/mcs51linkersettingsgroup_v10.cpp b/src/plugins/generator/iarew/archs/mcs51/mcs51linkersettingsgroup_v10.cpp
index a88ac22e1..d2c7f3846 100644
--- a/src/plugins/generator/iarew/archs/mcs51/mcs51linkersettingsgroup_v10.cpp
+++ b/src/plugins/generator/iarew/archs/mcs51/mcs51linkersettingsgroup_v10.cpp
@@ -59,8 +59,7 @@ struct ConfigPageOptions final
// Enumerate all product linker config files
// (which are set trough 'linkerscript' tag).
- const auto qbsGroups = qbsProduct.groups();
- for (const auto &qbsGroup : qbsGroups) {
+ for (const auto &qbsGroup : qbsProduct.groups()) {
const auto qbsArtifacts = qbsGroup.sourceArtifacts();
for (const auto &qbsArtifact : qbsArtifacts) {
const auto qbsTags = qbsArtifact.fileTags();
diff --git a/src/plugins/generator/iarew/archs/msp430/msp430generalsettingsgroup_v7.cpp b/src/plugins/generator/iarew/archs/msp430/msp430generalsettingsgroup_v7.cpp
index ac1c475d1..7b330ca7f 100644
--- a/src/plugins/generator/iarew/archs/msp430/msp430generalsettingsgroup_v7.cpp
+++ b/src/plugins/generator/iarew/archs/msp430/msp430generalsettingsgroup_v7.cpp
@@ -117,8 +117,7 @@ struct TargetPageOptions final
// Enumerate all product linker config files
// (which are set trough 'linkerscript' tag).
- const auto qbsGroups = qbsProduct.groups();
- for (const auto &qbsGroup : qbsGroups) {
+ for (const auto &qbsGroup : qbsProduct.groups()) {
const auto qbsArtifacts = qbsGroup.sourceArtifacts();
for (const auto &qbsArtifact : qbsArtifacts) {
const auto qbsTags = qbsArtifact.fileTags();
@@ -282,7 +281,7 @@ struct LibraryOptionsPageOptions final
else if (prop == QLatin1String("_printflarge"))
printfFormatter = PrintfLargeFormatter;
else if (prop == QLatin1String("_printflargenomb"))
- printfFormatter = PrintfLargeFormatter;
+ printfFormatter = PrintfLargeNoMultibytesFormatter;
else if (prop == QLatin1String("_printfsmall"))
printfFormatter = PrintfSmallFormatter;
else if (prop == QLatin1String("_printfsmallnomb"))
@@ -300,7 +299,7 @@ struct LibraryOptionsPageOptions final
else if (prop == QLatin1String("_scanflarge"))
scanfFormatter = ScanfLargeFormatter;
else if (prop == QLatin1String("_scanflargenomb"))
- scanfFormatter = ScanfLargeFormatter;
+ scanfFormatter = ScanfLargeNoMultibytesFormatter;
else if (prop == QLatin1String("_scanfsmall"))
scanfFormatter = ScanfSmallFormatter;
else if (prop == QLatin1String("_scanfsmallnomb"))
diff --git a/src/plugins/generator/iarew/archs/msp430/msp430linkersettingsgroup_v7.cpp b/src/plugins/generator/iarew/archs/msp430/msp430linkersettingsgroup_v7.cpp
index b85edbb83..85aac7e83 100644
--- a/src/plugins/generator/iarew/archs/msp430/msp430linkersettingsgroup_v7.cpp
+++ b/src/plugins/generator/iarew/archs/msp430/msp430linkersettingsgroup_v7.cpp
@@ -56,8 +56,7 @@ struct ConfigPageOptions final
// Enumerate all product linker config files
// (which are set trough 'linkerscript' tag).
- const auto qbsGroups = qbsProduct.groups();
- for (const auto &qbsGroup : qbsGroups) {
+ for (const auto &qbsGroup : qbsProduct.groups()) {
const auto qbsArtifacts = qbsGroup.sourceArtifacts();
for (const auto &qbsArtifact : qbsArtifacts) {
const auto qbsTags = qbsArtifact.fileTags();
diff --git a/src/plugins/generator/iarew/archs/stm8/stm8generalsettingsgroup_v3.cpp b/src/plugins/generator/iarew/archs/stm8/stm8generalsettingsgroup_v3.cpp
index 9477bc36e..83766442b 100644
--- a/src/plugins/generator/iarew/archs/stm8/stm8generalsettingsgroup_v3.cpp
+++ b/src/plugins/generator/iarew/archs/stm8/stm8generalsettingsgroup_v3.cpp
@@ -205,7 +205,7 @@ struct LibraryOptionsPageOptions final
else if (prop == QLatin1String("_printffullnomb"))
printfFormatter = PrintfFullNoMultibytesFormatter;
else if (prop == QLatin1String("_printflarge"))
- printfFormatter = PrintfLargeFormatter;
+ printfFormatter = PrintfLargeNoMultibytesFormatter;
else if (prop == QLatin1String("_printflargenomb"))
printfFormatter = PrintfLargeFormatter;
else if (prop == QLatin1String("_printfsmall"))
@@ -223,7 +223,7 @@ struct LibraryOptionsPageOptions final
else if (prop == QLatin1String("_scanffullnomb"))
scanfFormatter = ScanfFullNoMultibytesFormatter;
else if (prop == QLatin1String("_scanflarge"))
- scanfFormatter = ScanfLargeFormatter;
+ scanfFormatter = ScanfLargeNoMultibytesFormatter;
else if (prop == QLatin1String("_scanflargenomb"))
scanfFormatter = ScanfLargeFormatter;
else if (prop == QLatin1String("_scanfsmall"))
diff --git a/src/plugins/generator/iarew/archs/stm8/stm8linkersettingsgroup_v3.cpp b/src/plugins/generator/iarew/archs/stm8/stm8linkersettingsgroup_v3.cpp
index 2d3780e4f..441333976 100644
--- a/src/plugins/generator/iarew/archs/stm8/stm8linkersettingsgroup_v3.cpp
+++ b/src/plugins/generator/iarew/archs/stm8/stm8linkersettingsgroup_v3.cpp
@@ -32,6 +32,8 @@
#include "../../iarewutils.h"
+#include <tools/stlutils.h>
+
#include <QtCore/qdir.h>
namespace qbs {
@@ -56,8 +58,7 @@ struct ConfigPageOptions final
// Enumerate all product linker config files
// (which are set trough 'linkerscript' tag).
- const auto qbsGroups = qbsProduct.groups();
- for (const auto &qbsGroup : qbsGroups) {
+ for (const auto &qbsGroup : qbsProduct.groups()) {
const auto qbsArtifacts = qbsGroup.sourceArtifacts();
for (const auto &qbsArtifact : qbsArtifacts) {
const auto qbsTags = qbsArtifact.fileTags();
@@ -101,14 +102,11 @@ struct ConfigPageOptions final
// on the general page).
configDefinitions = IarewUtils::flagValues(
flags, QStringLiteral("--config_def"));
- configDefinitions.erase(std::remove_if(
- configDefinitions.begin(),
- configDefinitions.end(),
- [](const auto &definition){
+ Internal::removeIf(configDefinitions, [](const auto &definition){
const auto def = definition.toString();
return def.startsWith(QLatin1String("_CSTACK_SIZE"))
|| def.startsWith(QLatin1String("_HEAP_SIZE"));
- }), configDefinitions.end());
+ });
}
QVariantList configFilePaths;
@@ -175,7 +173,7 @@ struct OptimizationsPageOptions final
QLatin1String("--merge_duplicate_sections"));
}
- bool mergeDuplicateSections = 0;
+ bool mergeDuplicateSections = true;
};
// Output page options.
@@ -192,7 +190,7 @@ struct OutputPageOptions final
}
QString outputFile;
- bool enableDebugInfo = 1;
+ bool enableDebugInfo = true;
};
// List page options.
diff --git a/src/plugins/generator/iarew/iarew.pri b/src/plugins/generator/iarew/iarew.pri
deleted file mode 100644
index 68a3593fa..000000000
--- a/src/plugins/generator/iarew/iarew.pri
+++ /dev/null
@@ -1 +0,0 @@
-qbsPluginTarget = iarewgenerator
diff --git a/src/plugins/generator/iarew/iarew.pro b/src/plugins/generator/iarew/iarew.pro
deleted file mode 100644
index 074635047..000000000
--- a/src/plugins/generator/iarew/iarew.pro
+++ /dev/null
@@ -1,132 +0,0 @@
-include(iarew.pri)
-include(../../plugins.pri)
-include(../../../shared/json/json.pri)
-
-QT = core
-
-# Plugin file.
-
-SOURCES += \
- $$PWD/iarewgeneratorplugin.cpp \
-
-# Common files.
-
-HEADERS += \
- $$PWD/iarewfileversionproperty.h \
- $$PWD/iarewgenerator.h \
- $$PWD/iarewoptionpropertygroup.h \
- $$PWD/iarewproject.h \
- $$PWD/iarewprojectwriter.h \
- $$PWD/iarewsettingspropertygroup.h \
- $$PWD/iarewsourcefilepropertygroup.h \
- $$PWD/iarewsourcefilespropertygroup.h \
- $$PWD/iarewtoolchainpropertygroup.h \
- $$PWD/iarewutils.h \
- $$PWD/iarewversioninfo.h \
- $$PWD/iarewworkspace.h \
- $$PWD/iarewworkspacewriter.h
-
-SOURCES += \
- $$PWD/iarewfileversionproperty.cpp \
- $$PWD/iarewgenerator.cpp \
- $$PWD/iarewoptionpropertygroup.cpp \
- $$PWD/iarewproject.cpp \
- $$PWD/iarewprojectwriter.cpp \
- $$PWD/iarewsettingspropertygroup.cpp \
- $$PWD/iarewsourcefilepropertygroup.cpp \
- $$PWD/iarewsourcefilespropertygroup.cpp \
- $$PWD/iarewtoolchainpropertygroup.cpp \
- $$PWD/iarewutils.cpp \
- $$PWD/iarewversioninfo.cpp \
- $$PWD/iarewworkspace.cpp \
- $$PWD/iarewworkspacewriter.cpp
-
-# For ARM architecture.
-
-HEADERS += \
- $$PWD/archs/arm/armarchiversettingsgroup_v8.h \
- $$PWD/archs/arm/armassemblersettingsgroup_v8.h \
- $$PWD/archs/arm/armbuildconfigurationgroup_v8.h \
- $$PWD/archs/arm/armcompilersettingsgroup_v8.h \
- $$PWD/archs/arm/armgeneralsettingsgroup_v8.h \
- $$PWD/archs/arm/armlinkersettingsgroup_v8.h
-
-SOURCES += \
- $$PWD/archs/arm/armarchiversettingsgroup_v8.cpp \
- $$PWD/archs/arm/armassemblersettingsgroup_v8.cpp \
- $$PWD/archs/arm/armbuildconfigurationgroup_v8.cpp \
- $$PWD/archs/arm/armcompilersettingsgroup_v8.cpp \
- $$PWD/archs/arm/armgeneralsettingsgroup_v8.cpp \
- $$PWD/archs/arm/armlinkersettingsgroup_v8.cpp
-
-# For AVR architecture.
-
-HEADERS += \
- $$PWD/archs/avr/avrarchiversettingsgroup_v7.h \
- $$PWD/archs/avr/avrassemblersettingsgroup_v7.h \
- $$PWD/archs/avr/avrbuildconfigurationgroup_v7.h \
- $$PWD/archs/avr/avrcompilersettingsgroup_v7.h \
- $$PWD/archs/avr/avrgeneralsettingsgroup_v7.h \
- $$PWD/archs/avr/avrlinkersettingsgroup_v7.h
-
-SOURCES += \
- $$PWD/archs/avr/avrarchiversettingsgroup_v7.cpp \
- $$PWD/archs/avr/avrassemblersettingsgroup_v7.cpp \
- $$PWD/archs/avr/avrbuildconfigurationgroup_v7.cpp \
- $$PWD/archs/avr/avrcompilersettingsgroup_v7.cpp \
- $$PWD/archs/avr/avrgeneralsettingsgroup_v7.cpp \
- $$PWD/archs/avr/avrlinkersettingsgroup_v7.cpp
-
-# For MCS51 architecture.
-
-HEADERS += \
- $$PWD/archs/mcs51/mcs51archiversettingsgroup_v10.h \
- $$PWD/archs/mcs51/mcs51assemblersettingsgroup_v10.h \
- $$PWD/archs/mcs51/mcs51buildconfigurationgroup_v10.h \
- $$PWD/archs/mcs51/mcs51compilersettingsgroup_v10.h \
- $$PWD/archs/mcs51/mcs51generalsettingsgroup_v10.h \
- $$PWD/archs/mcs51/mcs51linkersettingsgroup_v10.h
-
-SOURCES += \
- $$PWD/archs/mcs51/mcs51archiversettingsgroup_v10.cpp \
- $$PWD/archs/mcs51/mcs51assemblersettingsgroup_v10.cpp \
- $$PWD/archs/mcs51/mcs51buildconfigurationgroup_v10.cpp \
- $$PWD/archs/mcs51/mcs51compilersettingsgroup_v10.cpp \
- $$PWD/archs/mcs51/mcs51generalsettingsgroup_v10.cpp \
- $$PWD/archs/mcs51/mcs51linkersettingsgroup_v10.cpp
-
-# For STM8 architecture.
-
-HEADERS += \
- $$PWD/archs/stm8/stm8archiversettingsgroup_v3.h \
- $$PWD/archs/stm8/stm8assemblersettingsgroup_v3.h \
- $$PWD/archs/stm8/stm8buildconfigurationgroup_v3.h \
- $$PWD/archs/stm8/stm8compilersettingsgroup_v3.h \
- $$PWD/archs/stm8/stm8generalsettingsgroup_v3.h \
- $$PWD/archs/stm8/stm8linkersettingsgroup_v3.h
-
-SOURCES += \
- $$PWD/archs/stm8/stm8archiversettingsgroup_v3.cpp \
- $$PWD/archs/stm8/stm8assemblersettingsgroup_v3.cpp \
- $$PWD/archs/stm8/stm8buildconfigurationgroup_v3.cpp \
- $$PWD/archs/stm8/stm8compilersettingsgroup_v3.cpp \
- $$PWD/archs/stm8/stm8generalsettingsgroup_v3.cpp \
- $$PWD/archs/stm8/stm8linkersettingsgroup_v3.cpp
-
-# For MSP430 architecture.
-
-HEADERS += \
- $$PWD/archs/msp430/msp430archiversettingsgroup_v7.h \
- $$PWD/archs/msp430/msp430assemblersettingsgroup_v7.h \
- $$PWD/archs/msp430/msp430buildconfigurationgroup_v7.h \
- $$PWD/archs/msp430/msp430compilersettingsgroup_v7.h \
- $$PWD/archs/msp430/msp430generalsettingsgroup_v7.h \
- $$PWD/archs/msp430/msp430linkersettingsgroup_v7.h
-
-SOURCES += \
- $$PWD/archs/msp430/msp430archiversettingsgroup_v7.cpp \
- $$PWD/archs/msp430/msp430assemblersettingsgroup_v7.cpp \
- $$PWD/archs/msp430/msp430buildconfigurationgroup_v7.cpp \
- $$PWD/archs/msp430/msp430compilersettingsgroup_v7.cpp \
- $$PWD/archs/msp430/msp430generalsettingsgroup_v7.cpp \
- $$PWD/archs/msp430/msp430linkersettingsgroup_v7.cpp
diff --git a/src/plugins/generator/iarew/iarew.qbs b/src/plugins/generator/iarew/iarew.qbs
index 7b2270426..cf1af83b8 100644
--- a/src/plugins/generator/iarew/iarew.qbs
+++ b/src/plugins/generator/iarew/iarew.qbs
@@ -1,4 +1,3 @@
-import qbs
import "../../qbsplugin.qbs" as QbsPlugin
QbsPlugin {
@@ -31,7 +30,6 @@ QbsPlugin {
"iarewtoolchainpropertygroup.h",
"iarewutils.cpp",
"iarewutils.h",
- "iarewversioninfo.cpp",
"iarewversioninfo.h",
"iarewworkspace.cpp",
"iarewworkspace.h",
diff --git a/src/plugins/generator/iarew/iarewfileversionproperty.cpp b/src/plugins/generator/iarew/iarewfileversionproperty.cpp
index b84f37301..4324405a1 100644
--- a/src/plugins/generator/iarew/iarewfileversionproperty.cpp
+++ b/src/plugins/generator/iarew/iarewfileversionproperty.cpp
@@ -33,7 +33,7 @@
namespace qbs {
-static QByteArray buildFileVersion(const IarewVersionInfo &versionInfo)
+static QByteArray buildFileVersion(const gen::VersionInfo &versionInfo)
{
switch (versionInfo.marketingVersion()) {
case 3:
@@ -47,7 +47,7 @@ static QByteArray buildFileVersion(const IarewVersionInfo &versionInfo)
}
IarewFileVersionProperty::IarewFileVersionProperty(
- const IarewVersionInfo &versionInfo)
+ const gen::VersionInfo &versionInfo)
{
setName(QByteArrayLiteral("fileVersion"));
const QByteArray fileVersion = buildFileVersion(versionInfo);
diff --git a/src/plugins/generator/iarew/iarewfileversionproperty.h b/src/plugins/generator/iarew/iarewfileversionproperty.h
index d377979fa..2ed56c1e6 100644
--- a/src/plugins/generator/iarew/iarewfileversionproperty.h
+++ b/src/plugins/generator/iarew/iarewfileversionproperty.h
@@ -31,17 +31,16 @@
#ifndef QBS_IAREWFILEVERSIONPROPERTY_H
#define QBS_IAREWFILEVERSIONPROPERTY_H
+#include <generators/generatorversioninfo.h>
#include <generators/xmlproperty.h>
namespace qbs {
-class IarewVersionInfo;
-
class IarewFileVersionProperty final : public gen::xml::Property
{
public:
explicit IarewFileVersionProperty(
- const IarewVersionInfo &versionInfo);
+ const gen::VersionInfo &versionInfo);
};
} // namespace qbs
diff --git a/src/plugins/generator/iarew/iarewgenerator.cpp b/src/plugins/generator/iarew/iarewgenerator.cpp
index e6b308182..c5345a6dd 100644
--- a/src/plugins/generator/iarew/iarewgenerator.cpp
+++ b/src/plugins/generator/iarew/iarewgenerator.cpp
@@ -96,7 +96,7 @@ static void writeWorkspace(const std::shared_ptr<IarewWorkspace> &wokspace,
QFileInfo(workspaceFilePath).fileName());
}
-IarewGenerator::IarewGenerator(const IarewVersionInfo &versionInfo)
+IarewGenerator::IarewGenerator(const gen::VersionInfo &versionInfo)
: m_versionInfo(versionInfo)
{
}
diff --git a/src/plugins/generator/iarew/iarewgenerator.h b/src/plugins/generator/iarew/iarewgenerator.h
index f8c1298f0..fa1d93bc3 100644
--- a/src/plugins/generator/iarew/iarewgenerator.h
+++ b/src/plugins/generator/iarew/iarewgenerator.h
@@ -44,7 +44,7 @@ class IarewWorkspace;
class IarewGenerator final : public ProjectGenerator, private IGeneratableProjectVisitor
{
public:
- explicit IarewGenerator(const IarewVersionInfo &versionInfo);
+ explicit IarewGenerator(const gen::VersionInfo &versionInfo);
QString generatorName() const final;
void generate() final;
@@ -59,7 +59,7 @@ private:
const GeneratableProjectData &projectData,
const GeneratableProductData &productData) final;
- const IarewVersionInfo m_versionInfo;
+ const gen::VersionInfo m_versionInfo;
std::shared_ptr<IarewWorkspace> m_workspace;
QString m_workspaceFilePath;
std::map<QString, std::shared_ptr<IarewProject>> m_projects;
diff --git a/src/plugins/generator/iarew/iarewgeneratorplugin.cpp b/src/plugins/generator/iarew/iarewgeneratorplugin.cpp
index bd75c7ad5..bdd15c485 100644
--- a/src/plugins/generator/iarew/iarewgeneratorplugin.cpp
+++ b/src/plugins/generator/iarew/iarewgeneratorplugin.cpp
@@ -45,7 +45,7 @@
static void QbsIarewGeneratorPluginLoad()
{
- for (const auto &info : qbs::IarewVersionInfo::knownVersions()) {
+ for (const auto &info : qbs::IarewVersionInfo::knownVersions) {
qbs::ProjectGeneratorManager::registerGenerator(
std::make_shared<qbs::IarewGenerator>(info));
}
diff --git a/src/plugins/generator/iarew/iarewoptionpropertygroup.cpp b/src/plugins/generator/iarew/iarewoptionpropertygroup.cpp
index bfe2c4548..c44b30ed2 100644
--- a/src/plugins/generator/iarew/iarewoptionpropertygroup.cpp
+++ b/src/plugins/generator/iarew/iarewoptionpropertygroup.cpp
@@ -33,12 +33,12 @@
namespace qbs {
IarewOptionPropertyGroup::IarewOptionPropertyGroup(
- QByteArray name, QVariantList states, int version)
+ const QByteArray &name, QVariantList states, int version)
: gen::xml::PropertyGroup(QByteArrayLiteral("option"))
{
// Append name property item.
appendChild<gen::xml::Property>(QByteArrayLiteral("name"),
- std::move(name));
+ name);
// Append version property item.
if (version >= 0)
diff --git a/src/plugins/generator/iarew/iarewoptionpropertygroup.h b/src/plugins/generator/iarew/iarewoptionpropertygroup.h
index 8844537c3..d80df2838 100644
--- a/src/plugins/generator/iarew/iarewoptionpropertygroup.h
+++ b/src/plugins/generator/iarew/iarewoptionpropertygroup.h
@@ -41,7 +41,7 @@ class IarewOptionPropertyGroup final
: public gen::xml::PropertyGroup
{
public:
- explicit IarewOptionPropertyGroup(QByteArray name, QVariantList states,
+ explicit IarewOptionPropertyGroup(const QByteArray &name, QVariantList states,
int version = -1);
};
diff --git a/src/plugins/generator/iarew/iarewproject.cpp b/src/plugins/generator/iarew/iarewproject.cpp
index 3e6a03ac2..0387ae114 100644
--- a/src/plugins/generator/iarew/iarewproject.cpp
+++ b/src/plugins/generator/iarew/iarewproject.cpp
@@ -42,11 +42,13 @@
#include <logging/translator.h>
+#include <tools/stlutils.h>
+
namespace qbs {
IarewProject::IarewProject(const GeneratableProject &genProject,
const GeneratableProductData &genProduct,
- const IarewVersionInfo &versionInfo)
+ const gen::VersionInfo &versionInfo)
{
Q_ASSERT(genProject.projects.size() == genProject.commandLines.size());
Q_ASSERT(genProject.projects.size() == genProduct.data.size());
@@ -112,12 +114,10 @@ IarewProject::IarewProject(const GeneratableProject &genProject,
continue;
auto sourceArtifacts = group.sourceArtifacts();
// Remove the linker script artifacts.
- sourceArtifacts.erase(std::remove_if(sourceArtifacts.begin(),
- sourceArtifacts.end(),
- [](const auto &artifact){
+ Internal::removeIf(sourceArtifacts, [](const auto &artifact) {
const auto tags = artifact.fileTags();
return tags.contains(QLatin1String("linkerscript"));
- }), sourceArtifacts.end());
+ });
if (sourceArtifacts.isEmpty())
continue;
diff --git a/src/plugins/generator/iarew/iarewproject.h b/src/plugins/generator/iarew/iarewproject.h
index 567a58c55..df260fcd8 100644
--- a/src/plugins/generator/iarew/iarewproject.h
+++ b/src/plugins/generator/iarew/iarewproject.h
@@ -39,14 +39,12 @@
namespace qbs {
-class IarewVersionInfo;
-
class IarewProject final : public gen::xml::Project
{
public:
explicit IarewProject(const GeneratableProject &genProject,
const GeneratableProductData &genProduct,
- const IarewVersionInfo &versionInfo);
+ const gen::VersionInfo &versionInfo);
private:
std::vector<std::unique_ptr<gen::xml::PropertyGroupFactory>> m_factories;
};
diff --git a/src/plugins/generator/iarew/iarewsettingspropertygroup.cpp b/src/plugins/generator/iarew/iarewsettingspropertygroup.cpp
index 1f08783ae..a07d59e0f 100644
--- a/src/plugins/generator/iarew/iarewsettingspropertygroup.cpp
+++ b/src/plugins/generator/iarew/iarewsettingspropertygroup.cpp
@@ -63,7 +63,7 @@ IarewSettingsPropertyGroup::IarewSettingsPropertyGroup()
QByteArrayLiteral("debug"), QVariant{});
}
-void IarewSettingsPropertyGroup::setName(QByteArray name)
+void IarewSettingsPropertyGroup::setName(const QByteArray &name)
{
// There is no way to move-construct a QVariant from T, thus name is shallow-copied
m_nameProperty->setValue(QVariant(name));
@@ -96,12 +96,11 @@ void IarewSettingsPropertyGroup::setDataDebugInfo(int debugInfo)
}
void IarewSettingsPropertyGroup::addOptionsGroup(
- QByteArray name,
+ const QByteArray &name,
QVariantList states,
int version)
{
- m_dataPropertyGroup->appendChild<IarewOptionPropertyGroup>(
- std::move(name), std::move(states), version);
+ m_dataPropertyGroup->appendChild<IarewOptionPropertyGroup>(name, std::move(states), version);
}
} // namespace qbs
diff --git a/src/plugins/generator/iarew/iarewsettingspropertygroup.h b/src/plugins/generator/iarew/iarewsettingspropertygroup.h
index 5ef9ef75c..b1feebcc7 100644
--- a/src/plugins/generator/iarew/iarewsettingspropertygroup.h
+++ b/src/plugins/generator/iarew/iarewsettingspropertygroup.h
@@ -40,7 +40,7 @@ class IarewSettingsPropertyGroup : public gen::xml::PropertyGroup
public:
explicit IarewSettingsPropertyGroup();
- void setName(QByteArray name);
+ void setName(const QByteArray &name);
QByteArray name() const;
void setArchiveVersion(int archiveVersion);
@@ -50,7 +50,7 @@ protected:
void setDataVersion(int dataVersion);
void setDataDebugInfo(int debugInfo);
- void addOptionsGroup(QByteArray name, QVariantList states,
+ void addOptionsGroup(const QByteArray &name, QVariantList states,
int version = -1);
private:
diff --git a/src/plugins/generator/iarew/iarewutils.cpp b/src/plugins/generator/iarew/iarewutils.cpp
index f00fce026..9d39ab264 100644
--- a/src/plugins/generator/iarew/iarewutils.cpp
+++ b/src/plugins/generator/iarew/iarewutils.cpp
@@ -76,7 +76,7 @@ QString projectRelativeFilePath(const QString &basePath,
OutputBinaryType outputBinaryType(const ProductData &qbsProduct)
{
- const auto qbsProductType = qbsProduct.type();
+ const auto &qbsProductType = qbsProduct.type();
if (qbsProductType.contains(QLatin1String("application")))
return ApplicationOutputType;
if (qbsProductType.contains(QLatin1String("staticlibrary")))
@@ -105,9 +105,9 @@ QString flagValue(const QStringList &flags, const QString &flagKey)
// Check that option is in form of 'flagKey=<flagValue>'.
if (flagIt->contains(QLatin1Char('='))) {
value = flagIt->split(QLatin1Char('=')).at(1).trimmed();
- } else if (flagKey.count() < flagIt->count()) {
+ } else if (flagKey.size() < flagIt->size()) {
// In this case an option is in form of 'flagKey<flagValue>'.
- value = flagIt->mid(flagKey.count()).trimmed();
+ value = flagIt->mid(flagKey.size()).trimmed();
} else {
// In this case an option is in form of 'flagKey <flagValue>'.
++flagIt;
diff --git a/src/plugins/generator/iarew/iarewversioninfo.cpp b/src/plugins/generator/iarew/iarewversioninfo.cpp
deleted file mode 100644
index 41a788e98..000000000
--- a/src/plugins/generator/iarew/iarewversioninfo.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "iarewversioninfo.h"
-
-#include <QtCore/qdebug.h>
-
-namespace qbs {
-
-IarewVersionInfo::IarewVersionInfo(
- const Version &version,
- const std::set<gen::utils::Architecture> &archs)
- : gen::VersionInfo(version, archs)
-{
-}
-
-std::set<IarewVersionInfo> IarewVersionInfo::knownVersions()
-{
- static const std::set<IarewVersionInfo> known = {
- {Version(8), {gen::utils::Architecture::Arm}},
- {Version(7), {gen::utils::Architecture::Avr,
- gen::utils::Architecture::Msp430}},
- {Version(10), {gen::utils::Architecture::Mcs51}},
- {Version(3), {gen::utils::Architecture::Stm8}},
- };
- return known;
-}
-
-int IarewVersionInfo::marketingVersion() const
-{
- const auto mv = gen::VersionInfo::marketingVersion();
- for (const IarewVersionInfo &known : knownVersions()) {
- if (known.version().majorVersion() == mv)
- return mv;
- }
- qWarning() << QStringLiteral("Unrecognized IAR EW version: ")
- << version().toString();
- return 0;
-}
-
-} // namespace qbs
diff --git a/src/plugins/generator/iarew/iarewversioninfo.h b/src/plugins/generator/iarew/iarewversioninfo.h
index 4fd1b1fbc..3dc459557 100644
--- a/src/plugins/generator/iarew/iarewversioninfo.h
+++ b/src/plugins/generator/iarew/iarewversioninfo.h
@@ -44,18 +44,17 @@
#include <generators/generatorversioninfo.h>
namespace qbs {
-
-class IarewVersionInfo final : public gen::VersionInfo
-{
-public:
- IarewVersionInfo(const Version &version,
- const std::set<gen::utils::Architecture> &archs);
-
- int marketingVersion() const final;
-
- static std::set<IarewVersionInfo> knownVersions();
+namespace IarewVersionInfo {
+
+constexpr gen::VersionInfo knownVersions[] = {
+ {Version(8), {gen::utils::Architecture::Arm}},
+ {Version(7), {gen::utils::Architecture::Avr,
+ gen::utils::Architecture::Msp430}},
+ {Version(10), {gen::utils::Architecture::Mcs51}},
+ {Version(3), {gen::utils::Architecture::Stm8}},
};
+} // namespace IarewVersionInfo
} // namespace qbs
#endif // QBS_IAREWVERSIONINFO_H
diff --git a/src/plugins/generator/keiluv/CMakeLists.txt b/src/plugins/generator/keiluv/CMakeLists.txt
new file mode 100644
index 000000000..3b4afbe16
--- /dev/null
+++ b/src/plugins/generator/keiluv/CMakeLists.txt
@@ -0,0 +1,80 @@
+set(COMMON_SOURCES
+ keiluvfilesgroupspropertygroup.cpp
+ keiluvfilesgroupspropertygroup.h
+ keiluvgenerator.cpp
+ keiluvgenerator.h
+ keiluvgeneratorplugin.cpp
+ keiluvproject.cpp
+ keiluvproject.h
+ keiluvprojectwriter.cpp
+ keiluvprojectwriter.h
+ keiluvutils.cpp
+ keiluvutils.h
+ keiluvversioninfo.h
+ keiluvworkspace.cpp
+ keiluvworkspace.h
+ keiluvworkspacewriter.cpp
+ keiluvworkspacewriter.h
+ )
+
+set(ARCHS_ARM_SOURCES
+ armbuildtargetgroup_v5.cpp
+ armbuildtargetgroup_v5.h
+ armcommonpropertygroup_v5.cpp
+ armcommonpropertygroup_v5.h
+ armdebugoptiongroup_v5.cpp
+ armdebugoptiongroup_v5.h
+ armdlloptiongroup_v5.cpp
+ armdlloptiongroup_v5.h
+ armtargetassemblergroup_v5.cpp
+ armtargetassemblergroup_v5.h
+ armtargetcommonoptionsgroup_v5.cpp
+ armtargetcommonoptionsgroup_v5.h
+ armtargetcompilergroup_v5.cpp
+ armtargetcompilergroup_v5.h
+ armtargetgroup_v5.cpp
+ armtargetgroup_v5.h
+ armtargetlinkergroup_v5.cpp
+ armtargetlinkergroup_v5.h
+ armtargetmiscgroup_v5.cpp
+ armtargetmiscgroup_v5.h
+ armutilitiesgroup_v5.cpp
+ armutilitiesgroup_v5.h
+ )
+list_transform_prepend(ARCHS_ARM_SOURCES archs/arm/)
+
+set(ARCHS_MCS51_SOURCES
+ mcs51buildtargetgroup_v5.cpp
+ mcs51buildtargetgroup_v5.h
+ mcs51commonpropertygroup_v5.cpp
+ mcs51commonpropertygroup_v5.h
+ mcs51debugoptiongroup_v5.cpp
+ mcs51debugoptiongroup_v5.h
+ mcs51dlloptiongroup_v5.cpp
+ mcs51dlloptiongroup_v5.h
+ mcs51targetassemblergroup_v5.cpp
+ mcs51targetassemblergroup_v5.h
+ mcs51targetcommonoptionsgroup_v5.cpp
+ mcs51targetcommonoptionsgroup_v5.h
+ mcs51targetcompilergroup_v5.cpp
+ mcs51targetcompilergroup_v5.h
+ mcs51targetgroup_v5.cpp
+ mcs51targetgroup_v5.h
+ mcs51targetlinkergroup_v5.cpp
+ mcs51targetlinkergroup_v5.h
+ mcs51targetmiscgroup_v5.cpp
+ mcs51targetmiscgroup_v5.h
+ mcs51utilitiesgroup_v5.cpp
+ mcs51utilitiesgroup_v5.h
+ mcs51utils.cpp
+ mcs51utils.h
+ )
+list_transform_prepend(ARCHS_MCS51_SOURCES archs/mcs51/)
+
+add_qbs_plugin(keiluvgenerator
+ DEPENDS qbscore qbsjson
+ SOURCES
+ ${COMMON_SOURCES}
+ ${ARCHS_ARM_SOURCES}
+ ${ARCHS_MCS51_SOURCES}
+ )
diff --git a/src/plugins/generator/keiluv/archs/arm/armtargetcommonoptionsgroup_v5.cpp b/src/plugins/generator/keiluv/archs/arm/armtargetcommonoptionsgroup_v5.cpp
index 0ffbcaa5b..f7e3de3d9 100644
--- a/src/plugins/generator/keiluv/archs/arm/armtargetcommonoptionsgroup_v5.cpp
+++ b/src/plugins/generator/keiluv/archs/arm/armtargetcommonoptionsgroup_v5.cpp
@@ -41,7 +41,7 @@ namespace v5 {
namespace {
-static const struct DeviceEntry {
+const struct DeviceEntry {
QByteArray cpu; // CPU option.
std::set<QByteArray> fpus; // FPU's options.
QByteArray device; // Project file entry.
diff --git a/src/plugins/generator/keiluv/archs/arm/armtargetlinkergroup_v5.cpp b/src/plugins/generator/keiluv/archs/arm/armtargetlinkergroup_v5.cpp
index 930aae020..f2bc8367e 100644
--- a/src/plugins/generator/keiluv/archs/arm/armtargetlinkergroup_v5.cpp
+++ b/src/plugins/generator/keiluv/archs/arm/armtargetlinkergroup_v5.cpp
@@ -34,6 +34,8 @@
#include <generators/generatorutils.h>
+#include <tools/stlutils.h>
+
namespace qbs {
namespace keiluv {
namespace arm {
@@ -64,8 +66,7 @@ struct LinkerPageOptions final
// Enumerate all product linker config files
// (which are set trough 'linkerscript' tag).
- const auto qbsGroups = qbsProduct.groups();
- for (const auto &qbsGroup : qbsGroups) {
+ for (const auto &qbsGroup : qbsProduct.groups()) {
if (!qbsGroup.isEnabled())
continue;
const auto qbsArtifacts = qbsGroup.sourceArtifacts();
@@ -92,18 +93,15 @@ struct LinkerPageOptions final
// Transform all paths to relative.
const QString baseDirectory = qbs::gen::utils::buildRootPath(qbsProject);
- std::transform(scatterFiles.begin(), scatterFiles.end(),
- std::back_inserter(scatterFiles),
- [baseDirectory](const QString &scatterFile) {
- return gen::utils::relativeFilePath(baseDirectory, scatterFile);
- });
+ Internal::transform(scatterFiles, [&baseDirectory](const auto &file) {
+ return gen::utils::relativeFilePath(baseDirectory, file); });
// Make a first scatter file as a main scatter file.
// Other scatter files will be interpretes as a misc controls.
if (scatterFiles.count() > 0)
mainScatterFile = scatterFiles.takeFirst();
- for (const auto &scatterFile : qAsConst(scatterFiles)) {
+ for (const auto &scatterFile : std::as_const(scatterFiles)) {
const auto control = QStringLiteral("--scatter %1").arg(scatterFile);
miscControls.push_back(control);
}
diff --git a/src/plugins/generator/keiluv/archs/mcs51/mcs51utils.cpp b/src/plugins/generator/keiluv/archs/mcs51/mcs51utils.cpp
index 698f0df78..70ec47a62 100644
--- a/src/plugins/generator/keiluv/archs/mcs51/mcs51utils.cpp
+++ b/src/plugins/generator/keiluv/archs/mcs51/mcs51utils.cpp
@@ -30,6 +30,8 @@
#include "mcs51utils.h"
+#include <tools/stlutils.h>
+
namespace qbs {
namespace keiluv {
namespace mcs51 {
@@ -71,8 +73,7 @@ QString flagValue(const QStringList &flags, const QString &flagKey)
QStringList flagValueParts(const QString &flagValue, const QLatin1Char &sep)
{
auto parts = flagValue.split(sep);
- std::transform(parts.begin(), parts.end(), parts.begin(),
- [](const auto &part) { return part.trimmed(); });
+ Internal::transform(parts, [](const auto &part) { return part.trimmed(); });
return parts;
}
diff --git a/src/plugins/generator/keiluv/keiluv.pri b/src/plugins/generator/keiluv/keiluv.pri
deleted file mode 100644
index 6995fb059..000000000
--- a/src/plugins/generator/keiluv/keiluv.pri
+++ /dev/null
@@ -1 +0,0 @@
-qbsPluginTarget = keiluvgenerator
diff --git a/src/plugins/generator/keiluv/keiluv.pro b/src/plugins/generator/keiluv/keiluv.pro
deleted file mode 100644
index 52d7c0ed4..000000000
--- a/src/plugins/generator/keiluv/keiluv.pro
+++ /dev/null
@@ -1,91 +0,0 @@
-include(keiluv.pri)
-include(../../plugins.pri)
-include(../../../shared/json/json.pri)
-
-QT = core
-
-# Plugin file.
-
-SOURCES += \
- $$PWD/keiluvgeneratorplugin.cpp \
-
-# Common files.
-
-HEADERS += \
- $$PWD/keiluvconstants.h \
- $$PWD/keiluvfilesgroupspropertygroup.h \
- $$PWD/keiluvgenerator.h \
- $$PWD/keiluvproject.h \
- $$PWD/keiluvprojectwriter.h \
- $$PWD/keiluvutils.h \
- $$PWD/keiluvversioninfo.h \
- $$PWD/keiluvworkspace.h \
- $$PWD/keiluvworkspacewriter.h
-
-SOURCES += \
- $$PWD/keiluvfilesgroupspropertygroup.cpp \
- $$PWD/keiluvgenerator.cpp \
- $$PWD/keiluvproject.cpp \
- $$PWD/keiluvprojectwriter.cpp \
- $$PWD/keiluvutils.cpp \
- $$PWD/keiluvversioninfo.cpp \
- $$PWD/keiluvworkspace.cpp \
- $$PWD/keiluvworkspacewriter.cpp
-
-# For MCS51 architecture.
-
-HEADERS += \
- $$PWD/archs/mcs51/mcs51buildtargetgroup_v5.h \
- $$PWD/archs/mcs51/mcs51commonpropertygroup_v5.h \
- $$PWD/archs/mcs51/mcs51debugoptiongroup_v5.h \
- $$PWD/archs/mcs51/mcs51dlloptiongroup_v5.h \
- $$PWD/archs/mcs51/mcs51targetassemblergroup_v5.h \
- $$PWD/archs/mcs51/mcs51targetcommonoptionsgroup_v5.h \
- $$PWD/archs/mcs51/mcs51targetcompilergroup_v5.h \
- $$PWD/archs/mcs51/mcs51targetgroup_v5.h \
- $$PWD/archs/mcs51/mcs51targetlinkergroup_v5.h \
- $$PWD/archs/mcs51/mcs51targetmiscgroup_v5.h \
- $$PWD/archs/mcs51/mcs51utilitiesgroup_v5.h \
- $$PWD/archs/mcs51/mcs51utils.h
-
-SOURCES += \
- $$PWD/archs/mcs51/mcs51buildtargetgroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51commonpropertygroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51debugoptiongroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51dlloptiongroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51targetassemblergroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51targetcommonoptionsgroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51targetcompilergroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51targetgroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51targetlinkergroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51targetmiscgroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51utilitiesgroup_v5.cpp \
- $$PWD/archs/mcs51/mcs51utils.cpp
-
-# For ARM architecture.
-
-HEADERS += \
- $$PWD/archs/arm/armbuildtargetgroup_v5.h \
- $$PWD/archs/arm/armcommonpropertygroup_v5.h \
- $$PWD/archs/arm/armdebugoptiongroup_v5.h \
- $$PWD/archs/arm/armdlloptiongroup_v5.h \
- $$PWD/archs/arm/armtargetassemblergroup_v5.h \
- $$PWD/archs/arm/armtargetcommonoptionsgroup_v5.h \
- $$PWD/archs/arm/armtargetcompilergroup_v5.h \
- $$PWD/archs/arm/armtargetgroup_v5.h \
- $$PWD/archs/arm/armtargetlinkergroup_v5.h \
- $$PWD/archs/arm/armtargetmiscgroup_v5.h \
- $$PWD/archs/arm/armutilitiesgroup_v5.h
-
-SOURCES += \
- $$PWD/archs/arm/armbuildtargetgroup_v5.cpp \
- $$PWD/archs/arm/armcommonpropertygroup_v5.cpp \
- $$PWD/archs/arm/armdebugoptiongroup_v5.cpp \
- $$PWD/archs/arm/armdlloptiongroup_v5.cpp \
- $$PWD/archs/arm/armtargetassemblergroup_v5.cpp \
- $$PWD/archs/arm/armtargetcommonoptionsgroup_v5.cpp \
- $$PWD/archs/arm/armtargetcompilergroup_v5.cpp \
- $$PWD/archs/arm/armtargetgroup_v5.cpp \
- $$PWD/archs/arm/armtargetlinkergroup_v5.cpp \
- $$PWD/archs/arm/armtargetmiscgroup_v5.cpp \
- $$PWD/archs/arm/armutilitiesgroup_v5.cpp
diff --git a/src/plugins/generator/keiluv/keiluv.qbs b/src/plugins/generator/keiluv/keiluv.qbs
index 65bf6d65b..6074380b4 100644
--- a/src/plugins/generator/keiluv/keiluv.qbs
+++ b/src/plugins/generator/keiluv/keiluv.qbs
@@ -1,4 +1,3 @@
-import qbs
import "../../qbsplugin.qbs" as QbsPlugin
QbsPlugin {
@@ -21,7 +20,6 @@ QbsPlugin {
"keiluvprojectwriter.h",
"keiluvutils.cpp",
"keiluvutils.h",
- "keiluvversioninfo.cpp",
"keiluvversioninfo.h",
"keiluvworkspace.cpp",
"keiluvworkspace.h",
diff --git a/src/plugins/generator/keiluv/keiluvfilesgroupspropertygroup.cpp b/src/plugins/generator/keiluv/keiluvfilesgroupspropertygroup.cpp
index b9a234d6a..79281a153 100644
--- a/src/plugins/generator/keiluv/keiluvfilesgroupspropertygroup.cpp
+++ b/src/plugins/generator/keiluv/keiluvfilesgroupspropertygroup.cpp
@@ -33,6 +33,7 @@
#include <generators/generatordata.h>
+#include <tools/stlutils.h>
#include <tools/stringconstants.h>
namespace qbs {
@@ -76,22 +77,24 @@ private:
if (fileSuffix.compare(QLatin1String("c"),
Qt::CaseInsensitive) == 0) {
return CSourceFileType;
- } else if (fileSuffix.compare(QLatin1String("cpp"),
+ }
+ if (fileSuffix.compare(QLatin1String("cpp"),
Qt::CaseInsensitive) == 0) {
return CppSourceFileType;
- } else if (fileSuffix.compare(QLatin1String("s"),
+ }
+ if (fileSuffix.compare(QLatin1String("s"),
Qt::CaseInsensitive) == 0
|| fileSuffix.compare(QLatin1String("a51"),
Qt::CaseInsensitive) == 0) {
return AssemblerFileType;
- } else if (fileSuffix.compare(QLatin1String("lib"),
+ }
+ if (fileSuffix.compare(QLatin1String("lib"),
Qt::CaseInsensitive) == 0) {
return LibraryFileType;
- } else {
- // All header files, text files and include files
- // interpretes as a text file types.
- return TextFileType;
}
+ // All header files, text files and include files
+ // interpretes as a text file types.
+ return TextFileType;
}
};
@@ -158,19 +161,16 @@ KeiluvFilesGroupsPropertyGroup::KeiluvFilesGroupsPropertyGroup(
const auto baseDirectory = gen::utils::buildRootPath(qbsProject);
// Build source items.
- const auto groups = qbsProduct.groups();
- for (const auto &group : groups) {
+ for (const auto &group : qbsProduct.groups()) {
// Ignore disabled groups (e.g. when its condition property is false).
if (!group.isEnabled())
continue;
auto sourceArtifacts = group.sourceArtifacts();
// Remove the linker script artifacts.
- sourceArtifacts.erase(std::remove_if(sourceArtifacts.begin(),
- sourceArtifacts.end(),
- [](const auto &artifact){
+ Internal::removeIf(sourceArtifacts, [](const auto &artifact){
const auto tags = artifact.fileTags();
return tags.contains(QLatin1String("linkerscript"));
- }), sourceArtifacts.end());
+ });
if (sourceArtifacts.isEmpty())
continue;
diff --git a/src/plugins/generator/keiluv/keiluvgenerator.cpp b/src/plugins/generator/keiluv/keiluvgenerator.cpp
index e3c959e8d..f98f55abe 100644
--- a/src/plugins/generator/keiluv/keiluvgenerator.cpp
+++ b/src/plugins/generator/keiluv/keiluvgenerator.cpp
@@ -84,7 +84,7 @@ static void writeWorkspace(const std::shared_ptr<KeiluvWorkspace> &wokspace,
QFileInfo(workspaceFilePath).fileName());
}
-KeiluvGenerator::KeiluvGenerator(const KeiluvVersionInfo &versionInfo)
+KeiluvGenerator::KeiluvGenerator(const gen::VersionInfo &versionInfo)
: m_versionInfo(versionInfo)
{
}
diff --git a/src/plugins/generator/keiluv/keiluvgenerator.h b/src/plugins/generator/keiluv/keiluvgenerator.h
index 769519562..25b89b9c8 100644
--- a/src/plugins/generator/keiluv/keiluvgenerator.h
+++ b/src/plugins/generator/keiluv/keiluvgenerator.h
@@ -45,7 +45,7 @@ class KeiluvGenerator final : public ProjectGenerator,
private IGeneratableProjectVisitor
{
public:
- explicit KeiluvGenerator(const KeiluvVersionInfo &versionInfo);
+ explicit KeiluvGenerator(const gen::VersionInfo &versionInfo);
QString generatorName() const final;
void generate() final;
@@ -60,7 +60,7 @@ private:
const GeneratableProjectData &projectData,
const GeneratableProductData &productData) final;
- const KeiluvVersionInfo m_versionInfo;
+ const gen::VersionInfo m_versionInfo;
std::shared_ptr<KeiluvWorkspace> m_workspace;
QString m_workspaceFilePath;
std::map<QString, std::shared_ptr<KeiluvProject>> m_projects;
diff --git a/src/plugins/generator/keiluv/keiluvgeneratorplugin.cpp b/src/plugins/generator/keiluv/keiluvgeneratorplugin.cpp
index 91b73ef71..abbccc3da 100644
--- a/src/plugins/generator/keiluv/keiluvgeneratorplugin.cpp
+++ b/src/plugins/generator/keiluv/keiluvgeneratorplugin.cpp
@@ -45,7 +45,7 @@
static void QbsKeiluvGeneratorPluginLoad()
{
- for (const auto &info : qbs::KeiluvVersionInfo::knownVersions()) {
+ for (const auto &info : qbs::KeiluvVersionInfo::knownVersions) {
qbs::ProjectGeneratorManager::registerGenerator(
std::make_shared<qbs::KeiluvGenerator>(info));
}
diff --git a/src/plugins/generator/keiluv/keiluvproject.cpp b/src/plugins/generator/keiluv/keiluvproject.cpp
index 476ce8e69..89db73b1f 100644
--- a/src/plugins/generator/keiluv/keiluvproject.cpp
+++ b/src/plugins/generator/keiluv/keiluvproject.cpp
@@ -40,7 +40,7 @@
namespace qbs {
-static QString keilProjectSchema(const KeiluvVersionInfo &info)
+static QString keilProjectSchema(const gen::VersionInfo &info)
{
const auto v = info.marketingVersion();
switch (v) {
@@ -51,10 +51,9 @@ static QString keilProjectSchema(const KeiluvVersionInfo &info)
}
}
-KeiluvProject::KeiluvProject(
- const qbs::GeneratableProject &genProject,
+KeiluvProject::KeiluvProject(const qbs::GeneratableProject &genProject,
const qbs::GeneratableProductData &genProduct,
- const KeiluvVersionInfo &versionInfo)
+ const gen::VersionInfo &versionInfo)
{
Q_ASSERT(genProject.projects.size() == genProject.commandLines.size());
Q_ASSERT(genProject.projects.size() == genProduct.data.size());
diff --git a/src/plugins/generator/keiluv/keiluvproject.h b/src/plugins/generator/keiluv/keiluvproject.h
index 98b2815ed..da86f71fd 100644
--- a/src/plugins/generator/keiluv/keiluvproject.h
+++ b/src/plugins/generator/keiluv/keiluvproject.h
@@ -40,15 +40,13 @@
namespace qbs {
-class KeiluvVersionInfo;
-
class KeiluvProject final : public gen::xml::Project
{
public:
explicit KeiluvProject(
const qbs::GeneratableProject &genProject,
const qbs::GeneratableProductData &genProduct,
- const KeiluvVersionInfo &versionInfo);
+ const gen::VersionInfo &versionInfo);
private:
std::vector<std::unique_ptr<gen::xml::PropertyGroupFactory>> m_factories;
};
diff --git a/src/plugins/generator/keiluv/keiluvutils.cpp b/src/plugins/generator/keiluv/keiluvutils.cpp
index 8932f0f16..628a8e98a 100644
--- a/src/plugins/generator/keiluv/keiluvutils.cpp
+++ b/src/plugins/generator/keiluv/keiluvutils.cpp
@@ -32,12 +32,14 @@
#include <generators/generatorutils.h>
+#include <tools/stlutils.h>
+
namespace qbs {
namespace KeiluvUtils {
OutputBinaryType outputBinaryType(const ProductData &qbsProduct)
{
- const auto qbsProductType = qbsProduct.type();
+ const auto &qbsProductType = qbsProduct.type();
if (qbsProductType.contains(QLatin1String("application")))
return ApplicationOutputType;
if (qbsProductType.contains(QLatin1String("staticlibrary")))
@@ -82,10 +84,8 @@ QStringList includes(const PropertyMap &qbsProps)
qbsProps, {QStringLiteral("includePaths"),
QStringLiteral("systemIncludePaths")});
// Transform include path separators to native.
- std::transform(paths.begin(), paths.end(), paths.begin(),
- [](const auto &path) {
- return QDir::toNativeSeparators(path);
- });
+ Internal::transform(paths, [](const auto &path) {
+ return QDir::toNativeSeparators(path); });
return paths;
}
@@ -100,23 +100,18 @@ QStringList staticLibraries(const PropertyMap &qbsProps)
auto libs = gen::utils::cppStringModuleProperties(
qbsProps, {QStringLiteral("staticLibraries")});
// Transform library path separators to native.
- std::transform(libs.begin(), libs.end(), libs.begin(),
- [](const auto &path) {
- return QDir::toNativeSeparators(path);
- });
+ Internal::transform(libs, [](const auto &path) {
+ return QDir::toNativeSeparators(path); });
return libs;
}
QStringList dependencies(const std::vector<ProductData> &qbsProductDeps)
{
- QStringList deps;
- for (const ProductData &qbsProductDep : qbsProductDeps) {
- const auto path = qbsProductDep.buildDirectory()
- + QLatin1String("/obj/")
- + gen::utils::targetBinary(qbsProductDep);
- deps.push_back(QDir::toNativeSeparators(path));
- }
- return deps;
+ return Internal::transformed<QStringList>(qbsProductDeps, [](const auto &dep) {
+ const auto path = dep.buildDirectory() + QLatin1String("/obj/")
+ + gen::utils::targetBinary(dep);
+ return QDir::toNativeSeparators(path);
+ });
}
} // namespace KeiluvUtils
diff --git a/src/plugins/generator/keiluv/keiluvversioninfo.cpp b/src/plugins/generator/keiluv/keiluvversioninfo.cpp
deleted file mode 100644
index 973d0a420..000000000
--- a/src/plugins/generator/keiluv/keiluvversioninfo.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Denis Shienkov <denis.shienkov@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qbs.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** 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 "keiluvconstants.h"
-#include "keiluvversioninfo.h"
-
-#include <QtCore/qdebug.h>
-
-namespace qbs {
-
-KeiluvVersionInfo::KeiluvVersionInfo(
- const Version &version,
- const std::set<gen::utils::Architecture> &archs)
- : gen::VersionInfo(version, archs)
-{
-}
-
-std::set<KeiluvVersionInfo> KeiluvVersionInfo::knownVersions()
-{
- static const std::set<KeiluvVersionInfo> known = {
- {Version(KeiluvConstants::v5::kUVisionVersion),
- {gen::utils::Architecture::Mcs51,
- gen::utils::Architecture::Arm}},
- };
- return known;
-}
-
-int KeiluvVersionInfo::marketingVersion() const
-{
- const auto mv = gen::VersionInfo::marketingVersion();
- for (const KeiluvVersionInfo &known : knownVersions()) {
- if (known.version().majorVersion() == mv)
- return mv;
- }
- qWarning() << QStringLiteral("Unrecognized KEIL UV version: ")
- << version().toString();
- return 0;
-}
-
-} // namespace qbs
diff --git a/src/plugins/generator/keiluv/keiluvversioninfo.h b/src/plugins/generator/keiluv/keiluvversioninfo.h
index a64513492..cc379c3d7 100644
--- a/src/plugins/generator/keiluv/keiluvversioninfo.h
+++ b/src/plugins/generator/keiluv/keiluvversioninfo.h
@@ -40,22 +40,21 @@
#ifndef QBS_KEILUVVERSIONINFO_H
#define QBS_KEILUVVERSIONINFO_H
+#include "keiluvconstants.h"
+
#include <generators/generatorutils.h>
#include <generators/generatorversioninfo.h>
namespace qbs {
+namespace KeiluvVersionInfo {
-class KeiluvVersionInfo final : public gen::VersionInfo
-{
-public:
- KeiluvVersionInfo(const Version &version,
- const std::set<gen::utils::Architecture> &archs);
-
- int marketingVersion() const final;
-
- static std::set<KeiluvVersionInfo> knownVersions();
+constexpr gen::VersionInfo knownVersions[] = {
+ {Version(KeiluvConstants::v5::kUVisionVersion),
+ {gen::utils::Architecture::Mcs51,
+ gen::utils::Architecture::Arm}},
};
+} // namespace KeiluvVersionInfo
} // namespace qbs
#endif // QBS_KEILUVVERSIONINFO_H
diff --git a/src/plugins/generator/makefilegenerator/CMakeLists.txt b/src/plugins/generator/makefilegenerator/CMakeLists.txt
new file mode 100644
index 000000000..8e6e3510a
--- /dev/null
+++ b/src/plugins/generator/makefilegenerator/CMakeLists.txt
@@ -0,0 +1,10 @@
+set(SOURCES
+ makefilegenerator.cpp
+ makefilegenerator.h
+ makefilegeneratorplugin.cpp
+ )
+
+add_qbs_plugin(makefilegenerator
+ DEPENDS qbscore
+ SOURCES ${SOURCES}
+ )
diff --git a/src/plugins/generator/makefilegenerator/makefilegenerator.cpp b/src/plugins/generator/makefilegenerator/makefilegenerator.cpp
index 24aff5a33..690d83569 100644
--- a/src/plugins/generator/makefilegenerator/makefilegenerator.cpp
+++ b/src/plugins/generator/makefilegenerator/makefilegenerator.cpp
@@ -167,7 +167,7 @@ void qbs::MakefileGenerator::generate()
}
QTextStream stream(&makefile);
ErrorInfo error;
- ProjectTransformerData projectTransformerData = theProject.transformerData(&error);
+ const ProjectTransformerData projectTransformerData = theProject.transformerData(&error);
if (error.hasError())
throw error;
stream << "# This file was generated by qbs" << "\n\n";
@@ -182,7 +182,7 @@ void qbs::MakefileGenerator::generate()
"is not supported by this generator.").arg(srcDir));
}
stream << "SRCDIR = " << QDir::toNativeSeparators(srcDir) << '\n';
- const QString buildDir = projectData.buildDirectory();
+ const QString &buildDir = projectData.buildDirectory();
if (buildDir.contains(QLatin1Char(' '))) {
throw ErrorInfo(Tr::tr("The build directory '%1' contains space characters, which"
"is not supported by this generator.").arg(buildDir));
@@ -286,8 +286,8 @@ void qbs::MakefileGenerator::generate()
}
if (!processCommandEncountered && builtByDefault) {
const auto outputs = transformerData.outputs();
- for (const ArtifactData &output : outputs)
- filesCreatedByJsCommands.push_back(output.filePath());
+ transform(outputs, filesCreatedByJsCommands, [](const auto &output) {
+ return output.filePath(); });
}
}
stream << "install-" << productTarget << ": " << productTarget << '\n';
@@ -319,8 +319,7 @@ void qbs::MakefileGenerator::generate()
<< transformedInputFilePath << ' ' << transformedOutputDir << '\n';
}
stream << "clean-" << productTarget << ":\n";
- const auto generatedArtifacts = productData.generatedArtifacts();
- for (const ArtifactData &artifact : generatedArtifacts) {
+ for (const ArtifactData &artifact : productData.generatedArtifacts()) {
const QFileInfo fileInfo(artifact.filePath());
const QString transformedFilePath = QDir::toNativeSeparators(
prefixifiedBuildDirPath(fileInfo.path())
@@ -333,15 +332,15 @@ void qbs::MakefileGenerator::generate()
}
stream << "all:";
- for (const QString &target : qAsConst(allDefaultTargets))
+ for (const QString &target : std::as_const(allDefaultTargets))
stream << ' ' << target;
stream << '\n';
stream << "install:";
- for (const QString &target : qAsConst(allDefaultTargets))
+ for (const QString &target : std::as_const(allDefaultTargets))
stream << ' ' << "install-" << target;
stream << '\n';
stream << "clean:";
- for (const QString &target : qAsConst(allTargets))
+ for (const QString &target : std::as_const(allTargets))
stream << ' ' << "clean-" << target;
stream << '\n';
if (!filesCreatedByJsCommands.empty()) {
diff --git a/src/plugins/generator/makefilegenerator/makefilegenerator.pri b/src/plugins/generator/makefilegenerator/makefilegenerator.pri
deleted file mode 100644
index 971e63d24..000000000
--- a/src/plugins/generator/makefilegenerator/makefilegenerator.pri
+++ /dev/null
@@ -1 +0,0 @@
-qbsPluginTarget = makefilegenerator
diff --git a/src/plugins/generator/makefilegenerator/makefilegenerator.pro b/src/plugins/generator/makefilegenerator/makefilegenerator.pro
deleted file mode 100644
index c03191c39..000000000
--- a/src/plugins/generator/makefilegenerator/makefilegenerator.pro
+++ /dev/null
@@ -1,11 +0,0 @@
-include(makefilegenerator.pri)
-include(../../plugins.pri)
-
-QT = core
-
-HEADERS += \
- $$PWD/makefilegenerator.h
-
-SOURCES += \
- $$PWD/makefilegenerator.cpp \
- $$PWD/makefilegeneratorplugin.cpp
diff --git a/src/plugins/generator/makefilegenerator/makefilegenerator.qbs b/src/plugins/generator/makefilegenerator/makefilegenerator.qbs
index baabc43e3..8d61defe1 100644
--- a/src/plugins/generator/makefilegenerator/makefilegenerator.qbs
+++ b/src/plugins/generator/makefilegenerator/makefilegenerator.qbs
@@ -1,4 +1,3 @@
-import qbs
import "../../qbsplugin.qbs" as QbsPlugin
QbsPlugin {
diff --git a/src/plugins/generator/visualstudio/CMakeLists.txt b/src/plugins/generator/visualstudio/CMakeLists.txt
new file mode 100644
index 000000000..01d0abbfa
--- /dev/null
+++ b/src/plugins/generator/visualstudio/CMakeLists.txt
@@ -0,0 +1,25 @@
+set(SOURCES
+ 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
+ visualstudiogeneratorplugin.cpp
+ )
+
+add_qbs_plugin(visualstudiogenerator
+ DEPENDS qbscore qbsmsbuild qbsjson
+ SOURCES ${SOURCES}
+ )
diff --git a/src/plugins/generator/visualstudio/io/msbuildprojectwriter.cpp b/src/plugins/generator/visualstudio/io/msbuildprojectwriter.cpp
deleted file mode 100644
index 12fbe2da5..000000000
--- a/src/plugins/generator/visualstudio/io/msbuildprojectwriter.cpp
+++ /dev/null
@@ -1,238 +0,0 @@
-/****************************************************************************
-**
-** 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 <QtCore/qxmlstream.h>
-
-#include <memory>
-
-namespace qbs {
-
-static const QString kMSBuildSchemaURI =
- QStringLiteral("http://schemas.microsoft.com/developer/msbuild/2003");
-
-class MSBuildProjectWriterPrivate : public IMSBuildNodeVisitor
-{
-public:
- std::ostream *device = nullptr;
- QByteArray buffer;
- std::unique_ptr<QXmlStreamWriter> 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(std::ostream *device)
- : d(new MSBuildProjectWriterPrivate)
-{
- d->device = device;
- d->writer = std::make_unique<QXmlStreamWriter>(&d->buffer);
- d->writer->setAutoFormatting(true);
-}
-
-MSBuildProjectWriter::~MSBuildProjectWriter()
-{
- delete d;
-}
-
-bool MSBuildProjectWriter::write(const MSBuildProject *project)
-{
- d->buffer.clear();
- d->writer->writeStartDocument();
- project->accept(d);
- d->writer->writeEndDocument();
- if (d->writer->hasError())
- return false;
- d->device->write(&*std::begin(d->buffer), d->buffer.size());
- return d->device->good();
-}
-
-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/plugins/generator/visualstudio/io/msbuildprojectwriter.h b/src/plugins/generator/visualstudio/io/msbuildprojectwriter.h
deleted file mode 100644
index cfe6beb7b..000000000
--- a/src/plugins/generator/visualstudio/io/msbuildprojectwriter.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/****************************************************************************
-**
-** 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 <ostream>
-
-#include <QtCore/qglobal.h>
-
-namespace qbs {
-
-class MSBuildProject;
-class MSBuildProjectWriterPrivate;
-
-class MSBuildProjectWriter
-{
- Q_DISABLE_COPY(MSBuildProjectWriter)
-public:
- explicit MSBuildProjectWriter(std::ostream *device);
- ~MSBuildProjectWriter();
-
- bool write(const MSBuildProject *project);
-
-private:
- MSBuildProjectWriterPrivate *d;
-};
-
-} // namespace qbs
-
-#endif // MSBUILDPROJECTWRITER_H
diff --git a/src/plugins/generator/visualstudio/io/visualstudiosolutionwriter.cpp b/src/plugins/generator/visualstudio/io/visualstudiosolutionwriter.cpp
deleted file mode 100644
index 625489ac6..000000000
--- a/src/plugins/generator/visualstudio/io/visualstudiosolutionwriter.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-/****************************************************************************
-**
-** 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 <tools/hostosinfo.h>
-#include <tools/pathutils.h>
-#include <tools/stlutils.h>
-#include <tools/visualstudioversioninfo.h>
-
-#include <QtCore/qdir.h>
-#include <QtCore/qfile.h>
-#include <QtCore/qfileinfo.h>
-#include <QtCore/quuid.h>
-
-#include <vector>
-
-namespace qbs {
-
-using namespace Internal;
-
-class VisualStudioSolutionWriterPrivate
-{
-public:
- std::ostream *device = nullptr;
- std::string baseDir;
-};
-
-VisualStudioSolutionWriter::VisualStudioSolutionWriter(std::ostream *device)
- : d(new VisualStudioSolutionWriterPrivate)
-{
- d->device = device;
-}
-
-VisualStudioSolutionWriter::~VisualStudioSolutionWriter() = default;
-
-std::string VisualStudioSolutionWriter::projectBaseDirectory() const
-{
- return d->baseDir;
-}
-
-void VisualStudioSolutionWriter::setProjectBaseDirectory(const std::string &dir)
-{
- d->baseDir = dir;
-}
-
-bool VisualStudioSolutionWriter::write(const VisualStudioSolution *solution)
-{
- auto &out = *d->device;
- out << u8"Microsoft Visual Studio Solution File, Format Version "
- << solution->versionInfo().solutionVersion().toStdString()
- << u8"\n# Visual Studio "
- << solution->versionInfo().version().majorVersion()
- << u8"\n";
-
- const auto fileProjects = solution->fileProjects();
- for (const auto &project : fileProjects) {
- auto projectFilePath = project->filePath().toStdString();
-
- // Try to make the project file path relative to the
- // solution file path if we're writing to a file device
- if (!d->baseDir.empty()) {
- const QDir solutionDir(QString::fromStdString(d->baseDir));
- projectFilePath = Internal::PathUtils::toNativeSeparators(
- solutionDir.relativeFilePath(QString::fromStdString(projectFilePath)),
- Internal::HostOsInfo::HostOsWindows).toStdString();
- }
-
- out << u8"Project(\""
- << project->projectTypeGuid().toString().toStdString()
- << u8"\") = \""
- << QFileInfo(QString::fromStdString(projectFilePath)).baseName().toStdString()
- << u8"\", \""
- << projectFilePath
- << u8"\", \""
- << project->guid().toString().toStdString()
- << u8"\"\n";
-
- const auto dependencies = solution->dependencies(project);
- if (!dependencies.empty()) {
- out << u8"\tProjectSection(ProjectDependencies) = postProject\n";
-
- for (const auto &dependency : dependencies)
- out << u8"\t\t"
- << dependency->guid().toString().toStdString()
- << u8" = "
- << dependency->guid().toString().toStdString()
- << u8"\n";
-
- out << u8"\tEndProjectSection\n";
- }
-
- out << u8"EndProject\n";
- }
-
- const auto folderProjects = solution->folderProjects();
- for (const auto &project : folderProjects) {
- out << u8"Project(\""
- << project->projectTypeGuid().toString().toStdString()
- << u8"\") = \""
- << project->name().toStdString()
- << u8"\", \""
- << project->name().toStdString()
- << u8"\", \""
- << project->guid().toString().toStdString()
- << u8"\"\n";
-
- out << u8"EndProject\n";
- }
-
- out << u8"Global\n";
-
- const auto globalSections = solution->globalSections();
- for (const auto &globalSection : globalSections) {
- out << u8"\tGlobalSection("
- << globalSection->name().toStdString()
- << u8") = "
- << (globalSection->isPost() ? u8"postSolution" : u8"preSolution")
- << u8"\n";
- for (const auto &property : globalSection->properties())
- out << u8"\t\t"
- << property.first.toStdString()
- << u8" = "
- << property.second.toStdString()
- << u8"\n";
-
- out << u8"\tEndGlobalSection\n";
- }
-
- out << u8"EndGlobal\n";
-
- return out.good();
-}
-
-} // namespace qbs
diff --git a/src/plugins/generator/visualstudio/msbuild/imsbuildproperty.cpp b/src/plugins/generator/visualstudio/msbuild/imsbuildproperty.cpp
deleted file mode 100644
index be18f1a5b..000000000
--- a/src/plugins/generator/visualstudio/msbuild/imsbuildproperty.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************************
-**
-** 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() = default;
-
-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/plugins/generator/visualstudio/msbuild/imsbuildproperty.h b/src/plugins/generator/visualstudio/msbuild/imsbuildproperty.h
deleted file mode 100644
index 13f26f070..000000000
--- a/src/plugins/generator/visualstudio/msbuild/imsbuildproperty.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
-**
-** 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 <QtCore/qobject.h>
-#include <QtCore/qvariant.h>
-
-#include <memory>
-
-namespace qbs {
-
-class IMSBuildPropertyPrivate;
-
-class IMSBuildProperty : public QObject
-{
- Q_OBJECT
-protected:
- explicit IMSBuildProperty(QObject *parent = nullptr);
-
-public:
- ~IMSBuildProperty() override;
-
- 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:
- std::unique_ptr<IMSBuildPropertyPrivate> d;
-};
-
-} // namespace qbs
-
-#endif // IMSBUILDPROPERTY_H
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuilditemmetadata.cpp b/src/plugins/generator/visualstudio/msbuild/msbuilditemmetadata.cpp
deleted file mode 100644
index daaa4c6b3..000000000
--- a/src/plugins/generator/visualstudio/msbuild/msbuilditemmetadata.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** 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/plugins/generator/visualstudio/msbuild/msbuilditemmetadata.h b/src/plugins/generator/visualstudio/msbuild/msbuilditemmetadata.h
deleted file mode 100644
index 09da2649c..000000000
--- a/src/plugins/generator/visualstudio/msbuild/msbuilditemmetadata.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
-**
-** 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 = nullptr);
- MSBuildItemMetadata(const QString &name, const QVariant &value = QVariant(),
- MSBuildItem *parent = nullptr);
-
- void accept(IMSBuildNodeVisitor *visitor) const override;
-};
-
-} // namespace qbs
-
-#endif // MSBUILDITEMMETADATA_H
diff --git a/src/plugins/generator/visualstudio/msbuild/msbuildproperty.cpp b/src/plugins/generator/visualstudio/msbuild/msbuildproperty.cpp
deleted file mode 100644
index 410e2a694..000000000
--- a/src/plugins/generator/visualstudio/msbuild/msbuildproperty.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** 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/plugins/generator/visualstudio/msbuild/msbuildproperty.h b/src/plugins/generator/visualstudio/msbuild/msbuildproperty.h
deleted file mode 100644
index de2c52394..000000000
--- a/src/plugins/generator/visualstudio/msbuild/msbuildproperty.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/****************************************************************************
-**
-** 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 = nullptr);
- MSBuildProperty(const QString &name, const QVariant &value = QVariant(),
- MSBuildPropertyGroup *parent = nullptr);
-
- void accept(IMSBuildNodeVisitor *visitor) const override;
-};
-
-} // namespace qbs
-
-#endif // MSBUILDPROPERTY_H
diff --git a/src/plugins/generator/visualstudio/msbuildfiltersproject.cpp b/src/plugins/generator/visualstudio/msbuildfiltersproject.cpp
index 1cd2573c3..127d6a438 100644
--- a/src/plugins/generator/visualstudio/msbuildfiltersproject.cpp
+++ b/src/plugins/generator/visualstudio/msbuildfiltersproject.cpp
@@ -45,110 +45,203 @@
namespace qbs {
-static QStringList sourceFileExtensions()
+namespace {
+
+const QStringList & sourceFileExtensions()
{
- return {QStringLiteral("c"), QStringLiteral("C"), QStringLiteral("cpp"),
- QStringLiteral("cxx"), QStringLiteral("c++"), QStringLiteral("cc"),
- QStringLiteral("cs"), QStringLiteral("def"), QStringLiteral("java"),
- QStringLiteral("m"), QStringLiteral("mm")};
+ static const QStringList EXTENSIONS{
+ QStringLiteral("c"),
+ QStringLiteral("C"),
+ QStringLiteral("cpp"),
+ QStringLiteral("cxx"),
+ QStringLiteral("c++"),
+ QStringLiteral("cc"),
+ QStringLiteral("cs"),
+ QStringLiteral("def"),
+ QStringLiteral("java"),
+ QStringLiteral("m"),
+ QStringLiteral("mm")};
+
+ return EXTENSIONS;
}
-static QStringList headerFileExtensions()
+const QStringList & headerFileExtensions()
{
- return {QStringLiteral("h"), QStringLiteral("H"), QStringLiteral("hpp"),
- QStringLiteral("hxx"), QStringLiteral("h++")};
+ static const QStringList EXTENSIONS{
+ QStringLiteral("h"),
+ QStringLiteral("H"),
+ QStringLiteral("hpp"),
+ QStringLiteral("hxx"),
+ QStringLiteral("h++")};
+
+ return EXTENSIONS;
}
-static std::vector<MSBuildFilter *> defaultItemGroupFilters(IMSBuildItemGroup *parent = nullptr)
+struct FilterInfo
+{
+ QString name;
+ QList<QString> extensions;
+ bool parseFiles{ true };
+ bool sourceControlFiles{ true };
+};
+
+const std::vector<FilterInfo> & getDefaultFilterInfo()
{
- const auto sourceFilter = new MSBuildFilter(QStringLiteral("Source Files"), sourceFileExtensions(), parent);
- const auto headerFilter = 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 std::vector<MSBuildFilter *> {
- sourceFilter, headerFilter, formFilter, resourceFilter, generatedFilter, translationFilter
+ static const std::vector<FilterInfo> INFOS {
+ {QStringLiteral("Source Files"), sourceFileExtensions()},
+ {QStringLiteral("Header Files"), headerFileExtensions()},
+ {QStringLiteral("Form Files"), QStringList() << QStringLiteral("ui")},
+ {QStringLiteral("Resource Files"), QStringList() << QStringLiteral("qrc") << QStringLiteral("rc") << QStringLiteral("*"), false},
+ {QStringLiteral("Generated Files"), QStringList() << QStringLiteral("moc"), true, false},
+ {QStringLiteral("Translation Files"), QStringList() << QStringLiteral("ts"), false},
};
+
+ return INFOS;
}
-static bool matchesFilter(const MSBuildFilter *filter, const QString &filePath)
+MSBuildFilter * makeBuildFilter(const FilterInfo &filterInfo,
+ MSBuildItemGroup *itemFiltersGroup)
{
- return filter->extensions().contains(QFileInfo(filePath).completeSuffix());
+ const auto filter = new MSBuildFilter(filterInfo.name, filterInfo.extensions, itemFiltersGroup);
+ filter->appendProperty(QStringLiteral("ParseFiles"), filterInfo.parseFiles);
+ filter->appendProperty(QStringLiteral("SourceControlFiles"), filterInfo.sourceControlFiles);
+ return filter;
}
-MSBuildFiltersProject::MSBuildFiltersProject(const GeneratableProductData &product,
- QObject *parent)
- : MSBuildProject(parent)
+bool matchesFilter(const FilterInfo &filterInfo,
+ const QString &filePath)
{
- // Normally this would be versionInfo.toolsVersion() but for some reason it seems
- // filters projects are always v4.0
- setToolsVersion(QStringLiteral("4.0"));
+ return filterInfo.extensions.contains(QFileInfo(filePath).completeSuffix());
+}
+
+bool isHeaderFile(const QString &filePath)
+{
+ return headerFileExtensions().contains(QFileInfo(filePath).completeSuffix());
+}
+
+bool isSourceFile(const QString &filePath)
+{
+ return sourceFileExtensions().contains(QFileInfo(filePath).completeSuffix());
+}
+
+MSBuildFileItem * makeFileItem(const QString& filePath,
+ MSBuildItemGroup *itemGroup)
+{
+ if (isHeaderFile(filePath))
+ return new MSBuildClInclude(itemGroup);
+
+ if (isSourceFile(filePath))
+ return new MSBuildClCompile(itemGroup);
+
+ return new MSBuildNone(itemGroup);
+}
+
- const auto itemGroup = new MSBuildItemGroup(this);
- const auto filterOptions = defaultItemGroupFilters();
- for (const auto options : filterOptions) {
- const auto filter = new MSBuildFilter(options->include(), options->extensions(), itemGroup);
- filter->appendProperty(QStringLiteral("ParseFiles"), options->parseFiles());
- filter->appendProperty(QStringLiteral("SourceControlFiles"), options->sourceControlFiles());
+class ProductProcessor
+{
+public:
+ using StringSet = Internal::Set<QString>;
+
+ ProductProcessor(MSBuildProject *parent)
+ : m_parent(parent)
+ , m_itemFiltersGroup(new MSBuildItemGroup(m_parent))
+ {
}
- Internal::Set<QString> allFiles;
- const auto productDatas = product.data.values();
- for (const auto &productData : productDatas) {
- const auto groups = productData.groups();
- for (const auto &groupData : groups)
- if (groupData.isEnabled())
- allFiles.unite(Internal::Set<QString>::fromList(groupData.allFilePaths()));
+ void operator()(const QList<ProductData> &productDatas)
+ {
+ for (const auto &productData : productDatas) {
+ const auto &productName = productData.name();
+
+ for (const auto &groupData : productData.groups()) {
+ if (groupData.name() == productName) {
+ processProductFiles(Internal::rangeTo<StringSet>(groupData.allFilePaths()));
+ } else {
+ processGroup(groupData);
+ }
+ }
+ }
}
- MSBuildItemGroup *headerFilesGroup = nullptr;
- MSBuildItemGroup *sourceFilesGroup = nullptr;
- MSBuildItemGroup *filesGroup = nullptr;
-
- for (const auto &filePath : allFiles) {
- 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);
+ void processProductFiles(const StringSet &files)
+ {
+ for (const auto &filePath : files) {
+ MSBuildFileItem *fileItem = nullptr;
+
+ for (const auto &filterInfo : getDefaultFilterInfo()) {
+ if (matchesFilter(filterInfo, filePath)) {
+ makeFilter(filterInfo);
+
+ if (filterInfo.name == QStringLiteral("Header Files")) {
+ if (!m_headerFilesGroup)
+ m_headerFilesGroup = new MSBuildItemGroup(m_parent);
+ fileItem = new MSBuildClInclude(m_headerFilesGroup);
+ } else if (filterInfo.name == QStringLiteral("Source Files")) {
+ if (!m_sourceFilesGroup)
+ m_sourceFilesGroup = new MSBuildItemGroup(m_parent);
+ fileItem = new MSBuildClCompile(m_sourceFilesGroup);
+ }
+
+ if (fileItem) {
+ fileItem->setFilterName(filterInfo.name);
+ break;
+ }
}
+ }
- if (fileItem) {
- fileItem->setFilterName(options->include());
- break;
+ if (!fileItem) {
+ if (!m_filesGroup) {
+ m_filesGroup = new MSBuildItemGroup(m_parent);
}
+
+ fileItem = new MSBuildNone(m_filesGroup);
}
+
+ fileItem->setFilePath(filePath);
}
+ }
- if (!fileItem) {
- if (!filesGroup)
- filesGroup = new MSBuildItemGroup(this);
- fileItem = new MSBuildNone(filesGroup);
+ void processGroup(const GroupData &groupData)
+ {
+ makeFilter({groupData.name(), QStringList() << QStringLiteral("*")});
+
+ auto *itemGroup = new MSBuildItemGroup(m_parent);
+ const auto &files = groupData.allFilePaths();
+ for (const auto &filePath : files) {
+ auto *fileItem = makeFileItem(filePath, itemGroup);
+ fileItem->setFilePath(filePath);
+ fileItem->setFilterName(groupData.name());
}
- fileItem->setFilePath(filePath);
}
- qDeleteAll(filterOptions);
+ void makeFilter(const FilterInfo &filterInfo)
+ {
+ if (!m_createdFilters.contains(filterInfo.name)) {
+ makeBuildFilter(filterInfo, m_itemFiltersGroup);
+ m_createdFilters.insert(filterInfo.name);
+ }
+ }
+
+private:
+ MSBuildProject *m_parent = nullptr;
+ MSBuildItemGroup *m_itemFiltersGroup = nullptr;
+ MSBuildItemGroup *m_headerFilesGroup = nullptr;
+ MSBuildItemGroup *m_sourceFilesGroup = nullptr;
+ MSBuildItemGroup *m_filesGroup = nullptr;
+ QSet<QString> m_createdFilters;
+};
+
+} // namespace
+
+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"));
+
+ ProductProcessor(this)(product.data.values());
}
} // namespace qbs
diff --git a/src/plugins/generator/visualstudio/msbuildqbsproductproject.cpp b/src/plugins/generator/visualstudio/msbuildqbsproductproject.cpp
index 2d679ceb1..6f77212d4 100644
--- a/src/plugins/generator/visualstudio/msbuildqbsproductproject.cpp
+++ b/src/plugins/generator/visualstudio/msbuildqbsproductproject.cpp
@@ -126,7 +126,7 @@ void MSBuildQbsProductProject::addConfiguration(const GeneratableProject &projec
relativeTargetDir = QStringLiteral("$(SolutionDir)$(Configuration)")
+ relativeTargetDir.mid(configurationDir.size());
- const auto properties = productData.moduleProperties();
+ const auto &properties = productData.moduleProperties();
const bool debugBuild = properties.getModuleProperty(StringConstants::qbsModule(),
QStringLiteral("debugInformation"))
@@ -248,7 +248,7 @@ static QString subsystemVersion(const QString &version)
void MSBuildQbsProductProject::addItemDefGroup(const Project &project,
const ProductData &productData)
{
- const auto properties = productData.moduleProperties();
+ const auto &properties = productData.moduleProperties();
const bool consoleApp = productData.properties().value(QStringLiteral("consoleApplication"))
.toBool();
diff --git a/src/plugins/generator/visualstudio/msbuildtargetproject.cpp b/src/plugins/generator/visualstudio/msbuildtargetproject.cpp
index bcd654061..33ce9d9ae 100644
--- a/src/plugins/generator/visualstudio/msbuildtargetproject.cpp
+++ b/src/plugins/generator/visualstudio/msbuildtargetproject.cpp
@@ -92,7 +92,7 @@ const Internal::VisualStudioVersionInfo &MSBuildTargetProject::versionInfo() con
QUuid MSBuildTargetProject::guid() const
{
- return {d->projectGuidProperty->value().toString()};
+ return QUuid{d->projectGuidProperty->value().toString()};
}
void MSBuildTargetProject::setGuid(const QUuid &guid)
diff --git a/src/plugins/generator/visualstudio/msbuildutils.h b/src/plugins/generator/visualstudio/msbuildutils.h
index 3eceeba2e..cf4c7eb0d 100644
--- a/src/plugins/generator/visualstudio/msbuildutils.h
+++ b/src/plugins/generator/visualstudio/msbuildutils.h
@@ -92,14 +92,12 @@ public:
static QString fullDisplayName(const qbs::Project &project)
{
- return QStringLiteral("%1|%2")
- .arg(configurationName(project))
- .arg(displayPlatform(project));
+ return QStringLiteral("%1|%2").arg(configurationName(project), displayPlatform(project));
}
static QString fullName(const qbs::Project &project)
{
- return QStringLiteral("%1|%2").arg(configurationName(project)).arg(platform(project));
+ return QStringLiteral("%1|%2").arg(configurationName(project), platform(project));
}
static QString buildTaskCondition(const Project &buildTask)
diff --git a/src/plugins/generator/visualstudio/solution/visualstudiosolution.cpp b/src/plugins/generator/visualstudio/solution/visualstudiosolution.cpp
deleted file mode 100644
index 89b266647..000000000
--- a/src/plugins/generator/visualstudio/solution/visualstudiosolution.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/****************************************************************************
-**
-** 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 <tools/visualstudioversioninfo.h>
-
-#include <QtCore/qmap.h>
-
-namespace qbs {
-
-class VisualStudioSolutionPrivate
-{
-public:
- VisualStudioSolutionPrivate(const Internal::VisualStudioVersionInfo &versionInfo)
- : versionInfo(versionInfo) { }
- const Internal::VisualStudioVersionInfo versionInfo;
- QList<IVisualStudioSolutionProject *> projects;
- QMap<VisualStudioSolutionFileProject *, QList<VisualStudioSolutionFileProject *>> dependencies;
- QList<VisualStudioSolutionGlobalSection *> globalSections;
-};
-
-VisualStudioSolution::VisualStudioSolution(const Internal::VisualStudioVersionInfo &versionInfo,
- QObject *parent)
- : QObject(parent)
- , d(new VisualStudioSolutionPrivate(versionInfo))
-{
-}
-
-VisualStudioSolution::~VisualStudioSolution() = default;
-
-Internal::VisualStudioVersionInfo VisualStudioSolution::versionInfo() const
-{
- return d->versionInfo;
-}
-
-QList<IVisualStudioSolutionProject *> VisualStudioSolution::projects() const
-{
- return d->projects;
-}
-
-QList<VisualStudioSolutionFileProject *> VisualStudioSolution::fileProjects() const
-{
- QList<VisualStudioSolutionFileProject *> list;
- for (const auto &project : qAsConst(d->projects))
- if (auto fileProject = qobject_cast<VisualStudioSolutionFileProject *>(project))
- list.push_back(fileProject);
- return list;
-}
-
-QList<VisualStudioSolutionFolderProject *> VisualStudioSolution::folderProjects() const
-{
- QList<VisualStudioSolutionFolderProject *> list;
- for (const auto &project : qAsConst(d->projects))
- if (auto folderProject = qobject_cast<VisualStudioSolutionFolderProject *>(project))
- list.push_back(folderProject);
- return list;
-}
-
-void VisualStudioSolution::appendProject(IVisualStudioSolutionProject *project)
-{
- d->projects.push_back(project);
-}
-
-QList<VisualStudioSolutionFileProject *> VisualStudioSolution::dependencies(
- VisualStudioSolutionFileProject *project) const
-{
- return d->dependencies.value(project);
-}
-
-void VisualStudioSolution::addDependency(VisualStudioSolutionFileProject *project,
- VisualStudioSolutionFileProject *dependency)
-{
- d->dependencies[project].push_back(dependency);
-}
-
-QList<VisualStudioSolutionGlobalSection *> VisualStudioSolution::globalSections() const
-{
- return d->globalSections;
-}
-
-void VisualStudioSolution::appendGlobalSection(VisualStudioSolutionGlobalSection *globalSection)
-{
- d->globalSections.push_back(globalSection);
-}
-
-} // namespace qbs
diff --git a/src/plugins/generator/visualstudio/solution/visualstudiosolutionfileproject.cpp b/src/plugins/generator/visualstudio/solution/visualstudiosolutionfileproject.cpp
deleted file mode 100644
index ab5db088d..000000000
--- a/src/plugins/generator/visualstudio/solution/visualstudiosolutionfileproject.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/****************************************************************************
-**
-** 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 <QtCore/qfileinfo.h>
-
-namespace qbs {
-
-class VisualStudioSolutionFileProjectPrivate
-{
-public:
- QString filePath;
-};
-
-VisualStudioSolutionFileProject::VisualStudioSolutionFileProject(const QString &filePath,
- QObject *parent)
- : IVisualStudioSolutionProject(parent)
- , d(new VisualStudioSolutionFileProjectPrivate)
-{
- setFilePath(filePath);
-}
-
-VisualStudioSolutionFileProject::~VisualStudioSolutionFileProject() = default;
-
-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/plugins/generator/visualstudio/solution/visualstudiosolutionfolderproject.cpp b/src/plugins/generator/visualstudio/solution/visualstudiosolutionfolderproject.cpp
deleted file mode 100644
index d59d1e1ed..000000000
--- a/src/plugins/generator/visualstudio/solution/visualstudiosolutionfolderproject.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/****************************************************************************
-**
-** 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 <QtCore/qfileinfo.h>
-
-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/plugins/generator/visualstudio/visualstudio.pri b/src/plugins/generator/visualstudio/visualstudio.pri
deleted file mode 100644
index 13a48e85d..000000000
--- a/src/plugins/generator/visualstudio/visualstudio.pri
+++ /dev/null
@@ -1 +0,0 @@
-qbsPluginTarget = visualstudiogenerator
diff --git a/src/plugins/generator/visualstudio/visualstudio.pro b/src/plugins/generator/visualstudio/visualstudio.pro
deleted file mode 100644
index 7d2398c8d..000000000
--- a/src/plugins/generator/visualstudio/visualstudio.pro
+++ /dev/null
@@ -1,94 +0,0 @@
-include(visualstudio.pri)
-include(../../plugins.pri)
-include(../../../shared/json/json.pri)
-
-QT = core
-
-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/visualstudiogeneratorplugin.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/plugins/generator/visualstudio/visualstudio.qbs b/src/plugins/generator/visualstudio/visualstudio.qbs
index 41a620252..718c8523e 100644
--- a/src/plugins/generator/visualstudio/visualstudio.qbs
+++ b/src/plugins/generator/visualstudio/visualstudio.qbs
@@ -1,8 +1,8 @@
-import qbs
import "../../qbsplugin.qbs" as QbsPlugin
QbsPlugin {
Depends { name: "qbsjson" }
+ Depends { name: "qbsmsbuild" }
name: "visualstudiogenerator"
@@ -30,79 +30,4 @@ QbsPlugin {
"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/plugins/generator/visualstudio/visualstudiogenerator.cpp b/src/plugins/generator/visualstudio/visualstudiogenerator.cpp
index 2ce54e73e..d6add9c6f 100644
--- a/src/plugins/generator/visualstudio/visualstudiogenerator.cpp
+++ b/src/plugins/generator/visualstudio/visualstudiogenerator.cpp
@@ -153,7 +153,7 @@ VisualStudioGenerator::VisualStudioGenerator(const VisualStudioVersionInfo &vers
{
if (d->versionInfo.usesVcBuild())
throw ErrorInfo(Tr::tr("VCBuild (Visual Studio 2008 and below) is not supported"));
- else if (!d->versionInfo.usesMsBuild())
+ if (!d->versionInfo.usesMsBuild())
throw ErrorInfo(Tr::tr("Unknown/unsupported build engine"));
Q_ASSERT(d->versionInfo.usesSolutions());
}
@@ -189,7 +189,7 @@ void VisualStudioGenerator::addPropertySheets(const GeneratableProject &project)
void VisualStudioGenerator::addPropertySheets(
const std::shared_ptr<MSBuildTargetProject> &targetProject)
{
- for (const auto &pair : qAsConst(d->propertySheetNames)) {
+ for (const auto &pair : std::as_const(d->propertySheetNames)) {
targetProject->appendPropertySheet(
QStringLiteral("$(SolutionDir)\\") + pair.first, pair.second);
}
@@ -220,18 +220,16 @@ static void addDefaultGlobalSections(const GeneratableProject &topLevelProject,
QStringLiteral("ProjectConfigurationPlatforms"), solution);
solution->appendGlobalSection(projectConfigurationPlatformsSection);
projectConfigurationPlatformsSection->setPost(true);
- const auto projects = solution->projects();
+ const auto projects = solution->fileProjects();
for (const auto project : projects) {
for (const auto &qbsProject : topLevelProject.projects) {
projectConfigurationPlatformsSection->appendProperty(
- QStringLiteral("%1.%2.ActiveCfg")
- .arg(project->guid().toString())
- .arg(MSBuildUtils::fullDisplayName(qbsProject)),
+ QStringLiteral("%1.%2.ActiveCfg").arg(project->guid().toString(),
+ MSBuildUtils::fullDisplayName(qbsProject)),
MSBuildUtils::fullName(qbsProject));
projectConfigurationPlatformsSection->appendProperty(
- QStringLiteral("%1.%2.Build.0")
- .arg(project->guid().toString())
- .arg(MSBuildUtils::fullDisplayName(qbsProject)),
+ QStringLiteral("%1.%2.Build.0").arg(project->guid().toString(),
+ MSBuildUtils::fullDisplayName(qbsProject)),
MSBuildUtils::fullName(qbsProject));
}
}
diff --git a/src/plugins/generator/visualstudio/visualstudioguidpool.cpp b/src/plugins/generator/visualstudio/visualstudioguidpool.cpp
index d6d716ff2..d770a448f 100644
--- a/src/plugins/generator/visualstudio/visualstudioguidpool.cpp
+++ b/src/plugins/generator/visualstudio/visualstudioguidpool.cpp
@@ -78,7 +78,7 @@ VisualStudioGuidPool::~VisualStudioGuidPool()
productData.insert(it.first, it.second.toString().toStdString());
const auto data = JsonDocument(productData).toJson();
- file.write(std::vector<char> { data.cbegin(), data.cend() });
+ file.write({data.data(), data.size()});
file.commit();
}
}
diff --git a/src/plugins/plugins.pri b/src/plugins/plugins.pri
deleted file mode 100644
index 43d7dcfac..000000000
--- a/src/plugins/plugins.pri
+++ /dev/null
@@ -1,21 +0,0 @@
-include(qbs_plugin_common.pri)
-
-TARGET = $$qbsPluginTarget
-DESTDIR = $$qbsPluginDestDir
-
-isEmpty(QBSLIBDIR): QBSLIBDIR = $$OUT_PWD/../../../../$${QBS_LIBRARY_DIRNAME}
-isEmpty(QBS_RPATH): QBS_RPATH = ../..
-include($${PWD}/../lib/corelib/use_corelib.pri)
-
-TEMPLATE = lib
-
-CONFIG += c++14
-CONFIG(static, static|shared): CONFIG += create_prl
-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
deleted file mode 100644
index 9fe2e6b5b..000000000
--- a/src/plugins/plugins.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS = generator scanner
diff --git a/src/plugins/plugins.qbs b/src/plugins/plugins.qbs
index ee101f60e..55fafe90a 100644
--- a/src/plugins/plugins.qbs
+++ b/src/plugins/plugins.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
name: "qbs plugins"
references: [
diff --git a/src/plugins/qbs_plugin_common.pri b/src/plugins/qbs_plugin_common.pri
deleted file mode 100644
index 45ade4989..000000000
--- a/src/plugins/qbs_plugin_common.pri
+++ /dev/null
@@ -1,9 +0,0 @@
-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}
-}
-qbsPluginDestDir = $${destdirPrefix}/qbs/plugins
diff --git a/src/plugins/qbsplugin.qbs b/src/plugins/qbsplugin.qbs
index b37fffe81..b4721198f 100644
--- a/src/plugins/qbsplugin.qbs
+++ b/src/plugins/qbsplugin.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.FileInfo
QbsProduct {
diff --git a/src/plugins/scanner/CMakeLists.txt b/src/plugins/scanner/CMakeLists.txt
new file mode 100644
index 000000000..e4cbfb9e7
--- /dev/null
+++ b/src/plugins/scanner/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(cpp)
+add_subdirectory(qt)
diff --git a/src/plugins/scanner/cpp/CMakeLists.txt b/src/plugins/scanner/cpp/CMakeLists.txt
new file mode 100644
index 000000000..a05b70a27
--- /dev/null
+++ b/src/plugins/scanner/cpp/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(SOURCES
+ ../scanner.h
+ CPlusPlusForwardDeclarations.h
+ Lexer.cpp
+ Lexer.h
+ Token.cpp
+ Token.h
+ cpp_global.h
+ cppscanner.cpp
+ )
+
+add_qbs_plugin(qbs_cpp_scanner
+ DEFINES "CPLUSPLUS_NO_PARSER"
+ DEPENDS qbscore
+ SOURCES ${SOURCES}
+ )
diff --git a/src/plugins/scanner/cpp/Lexer.cpp b/src/plugins/scanner/cpp/Lexer.cpp
index 272d32d09..6793080e9 100644
--- a/src/plugins/scanner/cpp/Lexer.cpp
+++ b/src/plugins/scanner/cpp/Lexer.cpp
@@ -64,14 +64,11 @@ namespace CPlusPlus {
Lexer::Lexer(const char *firstChar, const char *lastChar)
: _state(State_Default),
- _flags(0),
_currentLine(1)
{
setSource(firstChar, lastChar);
}
-Lexer::~Lexer() = default;
-
void Lexer::setSource(const char *firstChar, const char *lastChar)
{
_firstChar = firstChar;
@@ -224,7 +221,7 @@ void Lexer::scan_helper(Token *tok)
goto _Lagain;
case '"': case '\'': {
- const char quote = ch;
+ const unsigned char quote = ch;
tok->f.kind = quote == '"'
? T_STRING_LITERAL
@@ -233,7 +230,7 @@ void Lexer::scan_helper(Token *tok)
while (_yychar && _yychar != quote) {
if (_yychar == '\n')
break;
- else if (_yychar != '\\')
+ if (_yychar != '\\')
yyinp();
else {
yyinp(); // skip `\\'
@@ -402,7 +399,7 @@ void Lexer::scan_helper(Token *tok)
bool doxy = false;
if (_yychar == '*' || _yychar == '!') {
- const char ch = _yychar;
+ const unsigned char ch = _yychar;
yyinp();
@@ -567,14 +564,15 @@ void Lexer::scan_helper(Token *tok)
do {
yyinp();
- if (! (isalnum(_yychar) || _yychar == '_' || _yychar == '$'))
+ 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 == '"') {
+ }
+ if (ch == '@' && _yychar == '"') {
// objc @string literals
ch = _yychar;
yyinp();
@@ -608,7 +606,7 @@ void Lexer::scan_helper(Token *tok)
ch = _yychar;
yyinp();
- const char quote = ch;
+ const unsigned char quote = ch;
tok->f.kind = quote == '"'
? T_WIDE_STRING_LITERAL
@@ -667,4 +665,4 @@ void Lexer::scan_helper(Token *tok)
} // switch
}
-}
+} // namespace CPlusPlus
diff --git a/src/plugins/scanner/cpp/Lexer.h b/src/plugins/scanner/cpp/Lexer.h
index 1cf829ebb..42f733dc5 100644
--- a/src/plugins/scanner/cpp/Lexer.h
+++ b/src/plugins/scanner/cpp/Lexer.h
@@ -79,7 +79,6 @@ public:
};
Lexer(const char *firstChar, const char *lastChar);
- ~Lexer();
bool qtMocRunEnabled() const;
void setQtMocRunEnabled(bool onoff);
@@ -151,10 +150,7 @@ private:
const char *_tokenStart = nullptr;
unsigned char _yychar = 0;
int _state = 0;
- union {
- unsigned _flags;
- Flags f;
- };
+ Flags f{};
unsigned _currentLine = 0;
};
diff --git a/src/plugins/scanner/cpp/Token.cpp b/src/plugins/scanner/cpp/Token.cpp
index 603918f63..b488bf0d4 100644
--- a/src/plugins/scanner/cpp/Token.cpp
+++ b/src/plugins/scanner/cpp/Token.cpp
@@ -113,16 +113,9 @@ static const char *token_names[] = {
};
-Token::Token() :
- flags(0), offset(0), ptr(nullptr)
-{
-}
-
-Token::~Token() = default;
-
void Token::reset()
{
- flags = 0;
+ f = {};
offset = 0;
ptr = nullptr;
}
diff --git a/src/plugins/scanner/cpp/Token.h b/src/plugins/scanner/cpp/Token.h
index 846aa5a12..a042c1087 100644
--- a/src/plugins/scanner/cpp/Token.h
+++ b/src/plugins/scanner/cpp/Token.h
@@ -295,9 +295,6 @@ enum Kind {
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
@@ -348,15 +345,12 @@ public:
unsigned pad : 3;
unsigned length : 16;
};
- union {
- unsigned flags;
- Flags f;
- };
+ Flags f{};
unsigned offset = 0;
union {
- void *ptr;
+ void *ptr = nullptr;
#ifndef CPLUSPLUS_NO_PARSER
const Literal *literal;
const NumericLiteral *number;
diff --git a/src/plugins/scanner/cpp/cpp.pri b/src/plugins/scanner/cpp/cpp.pri
deleted file mode 100644
index 515e65635..000000000
--- a/src/plugins/scanner/cpp/cpp.pri
+++ /dev/null
@@ -1 +0,0 @@
-qbsPluginTarget = qbs_cpp_scanner
diff --git a/src/plugins/scanner/cpp/cpp.pro b/src/plugins/scanner/cpp/cpp.pro
deleted file mode 100644
index 12a6d7b6c..000000000
--- a/src/plugins/scanner/cpp/cpp.pro
+++ /dev/null
@@ -1,10 +0,0 @@
-include(cpp.pri)
-include(../../plugins.pri)
-DEFINES += CPLUSPLUS_NO_PARSER
-
-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
index 93a64e7cd..a274e0bba 100644
--- a/src/plugins/scanner/cpp/cpp.qbs
+++ b/src/plugins/scanner/cpp/cpp.qbs
@@ -1,4 +1,3 @@
-import qbs 1.0
import "../../qbsplugin.qbs" as QbsPlugin
QbsPlugin {
diff --git a/src/plugins/scanner/cpp/cppscanner.cpp b/src/plugins/scanner/cpp/cppscanner.cpp
index 1cebc52eb..6a500f013 100644
--- a/src/plugins/scanner/cpp/cppscanner.cpp
+++ b/src/plugins/scanner/cpp/cppscanner.cpp
@@ -65,9 +65,9 @@ using namespace CPlusPlus;
struct ScanResult
{
- char *fileName;
- unsigned int size;
- int flags;
+ char *fileName = nullptr;
+ int size = 0;
+ int flags = 0;
};
struct Opaq
@@ -163,7 +163,7 @@ static void scanCppFile(void *opaq, CPlusPlus::Lexer &yylex, bool scanForFileTag
yylex.setScanAngleStringLiteralTokens(false);
if (!tk.newline() && (tk.is(T_STRING_LITERAL) || tk.is(T_ANGLE_STRING_LITERAL))) {
- scanResult.size = tk.length() - 2;
+ scanResult.size = int(tk.length() - 2);
if (tk.is(T_STRING_LITERAL))
scanResult.flags = SC_LOCAL_INCLUDE_FLAG;
else
@@ -204,7 +204,7 @@ static void scanCppFile(void *opaq, CPlusPlus::Lexer &yylex, bool scanForFileTag
static void *openScanner(const unsigned short *filePath, const char *fileTags, int flags)
{
std::unique_ptr<Opaq> opaque(new Opaq);
- opaque->fileName = QString::fromUtf16(filePath);
+ opaque->fileName = QString::fromUtf16(reinterpret_cast<const char16_t *>(filePath));
const int fileTagsLength = static_cast<int>(std::strlen(fileTags));
const QList<QByteArray> &tagList = QByteArray::fromRawData(fileTags, fileTagsLength).split(',');
if (tagList.contains("hpp"))
@@ -226,15 +226,15 @@ static void *openScanner(const unsigned short *filePath, const char *fileTags, i
return nullptr;
}
- struct stat s;
+ struct stat s{};
int r = fstat(opaque->fd, &s);
if (r != 0)
return nullptr;
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)
+ void *vmap = mmap(nullptr, s.st_size, PROT_READ, MAP_PRIVATE, opaque->fd, 0);
+ if (vmap == MAP_FAILED) // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
return nullptr;
#else
opaque->file.setFileName(opaque->fileName);
@@ -319,7 +319,7 @@ ScannerPlugin includeScanner =
ScannerUsesCppIncludePaths | ScannerRecursiveDependencies
};
-ScannerPlugin *cppScanners[] = { &includeScanner, NULL };
+ScannerPlugin *cppScanners[] = { &includeScanner, nullptr };
static void QbsCppScannerPluginLoad()
{
diff --git a/src/plugins/scanner/qt/CMakeLists.txt b/src/plugins/scanner/qt/CMakeLists.txt
new file mode 100644
index 000000000..9a3649ad4
--- /dev/null
+++ b/src/plugins/scanner/qt/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_qbs_plugin(qbs_qt_scanner
+ DEPENDS qbscore
+ SOURCES ../scanner.h qtscanner.cpp
+ )
diff --git a/src/plugins/scanner/qt/qt.pri b/src/plugins/scanner/qt/qt.pri
deleted file mode 100644
index 71e3a8a45..000000000
--- a/src/plugins/scanner/qt/qt.pri
+++ /dev/null
@@ -1 +0,0 @@
-qbsPluginTarget = qbs_qt_scanner
diff --git a/src/plugins/scanner/qt/qt.pro b/src/plugins/scanner/qt/qt.pro
deleted file mode 100644
index f231ea9e2..000000000
--- a/src/plugins/scanner/qt/qt.pro
+++ /dev/null
@@ -1,8 +0,0 @@
-include(qt.pri)
-include(../../plugins.pri)
-
-QT = core
-
-HEADERS += ../scanner.h
-SOURCES += \
- qtscanner.cpp
diff --git a/src/plugins/scanner/qt/qt.qbs b/src/plugins/scanner/qt/qt.qbs
index a6aea1c33..94144665c 100644
--- a/src/plugins/scanner/qt/qt.qbs
+++ b/src/plugins/scanner/qt/qt.qbs
@@ -1,4 +1,3 @@
-import qbs 1.0
import "../../qbsplugin.qbs" as QbsPlugin
QbsPlugin {
diff --git a/src/plugins/scanner/qt/qtscanner.cpp b/src/plugins/scanner/qt/qtscanner.cpp
index 22fdcb79d..81f0d602b 100644
--- a/src/plugins/scanner/qt/qtscanner.cpp
+++ b/src/plugins/scanner/qt/qtscanner.cpp
@@ -71,11 +71,11 @@ struct OpaqQrc
int fd = 0;
int mapl = 0;
#else
- QFile *file = nullptr;
+ std::unique_ptr<QFile> file;
#endif
char *map = nullptr;
- QXmlStreamReader *xml = nullptr;
+ std::unique_ptr<QXmlStreamReader> xml;
QByteArray current;
OpaqQrc() = default;
@@ -86,10 +86,7 @@ struct OpaqQrc
munmap (map, mapl);
if (fd)
close (fd);
-#else
- delete file;
#endif
- delete xml;
}
};
@@ -100,25 +97,26 @@ static void *openScannerQrc(const unsigned short *filePath, const char *fileTags
std::unique_ptr<OpaqQrc> opaque(new OpaqQrc);
#ifdef Q_OS_UNIX
- QString filePathS = QString::fromUtf16(filePath);
+ QString filePathS = QString::fromUtf16(reinterpret_cast<const char16_t *>(filePath));
opaque->fd = open(qPrintable(filePathS), O_RDONLY);
if (opaque->fd == -1) {
opaque->fd = 0;
return nullptr;
}
- struct stat s;
+ struct stat s{};
int r = fstat(opaque->fd, &s);
if (r != 0)
return nullptr;
const int fileSize = static_cast<int>(s.st_size);
opaque->mapl = fileSize;
- void *map = mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, opaque->fd, 0);
+ void *map = mmap(nullptr, s.st_size, PROT_READ, MAP_PRIVATE, opaque->fd, 0);
if (map == nullptr)
return nullptr;
#else
- opaque->file = new QFile(QString::fromUtf16(filePath));
+ opaque->file = std::make_unique<QFile>(
+ QString::fromUtf16(reinterpret_cast<const char16_t *>(filePath)));
if (!opaque->file->open(QFile::ReadOnly))
return nullptr;
@@ -129,7 +127,8 @@ static void *openScannerQrc(const unsigned short *filePath, const char *fileTags
#endif
opaque->map = reinterpret_cast<char *>(map);
- opaque->xml = new QXmlStreamReader(QByteArray::fromRawData(opaque->map, fileSize));
+ opaque->xml = std::make_unique<QXmlStreamReader>(
+ QByteArray::fromRawData(opaque->map, fileSize));
return static_cast<void *>(opaque.release());
}
@@ -180,7 +179,7 @@ ScannerPlugin qrcScanner =
NoScannerFlags
};
-ScannerPlugin *qtScanners[] = {&qrcScanner, NULL};
+ScannerPlugin *qtScanners[] = {&qrcScanner, nullptr};
static void QbsQtScannerPluginLoad()
{
diff --git a/src/plugins/scanner/scanner.pro b/src/plugins/scanner/scanner.pro
deleted file mode 100644
index 68acae7da..000000000
--- a/src/plugins/scanner/scanner.pro
+++ /dev/null
@@ -1,3 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS = cpp qt
-
diff --git a/src/plugins/use_plugin.pri b/src/plugins/use_plugin.pri
deleted file mode 100644
index 53be9f4db..000000000
--- a/src/plugins/use_plugin.pri
+++ /dev/null
@@ -1,10 +0,0 @@
-include(qbs_plugin_common.pri)
-
-qbsPluginLibName = $$qbsPluginTarget
-win32:CONFIG(debug, debug|release):CONFIG(static, static|shared): \
- qbsPluginLibName = $${qbsPluginLibName}d
-LIBS += -l$$qbsPluginLibName
-
-macos: QMAKE_LFLAGS += -Wl,-u,_qbs_static_plugin_register_$$qbsPluginTarget
-!macos:gcc: QMAKE_LFLAGS += -Wl,--require-defined=qbs_static_plugin_register_$$qbsPluginTarget
-msvc: QMAKE_LFLAGS += /INCLUDE:qbs_static_plugin_register_$$qbsPluginTarget
diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt
new file mode 100644
index 000000000..226dd77ec
--- /dev/null
+++ b/src/shared/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_subdirectory(json)
+add_subdirectory(lsp)
+add_subdirectory(quickjs)
+add_subdirectory(variant)
diff --git a/src/shared/bundledqt/bundledqt.qbs b/src/shared/bundledqt/bundledqt.qbs
index 5995ae4d6..39409b938 100644
--- a/src/shared/bundledqt/bundledqt.qbs
+++ b/src/shared/bundledqt/bundledqt.qbs
@@ -1,12 +1,15 @@
-import qbs
import qbs.File
import qbs.FileInfo
+import qbs.Utilities
Product {
Depends { name: "qbsbuildconfig" }
Depends { name: "Qt"; submodules: ["core", "gui", "network", "printsupport", "widgets", "xml"] }
Depends { name: "Qt.test"; condition: project.withTests === true }
- Depends { name: "Qt.script"; condition: !qbsbuildconfig.useBundledQtScript; required: false }
+ Depends {
+ name: "Qt.core5compat";
+ condition: Utilities.versionCompare(Qt.core.version, "6") >= 0
+ }
Depends {
name: "Qt";
submodules: [ "dbus", "xcb_qpa_lib-private" ];
@@ -61,8 +64,15 @@ Product {
var dir = FileInfo.path(fp);
list.push(dir + "/" + basename + ".so");
list.push(dir + "/" + basename + ".so." + Qt.core.versionMajor);
- list.push(dir + "/" + basename + ".so." + Qt.core.versionMajor + "." + Qt.core.versionMinor);
- list.push(fp);
+ if (Utilities.versionCompare(Qt.core.version, "6") < 0) {
+ list.push(dir + "/" + basename + ".so."
+ + Qt.core.versionMajor + "."
+ + Qt.core.versionMinor);
+ }
+ list.push(dir + "/" + basename + ".so."
+ + Qt.core.versionMajor + "."
+ + Qt.core.versionMinor + "."
+ + Qt.core.versionPatch);
} else if (Qt.core.frameworkBuild) {
var fp = Qt[mod].libFilePathRelease;
@@ -74,7 +84,9 @@ Product {
var suffix = ".framework/";
var frameworkPath = fp.substr(0, fp.lastIndexOf(suffix) + suffix.length - 1);
var versionsPath = frameworkPath + "/Versions";
- var versionPath = versionsPath + "/" + Qt.core.versionMajor;
+ var versionName = Utilities.versionCompare(Qt.core.version, "6") >= 0
+ ? "A" : Qt.core.versionMajor;
+ var versionPath = versionsPath + "/" + versionName;
list.push(frameworkPath + "/Resources");
list.push(versionPath + "/Resources/Info.plist");
list.push(versionPath + "/" + FileInfo.fileName(fp));
@@ -105,6 +117,8 @@ Product {
return libraries;
}
+ fileTags: []
+
qbs.install: true
qbs.installDir: qbsbuildconfig.libInstallDir
qbs.installSourceBase: qbs.targetOS.contains("windows") ? Qt.core.binPath : Qt.core.libPath
diff --git a/src/shared/json/CMakeLists.txt b/src/shared/json/CMakeLists.txt
new file mode 100644
index 000000000..a25124cd9
--- /dev/null
+++ b/src/shared/json/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_qbs_library(qbsjson
+ STATIC
+ SOURCES json.cpp json.h
+ )
diff --git a/src/shared/json/json.cpp b/src/shared/json/json.cpp
index 949e2745f..0ea6e8fd0 100644
--- a/src/shared/json/json.cpp
+++ b/src/shared/json/json.cpp
@@ -37,6 +37,10 @@
**
****************************************************************************/
+#if defined(_MSC_VER) && _MSC_VER > 0
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
#include <algorithm>
#include <atomic>
#include <cmath>
@@ -811,7 +815,7 @@ JsonValue::JsonValue(const JsonObject &o)
JsonValue::~JsonValue()
{
if (t == String && stringData && !stringData->ref.deref())
- free(stringData);
+ delete stringData;
if (d && !d->ref.deref())
delete d;
@@ -837,8 +841,11 @@ JsonValue::JsonValue(const JsonValue &other)
*/
JsonValue &JsonValue::operator=(const JsonValue &other)
{
+ if (this == &other) // Self assignment guard.
+ return *this;
+
if (t == String && stringData && !stringData->ref.deref())
- free(stringData);
+ delete stringData;
t = other.t;
dbl = other.dbl;
@@ -1306,6 +1313,9 @@ JsonArray::JsonArray(const JsonArray &other)
*/
JsonArray &JsonArray::operator=(const JsonArray &other)
{
+ if (this == &other) // Self assignment guard.
+ return *this;
+
if (d != other.d) {
if (d && !d->ref.deref())
delete d;
@@ -2349,6 +2359,9 @@ JsonObject::JsonObject(const JsonObject &other)
*/
JsonObject &JsonObject::operator=(const JsonObject &other)
{
+ if (this == &other) // Self assignment guard.
+ return *this;
+
if (d != other.d) {
if (d && !d->ref.deref())
delete d;
@@ -3301,6 +3314,9 @@ JsonDocument::JsonDocument(const JsonDocument &other)
*/
JsonDocument &JsonDocument::operator=(const JsonDocument &other)
{
+ if (this == &other) // Self assignment guard.
+ return *this;
+
if (d != other.d) {
if (d && !d->ref.deref())
delete d;
@@ -3646,8 +3662,7 @@ bool JsonDocument::operator==(const JsonDocument &other) const
if (d->header->root()->isObject())
return JsonObject(d, static_cast<Internal::Object *>(d->header->root()))
== JsonObject(other.d, static_cast<Internal::Object *>(other.d->header->root()));
- else
- return JsonArray(d, static_cast<Internal::Array *>(d->header->root()))
+ return JsonArray(d, static_cast<Internal::Array *>(d->header->root()))
== JsonArray(other.d, static_cast<Internal::Array *>(other.d->header->root()));
}
@@ -3668,7 +3683,7 @@ bool JsonDocument::operator==(const JsonDocument &other) const
*/
bool JsonDocument::isNull() const
{
- return (d == 0);
+ return (d == nullptr);
}
@@ -3742,7 +3757,7 @@ static void valueToJson(const Base *b, const Value &v, std::string &json, int in
// +2 to format to ensure the expected precision
const int n = std::numeric_limits<double>::digits10 + 2;
char buf[30] = {0};
- sprintf(buf, "%.*g", n, d);
+ std::snprintf(buf, sizeof(buf), "%.*g", n, d);
// Hack:
if (buf[0] == '-' && buf[1] == '0' && buf[2] == '\0')
json += "0";
@@ -4205,7 +4220,7 @@ bool Parser::parseArray()
char token = nextToken();
if (token == EndArray)
break;
- else if (token != ValueSeparator) {
+ if (token != ValueSeparator) {
if (!eatSpace())
lastError = JsonParseError::UnterminatedArray;
else
@@ -4704,7 +4719,7 @@ int Base::reserveSpace(uint32_t dataSize, int posInTable, uint32_t numItems, boo
{
// assert(posInTable >= 0 && posInTable <= (int)length);
if (size + dataSize >= Value::MaxSize) {
- fprintf(stderr, "Json: Document too large to store in data structure %d %d %d\n", (uint32_t)size, dataSize, Value::MaxSize);
+ std::fprintf(stderr, "Json: Document too large to store in data structure %d %d %d\n", (uint32_t)size, dataSize, Value::MaxSize);
return 0;
}
@@ -4886,7 +4901,7 @@ int Value::requiredStorage(JsonValue &v, bool *compressed)
}
return sizeof(double);
case JsonValue::String: {
- std::string s = v.toString().data();
+ std::string s = v.toString();
*compressed = false;
return Internal::qStringSize(s);
}
diff --git a/src/shared/json/json.pri b/src/shared/json/json.pri
deleted file mode 100644
index 1b44027c6..000000000
--- a/src/shared/json/json.pri
+++ /dev/null
@@ -1,3 +0,0 @@
-INCLUDEPATH += $$PWD
-HEADERS += $$PWD/json.h
-SOURCES += $$PWD/json.cpp
diff --git a/src/shared/json/json.qbs b/src/shared/json/json.qbs
index 56d208962..f5b2517a6 100644
--- a/src/shared/json/json.qbs
+++ b/src/shared/json/json.qbs
@@ -1,16 +1,7 @@
-import qbs
-
-StaticLibrary {
+QbsStaticLibrary {
name: "qbsjson"
- Depends { name: "cpp" }
- cpp.cxxLanguageVersion: "c++14"
- cpp.minimumMacosVersion: "10.7"
files: [
"json.cpp",
"json.h",
]
- Export {
- Depends { name: "cpp" }
- cpp.includePaths: [product.sourceDirectory]
- }
}
diff --git a/src/shared/lsp/.clang-tidy b/src/shared/lsp/.clang-tidy
new file mode 100644
index 000000000..f451e9596
--- /dev/null
+++ b/src/shared/lsp/.clang-tidy
@@ -0,0 +1,3 @@
+Checks: '-*,misc-definitions-in-headers'
+CheckOptions:
+ - { key: HeaderFileExtensions, value: "x" }
diff --git a/src/shared/lsp/CMakeLists.txt b/src/shared/lsp/CMakeLists.txt
new file mode 100644
index 000000000..1ed95db25
--- /dev/null
+++ b/src/shared/lsp/CMakeLists.txt
@@ -0,0 +1,57 @@
+add_qbs_library(qtclsp
+ STATIC
+ DEPENDS Qt${QT_VERSION_MAJOR}::Core Qt6Core5Compat
+ SOURCES
+ algorithm.h
+ basemessage.cpp
+ basemessage.h
+ callhierarchy.cpp
+ callhierarchy.h
+ client.cpp
+ client.h
+ clientcapabilities.cpp
+ clientcapabilities.h
+ completion.cpp
+ completion.h
+ diagnostics.cpp
+ diagnostics.h
+ initializemessages.cpp
+ initializemessages.h
+ jsonkeys.h
+ jsonobject.cpp
+ jsonobject.h
+ jsonrpcmessages.cpp
+ jsonrpcmessages.h
+ languagefeatures.cpp
+ languagefeatures.h
+ languageserverprotocol_global.h
+ languageserverprotocoltr.h
+ lsptypes.cpp
+ lsptypes.h
+ lsputils.cpp
+ lsputils.h
+ messages.cpp
+ messages.h
+ progresssupport.cpp
+ progresssupport.h
+ semantictokens.cpp
+ semantictokens.h
+ servercapabilities.cpp
+ servercapabilities.h
+ shutdownmessages.cpp
+ shutdownmessages.h
+ textsynchronization.cpp
+ textsynchronization.h
+ textutils.cpp
+ textutils.h
+ workspace.cpp
+ workspace.h
+ )
+target_include_directories(
+ qtclsp
+ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../lib/corelib
+ PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..
+)
+target_compile_definitions(qtclsp
+ PUBLIC "LANGUAGESERVERPROTOCOL_STATIC_LIBRARY"
+)
diff --git a/src/shared/lsp/algorithm.h b/src/shared/lsp/algorithm.h
new file mode 100644
index 000000000..4c41fb649
--- /dev/null
+++ b/src/shared/lsp/algorithm.h
@@ -0,0 +1,1518 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "predicates.h"
+
+#include <qcompilerdetection.h> // for Q_REQUIRED_RESULT
+
+#include <algorithm>
+#include <map>
+#include <memory>
+#include <set>
+#include <tuple>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <QHash>
+#include <QObject>
+#include <QSet>
+#include <QStringList>
+
+#include <memory>
+#include <optional>
+#include <type_traits>
+
+namespace lsp::Utils {
+
+/////////////////////////
+// anyOf
+/////////////////////////
+template<typename T, typename F>
+bool anyOf(const T &container, F predicate);
+template<typename T, typename R, typename S>
+bool anyOf(const T &container, R (S::*predicate)() const);
+template<typename T, typename R, typename S>
+bool anyOf(const T &container, R S::*member);
+
+/////////////////////////
+// count
+/////////////////////////
+template<typename T, typename F>
+int count(const T &container, F predicate);
+
+/////////////////////////
+// allOf
+/////////////////////////
+template<typename T, typename F>
+bool allOf(const T &container, F predicate);
+
+/////////////////////////
+// erase
+/////////////////////////
+template<typename T, typename F>
+void erase(T &container, F predicate);
+template<typename T, typename F>
+bool eraseOne(T &container, F predicate);
+
+/////////////////////////
+// contains
+/////////////////////////
+template<typename T, typename F>
+bool contains(const T &container, F function);
+template<typename T, typename R, typename S>
+bool contains(const T &container, R (S::*function)() const);
+template<typename C, typename R, typename S>
+bool contains(const C &container, R S::*member);
+
+/////////////////////////
+// findOr
+/////////////////////////
+template<typename C, typename F>
+Q_REQUIRED_RESULT typename C::value_type findOr(const C &container,
+ typename C::value_type other,
+ F function);
+template<typename T, typename R, typename S>
+Q_REQUIRED_RESULT typename T::value_type findOr(const T &container,
+ typename T::value_type other,
+ R (S::*function)() const);
+template<typename T, typename R, typename S>
+Q_REQUIRED_RESULT typename T::value_type findOr(const T &container,
+ typename T::value_type other,
+ R S::*member);
+
+/////////////////////////
+// findOrDefault
+/////////////////////////
+template<typename C, typename F>
+Q_REQUIRED_RESULT typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value,
+ typename C::value_type>
+findOrDefault(const C &container, F function);
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value,
+ typename C::value_type>
+findOrDefault(const C &container, R (S::*function)() const);
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value,
+ typename C::value_type>
+findOrDefault(const C &container, R S::*member);
+
+/////////////////////////
+// indexOf
+/////////////////////////
+template<typename C, typename F>
+Q_REQUIRED_RESULT int indexOf(const C &container, F function);
+
+/////////////////////////
+// maxElementOr
+/////////////////////////
+template<typename T>
+typename T::value_type maxElementOr(const T &container, typename T::value_type other);
+
+/////////////////////////
+// filtered
+/////////////////////////
+template<typename C, typename F>
+Q_REQUIRED_RESULT C filtered(const C &container, F predicate);
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT C filtered(const C &container, R (S::*predicate)() const);
+
+/////////////////////////
+// partition
+/////////////////////////
+// Recommended usage:
+// C hit;
+// C miss;
+// std::tie(hit, miss) = Utils::partition(container, predicate);
+template<typename C, typename F>
+Q_REQUIRED_RESULT std::tuple<C, C> partition(const C &container, F predicate);
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT std::tuple<C, C> partition(const C &container, R (S::*predicate)() const);
+
+/////////////////////////
+// filteredUnique
+/////////////////////////
+template<typename C>
+Q_REQUIRED_RESULT C filteredUnique(const C &container);
+
+/////////////////////////
+// qobject_container_cast
+/////////////////////////
+template<class T, template<typename> class Container, typename Base>
+Container<T> qobject_container_cast(const Container<Base> &container);
+
+/////////////////////////
+// static_container_cast
+/////////////////////////
+template<class T, template<typename> class Container, typename Base>
+Container<T> static_container_cast(const Container<Base> &container);
+
+/////////////////////////
+// sort
+/////////////////////////
+template<typename Container>
+inline void sort(Container &container);
+template<typename Container, typename Predicate>
+inline void sort(Container &container, Predicate p);
+template<typename Container, typename R, typename S>
+inline void sort(Container &container, R S::*member);
+template<typename Container, typename R, typename S>
+inline void sort(Container &container, R (S::*function)() const);
+
+/////////////////////////
+// reverseForeach
+/////////////////////////
+template<typename Container, typename Op>
+inline void reverseForeach(const Container &c, const Op &operation);
+
+/////////////////////////
+// toReferences
+/////////////////////////
+template<template<typename...> class ResultContainer, typename SourceContainer>
+auto toReferences(SourceContainer &sources);
+template<typename SourceContainer>
+auto toReferences(SourceContainer &sources);
+
+/////////////////////////
+// toConstReferences
+/////////////////////////
+template<template<typename...> class ResultContainer, typename SourceContainer>
+auto toConstReferences(const SourceContainer &sources);
+template<typename SourceContainer>
+auto toConstReferences(const SourceContainer &sources);
+
+/////////////////////////
+// take
+/////////////////////////
+template<class C, typename P>
+Q_REQUIRED_RESULT std::optional<typename C::value_type> take(C &container, P predicate);
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT decltype(auto) take(C &container, R S::*member);
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT decltype(auto) take(C &container, R (S::*function)() const);
+
+/////////////////////////
+// setUnionMerge
+/////////////////////////
+// Works like std::set_union but provides a merge function for items that match
+// !(a > b) && !(b > a) which normally means that there is an "equal" match.
+// It uses iterators to support move_iterators.
+template<class InputIt1, class InputIt2, class OutputIt, class Merge, class Compare>
+OutputIt setUnionMerge(InputIt1 first1,
+ InputIt1 last1,
+ InputIt2 first2,
+ InputIt2 last2,
+ OutputIt d_first,
+ Merge merge,
+ Compare comp);
+template<class InputIt1, class InputIt2, class OutputIt, class Merge>
+OutputIt setUnionMerge(
+ InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first, Merge merge);
+template<class OutputContainer, class InputContainer1, class InputContainer2, class Merge, class Compare>
+OutputContainer setUnionMerge(InputContainer1 &&input1,
+ InputContainer2 &&input2,
+ Merge merge,
+ Compare comp);
+template<class OutputContainer, class InputContainer1, class InputContainer2, class Merge>
+OutputContainer setUnionMerge(InputContainer1 &&input1, InputContainer2 &&input2, Merge merge);
+
+/////////////////////////
+// setUnion
+/////////////////////////
+template<typename InputIterator1, typename InputIterator2, typename OutputIterator, typename Compare>
+OutputIterator set_union(InputIterator1 first1,
+ InputIterator1 last1,
+ InputIterator2 first2,
+ InputIterator2 last2,
+ OutputIterator result,
+ Compare comp);
+template<typename InputIterator1, typename InputIterator2, typename OutputIterator>
+OutputIterator set_union(InputIterator1 first1,
+ InputIterator1 last1,
+ InputIterator2 first2,
+ InputIterator2 last2,
+ OutputIterator result);
+
+/////////////////////////
+// transform
+/////////////////////////
+// function without result type deduction:
+template<typename ResultContainer, // complete result container type
+ typename SC, // input container type
+ typename F> // function type
+Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function);
+
+// function with result type deduction:
+template<template<typename> class C, // result container type
+ typename SC, // input container type
+ typename F, // function type
+ typename Value = typename std::decay_t<SC>::value_type,
+ typename Result = std::decay_t<std::result_of_t<F(Value &)>>,
+ typename ResultContainer = C<Result>>
+Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function);
+#ifdef Q_CC_CLANG
+// "Matching of template template-arguments excludes compatible templates"
+// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0522r0.html (P0522R0)
+// in C++17 makes the above match e.g. C=std::vector even though that takes two
+// template parameters. Unfortunately the following one matches too, and there is no additional
+// partial ordering rule, resulting in an ambiguous call for this previously valid code.
+// GCC and MSVC ignore that issue and follow the standard to the letter, but Clang only
+// enables the new behavior when given -frelaxed-template-template-args .
+// To avoid requiring everyone using this header to enable that feature, keep the old implementation
+// for Clang.
+template<template<typename, typename> class C, // result container type
+ typename SC, // input container type
+ typename F, // function type
+ typename Value = typename std::decay_t<SC>::value_type,
+ typename Result = std::decay_t<std::result_of_t<F(Value &)>>,
+ typename ResultContainer = C<Result, std::allocator<Result>>>
+Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function);
+#endif
+
+// member function without result type deduction:
+template<template<typename...> class C, // result container type
+ typename SC, // input container type
+ typename R,
+ typename S>
+Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R (S::*p)() const);
+
+// member function with result type deduction:
+template<typename ResultContainer, // complete result container type
+ typename SC, // input container type
+ typename R,
+ typename S>
+Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R (S::*p)() const);
+
+// member without result type deduction:
+template<typename ResultContainer, // complete result container type
+ typename SC, // input container
+ typename R,
+ typename S>
+Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R S::*p);
+
+// member with result type deduction:
+template<template<typename...> class C, // result container
+ typename SC, // input container
+ typename R,
+ typename S>
+Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R S::*p);
+
+// same container types for input and output, const input
+// function:
+template<template<typename...> class C, // container type
+ typename F, // function type
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT decltype(auto) transform(const C<CArgs...> &container, F function);
+
+// same container types for input and output, const input
+// member function:
+template<template<typename...> class C, // container type
+ typename R,
+ typename S,
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT decltype(auto) transform(const C<CArgs...> &container, R (S::*p)() const);
+
+// same container types for input and output, const input
+// members:
+template<template<typename...> class C, // container
+ typename R,
+ typename S,
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT decltype(auto) transform(const C<CArgs...> &container, R S::*p);
+
+// same container types for input and output, non-const input
+// function:
+template<template<typename...> class C, // container type
+ typename F, // function type
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT decltype(auto) transform(C<CArgs...> &container, F function);
+
+// same container types for input and output, non-const input
+// member function:
+template<template<typename...> class C, // container type
+ typename R,
+ typename S,
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT decltype(auto) transform(C<CArgs...> &container, R (S::*p)() const);
+
+// same container types for input and output, non-const input
+// members:
+template<template<typename...> class C, // container
+ typename R,
+ typename S,
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT decltype(auto) transform(C<CArgs...> &container, R S::*p);
+
+/////////////////////////////////////////////////////////////////////////////
+//////// Implementations //////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+//////////////////
+// anyOf
+/////////////////
+template<typename T, typename F>
+bool anyOf(const T &container, F predicate)
+{
+ return std::any_of(std::begin(container), std::end(container), predicate);
+}
+
+// anyOf taking a member function pointer
+template<typename T, typename R, typename S>
+bool anyOf(const T &container, R (S::*predicate)() const)
+{
+ return std::any_of(std::begin(container), std::end(container), std::mem_fn(predicate));
+}
+
+// anyOf taking a member pointer
+template<typename T, typename R, typename S>
+bool anyOf(const T &container, R S::*member)
+{
+ return std::any_of(std::begin(container), std::end(container), std::mem_fn(member));
+}
+
+
+//////////////////
+// count
+/////////////////
+template<typename T, typename F>
+int count(const T &container, F predicate)
+{
+ return std::count_if(std::begin(container), std::end(container), predicate);
+}
+
+//////////////////
+// allOf
+/////////////////
+template<typename T, typename F>
+bool allOf(const T &container, F predicate)
+{
+ return std::all_of(std::begin(container), std::end(container), predicate);
+}
+
+// allOf taking a member function pointer
+template<typename T, typename R, typename S>
+bool allOf(const T &container, R (S::*predicate)() const)
+{
+ return std::all_of(std::begin(container), std::end(container), std::mem_fn(predicate));
+}
+
+// allOf taking a member pointer
+template<typename T, typename R, typename S>
+bool allOf(const T &container, R S::*member)
+{
+ return std::all_of(std::begin(container), std::end(container), std::mem_fn(member));
+}
+
+//////////////////
+// erase
+/////////////////
+template<typename T, typename F>
+void erase(T &container, F predicate)
+{
+ container.erase(std::remove_if(std::begin(container), std::end(container), predicate),
+ std::end(container));
+}
+template<typename T, typename F>
+bool eraseOne(T &container, F predicate)
+{
+ const auto it = std::find_if(std::begin(container), std::end(container), predicate);
+ if (it == std::end(container))
+ return false;
+ container.erase(it);
+ return true;
+}
+
+//////////////////
+// contains
+/////////////////
+template<typename T, typename F>
+bool contains(const T &container, F function)
+{
+ return anyOf(container, function);
+}
+
+template<typename T, typename R, typename S>
+bool contains(const T &container, R (S::*function)() const)
+{
+ return anyOf(container, function);
+}
+
+template<typename C, typename R, typename S>
+bool contains(const C &container, R S::*member)
+{
+ return anyOf(container, std::mem_fn(member));
+}
+
+//////////////////
+// findOr
+/////////////////
+template<typename C, typename F>
+Q_REQUIRED_RESULT
+typename C::value_type findOr(const C &container, typename C::value_type other, F function)
+{
+ typename C::const_iterator begin = std::begin(container);
+ typename C::const_iterator end = std::end(container);
+
+ typename C::const_iterator it = std::find_if(begin, end, function);
+ return it == end ? other : *it;
+}
+
+template<typename T, typename R, typename S>
+Q_REQUIRED_RESULT
+typename T::value_type findOr(const T &container, typename T::value_type other, R (S::*function)() const)
+{
+ return findOr(container, other, std::mem_fn(function));
+}
+
+template<typename T, typename R, typename S>
+Q_REQUIRED_RESULT
+typename T::value_type findOr(const T &container, typename T::value_type other, R S::*member)
+{
+ return findOr(container, other, std::mem_fn(member));
+}
+
+//////////////////
+// findOrDefault
+//////////////////
+// Default implementation:
+template<typename C, typename F>
+Q_REQUIRED_RESULT
+typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, typename C::value_type>
+findOrDefault(const C &container, F function)
+{
+ return findOr(container, typename C::value_type(), function);
+}
+
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT
+typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, typename C::value_type>
+findOrDefault(const C &container, R (S::*function)() const)
+{
+ return findOr(container, typename C::value_type(), std::mem_fn(function));
+}
+
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT
+typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, typename C::value_type>
+findOrDefault(const C &container, R S::*member)
+{
+ return findOr(container, typename C::value_type(), std::mem_fn(member));
+}
+
+//////////////////
+// index of:
+//////////////////
+
+template<typename C, typename F>
+Q_REQUIRED_RESULT
+int indexOf(const C& container, F function)
+{
+ typename C::const_iterator begin = std::begin(container);
+ typename C::const_iterator end = std::end(container);
+
+ typename C::const_iterator it = std::find_if(begin, end, function);
+ return it == end ? -1 : std::distance(begin, it);
+}
+
+
+//////////////////
+// max element
+//////////////////
+
+template<typename T>
+typename T::value_type maxElementOr(const T &container, typename T::value_type other)
+{
+ typename T::const_iterator begin = std::begin(container);
+ typename T::const_iterator end = std::end(container);
+
+ typename T::const_iterator it = std::max_element(begin, end);
+ if (it == end)
+ return other;
+ return *it;
+}
+
+
+//////////////////
+// transform
+/////////////////
+
+namespace {
+/////////////////
+// helper code for transform to use back_inserter and thus push_back for everything
+// and insert for QSet<>
+//
+
+// SetInsertIterator, straight from the standard for insert_iterator
+// just without the additional parameter to insert
+template<class Container>
+class SetInsertIterator
+{
+protected:
+ Container *container;
+
+public:
+ using iterator_category = std::output_iterator_tag;
+ using container_type = Container;
+ explicit SetInsertIterator(Container &x)
+ : container(&x)
+ {}
+ SetInsertIterator<Container> &operator=(const typename Container::value_type &value)
+ {
+ container->insert(value);
+ return *this;
+ }
+ SetInsertIterator<Container> &operator=(typename Container::value_type &&value)
+ {
+ container->insert(std::move(value));
+ return *this;
+ }
+ SetInsertIterator<Container> &operator*() { return *this; }
+ SetInsertIterator<Container> &operator++() { return *this; }
+ SetInsertIterator<Container> operator++(int) { return *this; }
+};
+
+// for QMap / QHash, inserting a std::pair / QPair
+template<class Container>
+class MapInsertIterator
+{
+protected:
+ Container *container;
+
+public:
+ using iterator_category = std::output_iterator_tag;
+ using container_type = Container;
+ explicit MapInsertIterator(Container &x)
+ : container(&x)
+ {}
+ MapInsertIterator<Container> &operator=(
+ const std::pair<const typename Container::key_type, typename Container::mapped_type> &value)
+ { container->insert(value.first, value.second); return *this; }
+ MapInsertIterator<Container> &operator=(
+ const QPair<typename Container::key_type, typename Container::mapped_type> &value)
+ { container->insert(value.first, value.second); return *this; }
+ MapInsertIterator<Container> &operator*() { return *this; }
+ MapInsertIterator<Container> &operator++() { return *this; }
+ MapInsertIterator<Container> operator++(int) { return *this; }
+};
+
+// because Qt container are not implementing the standard interface we need
+// this helper functions for generic code
+template<typename Type>
+void append(QList<Type> *container, QList<Type> &&input)
+{
+ container->append(std::move(input));
+}
+
+template<typename Type>
+void append(QList<Type> *container, const QList<Type> &input)
+{
+ container->append(input);
+}
+
+template<typename Container>
+void append(Container *container, Container &&input)
+{
+ container->insert(container->end(),
+ std::make_move_iterator(input.begin()),
+ std::make_move_iterator(input.end()));
+}
+
+template<typename Container>
+void append(Container *container, const Container &input)
+{
+ container->insert(container->end(), input.begin(), input.end());
+}
+
+// BackInsertIterator behaves like std::back_insert_iterator except is adds the back insertion for
+// container of the same type
+template<typename Container>
+class BackInsertIterator
+{
+public:
+ using iterator_category = std::output_iterator_tag;
+ using value_type = void;
+ using difference_type = ptrdiff_t;
+ using pointer = void;
+ using reference = void;
+ using container_type = Container;
+
+ explicit constexpr BackInsertIterator(Container &container)
+ : m_container(std::addressof(container))
+ {}
+
+ constexpr BackInsertIterator &operator=(const typename Container::value_type &value)
+ {
+ m_container->push_back(value);
+ return *this;
+ }
+
+ constexpr BackInsertIterator &operator=(typename Container::value_type &&value)
+ {
+ m_container->push_back(std::move(value));
+ return *this;
+ }
+
+ constexpr BackInsertIterator &operator=(const Container &container)
+ {
+ append(m_container, container);
+ return *this;
+ }
+
+ constexpr BackInsertIterator &operator=(Container &&container)
+ {
+ append(m_container, container);
+ return *this;
+ }
+
+ [[nodiscard]] constexpr BackInsertIterator &operator*() { return *this; }
+
+ constexpr BackInsertIterator &operator++() { return *this; }
+
+ constexpr BackInsertIterator operator++(int) { return *this; }
+
+private:
+ Container *m_container;
+};
+
+// inserter helper function, returns a BackInsertIterator for most containers
+// and is overloaded for QSet<> and other containers without push_back, returning custom inserters
+template<typename Container>
+inline BackInsertIterator<Container> inserter(Container &container)
+{
+ return BackInsertIterator(container);
+}
+
+template<typename X>
+inline SetInsertIterator<QSet<X>>
+inserter(QSet<X> &container)
+{
+ return SetInsertIterator<QSet<X>>(container);
+}
+
+template<typename K, typename C, typename A>
+inline SetInsertIterator<std::set<K, C, A>>
+inserter(std::set<K, C, A> &container)
+{
+ return SetInsertIterator<std::set<K, C, A>>(container);
+}
+
+template<typename K, typename H, typename C, typename A>
+inline SetInsertIterator<std::unordered_set<K, H, C, A>>
+inserter(std::unordered_set<K, H, C, A> &container)
+{
+ return SetInsertIterator<std::unordered_set<K, H, C, A>>(container);
+}
+
+template<typename K, typename V, typename C, typename A>
+inline SetInsertIterator<std::map<K, V, C, A>>
+inserter(std::map<K, V, C, A> &container)
+{
+ return SetInsertIterator<std::map<K, V, C, A>>(container);
+}
+
+template<typename K, typename V, typename H, typename C, typename A>
+inline SetInsertIterator<std::unordered_map<K, V, H, C, A>>
+inserter(std::unordered_map<K, V, H, C, A> &container)
+{
+ return SetInsertIterator<std::unordered_map<K, V, H, C, A>>(container);
+}
+
+template<typename K, typename V>
+inline MapInsertIterator<QMap<K, V>>
+inserter(QMap<K, V> &container)
+{
+ return MapInsertIterator<QMap<K, V>>(container);
+}
+
+template<typename K, typename V>
+inline MapInsertIterator<QHash<K, V>>
+inserter(QHash<K, V> &container)
+{
+ return MapInsertIterator<QHash<K, V>>(container);
+}
+
+// Helper code for container.reserve that makes it possible to effectively disable it for
+// specific cases
+
+// default: do reserve
+// Template arguments are more specific than the second version below, so this is tried first
+template<template<typename...> class C, typename... CArgs,
+ typename = decltype(&C<CArgs...>::reserve)>
+void reserve(C<CArgs...> &c, typename C<CArgs...>::size_type s)
+{
+ c.reserve(s);
+}
+
+// containers that don't have reserve()
+template<typename C>
+void reserve(C &, typename C::size_type) { }
+
+} // anonymous
+
+// --------------------------------------------------------------------
+// Different containers for input and output:
+// --------------------------------------------------------------------
+
+// different container types for input and output, e.g. transforming a QList into a QSet
+
+// function without result type deduction:
+template<typename ResultContainer, // complete result container type
+ typename SC, // input container type
+ typename F> // function type
+Q_REQUIRED_RESULT
+decltype(auto) transform(SC &&container, F function)
+{
+ ResultContainer result;
+ reserve(result, typename ResultContainer::size_type(container.size()));
+ std::transform(std::begin(container), std::end(container), inserter(result), function);
+ return result;
+}
+
+// function with result type deduction:
+template<template<typename> class C, // result container type
+ typename SC, // input container type
+ typename F, // function type
+ typename Value,
+ typename Result,
+ typename ResultContainer>
+Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function)
+{
+ return transform<ResultContainer>(std::forward<SC>(container), function);
+}
+
+#ifdef Q_CC_CLANG
+template<template<typename, typename> class C, // result container type
+ typename SC, // input container type
+ typename F, // function type
+ typename Value,
+ typename Result,
+ typename ResultContainer>
+Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function)
+{
+ return transform<ResultContainer>(std::forward<SC>(container), function);
+}
+#endif
+
+// member function without result type deduction:
+template<template<typename...> class C, // result container type
+ typename SC, // input container type
+ typename R,
+ typename S>
+Q_REQUIRED_RESULT
+decltype(auto) transform(SC &&container, R (S::*p)() const)
+{
+ return transform<C>(std::forward<SC>(container), std::mem_fn(p));
+}
+
+// member function with result type deduction:
+template<typename ResultContainer, // complete result container type
+ typename SC, // input container type
+ typename R,
+ typename S>
+Q_REQUIRED_RESULT
+decltype(auto) transform(SC &&container, R (S::*p)() const)
+{
+ return transform<ResultContainer>(std::forward<SC>(container), std::mem_fn(p));
+}
+
+// member without result type deduction:
+template<typename ResultContainer, // complete result container type
+ typename SC, // input container
+ typename R,
+ typename S>
+Q_REQUIRED_RESULT
+decltype(auto) transform(SC &&container, R S::*p)
+{
+ return transform<ResultContainer>(std::forward<SC>(container), std::mem_fn(p));
+}
+
+// member with result type deduction:
+template<template<typename...> class C, // result container
+ typename SC, // input container
+ typename R,
+ typename S>
+Q_REQUIRED_RESULT
+decltype(auto) transform(SC &&container, R S::*p)
+{
+ return transform<C>(std::forward<SC>(container), std::mem_fn(p));
+}
+
+// same container types for input and output, const input
+
+// function:
+template<template<typename...> class C, // container type
+ typename F, // function type
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT
+decltype(auto) transform(const C<CArgs...> &container, F function)
+{
+ return transform<C, const C<CArgs...> &>(container, function);
+}
+
+// member function:
+template<template<typename...> class C, // container type
+ typename R,
+ typename S,
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT
+decltype(auto) transform(const C<CArgs...> &container, R (S::*p)() const)
+{
+ return transform<C, const C<CArgs...> &>(container, std::mem_fn(p));
+}
+
+// members:
+template<template<typename...> class C, // container
+ typename R,
+ typename S,
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT
+decltype(auto) transform(const C<CArgs...> &container, R S::*p)
+{
+ return transform<C, const C<CArgs...> &>(container, std::mem_fn(p));
+}
+
+// same container types for input and output, non-const input
+
+// function:
+template<template<typename...> class C, // container type
+ typename F, // function type
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT
+decltype(auto) transform(C<CArgs...> &container, F function)
+{
+ return transform<C, C<CArgs...> &>(container, function);
+}
+
+// member function:
+template<template<typename...> class C, // container type
+ typename R,
+ typename S,
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT
+decltype(auto) transform(C<CArgs...> &container, R (S::*p)() const)
+{
+ return transform<C, C<CArgs...> &>(container, std::mem_fn(p));
+}
+
+// members:
+template<template<typename...> class C, // container
+ typename R,
+ typename S,
+ typename... CArgs> // Arguments to SC
+Q_REQUIRED_RESULT
+decltype(auto) transform(C<CArgs...> &container, R S::*p)
+{
+ return transform<C, C<CArgs...> &>(container, std::mem_fn(p));
+}
+
+// Specialization for QStringList:
+
+template<template<typename...> class C = QList, // result container
+ typename F> // Arguments to C
+Q_REQUIRED_RESULT
+decltype(auto) transform(const QStringList &container, F function)
+{
+ return transform<C, const QList<QString> &>(static_cast<QList<QString>>(container), function);
+}
+
+// member function:
+template<template<typename...> class C = QList, // result container type
+ typename R,
+ typename S>
+Q_REQUIRED_RESULT
+decltype(auto) transform(const QStringList &container, R (S::*p)() const)
+{
+ return transform<C, const QList<QString> &>(static_cast<QList<QString>>(container), std::mem_fn(p));
+}
+
+// members:
+template<template<typename...> class C = QList, // result container
+ typename R,
+ typename S>
+Q_REQUIRED_RESULT
+decltype(auto) transform(const QStringList &container, R S::*p)
+{
+ return transform<C, const QList<QString> &>(static_cast<QList<QString>>(container), std::mem_fn(p));
+}
+
+//////////////////
+// filtered
+/////////////////
+template<typename C, typename F>
+Q_REQUIRED_RESULT
+C filtered(const C &container, F predicate)
+{
+ C out;
+ std::copy_if(std::begin(container), std::end(container),
+ inserter(out), predicate);
+ return out;
+}
+
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT
+C filtered(const C &container, R (S::*predicate)() const)
+{
+ C out;
+ std::copy_if(std::begin(container), std::end(container),
+ inserter(out), std::mem_fn(predicate));
+ return out;
+}
+
+//////////////////
+// filteredCast
+/////////////////
+template<typename R, typename C, typename F>
+Q_REQUIRED_RESULT R filteredCast(const C &container, F predicate)
+{
+ R out;
+ std::copy_if(std::begin(container), std::end(container), inserter(out), predicate);
+ return out;
+}
+
+//////////////////
+// partition
+/////////////////
+
+// Recommended usage:
+// C hit;
+// C miss;
+// std::tie(hit, miss) = Utils::partition(container, predicate);
+
+template<typename C, typename F>
+Q_REQUIRED_RESULT
+std::tuple<C, C> partition(const C &container, F predicate)
+{
+ C hit;
+ C miss;
+ reserve(hit, container.size());
+ reserve(miss, container.size());
+ auto hitIns = inserter(hit);
+ auto missIns = inserter(miss);
+ for (const auto &i : container) {
+ if (predicate(i))
+ hitIns = i;
+ else
+ missIns = i;
+ }
+ return std::make_tuple(hit, miss);
+}
+
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT
+std::tuple<C, C> partition(const C &container, R (S::*predicate)() const)
+{
+ return partition(container, std::mem_fn(predicate));
+}
+
+//////////////////
+// filteredUnique
+/////////////////
+
+template<typename C>
+Q_REQUIRED_RESULT
+C filteredUnique(const C &container)
+{
+ C result;
+ auto ins = inserter(result);
+
+ QSet<typename C::value_type> seen;
+ int setSize = 0;
+
+ auto endIt = std::end(container);
+ for (auto it = std::begin(container); it != endIt; ++it) {
+ seen.insert(*it);
+ if (setSize == seen.size()) // unchanged size => was already seen
+ continue;
+ ++setSize;
+ ins = *it;
+ }
+ return result;
+}
+
+//////////////////
+// qobject_container_cast
+/////////////////
+template <class T, template<typename> class Container, typename Base>
+Container<T> qobject_container_cast(const Container<Base> &container)
+{
+ Container<T> result;
+ auto ins = inserter(result);
+ for (Base val : container) {
+ if (T target = qobject_cast<T>(val))
+ ins = target;
+ }
+ return result;
+}
+
+//////////////////
+// static_container_cast
+/////////////////
+template <class T, template<typename> class Container, typename Base>
+Container<T> static_container_cast(const Container<Base> &container)
+{
+ Container<T> result;
+ reserve(result, container.size());
+ auto ins = inserter(result);
+ for (Base val : container)
+ ins = static_cast<T>(val);
+ return result;
+}
+
+//////////////////
+// sort
+/////////////////
+template <typename Container>
+inline void sort(Container &container)
+{
+ std::stable_sort(std::begin(container), std::end(container));
+}
+
+template <typename Container, typename Predicate>
+inline void sort(Container &container, Predicate p)
+{
+ std::stable_sort(std::begin(container), std::end(container), p);
+}
+
+// const lvalue
+template<typename Container>
+inline Container sorted(const Container &container)
+{
+ Container c = container;
+ sort(c);
+ return c;
+}
+
+// non-const lvalue
+// This is needed because otherwise the "universal" reference below is used, modifying the input
+// container.
+template<typename Container>
+inline Container sorted(Container &container)
+{
+ Container c = container;
+ sort(c);
+ return c;
+}
+
+// non-const rvalue (actually rvalue or lvalue, but lvalue is handled above)
+template<typename Container>
+inline Container sorted(Container &&container)
+{
+ sort(container);
+ return std::move(container);
+}
+
+// const rvalue
+template<typename Container>
+inline Container sorted(const Container &&container)
+{
+ return sorted(container);
+}
+
+// const lvalue
+template<typename Container, typename Predicate>
+inline Container sorted(const Container &container, Predicate p)
+{
+ Container c = container;
+ sort(c, p);
+ return c;
+}
+
+// non-const lvalue
+// This is needed because otherwise the "universal" reference below is used, modifying the input
+// container.
+template<typename Container, typename Predicate>
+inline Container sorted(Container &container, Predicate p)
+{
+ Container c = container;
+ sort(c, p);
+ return c;
+}
+
+// non-const rvalue (actually rvalue or lvalue, but lvalue is handled above)
+template<typename Container, typename Predicate>
+inline Container sorted(Container &&container, Predicate p)
+{
+ sort(container, p);
+ return std::move(container);
+}
+
+// const rvalue
+template<typename Container, typename Predicate>
+inline Container sorted(const Container &&container, Predicate p)
+{
+ return sorted(container, p);
+}
+
+// pointer to member
+template<typename Container, typename R, typename S>
+inline void sort(Container &container, R S::*member)
+{
+ auto f = std::mem_fn(member);
+ using const_ref = typename Container::const_reference;
+ std::stable_sort(std::begin(container), std::end(container),
+ [&f](const_ref a, const_ref b) {
+ return f(a) < f(b);
+ });
+}
+
+// const lvalue
+template<typename Container, typename R, typename S>
+inline Container sorted(const Container &container, R S::*member)
+{
+ Container c = container;
+ sort(c, member);
+ return c;
+}
+
+// non-const lvalue
+// This is needed because otherwise the "universal" reference below is used, modifying the input
+// container.
+template<typename Container, typename R, typename S>
+inline Container sorted(Container &container, R S::*member)
+{
+ Container c = container;
+ sort(c, member);
+ return c;
+}
+
+// non-const rvalue (actually rvalue or lvalue, but lvalue is handled above)
+template<typename Container, typename R, typename S>
+inline Container sorted(Container &&container, R S::*member)
+{
+ sort(container, member);
+ return std::move(container);
+}
+
+// const rvalue
+template<typename Container, typename R, typename S>
+inline Container sorted(const Container &&container, R S::*member)
+{
+ return sorted(container, member);
+}
+
+// pointer to member function
+template<typename Container, typename R, typename S>
+inline void sort(Container &container, R (S::*function)() const)
+{
+ auto f = std::mem_fn(function);
+ using const_ref = typename Container::const_reference;
+ std::stable_sort(std::begin(container), std::end(container),
+ [&f](const_ref a, const_ref b) {
+ return f(a) < f(b);
+ });
+}
+
+// const lvalue
+template<typename Container, typename R, typename S>
+inline Container sorted(const Container &container, R (S::*function)() const)
+{
+ Container c = container;
+ sort(c, function);
+ return c;
+}
+
+// non-const lvalue
+// This is needed because otherwise the "universal" reference below is used, modifying the input
+// container.
+template<typename Container, typename R, typename S>
+inline Container sorted(Container &container, R (S::*function)() const)
+{
+ Container c = container;
+ sort(c, function);
+ return c;
+}
+
+// non-const rvalue (actually rvalue or lvalue, but lvalue is handled above)
+template<typename Container, typename R, typename S>
+inline Container sorted(Container &&container, R (S::*function)() const)
+{
+ sort(container, function);
+ return std::move(container);
+}
+
+// const rvalue
+template<typename Container, typename R, typename S>
+inline Container sorted(const Container &&container, R (S::*function)() const)
+{
+ return sorted(container, function);
+}
+
+//////////////////
+// reverseForeach
+/////////////////
+template <typename Container, typename Op>
+inline void reverseForeach(const Container &c, const Op &operation)
+{
+ auto rend = c.rend();
+ for (auto it = c.rbegin(); it != rend; ++it)
+ operation(*it);
+}
+
+//////////////////
+// toReferences
+/////////////////
+template <template<typename...> class ResultContainer,
+ typename SourceContainer>
+auto toReferences(SourceContainer &sources)
+{
+ return transform<ResultContainer>(sources, [] (auto &value) { return std::ref(value); });
+}
+
+template <typename SourceContainer>
+auto toReferences(SourceContainer &sources)
+{
+ return transform(sources, [] (auto &value) { return std::ref(value); });
+}
+
+//////////////////
+// toConstReferences
+/////////////////
+template <template<typename...> class ResultContainer,
+ typename SourceContainer>
+auto toConstReferences(const SourceContainer &sources)
+{
+ return transform<ResultContainer>(sources, [] (const auto &value) { return std::cref(value); });
+}
+
+template <typename SourceContainer>
+auto toConstReferences(const SourceContainer &sources)
+{
+ return transform(sources, [] (const auto &value) { return std::cref(value); });
+}
+
+//////////////////
+// take:
+/////////////////
+
+template<class C, typename P>
+Q_REQUIRED_RESULT std::optional<typename C::value_type> take(C &container, P predicate)
+{
+ const auto end = std::end(container);
+
+ const auto it = std::find_if(std::begin(container), end, predicate);
+ if (it == end)
+ return std::nullopt;
+
+ std::optional<typename C::value_type> result = std::make_optional(std::move(*it));
+ container.erase(it);
+ return result;
+}
+
+// pointer to member
+template <typename C, typename R, typename S>
+Q_REQUIRED_RESULT decltype(auto) take(C &container, R S::*member)
+{
+ return take(container, std::mem_fn(member));
+}
+
+// pointer to member function
+template <typename C, typename R, typename S>
+Q_REQUIRED_RESULT decltype(auto) take(C &container, R (S::*function)() const)
+{
+ return take(container, std::mem_fn(function));
+}
+
+//////////////////
+// setUnionMerge: Works like std::set_union but provides a merge function for items that match
+// !(a > b) && !(b > a) which normally means that there is an "equal" match.
+// It uses iterators to support move_iterators.
+/////////////////
+
+template<class InputIt1,
+ class InputIt2,
+ class OutputIt,
+ class Merge,
+ class Compare>
+OutputIt setUnionMerge(InputIt1 first1,
+ InputIt1 last1,
+ InputIt2 first2,
+ InputIt2 last2,
+ OutputIt d_first,
+ Merge merge,
+ Compare comp)
+{
+ for (; first1 != last1; ++d_first) {
+ if (first2 == last2)
+ return std::copy(first1, last1, d_first);
+ if (comp(*first2, *first1)) {
+ *d_first = *first2++;
+ } else {
+ if (comp(*first1, *first2)) {
+ *d_first = *first1;
+ } else {
+ *d_first = merge(*first1, *first2);
+ ++first2;
+ }
+ ++first1;
+ }
+ }
+ return std::copy(first2, last2, d_first);
+}
+
+template<class InputIt1,
+ class InputIt2,
+ class OutputIt,
+ class Merge>
+OutputIt setUnionMerge(InputIt1 first1,
+ InputIt1 last1,
+ InputIt2 first2,
+ InputIt2 last2,
+ OutputIt d_first,
+ Merge merge)
+{
+ return setUnionMerge(first1,
+ last1,
+ first2,
+ last2,
+ d_first,
+ merge,
+ std::less<std::decay_t<decltype(*first1)>>{});
+}
+
+template<class OutputContainer,
+ class InputContainer1,
+ class InputContainer2,
+ class Merge,
+ class Compare>
+OutputContainer setUnionMerge(InputContainer1 &&input1,
+ InputContainer2 &&input2,
+ Merge merge,
+ Compare comp)
+{
+ OutputContainer results;
+ results.reserve(input1.size() + input2.size());
+
+ setUnionMerge(std::make_move_iterator(std::begin(input1)),
+ std::make_move_iterator(std::end(input1)),
+ std::make_move_iterator(std::begin(input2)),
+ std::make_move_iterator(std::end(input2)),
+ std::back_inserter(results),
+ merge,
+ comp);
+
+ return results;
+}
+
+template<class OutputContainer,
+ class InputContainer1,
+ class InputContainer2,
+ class Merge>
+OutputContainer setUnionMerge(InputContainer1 &&input1,
+ InputContainer2 &&input2,
+ Merge merge)
+{
+ return setUnionMerge<OutputContainer>(std::forward<InputContainer1>(input1),
+ std::forward<InputContainer2>(input2),
+ merge,
+ std::less<std::decay_t<decltype(*std::begin(input1))>>{});
+}
+
+template<typename Container>
+auto usize(const Container &container)
+{
+ return static_cast<std::make_unsigned_t<decltype(std::size(container))>>(std::size(container));
+}
+
+template<typename Container>
+auto ssize(const Container &container)
+{
+ return static_cast<std::make_signed_t<decltype(std::size(container))>>(std::size(container));
+}
+
+template<typename Compare>
+struct CompareIter
+{
+ Compare compare;
+
+ explicit constexpr CompareIter(Compare compare)
+ : compare(std::move(compare))
+ {}
+
+ template<typename Iterator1, typename Iterator2>
+ constexpr bool operator()(Iterator1 it1, Iterator2 it2)
+ {
+ return bool(compare(*it1, *it2));
+ }
+};
+
+template<typename InputIterator1, typename InputIterator2, typename OutputIterator, typename Compare>
+OutputIterator set_union_impl(InputIterator1 first1,
+ InputIterator1 last1,
+ InputIterator2 first2,
+ InputIterator2 last2,
+ OutputIterator result,
+ Compare comp)
+{
+ auto compare = CompareIter<Compare>(comp);
+
+ while (first1 != last1 && first2 != last2) {
+ if (compare(first1, first2)) {
+ *result = *first1;
+ ++first1;
+ } else if (compare(first2, first1)) {
+ *result = *first2;
+ ++first2;
+ } else {
+ *result = *first1;
+ ++first1;
+ ++first2;
+ }
+ ++result;
+ }
+
+ return std::copy(first2, last2, std::copy(first1, last1, result));
+}
+
+template<typename InputIterator1, typename InputIterator2, typename OutputIterator, typename Compare>
+OutputIterator set_union(InputIterator1 first1,
+ InputIterator1 last1,
+ InputIterator2 first2,
+ InputIterator2 last2,
+ OutputIterator result,
+ Compare comp)
+{
+ return set_union_impl(first1, last1, first2, last2, result, comp);
+}
+
+template<typename InputIterator1, typename InputIterator2, typename OutputIterator>
+OutputIterator set_union(InputIterator1 first1,
+ InputIterator1 last1,
+ InputIterator2 first2,
+ InputIterator2 last2,
+ OutputIterator result)
+{
+ return set_union_impl(
+ first1, last1, first2, last2, result, std::less<typename InputIterator1::value_type>{});
+}
+
+// Replacement for deprecated Qt functionality
+
+template <class T>
+QSet<T> toSet(const QList<T> &list)
+{
+ return QSet<T>(list.begin(), list.end());
+}
+
+template<class T>
+QList<T> toList(const QSet<T> &set)
+{
+ return QList<T>(set.begin(), set.end());
+}
+
+template <class Key, class T>
+void addToHash(QHash<Key, T> *result, const QHash<Key, T> &additionalContents)
+{
+ result->insert(additionalContents);
+}
+
+// Workaround for missing information from QSet::insert()
+// Return type could be a pair like for std::set, but we never use the iterator anyway.
+template<typename T, typename U> [[nodiscard]] bool insert(QSet<T> &s, const U &v)
+{
+ const int oldSize = s.size();
+ s.insert(v);
+ return s.size() > oldSize;
+}
+
+} // namespace lsp::Utils
diff --git a/src/shared/lsp/basemessage.cpp b/src/shared/lsp/basemessage.cpp
new file mode 100644
index 000000000..4b19190dc
--- /dev/null
+++ b/src/shared/lsp/basemessage.cpp
@@ -0,0 +1,188 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "basemessage.h"
+
+#include "jsonrpcmessages.h"
+#include "languageserverprotocoltr.h"
+
+#include <QBuffer>
+#include <QTextCodec>
+
+#include <cstring>
+#include <utility>
+
+namespace lsp {
+
+Q_LOGGING_CATEGORY(parseLog, "qtc.languageserverprotocol.parse", QtWarningMsg)
+
+constexpr char headerFieldSeparator[] = ": ";
+constexpr char contentCharsetName[] = "charset";
+constexpr char defaultCharset[] = "utf-8";
+constexpr char contentLengthFieldName[] = "Content-Length";
+constexpr char headerSeparator[] = "\r\n";
+constexpr char contentTypeFieldName[] = "Content-Type";
+
+BaseMessage::BaseMessage()
+ : mimeType(JsonRpcMessage::jsonRpcMimeType())
+{ }
+
+BaseMessage::BaseMessage(const QByteArray &mimeType, const QByteArray &content,
+ int expectedLength, QTextCodec *codec)
+ : mimeType(mimeType.isEmpty() ? JsonRpcMessage::jsonRpcMimeType() : mimeType)
+ , content(content)
+ , contentLength(expectedLength)
+ , codec(codec)
+{ }
+
+BaseMessage::BaseMessage(const QByteArray &mimeType, const QByteArray &content)
+ : BaseMessage(mimeType, content, content.length(), defaultCodec())
+{ }
+
+bool BaseMessage::operator==(const BaseMessage &other) const
+{
+ if (mimeType != other.mimeType || content != other.content)
+ return false;
+ if (codec) {
+ if (other.codec)
+ return codec->mibEnum() == other.codec->mibEnum();
+ return codec->mibEnum() == defaultCodec()->mibEnum();
+ }
+ if (other.codec)
+ return other.codec->mibEnum() == defaultCodec()->mibEnum();
+
+ return true;
+}
+
+static QPair<QByteArray, QByteArray> splitHeaderFieldLine(const QByteArray &headerFieldLine)
+{
+ static const int fieldSeparatorLength = int(std::strlen(headerFieldSeparator));
+ int assignmentIndex = headerFieldLine.indexOf(headerFieldSeparator);
+ if (assignmentIndex >= 0) {
+ return {headerFieldLine.mid(0, assignmentIndex),
+ headerFieldLine.mid(assignmentIndex + fieldSeparatorLength)};
+ }
+ qCWarning(parseLog) << "Unexpected header line:" << QLatin1String(headerFieldLine);
+ return {};
+}
+
+static void parseContentType(BaseMessage &message, QByteArray contentType, QString &parseError)
+{
+ if (contentType.startsWith('"') && contentType.endsWith('"'))
+ contentType = contentType.mid(1, contentType.length() - 2);
+ QList<QByteArray> contentTypeElements = contentType.split(';');
+ QByteArray mimeTypeName = contentTypeElements.takeFirst();
+ QTextCodec *codec = nullptr;
+ for (const QByteArray &_contentTypeElement : contentTypeElements) {
+ const QByteArray &contentTypeElement = _contentTypeElement.trimmed();
+ if (contentTypeElement.startsWith(contentCharsetName)) {
+ const int equalindex = contentTypeElement.indexOf('=');
+ const QByteArray charset = contentTypeElement.mid(equalindex + 1);
+ if (equalindex > 0)
+ codec = QTextCodec::codecForName(charset);
+ if (!codec) {
+ parseError = Tr::tr("Cannot decode content with \"%1\". Falling back to \"%2\".")
+ .arg(QLatin1String(charset),
+ QLatin1String(defaultCharset));
+ }
+ }
+ }
+ message.mimeType = mimeTypeName;
+ message.codec = codec ? codec : BaseMessage::defaultCodec();
+}
+
+static void parseContentLength(BaseMessage &message, QByteArray contentLength, QString &parseError)
+{
+ bool ok = true;
+ message.contentLength = contentLength.toInt(&ok);
+ if (!ok) {
+ parseError = Tr::tr("Expected an integer in \"%1\", but got \"%2\".")
+ .arg(QString::fromLatin1(contentLengthFieldName), QString::fromLatin1(contentLength));
+ }
+}
+
+void BaseMessage::parse(QBuffer *data, QString &parseError, BaseMessage &message)
+{
+ const qint64 startPos = data->pos();
+
+ if (message.isValid()) { // incomplete message from last parse
+ message.content.append(data->read(message.contentLength - message.content.length()));
+ return;
+ }
+
+ while (!data->atEnd()) {
+ const QByteArray &headerFieldLine = data->readLine();
+ if (headerFieldLine == headerSeparator) {
+ if (message.isValid())
+ message.content = data->read(message.contentLength);
+ return;
+ }
+ const QPair<QByteArray, QByteArray> nameAndValue = splitHeaderFieldLine(headerFieldLine);
+ const QByteArray &headerFieldName = nameAndValue.first.trimmed();
+ const QByteArray &headerFieldValue = nameAndValue.second.trimmed();
+
+ if (headerFieldName.isEmpty())
+ continue;
+ if (headerFieldName == contentLengthFieldName) {
+ parseContentLength(message, headerFieldValue, parseError);
+ } else if (headerFieldName == contentTypeFieldName) {
+ parseContentType(message, headerFieldValue, parseError);
+ } else {
+ qCWarning(parseLog) << "Unexpected header field" << QLatin1String(headerFieldName)
+ << "in" << QLatin1String(headerFieldLine);
+ }
+ }
+
+ // the complete header wasn't received jet, waiting for the rest of it and reparse
+ message = BaseMessage();
+ data->seek(startPos);
+}
+
+QTextCodec *BaseMessage::defaultCodec()
+{
+ static QTextCodec *codec = QTextCodec::codecForName(defaultCharset);
+ return codec;
+}
+
+bool BaseMessage::isComplete() const
+{
+ if (!isValid())
+ return false;
+ QBS_ASSERT(content.length() <= contentLength, return true);
+ return content.length() == contentLength;
+}
+
+bool BaseMessage::isValid() const
+{
+ return contentLength >= 0;
+}
+
+QByteArray BaseMessage::header() const
+{
+ QByteArray header;
+ header.append(lengthHeader());
+ if (codec != defaultCodec()
+ || (!mimeType.isEmpty() && mimeType != JsonRpcMessage::jsonRpcMimeType())) {
+ header.append(typeHeader());
+ }
+ header.append(headerSeparator);
+ return header;
+}
+
+QByteArray BaseMessage::lengthHeader() const
+{
+ return QByteArray(contentLengthFieldName)
+ + QByteArray(headerFieldSeparator)
+ + QString::number(content.size()).toLatin1()
+ + QByteArray(headerSeparator);
+}
+
+QByteArray BaseMessage::typeHeader() const
+{
+ return QByteArray(contentTypeFieldName)
+ + QByteArray(headerFieldSeparator)
+ + mimeType + "; " + contentCharsetName + "=" + codec->name()
+ + QByteArray(headerSeparator);
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/basemessage.h b/src/shared/lsp/basemessage.h
new file mode 100644
index 000000000..4c6f0cb48
--- /dev/null
+++ b/src/shared/lsp/basemessage.h
@@ -0,0 +1,48 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "languageserverprotocol_global.h"
+
+#include <QByteArray>
+#include <QCoreApplication>
+#include <QLoggingCategory>
+
+QT_BEGIN_NAMESPACE
+class QBuffer;
+class QTextCodec;
+QT_END_NAMESPACE
+
+namespace lsp {
+
+LANGUAGESERVERPROTOCOL_EXPORT Q_DECLARE_LOGGING_CATEGORY(parseLog)
+
+class LANGUAGESERVERPROTOCOL_EXPORT BaseMessage
+{
+public:
+ BaseMessage();
+ BaseMessage(const QByteArray &mimeType, const QByteArray &content,
+ int expectedLength, QTextCodec *codec);
+ BaseMessage(const QByteArray &mimeType, const QByteArray &content);
+
+ bool operator==(const BaseMessage &other) const;
+
+ static void parse(QBuffer *data, QString &parseError, BaseMessage &message);
+ static QTextCodec *defaultCodec();
+
+ bool isComplete() const;
+ bool isValid() const;
+ QByteArray header() const;
+
+ QByteArray mimeType;
+ QByteArray content;
+ int contentLength = -1;
+ QTextCodec *codec = defaultCodec();
+
+private:
+ QByteArray lengthHeader() const;
+ QByteArray typeHeader() const;
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/callhierarchy.cpp b/src/shared/lsp/callhierarchy.cpp
new file mode 100644
index 000000000..af786fa95
--- /dev/null
+++ b/src/shared/lsp/callhierarchy.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "callhierarchy.h"
+
+namespace lsp {
+
+bool CallHierarchyItem::isValid() const
+{
+ return contains(nameKey) && contains(symbolKindKey) && contains(rangeKey) && contains(uriKey)
+ && contains(selectionRangeKey);
+}
+
+PrepareCallHierarchyRequest::PrepareCallHierarchyRequest(const TextDocumentPositionParams &params)
+ : Request(methodName, params)
+{}
+
+CallHierarchyIncomingCallsRequest::CallHierarchyIncomingCallsRequest(
+ const CallHierarchyCallsParams &params)
+ : Request(methodName, params)
+{}
+
+CallHierarchyOutgoingCallsRequest::CallHierarchyOutgoingCallsRequest(
+ const CallHierarchyCallsParams &params)
+ : Request(methodName, params)
+{}
+
+} // namespace lsp
diff --git a/src/shared/lsp/callhierarchy.h b/src/shared/lsp/callhierarchy.h
new file mode 100644
index 000000000..3db1f08de
--- /dev/null
+++ b/src/shared/lsp/callhierarchy.h
@@ -0,0 +1,108 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyItem : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString name() const { return typedValue<QString>(nameKey); }
+ void setName(const QString &name) { insert(nameKey, name); }
+
+ SymbolKind symbolKind() const { return SymbolKind(typedValue<int>(symbolKindKey)); }
+ void setSymbolKind(const SymbolKind &symbolKind) { insert(symbolKindKey, int(symbolKind)); }
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ Range selectionRange() const { return typedValue<Range>(selectionRangeKey); }
+ void setSelectionRange(Range selectionRange) { insert(selectionRangeKey, selectionRange); }
+
+ std::optional<QString> detail() const { return optionalValue<QString>(detailKey); }
+ void setDetail(const QString &detail) { insert(detailKey, detail); }
+ void clearDetail() { remove(detailKey); }
+
+ std::optional<QList<DocumentSymbol>> children() const
+ { return optionalArray<DocumentSymbol>(childrenKey); }
+ void setChildren(const QList<DocumentSymbol> &children) { insertArray(childrenKey, children); }
+ void clearChildren() { remove(childrenKey); }
+
+ bool isValid() const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT PrepareCallHierarchyRequest : public Request<
+ LanguageClientArray<CallHierarchyItem>, std::nullptr_t, TextDocumentPositionParams>
+{
+public:
+ explicit PrepareCallHierarchyRequest(const TextDocumentPositionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/prepareCallHierarchy";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyCallsParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ CallHierarchyItem item() const { return typedValue<CallHierarchyItem>(itemKey); }
+ void setItem(const CallHierarchyItem &item) { insert(itemKey, item); }
+
+ bool isValid() const override { return contains(itemKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyIncomingCall : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ CallHierarchyItem from() const { return typedValue<CallHierarchyItem>(fromKey); }
+ void setFrom(const CallHierarchyItem &from) { insert(fromKey, from); }
+
+ QList<Range> fromRanges() const { return array<Range>(fromRangesKey); }
+ void setFromRanges(const QList<Range> &fromRanges) { insertArray(fromRangesKey, fromRanges); }
+
+ bool isValid() const override { return contains(fromRangesKey) && contains(fromRangesKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyIncomingCallsRequest : public Request<
+ LanguageClientArray<CallHierarchyIncomingCall>, std::nullptr_t, CallHierarchyCallsParams>
+{
+public:
+ explicit CallHierarchyIncomingCallsRequest(const CallHierarchyCallsParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "callHierarchy/incomingCalls";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyOutgoingCall : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ CallHierarchyItem to() const { return typedValue<CallHierarchyItem>(toKey); }
+ void setTo(const CallHierarchyItem &to) { insert(toKey, to); }
+
+ QList<Range> fromRanges() const { return array<Range>(fromRangesKey); }
+ void setFromRanges(const QList<Range> &fromRanges) { insertArray(fromRangesKey, fromRanges); }
+
+ bool isValid() const override { return contains(fromRangesKey) && contains(fromRangesKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyOutgoingCallsRequest : public Request<
+ LanguageClientArray<CallHierarchyOutgoingCall>, std::nullptr_t, CallHierarchyCallsParams>
+{
+public:
+ explicit CallHierarchyOutgoingCallsRequest(const CallHierarchyCallsParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "callHierarchy/outgoingCalls";
+};
+
+} // namespace lsp
diff --git a/src/shared/lsp/client.cpp b/src/shared/lsp/client.cpp
new file mode 100644
index 000000000..8479131a6
--- /dev/null
+++ b/src/shared/lsp/client.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "client.h"
+
+namespace lsp {
+
+constexpr const char RegisterCapabilityRequest::methodName[];
+constexpr const char UnregisterCapabilityRequest::methodName[];
+
+RegisterCapabilityRequest::RegisterCapabilityRequest(const RegistrationParams &params)
+ : Request(methodName, params) { }
+
+UnregisterCapabilityRequest::UnregisterCapabilityRequest(const UnregistrationParams &params)
+ : UnregisterCapabilityRequest(methodName, params) { }
+
+} // namespace lsp
diff --git a/src/shared/lsp/client.h b/src/shared/lsp/client.h
new file mode 100644
index 000000000..29ebc5bc6
--- /dev/null
+++ b/src/shared/lsp/client.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+
+namespace lsp {
+
+class Registration : public JsonObject
+{
+public:
+ Registration() : Registration(QString()) {}
+ explicit Registration(const QString &method)
+ {
+ setId(QUuid::createUuid().toString());
+ setMethod(method);
+ }
+ using JsonObject::JsonObject;
+
+ QString id() const { return typedValue<QString>(idKey); }
+ void setId(const QString &id) { insert(idKey, id); }
+
+ QString method() const { return typedValue<QString>(methodKey); }
+ void setMethod(const QString &method) { insert(methodKey, method); }
+
+ QJsonValue registerOptions() const { return value(registerOptionsKey); }
+ void setRegisterOptions(const QJsonValue &registerOptions)
+ { insert(registerOptionsKey, registerOptions); }
+
+ bool isValid() const override { return contains(idKey) && contains(methodKey); }
+};
+
+class RegistrationParams : public JsonObject
+{
+public:
+ RegistrationParams() : RegistrationParams(QList<Registration>()) {}
+ explicit RegistrationParams(const QList<Registration> &registrations)
+ { setRegistrations(registrations); }
+ using JsonObject::JsonObject;
+
+ QList<Registration> registrations() const { return array<Registration>(registrationsKey); }
+ void setRegistrations(const QList<Registration> &registrations)
+ { insertArray(registrationsKey, registrations); }
+
+ bool isValid() const override { return contains(registrationsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT RegisterCapabilityRequest : public Request<
+ std::nullptr_t, std::nullptr_t, RegistrationParams>
+{
+public:
+ explicit RegisterCapabilityRequest(const RegistrationParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "client/registerCapability";
+};
+
+class Unregistration : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString id() const { return typedValue<QString>(idKey); }
+ void setId(const QString &id) { insert(idKey, id); }
+
+ QString method() const { return typedValue<QString>(methodKey); }
+ void setMethod(const QString &method) { insert(methodKey, method); }
+
+ bool isValid() const override { return contains(idKey) && contains(methodKey); }
+};
+
+class UnregistrationParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QList<Unregistration> unregistrations() const
+ { return array<Unregistration>(unregistrationsKey); }
+ void setUnregistrations(const QList<Unregistration> &unregistrations)
+ { insertArray(unregistrationsKey, unregistrations); }
+
+ bool isValid() const override { return contains(unregistrationsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT UnregisterCapabilityRequest : public Request<
+ std::nullptr_t, std::nullptr_t, UnregistrationParams>
+{
+public:
+ explicit UnregisterCapabilityRequest(const UnregistrationParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "client/unregisterCapability";
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/clientcapabilities.cpp b/src/shared/lsp/clientcapabilities.cpp
new file mode 100644
index 000000000..34d9de9e7
--- /dev/null
+++ b/src/shared/lsp/clientcapabilities.cpp
@@ -0,0 +1,122 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "clientcapabilities.h"
+
+namespace lsp {
+
+std::optional<QList<SymbolKind>> SymbolCapabilities::SymbolKindCapabilities::valueSet() const
+{
+ if (std::optional<QList<int>> array = optionalArray<int>(valueSetKey)) {
+ return std::make_optional(
+ Utils::transform(*array, [](int value) { return static_cast<SymbolKind>(value); }));
+ }
+ return std::nullopt;
+}
+
+void SymbolCapabilities::SymbolKindCapabilities::setValueSet(const QList<SymbolKind> &valueSet)
+{
+ insert(valueSetKey, enumArrayToJsonArray<SymbolKind>(valueSet));
+}
+
+WorkspaceClientCapabilities::WorkspaceClientCapabilities()
+{
+ setWorkspaceFolders(true);
+}
+
+std::optional<std::variant<bool, QJsonObject>> SemanticTokensClientCapabilities::Requests::range()
+ const
+{
+ using RetType = std::variant<bool, QJsonObject>;
+ const QJsonValue &rangeOptions = value(rangeKey);
+ if (rangeOptions.isBool())
+ return RetType(rangeOptions.toBool());
+ if (rangeOptions.isObject())
+ return RetType(rangeOptions.toObject());
+ return std::nullopt;
+}
+
+void SemanticTokensClientCapabilities::Requests::setRange(
+ const std::variant<bool, QJsonObject> &range)
+{
+ insertVariant<bool, QJsonObject>(rangeKey, range);
+}
+
+std::optional<std::variant<bool, FullSemanticTokenOptions>>
+SemanticTokensClientCapabilities::Requests::full() const
+{
+ using RetType = std::variant<bool, FullSemanticTokenOptions>;
+ const QJsonValue &fullOptions = value(fullKey);
+ if (fullOptions.isBool())
+ return RetType(fullOptions.toBool());
+ if (fullOptions.isObject())
+ return RetType(FullSemanticTokenOptions(fullOptions.toObject()));
+ return std::nullopt;
+}
+
+void SemanticTokensClientCapabilities::Requests::setFull(
+ const std::variant<bool, FullSemanticTokenOptions> &full)
+{
+ insertVariant<bool, FullSemanticTokenOptions>(fullKey, full);
+}
+
+std::optional<SemanticTokensClientCapabilities> TextDocumentClientCapabilities::semanticTokens() const
+{
+ return optionalValue<SemanticTokensClientCapabilities>(semanticTokensKey);
+}
+
+void TextDocumentClientCapabilities::setSemanticTokens(
+ const SemanticTokensClientCapabilities &semanticTokens)
+{
+ insert(semanticTokensKey, semanticTokens);
+}
+
+bool SemanticTokensClientCapabilities::isValid() const
+{
+ return contains(requestsKey) && contains(tokenTypesKey) && contains(tokenModifiersKey)
+ && contains(formatsKey);
+}
+
+const char resourceOperationCreate[] = "create";
+const char resourceOperationRename[] = "rename";
+const char resourceOperationDelete[] = "delete";
+
+std::optional<QList<WorkspaceClientCapabilities::WorkspaceEditCapabilities::ResourceOperationKind>>
+WorkspaceClientCapabilities::WorkspaceEditCapabilities::resourceOperations() const
+{
+ if (!contains(resourceOperationsKey))
+ return std::nullopt;
+ QList<ResourceOperationKind> result;
+ for (const QJsonValue &value : this->value(resourceOperationsKey).toArray()) {
+ const QString str = value.toString();
+ if (str == resourceOperationCreate)
+ result << ResourceOperationKind::Create;
+ else if (str == resourceOperationRename)
+ result << ResourceOperationKind::Rename;
+ else if (str == resourceOperationDelete)
+ result << ResourceOperationKind::Delete;
+ }
+ return result;
+}
+
+void WorkspaceClientCapabilities::WorkspaceEditCapabilities::setResourceOperations(
+ const QList<ResourceOperationKind> &resourceOperations)
+{
+ QJsonArray array;
+ for (const auto &kind : resourceOperations) {
+ switch (kind) {
+ case ResourceOperationKind::Create:
+ array << resourceOperationCreate;
+ break;
+ case ResourceOperationKind::Rename:
+ array << resourceOperationRename;
+ break;
+ case ResourceOperationKind::Delete:
+ array << resourceOperationDelete;
+ break;
+ }
+ }
+ insert(resourceOperationsKey, array);
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/clientcapabilities.h b/src/shared/lsp/clientcapabilities.h
new file mode 100644
index 000000000..44add0a2c
--- /dev/null
+++ b/src/shared/lsp/clientcapabilities.h
@@ -0,0 +1,674 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonkeys.h"
+#include "lsptypes.h"
+#include "semantictokens.h"
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT DynamicRegistrationCapabilities : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ std::optional<bool> dynamicRegistration() const
+ {
+ return optionalValue<bool>(dynamicRegistrationKey);
+ }
+ void setDynamicRegistration(bool dynamicRegistration) { insert(dynamicRegistrationKey, dynamicRegistration); }
+ void clearDynamicRegistration() { remove(dynamicRegistrationKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT FullSemanticTokenOptions : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ /**
+ * The client will send the `textDocument/semanticTokens/full/delta`
+ * request if the server provides a corresponding handler.
+ */
+ std::optional<bool> delta() const { return optionalValue<bool>(deltaKey); }
+ void setDelta(bool delta) { insert(deltaKey, delta); }
+ void clearDelta() { remove(deltaKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensClientCapabilities : public DynamicRegistrationCapabilities
+{
+public:
+ using DynamicRegistrationCapabilities::DynamicRegistrationCapabilities;
+ class LANGUAGESERVERPROTOCOL_EXPORT Requests : public JsonObject
+ {
+ /**
+ * Which requests the client supports and might send to the server
+ * depending on the server's capability. Please note that clients might not
+ * show semantic tokens or degrade some of the user experience if a range
+ * or full request is advertised by the client but not provided by the
+ * server. If for example the client capability `requests.full` and
+ * `request.range` are both set to true but the server only provides a
+ * range provider the client might not render a minimap correctly or might
+ * even decide to not show any semantic tokens at all.
+ */
+ public:
+ using JsonObject::JsonObject;
+
+ /**
+ * The client will send the `textDocument/semanticTokens/range` request
+ * if the server provides a corresponding handler.
+ */
+ std::optional<std::variant<bool, QJsonObject>> range() const;
+ void setRange(const std::variant<bool, QJsonObject> &range);
+ void clearRange() { remove(rangeKey); }
+
+ /**
+ * The client will send the `textDocument/semanticTokens/full` request
+ * if the server provides a corresponding handler.
+ */
+ std::optional<std::variant<bool, FullSemanticTokenOptions>> full() const;
+ void setFull(const std::variant<bool, FullSemanticTokenOptions> &full);
+ void clearFull() { remove(fullKey); }
+ };
+
+ Requests requests() const { return typedValue<Requests>(requestsKey); }
+ void setRequests(const Requests &requests) { insert(requestsKey, requests); }
+
+ /// The token types that the client supports.
+ QList<QString> tokenTypes() const { return array<QString>(tokenTypesKey); }
+ void setTokenTypes(const QList<QString> &value) { insertArray(tokenTypesKey, value); }
+
+ /// The token modifiers that the client supports.
+ QList<QString> tokenModifiers() const { return array<QString>(tokenModifiersKey); }
+ void setTokenModifiers(const QList<QString> &value) { insertArray(tokenModifiersKey, value); }
+
+ /// The formats the clients supports.
+ QList<QString> formats() const { return array<QString>(formatsKey); }
+ void setFormats(const QList<QString> &value) { insertArray(formatsKey, value); }
+
+ /// Whether the client supports tokens that can overlap each other.
+ std::optional<bool> overlappingTokenSupport() const
+ {
+ return optionalValue<bool>(overlappingTokenSupportKey);
+ }
+ void setOverlappingTokenSupport(bool overlappingTokenSupport) { insert(overlappingTokenSupportKey, overlappingTokenSupport); }
+ void clearOverlappingTokenSupport() { remove(overlappingTokenSupportKey); }
+
+ /// Whether the client supports tokens that can span multiple lines.
+ std::optional<bool> multiLineTokenSupport() const
+ {
+ return optionalValue<bool>(multiLineTokenSupportKey);
+ }
+ void setMultiLineTokenSupport(bool multiLineTokenSupport) { insert(multiLineTokenSupportKey, multiLineTokenSupport); }
+ void clearMultiLineTokenSupport() { remove(multiLineTokenSupportKey); }
+
+ bool isValid() const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SymbolCapabilities : public DynamicRegistrationCapabilities
+{
+public:
+ using DynamicRegistrationCapabilities::DynamicRegistrationCapabilities;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT SymbolKindCapabilities : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ /*
+ * The symbol kind values the client supports. When this
+ * property exists the client also guarantees that it will
+ * handle values outside its set gracefully and falls back
+ * to a default value when unknown.
+ *
+ * If this property is not present the client only supports
+ * the symbol kinds from `File` to `Array` as defined in
+ * the initial version of the protocol.
+ */
+ std::optional<QList<SymbolKind>> valueSet() const;
+ void setValueSet(const QList<SymbolKind> &valueSet);
+ void clearValueSet() { remove(valueSetKey); }
+ };
+
+ // Specific capabilities for the `SymbolKind` in the `workspace/symbol` request.
+ std::optional<SymbolKindCapabilities> symbolKind() const
+ { return optionalValue<SymbolKindCapabilities>(symbolKindKey); }
+ void setSymbolKind(const SymbolKindCapabilities &symbolKind) { insert(symbolKindKey, symbolKind); }
+ void clearSymbolKind() { remove(symbolKindKey); }
+
+ std::optional<bool> hierarchicalDocumentSymbolSupport() const
+ { return optionalValue<bool>(hierarchicalDocumentSymbolSupportKey); }
+ void setHierarchicalDocumentSymbolSupport(bool hierarchicalDocumentSymbolSupport)
+ { insert(hierarchicalDocumentSymbolSupportKey, hierarchicalDocumentSymbolSupport); }
+ void clearHierachicalDocumentSymbolSupport() { remove(hierarchicalDocumentSymbolSupportKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentClientCapabilities : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT SynchronizationCapabilities : public DynamicRegistrationCapabilities
+ {
+ public:
+ using DynamicRegistrationCapabilities::DynamicRegistrationCapabilities;
+
+ // The client supports sending will save notifications.
+ std::optional<bool> willSave() const { return optionalValue<bool>(willSaveKey); }
+ void setWillSave(bool willSave) { insert(willSaveKey, willSave); }
+ void clearWillSave() { remove(willSaveKey); }
+
+ /*
+ * The client supports sending a will save request and
+ * waits for a response providing text edits which will
+ * be applied to the document before it is saved.
+ */
+ std::optional<bool> willSaveWaitUntil() const
+ { return optionalValue<bool>(willSaveWaitUntilKey); }
+ void setWillSaveWaitUntil(bool willSaveWaitUntil)
+ { insert(willSaveWaitUntilKey, willSaveWaitUntil); }
+ void clearWillSaveWaitUntil() { remove(willSaveWaitUntilKey); }
+
+ // The client supports did save notifications.
+ std::optional<bool> didSave() const { return optionalValue<bool>(didSaveKey); }
+ void setDidSave(bool didSave) { insert(didSaveKey, didSave); }
+ void clearDidSave() { remove(didSaveKey); }
+ };
+
+ std::optional<SynchronizationCapabilities> synchronization() const
+ { return optionalValue<SynchronizationCapabilities>(synchronizationKey); }
+ void setSynchronization(const SynchronizationCapabilities &synchronization)
+ { insert(synchronizationKey, synchronization); }
+ void clearSynchronization() { remove(synchronizationKey); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT CompletionCapabilities : public DynamicRegistrationCapabilities
+ {
+ public:
+ using DynamicRegistrationCapabilities::DynamicRegistrationCapabilities;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT CompletionItemCapbilities : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ /*
+ * Client supports snippets as insert text.
+ *
+ * A snippet can define tab stops and placeholders with `$1`, `$2`
+ * and `${3:foo}`. `$0` defines the final tab stop, it defaults to
+ * the end of the snippet. Placeholders with equal identifiers are linked,
+ * that is typing in one will update others too.
+ */
+ std::optional<bool> snippetSupport() const
+ { return optionalValue<bool>(snippetSupportKey); }
+ void setSnippetSupport(bool snippetSupport)
+ { insert(snippetSupportKey, snippetSupport); }
+ void clearSnippetSupport() { remove(snippetSupportKey); }
+
+ // Client supports commit characters on a completion item.
+ std::optional<bool> commitCharacterSupport() const
+ { return optionalValue<bool>(commitCharacterSupportKey); }
+ void setCommitCharacterSupport(bool commitCharacterSupport)
+ { insert(commitCharacterSupportKey, commitCharacterSupport); }
+ void clearCommitCharacterSupport() { remove(commitCharacterSupportKey); }
+
+ /*
+ * Client supports the follow content formats for the documentation
+ * property. The order describes the preferred format of the client.
+ */
+ std::optional<QList<MarkupKind>> documentationFormat() const;
+ void setDocumentationFormat(const QList<MarkupKind> &documentationFormat);
+ void clearDocumentationFormat() { remove(documentationFormatKey); }
+ };
+
+ // The client supports the following `CompletionItem` specific capabilities.
+ std::optional<CompletionItemCapbilities> completionItem() const
+ { return optionalValue<CompletionItemCapbilities>(completionItemKey); }
+ void setCompletionItem(const CompletionItemCapbilities &completionItem)
+ { insert(completionItemKey, completionItem); }
+ void clearCompletionItem() { remove(completionItemKey); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT CompletionItemKindCapabilities : public JsonObject
+ {
+ public:
+ CompletionItemKindCapabilities();
+ using JsonObject::JsonObject;
+ /*
+ * The completion item kind values the client supports. When this
+ * property exists the client also guarantees that it will
+ * handle values outside its set gracefully and falls back
+ * to a default value when unknown.
+ *
+ * If this property is not present the client only supports
+ * the completion items kinds from `Text` to `Reference` as defined in
+ * the initial version of the protocol.
+ */
+ std::optional<QList<CompletionItemKind::Kind>> valueSet() const;
+ void setValueSet(const QList<CompletionItemKind::Kind> &valueSet);
+ void clearValueSet() { remove(valueSetKey); }
+ };
+
+ std::optional<CompletionItemKindCapabilities> completionItemKind() const
+ { return optionalValue<CompletionItemKindCapabilities>(completionItemKindKey); }
+ void setCompletionItemKind(const CompletionItemKindCapabilities &completionItemKind)
+ { insert(completionItemKindKey, completionItemKind); }
+ void clearCompletionItemKind() { remove(completionItemKindKey); }
+
+ /*
+ * The client supports to send additional context information for a
+ * `textDocument/completion` request.
+ */
+ std::optional<bool> contextSupport() const
+ {
+ return optionalValue<bool>(contextSupportKey);
+ }
+ void setContextSupport(bool contextSupport) { insert(contextSupportKey, contextSupport); }
+ void clearContextSupport() { remove(contextSupportKey); }
+ };
+
+ // Capabilities specific to the `textDocument/completion`
+ std::optional<CompletionCapabilities> completion() const
+ { return optionalValue<CompletionCapabilities>(completionKey); }
+ void setCompletion(const CompletionCapabilities &completion)
+ { insert(completionKey, completion); }
+ void clearCompletion() { remove(completionKey); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT HoverCapabilities : public DynamicRegistrationCapabilities
+ {
+ public:
+ using DynamicRegistrationCapabilities::DynamicRegistrationCapabilities;
+ /*
+ * Client supports the follow content formats for the content
+ * property. The order describes the preferred format of the client.
+ */
+ std::optional<QList<MarkupKind>> contentFormat() const;
+ void setContentFormat(const QList<MarkupKind> &contentFormat);
+ void clearContentFormat() { remove(contentFormatKey); }
+ };
+
+ std::optional<HoverCapabilities> hover() const
+ {
+ return optionalValue<HoverCapabilities>(hoverKey);
+ }
+ void setHover(const HoverCapabilities &hover) { insert(hoverKey, hover); }
+ void clearHover() { remove(hoverKey); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT SignatureHelpCapabilities : public DynamicRegistrationCapabilities
+ {
+ public:
+ using DynamicRegistrationCapabilities::DynamicRegistrationCapabilities;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT SignatureInformationCapabilities : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+ /*
+ * Client supports the follow content formats for the documentation
+ * property. The order describes the preferred format of the client.
+ */
+ std::optional<QList<MarkupKind>> documentationFormat() const;
+ void setDocumentationFormat(const QList<MarkupKind> &documentationFormat);
+ void clearDocumentationFormat() { remove(documentationFormatKey); }
+
+ std::optional<bool> activeParameterSupport() const
+ { return optionalValue<bool>(activeParameterSupportKey); }
+ void setActiveParameterSupport(bool activeParameterSupport)
+ { insert(activeParameterSupportKey, activeParameterSupport); }
+ void clearActiveParameterSupport() { remove(activeParameterSupportKey); }
+ };
+
+ // The client supports the following `SignatureInformation` specific properties.
+ std::optional<SignatureInformationCapabilities> signatureInformation() const
+ { return optionalValue<SignatureInformationCapabilities>(signatureInformationKey); }
+ void setSignatureInformation(const SignatureInformationCapabilities &signatureInformation)
+ { insert(signatureInformationKey, signatureInformation); }
+ void clearSignatureInformation() { remove(signatureInformationKey); }
+ };
+
+ // Capabilities specific to the `textDocument/signatureHelp`
+ std::optional<SignatureHelpCapabilities> signatureHelp() const
+ { return optionalValue<SignatureHelpCapabilities>(signatureHelpKey); }
+ void setSignatureHelp(const SignatureHelpCapabilities &signatureHelp)
+ { insert(signatureHelpKey, signatureHelp); }
+ void clearSignatureHelp() { remove(signatureHelpKey); }
+
+ // Whether references supports dynamic registration.
+ std::optional<DynamicRegistrationCapabilities> references() const
+ { return optionalValue<DynamicRegistrationCapabilities>(referencesKey); }
+ void setReferences(const DynamicRegistrationCapabilities &references)
+ { insert(referencesKey, references); }
+ void clearReferences() { remove(referencesKey); }
+
+ // Whether document highlight supports dynamic registration.
+ std::optional<DynamicRegistrationCapabilities> documentHighlight() const
+ { return optionalValue<DynamicRegistrationCapabilities>(documentHighlightKey); }
+ void setDocumentHighlight(const DynamicRegistrationCapabilities &documentHighlight)
+ { insert(documentHighlightKey, documentHighlight); }
+ void clearDocumentHighlight() { remove(documentHighlightKey); }
+
+ // Capabilities specific to the `textDocument/documentSymbol`
+ std::optional<SymbolCapabilities> documentSymbol() const
+ { return optionalValue<SymbolCapabilities>(documentSymbolKey); }
+ void setDocumentSymbol(const SymbolCapabilities &documentSymbol)
+ { insert(documentSymbolKey, documentSymbol); }
+ void clearDocumentSymbol() { remove(documentSymbolKey); }
+
+ // Whether formatting supports dynamic registration.
+ std::optional<DynamicRegistrationCapabilities> formatting() const
+ { return optionalValue<DynamicRegistrationCapabilities>(formattingKey); }
+ void setFormatting(const DynamicRegistrationCapabilities &formatting)
+ { insert(formattingKey, formatting); }
+ void clearFormatting() { remove(formattingKey); }
+
+ // Whether range formatting supports dynamic registration.
+ std::optional<DynamicRegistrationCapabilities> rangeFormatting() const
+ { return optionalValue<DynamicRegistrationCapabilities>(rangeFormattingKey); }
+ void setRangeFormatting(const DynamicRegistrationCapabilities &rangeFormatting)
+ { insert(rangeFormattingKey, rangeFormatting); }
+ void clearRangeFormatting() { remove(rangeFormattingKey); }
+
+ // Whether on type formatting supports dynamic registration.
+ std::optional<DynamicRegistrationCapabilities> onTypeFormatting() const
+ { return optionalValue<DynamicRegistrationCapabilities>(onTypeFormattingKey); }
+ void setOnTypeFormatting(const DynamicRegistrationCapabilities &onTypeFormatting)
+ { insert(onTypeFormattingKey, onTypeFormatting); }
+ void clearOnTypeFormatting() { remove(onTypeFormattingKey); }
+
+ // Whether definition supports dynamic registration.
+ std::optional<DynamicRegistrationCapabilities> definition() const
+ { return optionalValue<DynamicRegistrationCapabilities>(definitionKey); }
+ void setDefinition(const DynamicRegistrationCapabilities &definition)
+ { insert(definitionKey, definition); }
+ void clearDefinition() { remove(definitionKey); }
+
+ /*
+ * Whether typeDefinition supports dynamic registration. If this is set to `true`
+ * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
+ * return value for the corresponding server capability as well.
+ */
+ std::optional<DynamicRegistrationCapabilities> typeDefinition() const
+ { return optionalValue<DynamicRegistrationCapabilities>(typeDefinitionKey); }
+ void setTypeDefinition(const DynamicRegistrationCapabilities &typeDefinition)
+ { insert(typeDefinitionKey, typeDefinition); }
+ void clearTypeDefinition() { remove(typeDefinitionKey); }
+
+ /*
+ * Whether implementation supports dynamic registration. If this is set to `true`
+ * the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
+ * return value for the corresponding server capability as well.
+ */
+ std::optional<DynamicRegistrationCapabilities> implementation() const
+ { return optionalValue<DynamicRegistrationCapabilities>(implementationKey); }
+ void setImplementation(const DynamicRegistrationCapabilities &implementation)
+ { insert(implementationKey, implementation); }
+ void clearImplementation() { remove(implementationKey); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT CodeActionCapabilities : public DynamicRegistrationCapabilities
+ {
+ public:
+ using DynamicRegistrationCapabilities::DynamicRegistrationCapabilities;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT CodeActionLiteralSupport : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT CodeActionKind : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+ CodeActionKind() : CodeActionKind(QList<QString>()) {}
+ explicit CodeActionKind(const QList<QString> &kinds) { setValueSet(kinds); }
+
+ QList<QString> valueSet() const { return array<QString>(valueSetKey); }
+ void setValueSet(const QList<QString> &valueSet)
+ { insertArray(valueSetKey, valueSet); }
+
+ bool isValid() const override { return contains(valueSetKey); }
+ };
+
+ CodeActionKind codeActionKind() const
+ { return typedValue<CodeActionKind>(codeActionKindKey); }
+ void setCodeActionKind(const CodeActionKind &codeActionKind)
+ { insert(codeActionKindKey, codeActionKind); }
+
+ bool isValid() const override { return contains(codeActionKindKey); }
+ };
+
+ std::optional<CodeActionLiteralSupport> codeActionLiteralSupport() const
+ { return optionalValue<CodeActionLiteralSupport>(codeActionLiteralSupportKey); }
+ void setCodeActionLiteralSupport(const CodeActionLiteralSupport &codeActionLiteralSupport)
+ { insert(codeActionLiteralSupportKey, codeActionLiteralSupport); }
+ void clearCodeActionLiteralSupport() { remove(codeActionLiteralSupportKey); }
+ };
+
+ // Whether code action supports dynamic registration.
+ std::optional<CodeActionCapabilities> codeAction() const
+ { return optionalValue<CodeActionCapabilities>(codeActionKey); }
+ void setCodeAction(const CodeActionCapabilities &codeAction)
+ { insert(codeActionKey, codeAction); }
+ void clearCodeAction() { remove(codeActionKey); }
+
+ // Whether code lens supports dynamic registration.
+ std::optional<DynamicRegistrationCapabilities> codeLens() const
+ { return optionalValue<DynamicRegistrationCapabilities>(codeLensKey); }
+ void setCodeLens(const DynamicRegistrationCapabilities &codeLens)
+ { insert(codeLensKey, codeLens); }
+ void clearCodeLens() { remove(codeLensKey); }
+
+ // Whether document link supports dynamic registration.
+ std::optional<DynamicRegistrationCapabilities> documentLink() const
+ { return optionalValue<DynamicRegistrationCapabilities>(documentLinkKey); }
+ void setDocumentLink(const DynamicRegistrationCapabilities &documentLink)
+ { insert(documentLinkKey, documentLink); }
+ void clearDocumentLink() { remove(documentLinkKey); }
+
+ /*
+ * Whether colorProvider supports dynamic registration. If this is set to `true`
+ * the client supports the new `(ColorProviderOptions & TextDocumentRegistrationOptions & StaticRegistrationOptions)`
+ * return value for the corresponding server capability as well.
+ */
+ std::optional<DynamicRegistrationCapabilities> colorProvider() const
+ { return optionalValue<DynamicRegistrationCapabilities>(colorProviderKey); }
+ void setColorProvider(const DynamicRegistrationCapabilities &colorProvider)
+ { insert(colorProviderKey, colorProvider); }
+ void clearColorProvider() { remove(colorProviderKey); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT RenameClientCapabilities : public DynamicRegistrationCapabilities
+ {
+ public:
+ using DynamicRegistrationCapabilities::DynamicRegistrationCapabilities;
+ /**
+ * Client supports testing for validity of rename operations
+ * before execution.
+ *
+ * @since version 3.12.0
+ */
+
+ std::optional<bool> prepareSupport() const
+ {
+ return optionalValue<bool>(prepareSupportKey);
+ }
+ void setPrepareSupport(bool prepareSupport) { insert(prepareSupportKey, prepareSupport); }
+ void clearPrepareSupport() { remove(prepareSupportKey); }
+ };
+
+ // Whether rename supports dynamic registration.
+ std::optional<RenameClientCapabilities> rename() const
+ { return optionalValue<RenameClientCapabilities>(renameKey); }
+ void setRename(const RenameClientCapabilities &rename)
+ { insert(renameKey, rename); }
+ void clearRename() { remove(renameKey); }
+
+ std::optional<SemanticTokensClientCapabilities> semanticTokens() const;
+ void setSemanticTokens(const SemanticTokensClientCapabilities &semanticTokens);
+ void clearSemanticTokens() { remove(semanticTokensKey); }
+
+ std::optional<DynamicRegistrationCapabilities> callHierarchy() const
+ { return optionalValue<DynamicRegistrationCapabilities>(callHierarchyKey); }
+ void setCallHierarchy(const DynamicRegistrationCapabilities &callHierarchy)
+ { insert(callHierarchyKey, callHierarchy); }
+ void clearCallHierarchy() { remove(callHierarchyKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensWorkspaceClientCapabilities : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+ /**
+ * Whether the client implementation supports a refresh request sent from
+ * the server to the client.
+ *
+ * Note that this event is global and will force the client to refresh all
+ * semantic tokens currently shown. It should be used with absolute care
+ * and is useful for situation where a server for example detect a project
+ * wide change that requires such a calculation.
+ */
+ std::optional<bool> refreshSupport() const { return optionalValue<bool>(refreshSupportKey); }
+ void setRefreshSupport(bool refreshSupport) { insert(refreshSupportKey, refreshSupport); }
+ void clearRefreshSupport() { remove(refreshSupportKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceClientCapabilities : public JsonObject
+{
+public:
+ WorkspaceClientCapabilities();
+ using JsonObject::JsonObject;
+
+ /*
+ * The client supports applying batch edits to the workspace by supporting the request
+ * 'workspace/applyEdit'
+ */
+ std::optional<bool> applyEdit() const { return optionalValue<bool>(applyEditKey); }
+ void setApplyEdit(bool applyEdit) { insert(applyEditKey, applyEdit); }
+ void clearApplyEdit() { remove(applyEditKey); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceEditCapabilities : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ // The client supports versioned document changes in `WorkspaceEdit`s
+ std::optional<bool> documentChanges() const
+ { return optionalValue<bool>(documentChangesKey); }
+ void setDocumentChanges(bool documentChanges)
+ { insert(documentChangesKey, documentChanges); }
+ void clearDocumentChanges() { remove(documentChangesKey); }
+
+ enum class ResourceOperationKind { Create, Rename, Delete };
+
+ // The resource operations the client supports. Clients should at least support 'create',
+ // 'rename' and 'delete' files and folders.
+ std::optional<QList<ResourceOperationKind>> resourceOperations() const;
+ void setResourceOperations(const QList<ResourceOperationKind> &resourceOperations);
+ void clearResourceOperations() { remove(resourceOperationsKey); }
+ };
+
+ // Capabilities specific to `WorkspaceEdit`s
+ std::optional<WorkspaceEditCapabilities> workspaceEdit() const
+ { return optionalValue<WorkspaceEditCapabilities>(workspaceEditKey); }
+ void setWorkspaceEdit(const WorkspaceEditCapabilities &workspaceEdit)
+ { insert(workspaceEditKey, workspaceEdit); }
+ void clearWorkspaceEdit() { remove(workspaceEditKey); }
+
+ // Capabilities specific to the `workspace/didChangeConfiguration` notification.
+ std::optional<DynamicRegistrationCapabilities> didChangeConfiguration() const
+ { return optionalValue<DynamicRegistrationCapabilities>(didChangeConfigurationKey); }
+ void setDidChangeConfiguration(const DynamicRegistrationCapabilities &didChangeConfiguration)
+ { insert(didChangeConfigurationKey, didChangeConfiguration); }
+ void clearDidChangeConfiguration() { remove(didChangeConfigurationKey); }
+
+ // Capabilities specific to the `workspace/didChangeWatchedFiles` notification.
+ std::optional<DynamicRegistrationCapabilities> didChangeWatchedFiles() const
+ { return optionalValue<DynamicRegistrationCapabilities>(didChangeWatchedFilesKey); }
+ void setDidChangeWatchedFiles(const DynamicRegistrationCapabilities &didChangeWatchedFiles)
+ { insert(didChangeWatchedFilesKey, didChangeWatchedFiles); }
+ void clearDidChangeWatchedFiles() { remove(didChangeWatchedFilesKey); }
+
+ // Specific capabilities for the `SymbolKind` in the `workspace/symbol` request.
+ std::optional<SymbolCapabilities> symbol() const
+ { return optionalValue<SymbolCapabilities>(symbolKey); }
+ void setSymbol(const SymbolCapabilities &symbol) { insert(symbolKey, symbol); }
+ void clearSymbol() { remove(symbolKey); }
+
+ // Capabilities specific to the `workspace/executeCommand` request.
+ std::optional<DynamicRegistrationCapabilities> executeCommand() const
+ { return optionalValue<DynamicRegistrationCapabilities>(executeCommandKey); }
+ void setExecuteCommand(const DynamicRegistrationCapabilities &executeCommand)
+ { insert(executeCommandKey, executeCommand); }
+ void clearExecuteCommand() { remove(executeCommandKey); }
+
+ // The client has support for workspace folders. Since 3.6.0
+ std::optional<bool> workspaceFolders() const
+ { return optionalValue<bool>(workspaceFoldersKey); }
+ void setWorkspaceFolders(bool workspaceFolders)
+ { insert(workspaceFoldersKey, workspaceFolders); }
+ void clearWorkspaceFolders() { remove(workspaceFoldersKey); }
+
+ // The client supports `workspace/configuration` requests. Since 3.6.0
+ std::optional<bool> configuration() const { return optionalValue<bool>(configurationKey); }
+ void setConfiguration(bool configuration) { insert(configurationKey, configuration); }
+ void clearConfiguration() { remove(configurationKey); }
+
+ std::optional<SemanticTokensWorkspaceClientCapabilities> semanticTokens() const
+ { return optionalValue<SemanticTokensWorkspaceClientCapabilities>(semanticTokensKey); }
+ void setSemanticTokens(const SemanticTokensWorkspaceClientCapabilities &semanticTokens)
+ { insert(semanticTokensKey, semanticTokens); }
+ void clearSemanticTokens() { remove(semanticTokensKey); }
+};
+
+class WindowClientClientCapabilities : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ /**
+ * Whether client supports handling progress notifications.
+ * If set, servers are allowed to report in `workDoneProgress` property
+ * in the request specific server capabilities.
+ *
+ */
+ std::optional<bool> workDoneProgress() const
+ { return optionalValue<bool>(workDoneProgressKey); }
+ void setWorkDoneProgress(bool workDoneProgress)
+ { insert(workDoneProgressKey, workDoneProgress); }
+ void clearWorkDoneProgress() { remove(workDoneProgressKey); }
+
+private:
+ constexpr static const char workDoneProgressKey[] = "workDoneProgress";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ClientCapabilities : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // Workspace specific client capabilities.
+ std::optional<WorkspaceClientCapabilities> workspace() const
+ { return optionalValue<WorkspaceClientCapabilities>(workspaceKey); }
+ void setWorkspace(const WorkspaceClientCapabilities &workspace)
+ { insert(workspaceKey, workspace); }
+ void clearWorkspace() { remove(workspaceKey); }
+
+ // Text document specific client capabilities.
+ std::optional<TextDocumentClientCapabilities> textDocument() const
+ { return optionalValue<TextDocumentClientCapabilities>(textDocumentKey); }
+ void setTextDocument(const TextDocumentClientCapabilities &textDocument)
+ { insert(textDocumentKey, textDocument); }
+ void clearTextDocument() { remove(textDocumentKey); }
+
+ // Window specific client capabilities.
+ std::optional<WindowClientClientCapabilities> window() const
+ { return optionalValue<WindowClientClientCapabilities>(windowKey); }
+ void setWindow(const WindowClientClientCapabilities &window)
+ { insert(windowKey, window); }
+ void clearWindow() { remove(windowKey); }
+
+ // Experimental client capabilities.
+ QJsonValue experimental() const { return value(experimentalKey); }
+ void setExperimental(const QJsonValue &experimental) { insert(experimentalKey, experimental); }
+ void clearExperimental() { remove(experimentalKey); }
+};
+
+} // namespace lsp
diff --git a/src/shared/lsp/completion.cpp b/src/shared/lsp/completion.cpp
new file mode 100644
index 000000000..48a2ac868
--- /dev/null
+++ b/src/shared/lsp/completion.cpp
@@ -0,0 +1,59 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "completion.h"
+
+namespace lsp {
+
+constexpr const char CompletionRequest::methodName[];
+constexpr const char CompletionItemResolveRequest::methodName[];
+
+CompletionRequest::CompletionRequest(const CompletionParams &params)
+ : Request(methodName, params)
+{ }
+
+std::optional<MarkupOrString> CompletionItem::documentation() const
+{
+ QJsonValue documentation = value(documentationKey);
+ if (documentation.isUndefined())
+ return std::nullopt;
+ return MarkupOrString(documentation);
+}
+
+std::optional<CompletionItem::InsertTextFormat> CompletionItem::insertTextFormat() const
+{
+ if (std::optional<int> value = optionalValue<int>(insertTextFormatKey))
+ return std::make_optional(CompletionItem::InsertTextFormat(*value));
+ return std::nullopt;
+}
+
+std::optional<QList<CompletionItem::CompletionItemTag>> CompletionItem::tags() const
+{
+ if (const auto value = optionalValue<QJsonArray>(tagsKey)) {
+ QList<CompletionItemTag> tags;
+ for (auto it = value->cbegin(); it != value->cend(); ++it)
+ tags << static_cast<CompletionItemTag>(it->toInt());
+ return tags;
+ }
+ return {};
+}
+
+CompletionItemResolveRequest::CompletionItemResolveRequest(const CompletionItem &params)
+ : Request(methodName, params)
+{ }
+
+CompletionResult::CompletionResult(const QJsonValue &value)
+{
+ if (value.isNull()) {
+ emplace<std::nullptr_t>(nullptr);
+ } else if (value.isArray()) {
+ QList<CompletionItem> items;
+ for (auto arrayElement : value.toArray())
+ items << CompletionItem(arrayElement);
+ emplace<QList<CompletionItem>>(items);
+ } else if (value.isObject()) {
+ emplace<CompletionList>(CompletionList(value));
+ }
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/completion.h b/src/shared/lsp/completion.h
new file mode 100644
index 000000000..665b5a5ae
--- /dev/null
+++ b/src/shared/lsp/completion.h
@@ -0,0 +1,269 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+#include "languagefeatures.h"
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT CompletionParams : public TextDocumentPositionParams
+{
+public:
+ using TextDocumentPositionParams::TextDocumentPositionParams;
+
+ enum CompletionTriggerKind {
+ /**
+ * Completion was triggered by typing an identifier (24x7 code
+ * complete), manual invocation (e.g Ctrl+Space) or via API.
+ */
+ Invoked = 1,
+ /**
+ * Completion was triggered by a trigger character specified by
+ * the `triggerCharacters` properties of the `CompletionRegistrationOptions`.
+ */
+ TriggerCharacter = 2,
+ /// Completion was re-triggered as the current completion list is incomplete.
+ TriggerForIncompleteCompletions = 3
+ };
+
+ class CompletionContext : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ /// How the completion was triggered.
+ CompletionTriggerKind triggerKind() const
+ { return CompletionTriggerKind(typedValue<int>(triggerKindKey)); }
+ void setTriggerKind(CompletionTriggerKind triggerKind)
+ { insert(triggerKindKey, triggerKind); }
+
+ /**
+ * The trigger character (a single character) that has trigger code complete.
+ * Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter`
+ */
+ std::optional<QString> triggerCharacter() const
+ { return optionalValue<QString>(triggerCharacterKey); }
+ void setTriggerCharacter(const QString &triggerCharacter)
+ { insert(triggerCharacterKey, triggerCharacter); }
+ void clearTriggerCharacter() { remove(triggerCharacterKey); }
+
+ bool isValid() const override { return contains(triggerKindKey); }
+ };
+
+ /**
+ * The completion context. This is only available it the client specifies
+ * to send this using `ClientCapabilities.textDocument.completion.contextSupport === true`
+ */
+ std::optional<CompletionContext> context() const
+ { return optionalValue<CompletionContext>(contextKey); }
+ void setContext(const CompletionContext &context)
+ { insert(contextKey, context); }
+ void clearContext() { remove(contextKey); }
+
+ // clangd extension
+ void setLimit(int limit) { insert(limitKey, limit); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CompletionItem : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ /**
+ * The label of this completion item. By default also the text that is inserted when selecting
+ * this completion.
+ */
+ QString label() const { return typedValue<QString>(labelKey); }
+ void setLabel(const QString &label) { insert(labelKey, label); }
+
+ /// The kind of this completion item. Based of the kind an icon is chosen by the editor.
+ std::optional<int> kind() const { return optionalValue<int>(kindKey); }
+ void setKind(int kind) { insert(kindKey, kind); }
+ void clearKind() { remove(kindKey); }
+
+ /// A human-readable string with additional information about this item, like type information.
+ std::optional<QString> detail() const { return optionalValue<QString>(detailKey); }
+ void setDetail(const QString &detail) { insert(detailKey, detail); }
+ void clearDetail() { remove(detailKey); }
+
+ /// A human-readable string that represents a doc-comment.
+ std::optional<MarkupOrString> documentation() const;
+ void setDocumentation(const MarkupOrString &documentation)
+ { insert(documentationKey, documentation.toJson()); }
+ void cleatDocumentation() { remove(documentationKey); }
+
+ /// A string that should be used when comparing this item
+ /// with other items. When `falsy` the label is used.
+ std::optional<QString> sortText() const { return optionalValue<QString>(sortTextKey); }
+ void setSortText(const QString &sortText) { insert(sortTextKey, sortText); }
+ void clearSortText() { remove(sortTextKey); }
+
+ /// A string that should be used when filtering a set of
+ /// completion items. When `falsy` the label is used.
+ std::optional<QString> filterText() const { return optionalValue<QString>(filterTextKey); }
+ void setFilterText(const QString &filterText) { insert(filterTextKey, filterText); }
+ void clearFilterText() { remove(filterTextKey); }
+
+ /**
+ * A string that should be inserted into a document when selecting
+ * this completion. When `falsy` the label is used.
+ *
+ * The `insertText` is subject to interpretation by the client side.
+ * Some tools might not take the string literally. For example
+ * VS Code when code complete is requested in this example `con<cursor position>`
+ * and a completion item with an `insertText` of `console` is provided it
+ * will only insert `sole`. Therefore it is recommended to use `textEdit` instead
+ * since it avoids additional client side interpretation.
+ *
+ * @deprecated Use textEdit instead.
+ */
+ std::optional<QString> insertText() const { return optionalValue<QString>(insertTextKey); }
+ void setInsertText(const QString &insertText) { insert(insertTextKey, insertText); }
+ void clearInsertText() { remove(insertTextKey); }
+
+ enum InsertTextFormat {
+ /// The primary text to be inserted is treated as a plain string.
+ PlainText = 1,
+
+ /**
+ * The primary text to be inserted is treated as a snippet.
+ *
+ * A snippet can define tab stops and placeholders with `$1`, `$2`
+ * and `${3:foo}`. `$0` defines the final tab stop, it defaults to
+ * the end of the snippet. Placeholders with equal identifiers are linked,
+ * that is typing in one will update others too.
+ */
+ Snippet = 2
+ };
+
+ /// The format of the insert text. The format applies to both the `insertText` property
+ /// and the `newText` property of a provided `textEdit`.
+ std::optional<InsertTextFormat> insertTextFormat() const;
+ void setInsertTextFormat(const InsertTextFormat &insertTextFormat)
+ { insert(insertTextFormatKey, insertTextFormat); }
+ void clearInsertTextFormat() { remove(insertTextFormatKey); }
+
+ /**
+ * An edit which is applied to a document when selecting this completion. When an edit is provided the value of
+ * `insertText` is ignored.
+ *
+ * *Note:* The range of the edit must be a single line range and it must contain the position at which completion
+ * has been requested.
+ */
+ std::optional<TextEdit> textEdit() const { return optionalValue<TextEdit>(textEditKey); }
+ void setTextEdit(const TextEdit &textEdit) { insert(textEditKey, textEdit); }
+ void clearTextEdit() { remove(textEditKey); }
+
+ /**
+ * An optional array of additional text edits that are applied when
+ * selecting this completion. Edits must not overlap with the main edit
+ * nor with themselves.
+ */
+ std::optional<QList<TextEdit>> additionalTextEdits() const
+ { return optionalArray<TextEdit>(additionalTextEditsKey); }
+ void setAdditionalTextEdits(const QList<TextEdit> &additionalTextEdits)
+ { insertArray(additionalTextEditsKey, additionalTextEdits); }
+ void clearAdditionalTextEdits() { remove(additionalTextEditsKey); }
+
+ /**
+ * An optional set of characters that when pressed while this completion is active will accept it first and
+ * then type that character. *Note* that all commit characters should have `length=1` and that superfluous
+ * characters will be ignored.
+ */
+ std::optional<QList<QString>> commitCharacters() const
+ { return optionalArray<QString>(commitCharactersKey); }
+ void setCommitCharacters(const QList<QString> &commitCharacters)
+ { insertArray(commitCharactersKey, commitCharacters); }
+ void clearCommitCharacters() { remove(commitCharactersKey); }
+
+ /**
+ * An optional command that is executed *after* inserting this completion. *Note* that
+ * additional modifications to the current document should be described with the
+ * additionalTextEdits-property.
+ */
+ std::optional<Command> command() const { return optionalValue<Command>(commandKey); }
+ void setCommand(const Command &command) { insert(commandKey, command); }
+ void clearCommand() { remove(commandKey); }
+
+ /**
+ * An data entry field that is preserved on a completion item between
+ * a completion and a completion resolve request.
+ */
+ std::optional<QJsonValue> data() const { return optionalValue<QJsonValue>(dataKey); }
+ void setData(const QJsonValue &data) { insert(dataKey, data); }
+ void clearData() { remove(dataKey); }
+
+ /**
+ * Completion item tags are extra annotations that tweak the rendering of a
+ * completion item.
+ * @since 3.15.0
+ */
+ enum CompletionItemTag {
+ Deprecated = 1,
+ };
+
+ /**
+ * Tags for this completion item.
+ * @since 3.15.0
+ */
+ std::optional<QList<CompletionItemTag>> tags() const;
+
+ /**
+ * Indicates if this item is deprecated.
+ * @deprecated Use `tags` instead if supported.
+ */
+ std::optional<bool> deprecated() const { return optionalValue<bool>(deprecatedKey); }
+
+ bool isValid() const override { return contains(labelKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CompletionList : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ /**
+ * This list it not complete. Further typing should result in recomputing
+ * this list.
+ */
+ bool isIncomplete() const { return typedValue<bool>(isIncompleteKey); }
+ void setIsIncomplete(bool isIncomplete) { insert(isIncompleteKey, isIncomplete); }
+
+ /// The completion items.
+ std::optional<QList<CompletionItem>> items() const { return array<CompletionItem>(itemsKey); }
+ void setItems(const QList<CompletionItem> &items) { insertArray(itemsKey, items); }
+ void clearItems() { remove(itemsKey); }
+
+ bool isValid() const override { return contains(isIncompleteKey); }
+};
+
+/// The result of a completion is CompletionItem[] | CompletionList | null
+class LANGUAGESERVERPROTOCOL_EXPORT CompletionResult
+ : public std::variant<QList<CompletionItem>, CompletionList, std::nullptr_t>
+{
+public:
+ using variant::variant;
+ explicit CompletionResult(const QJsonValue &value);
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CompletionRequest : public Request<
+ CompletionResult, std::nullptr_t, CompletionParams>
+{
+public:
+ explicit CompletionRequest(const CompletionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/completion";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CompletionItemResolveRequest : public Request<
+ CompletionItem, std::nullptr_t, CompletionItem>
+{
+public:
+ explicit CompletionItemResolveRequest(const CompletionItem &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "completionItem/resolve";
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/diagnostics.cpp b/src/shared/lsp/diagnostics.cpp
new file mode 100644
index 000000000..352b1572f
--- /dev/null
+++ b/src/shared/lsp/diagnostics.cpp
@@ -0,0 +1,13 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "diagnostics.h"
+
+namespace lsp {
+
+constexpr const char PublishDiagnosticsNotification::methodName[];
+
+PublishDiagnosticsNotification::PublishDiagnosticsNotification(const PublishDiagnosticsParams &params)
+ : Notification(methodName, params) { }
+
+} // namespace lsp
diff --git a/src/shared/lsp/diagnostics.h b/src/shared/lsp/diagnostics.h
new file mode 100644
index 000000000..ae479a23b
--- /dev/null
+++ b/src/shared/lsp/diagnostics.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+#include "languagefeatures.h"
+
+namespace lsp {
+
+class PublishDiagnosticsParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ QList<Diagnostic> diagnostics() const { return array<Diagnostic>(diagnosticsKey); }
+ void setDiagnostics(const QList<Diagnostic> &diagnostics)
+ { insertArray(diagnosticsKey, diagnostics); }
+
+ std::optional<int> version() const { return optionalValue<int>(versionKey); }
+ void setVersion(int version) { insert(versionKey, version); }
+ void clearVersion() { remove(versionKey); }
+
+ bool isValid() const override { return contains(diagnosticsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT PublishDiagnosticsNotification : public Notification<PublishDiagnosticsParams>
+{
+public:
+ explicit PublishDiagnosticsNotification(const PublishDiagnosticsParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "textDocument/publishDiagnostics";
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/filepath.h b/src/shared/lsp/filepath.h
new file mode 100644
index 000000000..736085828
--- /dev/null
+++ b/src/shared/lsp/filepath.h
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <tools/hostosinfo.h>
+
+#include <QDir>
+#include <QFileInfo>
+#include <QString>
+
+namespace lsp::Utils {
+
+class FilePath : public QString
+{
+public:
+ FilePath() = default;
+ FilePath(const QString &s) : QString(s) {}
+
+ QString scheme() const { return {}; }
+ QString host() const { return {}; }
+ Qt::CaseSensitivity caseSensitivity() const {
+ return qbs::Internal::HostOsInfo::fileNameCaseSensitivity();
+ }
+ QString toString() const { return *this; }
+ QString path() const { return toString(); }
+
+ static FilePath fromUserInput(const QString &s) { return QDir::fromNativeSeparators(s); }
+ QString toUserOutput() const { return QDir::toNativeSeparators(*this); }
+};
+
+} // namespace lsp::Utils
diff --git a/src/shared/lsp/initializemessages.cpp b/src/shared/lsp/initializemessages.cpp
new file mode 100644
index 000000000..db0abcf49
--- /dev/null
+++ b/src/shared/lsp/initializemessages.cpp
@@ -0,0 +1,137 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "initializemessages.h"
+
+#include <QCoreApplication>
+
+namespace lsp {
+
+constexpr const char InitializeRequest::methodName[];
+constexpr const char InitializeNotification::methodName[];
+constexpr Trace::Values s_trace = Trace::off;
+
+Trace Trace::fromString(const QString &val)
+{
+ if (val == "messages")
+ return messages;
+ if (val == "verbose")
+ return verbose;
+ return off;
+}
+
+#define RETURN_CASE(name) case Trace::name: return QString(#name);
+QString Trace::toString() const
+{
+ switch (m_value) {
+ RETURN_CASE(off);
+ RETURN_CASE(messages);
+ RETURN_CASE(verbose);
+ }
+ return QString("off");
+}
+#undef RETURN_CASE
+
+std::optional<QList<MarkupKind>>
+TextDocumentClientCapabilities::CompletionCapabilities::CompletionItemCapbilities::
+documentationFormat() const
+{
+ return optionalArray<MarkupKind>(documentationFormatKey);
+}
+
+void
+TextDocumentClientCapabilities::CompletionCapabilities::CompletionItemCapbilities::
+setDocumentationFormat(const QList<MarkupKind> &documentationFormat)
+{
+ insertArray(documentationFormatKey, documentationFormat);
+}
+
+TextDocumentClientCapabilities::CompletionCapabilities::CompletionItemKindCapabilities::CompletionItemKindCapabilities()
+{
+ setValueSet({CompletionItemKind::Text, CompletionItemKind::Method, CompletionItemKind::Function,
+ CompletionItemKind::Constructor, CompletionItemKind::Field, CompletionItemKind::Variable,
+ CompletionItemKind::Class, CompletionItemKind::Interface, CompletionItemKind::Module,
+ CompletionItemKind::Property, CompletionItemKind::Unit, CompletionItemKind::Value,
+ CompletionItemKind::Enum, CompletionItemKind::Keyword, CompletionItemKind::Snippet,
+ CompletionItemKind::Color, CompletionItemKind::File, CompletionItemKind::Reference,
+ CompletionItemKind::Folder, CompletionItemKind::EnumMember, CompletionItemKind::Constant,
+ CompletionItemKind::Struct, CompletionItemKind::Event, CompletionItemKind::Operator,
+ CompletionItemKind::TypeParameter});
+}
+
+std::optional<QList<CompletionItemKind::Kind>>
+TextDocumentClientCapabilities::CompletionCapabilities::CompletionItemKindCapabilities::
+valueSet() const
+{
+ if (std::optional<QList<int>> array = optionalArray<int>(valueSetKey)) {
+ return std::make_optional(Utils::transform(*array, [](int value) {
+ return static_cast<CompletionItemKind::Kind>(value);
+ }));
+ }
+ return std::nullopt;
+}
+
+void
+TextDocumentClientCapabilities::CompletionCapabilities::CompletionItemKindCapabilities::
+setValueSet(const QList<CompletionItemKind::Kind> &valueSet)
+{
+ insert(valueSetKey, enumArrayToJsonArray<CompletionItemKind::Kind>(valueSet));
+}
+
+std::optional<QList<MarkupKind> > TextDocumentClientCapabilities::HoverCapabilities::contentFormat() const
+{
+ return optionalArray<MarkupKind>(contentFormatKey);
+}
+
+void TextDocumentClientCapabilities::HoverCapabilities::setContentFormat(const QList<MarkupKind> &contentFormat)
+{
+ insertArray(contentFormatKey, contentFormat);
+}
+
+std::optional<QList<MarkupKind>>
+TextDocumentClientCapabilities::SignatureHelpCapabilities::SignatureInformationCapabilities::
+documentationFormat() const
+{
+ return optionalArray<MarkupKind>(documentationFormatKey);
+}
+
+void
+TextDocumentClientCapabilities::SignatureHelpCapabilities::SignatureInformationCapabilities::
+setDocumentationFormat(const QList<MarkupKind> &documentationFormat)
+{
+ insertArray(documentationFormatKey, documentationFormat);
+}
+
+InitializeParams::InitializeParams()
+{
+ setProcessId(int(QCoreApplication::applicationPid()));
+ setRootUri(LanguageClientValue<DocumentUri>());
+ setCapabilities(ClientCapabilities());
+ setTrace(s_trace);
+}
+
+std::optional<QJsonObject> InitializeParams::initializationOptions() const
+{
+ const QJsonValue &optionsValue = value(initializationOptionsKey);
+ if (optionsValue.isObject())
+ return optionsValue.toObject();
+ return std::nullopt;
+}
+
+std::optional<Trace> InitializeParams::trace() const
+{
+ const QJsonValue &traceValue = value(traceKey);
+ if (traceValue.isUndefined())
+ return std::nullopt;
+ return std::make_optional(Trace(traceValue.toString()));
+}
+
+InitializeRequest::InitializeRequest(const InitializeParams &params)
+ : Request(methodName, params)
+{ }
+
+InitializeNotification::InitializeNotification(const InitializedParams &params)
+ : Notification(methodName, params)
+{ }
+
+} // namespace lsp
diff --git a/src/shared/lsp/initializemessages.h b/src/shared/lsp/initializemessages.h
new file mode 100644
index 000000000..159d62ded
--- /dev/null
+++ b/src/shared/lsp/initializemessages.h
@@ -0,0 +1,187 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "clientcapabilities.h"
+#include "jsonrpcmessages.h"
+#include "lsptypes.h"
+#include "servercapabilities.h"
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT Trace
+{
+public:
+ enum Values
+ {
+ off,
+ messages,
+ verbose
+ };
+
+ Trace() = default;
+ Trace(Values val) : m_value(val) {}
+ Trace(QString val) : Trace(fromString(val)) {}
+
+ static Trace fromString(const QString& val);
+ QString toString() const;
+
+private:
+ Values m_value = off;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ClientInfo : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString name() const { return typedValue<QString>(nameKey); }
+ void setName(const QString &name) { insert(nameKey, name); }
+
+ std::optional<QString> version() const { return optionalValue<QString>(versionKey); }
+ void setVersion(const QString &version) { insert(versionKey, version); }
+ void clearVersion() { remove(versionKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT InitializeParams : public JsonObject
+{
+public:
+ InitializeParams();
+ using JsonObject::JsonObject;
+
+ /*
+ * The process Id of the parent process that started
+ * the server. Is null if the process has not been started by another process.
+ * If the parent process is not alive then the server should exit (see exit notification)
+ * its process.
+ */
+ LanguageClientValue<int> processId() const { return clientValue<int>(processIdKey); }
+ void setProcessId(const LanguageClientValue<int> &id) { insert(processIdKey, id); }
+
+ /*
+ * The rootPath of the workspace. Is null
+ * if no folder is open.
+ *
+ * @deprecated in favor of rootUri.
+ */
+ std::optional<LanguageClientValue<QString>> rootPath() const
+ { return optionalClientValue<QString>(rootPathKey); }
+ void setRootPath(const LanguageClientValue<QString> &path)
+ { insert(rootPathKey, path); }
+ void clearRootPath() { remove(rootPathKey); }
+
+ /*
+ * The rootUri of the workspace. Is null if no
+ * folder is open. If both `rootPath` and `rootUri` are set
+ * `rootUri` wins.
+ */
+ LanguageClientValue<DocumentUri> rootUri() const
+ { return clientValue<QString>(rootUriKey).transform<DocumentUri>(); }
+ void setRootUri(const LanguageClientValue<DocumentUri> &uri)
+ { insert(rootUriKey, uri); }
+
+ // User provided initialization options.
+ std::optional<QJsonObject> initializationOptions() const;
+ void setInitializationOptions(const QJsonObject &options)
+ { insert(initializationOptionsKey, options); }
+ void clearInitializationOptions() { remove(initializationOptionsKey); }
+
+ // The capabilities provided by the client (editor or tool)
+ ClientCapabilities capabilities() const { return typedValue<ClientCapabilities>(capabilitiesKey); }
+ void setCapabilities(const ClientCapabilities &capabilities)
+ { insert(capabilitiesKey, capabilities); }
+
+ // The initial trace setting. If omitted trace is disabled ('off').
+ std::optional<Trace> trace() const;
+ void setTrace(Trace trace) { insert(traceKey, trace.toString()); }
+ void clearTrace() { remove(traceKey); }
+
+ /*
+ * The workspace folders configured in the client when the server starts.
+ * This property is only available if the client supports workspace folders.
+ * It can be `null` if the client supports workspace folders but none are
+ * configured.
+ *
+ * Since 3.6.0
+ */
+ std::optional<LanguageClientArray<WorkSpaceFolder>> workspaceFolders() const
+ { return optionalClientArray<WorkSpaceFolder>(workspaceFoldersKey); }
+ void setWorkSpaceFolders(const LanguageClientArray<WorkSpaceFolder> &folders)
+ { insert(workspaceFoldersKey, folders.toJson()); }
+ void clearWorkSpaceFolders() { remove(workspaceFoldersKey); }
+
+ std::optional<ClientInfo> clientInfo() const { return optionalValue<ClientInfo>(clientInfoKey); }
+ void setClientInfo(const ClientInfo &clientInfo) { insert(clientInfoKey, clientInfo); }
+ void clearClientInfo() { remove(clientInfoKey); }
+
+ bool isValid() const override
+ { return contains(processIdKey) && contains(rootUriKey) && contains(capabilitiesKey); }
+};
+
+using InitializedParams = JsonObject;
+
+class LANGUAGESERVERPROTOCOL_EXPORT InitializeNotification : public Notification<InitializedParams>
+{
+public:
+ explicit InitializeNotification(const InitializedParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "initialized";
+
+ bool parametersAreValid(QString * /*errorMessage*/) const final { return true; }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ServerInfo : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString name() const { return typedValue<QString>(nameKey); }
+ std::optional<QString> version() const { return optionalValue<QString>(versionKey); }
+
+ bool isValid() const override { return contains(nameKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT InitializeResult : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ ServerCapabilities capabilities() const
+ { return typedValue<ServerCapabilities>(capabilitiesKey); }
+ void setCapabilities(const ServerCapabilities &capabilities)
+ { insert(capabilitiesKey, capabilities); }
+
+ std::optional<ServerInfo> serverInfo() const
+ { return optionalValue<ServerInfo>(serverInfoKey); }
+
+ bool isValid() const override { return contains(capabilitiesKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT InitializeError : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ /*
+ * Indicates whether the client execute the following retry logic:
+ * (1) show the message provided by the ResponseError to the user
+ * (2) user selects retry or cancel
+ * (3) if user selected retry the initialize method is sent again.
+ */
+ bool retry() const { return typedValue<bool>(retryKey); }
+ void setRetry(const bool &retry) { insert(retryKey, retry); }
+
+ bool isValid() const override { return contains(retryKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT InitializeRequest : public Request<
+ InitializeResult, InitializeError, InitializeParams>
+{
+public:
+ explicit InitializeRequest(const InitializeParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "initialize";
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/jsonkeys.h b/src/shared/lsp/jsonkeys.h
new file mode 100644
index 000000000..5e5491caa
--- /dev/null
+++ b/src/shared/lsp/jsonkeys.h
@@ -0,0 +1,231 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace lsp {
+
+constexpr char actionsKey[] = "actions";
+constexpr char activeParameterKey[] = "activeParameter";
+constexpr char activeParameterSupportKey[] = "activeParameterSupport";
+constexpr char activeSignatureKey[] = "activeSignature";
+constexpr char addedKey[] = "added";
+constexpr char additionalTextEditsKey[] = "additionalTextEdits";
+constexpr char alphaKey[] = "alpha";
+constexpr char appliedKey[] = "applied";
+constexpr char applyEditKey[] = "applyEdit";
+constexpr char argumentsKey[] = "arguments";
+constexpr char blueKey[] = "blue";
+constexpr char callHierarchyKey[] = "callHierarchy";
+constexpr char callHierarchyProviderKey[] = "callHierarchyProvider";
+constexpr char cancellableKey[] = "cancellable";
+constexpr char capabilitiesKey[] = "capabilities";
+constexpr char chKey[] = "ch";
+constexpr char changeKey[] = "change";
+constexpr char changeNotificationsKey[] = "changeNotifications";
+constexpr char changesKey[] = "changes";
+constexpr char characterKey[] = "character";
+constexpr char childrenKey[] = "children";
+constexpr char clientInfoKey[] = "clientInfo";
+constexpr char codeActionKey[] = "codeAction";
+constexpr char codeActionKindKey[] = "codeActionKind";
+constexpr char codeActionKindsKey[] = "codeActionKinds";
+constexpr char codeActionLiteralSupportKey[] = "codeActionLiteralSupport";
+constexpr char codeActionProviderKey[] = "codeActionProvider";
+constexpr char codeKey[] = "code";
+constexpr char codeLensKey[] = "codeLens";
+constexpr char codeLensProviderKey[] = "codeLensProvider";
+constexpr char colorInfoKey[] = "colorInfo";
+constexpr char colorKey[] = "color";
+constexpr char colorProviderKey[] = "colorProvider";
+constexpr char commandKey[] = "command";
+constexpr char commandsKey[] = "commands";
+constexpr char commitCharacterSupportKey[] = "commitCharacterSupport";
+constexpr char commitCharactersKey[] = "commitCharacters";
+constexpr char completionItemKey[] = "completionItem";
+constexpr char completionItemKindKey[] = "completionItemKind";
+constexpr char completionKey[] = "completion";
+constexpr char completionProviderKey[] = "completionProvider";
+constexpr char configurationKey[] = "configuration";
+constexpr char containerNameKey[] = "containerName";
+constexpr char contentChangesKey[] = "contentChanges";
+constexpr char contentFormatKey[] = "contentFormat";
+constexpr char contentKey[] = "value";
+constexpr char contentsKey[] = "contents";
+constexpr char contextKey[] = "context";
+constexpr char contextSupportKey[] = "contextSupport";
+constexpr char dataKey[] = "data";
+constexpr char definitionKey[] = "definition";
+constexpr char definitionProviderKey[] = "definitionProvider";
+constexpr char deleteCountKey[] = "deleteCount";
+constexpr char deltaKey[] = "delta";
+constexpr char deprecatedKey[] = "deprecated";
+constexpr char detailKey[] = "detail";
+constexpr char diagnosticsKey[] = "diagnostics";
+constexpr char didChangeConfigurationKey[] = "didChangeConfiguration";
+constexpr char didChangeWatchedFilesKey[] = "didChangeWatchedFiles";
+constexpr char didSaveKey[] = "didSave";
+constexpr char documentChangesKey[] = "documentChanges";
+constexpr char documentFormattingProviderKey[] = "documentFormattingProvider";
+constexpr char documentHighlightKey[] = "documentHighlight";
+constexpr char documentHighlightProviderKey[] = "documentHighlightProvider";
+constexpr char documentLinkKey[] = "documentLink";
+constexpr char documentLinkProviderKey[] = "documentLinkProvider";
+constexpr char documentRangeFormattingProviderKey[] = "documentRangeFormattingProvider";
+constexpr char documentSelectorKey[] = "documentSelector";
+constexpr char documentSymbolKey[] = "documentSymbol";
+constexpr char documentSymbolProviderKey[] = "documentSymbolProvider";
+constexpr char documentationFormatKey[] = "documentationFormat";
+constexpr char documentationKey[] = "documentation";
+constexpr char dynamicRegistrationKey[] = "dynamicRegistration";
+constexpr char editKey[] = "edit";
+constexpr char editsKey[] = "edits";
+constexpr char endKey[] = "end";
+constexpr char errorKey[] = "error";
+constexpr char eventKey[] = "event";
+constexpr char executeCommandKey[] = "executeCommand";
+constexpr char executeCommandProviderKey[] = "executeCommandProvider";
+constexpr char experimentalKey[] = "experimental";
+constexpr char filterTextKey[] = "filterText";
+constexpr char firstTriggerCharacterKey[] = "firstTriggerCharacter";
+constexpr char formatsKey[] = "formats";
+constexpr char formattingKey[] = "formatting";
+constexpr char fromKey[] = "from";
+constexpr char fromRangesKey[] = "fromRanges";
+constexpr char fullKey[] = "full";
+constexpr char greenKey[] = "green";
+constexpr char hierarchicalDocumentSymbolSupportKey[] = "hierarchicalDocumentSymbolSupport";
+constexpr char hoverKey[] = "hover";
+constexpr char hoverProviderKey[] = "hoverProvider";
+constexpr char idKey[] = "id";
+constexpr char ignoreIfExistsKey[] = "ignoreIfExists";
+constexpr char ignoreIfNotExistsKey[] = "ignoreIfNotExists";
+constexpr char implementationKey[] = "implementation";
+constexpr char implementationProviderKey[] = "implementationProvider";
+constexpr char includeDeclarationKey[] = "includeDeclaration";
+constexpr char includeTextKey[] = "includeText";
+constexpr char initializationOptionsKey[] = "initializationOptions";
+constexpr char insertFinalNewlineKey[] = "insertFinalNewline";
+constexpr char insertSpaceKey[] = "insertSpace";
+constexpr char insertTextFormatKey[] = "insertTextFormat";
+constexpr char insertTextKey[] = "insertText";
+constexpr char isIncompleteKey[] = "isIncomplete";
+constexpr char itemKey[] = "item";
+constexpr char itemsKey[] = "items";
+constexpr char jsonRpcVersionKey[] = "jsonrpc";
+constexpr char kindKey[] = "kind";
+constexpr char labelKey[] = "label";
+constexpr char languageIdKey[] = "languageId";
+constexpr char languageKey[] = "language";
+constexpr char legendKey[] = "legend";
+constexpr char limitKey[] = "limit";
+constexpr char lineKey[] = "line";
+constexpr char linesKey[] = "lines";
+constexpr char locationKey[] = "location";
+constexpr char messageKey[] = "message";
+constexpr char methodKey[] = "method";
+constexpr char moreTriggerCharacterKey[] = "moreTriggerCharacter";
+constexpr char multiLineTokenSupportKey[] = "multiLineTokenSupport";
+constexpr char nameKey[] = "name";
+constexpr char newNameKey[] = "newName";
+constexpr char newTextKey[] = "newText";
+constexpr char newUriKey[] = "newUri";
+constexpr char oldUriKey[] = "oldUri";
+constexpr char onTypeFormattingKey[] = "onTypeFormatting";
+constexpr char onlyKey[] = "only";
+constexpr char openCloseKey[] = "openClose";
+constexpr char optionsKey[] = "options";
+constexpr char overlappingTokenSupportKey[] = "overlappingTokenSupport";
+constexpr char overwriteKey[] = "overwrite";
+constexpr char parametersKey[] = "parameters";
+constexpr char paramsKey[] = "params";
+constexpr char patternKey[] = "pattern";
+constexpr char percentageKey[] = "percentage";
+constexpr char placeHolderKey[] = "placeHolder";
+constexpr char positionKey[] = "position";
+constexpr char prepareProviderKey[] = "prepareProvider";
+constexpr char prepareSupportKey[] = "prepareSupport";
+constexpr char previousResultIdKey[] = "previousResultId";
+constexpr char processIdKey[] = "processId";
+constexpr char queryKey[] = "query";
+constexpr char rangeFormattingKey[] = "rangeFormatting";
+constexpr char rangeKey[] = "range";
+constexpr char rangeLengthKey[] = "rangeLength";
+constexpr char reasonKey[] = "reason";
+constexpr char recursiveKey[] = "recursive";
+constexpr char redKey[] = "red";
+constexpr char referencesKey[] = "references";
+constexpr char referencesProviderKey[] = "referencesProvider";
+constexpr char refreshSupportKey[] = "refreshSupport";
+constexpr char registerOptionsKey[] = "registerOptions";
+constexpr char registrationsKey[] = "registrations";
+constexpr char removedKey[] = "removed";
+constexpr char renameKey[] = "rename";
+constexpr char renameProviderKey[] = "renameProvider";
+constexpr char requestsKey[] = "requests";
+constexpr char resolveProviderKey[] = "resolveProvider";
+constexpr char resourceOperationsKey[] = "resourceOperations";
+constexpr char resultIdKey[] = "resultId";
+constexpr char resultKey[] = "result";
+constexpr char retryKey[] = "retry";
+constexpr char rootPathKey[] = "rootPath";
+constexpr char rootUriKey[] = "rootUri";
+constexpr char saveKey[] = "save";
+constexpr char schemeKey[] = "scheme";
+constexpr char scopeUriKey[] = "scopeUri";
+constexpr char sectionKey[] = "section";
+constexpr char selectionRangeKey[] = "selectionRange";
+constexpr char semanticTokensKey[] = "semanticTokens";
+constexpr char semanticTokensProviderKey[] = "semanticTokensProvider";
+constexpr char serverInfoKey[] = "serverInfo";
+constexpr char settingsKey[] = "settings";
+constexpr char severityKey[] = "severity";
+constexpr char signatureHelpKey[] = "signatureHelp";
+constexpr char signatureHelpProviderKey[] = "signatureHelpProvider";
+constexpr char signatureInformationKey[] = "signatureInformation";
+constexpr char signaturesKey[] = "signatures";
+constexpr char snippetSupportKey[] = "snippetSupport";
+constexpr char sortTextKey[] = "sortText";
+constexpr char sourceKey[] = "source";
+constexpr char startKey[] = "start";
+constexpr char supportedKey[] = "supported";
+constexpr char symbolKey[] = "symbol";
+constexpr char symbolKindKey[] = "symbolKind";
+constexpr char syncKindKey[] = "syncKind";
+constexpr char synchronizationKey[] = "synchronization";
+constexpr char tabSizeKey[] = "tabSize";
+constexpr char tagsKey[] = "tags";
+constexpr char targetKey[] = "target";
+constexpr char textDocumentKey[] = "textDocument";
+constexpr char textDocumentSyncKey[] = "textDocumentSync";
+constexpr char textEditKey[] = "textEdit";
+constexpr char textKey[] = "text";
+constexpr char titleKey[] = "title";
+constexpr char toKey[] = "to";
+constexpr char tokenKey[] = "token";
+constexpr char tokenModifiersKey[] = "tokenModifiers";
+constexpr char tokenTypesKey[] = "tokenTypes";
+constexpr char traceKey[] = "trace";
+constexpr char triggerCharacterKey[] = "triggerCharacter";
+constexpr char triggerCharactersKey[] = "triggerCharacters";
+constexpr char triggerKindKey[] = "triggerKind";
+constexpr char trimFinalNewlinesKey[] = "trimFinalNewlines";
+constexpr char trimTrailingWhitespaceKey[] = "trimTrailingWhitespace";
+constexpr char typeDefinitionKey[] = "typeDefinition";
+constexpr char typeDefinitionProviderKey[] = "typeDefinitionProvider";
+constexpr char typeKey[] = "type";
+constexpr char unregistrationsKey[] = "unregistrations";
+constexpr char uriKey[] = "uri";
+constexpr char valueKey[] = "value";
+constexpr char valueSetKey[] = "valueSet";
+constexpr char versionKey[] = "version";
+constexpr char willSaveKey[] = "willSave";
+constexpr char willSaveWaitUntilKey[] = "willSaveWaitUntil";
+constexpr char windowKey[] = "window";
+constexpr char workDoneProgressKey[] = "workDoneProgress";
+constexpr char workspaceEditKey[] = "workspaceEdit";
+constexpr char workspaceFoldersKey[] = "workspaceFolders";
+constexpr char workspaceKey[] = "workspace";
+constexpr char workspaceSymbolProviderKey[] = "workspaceSymbolProvider";
+
+} // namespace lsp
diff --git a/src/shared/lsp/jsonobject.cpp b/src/shared/lsp/jsonobject.cpp
new file mode 100644
index 000000000..887d79a5e
--- /dev/null
+++ b/src/shared/lsp/jsonobject.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "jsonobject.h"
+
+#include <QCoreApplication>
+
+namespace lsp {
+
+JsonObject &JsonObject::operator=(const JsonObject &other) = default;
+
+JsonObject &JsonObject::operator=(JsonObject &&other)
+{
+ m_jsonObject.swap(other.m_jsonObject);
+ return *this;
+}
+
+QJsonObject::iterator JsonObject::insert(const std::string_view key, const JsonObject &object)
+{
+ return m_jsonObject.insert(QLatin1String(key.data()), object.m_jsonObject);
+}
+
+QJsonObject::iterator JsonObject::insert(const std::string_view key, const QJsonValue &value)
+{
+ return m_jsonObject.insert(QLatin1String(key.data()), value);
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/jsonobject.h b/src/shared/lsp/jsonobject.h
new file mode 100644
index 000000000..29811839f
--- /dev/null
+++ b/src/shared/lsp/jsonobject.h
@@ -0,0 +1,181 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "algorithm.h"
+#include "languageserverprotocol_global.h"
+#include "lsputils.h"
+
+#include <QCoreApplication>
+#include <QDebug>
+#include <QJsonObject>
+
+#include <optional>
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT JsonObject
+{
+public:
+ using iterator = QJsonObject::iterator;
+ using const_iterator = QJsonObject::const_iterator;
+
+ JsonObject() = default;
+
+ explicit JsonObject(const QJsonObject &object) : m_jsonObject(object) { }
+ explicit JsonObject(QJsonObject &&object) : m_jsonObject(std::move(object)) { }
+
+ JsonObject(const JsonObject &object) : m_jsonObject(object.m_jsonObject) { }
+ JsonObject(JsonObject &&object) : m_jsonObject(std::move(object.m_jsonObject)) { }
+
+ explicit JsonObject(const QJsonValue &value) : m_jsonObject(value.toObject()) { }
+
+ virtual ~JsonObject() = default;
+
+ JsonObject &operator=(const JsonObject &other);
+ JsonObject &operator=(JsonObject &&other);
+
+ bool operator==(const JsonObject &other) const { return m_jsonObject == other.m_jsonObject; }
+
+ operator const QJsonObject&() const { return m_jsonObject; }
+
+ virtual bool isValid() const { return true; }
+
+ iterator end() { return m_jsonObject.end(); }
+ const_iterator end() const { return m_jsonObject.end(); }
+
+ iterator insert(const std::string_view key, const JsonObject &value);
+ iterator insert(const std::string_view key, const QJsonValue &value);
+
+protected:
+
+ template <typename T, typename V>
+ iterator insertVariant(const std::string_view key, const V &variant);
+ template <typename T1, typename T2, typename... Args, typename V>
+ iterator insertVariant(const std::string_view key, const V &variant);
+
+ // QJSonObject redirections
+ QJsonValue value(const std::string_view key) const { return m_jsonObject.value(QLatin1String(key.data())); }
+ bool contains(const std::string_view key) const { return m_jsonObject.contains(QLatin1String(key.data())); }
+ iterator find(const std::string_view key) { return m_jsonObject.find(QLatin1String(key.data())); }
+ const_iterator find(const std::string_view key) const { return m_jsonObject.find(QLatin1String(key.data())); }
+ void remove(const std::string_view key) { m_jsonObject.remove(QLatin1String(key.data())); }
+ QStringList keys() const { return m_jsonObject.keys(); }
+
+ // convenience value access
+ template<typename T>
+ T typedValue(const std::string_view key) const;
+ template<typename T>
+ std::optional<T> optionalValue(const std::string_view key) const;
+ template<typename T>
+ LanguageClientValue<T> clientValue(const std::string_view key) const;
+ template<typename T>
+ std::optional<LanguageClientValue<T>> optionalClientValue(const std::string_view key) const;
+ template<typename T>
+ QList<T> array(const std::string_view key) const;
+ template<typename T>
+ std::optional<QList<T>> optionalArray(const std::string_view key) const;
+ template<typename T>
+ LanguageClientArray<T> clientArray(const std::string_view key) const;
+ template<typename T>
+ std::optional<LanguageClientArray<T>> optionalClientArray(const std::string_view key) const;
+ template<typename T>
+ void insertArray(const std::string_view key, const QList<T> &array);
+ template<typename>
+ void insertArray(const std::string_view key, const QList<JsonObject> &array);
+
+private:
+ QJsonObject m_jsonObject;
+};
+
+template<typename T, typename V>
+JsonObject::iterator JsonObject::insertVariant(const std::string_view key, const V &variant)
+{
+ return std::holds_alternative<T>(variant) ? insert(key, std::get<T>(variant)) : end();
+}
+
+template<typename T1, typename T2, typename... Args, typename V>
+JsonObject::iterator JsonObject::insertVariant(const std::string_view key, const V &variant)
+{
+ auto result = insertVariant<T1>(key, variant);
+ return result != end() ? result : insertVariant<T2, Args...>(key, variant);
+}
+
+template<typename T>
+T JsonObject::typedValue(const std::string_view key) const
+{
+ return fromJsonValue<T>(value(key));
+}
+
+template<typename T>
+std::optional<T> JsonObject::optionalValue(const std::string_view key) const
+{
+ const QJsonValue &val = value(key);
+ return val.isUndefined() ? std::nullopt : std::make_optional(fromJsonValue<T>(val));
+}
+
+template<typename T>
+LanguageClientValue<T> JsonObject::clientValue(const std::string_view key) const
+{
+ return LanguageClientValue<T>(value(key));
+}
+
+template<typename T>
+std::optional<LanguageClientValue<T>> JsonObject::optionalClientValue(const std::string_view key) const
+{
+ return contains(key) ? std::make_optional(clientValue<T>(key)) : std::nullopt;
+}
+
+template<typename T>
+QList<T> JsonObject::array(const std::string_view key) const
+{
+ if (const std::optional<QList<T>> &array = optionalArray<T>(key))
+ return *array;
+ qCDebug(conversionLog) << QString("Expected array under %1 in:")
+ .arg(QLatin1String(key.data())) << *this;
+ return {};
+}
+
+template<typename T>
+std::optional<QList<T>> JsonObject::optionalArray(const std::string_view key) const
+{
+ const QJsonValue &jsonValue = value(key);
+ if (jsonValue.isUndefined())
+ return std::nullopt;
+ return Utils::transform<QList<T>>(jsonValue.toArray(), &fromJsonValue<T>);
+}
+
+template<typename T>
+LanguageClientArray<T> JsonObject::clientArray(const std::string_view key) const
+{
+ return LanguageClientArray<T>(value(key));
+}
+
+template<typename T>
+std::optional<LanguageClientArray<T>> JsonObject::optionalClientArray(const std::string_view key) const
+{
+ const QJsonValue &val = value(key);
+ return !val.isUndefined() ? std::make_optional(LanguageClientArray<T>(value(key)))
+ : std::nullopt;
+}
+
+template<typename T>
+void JsonObject::insertArray(const std::string_view key, const QList<T> &array)
+{
+ QJsonArray jsonArray;
+ for (const T &item : array)
+ jsonArray.append(QJsonValue(item));
+ insert(key, jsonArray);
+}
+
+template<typename >
+void JsonObject::insertArray(const std::string_view key, const QList<JsonObject> &array)
+{
+ QJsonArray jsonArray;
+ for (const JsonObject &item : array)
+ jsonArray.append(item.m_jsonObject);
+ insert(key, jsonArray);
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/jsonrpcmessages.cpp b/src/shared/lsp/jsonrpcmessages.cpp
new file mode 100644
index 000000000..dbe354bc4
--- /dev/null
+++ b/src/shared/lsp/jsonrpcmessages.cpp
@@ -0,0 +1,107 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "jsonrpcmessages.h"
+
+#include "initializemessages.h"
+#include "languageserverprotocoltr.h"
+#include "lsputils.h"
+
+#include <QCoreApplication>
+#include <QObject>
+#include <QJsonDocument>
+#include <QTextCodec>
+
+namespace lsp {
+Q_LOGGING_CATEGORY(timingLog, "qtc.languageserverprotocol.timing", QtWarningMsg)
+
+constexpr const char CancelRequest::methodName[];
+
+QByteArray JsonRpcMessage::toRawData() const
+{
+ return QJsonDocument(m_jsonObject).toJson(QJsonDocument::Compact);
+}
+
+bool JsonRpcMessage::isValid(QString *errorMessage) const
+{
+ if (!m_parseError.isEmpty()) {
+ if (errorMessage)
+ *errorMessage = m_parseError;
+ return false;
+ }
+ return m_jsonObject[jsonRpcVersionKey] == "2.0";
+}
+
+const QJsonObject &JsonRpcMessage::toJsonObject() const
+{
+ return m_jsonObject;
+}
+
+JsonRpcMessage::JsonRpcMessage()
+{
+ // The language server protocol always uses “2.0” as the jsonrpc version
+ m_jsonObject[jsonRpcVersionKey] = "2.0";
+}
+
+constexpr int utf8mib = 106;
+
+static QString docType(const QJsonDocument &doc)
+{
+ if (doc.isArray())
+ return QString("array");
+ if (doc.isEmpty())
+ return QString("empty");
+ if (doc.isNull())
+ return QString("null");
+ if (doc.isObject())
+ return QString("object");
+ return {};
+}
+
+JsonRpcMessage::JsonRpcMessage(const BaseMessage &message)
+{
+ if (message.content.isEmpty())
+ return;
+ QByteArray content;
+ if (message.codec && message.codec->mibEnum() != utf8mib) {
+ QTextCodec *utf8 = QTextCodec::codecForMib(utf8mib);
+ if (utf8)
+ content = utf8->fromUnicode(message.codec->toUnicode(message.content));
+ }
+ if (content.isEmpty())
+ content = message.content;
+ QJsonParseError error = {0, QJsonParseError::NoError};
+ const QJsonDocument doc = QJsonDocument::fromJson(content, &error);
+ if (doc.isObject())
+ m_jsonObject = doc.object();
+ else if (doc.isNull())
+ m_parseError = Tr::tr("Could not parse JSON message: \"%1\".").arg(error.errorString());
+ else
+ m_parseError =
+ Tr::tr("Expected a JSON object, but got a JSON \"%1\" value.").arg(docType(doc));
+}
+
+JsonRpcMessage::JsonRpcMessage(const QJsonObject &jsonObject)
+ : m_jsonObject(jsonObject)
+{ }
+
+JsonRpcMessage::JsonRpcMessage(QJsonObject &&jsonObject)
+ : m_jsonObject(std::move(jsonObject))
+{ }
+
+QByteArray JsonRpcMessage::jsonRpcMimeType()
+{
+ return "application/vscode-jsonrpc";
+}
+
+CancelRequest::CancelRequest(const CancelParameter &params)
+ : Notification(methodName, params)
+{ }
+
+void logElapsedTime(const QString &method, const QElapsedTimer &t)
+{
+ qCDebug(timingLog) << "received server reply to" << method
+ << "after" << t.elapsed() << "ms";
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/jsonrpcmessages.h b/src/shared/lsp/jsonrpcmessages.h
new file mode 100644
index 000000000..a523dd5e1
--- /dev/null
+++ b/src/shared/lsp/jsonrpcmessages.h
@@ -0,0 +1,415 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "basemessage.h"
+#include "jsonkeys.h"
+#include "lsptypes.h"
+
+#include <QDebug>
+#include <QElapsedTimer>
+#include <QHash>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QCoreApplication>
+#include <QUuid>
+
+#include <optional>
+#include <variant>
+
+namespace lsp {
+
+class JsonRpcMessage;
+
+class LANGUAGESERVERPROTOCOL_EXPORT MessageId : public std::variant<int, QString>
+{
+public:
+ MessageId() : variant(QString()) {}
+ explicit MessageId(int id) : variant(id) {}
+ explicit MessageId(const QString &id) : variant(id) {}
+ explicit MessageId(const QJsonValue &value)
+ {
+ if (value.isDouble())
+ emplace<int>(value.toInt());
+ else
+ emplace<QString>(value.toString());
+ }
+
+ operator QJsonValue() const
+ {
+ if (auto id = std::get_if<int>(this))
+ return *id;
+ if (auto id = std::get_if<QString>(this))
+ return *id;
+ return QJsonValue();
+ }
+
+ bool isValid() const
+ {
+ if (std::holds_alternative<int>(*this))
+ return true;
+ const QString *id = std::get_if<QString>(this);
+ QBS_ASSERT(id, return false);
+ return !id->isEmpty();
+ }
+
+ QString toString() const
+ {
+ if (auto id = std::get_if<QString>(this))
+ return *id;
+ if (auto id = std::get_if<int>(this))
+ return QString::number(*id);
+ return {};
+ }
+
+private:
+ friend size_t qHash(const MessageId &id)
+ {
+ if (std::holds_alternative<int>(id))
+ return QT_PREPEND_NAMESPACE(qHash(std::get<int>(id)));
+ if (std::holds_alternative<QString>(id))
+ return QT_PREPEND_NAMESPACE(qHash(std::get<QString>(id)));
+ return QT_PREPEND_NAMESPACE(qHash(0));
+ }
+
+ friend QDebug operator<<(QDebug stream, const MessageId &id)
+ {
+ if (std::holds_alternative<int>(id))
+ stream << std::get<int>(id);
+ else
+ stream << std::get<QString>(id);
+ return stream;
+ }
+};
+
+struct ResponseHandler
+{
+ MessageId id;
+ using Callback = std::function<void(const JsonRpcMessage &)>;
+ Callback callback;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT JsonRpcMessage
+{
+public:
+ JsonRpcMessage();
+ explicit JsonRpcMessage(const BaseMessage &message);
+ explicit JsonRpcMessage(const QJsonObject &jsonObject);
+ explicit JsonRpcMessage(QJsonObject &&jsonObject);
+
+ virtual ~JsonRpcMessage() = default;
+
+ static QByteArray jsonRpcMimeType();
+
+ QByteArray toRawData() const;
+ virtual bool isValid(QString *errorMessage) const;
+
+ const QJsonObject &toJsonObject() const;
+
+ const QString parseError() { return m_parseError; }
+
+ virtual std::optional<ResponseHandler> responseHandler() const { return std::nullopt; }
+
+ BaseMessage toBaseMessage() const
+ { return BaseMessage(jsonRpcMimeType(), toRawData()); }
+
+protected:
+ QJsonObject m_jsonObject;
+
+private:
+ QString m_parseError;
+};
+
+template <typename Params>
+class Notification : public JsonRpcMessage
+{
+public:
+ Notification(const QString &methodName, const Params &params)
+ {
+ setMethod(methodName);
+ setParams(params);
+ }
+
+ explicit Notification(const QJsonObject &jsonObject) : JsonRpcMessage(jsonObject) {}
+ explicit Notification(QJsonObject &&jsonObject) : JsonRpcMessage(std::move(jsonObject)) {}
+
+ QString method() const
+ { return fromJsonValue<QString>(m_jsonObject.value(methodKey)); }
+ void setMethod(const QString &method)
+ { m_jsonObject.insert(methodKey, method); }
+
+ using Parameters = Params;
+ std::optional<Params> params() const
+ {
+ const QJsonValue &params = m_jsonObject.value(paramsKey);
+ return params.isUndefined() ? std::nullopt : std::make_optional(Params(params));
+ }
+ void setParams(const Params &params)
+ { m_jsonObject.insert(paramsKey, QJsonValue(params)); }
+ void clearParams() { m_jsonObject.remove(paramsKey); }
+
+ bool isValid(QString *errorMessage) const override
+ {
+ return JsonRpcMessage::isValid(errorMessage)
+ && m_jsonObject.value(methodKey).isString()
+ && parametersAreValid(errorMessage);
+ }
+
+ virtual bool parametersAreValid(QString *errorMessage) const
+ {
+ if (auto parameter = params())
+ return parameter->isValid();
+ if (errorMessage)
+ *errorMessage = QCoreApplication::translate("QtC::LanguageServerProtocol",
+ "No parameters in \"%1\".").arg(method());
+ return false;
+ }
+};
+
+template <>
+class Notification<std::nullptr_t> : public JsonRpcMessage
+{
+public:
+ Notification() : Notification(QString()) {}
+ explicit Notification(const QString &methodName, const std::nullptr_t &/*params*/ = nullptr)
+ {
+ setMethod(methodName);
+ setParams(nullptr);
+ }
+ using JsonRpcMessage::JsonRpcMessage;
+
+ QString method() const
+ { return fromJsonValue<QString>(m_jsonObject.value(methodKey)); }
+ void setMethod(const QString &method)
+ { m_jsonObject.insert(methodKey, method); }
+
+ std::optional<std::nullptr_t> params() const
+ { return nullptr; }
+ void setParams(const std::nullptr_t &/*params*/)
+ { m_jsonObject.insert(paramsKey, QJsonValue::Null); }
+ void clearParams() { m_jsonObject.remove(paramsKey); }
+
+ bool isValid(QString *errorMessage) const override
+ {
+ return JsonRpcMessage::isValid(errorMessage)
+ && m_jsonObject.value(methodKey).isString()
+ && parametersAreValid(errorMessage);
+ }
+
+ virtual bool parametersAreValid(QString * /*errorMessage*/) const
+ { return true; }
+};
+
+template <typename Error>
+class ResponseError : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ int code() const { return typedValue<int>(codeKey); }
+ void setCode(int code) { insert(codeKey, code); }
+
+ QString message() const { return typedValue<QString>(messageKey); }
+ void setMessage(const QString &message) { insert(messageKey, message); }
+
+ std::optional<Error> data() const { return optionalValue<Error>(dataKey); }
+ void setData(const Error &data) { insert(dataKey, data); }
+ void clearData() { remove(dataKey); }
+
+ bool isValid() const override { return contains(codeKey) && contains(messageKey); }
+
+ QString toString() const { return errorCodesToString(code()) + ": " + message(); }
+
+ // predefined error codes
+ enum ErrorCodes {
+ // Defined by JSON RPC
+ ParseError = -32700,
+ InvalidRequest = -32600,
+ MethodNotFound = -32601,
+ InvalidParams = -32602,
+ InternalError = -32603,
+ serverErrorStart = -32099,
+ serverErrorEnd = -32000,
+ ServerNotInitialized = -32002,
+ UnknownErrorCode = -32001,
+
+ // Defined by the protocol.
+ RequestCancelled = -32800
+ };
+
+#define CASE_ERRORCODES(x) case ErrorCodes::x: return QLatin1String(#x)
+ static QString errorCodesToString(int code)
+ {
+ switch (code) {
+ CASE_ERRORCODES(ParseError);
+ CASE_ERRORCODES(InvalidRequest);
+ CASE_ERRORCODES(MethodNotFound);
+ CASE_ERRORCODES(InvalidParams);
+ CASE_ERRORCODES(InternalError);
+ CASE_ERRORCODES(serverErrorStart);
+ CASE_ERRORCODES(serverErrorEnd);
+ CASE_ERRORCODES(ServerNotInitialized);
+ CASE_ERRORCODES(RequestCancelled);
+ default:
+ return QCoreApplication::translate("QtC::LanguageClient", "Error %1").arg(code);
+ }
+ }
+#undef CASE_ERRORCODES
+};
+
+template <typename Result, typename ErrorDataType>
+class Response : public JsonRpcMessage
+{
+public:
+ explicit Response(const MessageId &id) { setId(id); }
+ using JsonRpcMessage::JsonRpcMessage;
+
+ MessageId id() const
+ { return MessageId(m_jsonObject.value(idKey)); }
+ void setId(MessageId id)
+ { this->m_jsonObject.insert(idKey, id); }
+
+ std::optional<Result> result() const
+ {
+ const QJsonValue &result = m_jsonObject.value(resultKey);
+ if (result.isUndefined())
+ return std::nullopt;
+ return std::make_optional(Result(result));
+ }
+ void setResult(const Result &result) { m_jsonObject.insert(resultKey, QJsonValue(result)); }
+ void clearResult() { m_jsonObject.remove(resultKey); }
+
+ using Error = ResponseError<ErrorDataType>;
+ std::optional<Error> error() const
+ {
+ const QJsonValue &val = m_jsonObject.value(errorKey);
+ return val.isUndefined() ? std::nullopt : std::make_optional(fromJsonValue<Error>(val));
+ }
+ void setError(const Error &error)
+ {
+ m_jsonObject.insert(errorKey, QJsonValue(error));
+ }
+ void clearError() { m_jsonObject.remove(errorKey); }
+
+ bool isValid(QString *errorMessage) const override
+ { return JsonRpcMessage::isValid(errorMessage) && id().isValid(); }
+};
+
+template<typename ErrorDataType>
+class Response<std::nullptr_t, ErrorDataType> : public JsonRpcMessage
+{
+public:
+ explicit Response(const MessageId &id) { setId(id); }
+ using JsonRpcMessage::JsonRpcMessage;
+
+ MessageId id() const
+ { return MessageId(m_jsonObject.value(idKey)); }
+ void setId(MessageId id)
+ { this->m_jsonObject.insert(idKey, id); }
+
+ std::optional<std::nullptr_t> result() const
+ {
+ return m_jsonObject.value(resultKey).isNull() ? std::make_optional(nullptr) : std::nullopt;
+ }
+ void setResult(const std::nullptr_t &) { m_jsonObject.insert(resultKey, QJsonValue::Null); }
+ void clearResult() { m_jsonObject.remove(resultKey); }
+
+ using Error = ResponseError<ErrorDataType>;
+ std::optional<Error> error() const
+ {
+ const QJsonValue &val = m_jsonObject.value(errorKey);
+ return val.isUndefined() ? std::nullopt : std::make_optional(fromJsonValue<Error>(val));
+ }
+ void setError(const Error &error)
+ { m_jsonObject.insert(errorKey, QJsonValue(error)); }
+ void clearError() { m_jsonObject.remove(errorKey); }
+
+ bool isValid(QString *errorMessage) const override
+ { return JsonRpcMessage::isValid(errorMessage) && id().isValid(); }
+};
+
+void LANGUAGESERVERPROTOCOL_EXPORT logElapsedTime(const QString &method, const QElapsedTimer &t);
+
+template <typename Result, typename ErrorDataType, typename Params>
+class Request : public Notification<Params>
+{
+public:
+ Request(const QString &methodName, const Params &params)
+ : Notification<Params>(methodName, params)
+ { setId(MessageId(QUuid::createUuid().toString())); }
+ explicit Request(const QJsonObject &jsonObject) : Notification<Params>(jsonObject) { }
+ explicit Request(QJsonObject &&jsonObject) : Notification<Params>(std::move(jsonObject)) { }
+
+ MessageId id() const
+ { return MessageId(JsonRpcMessage::m_jsonObject.value(idKey)); }
+ void setId(const MessageId &id)
+ { JsonRpcMessage::m_jsonObject.insert(idKey, id); }
+
+ using Response = lsp::Response<Result, ErrorDataType>;
+ using ResponseCallback = std::function<void(Response)>;
+ void setResponseCallback(const ResponseCallback &callback)
+ { m_callBack = callback; }
+
+ std::optional<ResponseHandler> responseHandler() const final
+ {
+ QElapsedTimer timer;
+ timer.start();
+ auto callback = [callback = m_callBack, method = this->method(), t = std::move(timer)]
+ (const JsonRpcMessage &message) {
+ if (!callback)
+ return;
+ logElapsedTime(method, t);
+
+ callback(Response(message.toJsonObject()));
+ };
+ return std::make_optional(ResponseHandler{id(), callback});
+ }
+
+ bool isValid(QString *errorMessage) const override
+ {
+ if (!Notification<Params>::isValid(errorMessage))
+ return false;
+ if (id().isValid())
+ return true;
+ if (errorMessage)
+ *errorMessage = QCoreApplication::translate("QtC::LanguageServerProtocol",
+ "No ID set in \"%1\".").arg(this->method());
+ return false;
+ }
+
+private:
+ ResponseCallback m_callBack;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CancelParameter : public JsonObject
+{
+public:
+ explicit CancelParameter(const MessageId &id) { setId(id); }
+ CancelParameter() = default;
+ using JsonObject::JsonObject;
+
+ MessageId id() const { return typedValue<MessageId>(idKey); }
+ void setId(const MessageId &id) { insert(idKey, id); }
+
+ bool isValid() const override { return contains(idKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CancelRequest : public Notification<CancelParameter>
+{
+public:
+ explicit CancelRequest(const CancelParameter &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "$/cancelRequest";
+};
+
+} // namespace lsp
+
+template <typename Error>
+inline QDebug operator<<(QDebug stream, const lsp::ResponseError<Error> &error)
+{
+ stream.nospace() << error.toString();
+ return stream;
+}
+
+Q_DECLARE_METATYPE(lsp::JsonRpcMessage)
diff --git a/src/shared/lsp/languagefeatures.cpp b/src/shared/lsp/languagefeatures.cpp
new file mode 100644
index 000000000..8ca771f68
--- /dev/null
+++ b/src/shared/lsp/languagefeatures.cpp
@@ -0,0 +1,399 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "languagefeatures.h"
+
+#include <cstddef>
+
+namespace lsp {
+
+constexpr const char HoverRequest::methodName[];
+constexpr const char GotoDefinitionRequest::methodName[];
+constexpr const char GotoTypeDefinitionRequest::methodName[];
+constexpr const char GotoImplementationRequest::methodName[];
+constexpr const char FindReferencesRequest::methodName[];
+constexpr const char DocumentHighlightsRequest::methodName[];
+constexpr const char DocumentSymbolsRequest::methodName[];
+constexpr const char CodeActionRequest::methodName[];
+constexpr const char CodeLensRequest::methodName[];
+constexpr const char CodeLensResolveRequest::methodName[];
+constexpr const char DocumentLinkRequest::methodName[];
+constexpr const char DocumentLinkResolveRequest::methodName[];
+constexpr const char DocumentColorRequest::methodName[];
+constexpr const char ColorPresentationRequest::methodName[];
+constexpr const char DocumentFormattingRequest::methodName[];
+constexpr const char DocumentRangeFormattingRequest::methodName[];
+constexpr const char DocumentOnTypeFormattingRequest::methodName[];
+constexpr const char RenameRequest::methodName[];
+constexpr const char SignatureHelpRequest::methodName[];
+constexpr const char PrepareRenameRequest::methodName[];
+
+HoverContent Hover::content() const
+{
+ return HoverContent(value(contentsKey));
+}
+
+void Hover::setContent(const HoverContent &content)
+{
+ if (auto val = std::get_if<MarkedString>(&content))
+ insert(contentsKey, *val);
+ else if (auto val = std::get_if<MarkupContent>(&content))
+ insert(contentsKey, *val);
+ else if (auto val = std::get_if<QList<MarkedString>>(&content))
+ insert(contentsKey, LanguageClientArray<MarkedString>(*val).toJson());
+ else
+ QBS_ASSERT("LanguageClient Using unknown type Hover::setContent",;);
+}
+
+HoverRequest::HoverRequest(const TextDocumentPositionParams &params)
+ : Request(methodName, params)
+{ }
+
+std::optional<MarkupOrString> ParameterInformation::documentation() const
+{
+ QJsonValue documentation = value(documentationKey);
+ if (documentation.isUndefined())
+ return std::nullopt;
+ return MarkupOrString(documentation);
+}
+
+GotoDefinitionRequest::GotoDefinitionRequest(const TextDocumentPositionParams &params)
+ : Request(methodName, params)
+{ }
+
+GotoTypeDefinitionRequest::GotoTypeDefinitionRequest(const TextDocumentPositionParams &params)
+ : Request(methodName, params)
+{ }
+
+GotoImplementationRequest::GotoImplementationRequest(const TextDocumentPositionParams &params)
+ : Request(methodName, params)
+{ }
+
+FindReferencesRequest::FindReferencesRequest(const ReferenceParams &params)
+ : Request(methodName, params)
+{ }
+
+DocumentHighlightsRequest::DocumentHighlightsRequest(const TextDocumentPositionParams &params)
+ : Request(methodName, params)
+{ }
+
+DocumentSymbolsRequest::DocumentSymbolsRequest(const DocumentSymbolParams &params)
+ : Request(methodName, params)
+{ }
+
+std::optional<QList<CodeActionKind> > CodeActionParams::CodeActionContext::only() const
+{
+ return optionalArray<CodeActionKind>(onlyKey);
+}
+
+void CodeActionParams::CodeActionContext::setOnly(const QList<CodeActionKind> &only)
+{
+ insertArray(onlyKey, only);
+}
+
+CodeActionRequest::CodeActionRequest(const CodeActionParams &params)
+ : Request(methodName, params)
+{ }
+
+CodeLensRequest::CodeLensRequest(const CodeLensParams &params)
+ : Request(methodName, params)
+{ }
+
+CodeLensResolveRequest::CodeLensResolveRequest(const CodeLens &params)
+ : Request(methodName, params)
+{ }
+
+DocumentLinkRequest::DocumentLinkRequest(const DocumentLinkParams &params)
+ : Request(methodName, params)
+{ }
+
+DocumentLinkResolveRequest::DocumentLinkResolveRequest(const DocumentLink &params)
+ : Request(methodName, params)
+{ }
+
+DocumentColorRequest::DocumentColorRequest(const DocumentColorParams &params)
+ : Request(methodName, params)
+{ }
+
+ColorPresentationRequest::ColorPresentationRequest(const ColorPresentationParams &params)
+ : Request(methodName, params)
+{ }
+
+QHash<QString, DocumentFormattingProperty> FormattingOptions::properties() const
+{
+ QHash<QString, DocumentFormattingProperty> ret;
+ for (const QString &key : keys()) {
+ if (key == tabSizeKey || key == insertSpaceKey)
+ continue;
+ QJsonValue property = value(key.toStdString());
+ if (property.isBool())
+ ret[key] = property.toBool();
+ if (property.isDouble())
+ ret[key] = property.toDouble();
+ if (property.isString())
+ ret[key] = property.toString();
+ }
+ return ret;
+}
+
+void FormattingOptions::setProperty(const std::string_view key, const DocumentFormattingProperty &property)
+{
+ using namespace std;
+ if (auto val = get_if<double>(&property))
+ insert(key, *val);
+ else if (auto val = get_if<QString>(&property))
+ insert(key, *val);
+ else if (auto val = get_if<bool>(&property))
+ insert(key, *val);
+}
+
+DocumentFormattingRequest::DocumentFormattingRequest(const DocumentFormattingParams &params)
+ : Request(methodName, params)
+{ }
+
+DocumentRangeFormattingRequest::DocumentRangeFormattingRequest(
+ const DocumentRangeFormattingParams &params)
+ : Request(methodName, params)
+{ }
+
+bool DocumentOnTypeFormattingParams::isValid() const
+{
+ return contains(textDocumentKey) && contains(positionKey) && contains(chKey)
+ && contains(optionsKey);
+}
+
+DocumentOnTypeFormattingRequest::DocumentOnTypeFormattingRequest(
+ const DocumentOnTypeFormattingParams &params)
+ : Request(methodName, params)
+{ }
+
+PrepareRenameRequest::PrepareRenameRequest(const TextDocumentPositionParams &params)
+ : Request(methodName, params)
+{ }
+
+bool RenameParams::isValid() const
+{
+ return contains(textDocumentKey) && contains(positionKey) && contains(newNameKey);
+}
+
+RenameRequest::RenameRequest(const RenameParams &params)
+ : Request(methodName, params)
+{ }
+
+std::optional<DocumentUri> DocumentLink::target() const
+{
+ if (std::optional<QString> optionalTarget = optionalValue<QString>(targetKey))
+ return std::make_optional(DocumentUri::fromProtocol(*optionalTarget));
+ return std::nullopt;
+}
+
+std::optional<QJsonValue> DocumentLink::data() const
+{
+ return contains(dataKey) ? std::make_optional(value(dataKey)) : std::nullopt;
+}
+
+TextDocumentParams::TextDocumentParams()
+ : TextDocumentParams(TextDocumentIdentifier())
+{ }
+
+TextDocumentParams::TextDocumentParams(const TextDocumentIdentifier &identifier)
+ : JsonObject()
+{
+ setTextDocument(identifier);
+}
+
+GotoResult::GotoResult(const QJsonValue &value)
+{
+ if (value.isArray()) {
+ QList<Location> locations;
+ for (auto arrayValue : value.toArray()) {
+ if (arrayValue.isObject())
+ locations.append(Location(arrayValue.toObject()));
+ }
+ emplace<QList<Location>>(locations);
+ } else if (value.isObject()) {
+ emplace<Location>(value.toObject());
+ } else {
+ emplace<std::nullptr_t>(nullptr);
+ }
+}
+
+template<typename Symbol>
+QList<Symbol> documentSymbolsResultArray(const QJsonArray &array)
+{
+ QList<Symbol> ret;
+ for (const auto &arrayValue : array) {
+ if (arrayValue.isObject())
+ ret << Symbol(arrayValue.toObject());
+ }
+ return ret;
+}
+
+DocumentSymbolsResult::DocumentSymbolsResult(const QJsonValue &value)
+{
+ if (value.isArray()) {
+ QJsonArray array = value.toArray();
+ if (array.isEmpty()) {
+ *this = QList<SymbolInformation>();
+ } else {
+ QJsonObject arrayObject = array.first().toObject();
+ if (arrayObject.contains(rangeKey))
+ *this = documentSymbolsResultArray<DocumentSymbol>(array);
+ else
+ *this = documentSymbolsResultArray<SymbolInformation>(array);
+ }
+ } else {
+ *this = nullptr;
+ }
+}
+
+DocumentHighlightsResult::DocumentHighlightsResult(const QJsonValue &value)
+{
+ if (value.isArray()) {
+ QList<DocumentHighlight> highlights;
+ for (auto arrayValue : value.toArray()) {
+ if (arrayValue.isObject())
+ highlights.append(DocumentHighlight(arrayValue.toObject()));
+ }
+ *this = highlights;
+ } else {
+ *this = nullptr;
+ }
+}
+
+MarkedString::MarkedString(const QJsonValue &value)
+{
+ if (value.isObject())
+ emplace<MarkedLanguageString>(MarkedLanguageString(value.toObject()));
+ else
+ emplace<QString>(value.toString());
+}
+
+bool MarkedString::isValid() const
+{
+ if (auto markedLanguageString = std::get_if<MarkedLanguageString>(this))
+ return markedLanguageString->isValid();
+ return true;
+}
+
+MarkedString::operator QJsonValue() const
+{
+ if (auto val = std::get_if<QString>(this))
+ return *val;
+ if (auto val = std::get_if<MarkedLanguageString>(this))
+ return QJsonValue(*val);
+ return {};
+}
+
+HoverContent::HoverContent(const QJsonValue &value)
+{
+ if (value.isArray()) {
+ emplace<QList<MarkedString>>(LanguageClientArray<MarkedString>(value).toList());
+ } else if (value.isObject()) {
+ const QJsonObject &object = value.toObject();
+ MarkedLanguageString markedLanguageString(object);
+ if (markedLanguageString.isValid())
+ emplace<MarkedString>(markedLanguageString);
+ else
+ emplace<MarkupContent>(MarkupContent(object));
+ } else if (value.isString()) {
+ emplace<MarkedString>(MarkedString(value.toString()));
+ }
+}
+
+bool HoverContent::isValid() const
+{
+ if (std::holds_alternative<MarkedString>(*this))
+ return std::get<MarkedString>(*this).isValid();
+ return true;
+}
+
+DocumentFormattingProperty::DocumentFormattingProperty(const QJsonValue &value)
+{
+ if (value.isBool())
+ *this = value.toBool();
+ if (value.isDouble())
+ *this = value.toDouble();
+ if (value.isString())
+ *this = value.toString();
+}
+
+SignatureHelpRequest::SignatureHelpRequest(const TextDocumentPositionParams &params)
+ : Request(methodName, params)
+{ }
+
+CodeActionResult::CodeActionResult(const QJsonValue &val)
+{
+ using ResultArray = QList<std::variant<Command, CodeAction>>;
+ if (val.isArray()) {
+ const QJsonArray array = val.toArray();
+ ResultArray result;
+ for (const QJsonValue &val : array) {
+ if (val.toObject().value(commandKey).isString()) {
+ const Command command(val);
+ if (command.isValid())
+ result << command;
+ } else {
+ const CodeAction action(val);
+ if (action.isValid())
+ result << action;
+ }
+ }
+ emplace<ResultArray>(result);
+ return;
+ }
+ emplace<std::nullptr_t>(nullptr);
+}
+
+PrepareRenameResult::PrepareRenameResult()
+ : std::variant<PlaceHolderResult, Range, std::nullptr_t>(nullptr)
+{}
+
+PrepareRenameResult::PrepareRenameResult(
+ const std::variant<PlaceHolderResult, Range, std::nullptr_t> &val)
+ : std::variant<PlaceHolderResult, Range, std::nullptr_t>(val)
+{}
+
+PrepareRenameResult::PrepareRenameResult(const PlaceHolderResult &val)
+ : std::variant<PlaceHolderResult, Range, std::nullptr_t>(val)
+
+{}
+
+PrepareRenameResult::PrepareRenameResult(const Range &val)
+ : std::variant<PlaceHolderResult, Range, std::nullptr_t>(val)
+{}
+
+PrepareRenameResult::PrepareRenameResult(const QJsonValue &val)
+{
+ if (val.isNull()) {
+ emplace<std::nullptr_t>(nullptr);
+ } else if (val.isObject()) {
+ const QJsonObject object = val.toObject();
+ if (object.keys().contains(rangeKey))
+ emplace<PlaceHolderResult>(PlaceHolderResult(object));
+ else
+ emplace<Range>(Range(object));
+ }
+}
+
+std::optional<QJsonValue> CodeLens::data() const
+{
+ return contains(dataKey) ? std::make_optional(value(dataKey)) : std::nullopt;
+}
+
+HoverResult::HoverResult(const QJsonValue &value)
+{
+ if (value.isObject())
+ emplace<Hover>(Hover(value.toObject()));
+ else
+ emplace<std::nullptr_t>(nullptr);
+}
+
+bool HoverResult::isValid() const
+{
+ if (auto hover = std::get_if<Hover>(this))
+ return hover->isValid();
+ return true;
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/languagefeatures.h b/src/shared/lsp/languagefeatures.h
new file mode 100644
index 000000000..13e3e9857
--- /dev/null
+++ b/src/shared/lsp/languagefeatures.h
@@ -0,0 +1,836 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+
+namespace lsp {
+
+/**
+ * MarkedString can be used to render human readable text. It is either a markdown string
+ * or a code-block that provides a language and a code snippet. The language identifier
+ * is semantically equal to the optional language identifier in fenced code blocks in GitHub
+ * issues. See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting
+ *
+ * The pair of a language and a value is an equivalent to markdown:
+ * ```${language}
+ * ${value}
+ * ```
+ *
+ * Note that markdown strings will be sanitized - that means html will be escaped.
+* @deprecated use MarkupContent instead.
+*/
+class LANGUAGESERVERPROTOCOL_EXPORT MarkedLanguageString : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString language() const { return typedValue<QString>(languageKey); }
+ void setLanguage(const QString &language) { insert(languageKey, language); }
+
+ QString value() const { return typedValue<QString>(valueKey); }
+ void setValue(const QString &value) { insert(valueKey, value); }
+
+ bool isValid() const override { return contains(languageKey) && contains(valueKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT MarkedString
+ : public std::variant<QString, MarkedLanguageString>
+{
+public:
+ MarkedString() = default;
+ explicit MarkedString(const MarkedLanguageString &other)
+ : variant(other)
+ {}
+ explicit MarkedString(const QString &other)
+ : variant(other)
+ {}
+ explicit MarkedString(const QJsonValue &value);
+
+ bool isValid() const;
+ operator QJsonValue() const;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT HoverContent
+ : public std::variant<MarkedString, QList<MarkedString>, MarkupContent>
+{
+public:
+ HoverContent() = default;
+ explicit HoverContent(const MarkedString &other) : variant(other) {}
+ explicit HoverContent(const QList<MarkedString> &other) : variant(other) {}
+ explicit HoverContent(const MarkupContent &other) : variant(other) {}
+ explicit HoverContent(const QJsonValue &value);
+ bool isValid() const;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT Hover : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ HoverContent content() const;
+ void setContent(const HoverContent &content);
+
+ std::optional<Range> range() const { return optionalValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+ void clearRange() { remove(rangeKey); }
+
+ bool isValid() const override { return contains(contentsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT HoverResult : public std::variant<Hover, std::nullptr_t>
+{
+public:
+ HoverResult() : variant(nullptr) {}
+ explicit HoverResult(const Hover &hover) : variant(hover) {}
+ explicit HoverResult(const QJsonValue &value);
+ bool isValid() const;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT HoverRequest
+ : public Request<HoverResult, std::nullptr_t, TextDocumentPositionParams>
+{
+public:
+ explicit HoverRequest(const TextDocumentPositionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/hover";
+};
+
+/**
+ * Represents a parameter of a callable-signature. A parameter can
+ * have a label and a doc-comment.
+ */
+class LANGUAGESERVERPROTOCOL_EXPORT ParameterInformation : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString label() const { return typedValue<QString>(labelKey); }
+ void setLabel(const QString &label) { insert(labelKey, label); }
+
+ std::optional<MarkupOrString> documentation() const;
+ void setDocumentation(const MarkupOrString &documentation)
+ { insert(documentationKey, documentation.toJson()); }
+ void clearDocumentation() { remove(documentationKey); }
+
+ bool isValid() const override { return contains(labelKey); }
+};
+
+/**
+ * Represents the signature of something callable. A signature
+ * can have a label, like a function-name, a doc-comment, and
+ * a set of parameters.
+ */
+class LANGUAGESERVERPROTOCOL_EXPORT SignatureInformation : public ParameterInformation
+{
+public:
+ using ParameterInformation::ParameterInformation;
+
+ std::optional<QList<ParameterInformation>> parameters() const
+ { return optionalArray<ParameterInformation>(parametersKey); }
+ void setParameters(const QList<ParameterInformation> &parameters)
+ { insertArray(parametersKey, parameters); }
+ void clearParameters() { remove(parametersKey); }
+
+ std::optional<int> activeParameter() const { return optionalValue<int>(activeParameterKey); }
+ void setActiveParameter(int activeParameter) { insert(activeParameterKey, activeParameter); }
+ void clearActiveParameter() { remove(activeParameterKey); }
+};
+
+/**
+ * Signature help represents the signature of something
+ * callable. There can be multiple signature but only one
+ * active and only one active parameter.
+ */
+class LANGUAGESERVERPROTOCOL_EXPORT SignatureHelp : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ /// One or more signatures.
+ QList<SignatureInformation> signatures() const
+ { return array<SignatureInformation>(signaturesKey); }
+ void setSignatures(const QList<SignatureInformation> &signatures)
+ { insertArray(signaturesKey, signatures); }
+
+ /**
+ * The active signature. If omitted or the value lies outside the
+ * range of `signatures` the value defaults to zero or is ignored if
+ * `signatures.length === 0`. Whenever possible implementors should
+ * make an active decision about the active signature and shouldn't
+ * rely on a default value.
+ * In future version of the protocol this property might become
+ * mandatory to better express this.
+ */
+ std::optional<int> activeSignature() const { return optionalValue<int>(activeSignatureKey); }
+ void setActiveSignature(int activeSignature) { insert(activeSignatureKey, activeSignature); }
+ void clearActiveSignature() { remove(activeSignatureKey); }
+
+ /**
+ * The active parameter of the active signature. If omitted or the value
+ * lies outside the range of `signatures[activeSignature].parameters`
+ * defaults to 0 if the active signature has parameters. If
+ * the active signature has no parameters it is ignored.
+ * In future version of the protocol this property might become
+ * mandatory to better express the active parameter if the
+ * active signature does have any.
+ */
+ std::optional<int> activeParameter() const { return optionalValue<int>(activeParameterKey); }
+ void setActiveParameter(int activeParameter) { insert(activeParameterKey, activeParameter); }
+ void clearActiveParameter() { remove(activeParameterKey); }
+
+ bool isValid() const override { return contains(signaturesKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SignatureHelpRequest
+ : public Request<LanguageClientValue<SignatureHelp>, std::nullptr_t, TextDocumentPositionParams>
+{
+public:
+ explicit SignatureHelpRequest(const TextDocumentPositionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/signatureHelp";
+};
+
+/// The result of a goto request can either be a location, a list of locations or null
+class LANGUAGESERVERPROTOCOL_EXPORT GotoResult
+ : public std::variant<Location, QList<Location>, std::nullptr_t>
+{
+public:
+ explicit GotoResult(const QJsonValue &value);
+ using variant::variant;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT GotoDefinitionRequest
+ : public Request<GotoResult, std::nullptr_t, TextDocumentPositionParams>
+{
+public:
+ explicit GotoDefinitionRequest(const TextDocumentPositionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/definition";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT GotoTypeDefinitionRequest : public Request<
+ GotoResult, std::nullptr_t, TextDocumentPositionParams>
+{
+public:
+ explicit GotoTypeDefinitionRequest(const TextDocumentPositionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/typeDefinition";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT GotoImplementationRequest : public Request<
+ GotoResult, std::nullptr_t, TextDocumentPositionParams>
+{
+public:
+ explicit GotoImplementationRequest(const TextDocumentPositionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/implementation";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ReferenceParams : public TextDocumentPositionParams
+{
+public:
+ using TextDocumentPositionParams::TextDocumentPositionParams;
+
+ class ReferenceContext : public JsonObject
+ {
+ public:
+ explicit ReferenceContext(bool includeDeclaration)
+ { setIncludeDeclaration(includeDeclaration); }
+ ReferenceContext() = default;
+ using JsonObject::JsonObject;
+ bool includeDeclaration() const { return typedValue<bool>(includeDeclarationKey); }
+ void setIncludeDeclaration(bool includeDeclaration)
+ { insert(includeDeclarationKey, includeDeclaration); }
+
+ bool isValid() const override { return contains(includeDeclarationKey); }
+ };
+
+ ReferenceContext context() const { return typedValue<ReferenceContext>(contextKey); }
+ void setContext(const ReferenceContext &context) { insert(contextKey, context); }
+
+ bool isValid() const override
+ { return TextDocumentPositionParams::isValid() && contains(contextKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT FindReferencesRequest : public Request<
+ LanguageClientArray<Location>, std::nullptr_t, ReferenceParams>
+{
+public:
+ explicit FindReferencesRequest(const ReferenceParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/references";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentHighlight : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ enum DocumentHighlightKind {
+ Text = 1,
+ Read = 2,
+ Write = 3
+ };
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ std::optional<int> kind() const { return optionalValue<int>(kindKey); }
+ void setKind(int kind) { insert(kindKey, kind); }
+ void clearKind() { remove(kindKey); }
+
+ bool isValid() const override { return contains(rangeKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentHighlightsResult
+ : public std::variant<QList<DocumentHighlight>, std::nullptr_t>
+{
+public:
+ using variant::variant;
+ DocumentHighlightsResult() : variant(nullptr) {}
+ explicit DocumentHighlightsResult(const QJsonValue &value);
+ using variant::operator=;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentHighlightsRequest : public Request<
+ DocumentHighlightsResult, std::nullptr_t, TextDocumentPositionParams>
+{
+public:
+ explicit DocumentHighlightsRequest(const TextDocumentPositionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/documentHighlight";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentParams : public JsonObject
+{
+public:
+ TextDocumentParams();
+ explicit TextDocumentParams(const TextDocumentIdentifier &identifier);
+ using JsonObject::JsonObject;
+
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ bool isValid() const override { return contains(textDocumentKey); }
+};
+
+using DocumentSymbolParams = TextDocumentParams;
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentSymbolsResult
+ : public std::variant<QList<SymbolInformation>, QList<DocumentSymbol>, std::nullptr_t>
+{
+public:
+ using variant::variant;
+ DocumentSymbolsResult() : variant(nullptr) {}
+ explicit DocumentSymbolsResult(const QJsonValue &value);
+ DocumentSymbolsResult(const DocumentSymbolsResult &other) : variant(other) {}
+ DocumentSymbolsResult(DocumentSymbolsResult &&other) : variant(std::move(other)) {}
+
+ using variant::operator=;
+ DocumentSymbolsResult &operator =(DocumentSymbolsResult &&other)
+ {
+ variant::operator=(std::move(other));
+ return *this;
+ }
+ DocumentSymbolsResult &operator =(const DocumentSymbolsResult &other)
+ {
+ variant::operator=(other);
+ return *this;
+ }
+ // Needed to make it usable in Qt 6 signals.
+ bool operator<(const DocumentSymbolsResult &other) const = delete;
+};
+
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentSymbolsRequest
+ : public Request<DocumentSymbolsResult, std::nullptr_t, DocumentSymbolParams>
+{
+public:
+ explicit DocumentSymbolsRequest(const DocumentSymbolParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/documentSymbol";
+};
+
+/**
+ * The kind of a code action.
+ *
+ * Kinds are a hierarchical list of identifiers separated by `.`, e.g. `"refactor.extract.function"`.
+ *
+ * The set of kinds is open and client needs to announce the kinds it supports to the server during
+ * initialization.
+ */
+
+using CodeActionKind = QString;
+
+/**
+ * A set of predefined code action kinds
+ */
+
+namespace CodeActionKinds {
+constexpr char QuickFix[] = "quickfix";
+constexpr char Refactor[] = "refactor";
+constexpr char RefactorExtract[] = "refactor.extract";
+constexpr char RefactorInline[] = "refactor.inline";
+constexpr char RefactorRewrite[] = "refactor.rewrite";
+constexpr char Source[] = "source";
+constexpr char SourceOrganizeImports[] = "source.organizeImports";
+}
+
+class LANGUAGESERVERPROTOCOL_EXPORT CodeActionParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT CodeActionContext : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ QList<Diagnostic> diagnostics() const { return array<Diagnostic>(diagnosticsKey); }
+ void setDiagnostics(const QList<Diagnostic> &diagnostics)
+ { insertArray(diagnosticsKey, diagnostics); }
+
+ std::optional<QList<CodeActionKind>> only() const;
+ void setOnly(const QList<CodeActionKind> &only);
+ void clearOnly() { remove(onlyKey); }
+
+ bool isValid() const override { return contains(diagnosticsKey); }
+ };
+
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ CodeActionContext context() const { return typedValue<CodeActionContext>(contextKey); }
+ void setContext(const CodeActionContext &context) { insert(contextKey, context); }
+
+ bool isValid() const override
+ { return contains(textDocumentKey) && contains(rangeKey) && contains(contextKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CodeAction : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString title() const { return typedValue<QString>(titleKey); }
+ void setTitle(QString title) { insert(titleKey, title); }
+
+ std::optional<CodeActionKind> kind() const { return optionalValue<CodeActionKind>(kindKey); }
+ void setKind(const CodeActionKind &kind) { insert(kindKey, kind); }
+ void clearKind() { remove(kindKey); }
+
+ std::optional<QList<Diagnostic>> diagnostics() const
+ { return optionalArray<Diagnostic>(diagnosticsKey); }
+ void setDiagnostics(const QList<Diagnostic> &diagnostics)
+ { insertArray(diagnosticsKey, diagnostics); }
+ void clearDiagnostics() { remove(diagnosticsKey); }
+
+ std::optional<WorkspaceEdit> edit() const { return optionalValue<WorkspaceEdit>(editKey); }
+ void setEdit(const WorkspaceEdit &edit) { insert(editKey, edit); }
+ void clearEdit() { remove(editKey); }
+
+ std::optional<Command> command() const { return optionalValue<Command>(commandKey); }
+ void setCommand(const Command &command) { insert(commandKey, command); }
+ void clearCommand() { remove(commandKey); }
+
+ bool isValid() const override { return contains(titleKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CodeActionResult
+ : public std::variant<QList<std::variant<Command, CodeAction>>, std::nullptr_t>
+{
+public:
+ using variant::variant;
+ explicit CodeActionResult(const QJsonValue &val);
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CodeActionRequest : public Request<
+ CodeActionResult, std::nullptr_t, CodeActionParams>
+{
+public:
+ explicit CodeActionRequest(const CodeActionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/codeAction";
+};
+
+using CodeLensParams = TextDocumentParams;
+
+class LANGUAGESERVERPROTOCOL_EXPORT CodeLens : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ std::optional<Command> command() const { return optionalValue<Command>(commandKey); }
+ void setCommand(const Command &command) { insert(commandKey, command); }
+ void clearCommand() { remove(commandKey); }
+
+ std::optional<QJsonValue> data() const;
+ void setData(const QJsonValue &data) { insert(dataKey, data); }
+ void clearData() { remove(dataKey); }
+
+ bool isValid() const override { return contains(rangeKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CodeLensRequest : public Request<
+ LanguageClientArray<CodeLens>, std::nullptr_t, CodeLensParams>
+{
+public:
+ explicit CodeLensRequest(const CodeLensParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/codeLens";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CodeLensResolveRequest : public Request<
+ CodeLens, std::nullptr_t, CodeLens>
+{
+public:
+ explicit CodeLensResolveRequest(const CodeLens &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "codeLens/resolve";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentLink : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ std::optional<DocumentUri> target() const;
+ void setTarget(const DocumentUri &target) { insert(targetKey, target.toString()); }
+ void clearTarget() { remove(targetKey); }
+
+ std::optional<QJsonValue> data() const;
+ void setData(const QJsonValue &data) { insert(dataKey, data); }
+ void clearData() { remove(dataKey); }
+
+ bool isValid() const override { return contains(rangeKey); }
+};
+
+using DocumentLinkParams = TextDocumentParams;
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentLinkRequest : public Request<
+ LanguageClientValue<DocumentLink>, std::nullptr_t, DocumentLinkParams>
+{
+public:
+ explicit DocumentLinkRequest(const DocumentLinkParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/documentLink";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentLinkResolveRequest : public Request<
+ DocumentLink, std::nullptr_t, DocumentLink>
+{
+public:
+ explicit DocumentLinkResolveRequest(const DocumentLink &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "documentLink/resolve";
+};
+
+using DocumentColorParams = TextDocumentParams;
+
+class LANGUAGESERVERPROTOCOL_EXPORT Color : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ double red() const { return typedValue<double>(redKey); }
+ void setRed(double red) { insert(redKey, red); }
+
+ double green() const { return typedValue<double>(greenKey); }
+ void setGreen(double green) { insert(greenKey, green); }
+
+ double blue() const { return typedValue<double>(blueKey); }
+ void setBlue(double blue) { insert(blueKey, blue); }
+
+ double alpha() const { return typedValue<double>(alphaKey); }
+ void setAlpha(double alpha) { insert(alphaKey, alpha); }
+
+ bool isValid() const override
+ { return contains(redKey) && contains(greenKey) && contains(blueKey) && contains(alphaKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ColorInformation : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(Range range) { insert(rangeKey, range); }
+
+ Color color() const { return typedValue<Color>(colorKey); }
+ void setColor(const Color &color) { insert(colorKey, color); }
+
+ bool isValid() const override { return contains(rangeKey) && contains(colorKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentColorRequest : public Request<
+ QList<ColorInformation>, std::nullptr_t, DocumentColorParams>
+{
+public:
+ explicit DocumentColorRequest(const DocumentColorParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/documentColor";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ColorPresentationParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ Color colorInfo() const { return typedValue<Color>(colorInfoKey); }
+ void setColorInfo(const Color &colorInfo) { insert(colorInfoKey, colorInfo); }
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ bool isValid() const override
+ { return contains(textDocumentKey) && contains(colorInfoKey) && contains(rangeKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ColorPresentation : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString label() const { return typedValue<QString>(labelKey); }
+ void setLabel(const QString &label) { insert(labelKey, label); }
+
+ std::optional<TextEdit> textEdit() const { return optionalValue<TextEdit>(textEditKey); }
+ void setTextEdit(const TextEdit &textEdit) { insert(textEditKey, textEdit); }
+ void clearTextEdit() { remove(textEditKey); }
+
+ std::optional<QList<TextEdit>> additionalTextEdits() const
+ { return optionalArray<TextEdit>(additionalTextEditsKey); }
+ void setAdditionalTextEdits(const QList<TextEdit> &additionalTextEdits)
+ { insertArray(additionalTextEditsKey, additionalTextEdits); }
+ void clearAdditionalTextEdits() { remove(additionalTextEditsKey); }
+
+ bool isValid() const override { return contains(labelKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ColorPresentationRequest : public Request<
+ QList<ColorPresentation>, std::nullptr_t, ColorPresentationParams>
+{
+public:
+ explicit ColorPresentationRequest(const ColorPresentationParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/colorPresentation";
+};
+
+class DocumentFormattingProperty : public std::variant<bool, double, QString>
+{
+public:
+ DocumentFormattingProperty() = default;
+ explicit DocumentFormattingProperty(const QJsonValue &value);
+ explicit DocumentFormattingProperty(const DocumentFormattingProperty &other)
+ : std::variant<bool, double, QString>(other) {}
+
+ using variant::variant;
+ using variant::operator=;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT FormattingOptions : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ int tabSize() const { return typedValue<int>(tabSizeKey); }
+ void setTabSize(int tabSize) { insert(tabSizeKey, tabSize); }
+
+ bool insertSpace() const { return typedValue<bool>(insertSpaceKey); }
+ void setInsertSpace(bool insertSpace) { insert(insertSpaceKey, insertSpace); }
+
+ std::optional<bool> trimTrailingWhitespace() const
+ { return optionalValue<bool>(trimTrailingWhitespaceKey); }
+ void setTrimTrailingWhitespace(bool trimTrailingWhitespace)
+ { insert(trimTrailingWhitespaceKey, trimTrailingWhitespace); }
+ void clearTrimTrailingWhitespace() { remove(trimTrailingWhitespaceKey); }
+
+ std::optional<bool> insertFinalNewline() const
+ { return optionalValue<bool>(insertFinalNewlineKey); }
+ void setInsertFinalNewline(bool insertFinalNewline)
+ { insert(insertFinalNewlineKey, insertFinalNewline); }
+ void clearInsertFinalNewline() { remove(insertFinalNewlineKey); }
+
+ std::optional<bool> trimFinalNewlines() const
+ { return optionalValue<bool>(trimFinalNewlinesKey); }
+ void setTrimFinalNewlines(bool trimFinalNewlines)
+ { insert(trimFinalNewlinesKey, trimFinalNewlines); }
+ void clearTrimFinalNewlines() { remove(trimFinalNewlinesKey); }
+
+ QHash<QString, DocumentFormattingProperty> properties() const;
+ void setProperty(const std::string_view key, const DocumentFormattingProperty &property);
+ void removeProperty(const std::string_view &key) { remove(key); }
+
+ bool isValid() const override { return contains(insertSpaceKey) && contains(tabSizeKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentFormattingParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ FormattingOptions options() const { return typedValue<FormattingOptions>(optionsKey); }
+ void setOptions(const FormattingOptions &options) { insert(optionsKey, options); }
+
+ bool isValid() const override { return contains(textDocumentKey) && contains(optionsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentFormattingRequest : public Request<
+ LanguageClientArray<TextEdit>, std::nullptr_t, DocumentFormattingParams>
+{
+public:
+ explicit DocumentFormattingRequest(const DocumentFormattingParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/formatting";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentRangeFormattingParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ FormattingOptions options() const { return typedValue<FormattingOptions>(optionsKey); }
+ void setOptions(const FormattingOptions &options) { insert(optionsKey, options); }
+
+ bool isValid() const override
+ { return contains(textDocumentKey) && contains(rangeKey) && contains(optionsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentRangeFormattingRequest : public Request<
+ LanguageClientArray<TextEdit>, std::nullptr_t, DocumentRangeFormattingParams>
+{
+public:
+ explicit DocumentRangeFormattingRequest(const DocumentRangeFormattingParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/rangeFormatting";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentOnTypeFormattingParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ Position position() const { return typedValue<Position>(positionKey); }
+ void setPosition(const Position &position) { insert(positionKey, position); }
+
+ QString ch() const { return typedValue<QString>(chKey); }
+ void setCh(const QString &ch) { insert(chKey, ch); }
+
+ FormattingOptions options() const { return typedValue<FormattingOptions>(optionsKey); }
+ void setOptions(const FormattingOptions &options) { insert(optionsKey, options); }
+
+ bool isValid() const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentOnTypeFormattingRequest : public Request<
+ LanguageClientArray<TextEdit>, std::nullptr_t, DocumentOnTypeFormattingParams>
+{
+public:
+ explicit DocumentOnTypeFormattingRequest(const DocumentOnTypeFormattingParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/onTypeFormatting";
+};
+
+class PlaceHolderResult : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ QString placeHolder() const { return typedValue<QString>(placeHolderKey); }
+ void setPlaceHolder(const QString &placeHolder) { insert(placeHolderKey, placeHolder); }
+
+ bool isValid() const override { return contains(rangeKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT PrepareRenameResult
+ : public std::variant<PlaceHolderResult, Range, std::nullptr_t>
+{
+public:
+ PrepareRenameResult();
+ PrepareRenameResult(const std::variant<PlaceHolderResult, Range, std::nullptr_t> &val);
+ explicit PrepareRenameResult(const PlaceHolderResult &val);
+ explicit PrepareRenameResult(const Range &val);
+ explicit PrepareRenameResult(const QJsonValue &val);
+
+ bool isValid() const;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT PrepareRenameRequest
+ : public Request<PrepareRenameResult, std::nullptr_t, TextDocumentPositionParams>
+{
+public:
+ explicit PrepareRenameRequest(const TextDocumentPositionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/prepareRename";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT RenameParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ Position position() const { return typedValue<Position>(positionKey); }
+ void setPosition(const Position &position) { insert(positionKey, position); }
+
+ QString newName() const { return typedValue<QString>(newNameKey); }
+ void setNewName(const QString &newName) { insert(newNameKey, newName); }
+
+ bool isValid() const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT RenameRequest : public Request<
+ WorkspaceEdit, std::nullptr_t, RenameParams>
+{
+public:
+ explicit RenameRequest(const RenameParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/rename";
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/languageserverprotocol_global.h b/src/shared/lsp/languageserverprotocol_global.h
new file mode 100644
index 000000000..7f06f1216
--- /dev/null
+++ b/src/shared/lsp/languageserverprotocol_global.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QtGlobal>
+
+#if defined(LANGUAGESERVERPROTOCOL_LIBRARY)
+# define LANGUAGESERVERPROTOCOL_EXPORT Q_DECL_EXPORT
+#elif defined(LANGUAGESERVERPROTOCOL_STATIC_LIBRARY)
+# define LANGUAGESERVERPROTOCOL_EXPORT
+#else
+# define LANGUAGESERVERPROTOCOL_EXPORT Q_DECL_IMPORT
+#endif
diff --git a/src/shared/lsp/languageserverprotocoltr.h b/src/shared/lsp/languageserverprotocoltr.h
new file mode 100644
index 000000000..a4e2f324c
--- /dev/null
+++ b/src/shared/lsp/languageserverprotocoltr.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QCoreApplication>
+
+namespace lsp {
+
+struct Tr
+{
+ Q_DECLARE_TR_FUNCTIONS(QtC::LanguageServerProtocol)
+};
+
+} // LanguageServerProtocol
diff --git a/src/shared/lsp/link.cpp b/src/shared/lsp/link.cpp
new file mode 100644
index 000000000..e25b828d7
--- /dev/null
+++ b/src/shared/lsp/link.cpp
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "link.h"
+
+#include "textutils.h"
+
+namespace lsp::Utils {
+
+/*!
+ Returns the Link to \a filePath.
+ If \a canContainLineNumber is true the line number, and column number components
+ are extracted from the \a filePath's \c path() and the found \a postfix is set.
+
+ The following patterns are supported: \c {filepath.txt:19},
+ \c{filepath.txt:19:12}, \c {filepath.txt+19},
+ \c {filepath.txt+19+12}, and \c {filepath.txt(19)}.
+*/
+
+Link Link::fromString(const QString &filePathWithNumbers, bool canContainLineNumber)
+{
+ Link link;
+ if (!canContainLineNumber) {
+ link.targetFilePath = FilePath::fromUserInput(filePathWithNumbers);
+ } else {
+ int postfixPos = -1;
+ const Text::Position pos = Text::Position::fromFileName(filePathWithNumbers, postfixPos);
+ link.targetFilePath = FilePath::fromUserInput(filePathWithNumbers.left(postfixPos));
+ link.targetLine = pos.line;
+ link.targetColumn = pos.column;
+ }
+ return link;
+}
+
+} // namespace lsp::Utils
diff --git a/src/shared/lsp/link.h b/src/shared/lsp/link.h
new file mode 100644
index 000000000..eaafe280c
--- /dev/null
+++ b/src/shared/lsp/link.h
@@ -0,0 +1,78 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "filepath.h"
+
+#include <QMetaType>
+#include <QMultiHash>
+#include <QString>
+
+#include <functional>
+
+namespace lsp::Utils {
+
+class Link
+{
+public:
+ Link() = default;
+ Link(const FilePath &filePath, int line = 0, int column = 0)
+ : targetFilePath(filePath)
+ , targetLine(line)
+ , targetColumn(column)
+ {}
+
+ static Link fromString(const QString &filePathWithNumbers, bool canContainLineNumber = false);
+
+ bool hasValidTarget() const
+ {
+ if (!targetFilePath.isEmpty())
+ return true;
+ return !targetFilePath.scheme().isEmpty() || !targetFilePath.host().isEmpty();
+ }
+
+ bool hasValidLinkText() const
+ { return linkTextStart != linkTextEnd; }
+
+ bool operator==(const Link &other) const
+ {
+ return hasSameLocation(other)
+ && linkTextStart == other.linkTextStart
+ && linkTextEnd == other.linkTextEnd;
+ }
+ bool operator!=(const Link &other) const { return !(*this == other); }
+
+ bool hasSameLocation(const Link &other) const
+ {
+ return targetFilePath == other.targetFilePath
+ && targetLine == other.targetLine
+ && targetColumn == other.targetColumn;
+ }
+
+ friend size_t qHash(const Link &l, uint seed = 0)
+ { return QT_PREPEND_NAMESPACE(qHash)(l.targetFilePath, seed)
+ | QT_PREPEND_NAMESPACE(qHash(l.targetLine, seed))
+ | QT_PREPEND_NAMESPACE(qHash(l.targetColumn, seed)); }
+
+ int linkTextStart = -1;
+ int linkTextEnd = -1;
+
+ FilePath targetFilePath;
+ int targetLine = 0;
+ int targetColumn = 0;
+};
+
+using LinkHandler = std::function<void(const Link &)>;
+using Links = QList<Link>;
+
+} // namespace lsp::Utils
+
+namespace std {
+
+template<> struct hash<lsp::Utils::Link>
+{
+ size_t operator()(const lsp::Utils::Link &fn) const { return qHash(fn); }
+};
+
+} // std
diff --git a/src/shared/lsp/lsp.qbs b/src/shared/lsp/lsp.qbs
new file mode 100644
index 000000000..95323d332
--- /dev/null
+++ b/src/shared/lsp/lsp.qbs
@@ -0,0 +1,72 @@
+import qbs.Utilities
+
+QbsStaticLibrary {
+ name: "qtclsp"
+
+ Depends { name: "qbscore"; cpp.link: false }
+
+ Depends {
+ condition: Utilities.versionCompare(Qt.core.version, "6") >= 0
+ name: "Qt.core5compat"
+ }
+
+ cpp.defines: base.filter(function(d) { return d !== "QT_NO_CAST_FROM_ASCII"; })
+ .concat("LANGUAGESERVERPROTOCOL_STATIC_LIBRARY")
+
+ files: [
+ "algorithm.h",
+ "basemessage.cpp",
+ "basemessage.h",
+ "callhierarchy.cpp",
+ "callhierarchy.h",
+ "client.cpp",
+ "client.h",
+ "clientcapabilities.cpp",
+ "clientcapabilities.h",
+ "completion.cpp",
+ "completion.h",
+ "diagnostics.cpp",
+ "diagnostics.h",
+ "filepath.h",
+ "initializemessages.cpp",
+ "initializemessages.h",
+ "jsonkeys.h",
+ "jsonobject.cpp",
+ "jsonobject.h",
+ "jsonrpcmessages.cpp",
+ "jsonrpcmessages.h",
+ "languagefeatures.cpp",
+ "languagefeatures.h",
+ "languageserverprotocol_global.h",
+ "languageserverprotocoltr.h",
+ "link.cpp",
+ "link.h",
+ "lsptypes.cpp",
+ "lsptypes.h",
+ "lsputils.cpp",
+ "lsputils.h",
+ "messages.cpp",
+ "messages.h",
+ "predicates.h",
+ "progresssupport.cpp",
+ "progresssupport.h",
+ "semantictokens.cpp",
+ "semantictokens.h",
+ "servercapabilities.cpp",
+ "servercapabilities.h",
+ "shutdownmessages.cpp",
+ "shutdownmessages.h",
+ "textsynchronization.cpp",
+ "textsynchronization.h",
+ "textutils.cpp",
+ "textutils.h",
+ "workspace.cpp",
+ "workspace.h",
+ ]
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.defines: "LANGUAGESERVERPROTOCOL_STATIC_LIBRARY"
+ cpp.includePaths: exportingProduct.sourceDirectory + "/.."
+ }
+}
diff --git a/src/shared/lsp/lsptypes.cpp b/src/shared/lsp/lsptypes.cpp
new file mode 100644
index 000000000..751fc4e3d
--- /dev/null
+++ b/src/shared/lsp/lsptypes.cpp
@@ -0,0 +1,364 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "lsptypes.h"
+
+#include "languageserverprotocoltr.h"
+#include "lsputils.h"
+#include "textutils.h"
+
+#include <QFile>
+#include <QHash>
+#include <QJsonArray>
+#include <QMap>
+#include <QRegularExpression>
+#include <QVector>
+
+namespace lsp {
+
+std::optional<DiagnosticSeverity> Diagnostic::severity() const
+{
+ if (auto val = optionalValue<int>(severityKey))
+ return std::make_optional(static_cast<DiagnosticSeverity>(*val));
+ return std::nullopt;
+}
+
+std::optional<Diagnostic::Code> Diagnostic::code() const
+{
+ QJsonValue codeValue = value(codeKey);
+ auto it = find(codeKey);
+ if (codeValue.isUndefined())
+ return std::nullopt;
+ QJsonValue::Type type = it.value().type();
+ if (type != QJsonValue::String && type != QJsonValue::Double)
+ return std::make_optional(Code(QString()));
+ return std::make_optional(codeValue.isDouble() ? Code(codeValue.toInt())
+ : Code(codeValue.toString()));
+}
+
+void Diagnostic::setCode(const Diagnostic::Code &code)
+{
+ insertVariant<int, QString>(codeKey, code);
+}
+
+std::optional<WorkspaceEdit::Changes> WorkspaceEdit::changes() const
+{
+ auto it = find(changesKey);
+ if (it == end())
+ return std::nullopt;
+ const QJsonObject &changesObject = it.value().toObject();
+ Changes changesResult;
+ for (const QString &key : changesObject.keys())
+ changesResult[DocumentUri::fromProtocol(key)] = LanguageClientArray<TextEdit>(changesObject.value(key)).toList();
+ return std::make_optional(changesResult);
+}
+
+void WorkspaceEdit::setChanges(const Changes &changes)
+{
+ QJsonObject changesObject;
+ const auto end = changes.end();
+ for (auto it = changes.begin(); it != end; ++it) {
+ QJsonArray edits;
+ for (const TextEdit &edit : it.value())
+ edits.append(QJsonValue(edit));
+ changesObject.insert(QJsonValue(it.key()).toString(), edits);
+ }
+ insert(changesKey, changesObject);
+}
+
+WorkSpaceFolder::WorkSpaceFolder(const DocumentUri &uri, const QString &name)
+{
+ setUri(uri);
+ setName(name);
+}
+
+MarkupOrString::MarkupOrString(const std::variant<QString, MarkupContent> &val)
+ : std::variant<QString, MarkupContent>(val)
+{ }
+
+MarkupOrString::MarkupOrString(const QString &val)
+ : std::variant<QString, MarkupContent>(val)
+{ }
+
+MarkupOrString::MarkupOrString(const MarkupContent &val)
+ : std::variant<QString, MarkupContent>(val)
+{ }
+
+MarkupOrString::MarkupOrString(const QJsonValue &val)
+{
+ if (val.isString()) {
+ emplace<QString>(val.toString());
+ } else {
+ MarkupContent markupContent(val.toObject());
+ if (markupContent.isValid())
+ emplace<MarkupContent>(MarkupContent(val.toObject()));
+ }
+}
+
+QJsonValue MarkupOrString::toJson() const
+{
+ if (std::holds_alternative<QString>(*this))
+ return std::get<QString>(*this);
+ if (std::holds_alternative<MarkupContent>(*this))
+ return QJsonValue(std::get<MarkupContent>(*this));
+ return {};
+}
+
+QMap<QString, QString> languageIds()
+{
+ static const QMap<QString, QString> languages({
+ {"Windows Bat","bat" },
+ {"BibTeX","bibtex" },
+ {"Clojure","clojure" },
+ {"Coffeescript","coffeescript" },
+ {"C","c" },
+ {"C++","cpp" },
+ {"C#","csharp" },
+ {"CSS","css" },
+ {"Diff","diff" },
+ {"Dockerfile","dockerfile" },
+ {"F#","fsharp" },
+ {"Git commit","git-commit" },
+ {"Git rebase","git-rebase" },
+ {"Go","go" },
+ {"Groovy","groovy" },
+ {"Handlebars","handlebars" },
+ {"HTML","html" },
+ {"Ini","ini" },
+ {"Java","java" },
+ {"JavaScript","javascript" },
+ {"JSON","json" },
+ {"LaTeX","latex" },
+ {"Less","less" },
+ {"Lua","lua" },
+ {"Makefile","makefile" },
+ {"Markdown","markdown" },
+ {"Objective-C","objective-c" },
+ {"Objective-C++","objective-cpp" },
+ {"Perl6","perl6" },
+ {"Perl","perl" },
+ {"PHP","php" },
+ {"Powershell","powershell" },
+ {"Pug","jade" },
+ {"Python","python" },
+ {"R","r" },
+ {"Razor (cshtml)","razor" },
+ {"Ruby","ruby" },
+ {"Rust","rust" },
+ {"Scss (syntax using curly brackets)","scss"},
+ {"Sass (indented syntax)","sass" },
+ {"ShaderLab","shaderlab" },
+ {"Shell Script (Bash)","shellscript" },
+ {"SQL","sql" },
+ {"Swift","swift" },
+ {"TypeScript","typescript" },
+ {"TeX","tex" },
+ {"Visual Basic","vb" },
+ {"XML","xml" },
+ {"XSL","xsl" },
+ {"YAML","yaml" }
+ });
+ return languages;
+}
+
+bool TextDocumentItem::isValid() const
+{
+ return contains(uriKey) && contains(languageIdKey) && contains(versionKey) && contains(textKey);
+}
+
+TextDocumentPositionParams::TextDocumentPositionParams()
+ : TextDocumentPositionParams(TextDocumentIdentifier(), Position())
+{
+
+}
+
+TextDocumentPositionParams::TextDocumentPositionParams(
+ const TextDocumentIdentifier &document, const Position &position)
+{
+ setTextDocument(document);
+ setPosition(position);
+}
+
+Position::Position(int line, int character)
+{
+ setLine(line);
+ setCharacter(character);
+}
+
+Range::Range(const Position &start, const Position &end)
+{
+ setStart(start);
+ setEnd(end);
+}
+
+bool Range::contains(const Range &other) const
+{
+ if (start() > other.start())
+ return false;
+ if (end() < other.end())
+ return false;
+ return true;
+}
+
+bool Range::overlaps(const Range &range) const
+{
+ return !isLeftOf(range) && !range.isLeftOf(*this);
+}
+
+QString expressionForGlob(QString globPattern)
+{
+ const QString anySubDir("qtc_anysubdir_id");
+ globPattern.replace("**/", anySubDir);
+ QString regexp = QRegularExpression::wildcardToRegularExpression(globPattern);
+ regexp.replace(anySubDir,"(.*[/\\\\])*");
+ regexp.replace("\\{", "(");
+ regexp.replace("\\}", ")");
+ regexp.replace(",", "|");
+ return regexp;
+}
+
+bool DocumentFilter::applies(const Utils::FilePath &fileName) const
+{
+ if (std::optional<QString> _pattern = pattern()) {
+ QRegularExpression::PatternOption option = QRegularExpression::NoPatternOption;
+ if (fileName.caseSensitivity() == Qt::CaseInsensitive)
+ option = QRegularExpression::CaseInsensitiveOption;
+ const QRegularExpression regexp(expressionForGlob(*_pattern), option);
+ if (regexp.isValid() && regexp.match(fileName.toString()).hasMatch())
+ return true;
+ }
+ // return false when any of the filter didn't match but return true when no filter was defined
+ return !contains(schemeKey) && !contains(languageKey) && !contains(patternKey);
+}
+
+Utils::Link Location::toLink(const DocumentUri::PathMapper &mapToHostPath) const
+{
+ if (!isValid())
+ return Utils::Link();
+
+ return Utils::Link(uri().toFilePath(mapToHostPath),
+ range().start().line() + 1,
+ range().start().character());
+}
+
+// Ensure %xx like %20 are really decoded using fromPercentEncoding
+// Else, a path with spaces would keep its %20 which would cause failure
+// to open the file by the text editor. This is the cases with compilers in
+// C:\Programs Files on Windows.
+DocumentUri::DocumentUri(const QString &other)
+ : QUrl(QUrl::fromPercentEncoding(other.toUtf8()))
+{ }
+
+DocumentUri::DocumentUri(const Utils::FilePath &other, const PathMapper &mapToServerPath)
+ : QUrl(QUrl::fromLocalFile(mapToServerPath(other).path()))
+{ }
+
+Utils::FilePath DocumentUri::toFilePath(const PathMapper &mapToHostPath) const
+{
+ if (isLocalFile()) {
+ const Utils::FilePath serverPath = Utils::FilePath::fromUserInput(toLocalFile());
+ QBS_ASSERT(mapToHostPath, return serverPath);
+ return mapToHostPath(serverPath);
+ }
+ return {};
+}
+
+DocumentUri DocumentUri::fromProtocol(const QString &uri)
+{
+ return DocumentUri(uri);
+}
+
+DocumentUri DocumentUri::fromFilePath(const Utils::FilePath &file, const PathMapper &mapToServerPath)
+{
+ return DocumentUri(file, mapToServerPath);
+}
+
+MarkupKind::MarkupKind(const QJsonValue &value)
+{
+ m_value = value.toString() == "markdown" ? markdown : plaintext;
+}
+
+MarkupKind::operator QJsonValue() const
+{
+ switch (m_value) {
+ case MarkupKind::markdown:
+ return "markdown";
+ case MarkupKind::plaintext:
+ return "plaintext";
+ }
+ return {};
+}
+
+DocumentChange::DocumentChange(const QJsonValue &value)
+{
+ const QString kind = value["kind"].toString();
+ if (kind == "create")
+ emplace<CreateFileOperation>(value);
+ else if (kind == "rename")
+ emplace<RenameFileOperation>(value);
+ else if (kind == "delete")
+ emplace<DeleteFileOperation>(value);
+ else
+ emplace<TextDocumentEdit>(value);
+}
+
+using DocumentChangeBase = std::variant<TextDocumentEdit, CreateFileOperation, RenameFileOperation, DeleteFileOperation>;
+
+bool DocumentChange::isValid() const
+{
+ return std::visit([](const auto &v) { return v.isValid(); }, DocumentChangeBase(*this));
+}
+
+DocumentChange::operator const QJsonValue () const
+{
+ return std::visit([](const auto &v) { return QJsonValue(v); }, DocumentChangeBase(*this));
+}
+
+CreateFileOperation::CreateFileOperation()
+{
+ insert(kindKey, "create");
+}
+
+QString CreateFileOperation::message(const DocumentUri::PathMapper &mapToHostPath) const
+{
+ return Tr::tr("Create %1").arg(uri().toFilePath(mapToHostPath).toUserOutput());
+}
+
+bool CreateFileOperation::isValid() const
+{
+ return contains(uriKey) && value(kindKey) == "create";
+}
+
+RenameFileOperation::RenameFileOperation()
+{
+ insert(kindKey, "rename");
+}
+
+QString RenameFileOperation::message(const DocumentUri::PathMapper &mapToHostPath) const
+{
+ return Tr::tr("Rename %1 to %2")
+ .arg(oldUri().toFilePath(mapToHostPath).toUserOutput(),
+ newUri().toFilePath(mapToHostPath).toUserOutput());
+}
+
+bool RenameFileOperation::isValid() const
+{
+ return contains(oldUriKey) && contains(newUriKey) && value(kindKey) == "rename";
+}
+
+DeleteFileOperation::DeleteFileOperation()
+{
+ insert(kindKey, "delete");
+}
+
+QString DeleteFileOperation::message(const DocumentUri::PathMapper &mapToHostPath) const
+{
+ return Tr::tr("Delete %1").arg(uri().toFilePath(mapToHostPath).toUserOutput());
+}
+
+bool DeleteFileOperation::isValid() const
+{
+ return contains(uriKey) && value(kindKey) == "delete";
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/lsptypes.h b/src/shared/lsp/lsptypes.h
new file mode 100644
index 000000000..52e7b95a9
--- /dev/null
+++ b/src/shared/lsp/lsptypes.h
@@ -0,0 +1,680 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonkeys.h"
+#include "jsonobject.h"
+#include "lsputils.h"
+#include "filepath.h"
+#include "link.h"
+
+#include <QJsonObject>
+#include <QUrl>
+#include <QList>
+
+#include <optional>
+#include <variant>
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentUri : public QUrl
+{
+public:
+ DocumentUri() = default;
+ using PathMapper = std::function<Utils::FilePath(const Utils::FilePath &)>;
+ Utils::FilePath toFilePath(const PathMapper &mapToHostPath) const;
+
+ static DocumentUri fromProtocol(const QString &uri);
+ static DocumentUri fromFilePath(const Utils::FilePath &file, const PathMapper &mapToServerPath);
+
+ operator QJsonValue() const { return QJsonValue(toString()); }
+
+private:
+ DocumentUri(const QString &other);
+ DocumentUri(const Utils::FilePath &other, const PathMapper &mapToServerPath);
+
+ friend class LanguageClientValue<QString>;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT Position : public JsonObject
+{
+public:
+ Position() = default;
+ Position(int line, int character);
+ using JsonObject::JsonObject;
+
+ // Line position in a document (zero-based).
+ int line() const { return typedValue<int>(lineKey); }
+ void setLine(int line) { insert(lineKey, line); }
+
+ /*
+ * Character offset on a line in a document (zero-based). Assuming that the line is
+ * represented as a string, the `character` value represents the gap between the
+ * `character` and `character + 1`.
+ *
+ * If the character value is greater than the line length it defaults back to the
+ * line length.
+ */
+ int character() const { return typedValue<int>(characterKey); }
+ void setCharacter(int character) { insert(characterKey, character); }
+
+ bool isValid() const override
+ { return contains(lineKey) && contains(characterKey); }
+};
+
+inline bool operator<(const Position &first, const Position &second)
+{
+ return first.line() < second.line()
+ || (first.line() == second.line() && first.character() < second.character());
+}
+
+inline bool operator>(const Position &first, const Position &second)
+{
+ return second < first;
+}
+
+inline bool operator>=(const Position &first, const Position &second)
+{
+ return !(first < second);
+}
+
+inline bool operator<=(const Position &first, const Position &second)
+{
+ return !(first > second);
+}
+
+class LANGUAGESERVERPROTOCOL_EXPORT Range : public JsonObject
+{
+public:
+ Range() = default;
+ Range(const Position &start, const Position &end);
+ using JsonObject::JsonObject;
+
+ // The range's start position.
+ Position start() const { return typedValue<Position>(startKey); }
+ void setStart(const Position &start) { insert(startKey, start); }
+
+ // The range's end position.
+ Position end() const { return typedValue<Position>(endKey); }
+ void setEnd(const Position &end) { insert(endKey, end); }
+
+ bool isEmpty() const { return start() == end(); }
+ bool contains(const Position &pos) const { return start() <= pos && pos <= end(); }
+ bool contains(const Range &other) const;
+ bool overlaps(const Range &range) const;
+ bool isLeftOf(const Range &other) const
+ { return isEmpty() || other.isEmpty() ? end() < other.start() : end() <= other.start(); }
+
+ bool isValid() const override
+ { return JsonObject::contains(startKey) && JsonObject::contains(endKey); }
+};
+
+inline bool operator==(const Range &r1, const Range &r2)
+{
+ return r1.contains(r2) && r2.contains(r1);
+}
+inline bool operator!=(const Range &r1, const Range &r2) { return !(r1 == r2); }
+
+class LANGUAGESERVERPROTOCOL_EXPORT Location : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ Utils::Link toLink(const DocumentUri::PathMapper &mapToHostPath) const;
+
+ bool isValid() const override { return contains(uriKey) && contains(rangeKey); }
+};
+
+enum class DiagnosticSeverity
+{
+ Error = 1,
+ Warning = 2,
+ Information = 3,
+ Hint = 4
+
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT Diagnostic : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // The range at which the message applies.
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ // The diagnostic's severity. Can be omitted. If omitted it is up to the
+ // client to interpret diagnostics as error, warning, info or hint.
+ std::optional<DiagnosticSeverity> severity() const;
+ void setSeverity(const DiagnosticSeverity &severity)
+ { insert(severityKey, static_cast<int>(severity)); }
+ void clearSeverity() { remove(severityKey); }
+
+ // The diagnostic's code, which might appear in the user interface.
+ using Code = std::variant<int, QString>;
+ std::optional<Code> code() const;
+ void setCode(const Code &code);
+ void clearCode() { remove(codeKey); }
+
+ // A human-readable string describing the source of this
+ // diagnostic, e.g. 'typescript' or 'super lint'.
+ std::optional<QString> source() const
+ { return optionalValue<QString>(sourceKey); }
+ void setSource(const QString &source) { insert(sourceKey, source); }
+ void clearSource() { remove(sourceKey); }
+
+ // The diagnostic's message.
+ QString message() const
+ { return typedValue<QString>(messageKey); }
+ void setMessage(const QString &message) { insert(messageKey, message); }
+
+ bool isValid() const override { return contains(rangeKey) && contains(messageKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT Command : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // Title of the command, like `save`.
+ QString title() const { return typedValue<QString>(titleKey); }
+ void setTitle(const QString &title) { insert(titleKey, title); }
+ void clearTitle() { remove(titleKey); }
+
+ // The identifier of the actual command handler.
+ QString command() const { return typedValue<QString>(commandKey); }
+ void setCommand(const QString &command) { insert(commandKey, command); }
+ void clearCommand() { remove(commandKey); }
+
+ // Arguments that the command handler should be invoked with.
+ std::optional<QJsonArray> arguments() const { return typedValue<QJsonArray>(argumentsKey); }
+ void setArguments(const QJsonArray &arguments) { insert(argumentsKey, arguments); }
+ void clearArguments() { remove(argumentsKey); }
+
+ bool isValid() const override { return contains(titleKey) && contains(commandKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextEdit : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // The range of the text document to be manipulated. To insert
+ // text into a document create a range where start === end.
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ // The string to be inserted. For delete operations use an empty string.
+ QString newText() const { return typedValue<QString>(newTextKey); }
+ void setNewText(const QString &text) { insert(newTextKey, text); }
+
+ bool isValid() const override
+ { return contains(rangeKey) && contains(newTextKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentIdentifier : public JsonObject
+{
+public:
+ TextDocumentIdentifier() : TextDocumentIdentifier(DocumentUri()) {}
+ explicit TextDocumentIdentifier(const DocumentUri &uri) { setUri(uri); }
+ using JsonObject::JsonObject;
+
+ // The text document's URI.
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ bool isValid() const override { return contains(uriKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT VersionedTextDocumentIdentifier : public TextDocumentIdentifier
+{
+public:
+ using TextDocumentIdentifier::TextDocumentIdentifier;
+
+ /*
+ * The version number of this document. If a versioned text document identifier
+ * is sent from the server to the client and the file is not open in the editor
+ * (the server has not received an open notification before) the server can send
+ * `null` to indicate that the version is known and the content on disk is the
+ * truth (as speced with document content ownership)
+ */
+ LanguageClientValue<int> version() const { return clientValue<int>(versionKey); }
+ void setVersion(LanguageClientValue<int> version) { insert(versionKey, version); }
+
+ bool isValid() const override
+ {
+ return TextDocumentIdentifier::isValid() && contains(versionKey);
+ }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentEdit : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // The text document to change.
+ VersionedTextDocumentIdentifier textDocument() const
+ { return typedValue<VersionedTextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const VersionedTextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ // The edits to be applied.
+ QList<TextEdit> edits() const { return array<TextEdit>(editsKey); }
+ void setEdits(const QList<TextEdit> edits) { insertArray(editsKey, edits); }
+
+ bool isValid() const override { return contains(textDocumentKey) && contains(editsKey); }
+};
+
+class CreateFileOptions : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ std::optional<bool> overwrite() const { return optionalValue<bool>(overwriteKey); }
+ void setOverwrite(bool overwrite) { insert(overwriteKey, overwrite); }
+ void clearOverwrite() { remove(overwriteKey); }
+
+ std::optional<bool> ignoreIfExists() const { return optionalValue<bool>(ignoreIfExistsKey); }
+ void setIgnoreIfExists(bool ignoreIfExists) { insert(ignoreIfExistsKey, ignoreIfExists); }
+ void clearIgnoreIfExists() { remove(ignoreIfExistsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CreateFileOperation : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+ CreateFileOperation();
+
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ std::optional<CreateFileOptions> options() const
+ { return optionalValue<CreateFileOptions>(optionsKey); }
+ void setOptions(const CreateFileOptions &options) { insert(optionsKey, options); }
+ void clearOptions() { remove(optionsKey); }
+
+ QString message(const DocumentUri::PathMapper &mapToHostPath) const;
+
+ bool isValid() const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT RenameFileOperation : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+ RenameFileOperation();
+
+ DocumentUri oldUri() const { return DocumentUri::fromProtocol(typedValue<QString>(oldUriKey)); }
+ void setOldUri(const DocumentUri &oldUri) { insert(oldUriKey, oldUri); }
+
+ DocumentUri newUri() const { return DocumentUri::fromProtocol(typedValue<QString>(newUriKey)); }
+ void setNewUri(const DocumentUri &newUri) { insert(newUriKey, newUri); }
+
+ std::optional<CreateFileOptions> options() const
+ { return optionalValue<CreateFileOptions>(optionsKey); }
+ void setOptions(const CreateFileOptions &options) { insert(optionsKey, options); }
+ void clearOptions() { remove(optionsKey); }
+
+ QString message(const DocumentUri::PathMapper &mapToHostPath) const;
+
+ bool isValid() const override;
+};
+
+class DeleteFileOptions : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ std::optional<bool> recursive() const { return optionalValue<bool>(recursiveKey); }
+ void setRecursive(bool recursive) { insert(recursiveKey, recursive); }
+ void clearRecursive() { remove(recursiveKey); }
+
+ std::optional<bool> ignoreIfNotExists() const { return optionalValue<bool>(ignoreIfNotExistsKey); }
+ void setIgnoreIfNotExists(bool ignoreIfNotExists) { insert(ignoreIfNotExistsKey, ignoreIfNotExists); }
+ void clearIgnoreIfNotExists() { remove(ignoreIfNotExistsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DeleteFileOperation : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+ DeleteFileOperation();
+
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ std::optional<DeleteFileOptions> options() const
+ { return optionalValue<DeleteFileOptions>(optionsKey); }
+ void setOptions(const DeleteFileOptions &options) { insert(optionsKey, options); }
+ void clearOptions() { remove(optionsKey); }
+
+ QString message(const DocumentUri::PathMapper &mapToHostPath) const;
+
+ bool isValid() const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentChange
+ : public std::variant<TextDocumentEdit, CreateFileOperation, RenameFileOperation, DeleteFileOperation>
+{
+public:
+ using variant::variant;
+ DocumentChange(const QJsonValue &value);
+
+ bool isValid() const;
+
+ operator const QJsonValue() const;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceEdit : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // Holds changes to existing resources.
+ using Changes = QMap<DocumentUri, QList<TextEdit>>;
+ std::optional<Changes> changes() const;
+ void setChanges(const Changes &changes);
+
+ /*
+ * Depending on the client capability
+ * `workspace.workspaceEdit.resourceOperations` document changes are either
+ * an array of `TextDocumentEdit`s to express changes to n different text
+ * documents where each text document edit addresses a specific version of
+ * a text document. Or it can contain above `TextDocumentEdit`s mixed with
+ * create, rename and delete file / folder operations.
+ *
+ * Whether a client supports versioned document edits is expressed via
+ * `workspace.workspaceEdit.documentChanges` client capability.
+ *
+ * If a client neither supports `documentChanges` nor
+ * `workspace.workspaceEdit.resourceOperations` then only plain `TextEdit`s
+ * using the `changes` property are supported.
+ */
+ std::optional<QList<DocumentChange>> documentChanges() const
+ { return optionalArray<DocumentChange>(documentChangesKey); }
+ void setDocumentChanges(const QList<DocumentChange> &changes)
+ { insertArray(documentChangesKey, changes); }
+};
+
+LANGUAGESERVERPROTOCOL_EXPORT QMap<QString, QString> languageIds();
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentItem : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // The text document's URI.
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ // The text document's language identifier.
+ QString languageId() const { return typedValue<QString>(languageIdKey); }
+ void setLanguageId(const QString &id) { insert(languageIdKey, id); }
+
+ // The version number of this document (it will increase after each change, including undo/redo
+ int version() const { return typedValue<int>(versionKey); }
+ void setVersion(int version) { insert(versionKey, version); }
+
+ // The content of the opened text document.
+ QString text() const { return typedValue<QString>(textKey); }
+ void setText(const QString &text) { insert(textKey, text); }
+
+ bool isValid() const override;
+
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentPositionParams : public JsonObject
+{
+public:
+ TextDocumentPositionParams();
+ TextDocumentPositionParams(const TextDocumentIdentifier &document, const Position &position);
+ using JsonObject::JsonObject;
+
+ // The text document.
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &id) { insert(textDocumentKey, id); }
+
+ // The position inside the text document.
+ Position position() const { return typedValue<Position>(positionKey); }
+ void setPosition(const Position &position) { insert(positionKey, position); }
+
+ bool isValid() const override { return contains(textDocumentKey) && contains(positionKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentFilter : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // A language id, like `typescript`.
+ std::optional<QString> language() const { return optionalValue<QString>(languageKey); }
+ void setLanguage(const QString &language) { insert(languageKey, language); }
+ void clearLanguage() { remove(languageKey); }
+
+ // A Uri [scheme](#Uri.scheme), like `file` or `untitled`.
+ std::optional<QString> scheme() const { return optionalValue<QString>(schemeKey); }
+ void setScheme(const QString &scheme) { insert(schemeKey, scheme); }
+ void clearScheme() { remove(schemeKey); }
+
+ /**
+ * A glob pattern, like `*.{ts,js}`.
+ *
+ * Glob patterns can have the following syntax:
+ * - `*` to match one or more characters in a path segment
+ * - `?` to match on one character in a path segment
+ * - `**` to match any number of path segments, including none
+ * - `{}` to group sub patterns into an OR expression. (e.g. `*.{ts,js}`
+ * matches all TypeScript and JavaScript files)
+ * - `[]` to declare a range of characters to match in a path segment
+ * (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
+ * - `[!...]` to negate a range of characters to match in a path segment
+ * (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but
+ * not `example.0`)
+ */
+ std::optional<QString> pattern() const { return optionalValue<QString>(patternKey); }
+ void setPattern(const QString &pattern) { insert(patternKey, pattern); }
+ void clearPattern() { remove(patternKey); }
+
+ bool applies(const Utils::FilePath &fileName) const;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT MarkupKind
+{
+public:
+ enum Value { plaintext, markdown };
+ MarkupKind() = default;
+ MarkupKind(const Value value)
+ : m_value(value)
+ {}
+ explicit MarkupKind(const QJsonValue &value);
+
+ operator QJsonValue() const;
+ Value value() const { return m_value; }
+
+ bool operator==(const Value &value) const { return m_value == value; }
+
+ bool isValid() const { return true; }
+private:
+ Value m_value = plaintext;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT MarkupContent : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // The type of the Markup
+ MarkupKind kind() const { return MarkupKind(value(kindKey)); }
+ void setKind(MarkupKind kind) { insert(kindKey, kind); }
+ Qt::TextFormat textFormat() const
+ { return kind() == MarkupKind::markdown ? Qt::MarkdownText : Qt::PlainText; }
+
+ // The content itself
+ QString content() const { return typedValue<QString>(contentKey); }
+ void setContent(const QString &content) { insert(contentKey, content); }
+
+ bool isValid() const override
+ { return contains(kindKey) && contains(contentKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT MarkupOrString : public std::variant<QString, MarkupContent>
+{
+public:
+ MarkupOrString() = default;
+ explicit MarkupOrString(const std::variant<QString, MarkupContent> &val);
+ explicit MarkupOrString(const QString &val);
+ explicit MarkupOrString(const MarkupContent &val);
+ MarkupOrString(const QJsonValue &val);
+
+ bool isValid() const { return true; }
+
+ QJsonValue toJson() const;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkSpaceFolder : public JsonObject
+{
+public:
+ WorkSpaceFolder() = default;
+ WorkSpaceFolder(const DocumentUri &uri, const QString &name);
+ using JsonObject::JsonObject;
+
+ // The associated URI for this workspace folder.
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ // The name of the workspace folder. Defaults to the uri's basename.
+ QString name() const { return typedValue<QString>(nameKey); }
+ void setName(const QString &name) { insert(nameKey, name); }
+
+ bool isValid() const override
+ { return contains(uriKey) && contains(nameKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SymbolInformation : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString name() const { return typedValue<QString>(nameKey); }
+ void setName(const QString &name) { insert(nameKey, name); }
+
+ int kind() const { return typedValue<int>(kindKey); }
+ void setKind(int kind) { insert(kindKey, kind); }
+
+ std::optional<bool> deprecated() const { return optionalValue<bool>(deprecatedKey); }
+ void setDeprecated(bool deprecated) { insert(deprecatedKey, deprecated); }
+ void clearDeprecated() { remove(deprecatedKey); }
+
+ Location location() const { return typedValue<Location>(locationKey); }
+ void setLocation(const Location &location) { insert(locationKey, location); }
+
+ std::optional<QString> containerName() const
+ { return optionalValue<QString>(containerNameKey); }
+ void setContainerName(const QString &containerName) { insert(containerNameKey, containerName); }
+ void clearContainerName() { remove(containerNameKey); }
+
+ bool isValid() const override
+ { return contains(nameKey) && contains(kindKey) && contains(locationKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DocumentSymbol : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString name() const { return typedValue<QString>(nameKey); }
+ void setName(const QString &name) { insert(nameKey, name); }
+
+ std::optional<QString> detail() const { return optionalValue<QString>(detailKey); }
+ void setDetail(const QString &detail) { insert(detailKey, detail); }
+ void clearDetail() { remove(detailKey); }
+
+ int kind() const { return typedValue<int>(kindKey); }
+ void setKind(int kind) { insert(kindKey, kind); }
+
+ std::optional<bool> deprecated() const { return optionalValue<bool>(deprecatedKey); }
+ void setDeprecated(bool deprecated) { insert(deprecatedKey, deprecated); }
+ void clearDeprecated() { remove(deprecatedKey); }
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(Range range) { insert(rangeKey, range); }
+
+ Range selectionRange() const { return typedValue<Range>(selectionRangeKey); }
+ void setSelectionRange(Range selectionRange) { insert(selectionRangeKey, selectionRange); }
+
+ std::optional<QList<DocumentSymbol>> children() const
+ { return optionalArray<DocumentSymbol>(childrenKey); }
+ void setChildren(QList<DocumentSymbol> children) { insertArray(childrenKey, children); }
+ void clearChildren() { remove(childrenKey); }
+};
+
+enum class SymbolKind {
+ File = 1,
+ FirstSymbolKind = File,
+ Module = 2,
+ Namespace = 3,
+ Package = 4,
+ Class = 5,
+ Method = 6,
+ Property = 7,
+ Field = 8,
+ Constructor = 9,
+ Enum = 10,
+ Interface = 11,
+ Function = 12,
+ Variable = 13,
+ Constant = 14,
+ String = 15,
+ Number = 16,
+ Boolean = 17,
+ Array = 18,
+ Object = 19,
+ Key = 20,
+ Null = 21,
+ EnumMember = 22,
+ Struct = 23,
+ Event = 24,
+ Operator = 25,
+ TypeParameter = 26,
+ LastSymbolKind = TypeParameter,
+};
+
+namespace CompletionItemKind {
+enum Kind {
+ Text = 1,
+ Method = 2,
+ Function = 3,
+ Constructor = 4,
+ Field = 5,
+ Variable = 6,
+ Class = 7,
+ Interface = 8,
+ Module = 9,
+ Property = 10,
+ Unit = 11,
+ Value = 12,
+ Enum = 13,
+ Keyword = 14,
+ Snippet = 15,
+ Color = 16,
+ File = 17,
+ Reference = 18,
+ Folder = 19,
+ EnumMember = 20,
+ Constant = 21,
+ Struct = 22,
+ Event = 23,
+ Operator = 24,
+ TypeParameter = 25
+};
+} // namespace CompletionItemKind
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/lsputils.cpp b/src/shared/lsp/lsputils.cpp
new file mode 100644
index 000000000..f26a8a21c
--- /dev/null
+++ b/src/shared/lsp/lsputils.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "lsputils.h"
+
+#include <QHash>
+#include <QLoggingCategory>
+#include <QVector>
+
+namespace lsp {
+
+Q_LOGGING_CATEGORY(conversionLog, "qtc.languageserverprotocol.conversion", QtWarningMsg)
+
+template<>
+QString fromJsonValue<QString>(const QJsonValue &value)
+{
+ if (conversionLog().isDebugEnabled() && !value.isString())
+ qCDebug(conversionLog) << "Expected String in json value but got: " << value;
+ return value.toString();
+}
+
+template<>
+int fromJsonValue<int>(const QJsonValue &value)
+{
+ if (conversionLog().isDebugEnabled() && !value.isDouble())
+ qCDebug(conversionLog) << "Expected double in json value but got: " << value;
+ return value.toInt();
+}
+
+template<>
+double fromJsonValue<double>(const QJsonValue &value)
+{
+ if (conversionLog().isDebugEnabled() && !value.isDouble())
+ qCDebug(conversionLog) << "Expected double in json value but got: " << value;
+ return value.toDouble();
+}
+
+template<>
+bool fromJsonValue<bool>(const QJsonValue &value)
+{
+ if (conversionLog().isDebugEnabled() && !value.isBool())
+ qCDebug(conversionLog) << "Expected bool in json value but got: " << value;
+ return value.toBool();
+}
+
+template<>
+QJsonArray fromJsonValue<QJsonArray>(const QJsonValue &value)
+{
+ if (conversionLog().isDebugEnabled() && !value.isArray())
+ qCDebug(conversionLog) << "Expected Array in json value but got: " << value;
+ return value.toArray();
+}
+
+template<>
+QJsonObject fromJsonValue<QJsonObject>(const QJsonValue &value)
+{
+ if (conversionLog().isDebugEnabled() && !value.isObject())
+ qCDebug(conversionLog) << "Expected Object in json value but got: " << value;
+ return value.toObject();
+}
+
+template<>
+QJsonValue fromJsonValue<QJsonValue>(const QJsonValue &value)
+{
+ return value;
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/lsputils.h b/src/shared/lsp/lsputils.h
new file mode 100644
index 000000000..eb7a18342
--- /dev/null
+++ b/src/shared/lsp/lsputils.h
@@ -0,0 +1,160 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "languageserverprotocol_global.h"
+
+#include <tools/qbsassert.h>
+
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QLoggingCategory>
+
+#include <variant>
+
+namespace lsp {
+
+LANGUAGESERVERPROTOCOL_EXPORT Q_DECLARE_LOGGING_CATEGORY(conversionLog)
+
+template <typename T>
+T fromJsonValue(const QJsonValue &value)
+{
+ if (conversionLog().isDebugEnabled() && !value.isObject())
+ qCDebug(conversionLog) << "Expected Object in json value but got: " << value;
+ T result(value.toObject());
+ if (conversionLog().isDebugEnabled() && !result.isValid())
+ qCDebug(conversionLog) << typeid(result).name() << " is not valid: " << result;
+ return result;
+}
+
+template<>
+LANGUAGESERVERPROTOCOL_EXPORT QString fromJsonValue<QString>(const QJsonValue &value);
+
+template<>
+LANGUAGESERVERPROTOCOL_EXPORT int fromJsonValue<int>(const QJsonValue &value);
+
+template<>
+LANGUAGESERVERPROTOCOL_EXPORT double fromJsonValue<double>(const QJsonValue &value);
+
+template<>
+LANGUAGESERVERPROTOCOL_EXPORT bool fromJsonValue<bool>(const QJsonValue &value);
+
+template<>
+LANGUAGESERVERPROTOCOL_EXPORT QJsonObject fromJsonValue<QJsonObject>(const QJsonValue &value);
+
+template<>
+LANGUAGESERVERPROTOCOL_EXPORT QJsonArray fromJsonValue<QJsonArray>(const QJsonValue &value);
+
+template<>
+LANGUAGESERVERPROTOCOL_EXPORT QJsonValue fromJsonValue<QJsonValue>(const QJsonValue &value);
+
+template <typename T>
+class LanguageClientArray : public std::variant<QList<T>, std::nullptr_t>
+{
+public:
+ using std::variant<QList<T>, std::nullptr_t>::variant;
+ using std::variant<QList<T>, std::nullptr_t>::operator=;
+
+ LanguageClientArray() {}
+
+ explicit LanguageClientArray(const QList<T> &list)
+ { *this = list; }
+
+ explicit LanguageClientArray(const QJsonValue &value)
+ {
+ if (value.isArray()) {
+ QList<T> values;
+ values.reserve(value.toArray().count());
+ for (auto arrayValue : value.toArray())
+ values << fromJsonValue<T>(arrayValue);
+ *this = values;
+ } else {
+ *this = nullptr;
+ }
+ }
+
+ QJsonValue toJson() const
+ {
+ if (const auto list = std::get_if<QList<T>>(this)) {
+ QJsonArray array;
+ for (const T &value : *list)
+ array.append(QJsonValue(value));
+ return array;
+ }
+ return QJsonValue();
+ }
+
+ QList<T> toListOrEmpty() const
+ {
+ if (std::holds_alternative<QList<T>>(*this))
+ return std::get<QList<T>>(*this);
+ return {};
+ }
+
+ QList<T> toList() const
+ {
+ QBS_ASSERT(std::holds_alternative<QList<T>>(*this), return {});
+ return std::get<QList<T>>(*this);
+ }
+ bool isNull() const { return std::holds_alternative<std::nullptr_t>(*this); }
+};
+
+template <typename T>
+class LanguageClientValue : public std::variant<T, std::nullptr_t>
+{
+public:
+ using std::variant<T, std::nullptr_t>::operator=;
+
+ LanguageClientValue() : std::variant<T, std::nullptr_t>(nullptr) { }
+ LanguageClientValue(const T &value) : std::variant<T, std::nullptr_t>(value) { }
+ LanguageClientValue(const QJsonValue &value)
+ {
+ if (!QBS_GUARD(!value.isUndefined()) || value.isNull())
+ *this = nullptr;
+ else
+ *this = fromJsonValue<T>(value);
+ }
+
+ operator const QJsonValue() const
+ {
+ if (auto val = std::get_if<T>(this))
+ return QJsonValue(*val);
+ return QJsonValue();
+ }
+
+ T value(const T &defaultValue = T()) const
+ {
+ QBS_ASSERT(std::holds_alternative<T>(*this), return defaultValue);
+ return std::get<T>(*this);
+ }
+
+ template<typename Type>
+ LanguageClientValue<Type> transform()
+ {
+ QBS_ASSERT(!std::holds_alternative<T>(*this), return LanguageClientValue<Type>());
+ return Type(std::get<T>(*this));
+ }
+
+ bool isNull() const { return std::holds_alternative<std::nullptr_t>(*this); }
+};
+
+template <typename T>
+QJsonArray enumArrayToJsonArray(const QList<T> &values)
+{
+ QJsonArray array;
+ for (T value : values)
+ array.append(static_cast<int>(value));
+ return array;
+}
+
+template <typename T>
+QList<T> jsonArrayToList(const QJsonArray &array)
+{
+ QList<T> list;
+ for (const QJsonValue &val : array)
+ list << fromJsonValue<T>(val);
+ return list;
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/messages.cpp b/src/shared/lsp/messages.cpp
new file mode 100644
index 000000000..a909fdb82
--- /dev/null
+++ b/src/shared/lsp/messages.cpp
@@ -0,0 +1,45 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "messages.h"
+
+namespace lsp {
+
+constexpr const char ShowMessageNotification::methodName[];
+constexpr const char ShowMessageRequest::methodName[];
+constexpr const char LogMessageNotification::methodName[];
+constexpr const char TelemetryNotification::methodName[];
+
+ShowMessageNotification::ShowMessageNotification(const ShowMessageParams &params)
+ : Notification(methodName, params)
+{ }
+
+ShowMessageRequest::ShowMessageRequest(const ShowMessageRequestParams &params)
+ : Request(methodName, params)
+{ }
+
+LogMessageNotification::LogMessageNotification(const LogMessageParams &params)
+ : Notification(methodName, params)
+{ }
+
+TelemetryNotification::TelemetryNotification(const JsonObject &params)
+ : Notification(methodName, params)
+{ }
+
+static QString messageTypeName(int messageType)
+{
+ switch (messageType) {
+ case Error: return QString("Error");
+ case Warning: return QString("Warning");
+ case Info: return QString("Info");
+ case Log: return QString("Log");
+ }
+ return QString("");
+}
+
+QString ShowMessageParams::toString() const
+{
+ return messageTypeName(type()) + ": " + message();
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/messages.h b/src/shared/lsp/messages.h
new file mode 100644
index 000000000..8c7d05c6d
--- /dev/null
+++ b/src/shared/lsp/messages.h
@@ -0,0 +1,93 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+
+namespace lsp {
+
+enum MessageType {
+ Error = 1,
+ Warning = 2,
+ Info = 3,
+ Log = 4,
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ShowMessageParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ int type() const { return typedValue<int>(typeKey); }
+ void setType(int type) { insert(typeKey, type); }
+
+ QString message() const { return typedValue<QString>(messageKey); }
+ void setMessage(QString message) { insert(messageKey, message); }
+
+ QString toString() const;
+
+ bool isValid() const override
+ { return contains(typeKey) && contains(messageKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ShowMessageNotification : public Notification<ShowMessageParams>
+{
+public:
+ explicit ShowMessageNotification(const ShowMessageParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "window/showMessage";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT MessageActionItem : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString title() const { return typedValue<QString>(titleKey); }
+ void setTitle(QString title) { insert(titleKey, title); }
+
+ bool isValid() const override { return contains(titleKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ShowMessageRequestParams : public ShowMessageParams
+{
+public:
+ using ShowMessageParams::ShowMessageParams;
+
+ std::optional<QList<MessageActionItem>> actions() const
+ { return optionalArray<MessageActionItem>(actionsKey); }
+ void setActions(const QList<MessageActionItem> &actions) { insertArray(actionsKey, actions); }
+ void clearActions() { remove(actionsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ShowMessageRequest : public Request<
+ LanguageClientValue<MessageActionItem>, std::nullptr_t, ShowMessageRequestParams>
+{
+public:
+ explicit ShowMessageRequest(const ShowMessageRequestParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "window/showMessageRequest";
+};
+
+using LogMessageParams = ShowMessageParams;
+
+class LANGUAGESERVERPROTOCOL_EXPORT LogMessageNotification : public Notification<LogMessageParams>
+{
+public:
+ explicit LogMessageNotification(const LogMessageParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "window/logMessage";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TelemetryNotification : public Notification<JsonObject>
+{
+public:
+ explicit TelemetryNotification(const JsonObject &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "telemetry/event";
+
+ bool parametersAreValid(QString * /*error*/) const override { return params().has_value(); }
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/predicates.h b/src/shared/lsp/predicates.h
new file mode 100644
index 000000000..800a84757
--- /dev/null
+++ b/src/shared/lsp/predicates.h
@@ -0,0 +1,110 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+namespace lsp::Utils
+{
+
+//////////////////
+// find helpers
+//////////////////
+template<typename R, typename S, typename T>
+decltype(auto) equal(R (S::*function)() const, T value)
+{
+ // This should use std::equal_to<> instead of std::equal_to<T>,
+ // but that's not supported everywhere yet, since it is C++14
+ return std::bind<bool>(std::equal_to<T>(), value, std::bind(function, std::placeholders::_1));
+}
+
+template<typename R, typename S, typename T>
+decltype(auto) equal(R S::*member, T value)
+{
+ return std::bind<bool>(std::equal_to<T>(), value, std::bind(member, std::placeholders::_1));
+}
+
+//////////////////
+// comparison predicates
+//////////////////
+template <typename Type>
+auto equalTo(Type &&value)
+{
+ return [value = std::forward<Type>(value)] (const auto &entry)
+ {
+ static_assert(std::is_same<std::decay_t<Type>,
+ std::decay_t<decltype(entry)>>::value,
+ "The container and predicate type of equalTo should be the same to prevent "
+ "unnecessary conversion.");
+ return entry == value;
+ };
+}
+
+template <typename Type>
+auto unequalTo(Type &&value)
+{
+ return [value = std::forward<Type>(value)] (const auto &entry)
+ {
+ static_assert(std::is_same<std::decay_t<Type>,
+ std::decay_t<decltype(entry)>>::value,
+ "The container and predicate type of unequalTo should be the same to prevent "
+ "unnecessary conversion.");
+ return !(entry == value);
+ };
+}
+
+template <typename Type>
+auto lessThan(Type &&value)
+{
+ return [value = std::forward<Type>(value)] (const auto &entry)
+ {
+ static_assert(std::is_same<std::decay_t<Type>,
+ std::decay_t<decltype(entry)>>::value,
+ "The container and predicate type of unequalTo should be the same to prevent "
+ "unnecessary conversion.");
+ return entry < value;
+ };
+}
+
+template <typename Type>
+auto lessEqualThan(Type &&value)
+{
+ return [value = std::forward<Type>(value)] (const auto &entry)
+ {
+ static_assert(std::is_same<std::decay_t<Type>,
+ std::decay_t<decltype(entry)>>::value,
+ "The container and predicate type of lessEqualThan should be the same to "
+ "prevent unnecessary conversion.");
+ return !(value < entry);
+ };
+}
+
+template <typename Type>
+auto greaterThan(Type &&value)
+{
+ return [value = std::forward<Type>(value)] (const auto &entry)
+ {
+ static_assert(std::is_same<std::decay_t<Type>,
+ std::decay_t<decltype(entry)>>::value,
+ "The container and predicate type of greaterThan should be the same to "
+ "prevent unnecessary conversion.");
+ return value < entry;
+ };
+}
+
+template <typename Type>
+auto greaterEqualThan(Type &&value)
+{
+ return [value = std::forward<Type>(value)] (const auto &entry)
+ {
+ static_assert(std::is_same<std::decay_t<Type>,
+ std::decay_t<decltype(entry)>>::value,
+ "The container and predicate type of greaterEqualThan should be the same to "
+ "prevent unnecessary conversion.");
+ return !(entry < value);
+ };
+}
+} // namespace Utils
diff --git a/src/shared/lsp/progresssupport.cpp b/src/shared/lsp/progresssupport.cpp
new file mode 100644
index 000000000..6a554b4f4
--- /dev/null
+++ b/src/shared/lsp/progresssupport.cpp
@@ -0,0 +1,54 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "progresssupport.h"
+
+#include <QUuid>
+
+namespace lsp {
+
+ProgressToken::ProgressToken(const QJsonValue &value)
+{
+ if (!QBS_GUARD(value.isDouble() || value.isString()))
+ emplace<QString>(QUuid::createUuid().toString());
+ else if (value.isDouble())
+ emplace<int>(value.toInt());
+ else
+ emplace<QString>(value.toString());
+}
+
+ProgressToken::operator QJsonValue() const
+{
+ if (std::holds_alternative<QString>(*this))
+ return QJsonValue(std::get<QString>(*this));
+ return QJsonValue(std::get<int>(*this));
+}
+
+ProgressParams::ProgressType ProgressParams::value() const
+{
+ QJsonObject paramsValue = JsonObject::value(valueKey).toObject();
+ if (paramsValue[kindKey] == "begin")
+ return ProgressParams::ProgressType(WorkDoneProgressBegin(paramsValue));
+ if (paramsValue[kindKey] == "report")
+ return ProgressParams::ProgressType(WorkDoneProgressReport(paramsValue));
+ return ProgressParams::ProgressType(WorkDoneProgressEnd(paramsValue));
+}
+
+void ProgressParams::setValue(const ProgressParams::ProgressType &value)
+{
+ insertVariant<WorkDoneProgressBegin, WorkDoneProgressReport, WorkDoneProgressEnd>(valueKey, value);
+}
+
+ProgressNotification::ProgressNotification(const ProgressParams &params)
+ : Notification(methodName, params)
+{
+
+}
+
+WorkDoneProgressCreateRequest::WorkDoneProgressCreateRequest(const WorkDoneProgressCreateParams &params)
+ : Request(methodName, params)
+{
+
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/progresssupport.h b/src/shared/lsp/progresssupport.h
new file mode 100644
index 000000000..8ccc34c37
--- /dev/null
+++ b/src/shared/lsp/progresssupport.h
@@ -0,0 +1,152 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+
+#include <QJsonValue>
+
+#include <variant>
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT ProgressToken : public std::variant<int, QString>
+{
+public:
+ using variant::variant;
+ explicit ProgressToken(const QJsonValue &value);
+
+ bool isValid() { return true; }
+ operator QJsonValue() const;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkDoneProgressReport : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ /**
+ * Controls if a cancel button should be shown to allow the user to cancel the
+ * long running operation.
+ * Clients that don't support cancellation can ignore the setting.
+ */
+ std::optional<bool> cancellable() const { return optionalValue<bool>(cancellableKey); }
+ void setCancellable(bool cancellable) { insert(cancellableKey, cancellable); }
+ void clearCancellable() { remove(cancellableKey); }
+
+ /**
+ * Optional, more detailed associated progress message. Contains
+ * complementary information to the `title`.
+ *
+ * Examples: "3/25 files", "project/src/module2", "node_modules/some_dep".
+ * If unset, the previous progress message (if any) is still valid.
+ */
+ std::optional<QString> message() const { return optionalValue<QString>(messageKey); }
+ void setMessage(const QString &message) { insert(messageKey, message); }
+ void clearMessage() { remove(messageKey); }
+
+ /**
+ * Optional progress percentage to display (value 100 is considered 100%).
+ * If not provided infinite progress is assumed and clients are allowed
+ * to ignore the `percentage` value in subsequent in report notifications.
+ *
+ * The value should be steadily rising. Clients are free to ignore values
+ * that are not following this rule.
+ */
+
+ // Allthough percentage is defined as an uint by the protocol some server
+ // return a double here. Be less strict and also use a double.
+ // CAUTION: the range is still 0 - 100 and not 0 - 1
+ std::optional<double> percentage() const { return optionalValue<double>(percentageKey); }
+ void setPercentage(double percentage) { insert(percentageKey, percentage); }
+ void clearPercentage() { remove(percentageKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkDoneProgressBegin : public WorkDoneProgressReport
+{
+public:
+ using WorkDoneProgressReport::WorkDoneProgressReport;
+
+ /**
+ * Mandatory title of the progress operation. Used to briefly inform about
+ * the kind of operation being performed.
+ *
+ * Examples: "Indexing" or "Linking dependencies".
+ */
+
+ QString title() const { return typedValue<QString>(titleKey); }
+ void setTitle(const QString &title) { insert(titleKey, title); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkDoneProgressEnd : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ /**
+ * Optional, a final message indicating to for example indicate the outcome
+ * of the operation.
+ */
+ std::optional<QString> message() const { return optionalValue<QString>(messageKey); }
+ void setMessage(const QString &message) { insert(messageKey, message); }
+ void clearMessage() { remove(messageKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ProgressParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ ProgressToken token() const { return ProgressToken(JsonObject::value(tokenKey)); }
+ void setToken(const ProgressToken &token) { insert(tokenKey, token); }
+
+ using ProgressType
+ = std::variant<WorkDoneProgressBegin, WorkDoneProgressReport, WorkDoneProgressEnd>;
+ ProgressType value() const;
+ void setValue(const ProgressType &value);
+
+ bool isValid() const override { return contains(tokenKey) && contains(valueKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ProgressNotification : public Notification<ProgressParams>
+{
+public:
+ ProgressNotification(const ProgressParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "$/progress";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ProgressTokenParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // The token to be used to report progress.
+ ProgressToken token() const { return typedValue<ProgressToken>(tokenKey); }
+ void setToken(const ProgressToken &token) { insert(tokenKey, token); }
+};
+
+using WorkDoneProgressCreateParams = ProgressTokenParams;
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkDoneProgressCreateRequest
+ : public Request<std::nullptr_t, std::nullptr_t, WorkDoneProgressCreateParams>
+{
+public:
+ WorkDoneProgressCreateRequest(const WorkDoneProgressCreateParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "window/workDoneProgress/create";
+};
+
+using WorkDoneProgressCancelParams = ProgressTokenParams;
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkDoneProgressCancelRequest
+ : public Request<std::nullptr_t, std::nullptr_t, WorkDoneProgressCancelParams>
+{
+public:
+ using Request::Request;
+ constexpr static const char methodName[] = "window/workDoneProgress/cancel";
+};
+
+
+} // namespace lsp
diff --git a/src/shared/lsp/semantictokens.cpp b/src/shared/lsp/semantictokens.cpp
new file mode 100644
index 000000000..209966dcf
--- /dev/null
+++ b/src/shared/lsp/semantictokens.cpp
@@ -0,0 +1,141 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "semantictokens.h"
+
+namespace lsp {
+
+bool SemanticTokensLegend::isValid() const
+{
+ return contains(tokenTypesKey) && contains(tokenModifiersKey);
+}
+
+QMap<QString, int> SemanticTokens::defaultTokenTypesMap()
+{
+ QMap<QString, int> map;
+ map.insert("namespace", namespaceToken);
+ map.insert("type", typeToken);
+ map.insert("class", classToken);
+ map.insert("enum", enumToken);
+ map.insert("interface", interfaceToken);
+ map.insert("struct", structToken);
+ map.insert("typeParameter", typeParameterToken);
+ map.insert("parameter", parameterToken);
+ map.insert("variable", variableToken);
+ map.insert("property", propertyToken);
+ map.insert("enumMember", enumMemberToken);
+ map.insert("event", eventToken);
+ map.insert("function", functionToken);
+ map.insert("method", methodToken);
+ map.insert("macro", macroToken);
+ map.insert("keyword", keywordToken);
+ map.insert("modifier", modifierToken);
+ map.insert("comment", commentToken);
+ map.insert("string", stringToken);
+ map.insert("number", numberToken);
+ map.insert("regexp", regexpToken);
+ map.insert("operator", operatorToken);
+ return map;
+}
+
+QMap<QString, int> SemanticTokens::defaultTokenModifiersMap()
+{
+ QMap<QString, int> map;
+ map.insert("declaration", declarationModifier);
+ map.insert("definition", definitionModifier);
+ map.insert("readonly", readonlyModifier);
+ map.insert("static", staticModifier);
+ map.insert("deprecated", deprecatedModifier);
+ map.insert("abstract", abstractModifier);
+ map.insert("async", asyncModifier);
+ map.insert("modification", modificationModifier);
+ map.insert("documentation", documentationModifier);
+ map.insert("defaultLibrary", defaultLibraryModifier);
+ return map;
+}
+
+static int convertModifiers(int modifiersData, const QList<int> &tokenModifiers)
+{
+ int result = 0;
+ for (int i = 0; i < tokenModifiers.size() && modifiersData > 0; ++i) {
+ if (modifiersData & 0x1) {
+ const int modifier = tokenModifiers[i];
+ if (modifier > 0)
+ result |= modifier;
+ }
+ modifiersData = modifiersData >> 1;
+ }
+ return result;
+}
+
+QList<SemanticToken> SemanticTokens::toTokens(const QList<int> &tokenTypes,
+ const QList<int> &tokenModifiers) const
+{
+ const QList<int> &data = this->data();
+ if (data.size() % 5 != 0)
+ return {};
+ QList<SemanticToken> tokens;
+ tokens.reserve(int(data.size() / 5));
+ auto end = data.end();
+ for (auto it = data.begin(); it != end; it += 5) {
+ SemanticToken token;
+ token.deltaLine = *(it);
+ token.deltaStart = *(it + 1);
+ token.length = *(it + 2);
+ token.tokenIndex = *(it + 3);
+ token.tokenType = tokenTypes.value(token.tokenIndex, -1);
+ token.rawTokenModifiers = *(it + 4);
+ token.tokenModifiers = convertModifiers(token.rawTokenModifiers, tokenModifiers);
+ tokens << token;
+ }
+ return tokens;
+}
+
+bool SemanticTokensRangeParams::isValid() const
+{
+ return SemanticTokensParams::isValid() && contains(rangeKey);
+}
+
+SemanticTokensFullRequest::SemanticTokensFullRequest(const SemanticTokensParams &params)
+ : Request(methodName, params)
+{}
+
+SemanticTokensRangeRequest::SemanticTokensRangeRequest(const SemanticTokensRangeParams &params)
+ : Request(methodName, params)
+{}
+
+SemanticTokensResult::SemanticTokensResult(const QJsonValue &value)
+{
+ if (value.isObject())
+ emplace<SemanticTokens>(SemanticTokens(value.toObject()));
+ else
+ emplace<std::nullptr_t>(nullptr);
+}
+
+SemanticTokensFullDeltaRequest::SemanticTokensFullDeltaRequest(const SemanticTokensDeltaParams &params)
+ : Request(methodName, params)
+{}
+
+bool SemanticTokensDeltaParams::isValid() const
+{
+ return SemanticTokensParams::isValid() && contains(previousResultIdKey);
+}
+
+SemanticTokensDeltaResult::SemanticTokensDeltaResult(const QJsonValue &value)
+{
+ if (value.isObject()) {
+ QJsonObject object = value.toObject();
+ if (object.contains(editsKey))
+ emplace<SemanticTokensDelta>(object);
+ else
+ emplace<SemanticTokens>(object);
+ } else {
+ emplace<std::nullptr_t>(nullptr);
+ }
+}
+
+SemanticTokensRefreshRequest::SemanticTokensRefreshRequest()
+ : Request(methodName, nullptr)
+{}
+
+} // namespace lsp
diff --git a/src/shared/lsp/semantictokens.h b/src/shared/lsp/semantictokens.h
new file mode 100644
index 000000000..2dce74aa4
--- /dev/null
+++ b/src/shared/lsp/semantictokens.h
@@ -0,0 +1,230 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonkeys.h"
+#include "jsonobject.h"
+#include "jsonrpcmessages.h"
+#include "languageserverprotocol_global.h"
+#include "lsptypes.h"
+
+namespace lsp {
+
+struct LANGUAGESERVERPROTOCOL_EXPORT SemanticToken
+{
+ int deltaLine = 0;
+ int deltaStart = 0;
+ int length = 0;
+ int tokenIndex = 0;
+ int tokenType = 0;
+ int rawTokenModifiers = 0;
+ int tokenModifiers = 0;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensLegend : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // The token types a server uses.
+ QList<QString> tokenTypes() const { return array<QString>(tokenTypesKey); }
+ void setTokenTypes(const QList<QString> &tokenTypes) { insertArray(tokenTypesKey, tokenTypes); }
+
+ // The token modifiers a server uses.
+ QList<QString> tokenModifiers() const { return array<QString>(tokenModifiersKey); }
+ void setTokenModifiers(const QList<QString> &value) { insertArray(tokenModifiersKey, value); }
+
+ bool isValid() const override;
+};
+
+enum SemanticTokenTypes {
+ namespaceToken,
+ typeToken,
+ classToken,
+ enumToken,
+ interfaceToken,
+ structToken,
+ typeParameterToken,
+ parameterToken,
+ variableToken,
+ propertyToken,
+ enumMemberToken,
+ eventToken,
+ functionToken,
+ methodToken,
+ macroToken,
+ keywordToken,
+ modifierToken,
+ commentToken,
+ stringToken,
+ numberToken,
+ regexpToken,
+ operatorToken
+};
+
+enum SemanticTokenModifiers {
+ declarationModifier = 0x1,
+ definitionModifier = 0x2,
+ readonlyModifier = 0x4,
+ staticModifier = 0x8,
+ deprecatedModifier = 0x10,
+ abstractModifier = 0x20,
+ asyncModifier = 0x40,
+ modificationModifier = 0x80,
+ documentationModifier = 0x100,
+ defaultLibraryModifier = 0x200
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ bool isValid() const override { return contains(textDocumentKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensDeltaParams : public SemanticTokensParams
+{
+public:
+ using SemanticTokensParams::SemanticTokensParams;
+
+ QString previousResultId() const { return typedValue<QString>(previousResultIdKey); }
+ void setPreviousResultId(const QString &previousResultId)
+ {
+ insert(previousResultIdKey, previousResultId);
+ }
+
+ bool isValid() const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensRangeParams : public SemanticTokensParams
+{
+public:
+ using SemanticTokensParams::SemanticTokensParams;
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ bool isValid() const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokens : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ /**
+ * An optional result id. If provided and clients support delta updating
+ * the client will include the result id in the next semantic token request.
+ * A server can then instead of computing all semantic tokens again simply
+ * send a delta.
+ */
+ std::optional<QString> resultId() const { return optionalValue<QString>(resultIdKey); }
+ void setResultId(const QString &resultId) { insert(resultIdKey, resultId); }
+ void clearResultId() { remove(resultIdKey); }
+
+ /// The actual tokens.
+ QList<int> data() const { return array<int>(dataKey); }
+ void setData(const QList<int> &value) { insertArray(dataKey, value); }
+
+ bool isValid() const override { return contains(dataKey); }
+
+ QList<SemanticToken> toTokens(const QList<int> &tokenTypes,
+ const QList<int> &tokenModifiers) const;
+ static QMap<QString, int> defaultTokenTypesMap();
+ static QMap<QString, int> defaultTokenModifiersMap();
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensResult
+ : public std::variant<SemanticTokens, std::nullptr_t>
+{
+public:
+ using variant::variant;
+ explicit SemanticTokensResult(const QJsonValue &value);
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensFullRequest
+ : public Request<SemanticTokensResult, std::nullptr_t, SemanticTokensParams>
+{
+public:
+ explicit SemanticTokensFullRequest(const SemanticTokensParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/semanticTokens/full";
+};
+
+class SemanticTokensEdit : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ int start() const { return typedValue<int>(startKey); }
+ void setStart(int start) { insert(startKey, start); }
+
+ int deleteCount() const { return typedValue<int>(deleteCountKey); }
+ void setDeleteCount(int deleteCount) { insert(deleteCountKey, deleteCount); }
+
+ std::optional<QList<int>> data() const { return optionalArray<int>(dataKey); }
+ void setData(const QList<int> &value) { insertArray(dataKey, value); }
+ void clearData() { remove(dataKey); }
+
+ int dataSize() const { return contains(dataKey) ? value(dataKey).toArray().size() : 0; }
+
+ bool isValid() const override { return contains(dataKey) && contains(deleteCountKey); }
+};
+
+class SemanticTokensDelta : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString resultId() const { return typedValue<QString>(resultIdKey); }
+ void setResultId(const QString &resultId) { insert(resultIdKey, resultId); }
+
+ QList<SemanticTokensEdit> edits() const { return array<SemanticTokensEdit>(editsKey); }
+ void setEdits(const QList<SemanticTokensEdit> &edits) { insertArray(editsKey, edits); }
+
+ bool isValid() const override { return contains(resultIdKey) && contains(editsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensDeltaResult
+ : public std::variant<SemanticTokens, SemanticTokensDelta, std::nullptr_t>
+{
+public:
+ using variant::variant;
+ explicit SemanticTokensDeltaResult(const QJsonValue &value);
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensFullDeltaRequest
+ : public Request<SemanticTokensDeltaResult, std::nullptr_t, SemanticTokensDeltaParams>
+{
+public:
+ explicit SemanticTokensFullDeltaRequest(const SemanticTokensDeltaParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/semanticTokens/full/delta";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensRangeRequest
+ : public Request<SemanticTokensResult, std::nullptr_t, SemanticTokensRangeParams>
+{
+public:
+ explicit SemanticTokensRangeRequest(const SemanticTokensRangeParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/semanticTokens/range";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensRefreshRequest
+ : public Request<std::nullptr_t, std::nullptr_t, std::nullptr_t>
+{
+public:
+ explicit SemanticTokensRefreshRequest();
+ using Request::Request;
+ constexpr static const char methodName[] = "workspace/semanticTokens/refresh";
+};
+
+} // namespace lsp
diff --git a/src/shared/lsp/servercapabilities.cpp b/src/shared/lsp/servercapabilities.cpp
new file mode 100644
index 000000000..0923f76f3
--- /dev/null
+++ b/src/shared/lsp/servercapabilities.cpp
@@ -0,0 +1,386 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "servercapabilities.h"
+
+namespace lsp {
+
+std::optional<ServerCapabilities::TextDocumentSync> ServerCapabilities::textDocumentSync() const
+{
+ const QJsonValue &sync = value(textDocumentSyncKey);
+ if (sync.isUndefined())
+ return std::nullopt;
+ return std::make_optional(sync.isDouble()
+ ? TextDocumentSync(sync.toInt())
+ : TextDocumentSync(TextDocumentSyncOptions(sync.toObject())));
+}
+
+void ServerCapabilities::setTextDocumentSync(const ServerCapabilities::TextDocumentSync &textDocumentSync)
+{
+ insertVariant<TextDocumentSyncOptions, int>(textDocumentSyncKey, textDocumentSync);
+}
+
+TextDocumentSyncKind ServerCapabilities::textDocumentSyncKindHelper()
+{
+ if (std::optional<TextDocumentSync> sync = textDocumentSync()) {
+ if (auto kind = std::get_if<int>(&*sync))
+ return static_cast<TextDocumentSyncKind>(*kind);
+ if (auto options = std::get_if<TextDocumentSyncOptions>(&*sync)) {
+ if (const std::optional<int> &change = options->change())
+ return static_cast<TextDocumentSyncKind>(*change);
+ }
+ }
+ return TextDocumentSyncKind::None;
+}
+
+std::optional<std::variant<bool, WorkDoneProgressOptions>> ServerCapabilities::hoverProvider()
+ const
+{
+ using RetType = std::variant<bool, WorkDoneProgressOptions>;
+ const QJsonValue &provider = value(hoverProviderKey);
+ if (provider.isBool())
+ return std::make_optional(RetType(provider.toBool()));
+ if (provider.isObject())
+ return std::make_optional(RetType(WorkDoneProgressOptions(provider.toObject())));
+ return std::nullopt;
+}
+
+void ServerCapabilities::setHoverProvider(
+ const std::variant<bool, WorkDoneProgressOptions> &hoverProvider)
+{
+ insertVariant<bool, WorkDoneProgressOptions>(hoverProviderKey, hoverProvider);
+}
+
+std::optional<std::variant<bool, ServerCapabilities::RegistrationOptions>>
+ServerCapabilities::definitionProvider() const
+{
+ using RetType = std::variant<bool, ServerCapabilities::RegistrationOptions>;
+ const QJsonValue &provider = value(definitionProviderKey);
+ if (provider.isUndefined() || !(provider.isBool() || provider.isObject()))
+ return std::nullopt;
+ return std::make_optional(provider.isBool()
+ ? RetType(provider.toBool())
+ : RetType(RegistrationOptions(provider.toObject())));
+}
+
+void ServerCapabilities::setDefinitionProvider(
+ const std::variant<bool, RegistrationOptions> &definitionProvider)
+{
+ insertVariant<bool, RegistrationOptions>(definitionProviderKey, definitionProvider);
+}
+
+std::optional<std::variant<bool, ServerCapabilities::RegistrationOptions>>
+ServerCapabilities::typeDefinitionProvider() const
+{
+ using RetType = std::variant<bool, ServerCapabilities::RegistrationOptions>;
+ const QJsonValue &provider = value(typeDefinitionProviderKey);
+ if (provider.isUndefined() || !(provider.isBool() || provider.isObject()))
+ return std::nullopt;
+ return std::make_optional(provider.isBool()
+ ? RetType(provider.toBool())
+ : RetType(RegistrationOptions(provider.toObject())));
+}
+
+void ServerCapabilities::setTypeDefinitionProvider(
+ const std::variant<bool, ServerCapabilities::RegistrationOptions> &typeDefinitionProvider)
+{
+ insertVariant<bool, ServerCapabilities::RegistrationOptions>(typeDefinitionProviderKey,
+ typeDefinitionProvider);
+}
+
+std::optional<std::variant<bool, ServerCapabilities::RegistrationOptions>>
+ServerCapabilities::implementationProvider() const
+{
+ using RetType = std::variant<bool, ServerCapabilities::RegistrationOptions>;
+ const QJsonValue &provider = value(implementationProviderKey);
+ if (provider.isUndefined() || !(provider.isBool() || provider.isObject()))
+ return std::nullopt;
+ return std::make_optional(provider.isBool()
+ ? RetType(provider.toBool())
+ : RetType(RegistrationOptions(provider.toObject())));
+}
+
+void ServerCapabilities::setImplementationProvider(
+ const std::variant<bool, ServerCapabilities::RegistrationOptions> &implementationProvider)
+{
+ insertVariant<bool, RegistrationOptions>(implementationProviderKey, implementationProvider);
+}
+
+std::optional<std::variant<bool, WorkDoneProgressOptions>>
+ServerCapabilities::referencesProvider() const
+{
+ using RetType = std::variant<bool, WorkDoneProgressOptions>;
+ const QJsonValue &provider = value(referencesProviderKey);
+ if (provider.isBool())
+ return std::make_optional(RetType(provider.toBool()));
+ if (provider.isObject())
+ return std::make_optional(RetType(WorkDoneProgressOptions(provider.toObject())));
+ return std::nullopt;
+}
+
+void ServerCapabilities::setReferencesProvider(
+ const std::variant<bool, WorkDoneProgressOptions> &referencesProvider)
+{
+ insertVariant<bool, WorkDoneProgressOptions>(referencesProviderKey,
+ referencesProvider);
+}
+
+std::optional<std::variant<bool, WorkDoneProgressOptions>>
+ServerCapabilities::documentHighlightProvider() const
+{
+ using RetType = std::variant<bool, WorkDoneProgressOptions>;
+ const QJsonValue &provider = value(documentHighlightProviderKey);
+ if (provider.isBool())
+ return std::make_optional(RetType(provider.toBool()));
+ if (provider.isObject())
+ return std::make_optional(RetType(WorkDoneProgressOptions(provider.toObject())));
+ return std::nullopt;
+}
+
+void ServerCapabilities::setDocumentHighlightProvider(
+ const std::variant<bool, WorkDoneProgressOptions> &documentHighlightProvider)
+{
+ insertVariant<bool, WorkDoneProgressOptions>(documentHighlightProviderKey,
+ documentHighlightProvider);
+}
+
+std::optional<std::variant<bool, WorkDoneProgressOptions>>
+ServerCapabilities::documentSymbolProvider() const
+{
+ using RetType = std::variant<bool, WorkDoneProgressOptions>;
+ const QJsonValue &provider = value(documentSymbolProviderKey);
+ if (provider.isBool())
+ return std::make_optional(RetType(provider.toBool()));
+ if (provider.isObject())
+ return std::make_optional(RetType(WorkDoneProgressOptions(provider.toObject())));
+ return std::nullopt;
+}
+
+void ServerCapabilities::setDocumentSymbolProvider(
+ std::variant<bool, WorkDoneProgressOptions> documentSymbolProvider)
+{
+ insertVariant<bool, WorkDoneProgressOptions>(documentSymbolProviderKey,
+ documentSymbolProvider);
+}
+
+std::optional<SemanticTokensOptions> ServerCapabilities::semanticTokensProvider() const
+{
+ return optionalValue<SemanticTokensOptions>(semanticTokensProviderKey);
+}
+
+void ServerCapabilities::setSemanticTokensProvider(
+ const SemanticTokensOptions &semanticTokensProvider)
+{
+ insert(semanticTokensProviderKey, semanticTokensProvider);
+}
+
+std::optional<std::variant<bool, WorkDoneProgressOptions> >
+ServerCapabilities::callHierarchyProvider() const
+{
+ const QJsonValue &provider = value(callHierarchyProviderKey);
+ if (provider.isBool())
+ return provider.toBool();
+ else if (provider.isObject())
+ return WorkDoneProgressOptions(provider.toObject());
+ return std::nullopt;
+}
+
+void ServerCapabilities::setCallHierarchyProvider(
+ const std::variant<bool, WorkDoneProgressOptions> &callHierarchyProvider)
+{
+ QJsonValue val;
+ if (std::holds_alternative<bool>(callHierarchyProvider))
+ val = std::get<bool>(callHierarchyProvider);
+ else if (std::holds_alternative<WorkDoneProgressOptions>(callHierarchyProvider))
+ val = QJsonObject(std::get<WorkDoneProgressOptions>(callHierarchyProvider));
+ insert(callHierarchyProviderKey, val);
+}
+
+std::optional<std::variant<bool, WorkDoneProgressOptions>>
+ServerCapabilities::workspaceSymbolProvider() const
+{
+ using RetType = std::variant<bool, WorkDoneProgressOptions>;
+ const QJsonValue &provider = value(workspaceSymbolProviderKey);
+ if (provider.isBool())
+ return std::make_optional(RetType(provider.toBool()));
+ if (provider.isObject())
+ return std::make_optional(RetType(WorkDoneProgressOptions(provider.toObject())));
+ return std::nullopt;
+}
+
+void ServerCapabilities::setWorkspaceSymbolProvider(
+ std::variant<bool, WorkDoneProgressOptions> workspaceSymbolProvider)
+{
+ insertVariant<bool, WorkDoneProgressOptions>(workspaceSymbolProviderKey,
+ workspaceSymbolProvider);
+}
+
+std::optional<std::variant<bool, CodeActionOptions>> ServerCapabilities::codeActionProvider() const
+{
+ const QJsonValue &provider = value(codeActionProviderKey);
+ if (provider.isBool())
+ return std::make_optional(std::variant<bool, CodeActionOptions>(provider.toBool()));
+ if (provider.isObject()) {
+ CodeActionOptions options(provider);
+ if (options.isValid())
+ return std::make_optional(std::variant<bool, CodeActionOptions>(options));
+ }
+ return std::nullopt;
+}
+
+std::optional<std::variant<bool, WorkDoneProgressOptions>>
+ServerCapabilities::documentFormattingProvider() const
+{
+ using RetType = std::variant<bool, WorkDoneProgressOptions>;
+ const QJsonValue &provider = value(documentFormattingProviderKey);
+ if (provider.isBool())
+ return std::make_optional(RetType(provider.toBool()));
+ if (provider.isObject())
+ return std::make_optional(RetType(WorkDoneProgressOptions(provider.toObject())));
+ return std::nullopt;
+}
+
+void ServerCapabilities::setDocumentFormattingProvider(
+ const std::variant<bool, WorkDoneProgressOptions> &documentFormattingProvider)
+{
+ insertVariant<bool, WorkDoneProgressOptions>(documentFormattingProviderKey,
+ documentFormattingProvider);
+}
+
+std::optional<std::variant<bool, WorkDoneProgressOptions>>
+ServerCapabilities::documentRangeFormattingProvider() const
+{
+ using RetType = std::variant<bool, WorkDoneProgressOptions>;
+ const QJsonValue &provider = value(documentRangeFormattingProviderKey);
+ if (provider.isBool())
+ return std::make_optional(RetType(provider.toBool()));
+ if (provider.isObject())
+ return std::make_optional(RetType(WorkDoneProgressOptions(provider.toObject())));
+ return std::nullopt;
+}
+
+void ServerCapabilities::setDocumentRangeFormattingProvider(
+ std::variant<bool, WorkDoneProgressOptions> documentRangeFormattingProvider)
+{
+ insertVariant<bool, WorkDoneProgressOptions>(documentRangeFormattingProviderKey,
+ documentRangeFormattingProvider);
+}
+
+std::optional<std::variant<ServerCapabilities::RenameOptions, bool>> ServerCapabilities::renameProvider() const
+{
+ using RetType = std::variant<ServerCapabilities::RenameOptions, bool>;
+ const QJsonValue &localValue = value(renameProviderKey);
+ if (localValue.isBool())
+ return RetType(localValue.toBool());
+ if (localValue.isObject())
+ return RetType(RenameOptions(localValue.toObject()));
+ return std::nullopt;
+}
+
+void ServerCapabilities::setRenameProvider(std::variant<ServerCapabilities::RenameOptions, bool> renameProvider)
+{
+ insertVariant<RenameOptions, bool>(renameProviderKey, renameProvider);
+}
+
+std::optional<std::variant<bool, JsonObject>> ServerCapabilities::colorProvider() const
+{
+ using RetType = std::variant<bool, JsonObject>;
+ const QJsonValue &localValue = value(colorProviderKey);
+ if (localValue.isBool())
+ return RetType(localValue.toBool());
+ if (localValue.isObject())
+ return RetType(JsonObject(localValue.toObject()));
+ return std::nullopt;
+}
+
+void ServerCapabilities::setColorProvider(std::variant<bool, JsonObject> colorProvider)
+{
+ insertVariant<bool, JsonObject>(renameProviderKey, colorProvider);
+}
+
+std::optional<std::variant<QString, bool> >
+ServerCapabilities::WorkspaceServerCapabilities::WorkspaceFoldersCapabilities::changeNotifications() const
+{
+ using RetType = std::variant<QString, bool>;
+ const QJsonValue &change = value(changeNotificationsKey);
+ if (change.isUndefined())
+ return std::nullopt;
+ return std::make_optional(change.isBool() ? RetType(change.toBool())
+ : RetType(change.toString()));
+}
+
+void ServerCapabilities::WorkspaceServerCapabilities::WorkspaceFoldersCapabilities::setChangeNotifications(
+ std::variant<QString, bool> changeNotifications)
+{
+ insertVariant<QString, bool>(changeNotificationsKey, changeNotifications);
+}
+
+bool TextDocumentRegistrationOptions::filterApplies(const Utils::FilePath &fileName) const
+{
+ Q_UNUSED(fileName)
+ return true;
+}
+
+bool ServerCapabilities::ExecuteCommandOptions::isValid() const
+{
+ return WorkDoneProgressOptions::isValid() && contains(commandsKey);
+}
+
+bool CodeActionOptions::isValid() const
+{
+ return WorkDoneProgressOptions::isValid() && contains(codeActionKindsKey);
+}
+
+std::optional<std::variant<bool, QJsonObject>> SemanticTokensOptions::range() const
+{
+ using RetType = std::variant<bool, QJsonObject>;
+ const QJsonValue &rangeOptions = value(rangeKey);
+ if (rangeOptions.isBool())
+ return RetType(rangeOptions.toBool());
+ if (rangeOptions.isObject())
+ return RetType(rangeOptions.toObject());
+ return std::nullopt;
+}
+
+void SemanticTokensOptions::setRange(const std::variant<bool, QJsonObject> &range)
+{
+ insertVariant<bool, QJsonObject>(rangeKey, range);
+}
+
+std::optional<std::variant<bool, SemanticTokensOptions::FullSemanticTokenOptions>>
+SemanticTokensOptions::full() const
+{
+ using RetType = std::variant<bool, SemanticTokensOptions::FullSemanticTokenOptions>;
+ const QJsonValue &fullOptions = value(fullKey);
+ if (fullOptions.isBool())
+ return RetType(fullOptions.toBool());
+ if (fullOptions.isObject())
+ return RetType(FullSemanticTokenOptions(fullOptions.toObject()));
+ return std::nullopt;
+}
+
+void SemanticTokensOptions::setFull(
+ const std::variant<bool, SemanticTokensOptions::FullSemanticTokenOptions> &full)
+{
+ insertVariant<bool, FullSemanticTokenOptions>(fullKey, full);
+}
+
+SemanticRequestTypes SemanticTokensOptions::supportedRequests() const
+{
+ SemanticRequestTypes result;
+ QJsonValue rangeValue = value(rangeKey);
+ if (rangeValue.isObject() || rangeValue.toBool())
+ result |= SemanticRequestType::Range;
+ QJsonValue fullValue = value(fullKey);
+ if (fullValue.isObject()) {
+ SemanticTokensOptions::FullSemanticTokenOptions options(fullValue.toObject());
+ if (options.delta().value_or(false))
+ result |= SemanticRequestType::FullDelta;
+ result |= SemanticRequestType::Full;
+ } else if (fullValue.toBool()) {
+ result |= SemanticRequestType::Full;
+ }
+ return result;
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/servercapabilities.h b/src/shared/lsp/servercapabilities.h
new file mode 100644
index 000000000..03c0371e8
--- /dev/null
+++ b/src/shared/lsp/servercapabilities.h
@@ -0,0 +1,435 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "lsptypes.h"
+#include "semantictokens.h"
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkDoneProgressOptions : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ std::optional<bool> workDoneProgress() const { return optionalValue<bool>(workDoneProgressKey); }
+ void setWorkDoneProgress(bool workDoneProgress) { insert(workDoneProgressKey, workDoneProgress); }
+ void clearWorkDoneProgress() { remove(workDoneProgressKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ResolveProviderOption : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ std::optional<bool> resolveProvider() const { return optionalValue<bool>(resolveProviderKey); }
+ void setResolveProvider(bool resolveProvider) { insert(resolveProviderKey, resolveProvider); }
+ void clearResolveProvider() { remove(resolveProviderKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentRegistrationOptions : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ LanguageClientArray<DocumentFilter> documentSelector() const
+ { return clientArray<DocumentFilter>(documentSelectorKey); }
+ void setDocumentSelector(const LanguageClientArray<DocumentFilter> &documentSelector)
+ { insert(documentSelectorKey, documentSelector.toJson()); }
+
+ bool filterApplies(const Utils::FilePath &fileName) const;
+
+ bool isValid() const override { return contains(documentSelectorKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT SaveOptions : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // The client is supposed to include the content on save.
+ std::optional<bool> includeText() const { return optionalValue<bool>(includeTextKey); }
+ void setIncludeText(bool includeText) { insert(includeTextKey, includeText); }
+ void clearIncludeText() { remove(includeTextKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentSyncOptions : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // Open and close notifications are sent to the server.
+ std::optional<bool> openClose() const { return optionalValue<bool>(openCloseKey); }
+ void setOpenClose(bool openClose) { insert(openCloseKey, openClose); }
+ void clearOpenClose() { remove(openCloseKey); }
+
+ // Change notifications are sent to the server. See TextDocumentSyncKind.None,
+ // TextDocumentSyncKind.Full and TextDocumentSyncKind.Incremental.
+ std::optional<int> change() const { return optionalValue<int>(changeKey); }
+ void setChange(int change) { insert(changeKey, change); }
+ void clearChange() { remove(changeKey); }
+
+ // Will save notifications are sent to the server.
+ std::optional<bool> willSave() const { return optionalValue<bool>(willSaveKey); }
+ void setWillSave(bool willSave) { insert(willSaveKey, willSave); }
+ void clearWillSave() { remove(willSaveKey); }
+
+ // Will save wait until requests are sent to the server.
+ std::optional<bool> willSaveWaitUntil() const
+ { return optionalValue<bool>(willSaveWaitUntilKey); }
+ void setWillSaveWaitUntil(bool willSaveWaitUntil)
+ { insert(willSaveWaitUntilKey, willSaveWaitUntil); }
+ void clearWillSaveWaitUntil() { remove(willSaveWaitUntilKey); }
+
+ // Save notifications are sent to the server.
+ std::optional<SaveOptions> save() const { return optionalValue<SaveOptions>(saveKey); }
+ void setSave(const SaveOptions &save) { insert(saveKey, save); }
+ void clearSave() { remove(saveKey); }
+};
+
+enum class TextDocumentSyncKind
+{
+ // Documents should not be synced at all.
+ None = 0,
+ // Documents are synced by always sending the full content of the document.
+ Full = 1,
+ // Documents are synced by sending the full content on open.
+ // After that only incremental updates to the document are send.
+ Incremental = 2
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CodeActionOptions : public WorkDoneProgressOptions
+{
+public:
+ using WorkDoneProgressOptions::WorkDoneProgressOptions;
+
+ QList<QString> codeActionKinds() const { return array<QString>(codeActionKindsKey); }
+ void setCodeActionKinds(const QList<QString> &codeActionKinds)
+ { insertArray(codeActionKindsKey, codeActionKinds); }
+
+ bool isValid() const override;
+};
+
+enum class SemanticRequestType {
+ None = 0x0,
+ Full = 0x1,
+ FullDelta = 0x2,
+ Range = 0x4
+};
+Q_DECLARE_FLAGS(SemanticRequestTypes, SemanticRequestType)
+
+class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensOptions : public WorkDoneProgressOptions
+{
+public:
+ using WorkDoneProgressOptions::WorkDoneProgressOptions;
+
+ /// The legend used by the server
+ SemanticTokensLegend legend() const { return typedValue<SemanticTokensLegend>(legendKey); }
+ void setLegend(const SemanticTokensLegend &legend) { insert(legendKey, legend); }
+
+ /// Server supports providing semantic tokens for a specific range of a document.
+ std::optional<std::variant<bool, QJsonObject>> range() const;
+ void setRange(const std::variant<bool, QJsonObject> &range);
+ void clearRange() { remove(rangeKey); }
+
+ class FullSemanticTokenOptions : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ /// The server supports deltas for full documents.
+ std::optional<bool> delta() const { return optionalValue<bool>(deltaKey); }
+ void setDelta(bool delta) { insert(deltaKey, delta); }
+ void clearDelta() { remove(deltaKey); }
+ };
+
+ /// Server supports providing semantic tokens for a full document.
+ std::optional<std::variant<bool, FullSemanticTokenOptions>> full() const;
+ void setFull(const std::variant<bool, FullSemanticTokenOptions> &full);
+ void clearFull() { remove(fullKey); }
+
+ bool isValid() const override { return contains(legendKey); }
+
+ SemanticRequestTypes supportedRequests() const;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ServerCapabilities : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ // Defines how the host (editor) should sync document changes to the language server.
+
+ class LANGUAGESERVERPROTOCOL_EXPORT CompletionOptions : public WorkDoneProgressOptions
+ {
+ public:
+ using WorkDoneProgressOptions::WorkDoneProgressOptions;
+
+ // The characters that trigger completion automatically.
+ std::optional<QList<QString>> triggerCharacters() const
+ { return optionalArray<QString>(triggerCharactersKey); }
+ void setTriggerCharacters(const QList<QString> &triggerCharacters)
+ { insertArray(triggerCharactersKey, triggerCharacters); }
+ void clearTriggerCharacters() { remove(triggerCharactersKey); }
+
+ std::optional<bool> resolveProvider() const { return optionalValue<bool>(resolveProviderKey); }
+ void setResolveProvider(bool resolveProvider) { insert(resolveProviderKey, resolveProvider); }
+ void clearResolveProvider() { remove(resolveProviderKey); }
+ };
+
+ class LANGUAGESERVERPROTOCOL_EXPORT SignatureHelpOptions : public WorkDoneProgressOptions
+ {
+ public:
+ using WorkDoneProgressOptions::WorkDoneProgressOptions;
+
+ // The characters that trigger signature help automatically.
+ std::optional<QList<QString>> triggerCharacters() const
+ { return optionalArray<QString>(triggerCharactersKey); }
+ void setTriggerCharacters(const QList<QString> &triggerCharacters)
+ { insertArray(triggerCharactersKey, triggerCharacters); }
+ void clearTriggerCharacters() { remove(triggerCharactersKey); }
+ };
+
+ using CodeLensOptions = ResolveProviderOption;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT DocumentOnTypeFormattingOptions : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ // A character on which formatting should be triggered, like `}`.
+ QString firstTriggerCharacter() const { return typedValue<QString>(firstTriggerCharacterKey); }
+ void setFirstTriggerCharacter(QString firstTriggerCharacter)
+ { insert(firstTriggerCharacterKey, firstTriggerCharacter); }
+
+ // More trigger characters.
+ std::optional<QList<QString>> moreTriggerCharacter() const
+ { return optionalArray<QString>(moreTriggerCharacterKey); }
+ void setMoreTriggerCharacter(const QList<QString> &moreTriggerCharacter)
+ { insertArray(moreTriggerCharacterKey, moreTriggerCharacter); }
+ void clearMoreTriggerCharacter() { remove(moreTriggerCharacterKey); }
+
+ bool isValid() const override { return contains(firstTriggerCharacterKey); }
+ };
+
+ using DocumentLinkOptions = ResolveProviderOption;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT ExecuteCommandOptions : public WorkDoneProgressOptions
+ {
+ public:
+ using WorkDoneProgressOptions::WorkDoneProgressOptions;
+
+ QList<QString> commands() const { return array<QString>(commandsKey); }
+ void setCommands(const QList<QString> &commands) { insertArray(commandsKey, commands); }
+
+ bool isValid() const override;
+ };
+
+ using ColorProviderOptions = JsonObject;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT StaticRegistrationOptions : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ // The id used to register the request. The id can be used to deregister
+ // the request again. See also Registration#id.
+ std::optional<QString> id() const { return optionalValue<QString>(idKey); }
+ void setId(const QString &id) { insert(idKey, id); }
+ void clearId() { remove(idKey); }
+ };
+
+ // Defines how text documents are synced. Is either a detailed structure defining each
+ // notification or for backwards compatibility the TextDocumentSyncKind number.
+ using TextDocumentSync = std::variant<TextDocumentSyncOptions, int>;
+ std::optional<TextDocumentSync> textDocumentSync() const;
+ void setTextDocumentSync(const TextDocumentSync &textDocumentSync);
+ void clearTextDocumentSync() { remove(textDocumentSyncKey); }
+
+ TextDocumentSyncKind textDocumentSyncKindHelper();
+
+ // The server provides hover support.
+ std::optional<std::variant<bool, WorkDoneProgressOptions>> hoverProvider() const;
+ void setHoverProvider(const std::variant<bool, WorkDoneProgressOptions> &hoverProvider);
+ void clearHoverProvider() { remove(hoverProviderKey); }
+
+ // The server provides completion support.
+ std::optional<CompletionOptions> completionProvider() const
+ { return optionalValue<CompletionOptions>(completionProviderKey); }
+ void setCompletionProvider(const CompletionOptions &completionProvider)
+ { insert(completionProviderKey, completionProvider); }
+ void clearCompletionProvider() { remove(completionProviderKey); }
+
+ // The server provides signature help support.
+ std::optional<SignatureHelpOptions> signatureHelpProvider() const
+ { return optionalValue<SignatureHelpOptions>(signatureHelpProviderKey); }
+ void setSignatureHelpProvider(const SignatureHelpOptions &signatureHelpProvider)
+ { insert(signatureHelpProviderKey, signatureHelpProvider); }
+ void clearSignatureHelpProvider() { remove(signatureHelpProviderKey); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT RegistrationOptions : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ LanguageClientArray<DocumentFilter> documentSelector() const
+ { return clientArray<DocumentFilter>(documentSelectorKey); }
+ void setDocumentSelector(const LanguageClientArray<DocumentFilter> &documentSelector)
+ { insert(documentSelectorKey, documentSelector.toJson()); }
+
+ bool filterApplies(const Utils::FilePath &fileName) const;
+
+ // The id used to register the request. The id can be used to deregister
+ // the request again. See also Registration#id.
+ std::optional<QString> id() const { return optionalValue<QString>(idKey); }
+ void setId(const QString &id) { insert(idKey, id); }
+ void clearId() { remove(idKey); }
+
+ bool isValid() const override { return contains(documentSelectorKey); }
+ };
+
+ // The server provides goto definition support.
+ std::optional<std::variant<bool, RegistrationOptions>> definitionProvider() const;
+ void setDefinitionProvider(const std::variant<bool, RegistrationOptions> &typeDefinitionProvider);
+ void clearDefinitionProvider() { remove(typeDefinitionProviderKey); }
+
+ // The server provides Goto Type Definition support.
+ std::optional<std::variant<bool, RegistrationOptions>> typeDefinitionProvider() const;
+ void setTypeDefinitionProvider(const std::variant<bool, RegistrationOptions> &typeDefinitionProvider);
+ void clearTypeDefinitionProvider() { remove(typeDefinitionProviderKey); }
+
+ // The server provides Goto Implementation support.
+ std::optional<std::variant<bool, RegistrationOptions>> implementationProvider() const;
+ void setImplementationProvider(const std::variant<bool, RegistrationOptions> &implementationProvider);
+ void clearImplementationProvider() { remove(implementationProviderKey); }
+
+ // The server provides find references support.
+ std::optional<std::variant<bool, WorkDoneProgressOptions>> referencesProvider() const;
+ void setReferencesProvider(const std::variant<bool, WorkDoneProgressOptions> &referencesProvider);
+ void clearReferencesProvider() { remove(referencesProviderKey); }
+
+ // The server provides document highlight support.
+ std::optional<std::variant<bool, WorkDoneProgressOptions>> documentHighlightProvider() const;
+ void setDocumentHighlightProvider(
+ const std::variant<bool, WorkDoneProgressOptions> &documentHighlightProvider);
+ void clearDocumentHighlightProvider() { remove(documentHighlightProviderKey); }
+
+ // The server provides document symbol support.
+ std::optional<std::variant<bool, WorkDoneProgressOptions>> documentSymbolProvider() const;
+ void setDocumentSymbolProvider(std::variant<bool, WorkDoneProgressOptions> documentSymbolProvider);
+ void clearDocumentSymbolProvider() { remove(documentSymbolProviderKey); }
+
+ std::optional<SemanticTokensOptions> semanticTokensProvider() const;
+ void setSemanticTokensProvider(const SemanticTokensOptions &semanticTokensProvider);
+ void clearSemanticTokensProvider() { remove(semanticTokensProviderKey); }
+
+ std::optional<std::variant<bool, WorkDoneProgressOptions>> callHierarchyProvider() const;
+ void setCallHierarchyProvider(const std::variant<bool, WorkDoneProgressOptions> &callHierarchyProvider);
+ void clearCallHierarchyProvider() { remove(callHierarchyProviderKey); }
+
+ // The server provides workspace symbol support.
+ std::optional<std::variant<bool, WorkDoneProgressOptions>> workspaceSymbolProvider() const;
+ void setWorkspaceSymbolProvider(std::variant<bool, WorkDoneProgressOptions> workspaceSymbolProvider);
+ void clearWorkspaceSymbolProvider() { remove(workspaceSymbolProviderKey); }
+
+ // The server provides code actions.
+ std::optional<std::variant<bool, CodeActionOptions>> codeActionProvider() const;
+ void setCodeActionProvider(bool codeActionProvider)
+ { insert(codeActionProviderKey, codeActionProvider); }
+ void setCodeActionProvider(CodeActionOptions options)
+ { insert(codeActionProviderKey, options); }
+ void clearCodeActionProvider() { remove(codeActionProviderKey); }
+
+ // The server provides code lens.
+ std::optional<CodeLensOptions> codeLensProvider() const
+ { return optionalValue<CodeLensOptions>(codeLensProviderKey); }
+ void setCodeLensProvider(CodeLensOptions codeLensProvider)
+ { insert(codeLensProviderKey, codeLensProvider); }
+ void clearCodeLensProvider() { remove(codeLensProviderKey); }
+
+ // The server provides document formatting.
+ std::optional<std::variant<bool, WorkDoneProgressOptions>> documentFormattingProvider() const;
+ void setDocumentFormattingProvider(
+ const std::variant<bool, WorkDoneProgressOptions> &documentFormattingProvider);
+ void clearDocumentFormattingProvider() { remove(documentFormattingProviderKey); }
+
+ // The server provides document formatting on typing.
+ std::optional<std::variant<bool, WorkDoneProgressOptions>> documentRangeFormattingProvider() const;
+ void setDocumentRangeFormattingProvider(std::variant<bool, WorkDoneProgressOptions> documentRangeFormattingProvider);
+ void clearDocumentRangeFormattingProvider() { remove(documentRangeFormattingProviderKey); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT RenameOptions : public WorkDoneProgressOptions
+ {
+ public:
+ using WorkDoneProgressOptions::WorkDoneProgressOptions;
+
+ // Renames should be checked and tested before being executed.
+ std::optional<bool> prepareProvider() const { return optionalValue<bool>(prepareProviderKey); }
+ void setPrepareProvider(bool prepareProvider) { insert(prepareProviderKey, prepareProvider); }
+ void clearPrepareProvider() { remove(prepareProviderKey); }
+ };
+
+ // The server provides rename support.
+ std::optional<std::variant<RenameOptions, bool>> renameProvider() const;
+ void setRenameProvider(std::variant<RenameOptions,bool> renameProvider);
+ void clearRenameProvider() { remove(renameProviderKey); }
+
+ // The server provides document link support.
+ std::optional<DocumentLinkOptions> documentLinkProvider() const
+ { return optionalValue<DocumentLinkOptions>(documentLinkProviderKey); }
+ void setDocumentLinkProvider(const DocumentLinkOptions &documentLinkProvider)
+ { insert(documentLinkProviderKey, documentLinkProvider); }
+ void clearDocumentLinkProvider() { remove(documentLinkProviderKey); }
+
+ // The server provides color provider support.
+ std::optional<std::variant<bool, JsonObject>> colorProvider() const;
+ void setColorProvider(std::variant<bool, JsonObject> colorProvider);
+ void clearColorProvider() { remove(colorProviderKey); }
+
+ // The server provides execute command support.
+ std::optional<ExecuteCommandOptions> executeCommandProvider() const
+ { return optionalValue<ExecuteCommandOptions>(executeCommandProviderKey); }
+ void setExecuteCommandProvider(ExecuteCommandOptions executeCommandProvider)
+ { insert(executeCommandProviderKey, executeCommandProvider); }
+ void clearExecuteCommandProvider() { remove(executeCommandProviderKey); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceServerCapabilities : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceFoldersCapabilities : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ // The server has support for workspace folders
+ std::optional<bool> supported() const { return optionalValue<bool>(supportedKey); }
+ void setSupported(bool supported) { insert(supportedKey, supported); }
+ void clearSupported() { remove(supportedKey); }
+
+ std::optional<std::variant<QString, bool>> changeNotifications() const;
+ void setChangeNotifications(std::variant<QString, bool> changeNotifications);
+ void clearChangeNotifications() { remove(changeNotificationsKey); }
+ };
+
+ std::optional<WorkspaceFoldersCapabilities> workspaceFolders() const
+ { return optionalValue<WorkspaceFoldersCapabilities>(workspaceFoldersKey); }
+ void setWorkspaceFolders(const WorkspaceFoldersCapabilities &workspaceFolders)
+ { insert(workspaceFoldersKey, workspaceFolders); }
+ void clearWorkspaceFolders() { remove(workspaceFoldersKey); }
+ };
+
+ std::optional<WorkspaceServerCapabilities> workspace() const
+ { return optionalValue<WorkspaceServerCapabilities>(workspaceKey); }
+ void setWorkspace(const WorkspaceServerCapabilities &workspace)
+ { insert(workspaceKey, workspace); }
+ void clearWorkspace() { remove(workspaceKey); }
+
+ std::optional<JsonObject> experimental() const { return optionalValue<JsonObject>(experimentalKey); }
+ void setExperimental(const JsonObject &experimental) { insert(experimentalKey, experimental); }
+ void clearExperimental() { remove(experimentalKey); }
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/shutdownmessages.cpp b/src/shared/lsp/shutdownmessages.cpp
new file mode 100644
index 000000000..21c41c1d3
--- /dev/null
+++ b/src/shared/lsp/shutdownmessages.cpp
@@ -0,0 +1,13 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "shutdownmessages.h"
+
+namespace lsp {
+
+constexpr const char ShutdownRequest::methodName[];
+constexpr const char ExitNotification::methodName[];
+ShutdownRequest::ShutdownRequest() : Request(methodName, nullptr) { }
+ExitNotification::ExitNotification() : Notification(methodName) { }
+
+} // namespace lsp
diff --git a/src/shared/lsp/shutdownmessages.h b/src/shared/lsp/shutdownmessages.h
new file mode 100644
index 000000000..2e8da4c66
--- /dev/null
+++ b/src/shared/lsp/shutdownmessages.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT ShutdownRequest : public Request<
+ std::nullptr_t, std::nullptr_t, std::nullptr_t>
+{
+public:
+ ShutdownRequest();
+ using Request::Request;
+ constexpr static const char methodName[] = "shutdown";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ExitNotification : public Notification<std::nullptr_t>
+{
+public:
+ ExitNotification();
+ using Notification::Notification;
+ constexpr static const char methodName[] = "exit";
+
+ bool parametersAreValid(QString * /*errorMessage*/) const final { return true; }
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/textsynchronization.cpp b/src/shared/lsp/textsynchronization.cpp
new file mode 100644
index 000000000..ba9ee2f32
--- /dev/null
+++ b/src/shared/lsp/textsynchronization.cpp
@@ -0,0 +1,84 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "textsynchronization.h"
+
+namespace lsp {
+
+constexpr const char DidOpenTextDocumentNotification::methodName[];
+constexpr const char DidChangeTextDocumentNotification::methodName[];
+constexpr const char WillSaveTextDocumentNotification::methodName[];
+constexpr const char WillSaveWaitUntilTextDocumentRequest::methodName[];
+constexpr const char DidSaveTextDocumentNotification::methodName[];
+constexpr const char DidCloseTextDocumentNotification::methodName[];
+
+DidOpenTextDocumentNotification::DidOpenTextDocumentNotification(
+ const DidOpenTextDocumentParams &params)
+ : Notification(methodName, params)
+{ }
+
+DidChangeTextDocumentNotification::DidChangeTextDocumentNotification(
+ const DidChangeTextDocumentParams &params)
+ : DidChangeTextDocumentNotification(methodName, params)
+{ }
+
+WillSaveTextDocumentNotification::WillSaveTextDocumentNotification(
+ const WillSaveTextDocumentParams &params)
+ : Notification(methodName, params)
+{ }
+
+WillSaveWaitUntilTextDocumentRequest::WillSaveWaitUntilTextDocumentRequest(const WillSaveTextDocumentParams &params)
+ : Request(methodName, params)
+{ }
+
+DidSaveTextDocumentNotification::DidSaveTextDocumentNotification(
+ const DidSaveTextDocumentParams &params)
+ : Notification(methodName, params)
+{ }
+
+DidCloseTextDocumentNotification::DidCloseTextDocumentNotification(
+ const DidCloseTextDocumentParams &params)
+ : Notification(methodName, params)
+{ }
+
+DidChangeTextDocumentParams::DidChangeTextDocumentParams()
+ : DidChangeTextDocumentParams(VersionedTextDocumentIdentifier())
+{ }
+
+DidChangeTextDocumentParams::DidChangeTextDocumentParams(
+ const VersionedTextDocumentIdentifier &docId, const QString &text)
+{
+ setTextDocument(docId);
+ setContentChanges({TextDocumentContentChangeEvent(text)});
+}
+
+DidOpenTextDocumentParams::DidOpenTextDocumentParams(const TextDocumentItem &document)
+{
+ setTextDocument(document);
+}
+
+DidCloseTextDocumentParams::DidCloseTextDocumentParams(const TextDocumentIdentifier &document)
+{
+ setTextDocument(document);
+}
+
+DidChangeTextDocumentParams::TextDocumentContentChangeEvent::TextDocumentContentChangeEvent(
+ const QString &text)
+{
+ setText(text);
+}
+
+DidSaveTextDocumentParams::DidSaveTextDocumentParams(const TextDocumentIdentifier &document)
+{
+ setTextDocument(document);
+}
+
+WillSaveTextDocumentParams::WillSaveTextDocumentParams(
+ const TextDocumentIdentifier &document,
+ const WillSaveTextDocumentParams::TextDocumentSaveReason &reason)
+{
+ setTextDocument(document);
+ setReason(reason);
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/textsynchronization.h b/src/shared/lsp/textsynchronization.h
new file mode 100644
index 000000000..9ec3d4164
--- /dev/null
+++ b/src/shared/lsp/textsynchronization.h
@@ -0,0 +1,215 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+#include "servercapabilities.h"
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidOpenTextDocumentParams : public JsonObject
+{
+public:
+ DidOpenTextDocumentParams() = default;
+ explicit DidOpenTextDocumentParams(const TextDocumentItem &document);
+ using JsonObject::JsonObject;
+
+ TextDocumentItem textDocument() const { return typedValue<TextDocumentItem>(textDocumentKey); }
+ void setTextDocument(TextDocumentItem textDocument) { insert(textDocumentKey, textDocument); }
+
+ bool isValid() const override { return contains(textDocumentKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidOpenTextDocumentNotification : public Notification<
+ DidOpenTextDocumentParams>
+{
+public:
+ explicit DidOpenTextDocumentNotification(const DidOpenTextDocumentParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "textDocument/didOpen";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentChangeRegistrationOptions : public JsonObject
+{
+public:
+ TextDocumentChangeRegistrationOptions();
+ explicit TextDocumentChangeRegistrationOptions(TextDocumentSyncKind kind);
+ using JsonObject::JsonObject;
+
+ TextDocumentSyncKind syncKind() const
+ { return static_cast<TextDocumentSyncKind>(typedValue<int>(syncKindKey)); }
+ void setSyncKind(TextDocumentSyncKind syncKind)
+ { insert(syncKindKey, static_cast<int>(syncKind)); }
+
+ bool isValid() const override { return contains(syncKindKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidChangeTextDocumentParams : public JsonObject
+{
+public:
+ DidChangeTextDocumentParams();
+ explicit DidChangeTextDocumentParams(const VersionedTextDocumentIdentifier &docId,
+ const QString &text = QString());
+ using JsonObject::JsonObject;
+
+ VersionedTextDocumentIdentifier textDocument() const
+ { return typedValue<VersionedTextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const VersionedTextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentContentChangeEvent : public JsonObject
+ {
+ /*
+ * An event describing a change to a text document. If range and rangeLength are omitted
+ * the new text is considered to be the full content of the document.
+ */
+ public:
+ TextDocumentContentChangeEvent() = default;
+ explicit TextDocumentContentChangeEvent(const QString &text);
+ using JsonObject::JsonObject;
+
+ // The range of the document that changed.
+ std::optional<Range> range() const { return optionalValue<Range>(rangeKey); }
+ void setRange(Range range) { insert(rangeKey, range); }
+ void clearRange() { remove(rangeKey); }
+
+ // The length of the range that got replaced.
+ std::optional<int> rangeLength() const { return optionalValue<int>(rangeLengthKey); }
+ void setRangeLength(int rangeLength) { insert(rangeLengthKey, rangeLength); }
+ void clearRangeLength() { remove(rangeLengthKey); }
+
+ // The new text of the range/document.
+ QString text() const { return typedValue<QString>(textKey); }
+ void setText(const QString &text) { insert(textKey, text); }
+
+ bool isValid() const override { return contains(textKey); }
+ };
+
+ QList<TextDocumentContentChangeEvent> contentChanges() const
+ { return array<TextDocumentContentChangeEvent>(contentChangesKey); }
+ void setContentChanges(const QList<TextDocumentContentChangeEvent> &contentChanges)
+ { insertArray(contentChangesKey, contentChanges); }
+
+ bool isValid() const override
+ { return contains(textDocumentKey) && contains(contentChangesKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidChangeTextDocumentNotification : public Notification<
+ DidChangeTextDocumentParams>
+{
+public:
+ explicit DidChangeTextDocumentNotification(const DidChangeTextDocumentParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "textDocument/didChange";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WillSaveTextDocumentParams : public JsonObject
+{
+public:
+ enum class TextDocumentSaveReason {
+ Manual = 1,
+ AfterDelay = 2,
+ FocusOut = 3
+ };
+
+ WillSaveTextDocumentParams() : WillSaveTextDocumentParams(TextDocumentIdentifier()) {}
+ explicit WillSaveTextDocumentParams(
+ const TextDocumentIdentifier &document,
+ const TextDocumentSaveReason &reason = TextDocumentSaveReason::Manual);
+ using JsonObject::JsonObject;
+
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ TextDocumentSaveReason reason() const
+ { return static_cast<TextDocumentSaveReason>(typedValue<int>(reasonKey)); }
+ void setReason(TextDocumentSaveReason reason) { insert(reasonKey, static_cast<int>(reason)); }
+
+ bool isValid() const override { return contains(textDocumentKey) && contains(reasonKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WillSaveTextDocumentNotification : public Notification<
+ WillSaveTextDocumentParams>
+{
+public:
+ explicit WillSaveTextDocumentNotification(const WillSaveTextDocumentParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "textDocument/willSave";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WillSaveWaitUntilTextDocumentRequest : public Request<
+ LanguageClientArray<TextEdit>, std::nullptr_t, WillSaveTextDocumentParams>
+{
+public:
+ explicit WillSaveWaitUntilTextDocumentRequest(const WillSaveTextDocumentParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/willSaveWaitUntil";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TextDocumentSaveRegistrationOptions
+ : public TextDocumentRegistrationOptions
+{
+public:
+ using TextDocumentRegistrationOptions::TextDocumentRegistrationOptions;
+
+ std::optional<bool> includeText() const { return optionalValue<bool>(includeTextKey); }
+ void setIncludeText(bool includeText) { insert(includeTextKey, includeText); }
+ void clearIncludeText() { remove(includeTextKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidSaveTextDocumentParams : public JsonObject
+{
+public:
+ DidSaveTextDocumentParams() : DidSaveTextDocumentParams(TextDocumentIdentifier()) {}
+ explicit DidSaveTextDocumentParams(const TextDocumentIdentifier &document);
+ using JsonObject::JsonObject;
+
+ TextDocumentIdentifier textDocument()
+ const { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(TextDocumentIdentifier textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ std::optional<QString> text() const { return optionalValue<QString>(textKey); }
+ void setText(const QString &text) { insert(textKey, text); }
+ void clearText() { remove(textKey); }
+
+ bool isValid() const override { return contains(textDocumentKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidSaveTextDocumentNotification : public Notification<
+ DidSaveTextDocumentParams>
+{
+public:
+ explicit DidSaveTextDocumentNotification(const DidSaveTextDocumentParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "textDocument/didSave";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidCloseTextDocumentParams : public JsonObject
+{
+public:
+ DidCloseTextDocumentParams() = default;
+ explicit DidCloseTextDocumentParams(const TextDocumentIdentifier &document);
+ using JsonObject::JsonObject;
+
+ TextDocumentIdentifier textDocument() const
+ { return typedValue<TextDocumentIdentifier>(textDocumentKey); }
+ void setTextDocument(const TextDocumentIdentifier &textDocument)
+ { insert(textDocumentKey, textDocument); }
+
+ bool isValid() const override { return contains(textDocumentKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidCloseTextDocumentNotification : public Notification<
+ DidCloseTextDocumentParams>
+{
+public:
+ explicit DidCloseTextDocumentNotification(const DidCloseTextDocumentParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "textDocument/didClose";
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/lsp/textutils.cpp b/src/shared/lsp/textutils.cpp
new file mode 100644
index 000000000..1c0bff9d0
--- /dev/null
+++ b/src/shared/lsp/textutils.cpp
@@ -0,0 +1,126 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "textutils.h"
+
+#include <tools/qbsassert.h>
+
+#include <QRegularExpression>
+#include <QtDebug>
+
+namespace lsp::Utils::Text {
+
+bool Position::operator==(const Position &other) const
+{
+ return line == other.line && column == other.column;
+}
+
+/*!
+ Returns the text position of a \a fileName and sets the \a postfixPos if
+ it can find a positional postfix.
+
+ The following patterns are supported: \c {filepath.txt:19},
+ \c{filepath.txt:19:12}, \c {filepath.txt+19},
+ \c {filepath.txt+19+12}, and \c {filepath.txt(19)}.
+*/
+
+Position Position::fromFileName(QStringView fileName, int &postfixPos)
+{
+ static const auto regexp = QRegularExpression("[:+](\\d+)?([:+](\\d+)?)?$");
+ // (10) MSVC-style
+ static const auto vsRegexp = QRegularExpression("[(]((\\d+)[)]?)?$");
+ const QRegularExpressionMatch match = regexp.match(fileName);
+ Position pos;
+ if (match.hasMatch()) {
+ postfixPos = match.capturedStart(0);
+ if (match.lastCapturedIndex() > 0) {
+ pos.line = match.captured(1).toInt();
+ if (match.lastCapturedIndex() > 2) // index 2 includes the + or : for the column number
+ pos.column = match.captured(3).toInt() - 1; //column is 0 based, despite line being 1 based
+ }
+ } else {
+ const QRegularExpressionMatch vsMatch = vsRegexp.match(fileName);
+ postfixPos = vsMatch.capturedStart(0);
+ if (vsMatch.lastCapturedIndex() > 1) // index 1 includes closing )
+ pos.line = vsMatch.captured(2).toInt();
+ }
+ if (pos.line > 0 && pos.column < 0)
+ pos.column = 0; // if we got a valid line make sure to return a valid TextPosition
+ return pos;
+}
+
+int Range::length(const QString &text) const
+{
+ if (end.line < begin.line)
+ return -1;
+
+ if (begin.line == end.line)
+ return end.column - begin.column;
+
+ int index = 0;
+ int currentLine = 1;
+ while (currentLine < begin.line) {
+ index = text.indexOf(QChar::LineFeed, index);
+ if (index < 0)
+ return -1;
+ ++index;
+ ++currentLine;
+ }
+ const int beginIndex = index + begin.column;
+ while (currentLine < end.line) {
+ index = text.indexOf(QChar::LineFeed, index);
+ if (index < 0)
+ return -1;
+ ++index;
+ ++currentLine;
+ }
+ return index + end.column - beginIndex;
+}
+
+bool Range::operator==(const Range &other) const
+{
+ return begin == other.begin && end == other.end;
+}
+
+QString utf16LineTextInUtf8Buffer(const QByteArray &utf8Buffer, int currentUtf8Offset)
+{
+ const int lineStartUtf8Offset = currentUtf8Offset
+ ? (utf8Buffer.lastIndexOf('\n', currentUtf8Offset - 1) + 1)
+ : 0;
+ const int lineEndUtf8Offset = utf8Buffer.indexOf('\n', currentUtf8Offset);
+ return QString::fromUtf8(
+ utf8Buffer.mid(lineStartUtf8Offset, lineEndUtf8Offset - lineStartUtf8Offset));
+}
+
+static bool isByteOfMultiByteCodePoint(unsigned char byte)
+{
+ return byte & 0x80; // Check if most significant bit is set
+}
+
+bool utf8AdvanceCodePoint(const char *&current)
+{
+ if (Q_UNLIKELY(*current == '\0'))
+ return false;
+
+ // Process multi-byte UTF-8 code point (non-latin1)
+ if (Q_UNLIKELY(isByteOfMultiByteCodePoint(*current))) {
+ unsigned trailingBytesCurrentCodePoint = 1;
+ for (unsigned char c = (*current) << 2; isByteOfMultiByteCodePoint(c); c <<= 1)
+ ++trailingBytesCurrentCodePoint;
+ current += trailingBytesCurrentCodePoint + 1;
+
+ // Process single-byte UTF-8 code point (latin1)
+ } else {
+ ++current;
+ }
+
+ return true;
+}
+
+QDebug &operator<<(QDebug &stream, const Position &pos)
+{
+ stream << "line: " << pos.line << ", column: " << pos.column;
+ return stream;
+}
+
+} // namespace Utils::Text
diff --git a/src/shared/lsp/textutils.h b/src/shared/lsp/textutils.h
new file mode 100644
index 000000000..bf4ae083f
--- /dev/null
+++ b/src/shared/lsp/textutils.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QMetaType>
+#include <QString>
+
+namespace lsp::Utils::Text {
+
+class Position
+{
+public:
+ int line = 0; // 1-based
+ int column = -1; // 0-based
+
+ bool operator<(const Position &other) const
+ { return line < other.line || (line == other.line && column < other.column); }
+ bool operator==(const Position &other) const;
+
+ bool operator!=(const Position &other) const { return !(operator==(other)); }
+
+ bool isValid() const { return line > 0 && column >= 0; }
+
+ static Position fromFileName(QStringView fileName, int &postfixPos);
+};
+
+class Range
+{
+public:
+ int length(const QString &text) const;
+
+ Position begin;
+ Position end;
+
+ bool operator<(const Range &other) const { return begin < other.begin; }
+ bool operator==(const Range &other) const;
+
+ bool operator!=(const Range &other) const { return !(operator==(other)); }
+};
+
+QString utf16LineTextInUtf8Buffer(const QByteArray &utf8Buffer, int currentUtf8Offset);
+
+QDebug &operator<<(QDebug &stream, const Position &pos);
+
+} // Text
diff --git a/src/shared/lsp/workspace.cpp b/src/shared/lsp/workspace.cpp
new file mode 100644
index 000000000..e653bf96b
--- /dev/null
+++ b/src/shared/lsp/workspace.cpp
@@ -0,0 +1,82 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "workspace.h"
+
+namespace lsp {
+
+constexpr const char WorkSpaceFolderRequest::methodName[];
+constexpr const char DidChangeWorkspaceFoldersNotification::methodName[];
+constexpr const char DidChangeConfigurationNotification::methodName[];
+constexpr const char ConfigurationRequest::methodName[];
+constexpr const char WorkspaceSymbolRequest::methodName[];
+constexpr const char ExecuteCommandRequest::methodName[];
+constexpr const char ApplyWorkspaceEditRequest::methodName[];
+constexpr const char DidChangeWatchedFilesNotification::methodName[];
+
+WorkSpaceFolderRequest::WorkSpaceFolderRequest()
+ : Request(methodName, nullptr)
+{ }
+
+DidChangeWorkspaceFoldersNotification::DidChangeWorkspaceFoldersNotification(
+ const DidChangeWorkspaceFoldersParams &params)
+ : Notification(methodName, params)
+{ }
+
+DidChangeConfigurationNotification::DidChangeConfigurationNotification(
+ const DidChangeConfigurationParams &params)
+ : Notification(methodName, params)
+{ }
+
+ConfigurationRequest::ConfigurationRequest(const ConfigurationParams &params)
+ : Request(methodName, params)
+{ }
+
+WorkspaceSymbolRequest::WorkspaceSymbolRequest(const WorkspaceSymbolParams &params)
+ : Request(methodName, params)
+{ }
+
+ExecuteCommandRequest::ExecuteCommandRequest(const ExecuteCommandParams &params)
+ : Request(methodName, params)
+{ }
+
+ApplyWorkspaceEditRequest::ApplyWorkspaceEditRequest(const ApplyWorkspaceEditParams &params)
+ : Request(methodName, params)
+{ }
+
+WorkspaceFoldersChangeEvent::WorkspaceFoldersChangeEvent()
+{
+ insert(addedKey, QJsonArray());
+ insert(removedKey, QJsonArray());
+}
+
+DidChangeWatchedFilesNotification::DidChangeWatchedFilesNotification(
+ const DidChangeWatchedFilesParams &params)
+ : Notification(methodName, params)
+{ }
+
+ExecuteCommandParams::ExecuteCommandParams(const Command &command)
+{
+ setCommand(command.command());
+ if (command.arguments().has_value())
+ setArguments(*command.arguments());
+}
+
+WorkSpaceFolderResult::operator const QJsonValue() const
+{
+ if (!std::holds_alternative<QList<WorkSpaceFolder>>(*this))
+ return QJsonValue::Null;
+ QJsonArray array;
+ for (const auto &folder : std::get<QList<WorkSpaceFolder>>(*this))
+ array.append(QJsonValue(folder));
+ return array;
+}
+
+std::optional<DocumentUri> ConfigurationParams::ConfigurationItem::scopeUri() const
+{
+ if (const std::optional<QString> optionalScope = optionalValue<QString>(scopeUriKey))
+ return std::make_optional(DocumentUri::fromProtocol(*optionalScope));
+ return std::nullopt;
+}
+
+} // namespace lsp
diff --git a/src/shared/lsp/workspace.h b/src/shared/lsp/workspace.h
new file mode 100644
index 000000000..ffd37c1e6
--- /dev/null
+++ b/src/shared/lsp/workspace.h
@@ -0,0 +1,245 @@
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+
+namespace lsp {
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkSpaceFolderResult
+ : public std::variant<QList<WorkSpaceFolder>, std::nullptr_t>
+{
+public:
+ using variant::variant;
+ using variant::operator=;
+ operator const QJsonValue() const;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkSpaceFolderRequest : public Request<
+ WorkSpaceFolderResult, std::nullptr_t, std::nullptr_t>
+{
+public:
+ WorkSpaceFolderRequest();
+ using Request::Request;
+ constexpr static const char methodName[] = "workspace/workspaceFolders";
+
+ bool parametersAreValid(QString * /*errorMessage*/) const override { return true; }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceFoldersChangeEvent : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+ WorkspaceFoldersChangeEvent();
+
+ QList<WorkSpaceFolder> added() const { return array<WorkSpaceFolder>(addedKey); }
+ void setAdded(const QList<WorkSpaceFolder> &added) { insertArray(addedKey, added); }
+
+ QList<WorkSpaceFolder> removed() const { return array<WorkSpaceFolder>(removedKey); }
+ void setRemoved(const QList<WorkSpaceFolder> &removed) { insertArray(removedKey, removed); }
+
+ bool isValid() const override { return contains(addedKey) && contains(removedKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidChangeWorkspaceFoldersParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ WorkspaceFoldersChangeEvent event() const
+ { return typedValue<WorkspaceFoldersChangeEvent>(eventKey); }
+ void setEvent(const WorkspaceFoldersChangeEvent &event) { insert(eventKey, event); }
+
+ bool isValid() const override { return contains(eventKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidChangeWorkspaceFoldersNotification : public Notification<
+ DidChangeWorkspaceFoldersParams>
+{
+public:
+ explicit DidChangeWorkspaceFoldersNotification(const DidChangeWorkspaceFoldersParams &params);
+ constexpr static const char methodName[] = "workspace/didChangeWorkspaceFolders";
+ using Notification::Notification;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidChangeConfigurationParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QJsonValue settings() const { return value(settingsKey); }
+ void setSettings(QJsonValue settings) { insert(settingsKey, settings); }
+
+ bool isValid() const override { return contains(settingsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidChangeConfigurationNotification : public Notification<
+ DidChangeConfigurationParams>
+{
+public:
+ explicit DidChangeConfigurationNotification(const DidChangeConfigurationParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "workspace/didChangeConfiguration";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ConfigurationParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+ class LANGUAGESERVERPROTOCOL_EXPORT ConfigurationItem : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ std::optional<DocumentUri> scopeUri() const;
+ void setScopeUri(const DocumentUri &scopeUri) { insert(scopeUriKey, scopeUri); }
+ void clearScopeUri() { remove(scopeUriKey); }
+
+ std::optional<QString> section() const { return optionalValue<QString>(sectionKey); }
+ void setSection(const QString &section) { insert(sectionKey, section); }
+ void clearSection() { remove(sectionKey); }
+
+ bool isValid() const override { return contains(scopeUriKey); }
+ };
+
+ QList<ConfigurationItem> items() const { return array<ConfigurationItem>(itemsKey); }
+ void setItems(const QList<ConfigurationItem> &items) { insertArray(itemsKey, items); }
+
+ bool isValid() const override { return contains(itemsKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ConfigurationRequest
+ : public Request<QJsonArray, std::nullptr_t, ConfigurationParams>
+{
+public:
+ explicit ConfigurationRequest(const ConfigurationParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "workspace/configuration";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidChangeWatchedFilesParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ class FileEvent : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ int type() const { return typedValue<int>(typeKey); }
+ void setType(int type) { insert(typeKey, type); }
+
+ enum FileChangeType {
+ Created = 1,
+ Changed = 2,
+ Deleted = 3
+ };
+
+ bool isValid() const override { return contains(uriKey) && contains(typeKey); }
+ };
+
+ QList<FileEvent> changes() const { return array<FileEvent>(changesKey); }
+ void setChanges(const QList<FileEvent> &changes) { insertArray(changesKey, changes); }
+
+ bool isValid() const override { return contains(changesKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT DidChangeWatchedFilesNotification : public Notification<
+ DidChangeWatchedFilesParams>
+{
+public:
+ explicit DidChangeWatchedFilesNotification(const DidChangeWatchedFilesParams &params);
+ using Notification::Notification;
+ constexpr static const char methodName[] = "workspace/didChangeWatchedFiles";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceSymbolParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString query() const { return typedValue<QString>(queryKey); }
+ void setQuery(const QString &query) { insert(queryKey, query); }
+
+ void setLimit(int limit) { insert("limit", limit); } // clangd extension
+
+ bool isValid() const override { return contains(queryKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT WorkspaceSymbolRequest : public Request<
+ LanguageClientArray<SymbolInformation>, std::nullptr_t, WorkspaceSymbolParams>
+{
+public:
+ explicit WorkspaceSymbolRequest(const WorkspaceSymbolParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "workspace/symbol";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ExecuteCommandParams : public JsonObject
+{
+public:
+ explicit ExecuteCommandParams(const Command &command);
+ explicit ExecuteCommandParams(const QJsonValue &value) : JsonObject(value) {}
+ ExecuteCommandParams() : JsonObject() {}
+
+ QString command() const { return typedValue<QString>(commandKey); }
+ void setCommand(const QString &command) { insert(commandKey, command); }
+ void clearCommand() { remove(commandKey); }
+
+ std::optional<QJsonArray> arguments() const { return typedValue<QJsonArray>(argumentsKey); }
+ void setArguments(const QJsonArray &arguments) { insert(argumentsKey, arguments); }
+ void clearArguments() { remove(argumentsKey); }
+
+ bool isValid() const override { return contains(commandKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ExecuteCommandRequest : public Request<
+ QJsonValue, std::nullptr_t, ExecuteCommandParams>
+{
+public:
+ explicit ExecuteCommandRequest(const ExecuteCommandParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "workspace/executeCommand";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ApplyWorkspaceEditParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ std::optional<QString> label() const { return optionalValue<QString>(labelKey); }
+ void setLabel(const QString &label) { insert(labelKey, label); }
+ void clearLabel() { remove(labelKey); }
+
+ WorkspaceEdit edit() const { return typedValue<WorkspaceEdit>(editKey); }
+ void setEdit(const WorkspaceEdit &edit) { insert(editKey, edit); }
+
+ bool isValid() const override { return contains(editKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ApplyWorkspaceEditResult : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ bool applied() const { return typedValue<bool>(appliedKey); }
+ void setApplied(bool applied) { insert(appliedKey, applied); }
+
+ bool isValid() const override { return contains(appliedKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT ApplyWorkspaceEditRequest : public Request<
+ ApplyWorkspaceEditResult, std::nullptr_t, ApplyWorkspaceEditParams>
+{
+public:
+ explicit ApplyWorkspaceEditRequest(const ApplyWorkspaceEditParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "workspace/applyEdit";
+};
+
+} // namespace LanguageClient
diff --git a/src/shared/qtscript b/src/shared/qtscript
deleted file mode 160000
-Subproject 0d3b268f3880dea9555601fbd4d559201c239b7
diff --git a/src/shared/quickjs/.clang-tidy b/src/shared/quickjs/.clang-tidy
new file mode 100644
index 000000000..b9209aae5
--- /dev/null
+++ b/src/shared/quickjs/.clang-tidy
@@ -0,0 +1,3 @@
+Checks: '-*,misc-definitions-in-headers'
+
+InheritParentConfig: true
diff --git a/src/shared/quickjs/CMakeLists.txt b/src/shared/quickjs/CMakeLists.txt
new file mode 100644
index 000000000..a0af03c79
--- /dev/null
+++ b/src/shared/quickjs/CMakeLists.txt
@@ -0,0 +1,33 @@
+add_qbs_library(qbsquickjs
+ STATIC
+ SOURCES
+ cutils.c cutils.h
+ libregexp-opcode.h
+ libregexp.c libregexp.h
+ libunicode-table.h
+ libunicode.c libunicode.h
+ list.h
+ quickjs-atom.h
+ quickjs-opcode.h
+ quickjs.c quickjs.h
+ DEFINES
+ "CONFIG_VERSION=\"2021-03-27\""
+)
+if(QBS_QUICKJS_LEAK_CHECK)
+ add_definitions(-DDUMP_LEAKS)
+endif()
+target_include_directories(
+ qbsquickjs
+ SYSTEM
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+)
+
+add_library(qbsquickjsheaders INTERFACE)
+target_include_directories(
+ qbsquickjsheaders
+ SYSTEM
+ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
+)
+if(MSVC)
+ target_compile_options(qbsquickjs PUBLIC /wd4018 /wd4334 /wd4101 /wd4146 /wd4244 /wd4267 /wd4996)
+endif()
diff --git a/src/shared/quickjs/LICENSE b/src/shared/quickjs/LICENSE
new file mode 100644
index 000000000..2c8fdebaf
--- /dev/null
+++ b/src/shared/quickjs/LICENSE
@@ -0,0 +1,22 @@
+QuickJS Javascript Engine
+
+Copyright (c) 2017-2021 Fabrice Bellard
+Copyright (c) 2017-2021 Charlie Gordon
+
+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.
diff --git a/src/shared/quickjs/cutils.c b/src/shared/quickjs/cutils.c
new file mode 100644
index 000000000..1f66fff3f
--- /dev/null
+++ b/src/shared/quickjs/cutils.c
@@ -0,0 +1,630 @@
+/*
+ * C utilities
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ * Copyright (c) 2018 Charlie Gordon
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "cutils.h"
+
+void pstrcpy(char *buf, int buf_size, const char *str)
+{
+ int c;
+ char *q = buf;
+
+ if (buf_size <= 0)
+ return;
+
+ for(;;) {
+ c = *str++;
+ if (c == 0 || q >= buf + buf_size - 1)
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+}
+
+/* strcat and truncate. */
+char *pstrcat(char *buf, int buf_size, const char *s)
+{
+ int len;
+ len = strlen(buf);
+ if (len < buf_size)
+ pstrcpy(buf + len, buf_size - len, s);
+ return buf;
+}
+
+int strstart(const char *str, const char *val, const char **ptr)
+{
+ const char *p, *q;
+ p = str;
+ q = val;
+ while (*q != '\0') {
+ if (*p != *q)
+ return 0;
+ p++;
+ q++;
+ }
+ if (ptr)
+ *ptr = p;
+ return 1;
+}
+
+int has_suffix(const char *str, const char *suffix)
+{
+ size_t len = strlen(str);
+ size_t slen = strlen(suffix);
+ return (len >= slen && !memcmp(str + len - slen, suffix, slen));
+}
+
+/* Dynamic buffer package */
+
+static void *dbuf_default_realloc(void *opaque, void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func)
+{
+ memset(s, 0, sizeof(*s));
+ if (!realloc_func)
+ realloc_func = dbuf_default_realloc;
+ s->opaque = opaque;
+ s->realloc_func = realloc_func;
+}
+
+void dbuf_init(DynBuf *s)
+{
+ dbuf_init2(s, NULL, NULL);
+}
+
+/* return < 0 if error */
+int dbuf_realloc(DynBuf *s, size_t new_size)
+{
+ size_t size;
+ uint8_t *new_buf;
+ if (new_size > s->allocated_size) {
+ if (s->error)
+ return -1;
+ size = s->allocated_size * 3 / 2;
+ if (size > new_size)
+ new_size = size;
+ new_buf = s->realloc_func(s->opaque, s->buf, new_size);
+ if (!new_buf) {
+ s->error = TRUE;
+ return -1;
+ }
+ s->buf = new_buf;
+ s->allocated_size = new_size;
+ }
+ return 0;
+}
+
+int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len)
+{
+ size_t end;
+ end = offset + len;
+ if (dbuf_realloc(s, end))
+ return -1;
+ memcpy(s->buf + offset, data, len);
+ if (end > s->size)
+ s->size = end;
+ return 0;
+}
+
+int dbuf_put(DynBuf *s, const uint8_t *data, size_t len)
+{
+ if (unlikely((s->size + len) > s->allocated_size)) {
+ if (dbuf_realloc(s, s->size + len))
+ return -1;
+ }
+ memcpy(s->buf + s->size, data, len);
+ s->size += len;
+ return 0;
+}
+
+int dbuf_put_self(DynBuf *s, size_t offset, size_t len)
+{
+ if (unlikely((s->size + len) > s->allocated_size)) {
+ if (dbuf_realloc(s, s->size + len))
+ return -1;
+ }
+ memcpy(s->buf + s->size, s->buf + offset, len);
+ s->size += len;
+ return 0;
+}
+
+int dbuf_putc(DynBuf *s, uint8_t c)
+{
+ return dbuf_put(s, &c, 1);
+}
+
+int dbuf_putstr(DynBuf *s, const char *str)
+{
+ return dbuf_put(s, (const uint8_t *)str, strlen(str));
+}
+
+int FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[128];
+ int len;
+
+ va_start(ap, fmt);
+ len = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ if (len < sizeof(buf)) {
+ /* fast case */
+ return dbuf_put(s, (uint8_t *)buf, len);
+ } else {
+ if (dbuf_realloc(s, s->size + len + 1))
+ return -1;
+ va_start(ap, fmt);
+ vsnprintf((char *)(s->buf + s->size), s->allocated_size - s->size,
+ fmt, ap);
+ va_end(ap);
+ s->size += len;
+ }
+ return 0;
+}
+
+void dbuf_free(DynBuf *s)
+{
+ /* we test s->buf as a fail safe to avoid crashing if dbuf_free()
+ is called twice */
+ if (s->buf) {
+ s->realloc_func(s->opaque, s->buf, 0);
+ }
+ memset(s, 0, sizeof(*s));
+}
+
+/* Note: at most 31 bits are encoded. At most UTF8_CHAR_LEN_MAX bytes
+ are output. */
+int unicode_to_utf8(uint8_t *buf, unsigned int c)
+{
+ uint8_t *q = buf;
+
+ if (c < 0x80) {
+ *q++ = c;
+ } else {
+ if (c < 0x800) {
+ *q++ = (c >> 6) | 0xc0;
+ } else {
+ if (c < 0x10000) {
+ *q++ = (c >> 12) | 0xe0;
+ } else {
+ if (c < 0x00200000) {
+ *q++ = (c >> 18) | 0xf0;
+ } else {
+ if (c < 0x04000000) {
+ *q++ = (c >> 24) | 0xf8;
+ } else if (c < 0x80000000) {
+ *q++ = (c >> 30) | 0xfc;
+ *q++ = ((c >> 24) & 0x3f) | 0x80;
+ } else {
+ return 0;
+ }
+ *q++ = ((c >> 18) & 0x3f) | 0x80;
+ }
+ *q++ = ((c >> 12) & 0x3f) | 0x80;
+ }
+ *q++ = ((c >> 6) & 0x3f) | 0x80;
+ }
+ *q++ = (c & 0x3f) | 0x80;
+ }
+ return q - buf;
+}
+
+static const unsigned int utf8_min_code[5] = {
+ 0x80, 0x800, 0x10000, 0x00200000, 0x04000000,
+};
+
+static const unsigned char utf8_first_code_mask[5] = {
+ 0x1f, 0xf, 0x7, 0x3, 0x1,
+};
+
+/* return -1 if error. *pp is not updated in this case. max_len must
+ be >= 1. The maximum length for a UTF8 byte sequence is 6 bytes. */
+int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp)
+{
+ int l, c, b, i;
+
+ c = *p++;
+ if (c < 0x80) {
+ *pp = p;
+ return c;
+ }
+ switch(c) {
+ case 0xc0: case 0xc1: case 0xc2: case 0xc3:
+ case 0xc4: case 0xc5: case 0xc6: case 0xc7:
+ case 0xc8: case 0xc9: case 0xca: case 0xcb:
+ case 0xcc: case 0xcd: case 0xce: case 0xcf:
+ case 0xd0: case 0xd1: case 0xd2: case 0xd3:
+ case 0xd4: case 0xd5: case 0xd6: case 0xd7:
+ case 0xd8: case 0xd9: case 0xda: case 0xdb:
+ case 0xdc: case 0xdd: case 0xde: case 0xdf:
+ l = 1;
+ break;
+ case 0xe0: case 0xe1: case 0xe2: case 0xe3:
+ case 0xe4: case 0xe5: case 0xe6: case 0xe7:
+ case 0xe8: case 0xe9: case 0xea: case 0xeb:
+ case 0xec: case 0xed: case 0xee: case 0xef:
+ l = 2;
+ break;
+ case 0xf0: case 0xf1: case 0xf2: case 0xf3:
+ case 0xf4: case 0xf5: case 0xf6: case 0xf7:
+ l = 3;
+ break;
+ case 0xf8: case 0xf9: case 0xfa: case 0xfb:
+ l = 4;
+ break;
+ case 0xfc: case 0xfd:
+ l = 5;
+ break;
+ default:
+ return -1;
+ }
+ /* check that we have enough characters */
+ if (l > (max_len - 1))
+ return -1;
+ c &= utf8_first_code_mask[l - 1];
+ for(i = 0; i < l; i++) {
+ b = *p++;
+ if (b < 0x80 || b >= 0xc0)
+ return -1;
+ c = (c << 6) | (b & 0x3f);
+ }
+ if (c < utf8_min_code[l - 1])
+ return -1;
+ *pp = p;
+ return c;
+}
+
+#if 0
+
+#if defined(EMSCRIPTEN) || defined(__ANDROID__)
+
+static void *rqsort_arg;
+static int (*rqsort_cmp)(const void *, const void *, void *);
+
+static int rqsort_cmp2(const void *p1, const void *p2)
+{
+ return rqsort_cmp(p1, p2, rqsort_arg);
+}
+
+/* not reentrant, but not needed with emscripten */
+void rqsort(void *base, size_t nmemb, size_t size,
+ int (*cmp)(const void *, const void *, void *),
+ void *arg)
+{
+ rqsort_arg = arg;
+ rqsort_cmp = cmp;
+ qsort(base, nmemb, size, rqsort_cmp2);
+}
+
+#endif
+
+#else
+
+typedef void (*exchange_f)(void *a, void *b, size_t size);
+typedef int (*cmp_f)(const void *, const void *, void *opaque);
+
+static void exchange_bytes(void *a, void *b, size_t size) {
+ uint8_t *ap = (uint8_t *)a;
+ uint8_t *bp = (uint8_t *)b;
+
+ while (size-- != 0) {
+ uint8_t t = *ap;
+ *ap++ = *bp;
+ *bp++ = t;
+ }
+}
+
+static void exchange_one_byte(void *a, void *b, size_t size) {
+ uint8_t *ap = (uint8_t *)a;
+ uint8_t *bp = (uint8_t *)b;
+ uint8_t t = *ap;
+ *ap = *bp;
+ *bp = t;
+}
+
+static void exchange_int16s(void *a, void *b, size_t size) {
+ uint16_t *ap = (uint16_t *)a;
+ uint16_t *bp = (uint16_t *)b;
+
+ for (size /= sizeof(uint16_t); size-- != 0;) {
+ uint16_t t = *ap;
+ *ap++ = *bp;
+ *bp++ = t;
+ }
+}
+
+static void exchange_one_int16(void *a, void *b, size_t size) {
+ uint16_t *ap = (uint16_t *)a;
+ uint16_t *bp = (uint16_t *)b;
+ uint16_t t = *ap;
+ *ap = *bp;
+ *bp = t;
+}
+
+static void exchange_int32s(void *a, void *b, size_t size) {
+ uint32_t *ap = (uint32_t *)a;
+ uint32_t *bp = (uint32_t *)b;
+
+ for (size /= sizeof(uint32_t); size-- != 0;) {
+ uint32_t t = *ap;
+ *ap++ = *bp;
+ *bp++ = t;
+ }
+}
+
+static void exchange_one_int32(void *a, void *b, size_t size) {
+ uint32_t *ap = (uint32_t *)a;
+ uint32_t *bp = (uint32_t *)b;
+ uint32_t t = *ap;
+ *ap = *bp;
+ *bp = t;
+}
+
+static void exchange_int64s(void *a, void *b, size_t size) {
+ uint64_t *ap = (uint64_t *)a;
+ uint64_t *bp = (uint64_t *)b;
+
+ for (size /= sizeof(uint64_t); size-- != 0;) {
+ uint64_t t = *ap;
+ *ap++ = *bp;
+ *bp++ = t;
+ }
+}
+
+static void exchange_one_int64(void *a, void *b, size_t size) {
+ uint64_t *ap = (uint64_t *)a;
+ uint64_t *bp = (uint64_t *)b;
+ uint64_t t = *ap;
+ *ap = *bp;
+ *bp = t;
+}
+
+static void exchange_int128s(void *a, void *b, size_t size) {
+ uint64_t *ap = (uint64_t *)a;
+ uint64_t *bp = (uint64_t *)b;
+
+ for (size /= sizeof(uint64_t) * 2; size-- != 0; ap += 2, bp += 2) {
+ uint64_t t = ap[0];
+ uint64_t u = ap[1];
+ ap[0] = bp[0];
+ ap[1] = bp[1];
+ bp[0] = t;
+ bp[1] = u;
+ }
+}
+
+static void exchange_one_int128(void *a, void *b, size_t size) {
+ uint64_t *ap = (uint64_t *)a;
+ uint64_t *bp = (uint64_t *)b;
+ uint64_t t = ap[0];
+ uint64_t u = ap[1];
+ ap[0] = bp[0];
+ ap[1] = bp[1];
+ bp[0] = t;
+ bp[1] = u;
+}
+
+static inline exchange_f exchange_func(const void *base, size_t size) {
+ switch (((uintptr_t)base | (uintptr_t)size) & 15) {
+ case 0:
+ if (size == sizeof(uint64_t) * 2)
+ return exchange_one_int128;
+ else
+ return exchange_int128s;
+ case 8:
+ if (size == sizeof(uint64_t))
+ return exchange_one_int64;
+ else
+ return exchange_int64s;
+ case 4:
+ case 12:
+ if (size == sizeof(uint32_t))
+ return exchange_one_int32;
+ else
+ return exchange_int32s;
+ case 2:
+ case 6:
+ case 10:
+ case 14:
+ if (size == sizeof(uint16_t))
+ return exchange_one_int16;
+ else
+ return exchange_int16s;
+ default:
+ if (size == 1)
+ return exchange_one_byte;
+ else
+ return exchange_bytes;
+ }
+}
+
+static void heapsortx(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
+{
+ uint8_t *basep = (uint8_t *)base;
+ size_t i, n, c, r;
+ exchange_f swap = exchange_func(base, size);
+
+ if (nmemb > 1) {
+ i = (nmemb / 2) * size;
+ n = nmemb * size;
+
+ while (i > 0) {
+ i -= size;
+ for (r = i; (c = r * 2 + size) < n; r = c) {
+ if (c < n - size && cmp(basep + c, basep + c + size, opaque) <= 0)
+ c += size;
+ if (cmp(basep + r, basep + c, opaque) > 0)
+ break;
+ swap(basep + r, basep + c, size);
+ }
+ }
+ for (i = n - size; i > 0; i -= size) {
+ swap(basep, basep + i, size);
+
+ for (r = 0; (c = r * 2 + size) < i; r = c) {
+ if (c < i - size && cmp(basep + c, basep + c + size, opaque) <= 0)
+ c += size;
+ if (cmp(basep + r, basep + c, opaque) > 0)
+ break;
+ swap(basep + r, basep + c, size);
+ }
+ }
+ }
+}
+
+static inline void *med3(void *a, void *b, void *c, cmp_f cmp, void *opaque)
+{
+ return cmp(a, b, opaque) < 0 ?
+ (cmp(b, c, opaque) < 0 ? b : (cmp(a, c, opaque) < 0 ? c : a )) :
+ (cmp(b, c, opaque) > 0 ? b : (cmp(a, c, opaque) < 0 ? a : c ));
+}
+
+/* pointer based version with local stack and insertion sort threshhold */
+void rqsort(void *base, size_t nmemb, size_t size, cmp_f cmp, void *opaque)
+{
+ struct { uint8_t *base; size_t count; int depth; } stack[50], *sp = stack;
+ uint8_t *ptr, *pi, *pj, *plt, *pgt, *top, *m;
+ size_t m4, i, lt, gt, span, span2;
+ int c, depth;
+ exchange_f swap = exchange_func(base, size);
+ exchange_f swap_block = exchange_func(base, size | 128);
+
+ if (nmemb < 2 || size <= 0)
+ return;
+
+ sp->base = (uint8_t *)base;
+ sp->count = nmemb;
+ sp->depth = 0;
+ sp++;
+
+ while (sp > stack) {
+ sp--;
+ ptr = sp->base;
+ nmemb = sp->count;
+ depth = sp->depth;
+
+ while (nmemb > 6) {
+ if (++depth > 50) {
+ /* depth check to ensure worst case logarithmic time */
+ heapsortx(ptr, nmemb, size, cmp, opaque);
+ nmemb = 0;
+ break;
+ }
+ /* select median of 3 from 1/4, 1/2, 3/4 positions */
+ /* should use median of 5 or 9? */
+ m4 = (nmemb >> 2) * size;
+ m = med3(ptr + m4, ptr + 2 * m4, ptr + 3 * m4, cmp, opaque);
+ swap(ptr, m, size); /* move the pivot to the start or the array */
+ i = lt = 1;
+ pi = plt = ptr + size;
+ gt = nmemb;
+ pj = pgt = top = ptr + nmemb * size;
+ for (;;) {
+ while (pi < pj && (c = cmp(ptr, pi, opaque)) >= 0) {
+ if (c == 0) {
+ swap(plt, pi, size);
+ lt++;
+ plt += size;
+ }
+ i++;
+ pi += size;
+ }
+ while (pi < (pj -= size) && (c = cmp(ptr, pj, opaque)) <= 0) {
+ if (c == 0) {
+ gt--;
+ pgt -= size;
+ swap(pgt, pj, size);
+ }
+ }
+ if (pi >= pj)
+ break;
+ swap(pi, pj, size);
+ i++;
+ pi += size;
+ }
+ /* array has 4 parts:
+ * from 0 to lt excluded: elements identical to pivot
+ * from lt to pi excluded: elements smaller than pivot
+ * from pi to gt excluded: elements greater than pivot
+ * from gt to n excluded: elements identical to pivot
+ */
+ /* move elements identical to pivot in the middle of the array: */
+ /* swap values in ranges [0..lt[ and [i-lt..i[
+ swapping the smallest span between lt and i-lt is sufficient
+ */
+ span = plt - ptr;
+ span2 = pi - plt;
+ lt = i - lt;
+ if (span > span2)
+ span = span2;
+ swap_block(ptr, pi - span, span);
+ /* swap values in ranges [gt..top[ and [i..top-(top-gt)[
+ swapping the smallest span between top-gt and gt-i is sufficient
+ */
+ span = top - pgt;
+ span2 = pgt - pi;
+ pgt = top - span2;
+ gt = nmemb - (gt - i);
+ if (span > span2)
+ span = span2;
+ swap_block(pi, top - span, span);
+
+ /* now array has 3 parts:
+ * from 0 to lt excluded: elements smaller than pivot
+ * from lt to gt excluded: elements identical to pivot
+ * from gt to n excluded: elements greater than pivot
+ */
+ /* stack the larger segment and keep processing the smaller one
+ to minimize stack use for pathological distributions */
+ if (lt > nmemb - gt) {
+ sp->base = ptr;
+ sp->count = lt;
+ sp->depth = depth;
+ sp++;
+ ptr = pgt;
+ nmemb -= gt;
+ } else {
+ sp->base = pgt;
+ sp->count = nmemb - gt;
+ sp->depth = depth;
+ sp++;
+ nmemb = lt;
+ }
+ }
+ /* Use insertion sort for small fragments */
+ for (pi = ptr + size, top = ptr + nmemb * size; pi < top; pi += size) {
+ for (pj = pi; pj > ptr && cmp(pj - size, pj, opaque) > 0; pj -= size)
+ swap(pj, pj - size, size);
+ }
+ }
+}
+
+#endif
diff --git a/src/shared/quickjs/cutils.h b/src/shared/quickjs/cutils.h
new file mode 100644
index 000000000..ee0ce4a2e
--- /dev/null
+++ b/src/shared/quickjs/cutils.h
@@ -0,0 +1,309 @@
+/*
+ * C utilities
+ *
+ * Copyright (c) 2017 Fabrice Bellard
+ * Copyright (c) 2018 Charlie Gordon
+ *
+ * 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 CUTILS_H
+#define CUTILS_H
+
+#include <stdlib.h>
+#include <inttypes.h>
+
+#if defined(_MSC_VER)
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#else
+#include <sys/types.h>
+#endif
+
+/* set if CPU is big endian */
+#undef WORDS_BIGENDIAN
+
+#ifdef __GNUC__
+#define likely(x) __builtin_expect(!!(x), 1)
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#define force_inline inline __attribute__((always_inline))
+#define no_inline __attribute__((noinline))
+#define maybe_unused __attribute__((unused))
+#else
+#define likely(x) (x)
+#define unlikely(x) (x)
+#define force_inline
+#define no_inline
+#define maybe_unused
+#endif
+
+#ifdef _MSC_VER
+#define alloca _alloca
+#endif
+
+#define xglue(x, y) x ## y
+#define glue(x, y) xglue(x, y)
+#define stringify(s) tostring(s)
+#define tostring(s) #s
+
+#ifndef offsetof
+#define offsetof(type, field) ((size_t) &((type *)0)->field)
+#endif
+#ifndef countof
+#define countof(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+typedef int BOOL;
+
+#ifndef FALSE
+enum {
+ FALSE = 0,
+ TRUE = 1,
+};
+#endif
+
+void pstrcpy(char *buf, int buf_size, const char *str);
+char *pstrcat(char *buf, int buf_size, const char *s);
+int strstart(const char *str, const char *val, const char **ptr);
+int has_suffix(const char *str, const char *suffix);
+
+static inline int max_int(int a, int b)
+{
+ if (a > b)
+ return a;
+ else
+ return b;
+}
+
+static inline int min_int(int a, int b)
+{
+ if (a < b)
+ return a;
+ else
+ return b;
+}
+
+static inline uint32_t max_uint32(uint32_t a, uint32_t b)
+{
+ if (a > b)
+ return a;
+ else
+ return b;
+}
+
+static inline uint32_t min_uint32(uint32_t a, uint32_t b)
+{
+ if (a < b)
+ return a;
+ else
+ return b;
+}
+
+static inline int64_t max_int64(int64_t a, int64_t b)
+{
+ if (a > b)
+ return a;
+ else
+ return b;
+}
+
+static inline int64_t min_int64(int64_t a, int64_t b)
+{
+ if (a < b)
+ return a;
+ else
+ return b;
+}
+
+/* WARNING: undefined if a = 0 */
+static inline int clz32(unsigned int a)
+{
+#ifdef _MSC_VER
+ return (int) __lzcnt(a);
+#else
+ return __builtin_clz(a);
+#endif
+}
+
+#pragma pack(push, 1)
+struct packed_u64 {
+ uint64_t v;
+};
+struct packed_u32 {
+ uint32_t v;
+};
+struct packed_u16 {
+ uint16_t v;
+};
+#pragma pack(pop)
+
+static inline uint64_t get_u64(const uint8_t *tab)
+{
+ return ((const struct packed_u64 *)tab)->v;
+}
+
+static inline int64_t get_i64(const uint8_t *tab)
+{
+ return (int64_t)((const struct packed_u64 *)tab)->v;
+}
+
+static inline void put_u64(uint8_t *tab, uint64_t val)
+{
+ ((struct packed_u64 *)tab)->v = val;
+}
+
+static inline uint32_t get_u32(const uint8_t *tab)
+{
+ return ((const struct packed_u32 *)tab)->v;
+}
+
+static inline int32_t get_i32(const uint8_t *tab)
+{
+ return (int32_t)((const struct packed_u32 *)tab)->v;
+}
+
+static inline void put_u32(uint8_t *tab, uint32_t val)
+{
+ ((struct packed_u32 *)tab)->v = val;
+}
+
+static inline uint32_t get_u16(const uint8_t *tab)
+{
+ return ((const struct packed_u16 *)tab)->v;
+}
+
+static inline int32_t get_i16(const uint8_t *tab)
+{
+ return (int16_t)((const struct packed_u16 *)tab)->v;
+}
+
+static inline void put_u16(uint8_t *tab, uint16_t val)
+{
+ ((struct packed_u16 *)tab)->v = val;
+}
+
+static inline uint32_t get_u8(const uint8_t *tab)
+{
+ return *tab;
+}
+
+static inline int32_t get_i8(const uint8_t *tab)
+{
+ return (int8_t)*tab;
+}
+
+static inline void put_u8(uint8_t *tab, uint8_t val)
+{
+ *tab = val;
+}
+
+static inline uint16_t bswap16(uint16_t x)
+{
+ return (x >> 8) | (x << 8);
+}
+
+static inline uint32_t bswap32(uint32_t v)
+{
+ return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) |
+ ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24);
+}
+
+static inline uint64_t bswap64(uint64_t v)
+{
+ return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) |
+ ((v & ((uint64_t)0xff << (6 * 8))) >> (5 * 8)) |
+ ((v & ((uint64_t)0xff << (5 * 8))) >> (3 * 8)) |
+ ((v & ((uint64_t)0xff << (4 * 8))) >> (1 * 8)) |
+ ((v & ((uint64_t)0xff << (3 * 8))) << (1 * 8)) |
+ ((v & ((uint64_t)0xff << (2 * 8))) << (3 * 8)) |
+ ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) |
+ ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8));
+}
+
+/* XXX: should take an extra argument to pass slack information to the caller */
+typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size);
+
+typedef struct DynBuf {
+ uint8_t *buf;
+ size_t size;
+ size_t allocated_size;
+ BOOL error; /* true if a memory allocation error occurred */
+ DynBufReallocFunc *realloc_func;
+ void *opaque; /* for realloc_func */
+} DynBuf;
+
+void dbuf_init(DynBuf *s);
+void dbuf_init2(DynBuf *s, void *opaque, DynBufReallocFunc *realloc_func);
+int dbuf_realloc(DynBuf *s, size_t new_size);
+int dbuf_write(DynBuf *s, size_t offset, const uint8_t *data, size_t len);
+int dbuf_put(DynBuf *s, const uint8_t *data, size_t len);
+int dbuf_put_self(DynBuf *s, size_t offset, size_t len);
+int dbuf_putc(DynBuf *s, uint8_t c);
+int dbuf_putstr(DynBuf *s, const char *str);
+static inline int dbuf_put_u16(DynBuf *s, uint16_t val)
+{
+ return dbuf_put(s, (uint8_t *)&val, 2);
+}
+static inline int dbuf_put_u32(DynBuf *s, uint32_t val)
+{
+ return dbuf_put(s, (uint8_t *)&val, 4);
+}
+static inline int dbuf_put_u64(DynBuf *s, uint64_t val)
+{
+ return dbuf_put(s, (uint8_t *)&val, 8);
+}
+
+#ifdef __GNUC__
+#define FORMAT_ATTR(x, y) __attribute__((format(printf, x, y)))
+#else
+#define FORMAT_ATTR(x, y)
+#endif
+
+int FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, const char *fmt, ...);
+
+void dbuf_free(DynBuf *s);
+static inline BOOL dbuf_error(DynBuf *s) {
+ return s->error;
+}
+static inline void dbuf_set_error(DynBuf *s)
+{
+ s->error = TRUE;
+}
+
+#define UTF8_CHAR_LEN_MAX 6
+
+int unicode_to_utf8(uint8_t *buf, unsigned int c);
+int unicode_from_utf8(const uint8_t *p, int max_len, const uint8_t **pp);
+
+static inline int from_hex(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else
+ return -1;
+}
+
+void rqsort(void *base, size_t nmemb, size_t size,
+ int (*cmp)(const void *, const void *, void *),
+ void *arg);
+
+#endif /* CUTILS_H */
diff --git a/src/shared/quickjs/libregexp-opcode.h b/src/shared/quickjs/libregexp-opcode.h
new file mode 100644
index 000000000..f90c23b34
--- /dev/null
+++ b/src/shared/quickjs/libregexp-opcode.h
@@ -0,0 +1,58 @@
+/*
+ * Regular Expression Engine
+ *
+ * Copyright (c) 2017-2018 Fabrice Bellard
+ *
+ * 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.
+ */
+
+#ifdef DEF
+
+DEF(invalid, 1) /* never used */
+DEF(char, 3)
+DEF(char32, 5)
+DEF(dot, 1)
+DEF(any, 1) /* same as dot but match any character including line terminator */
+DEF(line_start, 1)
+DEF(line_end, 1)
+DEF(goto, 5)
+DEF(split_goto_first, 5)
+DEF(split_next_first, 5)
+DEF(match, 1)
+DEF(save_start, 2) /* save start position */
+DEF(save_end, 2) /* save end position, must come after saved_start */
+DEF(save_reset, 3) /* reset save positions */
+DEF(loop, 5) /* decrement the top the stack and goto if != 0 */
+DEF(push_i32, 5) /* push integer on the stack */
+DEF(drop, 1)
+DEF(word_boundary, 1)
+DEF(not_word_boundary, 1)
+DEF(back_reference, 2)
+DEF(backward_back_reference, 2) /* must come after back_reference */
+DEF(range, 3) /* variable length */
+DEF(range32, 3) /* variable length */
+DEF(lookahead, 5)
+DEF(negative_lookahead, 5)
+DEF(push_char_pos, 1) /* push the character position on the stack */
+DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character
+ position */
+DEF(prev, 1) /* go to the previous char */
+DEF(simple_greedy_quant, 17)
+
+#endif /* DEF */
diff --git a/src/shared/quickjs/libregexp.c b/src/shared/quickjs/libregexp.c
new file mode 100644
index 000000000..ad91f781a
--- /dev/null
+++ b/src/shared/quickjs/libregexp.c
@@ -0,0 +1,2610 @@
+/*
+ * Regular Expression Engine
+ *
+ * Copyright (c) 2017-2018 Fabrice Bellard
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "libregexp.h"
+
+/*
+ TODO:
+
+ - Add full unicode canonicalize rules for character ranges (not
+ really useful but needed for exact "ignorecase" compatibility).
+
+ - Add a lock step execution mode (=linear time execution guaranteed)
+ when the regular expression is "simple" i.e. no backreference nor
+ complicated lookahead. The opcodes are designed for this execution
+ model.
+*/
+
+#if defined(TEST)
+#define DUMP_REOP
+#endif
+
+typedef enum {
+#define DEF(id, size) REOP_ ## id,
+#include "libregexp-opcode.h"
+#undef DEF
+ REOP_COUNT,
+} REOPCodeEnum;
+
+#define CAPTURE_COUNT_MAX 255
+#define STACK_SIZE_MAX 255
+
+/* unicode code points */
+#define CP_LS 0x2028
+#define CP_PS 0x2029
+
+#define TMP_BUF_SIZE 128
+
+typedef struct {
+ DynBuf byte_code;
+ const uint8_t *buf_ptr;
+ const uint8_t *buf_end;
+ const uint8_t *buf_start;
+ int re_flags;
+ BOOL is_utf16;
+ BOOL ignore_case;
+ BOOL dotall;
+ int capture_count;
+ int total_capture_count; /* -1 = not computed yet */
+ int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */
+ void *opaque;
+ DynBuf group_names;
+ union {
+ char error_msg[TMP_BUF_SIZE];
+ char tmp_buf[TMP_BUF_SIZE];
+ } u;
+} REParseState;
+
+typedef struct {
+#ifdef DUMP_REOP
+ const char *name;
+#endif
+ uint8_t size;
+} REOpCode;
+
+static const REOpCode reopcode_info[REOP_COUNT] = {
+#ifdef DUMP_REOP
+#define DEF(id, size) { #id, size },
+#else
+#define DEF(id, size) { size },
+#endif
+#include "libregexp-opcode.h"
+#undef DEF
+};
+
+#define RE_HEADER_FLAGS 0
+#define RE_HEADER_CAPTURE_COUNT 1
+#define RE_HEADER_STACK_SIZE 2
+
+#define RE_HEADER_LEN 7
+
+static inline int is_digit(int c) {
+ return c >= '0' && c <= '9';
+}
+
+/* insert 'len' bytes at position 'pos'. Return < 0 if error. */
+static int dbuf_insert(DynBuf *s, int pos, int len)
+{
+ if (dbuf_realloc(s, s->size + len))
+ return -1;
+ memmove(s->buf + pos + len, s->buf + pos, s->size - pos);
+ s->size += len;
+ return 0;
+}
+
+/* canonicalize with the specific JS regexp rules */
+static uint32_t lre_canonicalize(uint32_t c, BOOL is_utf16)
+{
+ uint32_t res[LRE_CC_RES_LEN_MAX];
+ int len;
+ if (is_utf16) {
+ if (likely(c < 128)) {
+ if (c >= 'A' && c <= 'Z')
+ c = c - 'A' + 'a';
+ } else {
+ lre_case_conv(res, c, 2);
+ c = res[0];
+ }
+ } else {
+ if (likely(c < 128)) {
+ if (c >= 'a' && c <= 'z')
+ c = c - 'a' + 'A';
+ } else {
+ /* legacy regexp: to upper case if single char >= 128 */
+ len = lre_case_conv(res, c, FALSE);
+ if (len == 1 && res[0] >= 128)
+ c = res[0];
+ }
+ }
+ return c;
+}
+
+static const uint16_t char_range_d[] = {
+ 1,
+ 0x0030, 0x0039 + 1,
+};
+
+/* code point ranges for Zs,Zl or Zp property */
+static const uint16_t char_range_s[] = {
+ 10,
+ 0x0009, 0x000D + 1,
+ 0x0020, 0x0020 + 1,
+ 0x00A0, 0x00A0 + 1,
+ 0x1680, 0x1680 + 1,
+ 0x2000, 0x200A + 1,
+ /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */
+ /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */
+ 0x2028, 0x2029 + 1,
+ 0x202F, 0x202F + 1,
+ 0x205F, 0x205F + 1,
+ 0x3000, 0x3000 + 1,
+ /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */
+ 0xFEFF, 0xFEFF + 1,
+};
+
+BOOL lre_is_space(int c)
+{
+ int i, n, low, high;
+ n = (countof(char_range_s) - 1) / 2;
+ for(i = 0; i < n; i++) {
+ low = char_range_s[2 * i + 1];
+ if (c < low)
+ return FALSE;
+ high = char_range_s[2 * i + 2];
+ if (c < high)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+uint32_t const lre_id_start_table_ascii[4] = {
+ /* $ A-Z _ a-z */
+ 0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE
+};
+
+uint32_t const lre_id_continue_table_ascii[4] = {
+ /* $ 0-9 A-Z _ a-z */
+ 0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE
+};
+
+
+static const uint16_t char_range_w[] = {
+ 4,
+ 0x0030, 0x0039 + 1,
+ 0x0041, 0x005A + 1,
+ 0x005F, 0x005F + 1,
+ 0x0061, 0x007A + 1,
+};
+
+#define CLASS_RANGE_BASE 0x40000000
+
+typedef enum {
+ CHAR_RANGE_d,
+ CHAR_RANGE_D,
+ CHAR_RANGE_s,
+ CHAR_RANGE_S,
+ CHAR_RANGE_w,
+ CHAR_RANGE_W,
+} CharRangeEnum;
+
+static const uint16_t *char_range_table[] = {
+ char_range_d,
+ char_range_s,
+ char_range_w,
+};
+
+static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c)
+{
+ BOOL invert;
+ const uint16_t *c_pt;
+ int len, i;
+
+ invert = c & 1;
+ c_pt = char_range_table[c >> 1];
+ len = *c_pt++;
+ cr_init(cr, s->opaque, lre_realloc);
+ for(i = 0; i < len * 2; i++) {
+ if (cr_add_point(cr, c_pt[i]))
+ goto fail;
+ }
+ if (invert) {
+ if (cr_invert(cr))
+ goto fail;
+ }
+ return 0;
+ fail:
+ cr_free(cr);
+ return -1;
+}
+
+static int cr_canonicalize(CharRange *cr)
+{
+ CharRange a;
+ uint32_t pt[2];
+ int i, ret;
+
+ cr_init(&a, cr->mem_opaque, lre_realloc);
+ pt[0] = 'a';
+ pt[1] = 'z' + 1;
+ ret = cr_op(&a, cr->points, cr->len, pt, 2, CR_OP_INTER);
+ if (ret)
+ goto fail;
+ /* convert to upper case */
+ /* XXX: the generic unicode case would be much more complicated
+ and not really useful */
+ for(i = 0; i < a.len; i++) {
+ a.points[i] += 'A' - 'a';
+ }
+ /* Note: for simplicity we keep the lower case ranges */
+ ret = cr_union1(cr, a.points, a.len);
+ fail:
+ cr_free(&a);
+ return ret;
+}
+
+#ifdef DUMP_REOP
+static MAYBE_UNUSED void lre_dump_bytecode(const uint8_t *buf,
+ int buf_len)
+{
+ int pos, len, opcode, bc_len, re_flags, i;
+ uint32_t val;
+
+ assert(buf_len >= RE_HEADER_LEN);
+
+ re_flags= buf[0];
+ bc_len = get_u32(buf + 3);
+ assert(bc_len + RE_HEADER_LEN <= buf_len);
+ printf("flags: 0x%x capture_count=%d stack_size=%d\n",
+ re_flags, buf[1], buf[2]);
+ if (re_flags & LRE_FLAG_NAMED_GROUPS) {
+ const char *p;
+ p = (char *)buf + RE_HEADER_LEN + bc_len;
+ printf("named groups: ");
+ for(i = 1; i < buf[1]; i++) {
+ if (i != 1)
+ printf(",");
+ printf("<%s>", p);
+ p += strlen(p) + 1;
+ }
+ printf("\n");
+ assert(p == (char *)(buf + buf_len));
+ }
+ printf("bytecode_len=%d\n", bc_len);
+
+ buf += RE_HEADER_LEN;
+ pos = 0;
+ while (pos < bc_len) {
+ printf("%5u: ", pos);
+ opcode = buf[pos];
+ len = reopcode_info[opcode].size;
+ if (opcode >= REOP_COUNT) {
+ printf(" invalid opcode=0x%02x\n", opcode);
+ break;
+ }
+ if ((pos + len) > bc_len) {
+ printf(" buffer overflow (opcode=0x%02x)\n", opcode);
+ break;
+ }
+ printf("%s", reopcode_info[opcode].name);
+ switch(opcode) {
+ case REOP_char:
+ val = get_u16(buf + pos + 1);
+ if (val >= ' ' && val <= 126)
+ printf(" '%c'", val);
+ else
+ printf(" 0x%04x", val);
+ break;
+ case REOP_char32:
+ val = get_u32(buf + pos + 1);
+ if (val >= ' ' && val <= 126)
+ printf(" '%c'", val);
+ else
+ printf(" 0x%08x", val);
+ break;
+ case REOP_goto:
+ case REOP_split_goto_first:
+ case REOP_split_next_first:
+ case REOP_loop:
+ case REOP_lookahead:
+ case REOP_negative_lookahead:
+ case REOP_bne_char_pos:
+ val = get_u32(buf + pos + 1);
+ val += (pos + 5);
+ printf(" %u", val);
+ break;
+ case REOP_simple_greedy_quant:
+ printf(" %u %u %u %u",
+ get_u32(buf + pos + 1) + (pos + 17),
+ get_u32(buf + pos + 1 + 4),
+ get_u32(buf + pos + 1 + 8),
+ get_u32(buf + pos + 1 + 12));
+ break;
+ case REOP_save_start:
+ case REOP_save_end:
+ case REOP_back_reference:
+ case REOP_backward_back_reference:
+ printf(" %u", buf[pos + 1]);
+ break;
+ case REOP_save_reset:
+ printf(" %u %u", buf[pos + 1], buf[pos + 2]);
+ break;
+ case REOP_push_i32:
+ val = get_u32(buf + pos + 1);
+ printf(" %d", val);
+ break;
+ case REOP_range:
+ {
+ int n, i;
+ n = get_u16(buf + pos + 1);
+ len += n * 4;
+ for(i = 0; i < n * 2; i++) {
+ val = get_u16(buf + pos + 3 + i * 2);
+ printf(" 0x%04x", val);
+ }
+ }
+ break;
+ case REOP_range32:
+ {
+ int n, i;
+ n = get_u16(buf + pos + 1);
+ len += n * 8;
+ for(i = 0; i < n * 2; i++) {
+ val = get_u32(buf + pos + 3 + i * 4);
+ printf(" 0x%08x", val);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ pos += len;
+ }
+}
+#endif
+
+static void re_emit_op(REParseState *s, int op)
+{
+ dbuf_putc(&s->byte_code, op);
+}
+
+/* return the offset of the u32 value */
+static int re_emit_op_u32(REParseState *s, int op, uint32_t val)
+{
+ int pos;
+ dbuf_putc(&s->byte_code, op);
+ pos = s->byte_code.size;
+ dbuf_put_u32(&s->byte_code, val);
+ return pos;
+}
+
+static int re_emit_goto(REParseState *s, int op, uint32_t val)
+{
+ int pos;
+ dbuf_putc(&s->byte_code, op);
+ pos = s->byte_code.size;
+ dbuf_put_u32(&s->byte_code, val - (pos + 4));
+ return pos;
+}
+
+static void re_emit_op_u8(REParseState *s, int op, uint32_t val)
+{
+ dbuf_putc(&s->byte_code, op);
+ dbuf_putc(&s->byte_code, val);
+}
+
+static void re_emit_op_u16(REParseState *s, int op, uint32_t val)
+{
+ dbuf_putc(&s->byte_code, op);
+ dbuf_put_u16(&s->byte_code, val);
+}
+
+static int FORMAT_ATTR(2, 3) re_parse_error(REParseState *s, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(s->u.error_msg, sizeof(s->u.error_msg), fmt, ap);
+ va_end(ap);
+ return -1;
+}
+
+static int re_parse_out_of_memory(REParseState *s)
+{
+ return re_parse_error(s, "out of memory");
+}
+
+/* If allow_overflow is false, return -1 in case of
+ overflow. Otherwise return INT32_MAX. */
+static int parse_digits(const uint8_t **pp, BOOL allow_overflow)
+{
+ const uint8_t *p;
+ uint64_t v;
+ int c;
+
+ p = *pp;
+ v = 0;
+ for(;;) {
+ c = *p;
+ if (c < '0' || c > '9')
+ break;
+ v = v * 10 + c - '0';
+ if (v >= INT32_MAX) {
+ if (allow_overflow)
+ v = INT32_MAX;
+ else
+ return -1;
+ }
+ p++;
+ }
+ *pp = p;
+ return v;
+}
+
+static int re_parse_expect(REParseState *s, const uint8_t **pp, int c)
+{
+ const uint8_t *p;
+ p = *pp;
+ if (*p != c)
+ return re_parse_error(s, "expecting '%c'", c);
+ p++;
+ *pp = p;
+ return 0;
+}
+
+/* Parse an escape sequence, *pp points after the '\':
+ allow_utf16 value:
+ 0 : no UTF-16 escapes allowed
+ 1 : UTF-16 escapes allowed
+ 2 : UTF-16 escapes allowed and escapes of surrogate pairs are
+ converted to a unicode character (unicode regexp case).
+
+ Return the unicode char and update *pp if recognized,
+ return -1 if malformed escape,
+ return -2 otherwise. */
+int lre_parse_escape(const uint8_t **pp, int allow_utf16)
+{
+ const uint8_t *p;
+ uint32_t c;
+
+ p = *pp;
+ c = *p++;
+ switch(c) {
+ case 'b':
+ c = '\b';
+ break;
+ case 'f':
+ c = '\f';
+ break;
+ case 'n':
+ c = '\n';
+ break;
+ case 'r':
+ c = '\r';
+ break;
+ case 't':
+ c = '\t';
+ break;
+ case 'v':
+ c = '\v';
+ break;
+ case 'x':
+ case 'u':
+ {
+ int h, n, i;
+ uint32_t c1;
+
+ if (*p == '{' && allow_utf16) {
+ p++;
+ c = 0;
+ for(;;) {
+ h = from_hex(*p++);
+ if (h < 0)
+ return -1;
+ c = (c << 4) | h;
+ if (c > 0x10FFFF)
+ return -1;
+ if (*p == '}')
+ break;
+ }
+ p++;
+ } else {
+ if (c == 'x') {
+ n = 2;
+ } else {
+ n = 4;
+ }
+
+ c = 0;
+ for(i = 0; i < n; i++) {
+ h = from_hex(*p++);
+ if (h < 0) {
+ return -1;
+ }
+ c = (c << 4) | h;
+ }
+ if (c >= 0xd800 && c < 0xdc00 &&
+ allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') {
+ /* convert an escaped surrogate pair into a
+ unicode char */
+ c1 = 0;
+ for(i = 0; i < 4; i++) {
+ h = from_hex(p[2 + i]);
+ if (h < 0)
+ break;
+ c1 = (c1 << 4) | h;
+ }
+ if (i == 4 && c1 >= 0xdc00 && c1 < 0xe000) {
+ p += 6;
+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
+ }
+ }
+ }
+ }
+ break;
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ c -= '0';
+ if (allow_utf16 == 2) {
+ /* only accept \0 not followed by digit */
+ if (c != 0 || is_digit(*p))
+ return -1;
+ } else {
+ /* parse a legacy octal sequence */
+ uint32_t v;
+ v = *p - '0';
+ if (v > 7)
+ break;
+ c = (c << 3) | v;
+ p++;
+ if (c >= 32)
+ break;
+ v = *p - '0';
+ if (v > 7)
+ break;
+ c = (c << 3) | v;
+ p++;
+ }
+ break;
+ default:
+ return -2;
+ }
+ *pp = p;
+ return c;
+}
+
+#ifdef CONFIG_ALL_UNICODE
+/* XXX: we use the same chars for name and value */
+static BOOL is_unicode_char(int c)
+{
+ return ((c >= '0' && c <= '9') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ (c == '_'));
+}
+
+static int parse_unicode_property(REParseState *s, CharRange *cr,
+ const uint8_t **pp, BOOL is_inv)
+{
+ const uint8_t *p;
+ char name[64], value[64];
+ char *q;
+ BOOL script_ext;
+ int ret;
+
+ p = *pp;
+ if (*p != '{')
+ return re_parse_error(s, "expecting '{' after \\p");
+ p++;
+ q = name;
+ while (is_unicode_char(*p)) {
+ if ((q - name) >= sizeof(name) - 1)
+ goto unknown_property_name;
+ *q++ = *p++;
+ }
+ *q = '\0';
+ q = value;
+ if (*p == '=') {
+ p++;
+ while (is_unicode_char(*p)) {
+ if ((q - value) >= sizeof(value) - 1)
+ return re_parse_error(s, "unknown unicode property value");
+ *q++ = *p++;
+ }
+ }
+ *q = '\0';
+ if (*p != '}')
+ return re_parse_error(s, "expecting '}'");
+ p++;
+ // printf("name=%s value=%s\n", name, value);
+
+ if (!strcmp(name, "Script") || !strcmp(name, "sc")) {
+ script_ext = FALSE;
+ goto do_script;
+ } else if (!strcmp(name, "Script_Extensions") || !strcmp(name, "scx")) {
+ script_ext = TRUE;
+ do_script:
+ cr_init(cr, s->opaque, lre_realloc);
+ ret = unicode_script(cr, value, script_ext);
+ if (ret) {
+ cr_free(cr);
+ if (ret == -2)
+ return re_parse_error(s, "unknown unicode script");
+ else
+ goto out_of_memory;
+ }
+ } else if (!strcmp(name, "General_Category") || !strcmp(name, "gc")) {
+ cr_init(cr, s->opaque, lre_realloc);
+ ret = unicode_general_category(cr, value);
+ if (ret) {
+ cr_free(cr);
+ if (ret == -2)
+ return re_parse_error(s, "unknown unicode general category");
+ else
+ goto out_of_memory;
+ }
+ } else if (value[0] == '\0') {
+ cr_init(cr, s->opaque, lre_realloc);
+ ret = unicode_general_category(cr, name);
+ if (ret == -1) {
+ cr_free(cr);
+ goto out_of_memory;
+ }
+ if (ret < 0) {
+ ret = unicode_prop(cr, name);
+ if (ret) {
+ cr_free(cr);
+ if (ret == -2)
+ goto unknown_property_name;
+ else
+ goto out_of_memory;
+ }
+ }
+ } else {
+ unknown_property_name:
+ return re_parse_error(s, "unknown unicode property name");
+ }
+
+ if (is_inv) {
+ if (cr_invert(cr)) {
+ cr_free(cr);
+ return -1;
+ }
+ }
+ *pp = p;
+ return 0;
+ out_of_memory:
+ return re_parse_out_of_memory(s);
+}
+#endif /* CONFIG_ALL_UNICODE */
+
+/* return -1 if error otherwise the character or a class range
+ (CLASS_RANGE_BASE). In case of class range, 'cr' is
+ initialized. Otherwise, it is ignored. */
+static int get_class_atom(REParseState *s, CharRange *cr,
+ const uint8_t **pp, BOOL inclass)
+{
+ const uint8_t *p;
+ uint32_t c;
+ int ret;
+
+ p = *pp;
+
+ c = *p;
+ switch(c) {
+ case '\\':
+ p++;
+ if (p >= s->buf_end)
+ goto unexpected_end;
+ c = *p++;
+ switch(c) {
+ case 'd':
+ c = CHAR_RANGE_d;
+ goto class_range;
+ case 'D':
+ c = CHAR_RANGE_D;
+ goto class_range;
+ case 's':
+ c = CHAR_RANGE_s;
+ goto class_range;
+ case 'S':
+ c = CHAR_RANGE_S;
+ goto class_range;
+ case 'w':
+ c = CHAR_RANGE_w;
+ goto class_range;
+ case 'W':
+ c = CHAR_RANGE_W;
+ class_range:
+ if (cr_init_char_range(s, cr, c))
+ return -1;
+ c = CLASS_RANGE_BASE;
+ break;
+ case 'c':
+ c = *p;
+ if ((c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (((c >= '0' && c <= '9') || c == '_') &&
+ inclass && !s->is_utf16)) { /* Annex B.1.4 */
+ c &= 0x1f;
+ p++;
+ } else if (s->is_utf16) {
+ goto invalid_escape;
+ } else {
+ /* otherwise return '\' and 'c' */
+ p--;
+ c = '\\';
+ }
+ break;
+#ifdef CONFIG_ALL_UNICODE
+ case 'p':
+ case 'P':
+ if (s->is_utf16) {
+ if (parse_unicode_property(s, cr, &p, (c == 'P')))
+ return -1;
+ c = CLASS_RANGE_BASE;
+ break;
+ }
+ /* fall thru */
+#endif
+ default:
+ p--;
+ ret = lre_parse_escape(&p, s->is_utf16 * 2);
+ if (ret >= 0) {
+ c = ret;
+ } else {
+ if (ret == -2 && *p != '\0' && strchr("^$\\.*+?()[]{}|/", *p)) {
+ /* always valid to escape these characters */
+ goto normal_char;
+ } else if (s->is_utf16) {
+ invalid_escape:
+ return re_parse_error(s, "invalid escape sequence in regular expression");
+ } else {
+ /* just ignore the '\' */
+ goto normal_char;
+ }
+ }
+ break;
+ }
+ break;
+ case '\0':
+ if (p >= s->buf_end) {
+ unexpected_end:
+ return re_parse_error(s, "unexpected end");
+ }
+ /* fall thru */
+ default:
+ normal_char:
+ /* normal char */
+ if (c >= 128) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ if ((unsigned)c > 0xffff && !s->is_utf16) {
+ /* XXX: should handle non BMP-1 code points */
+ return re_parse_error(s, "malformed unicode char");
+ }
+ } else {
+ p++;
+ }
+ break;
+ }
+ *pp = p;
+ return c;
+}
+
+static int re_emit_range(REParseState *s, const CharRange *cr)
+{
+ int len, i;
+ uint32_t high;
+
+ len = (unsigned)cr->len / 2;
+ if (len >= 65535)
+ return re_parse_error(s, "too many ranges");
+ if (len == 0) {
+ /* not sure it can really happen. Emit a match that is always
+ false */
+ re_emit_op_u32(s, REOP_char32, -1);
+ } else {
+ high = cr->points[cr->len - 1];
+ if (high == UINT32_MAX)
+ high = cr->points[cr->len - 2];
+ if (high <= 0xffff) {
+ /* can use 16 bit ranges with the conversion that 0xffff =
+ infinity */
+ re_emit_op_u16(s, REOP_range, len);
+ for(i = 0; i < cr->len; i += 2) {
+ dbuf_put_u16(&s->byte_code, cr->points[i]);
+ high = cr->points[i + 1] - 1;
+ if (high == UINT32_MAX - 1)
+ high = 0xffff;
+ dbuf_put_u16(&s->byte_code, high);
+ }
+ } else {
+ re_emit_op_u16(s, REOP_range32, len);
+ for(i = 0; i < cr->len; i += 2) {
+ dbuf_put_u32(&s->byte_code, cr->points[i]);
+ dbuf_put_u32(&s->byte_code, cr->points[i + 1] - 1);
+ }
+ }
+ }
+ return 0;
+}
+
+static int re_parse_char_class(REParseState *s, const uint8_t **pp)
+{
+ const uint8_t *p;
+ uint32_t c1, c2;
+ CharRange cr_s, *cr = &cr_s;
+ CharRange cr1_s, *cr1 = &cr1_s;
+ BOOL invert;
+
+ cr_init(cr, s->opaque, lre_realloc);
+ p = *pp;
+ p++; /* skip '[' */
+ invert = FALSE;
+ if (*p == '^') {
+ p++;
+ invert = TRUE;
+ }
+ for(;;) {
+ if (*p == ']')
+ break;
+ c1 = get_class_atom(s, cr1, &p, TRUE);
+ if ((int)c1 < 0)
+ goto fail;
+ if (*p == '-' && p[1] != ']') {
+ const uint8_t *p0 = p + 1;
+ if (c1 >= CLASS_RANGE_BASE) {
+ if (s->is_utf16) {
+ cr_free(cr1);
+ goto invalid_class_range;
+ }
+ /* Annex B: match '-' character */
+ goto class_atom;
+ }
+ c2 = get_class_atom(s, cr1, &p0, TRUE);
+ if ((int)c2 < 0)
+ goto fail;
+ if (c2 >= CLASS_RANGE_BASE) {
+ cr_free(cr1);
+ if (s->is_utf16) {
+ goto invalid_class_range;
+ }
+ /* Annex B: match '-' character */
+ goto class_atom;
+ }
+ p = p0;
+ if (c2 < c1) {
+ invalid_class_range:
+ re_parse_error(s, "invalid class range");
+ goto fail;
+ }
+ if (cr_union_interval(cr, c1, c2))
+ goto memory_error;
+ } else {
+ class_atom:
+ if (c1 >= CLASS_RANGE_BASE) {
+ int ret;
+ ret = cr_union1(cr, cr1->points, cr1->len);
+ cr_free(cr1);
+ if (ret)
+ goto memory_error;
+ } else {
+ if (cr_union_interval(cr, c1, c1))
+ goto memory_error;
+ }
+ }
+ }
+ if (s->ignore_case) {
+ if (cr_canonicalize(cr))
+ goto memory_error;
+ }
+ if (invert) {
+ if (cr_invert(cr))
+ goto memory_error;
+ }
+ if (re_emit_range(s, cr))
+ goto fail;
+ cr_free(cr);
+ p++; /* skip ']' */
+ *pp = p;
+ return 0;
+ memory_error:
+ re_parse_out_of_memory(s);
+ fail:
+ cr_free(cr);
+ return -1;
+}
+
+/* Return:
+ 1 if the opcodes in bc_buf[] always advance the character pointer.
+ 0 if the character pointer may not be advanced.
+ -1 if the code may depend on side effects of its previous execution (backreference)
+*/
+static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len)
+{
+ int pos, opcode, ret, len, i;
+ uint32_t val, last;
+ BOOL has_back_reference;
+ uint8_t capture_bitmap[CAPTURE_COUNT_MAX];
+
+ ret = -2; /* not known yet */
+ pos = 0;
+ has_back_reference = FALSE;
+ memset(capture_bitmap, 0, sizeof(capture_bitmap));
+
+ while (pos < bc_buf_len) {
+ opcode = bc_buf[pos];
+ len = reopcode_info[opcode].size;
+ switch(opcode) {
+ case REOP_range:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 4;
+ goto simple_char;
+ case REOP_range32:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 8;
+ goto simple_char;
+ case REOP_char:
+ case REOP_char32:
+ case REOP_dot:
+ case REOP_any:
+ simple_char:
+ if (ret == -2)
+ ret = 1;
+ break;
+ case REOP_line_start:
+ case REOP_line_end:
+ case REOP_push_i32:
+ case REOP_push_char_pos:
+ case REOP_drop:
+ case REOP_word_boundary:
+ case REOP_not_word_boundary:
+ case REOP_prev:
+ /* no effect */
+ break;
+ case REOP_save_start:
+ case REOP_save_end:
+ val = bc_buf[pos + 1];
+ capture_bitmap[val] |= 1;
+ break;
+ case REOP_save_reset:
+ {
+ val = bc_buf[pos + 1];
+ last = bc_buf[pos + 2];
+ while (val < last)
+ capture_bitmap[val++] |= 1;
+ }
+ break;
+ case REOP_back_reference:
+ case REOP_backward_back_reference:
+ val = bc_buf[pos + 1];
+ capture_bitmap[val] |= 2;
+ has_back_reference = TRUE;
+ break;
+ default:
+ /* safe behvior: we cannot predict the outcome */
+ if (ret == -2)
+ ret = 0;
+ break;
+ }
+ pos += len;
+ }
+ if (has_back_reference) {
+ /* check if there is back reference which references a capture
+ made in the some code */
+ for(i = 0; i < CAPTURE_COUNT_MAX; i++) {
+ if (capture_bitmap[i] == 3)
+ return -1;
+ }
+ }
+ if (ret == -2)
+ ret = 0;
+ return ret;
+}
+
+/* return -1 if a simple quantifier cannot be used. Otherwise return
+ the number of characters in the atom. */
+static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len)
+{
+ int pos, opcode, len, count;
+ uint32_t val;
+
+ count = 0;
+ pos = 0;
+ while (pos < bc_buf_len) {
+ opcode = bc_buf[pos];
+ len = reopcode_info[opcode].size;
+ switch(opcode) {
+ case REOP_range:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 4;
+ goto simple_char;
+ case REOP_range32:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 8;
+ goto simple_char;
+ case REOP_char:
+ case REOP_char32:
+ case REOP_dot:
+ case REOP_any:
+ simple_char:
+ count++;
+ break;
+ case REOP_line_start:
+ case REOP_line_end:
+ case REOP_word_boundary:
+ case REOP_not_word_boundary:
+ break;
+ default:
+ return -1;
+ }
+ pos += len;
+ }
+ return count;
+}
+
+/* '*pp' is the first char after '<' */
+static int re_parse_group_name(char *buf, int buf_size,
+ const uint8_t **pp, BOOL is_utf16)
+{
+ const uint8_t *p;
+ uint32_t c;
+ char *q;
+
+ p = *pp;
+ q = buf;
+ for(;;) {
+ c = *p;
+ if (c == '\\') {
+ p++;
+ if (*p != 'u')
+ return -1;
+ c = lre_parse_escape(&p, is_utf16 * 2);
+ } else if (c == '>') {
+ break;
+ } else if (c >= 128) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ } else {
+ p++;
+ }
+ if (c > 0x10FFFF)
+ return -1;
+ if (q == buf) {
+ if (!lre_js_is_ident_first(c))
+ return -1;
+ } else {
+ if (!lre_js_is_ident_next(c))
+ return -1;
+ }
+ if ((q - buf + UTF8_CHAR_LEN_MAX + 1) > buf_size)
+ return -1;
+ if (c < 128) {
+ *q++ = c;
+ } else {
+ q += unicode_to_utf8((uint8_t*)q, c);
+ }
+ }
+ if (q == buf)
+ return -1;
+ *q = '\0';
+ p++;
+ *pp = p;
+ return 0;
+}
+
+/* if capture_name = NULL: return the number of captures + 1.
+ Otherwise, return the capture index corresponding to capture_name
+ or -1 if none */
+static int re_parse_captures(REParseState *s, int *phas_named_captures,
+ const char *capture_name)
+{
+ const uint8_t *p;
+ int capture_index;
+ char name[TMP_BUF_SIZE];
+
+ capture_index = 1;
+ *phas_named_captures = 0;
+ for (p = s->buf_start; p < s->buf_end; p++) {
+ switch (*p) {
+ case '(':
+ if (p[1] == '?') {
+ if (p[2] == '<' && p[3] != '=' && p[3] != '!') {
+ *phas_named_captures = 1;
+ /* potential named capture */
+ if (capture_name) {
+ p += 3;
+ if (re_parse_group_name(name, sizeof(name), &p,
+ s->is_utf16) == 0) {
+ if (!strcmp(name, capture_name))
+ return capture_index;
+ }
+ }
+ capture_index++;
+ if (capture_index >= CAPTURE_COUNT_MAX)
+ goto done;
+ }
+ } else {
+ capture_index++;
+ if (capture_index >= CAPTURE_COUNT_MAX)
+ goto done;
+ }
+ break;
+ case '\\':
+ p++;
+ break;
+ case '[':
+ for (p += 1 + (*p == ']'); p < s->buf_end && *p != ']'; p++) {
+ if (*p == '\\')
+ p++;
+ }
+ break;
+ }
+ }
+ done:
+ if (capture_name)
+ return -1;
+ else
+ return capture_index;
+}
+
+static int re_count_captures(REParseState *s)
+{
+ if (s->total_capture_count < 0) {
+ s->total_capture_count = re_parse_captures(s, &s->has_named_captures,
+ NULL);
+ }
+ return s->total_capture_count;
+}
+
+static BOOL re_has_named_captures(REParseState *s)
+{
+ if (s->has_named_captures < 0)
+ re_count_captures(s);
+ return s->has_named_captures;
+}
+
+static int find_group_name(REParseState *s, const char *name)
+{
+ const char *p, *buf_end;
+ size_t len, name_len;
+ int capture_index;
+
+ name_len = strlen(name);
+ p = (char *)s->group_names.buf;
+ buf_end = (char *)s->group_names.buf + s->group_names.size;
+ capture_index = 1;
+ while (p < buf_end) {
+ len = strlen(p);
+ if (len == name_len && memcmp(name, p, name_len) == 0)
+ return capture_index;
+ p += len + 1;
+ capture_index++;
+ }
+ return -1;
+}
+
+static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir);
+
+static int re_parse_term(REParseState *s, BOOL is_backward_dir)
+{
+ const uint8_t *p;
+ int c, last_atom_start, quant_min, quant_max, last_capture_count;
+ BOOL greedy, add_zero_advance_check, is_neg, is_backward_lookahead;
+ CharRange cr_s, *cr = &cr_s;
+
+ last_atom_start = -1;
+ last_capture_count = 0;
+ p = s->buf_ptr;
+ c = *p;
+ switch(c) {
+ case '^':
+ p++;
+ re_emit_op(s, REOP_line_start);
+ break;
+ case '$':
+ p++;
+ re_emit_op(s, REOP_line_end);
+ break;
+ case '.':
+ p++;
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ re_emit_op(s, s->dotall ? REOP_any : REOP_dot);
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ break;
+ case '{':
+ if (s->is_utf16) {
+ return re_parse_error(s, "syntax error");
+ } else if (!is_digit(p[1])) {
+ /* Annex B: we accept '{' not followed by digits as a
+ normal atom */
+ goto parse_class_atom;
+ } else {
+ const uint8_t *p1 = p + 1;
+ /* Annex B: error if it is like a repetition count */
+ parse_digits(&p1, TRUE);
+ if (*p1 == ',') {
+ p1++;
+ if (is_digit(*p1)) {
+ parse_digits(&p1, TRUE);
+ }
+ }
+ if (*p1 != '}') {
+ goto parse_class_atom;
+ }
+ }
+ /* fall thru */
+ case '*':
+ case '+':
+ case '?':
+ return re_parse_error(s, "nothing to repeat");
+ case '(':
+ if (p[1] == '?') {
+ if (p[2] == ':') {
+ p += 3;
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ s->buf_ptr = p;
+ if (re_parse_disjunction(s, is_backward_dir))
+ return -1;
+ p = s->buf_ptr;
+ if (re_parse_expect(s, &p, ')'))
+ return -1;
+ } else if ((p[2] == '=' || p[2] == '!')) {
+ is_neg = (p[2] == '!');
+ is_backward_lookahead = FALSE;
+ p += 3;
+ goto lookahead;
+ } else if (p[2] == '<' &&
+ (p[3] == '=' || p[3] == '!')) {
+ int pos;
+ is_neg = (p[3] == '!');
+ is_backward_lookahead = TRUE;
+ p += 4;
+ /* lookahead */
+ lookahead:
+ /* Annex B allows lookahead to be used as an atom for
+ the quantifiers */
+ if (!s->is_utf16 && !is_backward_lookahead) {
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ }
+ pos = re_emit_op_u32(s, REOP_lookahead + is_neg, 0);
+ s->buf_ptr = p;
+ if (re_parse_disjunction(s, is_backward_lookahead))
+ return -1;
+ p = s->buf_ptr;
+ if (re_parse_expect(s, &p, ')'))
+ return -1;
+ re_emit_op(s, REOP_match);
+ /* jump after the 'match' after the lookahead is successful */
+ if (dbuf_error(&s->byte_code))
+ return -1;
+ put_u32(s->byte_code.buf + pos, s->byte_code.size - (pos + 4));
+ } else if (p[2] == '<') {
+ p += 3;
+ if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
+ &p, s->is_utf16)) {
+ return re_parse_error(s, "invalid group name");
+ }
+ if (find_group_name(s, s->u.tmp_buf) > 0) {
+ return re_parse_error(s, "duplicate group name");
+ }
+ /* group name with a trailing zero */
+ dbuf_put(&s->group_names, (uint8_t *)s->u.tmp_buf,
+ strlen(s->u.tmp_buf) + 1);
+ s->has_named_captures = 1;
+ goto parse_capture;
+ } else {
+ return re_parse_error(s, "invalid group");
+ }
+ } else {
+ int capture_index;
+ p++;
+ /* capture without group name */
+ dbuf_putc(&s->group_names, 0);
+ parse_capture:
+ if (s->capture_count >= CAPTURE_COUNT_MAX)
+ return re_parse_error(s, "too many captures");
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ capture_index = s->capture_count++;
+ re_emit_op_u8(s, REOP_save_start + is_backward_dir,
+ capture_index);
+
+ s->buf_ptr = p;
+ if (re_parse_disjunction(s, is_backward_dir))
+ return -1;
+ p = s->buf_ptr;
+
+ re_emit_op_u8(s, REOP_save_start + 1 - is_backward_dir,
+ capture_index);
+
+ if (re_parse_expect(s, &p, ')'))
+ return -1;
+ }
+ break;
+ case '\\':
+ switch(p[1]) {
+ case 'b':
+ case 'B':
+ re_emit_op(s, REOP_word_boundary + (p[1] != 'b'));
+ p += 2;
+ break;
+ case 'k':
+ {
+ const uint8_t *p1;
+ int dummy_res;
+
+ p1 = p;
+ if (p1[2] != '<') {
+ /* annex B: we tolerate invalid group names in non
+ unicode mode if there is no named capture
+ definition */
+ if (s->is_utf16 || re_has_named_captures(s))
+ return re_parse_error(s, "expecting group name");
+ else
+ goto parse_class_atom;
+ }
+ p1 += 3;
+ if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf),
+ &p1, s->is_utf16)) {
+ if (s->is_utf16 || re_has_named_captures(s))
+ return re_parse_error(s, "invalid group name");
+ else
+ goto parse_class_atom;
+ }
+ c = find_group_name(s, s->u.tmp_buf);
+ if (c < 0) {
+ /* no capture name parsed before, try to look
+ after (inefficient, but hopefully not common */
+ c = re_parse_captures(s, &dummy_res, s->u.tmp_buf);
+ if (c < 0) {
+ if (s->is_utf16 || re_has_named_captures(s))
+ return re_parse_error(s, "group name not defined");
+ else
+ goto parse_class_atom;
+ }
+ }
+ p = p1;
+ }
+ goto emit_back_reference;
+ case '0':
+ p += 2;
+ c = 0;
+ if (s->is_utf16) {
+ if (is_digit(*p)) {
+ return re_parse_error(s, "invalid decimal escape in regular expression");
+ }
+ } else {
+ /* Annex B.1.4: accept legacy octal */
+ if (*p >= '0' && *p <= '7') {
+ c = *p++ - '0';
+ if (*p >= '0' && *p <= '7') {
+ c = (c << 3) + *p++ - '0';
+ }
+ }
+ }
+ goto normal_char;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ case '9':
+ {
+ const uint8_t *q = ++p;
+
+ c = parse_digits(&p, FALSE);
+ if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) {
+ if (!s->is_utf16) {
+ /* Annex B.1.4: accept legacy octal */
+ p = q;
+ if (*p <= '7') {
+ c = 0;
+ if (*p <= '3')
+ c = *p++ - '0';
+ if (*p >= '0' && *p <= '7') {
+ c = (c << 3) + *p++ - '0';
+ if (*p >= '0' && *p <= '7') {
+ c = (c << 3) + *p++ - '0';
+ }
+ }
+ } else {
+ c = *p++;
+ }
+ goto normal_char;
+ }
+ return re_parse_error(s, "back reference out of range in regular expression");
+ }
+ emit_back_reference:
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ re_emit_op_u8(s, REOP_back_reference + is_backward_dir, c);
+ }
+ break;
+ default:
+ goto parse_class_atom;
+ }
+ break;
+ case '[':
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ if (re_parse_char_class(s, &p))
+ return -1;
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ break;
+ case ']':
+ case '}':
+ if (s->is_utf16)
+ return re_parse_error(s, "syntax error");
+ goto parse_class_atom;
+ default:
+ parse_class_atom:
+ c = get_class_atom(s, cr, &p, FALSE);
+ if (c < 0)
+ return -1;
+ normal_char:
+ last_atom_start = s->byte_code.size;
+ last_capture_count = s->capture_count;
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ if (c >= CLASS_RANGE_BASE) {
+ int ret;
+ /* Note: canonicalization is not needed */
+ ret = re_emit_range(s, cr);
+ cr_free(cr);
+ if (ret)
+ return -1;
+ } else {
+ if (s->ignore_case)
+ c = lre_canonicalize(c, s->is_utf16);
+ if (c <= 0xffff)
+ re_emit_op_u16(s, REOP_char, c);
+ else
+ re_emit_op_u32(s, REOP_char32, c);
+ }
+ if (is_backward_dir)
+ re_emit_op(s, REOP_prev);
+ break;
+ }
+
+ /* quantifier */
+ if (last_atom_start >= 0) {
+ c = *p;
+ switch(c) {
+ case '*':
+ p++;
+ quant_min = 0;
+ quant_max = INT32_MAX;
+ goto quantifier;
+ case '+':
+ p++;
+ quant_min = 1;
+ quant_max = INT32_MAX;
+ goto quantifier;
+ case '?':
+ p++;
+ quant_min = 0;
+ quant_max = 1;
+ goto quantifier;
+ case '{':
+ {
+ const uint8_t *p1 = p;
+ /* As an extension (see ES6 annex B), we accept '{' not
+ followed by digits as a normal atom */
+ if (!is_digit(p[1])) {
+ if (s->is_utf16)
+ goto invalid_quant_count;
+ break;
+ }
+ p++;
+ quant_min = parse_digits(&p, TRUE);
+ quant_max = quant_min;
+ if (*p == ',') {
+ p++;
+ if (is_digit(*p)) {
+ quant_max = parse_digits(&p, TRUE);
+ if (quant_max < quant_min) {
+ invalid_quant_count:
+ return re_parse_error(s, "invalid repetition count");
+ }
+ } else {
+ quant_max = INT32_MAX; /* infinity */
+ }
+ }
+ if (*p != '}' && !s->is_utf16) {
+ /* Annex B: normal atom if invalid '{' syntax */
+ p = p1;
+ break;
+ }
+ if (re_parse_expect(s, &p, '}'))
+ return -1;
+ }
+ quantifier:
+ greedy = TRUE;
+ if (*p == '?') {
+ p++;
+ greedy = FALSE;
+ }
+ if (last_atom_start < 0) {
+ return re_parse_error(s, "nothing to repeat");
+ }
+ if (greedy) {
+ int len, pos;
+
+ if (quant_max > 0) {
+ /* specific optimization for simple quantifiers */
+ if (dbuf_error(&s->byte_code))
+ goto out_of_memory;
+ len = re_is_simple_quantifier(s->byte_code.buf + last_atom_start,
+ s->byte_code.size - last_atom_start);
+ if (len > 0) {
+ re_emit_op(s, REOP_match);
+
+ if (dbuf_insert(&s->byte_code, last_atom_start, 17))
+ goto out_of_memory;
+ pos = last_atom_start;
+ s->byte_code.buf[pos++] = REOP_simple_greedy_quant;
+ put_u32(&s->byte_code.buf[pos],
+ s->byte_code.size - last_atom_start - 17);
+ pos += 4;
+ put_u32(&s->byte_code.buf[pos], quant_min);
+ pos += 4;
+ put_u32(&s->byte_code.buf[pos], quant_max);
+ pos += 4;
+ put_u32(&s->byte_code.buf[pos], len);
+ pos += 4;
+ goto done;
+ }
+ }
+
+ if (dbuf_error(&s->byte_code))
+ goto out_of_memory;
+ add_zero_advance_check = (re_check_advance(s->byte_code.buf + last_atom_start,
+ s->byte_code.size - last_atom_start) == 0);
+ } else {
+ add_zero_advance_check = FALSE;
+ }
+
+ {
+ int len, pos;
+ len = s->byte_code.size - last_atom_start;
+ if (quant_min == 0) {
+ /* need to reset the capture in case the atom is
+ not executed */
+ if (last_capture_count != s->capture_count) {
+ if (dbuf_insert(&s->byte_code, last_atom_start, 3))
+ goto out_of_memory;
+ s->byte_code.buf[last_atom_start++] = REOP_save_reset;
+ s->byte_code.buf[last_atom_start++] = last_capture_count;
+ s->byte_code.buf[last_atom_start++] = s->capture_count - 1;
+ }
+ if (quant_max == 0) {
+ s->byte_code.size = last_atom_start;
+ } else if (quant_max == 1) {
+ if (dbuf_insert(&s->byte_code, last_atom_start, 5))
+ goto out_of_memory;
+ s->byte_code.buf[last_atom_start] = REOP_split_goto_first +
+ greedy;
+ put_u32(s->byte_code.buf + last_atom_start + 1, len);
+ } else if (quant_max == INT32_MAX) {
+ if (dbuf_insert(&s->byte_code, last_atom_start, 5 + add_zero_advance_check))
+ goto out_of_memory;
+ s->byte_code.buf[last_atom_start] = REOP_split_goto_first +
+ greedy;
+ put_u32(s->byte_code.buf + last_atom_start + 1,
+ len + 5 + add_zero_advance_check);
+ if (add_zero_advance_check) {
+ /* avoid infinite loop by stoping the
+ recursion if no advance was made in the
+ atom (only works if the atom has no
+ side effect) */
+ s->byte_code.buf[last_atom_start + 1 + 4] = REOP_push_char_pos;
+ re_emit_goto(s, REOP_bne_char_pos, last_atom_start);
+ } else {
+ re_emit_goto(s, REOP_goto, last_atom_start);
+ }
+ } else {
+ if (dbuf_insert(&s->byte_code, last_atom_start, 10))
+ goto out_of_memory;
+ pos = last_atom_start;
+ s->byte_code.buf[pos++] = REOP_push_i32;
+ put_u32(s->byte_code.buf + pos, quant_max);
+ pos += 4;
+ s->byte_code.buf[pos++] = REOP_split_goto_first + greedy;
+ put_u32(s->byte_code.buf + pos, len + 5);
+ re_emit_goto(s, REOP_loop, last_atom_start + 5);
+ re_emit_op(s, REOP_drop);
+ }
+ } else if (quant_min == 1 && quant_max == INT32_MAX &&
+ !add_zero_advance_check) {
+ re_emit_goto(s, REOP_split_next_first - greedy,
+ last_atom_start);
+ } else {
+ if (quant_min == 1) {
+ /* nothing to add */
+ } else {
+ if (dbuf_insert(&s->byte_code, last_atom_start, 5))
+ goto out_of_memory;
+ s->byte_code.buf[last_atom_start] = REOP_push_i32;
+ put_u32(s->byte_code.buf + last_atom_start + 1,
+ quant_min);
+ last_atom_start += 5;
+ re_emit_goto(s, REOP_loop, last_atom_start);
+ re_emit_op(s, REOP_drop);
+ }
+ if (quant_max == INT32_MAX) {
+ pos = s->byte_code.size;
+ re_emit_op_u32(s, REOP_split_goto_first + greedy,
+ len + 5 + add_zero_advance_check);
+ if (add_zero_advance_check)
+ re_emit_op(s, REOP_push_char_pos);
+ /* copy the atom */
+ dbuf_put_self(&s->byte_code, last_atom_start, len);
+ if (add_zero_advance_check)
+ re_emit_goto(s, REOP_bne_char_pos, pos);
+ else
+ re_emit_goto(s, REOP_goto, pos);
+ } else if (quant_max > quant_min) {
+ re_emit_op_u32(s, REOP_push_i32, quant_max - quant_min);
+ pos = s->byte_code.size;
+ re_emit_op_u32(s, REOP_split_goto_first + greedy, len + 5);
+ /* copy the atom */
+ dbuf_put_self(&s->byte_code, last_atom_start, len);
+
+ re_emit_goto(s, REOP_loop, pos);
+ re_emit_op(s, REOP_drop);
+ }
+ }
+ last_atom_start = -1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ done:
+ s->buf_ptr = p;
+ return 0;
+ out_of_memory:
+ return re_parse_out_of_memory(s);
+}
+
+static int re_parse_alternative(REParseState *s, BOOL is_backward_dir)
+{
+ const uint8_t *p;
+ int ret;
+ size_t start, term_start, end, term_size;
+
+ start = s->byte_code.size;
+ for(;;) {
+ p = s->buf_ptr;
+ if (p >= s->buf_end)
+ break;
+ if (*p == '|' || *p == ')')
+ break;
+ term_start = s->byte_code.size;
+ ret = re_parse_term(s, is_backward_dir);
+ if (ret)
+ return ret;
+ if (is_backward_dir) {
+ /* reverse the order of the terms (XXX: inefficient, but
+ speed is not really critical here) */
+ end = s->byte_code.size;
+ term_size = end - term_start;
+ if (dbuf_realloc(&s->byte_code, end + term_size))
+ return -1;
+ memmove(s->byte_code.buf + start + term_size,
+ s->byte_code.buf + start,
+ end - start);
+ memcpy(s->byte_code.buf + start, s->byte_code.buf + end,
+ term_size);
+ }
+ }
+ return 0;
+}
+
+static int re_parse_disjunction(REParseState *s, BOOL is_backward_dir)
+{
+ int start, len, pos;
+
+ if (lre_check_stack_overflow(s->opaque, 0))
+ return re_parse_error(s, "stack overflow");
+
+ start = s->byte_code.size;
+ if (re_parse_alternative(s, is_backward_dir))
+ return -1;
+ while (*s->buf_ptr == '|') {
+ s->buf_ptr++;
+
+ len = s->byte_code.size - start;
+
+ /* insert a split before the first alternative */
+ if (dbuf_insert(&s->byte_code, start, 5)) {
+ return re_parse_out_of_memory(s);
+ }
+ s->byte_code.buf[start] = REOP_split_next_first;
+ put_u32(s->byte_code.buf + start + 1, len + 5);
+
+ pos = re_emit_op_u32(s, REOP_goto, 0);
+
+ if (re_parse_alternative(s, is_backward_dir))
+ return -1;
+
+ /* patch the goto */
+ len = s->byte_code.size - (pos + 4);
+ put_u32(s->byte_code.buf + pos, len);
+ }
+ return 0;
+}
+
+/* the control flow is recursive so the analysis can be linear */
+static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len)
+{
+ int stack_size, stack_size_max, pos, opcode, len;
+ uint32_t val;
+
+ stack_size = 0;
+ stack_size_max = 0;
+ bc_buf += RE_HEADER_LEN;
+ bc_buf_len -= RE_HEADER_LEN;
+ pos = 0;
+ while (pos < bc_buf_len) {
+ opcode = bc_buf[pos];
+ len = reopcode_info[opcode].size;
+ assert(opcode < REOP_COUNT);
+ assert((pos + len) <= bc_buf_len);
+ switch(opcode) {
+ case REOP_push_i32:
+ case REOP_push_char_pos:
+ stack_size++;
+ if (stack_size > stack_size_max) {
+ if (stack_size > STACK_SIZE_MAX)
+ return -1;
+ stack_size_max = stack_size;
+ }
+ break;
+ case REOP_drop:
+ case REOP_bne_char_pos:
+ assert(stack_size > 0);
+ stack_size--;
+ break;
+ case REOP_range:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 4;
+ break;
+ case REOP_range32:
+ val = get_u16(bc_buf + pos + 1);
+ len += val * 8;
+ break;
+ }
+ pos += len;
+ }
+ return stack_size_max;
+}
+
+/* 'buf' must be a zero terminated UTF-8 string of length buf_len.
+ Return NULL if error and allocate an error message in *perror_msg,
+ otherwise the compiled bytecode and its length in plen.
+*/
+uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
+ const char *buf, size_t buf_len, int re_flags,
+ void *opaque)
+{
+ REParseState s_s, *s = &s_s;
+ int stack_size;
+ BOOL is_sticky;
+
+ memset(s, 0, sizeof(*s));
+ s->opaque = opaque;
+ s->buf_ptr = (const uint8_t *)buf;
+ s->buf_end = s->buf_ptr + buf_len;
+ s->buf_start = s->buf_ptr;
+ s->re_flags = re_flags;
+ s->is_utf16 = ((re_flags & LRE_FLAG_UTF16) != 0);
+ is_sticky = ((re_flags & LRE_FLAG_STICKY) != 0);
+ s->ignore_case = ((re_flags & LRE_FLAG_IGNORECASE) != 0);
+ s->dotall = ((re_flags & LRE_FLAG_DOTALL) != 0);
+ s->capture_count = 1;
+ s->total_capture_count = -1;
+ s->has_named_captures = -1;
+
+ dbuf_init2(&s->byte_code, opaque, lre_realloc);
+ dbuf_init2(&s->group_names, opaque, lre_realloc);
+
+ dbuf_putc(&s->byte_code, re_flags); /* first element is the flags */
+ dbuf_putc(&s->byte_code, 0); /* second element is the number of captures */
+ dbuf_putc(&s->byte_code, 0); /* stack size */
+ dbuf_put_u32(&s->byte_code, 0); /* bytecode length */
+
+ if (!is_sticky) {
+ /* iterate thru all positions (about the same as .*?( ... ) )
+ . We do it without an explicit loop so that lock step
+ thread execution will be possible in an optimized
+ implementation */
+ re_emit_op_u32(s, REOP_split_goto_first, 1 + 5);
+ re_emit_op(s, REOP_any);
+ re_emit_op_u32(s, REOP_goto, -(5 + 1 + 5));
+ }
+ re_emit_op_u8(s, REOP_save_start, 0);
+
+ if (re_parse_disjunction(s, FALSE)) {
+ error:
+ dbuf_free(&s->byte_code);
+ dbuf_free(&s->group_names);
+ pstrcpy(error_msg, error_msg_size, s->u.error_msg);
+ *plen = 0;
+ return NULL;
+ }
+
+ re_emit_op_u8(s, REOP_save_end, 0);
+
+ re_emit_op(s, REOP_match);
+
+ if (*s->buf_ptr != '\0') {
+ re_parse_error(s, "extraneous characters at the end");
+ goto error;
+ }
+
+ if (dbuf_error(&s->byte_code)) {
+ re_parse_out_of_memory(s);
+ goto error;
+ }
+
+ stack_size = compute_stack_size(s->byte_code.buf, s->byte_code.size);
+ if (stack_size < 0) {
+ re_parse_error(s, "too many imbricated quantifiers");
+ goto error;
+ }
+
+ s->byte_code.buf[RE_HEADER_CAPTURE_COUNT] = s->capture_count;
+ s->byte_code.buf[RE_HEADER_STACK_SIZE] = stack_size;
+ put_u32(s->byte_code.buf + 3, s->byte_code.size - RE_HEADER_LEN);
+
+ /* add the named groups if needed */
+ if (s->group_names.size > (s->capture_count - 1)) {
+ dbuf_put(&s->byte_code, s->group_names.buf, s->group_names.size);
+ s->byte_code.buf[RE_HEADER_FLAGS] |= LRE_FLAG_NAMED_GROUPS;
+ }
+ dbuf_free(&s->group_names);
+
+#ifdef DUMP_REOP
+ lre_dump_bytecode(s->byte_code.buf, s->byte_code.size);
+#endif
+
+ error_msg[0] = '\0';
+ *plen = s->byte_code.size;
+ return s->byte_code.buf;
+}
+
+static BOOL is_line_terminator(uint32_t c)
+{
+ return (c == '\n' || c == '\r' || c == CP_LS || c == CP_PS);
+}
+
+static BOOL is_word_char(uint32_t c)
+{
+ return ((c >= '0' && c <= '9') ||
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c == '_'));
+}
+
+#define GET_CHAR(c, cptr, cbuf_end) \
+ do { \
+ if (cbuf_type == 0) { \
+ (c) = *(cptr)++; \
+ } else { \
+ uint32_t __c1; \
+ (c) = *(uint16_t *)(cptr); \
+ (cptr) += 2; \
+ if ((c) >= 0xd800 && (c) < 0xdc00 && \
+ cbuf_type == 2 && (cptr) < (cbuf_end)) { \
+ __c1 = *(uint16_t *)(cptr); \
+ if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
+ (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
+ (cptr) += 2; \
+ } \
+ } \
+ } \
+ } while (0)
+
+#define PEEK_CHAR(c, cptr, cbuf_end) \
+ do { \
+ if (cbuf_type == 0) { \
+ (c) = (cptr)[0]; \
+ } else { \
+ uint32_t __c1; \
+ (c) = ((uint16_t *)(cptr))[0]; \
+ if ((c) >= 0xd800 && (c) < 0xdc00 && \
+ cbuf_type == 2 && ((cptr) + 2) < (cbuf_end)) { \
+ __c1 = ((uint16_t *)(cptr))[1]; \
+ if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
+ (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+ } while (0)
+
+#define PEEK_PREV_CHAR(c, cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+ (c) = (cptr)[-1]; \
+ } else { \
+ uint32_t __c1; \
+ (c) = ((uint16_t *)(cptr))[-1]; \
+ if ((c) >= 0xdc00 && (c) < 0xe000 && \
+ cbuf_type == 2 && ((cptr) - 4) >= (cbuf_start)) { \
+ __c1 = ((uint16_t *)(cptr))[-2]; \
+ if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
+ (c) = (((__c1 & 0x3ff) << 10) | ((c) & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+ } while (0)
+
+#define GET_PREV_CHAR(c, cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+ (cptr)--; \
+ (c) = (cptr)[0]; \
+ } else { \
+ uint32_t __c1; \
+ (cptr) -= 2; \
+ (c) = ((uint16_t *)(cptr))[0]; \
+ if ((c) >= 0xdc00 && (c) < 0xe000 && \
+ cbuf_type == 2 && (cptr) > (cbuf_start)) { \
+ __c1 = ((uint16_t *)(cptr))[-1]; \
+ if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
+ (cptr) -= 2; \
+ (c) = (((__c1 & 0x3ff) << 10) | ((c) & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+ } while (0)
+
+#define PREV_CHAR(cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+ (cptr)--; \
+ } else { \
+ (cptr) -= 2; \
+ if (cbuf_type == 2) { \
+ c = ((uint16_t *)(cptr))[0]; \
+ if (c >= 0xdc00 && c < 0xe000 && (cptr) > (cbuf_start)) { \
+ c = ((uint16_t *)(cptr))[-1]; \
+ if (c >= 0xd800 && c < 0xdc00) \
+ (cptr) -= 2; \
+ } \
+ } \
+ } \
+ } while (0)
+
+typedef uintptr_t StackInt;
+
+typedef enum {
+ RE_EXEC_STATE_SPLIT,
+ RE_EXEC_STATE_LOOKAHEAD,
+ RE_EXEC_STATE_NEGATIVE_LOOKAHEAD,
+ RE_EXEC_STATE_GREEDY_QUANT,
+} REExecStateEnum;
+
+typedef struct REExecState {
+ REExecStateEnum type : 8;
+ uint8_t stack_len;
+ size_t count; /* only used for RE_EXEC_STATE_GREEDY_QUANT */
+ const uint8_t *cptr;
+ const uint8_t *pc;
+ void *buf[0];
+} REExecState;
+
+typedef struct {
+ const uint8_t *cbuf;
+ const uint8_t *cbuf_end;
+ /* 0 = 8 bit chars, 1 = 16 bit chars, 2 = 16 bit chars, UTF-16 */
+ int cbuf_type;
+ int capture_count;
+ int stack_size_max;
+ BOOL multi_line;
+ BOOL ignore_case;
+ BOOL is_utf16;
+ void *opaque; /* used for stack overflow check */
+
+ size_t state_size;
+ uint8_t *state_stack;
+ size_t state_stack_size;
+ size_t state_stack_len;
+} REExecContext;
+
+static int push_state(REExecContext *s,
+ uint8_t **capture,
+ const StackInt *stack, size_t stack_len,
+ const uint8_t *pc, const uint8_t *cptr,
+ REExecStateEnum type, size_t count)
+{
+ REExecState *rs;
+ uint8_t *new_stack;
+ size_t new_size, i, n;
+ StackInt *stack_buf;
+
+ if (unlikely((s->state_stack_len + 1) > s->state_stack_size)) {
+ /* reallocate the stack */
+ new_size = s->state_stack_size * 3 / 2;
+ if (new_size < 8)
+ new_size = 8;
+ new_stack = lre_realloc(s->opaque, s->state_stack, new_size * s->state_size);
+ if (!new_stack)
+ return -1;
+ s->state_stack_size = new_size;
+ s->state_stack = new_stack;
+ }
+ rs = (REExecState *)(s->state_stack + s->state_stack_len * s->state_size);
+ s->state_stack_len++;
+ rs->type = type;
+ rs->count = count;
+ rs->stack_len = stack_len;
+ rs->cptr = cptr;
+ rs->pc = pc;
+ n = 2 * s->capture_count;
+ for(i = 0; i < n; i++)
+ rs->buf[i] = capture[i];
+ stack_buf = (StackInt *)(rs->buf + n);
+ for(i = 0; i < stack_len; i++)
+ stack_buf[i] = stack[i];
+ return 0;
+}
+
+/* return 1 if match, 0 if not match or -1 if error. */
+static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture,
+ StackInt *stack, int stack_len,
+ const uint8_t *pc, const uint8_t *cptr,
+ BOOL no_recurse)
+{
+ int opcode, ret;
+ int cbuf_type;
+ uint32_t val, c;
+ const uint8_t *cbuf_end;
+
+ cbuf_type = s->cbuf_type;
+ cbuf_end = s->cbuf_end;
+
+ for(;;) {
+ // printf("top=%p: pc=%d\n", th_list.top, (int)(pc - (bc_buf + RE_HEADER_LEN)));
+ opcode = *pc++;
+ switch(opcode) {
+ case REOP_match:
+ {
+ REExecState *rs;
+ if (no_recurse)
+ return (intptr_t)cptr;
+ ret = 1;
+ goto recurse;
+ no_match:
+ if (no_recurse)
+ return 0;
+ ret = 0;
+ recurse:
+ for(;;) {
+ if (s->state_stack_len == 0)
+ return ret;
+ rs = (REExecState *)(s->state_stack +
+ (s->state_stack_len - 1) * s->state_size);
+ if (rs->type == RE_EXEC_STATE_SPLIT) {
+ if (!ret) {
+ pop_state:
+ memcpy(capture, rs->buf,
+ sizeof(capture[0]) * 2 * s->capture_count);
+ pop_state1:
+ pc = rs->pc;
+ cptr = rs->cptr;
+ stack_len = rs->stack_len;
+ memcpy(stack, rs->buf + 2 * s->capture_count,
+ stack_len * sizeof(stack[0]));
+ s->state_stack_len--;
+ break;
+ }
+ } else if (rs->type == RE_EXEC_STATE_GREEDY_QUANT) {
+ if (!ret) {
+ uint32_t char_count, i;
+ memcpy(capture, rs->buf,
+ sizeof(capture[0]) * 2 * s->capture_count);
+ stack_len = rs->stack_len;
+ memcpy(stack, rs->buf + 2 * s->capture_count,
+ stack_len * sizeof(stack[0]));
+ pc = rs->pc;
+ cptr = rs->cptr;
+ /* go backward */
+ char_count = get_u32(pc + 12);
+ for(i = 0; i < char_count; i++) {
+ PREV_CHAR(cptr, s->cbuf);
+ }
+ pc = (pc + 16) + (int)get_u32(pc);
+ rs->cptr = cptr;
+ rs->count--;
+ if (rs->count == 0) {
+ s->state_stack_len--;
+ }
+ break;
+ }
+ } else {
+ ret = ((rs->type == RE_EXEC_STATE_LOOKAHEAD && ret) ||
+ (rs->type == RE_EXEC_STATE_NEGATIVE_LOOKAHEAD && !ret));
+ if (ret) {
+ /* keep the capture in case of positive lookahead */
+ if (rs->type == RE_EXEC_STATE_LOOKAHEAD)
+ goto pop_state1;
+ else
+ goto pop_state;
+ }
+ }
+ s->state_stack_len--;
+ }
+ }
+ break;
+ case REOP_char32:
+ val = get_u32(pc);
+ pc += 4;
+ goto test_char;
+ case REOP_char:
+ val = get_u16(pc);
+ pc += 2;
+ test_char:
+ if (cptr >= cbuf_end)
+ goto no_match;
+ GET_CHAR(c, cptr, cbuf_end);
+ if (s->ignore_case) {
+ c = lre_canonicalize(c, s->is_utf16);
+ }
+ if (val != c)
+ goto no_match;
+ break;
+ case REOP_split_goto_first:
+ case REOP_split_next_first:
+ {
+ const uint8_t *pc1;
+
+ val = get_u32(pc);
+ pc += 4;
+ if (opcode == REOP_split_next_first) {
+ pc1 = pc + (int)val;
+ } else {
+ pc1 = pc;
+ pc = pc + (int)val;
+ }
+ ret = push_state(s, capture, stack, stack_len,
+ pc1, cptr, RE_EXEC_STATE_SPLIT, 0);
+ if (ret < 0)
+ return -1;
+ break;
+ }
+ case REOP_lookahead:
+ case REOP_negative_lookahead:
+ val = get_u32(pc);
+ pc += 4;
+ ret = push_state(s, capture, stack, stack_len,
+ pc + (int)val, cptr,
+ RE_EXEC_STATE_LOOKAHEAD + opcode - REOP_lookahead,
+ 0);
+ if (ret < 0)
+ return -1;
+ break;
+
+ case REOP_goto:
+ val = get_u32(pc);
+ pc += 4 + (int)val;
+ break;
+ case REOP_line_start:
+ if (cptr == s->cbuf)
+ break;
+ if (!s->multi_line)
+ goto no_match;
+ PEEK_PREV_CHAR(c, cptr, s->cbuf);
+ if (!is_line_terminator(c))
+ goto no_match;
+ break;
+ case REOP_line_end:
+ if (cptr == cbuf_end)
+ break;
+ if (!s->multi_line)
+ goto no_match;
+ PEEK_CHAR(c, cptr, cbuf_end);
+ if (!is_line_terminator(c))
+ goto no_match;
+ break;
+ case REOP_dot:
+ if (cptr == cbuf_end)
+ goto no_match;
+ GET_CHAR(c, cptr, cbuf_end);
+ if (is_line_terminator(c))
+ goto no_match;
+ break;
+ case REOP_any:
+ if (cptr == cbuf_end)
+ goto no_match;
+ GET_CHAR(c, cptr, cbuf_end);
+ break;
+ case REOP_save_start:
+ case REOP_save_end:
+ val = *pc++;
+ assert(val < s->capture_count);
+ capture[2 * val + opcode - REOP_save_start] = (uint8_t *)cptr;
+ break;
+ case REOP_save_reset:
+ {
+ uint32_t val2;
+ val = pc[0];
+ val2 = pc[1];
+ pc += 2;
+ assert(val2 < s->capture_count);
+ while (val <= val2) {
+ capture[2 * val] = NULL;
+ capture[2 * val + 1] = NULL;
+ val++;
+ }
+ }
+ break;
+ case REOP_push_i32:
+ val = get_u32(pc);
+ pc += 4;
+ stack[stack_len++] = val;
+ break;
+ case REOP_drop:
+ stack_len--;
+ break;
+ case REOP_loop:
+ val = get_u32(pc);
+ pc += 4;
+ if (--stack[stack_len - 1] != 0) {
+ pc += (int)val;
+ }
+ break;
+ case REOP_push_char_pos:
+ stack[stack_len++] = (uintptr_t)cptr;
+ break;
+ case REOP_bne_char_pos:
+ val = get_u32(pc);
+ pc += 4;
+ if (stack[--stack_len] != (uintptr_t)cptr)
+ pc += (int)val;
+ break;
+ case REOP_word_boundary:
+ case REOP_not_word_boundary:
+ {
+ BOOL v1, v2;
+ /* char before */
+ if (cptr == s->cbuf) {
+ v1 = FALSE;
+ } else {
+ PEEK_PREV_CHAR(c, cptr, s->cbuf);
+ v1 = is_word_char(c);
+ }
+ /* current char */
+ if (cptr >= cbuf_end) {
+ v2 = FALSE;
+ } else {
+ PEEK_CHAR(c, cptr, cbuf_end);
+ v2 = is_word_char(c);
+ }
+ if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode))
+ goto no_match;
+ }
+ break;
+ case REOP_back_reference:
+ case REOP_backward_back_reference:
+ {
+ const uint8_t *cptr1, *cptr1_end, *cptr1_start;
+ uint32_t c1, c2;
+
+ val = *pc++;
+ if (val >= s->capture_count)
+ goto no_match;
+ cptr1_start = capture[2 * val];
+ cptr1_end = capture[2 * val + 1];
+ if (!cptr1_start || !cptr1_end)
+ break;
+ if (opcode == REOP_back_reference) {
+ cptr1 = cptr1_start;
+ while (cptr1 < cptr1_end) {
+ if (cptr >= cbuf_end)
+ goto no_match;
+ GET_CHAR(c1, cptr1, cptr1_end);
+ GET_CHAR(c2, cptr, cbuf_end);
+ if (s->ignore_case) {
+ c1 = lre_canonicalize(c1, s->is_utf16);
+ c2 = lre_canonicalize(c2, s->is_utf16);
+ }
+ if (c1 != c2)
+ goto no_match;
+ }
+ } else {
+ cptr1 = cptr1_end;
+ while (cptr1 > cptr1_start) {
+ if (cptr == s->cbuf)
+ goto no_match;
+ GET_PREV_CHAR(c1, cptr1, cptr1_start);
+ GET_PREV_CHAR(c2, cptr, s->cbuf);
+ if (s->ignore_case) {
+ c1 = lre_canonicalize(c1, s->is_utf16);
+ c2 = lre_canonicalize(c2, s->is_utf16);
+ }
+ if (c1 != c2)
+ goto no_match;
+ }
+ }
+ }
+ break;
+ case REOP_range:
+ {
+ int n;
+ uint32_t low, high, idx_min, idx_max, idx;
+
+ n = get_u16(pc); /* n must be >= 1 */
+ pc += 2;
+ if (cptr >= cbuf_end)
+ goto no_match;
+ GET_CHAR(c, cptr, cbuf_end);
+ if (s->ignore_case) {
+ c = lre_canonicalize(c, s->is_utf16);
+ }
+ idx_min = 0;
+ low = get_u16(pc + 0 * 4);
+ if (c < low)
+ goto no_match;
+ idx_max = n - 1;
+ high = get_u16(pc + idx_max * 4 + 2);
+ /* 0xffff in for last value means +infinity */
+ if (unlikely(c >= 0xffff) && high == 0xffff)
+ goto range_match;
+ if (c > high)
+ goto no_match;
+ while (idx_min <= idx_max) {
+ idx = (idx_min + idx_max) / 2;
+ low = get_u16(pc + idx * 4);
+ high = get_u16(pc + idx * 4 + 2);
+ if (c < low)
+ idx_max = idx - 1;
+ else if (c > high)
+ idx_min = idx + 1;
+ else
+ goto range_match;
+ }
+ goto no_match;
+ range_match:
+ pc += 4 * n;
+ }
+ break;
+ case REOP_range32:
+ {
+ int n;
+ uint32_t low, high, idx_min, idx_max, idx;
+
+ n = get_u16(pc); /* n must be >= 1 */
+ pc += 2;
+ if (cptr >= cbuf_end)
+ goto no_match;
+ GET_CHAR(c, cptr, cbuf_end);
+ if (s->ignore_case) {
+ c = lre_canonicalize(c, s->is_utf16);
+ }
+ idx_min = 0;
+ low = get_u32(pc + 0 * 8);
+ if (c < low)
+ goto no_match;
+ idx_max = n - 1;
+ high = get_u32(pc + idx_max * 8 + 4);
+ if (c > high)
+ goto no_match;
+ while (idx_min <= idx_max) {
+ idx = (idx_min + idx_max) / 2;
+ low = get_u32(pc + idx * 8);
+ high = get_u32(pc + idx * 8 + 4);
+ if (c < low)
+ idx_max = idx - 1;
+ else if (c > high)
+ idx_min = idx + 1;
+ else
+ goto range32_match;
+ }
+ goto no_match;
+ range32_match:
+ pc += 8 * n;
+ }
+ break;
+ case REOP_prev:
+ /* go to the previous char */
+ if (cptr == s->cbuf)
+ goto no_match;
+ PREV_CHAR(cptr, s->cbuf);
+ break;
+ case REOP_simple_greedy_quant:
+ {
+ uint32_t next_pos, quant_min, quant_max;
+ size_t q;
+ intptr_t res;
+ const uint8_t *pc1;
+
+ next_pos = get_u32(pc);
+ quant_min = get_u32(pc + 4);
+ quant_max = get_u32(pc + 8);
+ pc += 16;
+ pc1 = pc;
+ pc += (int)next_pos;
+
+ q = 0;
+ for(;;) {
+ res = lre_exec_backtrack(s, capture, stack, stack_len,
+ pc1, cptr, TRUE);
+ if (res == -1)
+ return res;
+ if (!res)
+ break;
+ cptr = (uint8_t *)res;
+ q++;
+ if (q >= quant_max && quant_max != INT32_MAX)
+ break;
+ }
+ if (q < quant_min)
+ goto no_match;
+ if (q > quant_min) {
+ /* will examine all matches down to quant_min */
+ ret = push_state(s, capture, stack, stack_len,
+ pc1 - 16, cptr,
+ RE_EXEC_STATE_GREEDY_QUANT,
+ q - quant_min);
+ if (ret < 0)
+ return -1;
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+}
+
+/* Return 1 if match, 0 if not match or -1 if error. cindex is the
+ starting position of the match and must be such as 0 <= cindex <=
+ clen. */
+int lre_exec(uint8_t **capture,
+ const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
+ int cbuf_type, void *opaque)
+{
+ REExecContext s_s, *s = &s_s;
+ int re_flags, i, alloca_size, ret;
+ StackInt *stack_buf;
+
+ re_flags = bc_buf[RE_HEADER_FLAGS];
+ s->multi_line = (re_flags & LRE_FLAG_MULTILINE) != 0;
+ s->ignore_case = (re_flags & LRE_FLAG_IGNORECASE) != 0;
+ s->is_utf16 = (re_flags & LRE_FLAG_UTF16) != 0;
+ s->capture_count = bc_buf[RE_HEADER_CAPTURE_COUNT];
+ s->stack_size_max = bc_buf[RE_HEADER_STACK_SIZE];
+ s->cbuf = cbuf;
+ s->cbuf_end = cbuf + (clen << cbuf_type);
+ s->cbuf_type = cbuf_type;
+ if (s->cbuf_type == 1 && s->is_utf16)
+ s->cbuf_type = 2;
+ s->opaque = opaque;
+
+ s->state_size = sizeof(REExecState) +
+ s->capture_count * sizeof(capture[0]) * 2 +
+ s->stack_size_max * sizeof(stack_buf[0]);
+ s->state_stack = NULL;
+ s->state_stack_len = 0;
+ s->state_stack_size = 0;
+
+ for(i = 0; i < s->capture_count * 2; i++)
+ capture[i] = NULL;
+ alloca_size = s->stack_size_max * sizeof(stack_buf[0]);
+ stack_buf = alloca(alloca_size);
+ ret = lre_exec_backtrack(s, capture, stack_buf, 0, bc_buf + RE_HEADER_LEN,
+ cbuf + (cindex << cbuf_type), FALSE);
+ lre_realloc(s->opaque, s->state_stack, 0);
+ return ret;
+}
+
+int lre_get_capture_count(const uint8_t *bc_buf)
+{
+ return bc_buf[RE_HEADER_CAPTURE_COUNT];
+}
+
+int lre_get_flags(const uint8_t *bc_buf)
+{
+ return bc_buf[RE_HEADER_FLAGS];
+}
+
+/* Return NULL if no group names. Otherwise, return a pointer to
+ 'capture_count - 1' zero terminated UTF-8 strings. */
+const char *lre_get_groupnames(const uint8_t *bc_buf)
+{
+ uint32_t re_bytecode_len;
+ if ((lre_get_flags(bc_buf) & LRE_FLAG_NAMED_GROUPS) == 0)
+ return NULL;
+ re_bytecode_len = get_u32(bc_buf + 3);
+ return (const char *)(bc_buf + 7 + re_bytecode_len);
+}
+
+#ifdef TEST
+
+BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)
+{
+ return FALSE;
+}
+
+void *lre_realloc(void *opaque, void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+int main(int argc, char **argv)
+{
+ int len, ret, i;
+ uint8_t *bc;
+ char error_msg[64];
+ uint8_t *capture[CAPTURE_COUNT_MAX * 2];
+ const char *input;
+ int input_len, capture_count;
+
+ if (argc < 3) {
+ printf("usage: %s regexp input\n", argv[0]);
+ exit(1);
+ }
+ bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1],
+ strlen(argv[1]), 0, NULL);
+ if (!bc) {
+ fprintf(stderr, "error: %s\n", error_msg);
+ exit(1);
+ }
+
+ input = argv[2];
+ input_len = strlen(input);
+
+ ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL);
+ printf("ret=%d\n", ret);
+ if (ret == 1) {
+ capture_count = lre_get_capture_count(bc);
+ for(i = 0; i < 2 * capture_count; i++) {
+ uint8_t *ptr;
+ ptr = capture[i];
+ printf("%d: ", i);
+ if (!ptr)
+ printf("<nil>");
+ else
+ printf("%u", (int)(ptr - (uint8_t *)input));
+ printf("\n");
+ }
+ }
+ return 0;
+}
+#endif
diff --git a/src/shared/quickjs/libregexp.h b/src/shared/quickjs/libregexp.h
new file mode 100644
index 000000000..9aedb7e93
--- /dev/null
+++ b/src/shared/quickjs/libregexp.h
@@ -0,0 +1,92 @@
+/*
+ * Regular Expression Engine
+ *
+ * Copyright (c) 2017-2018 Fabrice Bellard
+ *
+ * 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 LIBREGEXP_H
+#define LIBREGEXP_H
+
+#include <stddef.h>
+
+#include "libunicode.h"
+
+#define LRE_BOOL int /* for documentation purposes */
+
+#define LRE_FLAG_GLOBAL (1 << 0)
+#define LRE_FLAG_IGNORECASE (1 << 1)
+#define LRE_FLAG_MULTILINE (1 << 2)
+#define LRE_FLAG_DOTALL (1 << 3)
+#define LRE_FLAG_UTF16 (1 << 4)
+#define LRE_FLAG_STICKY (1 << 5)
+
+#define LRE_FLAG_NAMED_GROUPS (1 << 7) /* named groups are present in the regexp */
+
+uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size,
+ const char *buf, size_t buf_len, int re_flags,
+ void *opaque);
+int lre_get_capture_count(const uint8_t *bc_buf);
+int lre_get_flags(const uint8_t *bc_buf);
+const char *lre_get_groupnames(const uint8_t *bc_buf);
+int lre_exec(uint8_t **capture,
+ const uint8_t *bc_buf, const uint8_t *cbuf, int cindex, int clen,
+ int cbuf_type, void *opaque);
+
+int lre_parse_escape(const uint8_t **pp, int allow_utf16);
+LRE_BOOL lre_is_space(int c);
+
+/* must be provided by the user */
+LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size);
+void *lre_realloc(void *opaque, void *ptr, size_t size);
+
+/* JS identifier test */
+extern uint32_t const lre_id_start_table_ascii[4];
+extern uint32_t const lre_id_continue_table_ascii[4];
+
+static inline int lre_js_is_ident_first(int c)
+{
+ if ((uint32_t)c < 128) {
+ return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1;
+ } else {
+#ifdef CONFIG_ALL_UNICODE
+ return lre_is_id_start(c);
+#else
+ return !lre_is_space(c);
+#endif
+ }
+}
+
+static inline int lre_js_is_ident_next(int c)
+{
+ if ((uint32_t)c < 128) {
+ return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1;
+ } else {
+ /* ZWNJ and ZWJ are accepted in identifiers */
+#ifdef CONFIG_ALL_UNICODE
+ return lre_is_id_continue(c) || c == 0x200C || c == 0x200D;
+#else
+ return !lre_is_space(c) || c == 0x200C || c == 0x200D;
+#endif
+ }
+}
+
+#undef LRE_BOOL
+
+#endif /* LIBREGEXP_H */
diff --git a/src/shared/quickjs/libunicode-table.h b/src/shared/quickjs/libunicode-table.h
new file mode 100644
index 000000000..1727525fb
--- /dev/null
+++ b/src/shared/quickjs/libunicode-table.h
@@ -0,0 +1,4449 @@
+/* Compressed unicode tables */
+/* Automatically generated file - do not edit */
+
+#include <stdint.h>
+
+static const uint32_t case_conv_table1[370] = {
+ 0x00209a30, 0x00309a00, 0x005a8173, 0x00601730,
+ 0x006c0730, 0x006f81b3, 0x00701700, 0x007c0700,
+ 0x007f8100, 0x00803040, 0x009801c3, 0x00988190,
+ 0x00990640, 0x009c9040, 0x00a481b4, 0x00a52e40,
+ 0x00bc0130, 0x00bc8640, 0x00bf8170, 0x00c00100,
+ 0x00c08130, 0x00c10440, 0x00c30130, 0x00c38240,
+ 0x00c48230, 0x00c58240, 0x00c70130, 0x00c78130,
+ 0x00c80130, 0x00c88240, 0x00c98130, 0x00ca0130,
+ 0x00ca8100, 0x00cb0130, 0x00cb8130, 0x00cc0240,
+ 0x00cd0100, 0x00ce0130, 0x00ce8130, 0x00cf0100,
+ 0x00cf8130, 0x00d00640, 0x00d30130, 0x00d38240,
+ 0x00d48130, 0x00d60240, 0x00d70130, 0x00d78240,
+ 0x00d88230, 0x00d98440, 0x00db8130, 0x00dc0240,
+ 0x00de0240, 0x00df8100, 0x00e20350, 0x00e38350,
+ 0x00e50350, 0x00e69040, 0x00ee8100, 0x00ef1240,
+ 0x00f801b4, 0x00f88350, 0x00fa0240, 0x00fb0130,
+ 0x00fb8130, 0x00fc2840, 0x01100130, 0x01111240,
+ 0x011d0131, 0x011d8240, 0x011e8130, 0x011f0131,
+ 0x011f8201, 0x01208240, 0x01218130, 0x01220130,
+ 0x01228130, 0x01230a40, 0x01280101, 0x01288101,
+ 0x01290101, 0x01298100, 0x012a0100, 0x012b0200,
+ 0x012c8100, 0x012d8100, 0x012e0101, 0x01300100,
+ 0x01308101, 0x01318100, 0x01328101, 0x01330101,
+ 0x01340100, 0x01348100, 0x01350101, 0x01358101,
+ 0x01360101, 0x01378100, 0x01388101, 0x01390100,
+ 0x013a8100, 0x013e8101, 0x01400100, 0x01410101,
+ 0x01418100, 0x01438101, 0x01440100, 0x01448100,
+ 0x01450200, 0x01460100, 0x01490100, 0x014e8101,
+ 0x014f0101, 0x01a28173, 0x01b80440, 0x01bb0240,
+ 0x01bd8300, 0x01bf8130, 0x01c30130, 0x01c40330,
+ 0x01c60130, 0x01c70230, 0x01c801d0, 0x01c89130,
+ 0x01d18930, 0x01d60100, 0x01d68300, 0x01d801d3,
+ 0x01d89100, 0x01e10173, 0x01e18900, 0x01e60100,
+ 0x01e68200, 0x01e78130, 0x01e80173, 0x01e88173,
+ 0x01ea8173, 0x01eb0173, 0x01eb8100, 0x01ec1840,
+ 0x01f80173, 0x01f88173, 0x01f90100, 0x01f98100,
+ 0x01fa01a0, 0x01fa8173, 0x01fb8240, 0x01fc8130,
+ 0x01fd0240, 0x01fe8330, 0x02001030, 0x02082030,
+ 0x02182000, 0x02281000, 0x02302240, 0x02453640,
+ 0x02600130, 0x02608e40, 0x02678100, 0x02686040,
+ 0x0298a630, 0x02b0a600, 0x02c381b5, 0x08502631,
+ 0x08638131, 0x08668131, 0x08682b00, 0x087e8300,
+ 0x09d05011, 0x09f80610, 0x09fc0620, 0x0e400174,
+ 0x0e408174, 0x0e410174, 0x0e418174, 0x0e420174,
+ 0x0e428174, 0x0e430174, 0x0e438180, 0x0e440180,
+ 0x0e482b30, 0x0e5e8330, 0x0ebc8101, 0x0ebe8101,
+ 0x0ec70101, 0x0f007e40, 0x0f3f1840, 0x0f4b01b5,
+ 0x0f4b81b6, 0x0f4c01b6, 0x0f4c81b6, 0x0f4d01b7,
+ 0x0f4d8180, 0x0f4f0130, 0x0f506040, 0x0f800800,
+ 0x0f840830, 0x0f880600, 0x0f8c0630, 0x0f900800,
+ 0x0f940830, 0x0f980800, 0x0f9c0830, 0x0fa00600,
+ 0x0fa40630, 0x0fa801b0, 0x0fa88100, 0x0fa901d3,
+ 0x0fa98100, 0x0faa01d3, 0x0faa8100, 0x0fab01d3,
+ 0x0fab8100, 0x0fac8130, 0x0fad8130, 0x0fae8130,
+ 0x0faf8130, 0x0fb00800, 0x0fb40830, 0x0fb80200,
+ 0x0fb90400, 0x0fbb0200, 0x0fbc0201, 0x0fbd0201,
+ 0x0fbe0201, 0x0fc008b7, 0x0fc40867, 0x0fc808b8,
+ 0x0fcc0868, 0x0fd008b8, 0x0fd40868, 0x0fd80200,
+ 0x0fd901b9, 0x0fd981b1, 0x0fda01b9, 0x0fdb01b1,
+ 0x0fdb81d7, 0x0fdc0230, 0x0fdd0230, 0x0fde0161,
+ 0x0fdf0173, 0x0fe101b9, 0x0fe181b2, 0x0fe201ba,
+ 0x0fe301b2, 0x0fe381d8, 0x0fe40430, 0x0fe60162,
+ 0x0fe80200, 0x0fe901d0, 0x0fe981d0, 0x0feb01b0,
+ 0x0feb81d0, 0x0fec0230, 0x0fed0230, 0x0ff00201,
+ 0x0ff101d3, 0x0ff181d3, 0x0ff201ba, 0x0ff28101,
+ 0x0ff301b0, 0x0ff381d3, 0x0ff40230, 0x0ff50230,
+ 0x0ff60131, 0x0ff901ba, 0x0ff981b2, 0x0ffa01bb,
+ 0x0ffb01b2, 0x0ffb81d9, 0x0ffc0230, 0x0ffd0230,
+ 0x0ffe0162, 0x109301a0, 0x109501a0, 0x109581a0,
+ 0x10990131, 0x10a70101, 0x10b01031, 0x10b81001,
+ 0x10c18240, 0x125b1a31, 0x12681a01, 0x16003031,
+ 0x16183001, 0x16300240, 0x16310130, 0x16318130,
+ 0x16320130, 0x16328100, 0x16330100, 0x16338640,
+ 0x16368130, 0x16370130, 0x16378130, 0x16380130,
+ 0x16390240, 0x163a8240, 0x163f0230, 0x16406440,
+ 0x16758440, 0x16790240, 0x16802600, 0x16938100,
+ 0x16968100, 0x53202e40, 0x53401c40, 0x53910e40,
+ 0x53993e40, 0x53bc8440, 0x53be8130, 0x53bf0a40,
+ 0x53c58240, 0x53c68130, 0x53c80440, 0x53ca0101,
+ 0x53cb1440, 0x53d50130, 0x53d58130, 0x53d60130,
+ 0x53d68130, 0x53d70130, 0x53d80130, 0x53d88130,
+ 0x53d90130, 0x53d98131, 0x53da1040, 0x53e20131,
+ 0x53e28130, 0x53e30130, 0x53e38440, 0x53e80240,
+ 0x53eb0440, 0x53fa8240, 0x55a98101, 0x55b85020,
+ 0x7d8001b2, 0x7d8081b2, 0x7d8101b2, 0x7d8181da,
+ 0x7d8201da, 0x7d8281b3, 0x7d8301b3, 0x7d8981bb,
+ 0x7d8a01bb, 0x7d8a81bb, 0x7d8b01bc, 0x7d8b81bb,
+ 0x7f909a31, 0x7fa09a01, 0x82002831, 0x82142801,
+ 0x82582431, 0x826c2401, 0x82b80b31, 0x82be0f31,
+ 0x82c60731, 0x82ca0231, 0x82cb8b01, 0x82d18f01,
+ 0x82d98701, 0x82dd8201, 0x86403331, 0x86603301,
+ 0x8c502031, 0x8c602001, 0xb7202031, 0xb7302001,
+ 0xf4802231, 0xf4912201,
+};
+
+static const uint8_t case_conv_table2[370] = {
+ 0x01, 0x00, 0x9c, 0x06, 0x07, 0x4d, 0x03, 0x04,
+ 0x10, 0x00, 0x8f, 0x0b, 0x00, 0x00, 0x11, 0x00,
+ 0x08, 0x00, 0x53, 0x4a, 0x51, 0x00, 0x52, 0x00,
+ 0x53, 0x00, 0x3a, 0x54, 0x55, 0x00, 0x57, 0x59,
+ 0x3f, 0x5d, 0x5c, 0x00, 0x46, 0x61, 0x63, 0x42,
+ 0x64, 0x00, 0x66, 0x00, 0x68, 0x00, 0x6a, 0x00,
+ 0x6c, 0x00, 0x6e, 0x00, 0x00, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x1a, 0x00, 0x93, 0x00, 0x00, 0x20,
+ 0x35, 0x00, 0x27, 0x00, 0x21, 0x00, 0x24, 0x22,
+ 0x2a, 0x00, 0x13, 0x6b, 0x6d, 0x00, 0x26, 0x24,
+ 0x27, 0x14, 0x16, 0x18, 0x1b, 0x1c, 0x3e, 0x1e,
+ 0x3f, 0x1f, 0x39, 0x3d, 0x22, 0x21, 0x41, 0x1e,
+ 0x40, 0x25, 0x25, 0x26, 0x28, 0x20, 0x2a, 0x48,
+ 0x2c, 0x43, 0x2e, 0x4b, 0x30, 0x4c, 0x32, 0x44,
+ 0x42, 0x99, 0x00, 0x00, 0x95, 0x8f, 0x7d, 0x7e,
+ 0x83, 0x84, 0x12, 0x80, 0x82, 0x76, 0x77, 0x12,
+ 0x7b, 0xa3, 0x7c, 0x78, 0x79, 0x8a, 0x92, 0x98,
+ 0xa6, 0xa0, 0x85, 0x00, 0x9a, 0xa1, 0x93, 0x75,
+ 0x33, 0x95, 0x00, 0x8e, 0x00, 0x74, 0x99, 0x98,
+ 0x97, 0x96, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00,
+ 0xa1, 0xa0, 0x15, 0x2e, 0x2f, 0x30, 0xb4, 0xb5,
+ 0x4f, 0xaa, 0xa9, 0x12, 0x14, 0x1e, 0x21, 0x22,
+ 0x22, 0x2a, 0x34, 0x35, 0xa6, 0xa7, 0x36, 0x1f,
+ 0x49, 0x00, 0x00, 0x97, 0x01, 0x5a, 0xda, 0x1d,
+ 0x36, 0x05, 0x00, 0xc4, 0xc3, 0xc6, 0xc5, 0xc8,
+ 0xc7, 0xca, 0xc9, 0xcc, 0xcb, 0xc4, 0xd5, 0x45,
+ 0xd6, 0x42, 0xd7, 0x46, 0xd8, 0xce, 0xd0, 0xd2,
+ 0xd4, 0xda, 0xd9, 0xee, 0xf6, 0xfe, 0x0e, 0x07,
+ 0x0f, 0x80, 0x9f, 0x00, 0x21, 0x80, 0xa3, 0xed,
+ 0x00, 0xc0, 0x40, 0xc6, 0x60, 0xe7, 0xdb, 0xe6,
+ 0x99, 0xc0, 0x00, 0x00, 0x06, 0x60, 0xdc, 0x29,
+ 0xfd, 0x15, 0x12, 0x06, 0x16, 0xf8, 0xdd, 0x06,
+ 0x15, 0x12, 0x84, 0x08, 0xc6, 0x16, 0xff, 0xdf,
+ 0x03, 0xc0, 0x40, 0x00, 0x46, 0x60, 0xde, 0xe0,
+ 0x6d, 0x37, 0x38, 0x39, 0x15, 0x14, 0x17, 0x16,
+ 0x00, 0x1a, 0x19, 0x1c, 0x1b, 0x00, 0x5f, 0xb7,
+ 0x65, 0x44, 0x47, 0x00, 0x4f, 0x62, 0x4e, 0x50,
+ 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xa3, 0xa4,
+ 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x00,
+ 0x00, 0x5a, 0x00, 0x47, 0x00, 0x5b, 0x56, 0x58,
+ 0x60, 0x5e, 0x70, 0x69, 0x6f, 0x4e, 0x00, 0x3b,
+ 0x67, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x45, 0xa8,
+ 0x8a, 0x8b, 0x8c, 0xab, 0xac, 0x58, 0x58, 0xaf,
+ 0x94, 0xb0, 0x6f, 0xb2, 0x5d, 0x5c, 0x5f, 0x5e,
+ 0x61, 0x60, 0x66, 0x67, 0x68, 0x69, 0x62, 0x63,
+ 0x64, 0x65, 0x6b, 0x6a, 0x6d, 0x6c, 0x6f, 0x6e,
+ 0x71, 0x70,
+};
+
+static const uint16_t case_conv_ext[58] = {
+ 0x0399, 0x0308, 0x0301, 0x03a5, 0x0313, 0x0300, 0x0342, 0x0391,
+ 0x0397, 0x03a9, 0x0046, 0x0049, 0x004c, 0x0053, 0x0069, 0x0307,
+ 0x02bc, 0x004e, 0x004a, 0x030c, 0x0535, 0x0552, 0x0048, 0x0331,
+ 0x0054, 0x0057, 0x030a, 0x0059, 0x0041, 0x02be, 0x1f08, 0x1f80,
+ 0x1f28, 0x1f90, 0x1f68, 0x1fa0, 0x1fba, 0x0386, 0x1fb3, 0x1fca,
+ 0x0389, 0x1fc3, 0x03a1, 0x1ffa, 0x038f, 0x1ff3, 0x0544, 0x0546,
+ 0x053b, 0x054e, 0x053d, 0x03b8, 0x0462, 0xa64a, 0x1e60, 0x03c9,
+ 0x006b, 0x00e5,
+};
+
+static const uint8_t unicode_prop_Cased1_table[188] = {
+ 0x40, 0xa9, 0x80, 0x8e, 0x80, 0xfc, 0x80, 0xd3,
+ 0x80, 0x8c, 0x80, 0x8d, 0x81, 0x8d, 0x02, 0x80,
+ 0xe1, 0x80, 0x91, 0x85, 0x9a, 0x01, 0x00, 0x01,
+ 0x11, 0x00, 0x01, 0x04, 0x08, 0x01, 0x08, 0x30,
+ 0x08, 0x01, 0x15, 0x20, 0x00, 0x39, 0x99, 0x31,
+ 0x9d, 0x84, 0x40, 0x94, 0x80, 0xd6, 0x82, 0xa6,
+ 0x80, 0x41, 0x62, 0x80, 0xa6, 0x80, 0x57, 0x76,
+ 0xf8, 0x02, 0x80, 0x8f, 0x80, 0xb0, 0x40, 0xdb,
+ 0x08, 0x80, 0x41, 0xd0, 0x80, 0x8c, 0x80, 0x8f,
+ 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00, 0x14, 0x28,
+ 0x10, 0x11, 0x02, 0x01, 0x18, 0x0b, 0x24, 0x4b,
+ 0x26, 0x01, 0x01, 0x86, 0xe5, 0x80, 0x60, 0x79,
+ 0xb6, 0x81, 0x40, 0x91, 0x81, 0xbd, 0x88, 0x94,
+ 0x05, 0x80, 0x98, 0x80, 0xa2, 0x00, 0x80, 0xa1,
+ 0x82, 0x43, 0x34, 0xa2, 0x06, 0x80, 0x8c, 0x60,
+ 0x5c, 0x16, 0x01, 0x10, 0xa9, 0x80, 0x88, 0x60,
+ 0xcc, 0x44, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09,
+ 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03,
+ 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16,
+ 0x80, 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80,
+ 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80,
+ 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07,
+ 0x47, 0x33, 0x89, 0x80, 0x93, 0x52, 0x10, 0x99,
+ 0x85, 0x99, 0x85, 0x99,
+};
+
+static const uint8_t unicode_prop_Cased1_index[18] = {
+ 0xb9, 0x02, 0xe0, 0xa0, 0x1e, 0x40, 0x9e, 0xa6,
+ 0x40, 0x55, 0xd4, 0x61, 0xfb, 0xd6, 0x21, 0x8a,
+ 0xf1, 0x01,
+};
+
+static const uint8_t unicode_prop_Case_Ignorable_table[720] = {
+ 0xa6, 0x05, 0x80, 0x8a, 0x80, 0xa2, 0x00, 0x80,
+ 0xc6, 0x03, 0x00, 0x03, 0x01, 0x81, 0x41, 0xf6,
+ 0x40, 0xbf, 0x19, 0x18, 0x88, 0x08, 0x80, 0x40,
+ 0xfa, 0x86, 0x40, 0xce, 0x04, 0x80, 0xb0, 0xac,
+ 0x00, 0x01, 0x01, 0x00, 0xab, 0x80, 0x8a, 0x85,
+ 0x89, 0x8a, 0x00, 0xa2, 0x80, 0x89, 0x94, 0x8f,
+ 0x80, 0xe4, 0x38, 0x89, 0x03, 0xa0, 0x00, 0x80,
+ 0x9d, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0x18, 0x08,
+ 0x97, 0x97, 0xaa, 0x82, 0xab, 0x06, 0x0d, 0x87,
+ 0xa8, 0xb9, 0xb6, 0x00, 0x03, 0x3b, 0x02, 0x86,
+ 0x89, 0x81, 0x8c, 0x80, 0x8e, 0x80, 0xb9, 0x03,
+ 0x1f, 0x80, 0x93, 0x81, 0x99, 0x01, 0x81, 0xb8,
+ 0x03, 0x0b, 0x09, 0x12, 0x80, 0x9d, 0x0a, 0x80,
+ 0x8a, 0x81, 0xb8, 0x03, 0x20, 0x0b, 0x80, 0x93,
+ 0x81, 0x95, 0x28, 0x80, 0xb9, 0x01, 0x00, 0x1f,
+ 0x06, 0x81, 0x8a, 0x81, 0x9d, 0x80, 0xbc, 0x80,
+ 0x8b, 0x80, 0xb1, 0x02, 0x80, 0xb6, 0x00, 0x14,
+ 0x10, 0x1e, 0x81, 0x8a, 0x81, 0x9c, 0x80, 0xb9,
+ 0x01, 0x05, 0x04, 0x81, 0x93, 0x81, 0x9b, 0x81,
+ 0xb8, 0x0b, 0x1f, 0x80, 0x93, 0x81, 0x9c, 0x80,
+ 0xc7, 0x06, 0x10, 0x80, 0xd9, 0x01, 0x86, 0x8a,
+ 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x85, 0xc9,
+ 0x81, 0x9a, 0x00, 0x00, 0x80, 0xb6, 0x8d, 0x04,
+ 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe5,
+ 0x18, 0x28, 0x09, 0x81, 0x98, 0x0b, 0x82, 0x8f,
+ 0x83, 0x8c, 0x01, 0x0d, 0x80, 0x8e, 0x80, 0xdd,
+ 0x80, 0x42, 0x5f, 0x82, 0x43, 0xb1, 0x82, 0x9c,
+ 0x81, 0x9d, 0x81, 0x9d, 0x81, 0xbf, 0x08, 0x37,
+ 0x01, 0x8a, 0x10, 0x20, 0xac, 0x84, 0xb2, 0x80,
+ 0xc0, 0x81, 0xa1, 0x80, 0xf5, 0x13, 0x81, 0x88,
+ 0x05, 0x82, 0x40, 0xda, 0x09, 0x80, 0xb9, 0x00,
+ 0x30, 0x00, 0x01, 0x3d, 0x89, 0x08, 0xa6, 0x07,
+ 0x9e, 0xb0, 0x83, 0xaf, 0x00, 0x20, 0x04, 0x80,
+ 0xa7, 0x88, 0x8b, 0x81, 0x9f, 0x19, 0x08, 0x82,
+ 0xb7, 0x00, 0x0a, 0x00, 0x82, 0xb9, 0x39, 0x81,
+ 0xbf, 0x85, 0xd1, 0x10, 0x8c, 0x06, 0x18, 0x28,
+ 0x11, 0xb1, 0xbe, 0x8c, 0x80, 0xa1, 0xe4, 0x41,
+ 0xbc, 0x00, 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c,
+ 0x82, 0x8c, 0x81, 0x8b, 0x27, 0x81, 0x89, 0x01,
+ 0x01, 0x84, 0xb0, 0x20, 0x89, 0x00, 0x8c, 0x80,
+ 0x8f, 0x8c, 0xb2, 0xa0, 0x4b, 0x8a, 0x81, 0xf0,
+ 0x82, 0xfc, 0x80, 0x8e, 0x80, 0xdf, 0x9f, 0xae,
+ 0x80, 0x41, 0xd4, 0x80, 0xa3, 0x1a, 0x24, 0x80,
+ 0xdc, 0x85, 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80,
+ 0x44, 0xe1, 0x85, 0x41, 0x0d, 0x80, 0xe1, 0x18,
+ 0x89, 0x00, 0x9b, 0x83, 0xcf, 0x81, 0x8d, 0xa1,
+ 0xcd, 0x80, 0x96, 0x82, 0xe6, 0x12, 0x0f, 0x02,
+ 0x03, 0x80, 0x98, 0x0c, 0x80, 0x40, 0x96, 0x81,
+ 0x99, 0x91, 0x8c, 0x80, 0xa5, 0x87, 0x98, 0x8a,
+ 0xad, 0x82, 0xaf, 0x01, 0x19, 0x81, 0x90, 0x80,
+ 0x94, 0x81, 0xc1, 0x29, 0x09, 0x81, 0x8b, 0x07,
+ 0x80, 0xa2, 0x80, 0x8a, 0x80, 0xb2, 0x00, 0x11,
+ 0x0c, 0x08, 0x80, 0x9a, 0x80, 0x8d, 0x0c, 0x08,
+ 0x80, 0xe3, 0x84, 0x88, 0x82, 0xf8, 0x01, 0x03,
+ 0x80, 0x60, 0x4f, 0x2f, 0x80, 0x40, 0x92, 0x90,
+ 0x42, 0x3c, 0x8f, 0x10, 0x8b, 0x8f, 0xa1, 0x01,
+ 0x80, 0x40, 0xa8, 0x06, 0x05, 0x80, 0x8a, 0x80,
+ 0xa2, 0x00, 0x80, 0xae, 0x80, 0xac, 0x81, 0xc2,
+ 0x80, 0x94, 0x82, 0x42, 0x00, 0x80, 0x40, 0xe1,
+ 0x80, 0x40, 0x94, 0x84, 0x44, 0x04, 0x28, 0xa9,
+ 0x80, 0x88, 0x42, 0x45, 0x10, 0x0c, 0x83, 0xa7,
+ 0x13, 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x83,
+ 0x41, 0x82, 0x81, 0x40, 0x98, 0x8a, 0xb0, 0x83,
+ 0xfa, 0x80, 0xb5, 0x8e, 0xa8, 0x01, 0x81, 0x89,
+ 0x82, 0xb0, 0x19, 0x09, 0x03, 0x80, 0x89, 0x80,
+ 0xb1, 0x82, 0xa3, 0x20, 0x87, 0xbd, 0x80, 0x8b,
+ 0x81, 0xb3, 0x88, 0x89, 0x19, 0x80, 0xde, 0x11,
+ 0x00, 0x0d, 0x80, 0x40, 0x9f, 0x02, 0x87, 0x94,
+ 0x81, 0xb8, 0x0a, 0x80, 0xa4, 0x32, 0x84, 0x40,
+ 0xc2, 0x39, 0x10, 0x80, 0x96, 0x80, 0xd3, 0x28,
+ 0x03, 0x08, 0x81, 0x40, 0xed, 0x1d, 0x08, 0x81,
+ 0x9a, 0x81, 0xd4, 0x39, 0x00, 0x81, 0xe9, 0x00,
+ 0x01, 0x28, 0x80, 0xe4, 0x11, 0x18, 0x84, 0x41,
+ 0x02, 0x88, 0x01, 0x40, 0xff, 0x08, 0x03, 0x80,
+ 0x40, 0x8f, 0x19, 0x0b, 0x80, 0x9f, 0x89, 0xa7,
+ 0x29, 0x1f, 0x80, 0x88, 0x29, 0x82, 0xad, 0x8c,
+ 0x01, 0x41, 0x95, 0x30, 0x28, 0x80, 0xd1, 0x95,
+ 0x0e, 0x01, 0x01, 0xf9, 0x2a, 0x00, 0x08, 0x30,
+ 0x80, 0xc7, 0x0a, 0x00, 0x80, 0x41, 0x5a, 0x81,
+ 0x55, 0x3a, 0x88, 0x60, 0x36, 0xb6, 0x84, 0xba,
+ 0x86, 0x88, 0x83, 0x44, 0x0a, 0x80, 0xbe, 0x90,
+ 0xbf, 0x08, 0x81, 0x60, 0x40, 0x0a, 0x18, 0x30,
+ 0x81, 0x4c, 0x9d, 0x08, 0x83, 0x52, 0x5b, 0xad,
+ 0x81, 0x96, 0x42, 0x1f, 0x82, 0x88, 0x8f, 0x0e,
+ 0x9d, 0x83, 0x40, 0x93, 0x82, 0x47, 0xba, 0xb6,
+ 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20, 0x8e,
+ 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04, 0x41,
+ 0x04, 0x8d, 0x41, 0x6f, 0x80, 0xbc, 0x83, 0x45,
+ 0xdf, 0x86, 0xec, 0x87, 0x4a, 0xae, 0x84, 0x6c,
+ 0x0c, 0x00, 0x80, 0x9d, 0xdf, 0xff, 0x40, 0xef,
+};
+
+static const uint8_t unicode_prop_Case_Ignorable_index[69] = {
+ 0xbe, 0x05, 0x00, 0xfe, 0x07, 0x00, 0x52, 0x0a,
+ 0xa0, 0xc1, 0x0b, 0x00, 0x82, 0x0d, 0x00, 0x3f,
+ 0x10, 0x80, 0xd4, 0x17, 0x40, 0xcf, 0x1a, 0x20,
+ 0xf5, 0x1c, 0x00, 0x80, 0x20, 0x00, 0x16, 0xa0,
+ 0x00, 0xc6, 0xa8, 0x00, 0xc2, 0xaa, 0x60, 0x56,
+ 0xfe, 0x20, 0xb1, 0x07, 0x01, 0x82, 0x10, 0x21,
+ 0x02, 0x13, 0x21, 0xb8, 0x16, 0x61, 0x97, 0x1a,
+ 0x01, 0x37, 0x6b, 0x21, 0x8c, 0xd1, 0x01, 0xd7,
+ 0xe8, 0x41, 0xf0, 0x01, 0x0e,
+};
+
+static const uint8_t unicode_prop_ID_Start_table[1079] = {
+ 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03,
+ 0x04, 0x96, 0x80, 0x9e, 0x80, 0x41, 0xc9, 0x83,
+ 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20,
+ 0x09, 0x18, 0x05, 0x00, 0x10, 0x00, 0x93, 0x80,
+ 0xd2, 0x80, 0x40, 0x8a, 0x87, 0x40, 0xa5, 0x80,
+ 0xa5, 0x08, 0x85, 0xa8, 0xc6, 0x9a, 0x1b, 0xac,
+ 0xaa, 0xa2, 0x08, 0xe2, 0x00, 0x8e, 0x0e, 0x81,
+ 0x89, 0x11, 0x80, 0x8f, 0x00, 0x9d, 0x9c, 0xd8,
+ 0x8a, 0x80, 0x97, 0xa0, 0x88, 0x0b, 0x04, 0x95,
+ 0x18, 0x88, 0x02, 0x80, 0x96, 0x98, 0x86, 0x8a,
+ 0x84, 0x97, 0x05, 0x90, 0xa9, 0xb9, 0xb5, 0x10,
+ 0x91, 0x06, 0x89, 0x8e, 0x8f, 0x1f, 0x09, 0x81,
+ 0x95, 0x06, 0x00, 0x13, 0x10, 0x8f, 0x80, 0x8c,
+ 0x08, 0x82, 0x8d, 0x81, 0x89, 0x07, 0x2b, 0x09,
+ 0x95, 0x06, 0x01, 0x01, 0x01, 0x9e, 0x18, 0x80,
+ 0x92, 0x82, 0x8f, 0x88, 0x02, 0x80, 0x95, 0x06,
+ 0x01, 0x04, 0x10, 0x91, 0x80, 0x8e, 0x81, 0x96,
+ 0x80, 0x8a, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04,
+ 0x10, 0x9d, 0x08, 0x82, 0x8e, 0x80, 0x90, 0x00,
+ 0x2a, 0x10, 0x1a, 0x08, 0x00, 0x0a, 0x0a, 0x12,
+ 0x8b, 0x95, 0x80, 0xb3, 0x38, 0x10, 0x96, 0x80,
+ 0x8f, 0x10, 0x99, 0x11, 0x01, 0x81, 0x9d, 0x03,
+ 0x38, 0x10, 0x96, 0x80, 0x89, 0x04, 0x10, 0x9e,
+ 0x08, 0x81, 0x8e, 0x81, 0x90, 0x88, 0x02, 0x80,
+ 0xa8, 0x08, 0x8f, 0x04, 0x17, 0x82, 0x97, 0x2c,
+ 0x91, 0x82, 0x97, 0x80, 0x88, 0x00, 0x0e, 0xb9,
+ 0xaf, 0x01, 0x8b, 0x86, 0xb9, 0x08, 0x00, 0x20,
+ 0x97, 0x00, 0x80, 0x89, 0x01, 0x88, 0x01, 0x20,
+ 0x80, 0x94, 0x83, 0x9f, 0x80, 0xbe, 0x38, 0xa3,
+ 0x9a, 0x84, 0xf2, 0xaa, 0x93, 0x80, 0x8f, 0x2b,
+ 0x1a, 0x02, 0x0e, 0x13, 0x8c, 0x8b, 0x80, 0x90,
+ 0xa5, 0x00, 0x20, 0x81, 0xaa, 0x80, 0x41, 0x4c,
+ 0x03, 0x0e, 0x00, 0x03, 0x81, 0xa8, 0x03, 0x81,
+ 0xa0, 0x03, 0x0e, 0x00, 0x03, 0x81, 0x8e, 0x80,
+ 0xb8, 0x03, 0x81, 0xc2, 0xa4, 0x8f, 0x8f, 0xd5,
+ 0x0d, 0x82, 0x42, 0x6b, 0x81, 0x90, 0x80, 0x99,
+ 0x84, 0xca, 0x82, 0x8a, 0x86, 0x91, 0x8c, 0x92,
+ 0x8d, 0x91, 0x8d, 0x8c, 0x02, 0x8e, 0xb3, 0xa2,
+ 0x03, 0x80, 0xc2, 0xd8, 0x86, 0xa8, 0x00, 0x84,
+ 0xc5, 0x89, 0x9e, 0xb0, 0x9d, 0x0c, 0x8a, 0xab,
+ 0x83, 0x99, 0xb5, 0x96, 0x88, 0xb4, 0xd1, 0x80,
+ 0xdc, 0xae, 0x90, 0x87, 0xb5, 0x9d, 0x8c, 0x81,
+ 0x89, 0xab, 0x99, 0xa3, 0xa8, 0x82, 0x89, 0xa3,
+ 0x81, 0x88, 0x86, 0xaa, 0x0a, 0xa8, 0x18, 0x28,
+ 0x0a, 0x04, 0x40, 0xbf, 0xbf, 0x41, 0x15, 0x0d,
+ 0x81, 0xa5, 0x0d, 0x0f, 0x00, 0x00, 0x00, 0x80,
+ 0x9e, 0x81, 0xb4, 0x06, 0x00, 0x12, 0x06, 0x13,
+ 0x0d, 0x83, 0x8c, 0x22, 0x06, 0xf3, 0x80, 0x8c,
+ 0x80, 0x8f, 0x8c, 0xe4, 0x03, 0x01, 0x89, 0x00,
+ 0x0d, 0x28, 0x00, 0x00, 0x80, 0x8f, 0x0b, 0x24,
+ 0x18, 0x90, 0xa8, 0x4a, 0x76, 0x40, 0xe4, 0x2b,
+ 0x11, 0x8b, 0xa5, 0x00, 0x20, 0x81, 0xb7, 0x30,
+ 0x8f, 0x96, 0x88, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x86, 0x42, 0x25, 0x82, 0x98, 0x88,
+ 0x34, 0x0c, 0x83, 0xd5, 0x1c, 0x80, 0xd9, 0x03,
+ 0x84, 0xaa, 0x80, 0xdd, 0x90, 0x9f, 0xaf, 0x8f,
+ 0x41, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x56, 0x8c,
+ 0xc2, 0xad, 0x81, 0x41, 0x0c, 0x82, 0x8f, 0x89,
+ 0x81, 0x93, 0xae, 0x8f, 0x9e, 0x81, 0xcf, 0xa6,
+ 0x88, 0x81, 0xe6, 0x81, 0xbf, 0x21, 0x00, 0x04,
+ 0x97, 0x8f, 0x02, 0x03, 0x80, 0x96, 0x9c, 0xb3,
+ 0x8d, 0xb1, 0xbd, 0x2a, 0x00, 0x81, 0x8a, 0x9b,
+ 0x89, 0x96, 0x98, 0x9c, 0x86, 0xae, 0x9b, 0x80,
+ 0x8f, 0x20, 0x89, 0x89, 0x20, 0xa8, 0x96, 0x10,
+ 0x87, 0x93, 0x96, 0x10, 0x82, 0xb1, 0x00, 0x11,
+ 0x0c, 0x08, 0x00, 0x97, 0x11, 0x8a, 0x32, 0x8b,
+ 0x29, 0x29, 0x85, 0x88, 0x30, 0x30, 0xaa, 0x80,
+ 0x8d, 0x85, 0xf2, 0x9c, 0x60, 0x2b, 0xa3, 0x8b,
+ 0x96, 0x83, 0xb0, 0x60, 0x21, 0x03, 0x41, 0x6d,
+ 0x81, 0xe9, 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x89,
+ 0x80, 0x8c, 0x04, 0x00, 0x01, 0x01, 0x80, 0xeb,
+ 0xa0, 0x41, 0x6a, 0x91, 0xbf, 0x81, 0xb5, 0xa7,
+ 0x8b, 0xf3, 0x20, 0x40, 0x86, 0xa3, 0x99, 0x85,
+ 0x99, 0x8a, 0xd8, 0x15, 0x0d, 0x0d, 0x0a, 0xa2,
+ 0x8b, 0x80, 0x99, 0x80, 0x92, 0x01, 0x80, 0x8e,
+ 0x81, 0x8d, 0xa1, 0xfa, 0xc4, 0xb4, 0x41, 0x0a,
+ 0x9c, 0x82, 0xb0, 0xae, 0x9f, 0x8c, 0x9d, 0x84,
+ 0xa5, 0x89, 0x9d, 0x81, 0xa3, 0x1f, 0x04, 0xa9,
+ 0x40, 0x9d, 0x91, 0xa3, 0x83, 0xa3, 0x83, 0xa7,
+ 0x87, 0xb3, 0x8b, 0x8a, 0x80, 0x8e, 0x06, 0x01,
+ 0x80, 0x8a, 0x80, 0x8e, 0x06, 0x01, 0xc2, 0x41,
+ 0x36, 0x88, 0x95, 0x89, 0x87, 0x97, 0x28, 0xa9,
+ 0x80, 0x88, 0xc4, 0x29, 0x00, 0xab, 0x01, 0x10,
+ 0x81, 0x96, 0x89, 0x96, 0x88, 0x9e, 0xc0, 0x92,
+ 0x01, 0x89, 0x95, 0x89, 0x99, 0xc5, 0xb7, 0x29,
+ 0xbf, 0x80, 0x8e, 0x18, 0x10, 0x9c, 0xa9, 0x9c,
+ 0x82, 0x9c, 0xa2, 0x38, 0x9b, 0x9a, 0xb5, 0x89,
+ 0x95, 0x89, 0x92, 0x8c, 0x91, 0xed, 0xc8, 0xb6,
+ 0xb2, 0x8c, 0xb2, 0x8c, 0xa3, 0x41, 0x5b, 0xa9,
+ 0x29, 0xcd, 0x9c, 0x89, 0x07, 0x95, 0xa9, 0x91,
+ 0xad, 0x94, 0x9a, 0x96, 0x8b, 0xb4, 0xb8, 0x09,
+ 0x80, 0x8c, 0xac, 0x9f, 0x98, 0x99, 0xa3, 0x9c,
+ 0x01, 0x07, 0xa2, 0x10, 0x8b, 0xaf, 0x8d, 0x83,
+ 0x94, 0x00, 0x80, 0xa2, 0x91, 0x80, 0x98, 0xd3,
+ 0x30, 0x00, 0x18, 0x8e, 0x80, 0x89, 0x86, 0xae,
+ 0xa5, 0x39, 0x09, 0x95, 0x06, 0x01, 0x04, 0x10,
+ 0x91, 0x80, 0x8b, 0x84, 0x40, 0x9d, 0xb4, 0x91,
+ 0x83, 0x93, 0x82, 0x9d, 0xaf, 0x93, 0x08, 0x80,
+ 0x40, 0xb7, 0xae, 0xa8, 0x83, 0xa3, 0xaf, 0x93,
+ 0x80, 0xba, 0xaa, 0x8c, 0x80, 0xc6, 0x9a, 0xa4,
+ 0x86, 0x40, 0xb8, 0xab, 0xf3, 0xbf, 0x9e, 0x39,
+ 0x01, 0x38, 0x08, 0x97, 0x8e, 0x00, 0x80, 0xdd,
+ 0x39, 0xa6, 0x8f, 0x00, 0x80, 0x9b, 0x80, 0x89,
+ 0xa7, 0x30, 0x94, 0x80, 0x8a, 0xad, 0x92, 0x80,
+ 0x91, 0xc8, 0x41, 0x06, 0x88, 0x80, 0xa4, 0x90,
+ 0x80, 0xb0, 0x9d, 0xef, 0x30, 0x08, 0xa5, 0x94,
+ 0x80, 0x98, 0x28, 0x08, 0x9f, 0x8d, 0x80, 0x41,
+ 0x46, 0x92, 0x40, 0xbc, 0x80, 0xce, 0x43, 0x99,
+ 0xe5, 0xee, 0x90, 0x40, 0xc3, 0x4a, 0x4b, 0xe0,
+ 0x8e, 0x44, 0x2e, 0x4f, 0xd0, 0x42, 0x46, 0x60,
+ 0x21, 0xb8, 0x42, 0x38, 0x86, 0x9e, 0x90, 0xce,
+ 0x90, 0x9d, 0x91, 0xaf, 0x8f, 0x83, 0x9e, 0x94,
+ 0x84, 0x92, 0x42, 0xaf, 0xbf, 0xff, 0xca, 0x20,
+ 0xc1, 0x8c, 0xbf, 0x08, 0x80, 0x9b, 0x57, 0xf7,
+ 0x87, 0x44, 0xd5, 0xa9, 0x88, 0x60, 0x22, 0xe6,
+ 0x18, 0x30, 0x08, 0x41, 0x22, 0xac, 0x82, 0x90,
+ 0x1f, 0x41, 0x8b, 0x49, 0x03, 0xea, 0x84, 0x8c,
+ 0x82, 0x88, 0x86, 0x89, 0x57, 0x65, 0xd4, 0x80,
+ 0xc6, 0x01, 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00,
+ 0x06, 0x80, 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b,
+ 0x03, 0x04, 0x00, 0x16, 0x80, 0x41, 0x53, 0x81,
+ 0x98, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80,
+ 0x9e, 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80,
+ 0x9e, 0x80, 0x98, 0x07, 0x47, 0x33, 0x9e, 0x41,
+ 0xe0, 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, 0x40,
+ 0x9d, 0x91, 0xab, 0x44, 0xf3, 0x30, 0x18, 0x08,
+ 0x8e, 0x80, 0x40, 0xc4, 0xba, 0xc3, 0x30, 0x44,
+ 0xb3, 0x18, 0x9a, 0x01, 0x00, 0x08, 0x80, 0x89,
+ 0x03, 0x00, 0x00, 0x28, 0x18, 0x00, 0x00, 0x02,
+ 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00, 0x80, 0x89,
+ 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, 0x51, 0x43,
+ 0x60, 0xa6, 0xdf, 0x9f, 0x50, 0x38, 0x86, 0x40,
+ 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x4c,
+ 0x1e, 0x42, 0x1d, 0x45, 0xe1, 0x53, 0x4a,
+};
+
+static const uint8_t unicode_prop_ID_Start_index[102] = {
+ 0xf6, 0x03, 0x20, 0xa6, 0x07, 0x00, 0xa9, 0x09,
+ 0x20, 0xb1, 0x0a, 0x00, 0xba, 0x0b, 0x20, 0x3b,
+ 0x0d, 0x20, 0xc7, 0x0e, 0x20, 0x49, 0x12, 0x00,
+ 0x9b, 0x16, 0x00, 0xac, 0x19, 0x00, 0xc0, 0x1d,
+ 0x80, 0x80, 0x20, 0x20, 0x70, 0x2d, 0x00, 0x00,
+ 0x32, 0x00, 0xda, 0xa7, 0x00, 0x4c, 0xaa, 0x20,
+ 0xc7, 0xd7, 0x20, 0xfc, 0xfd, 0x20, 0x9d, 0x02,
+ 0x21, 0x96, 0x05, 0x01, 0xf3, 0x08, 0x01, 0xb3,
+ 0x0c, 0x21, 0x73, 0x11, 0x61, 0x3e, 0x13, 0x01,
+ 0x47, 0x17, 0x21, 0x9e, 0x1a, 0x01, 0x9a, 0x23,
+ 0x01, 0x78, 0x6b, 0x01, 0xfc, 0xb2, 0x61, 0x3a,
+ 0xd5, 0x01, 0x2d, 0xe1, 0x41, 0x33, 0xee, 0x01,
+ 0xe0, 0xa6, 0x62, 0x4b, 0x13, 0x03,
+};
+
+static const uint8_t unicode_prop_ID_Continue1_table[640] = {
+ 0xaf, 0x89, 0xa4, 0x80, 0xd6, 0x80, 0x42, 0x47,
+ 0xef, 0x96, 0x80, 0x40, 0xfa, 0x84, 0x41, 0x08,
+ 0xac, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf,
+ 0x9e, 0x28, 0xe4, 0x31, 0x29, 0x08, 0x19, 0x89,
+ 0x96, 0x80, 0x9d, 0x9a, 0xda, 0x8a, 0x8e, 0x89,
+ 0xa0, 0x88, 0x88, 0x80, 0x97, 0x18, 0x88, 0x02,
+ 0x04, 0xaa, 0x82, 0xbb, 0x87, 0xa9, 0x97, 0x80,
+ 0xa0, 0xb5, 0x10, 0x91, 0x06, 0x89, 0x09, 0x89,
+ 0x90, 0x82, 0xb7, 0x00, 0x31, 0x09, 0x82, 0x88,
+ 0x80, 0x89, 0x09, 0x89, 0x8d, 0x01, 0x82, 0xb7,
+ 0x00, 0x23, 0x09, 0x12, 0x80, 0x93, 0x8b, 0x10,
+ 0x8a, 0x82, 0xb7, 0x00, 0x38, 0x10, 0x82, 0x93,
+ 0x09, 0x89, 0x89, 0x28, 0x82, 0xb7, 0x00, 0x31,
+ 0x09, 0x16, 0x82, 0x89, 0x09, 0x89, 0x91, 0x80,
+ 0xba, 0x22, 0x10, 0x83, 0x88, 0x80, 0x8d, 0x89,
+ 0x8f, 0x84, 0xb6, 0x00, 0x30, 0x10, 0x1e, 0x81,
+ 0x8a, 0x09, 0x89, 0x90, 0x82, 0xb7, 0x00, 0x30,
+ 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x8f, 0x83,
+ 0xb6, 0x08, 0x30, 0x10, 0x83, 0x88, 0x80, 0x89,
+ 0x09, 0x89, 0x90, 0x82, 0xc5, 0x03, 0x28, 0x00,
+ 0x3d, 0x89, 0x09, 0xbc, 0x01, 0x86, 0x8b, 0x38,
+ 0x89, 0xd6, 0x01, 0x88, 0x8a, 0x29, 0x89, 0xbd,
+ 0x0d, 0x89, 0x8a, 0x00, 0x00, 0x03, 0x81, 0xb0,
+ 0x93, 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80,
+ 0xe3, 0x93, 0x80, 0x89, 0x8b, 0x1b, 0x10, 0x11,
+ 0x32, 0x83, 0x8c, 0x8b, 0x80, 0x8e, 0x42, 0xbe,
+ 0x82, 0x88, 0x88, 0x43, 0x9f, 0x83, 0x9b, 0x82,
+ 0x9c, 0x81, 0x9d, 0x81, 0xbf, 0x9f, 0x88, 0x01,
+ 0x89, 0xa0, 0x10, 0x8a, 0x40, 0x8e, 0x80, 0xf5,
+ 0x8b, 0x83, 0x8b, 0x89, 0x89, 0xff, 0x8a, 0xbb,
+ 0x84, 0xb8, 0x89, 0x80, 0x9c, 0x81, 0x8a, 0x85,
+ 0x89, 0x95, 0x8d, 0x80, 0x8f, 0xb0, 0x84, 0xae,
+ 0x90, 0x8a, 0x89, 0x90, 0x88, 0x8b, 0x82, 0x9d,
+ 0x8c, 0x81, 0x89, 0xab, 0x8d, 0xaf, 0x93, 0x87,
+ 0x89, 0x85, 0x89, 0xf5, 0x10, 0x94, 0x18, 0x28,
+ 0x0a, 0x40, 0xc5, 0xbf, 0x42, 0x3e, 0x81, 0x92,
+ 0x80, 0xfa, 0x8c, 0x18, 0x82, 0x8b, 0x4b, 0xfd,
+ 0x82, 0x40, 0x8c, 0x80, 0xdf, 0x9f, 0x42, 0x29,
+ 0x85, 0xe8, 0x81, 0x60, 0x75, 0x84, 0x89, 0xc4,
+ 0x03, 0x89, 0x9f, 0x81, 0xcf, 0x81, 0x41, 0x0f,
+ 0x02, 0x03, 0x80, 0x96, 0x23, 0x80, 0xd2, 0x81,
+ 0xb1, 0x91, 0x89, 0x89, 0x85, 0x91, 0x8c, 0x8a,
+ 0x9b, 0x87, 0x98, 0x8c, 0xab, 0x83, 0xae, 0x8d,
+ 0x8e, 0x89, 0x8a, 0x80, 0x89, 0x89, 0xae, 0x8d,
+ 0x8b, 0x07, 0x09, 0x89, 0xa0, 0x82, 0xb1, 0x00,
+ 0x11, 0x0c, 0x08, 0x80, 0xa8, 0x24, 0x81, 0x40,
+ 0xeb, 0x38, 0x09, 0x89, 0x60, 0x4f, 0x23, 0x80,
+ 0x42, 0xe0, 0x8f, 0x8f, 0x8f, 0x11, 0x97, 0x82,
+ 0x40, 0xbf, 0x89, 0xa4, 0x80, 0x42, 0xbc, 0x80,
+ 0x40, 0xe1, 0x80, 0x40, 0x94, 0x84, 0x41, 0x24,
+ 0x89, 0x45, 0x56, 0x10, 0x0c, 0x83, 0xa7, 0x13,
+ 0x80, 0x40, 0xa4, 0x81, 0x42, 0x3c, 0x1f, 0x89,
+ 0x41, 0x70, 0x81, 0x40, 0x98, 0x8a, 0xb0, 0x83,
+ 0xf9, 0x82, 0xb4, 0x8e, 0x9e, 0x8a, 0x09, 0x89,
+ 0x83, 0xac, 0x8a, 0x30, 0xac, 0x89, 0x2a, 0xa3,
+ 0x8d, 0x80, 0x89, 0x21, 0xab, 0x80, 0x8b, 0x82,
+ 0xaf, 0x8d, 0x3b, 0x80, 0x8b, 0xd1, 0x8b, 0x28,
+ 0x40, 0x9f, 0x8b, 0x84, 0x89, 0x2b, 0xb6, 0x08,
+ 0x31, 0x09, 0x82, 0x88, 0x80, 0x89, 0x09, 0x32,
+ 0x84, 0x40, 0xbf, 0x91, 0x88, 0x89, 0x18, 0xd0,
+ 0x93, 0x8b, 0x89, 0x40, 0xd4, 0x31, 0x88, 0x9a,
+ 0x81, 0xd1, 0x90, 0x8e, 0x89, 0xd0, 0x8c, 0x87,
+ 0x89, 0xd2, 0x8e, 0x83, 0x89, 0x40, 0xf1, 0x8e,
+ 0x40, 0xa4, 0x89, 0xc5, 0x28, 0x09, 0x18, 0x00,
+ 0x81, 0x8b, 0x89, 0xf6, 0x31, 0x32, 0x80, 0x9b,
+ 0x89, 0xa7, 0x30, 0x1f, 0x80, 0x88, 0x8a, 0xad,
+ 0x8f, 0x41, 0x94, 0x38, 0x87, 0x8f, 0x89, 0xb7,
+ 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00, 0x08, 0x30,
+ 0x07, 0x89, 0xaf, 0x20, 0x08, 0x27, 0x89, 0x41,
+ 0x48, 0x83, 0x60, 0x4b, 0x68, 0x89, 0xd5, 0x89,
+ 0xa5, 0x84, 0xba, 0x86, 0x98, 0x89, 0x43, 0xf4,
+ 0x00, 0xb6, 0x33, 0xd0, 0x80, 0x8a, 0x81, 0x60,
+ 0x4c, 0xaa, 0x81, 0x52, 0x60, 0xad, 0x81, 0x96,
+ 0x42, 0x1d, 0x22, 0x2f, 0x39, 0x86, 0x9d, 0x83,
+ 0x40, 0x93, 0x82, 0x45, 0x88, 0xb1, 0x41, 0xff,
+ 0xb6, 0x83, 0xb1, 0x38, 0x8d, 0x80, 0x95, 0x20,
+ 0x8e, 0x45, 0x4f, 0x30, 0x90, 0x0e, 0x01, 0x04,
+ 0x41, 0x04, 0x86, 0x88, 0x89, 0x41, 0x63, 0x80,
+ 0xbc, 0x8d, 0x45, 0xd5, 0x86, 0xec, 0x34, 0x89,
+ 0x52, 0x95, 0x89, 0x6c, 0x05, 0x05, 0x40, 0xef,
+};
+
+static const uint8_t unicode_prop_ID_Continue1_index[60] = {
+ 0xfa, 0x06, 0x00, 0x70, 0x09, 0x00, 0xf0, 0x0a,
+ 0x40, 0x57, 0x0c, 0x00, 0xf0, 0x0d, 0x40, 0xc7,
+ 0x0f, 0x00, 0xea, 0x17, 0x20, 0x45, 0x1b, 0x20,
+ 0x55, 0x20, 0x20, 0x0c, 0xa8, 0x60, 0x37, 0xaa,
+ 0x00, 0x50, 0xfe, 0x00, 0x3a, 0x0d, 0x01, 0x83,
+ 0x11, 0x01, 0xc4, 0x14, 0x21, 0x44, 0x19, 0x21,
+ 0x5a, 0x1d, 0x41, 0x9f, 0xbc, 0x61, 0xb0, 0xda,
+ 0x21, 0xf0, 0x01, 0x0e,
+};
+
+#ifdef CONFIG_ALL_UNICODE
+
+static const uint8_t unicode_cc_table[881] = {
+ 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00,
+ 0xe8, 0x00, 0xd8, 0x04, 0xdc, 0x01, 0xca, 0x03,
+ 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03,
+ 0xdc, 0xc7, 0x00, 0xf0, 0xc0, 0x02, 0xdc, 0xc2,
+ 0x01, 0xdc, 0x80, 0xc2, 0x03, 0xdc, 0xc0, 0x00,
+ 0xe8, 0x01, 0xdc, 0xc0, 0x41, 0xe9, 0x00, 0xea,
+ 0x41, 0xe9, 0x00, 0xea, 0x00, 0xe9, 0xcc, 0xb0,
+ 0xe2, 0xc4, 0xb0, 0xd8, 0x00, 0xdc, 0xc3, 0x00,
+ 0xdc, 0xc2, 0x00, 0xde, 0x00, 0xdc, 0xc5, 0x05,
+ 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xde, 0x00,
+ 0xe4, 0xc0, 0x49, 0x0a, 0x43, 0x13, 0x80, 0x00,
+ 0x17, 0x80, 0x41, 0x18, 0x80, 0xc0, 0x00, 0xdc,
+ 0x80, 0x00, 0x12, 0xb0, 0x17, 0xc7, 0x42, 0x1e,
+ 0xaf, 0x47, 0x1b, 0xc1, 0x01, 0xdc, 0xc4, 0x00,
+ 0xdc, 0xc1, 0x00, 0xdc, 0x8f, 0x00, 0x23, 0xb0,
+ 0x34, 0xc6, 0x81, 0xc3, 0x00, 0xdc, 0xc0, 0x81,
+ 0xc1, 0x80, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xa2,
+ 0x00, 0x24, 0x9d, 0xc0, 0x00, 0xdc, 0xc1, 0x00,
+ 0xdc, 0xc1, 0x02, 0xdc, 0xc0, 0x01, 0xdc, 0xc0,
+ 0x00, 0xdc, 0xc2, 0x00, 0xdc, 0xc0, 0x00, 0xdc,
+ 0xc0, 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xc1, 0xb0,
+ 0x6f, 0xc6, 0x00, 0xdc, 0xc0, 0x88, 0x00, 0xdc,
+ 0x97, 0xc3, 0x80, 0xc8, 0x80, 0xc2, 0x80, 0xc4,
+ 0xaa, 0x02, 0xdc, 0xb0, 0x0b, 0xc0, 0x02, 0xdc,
+ 0xc3, 0xa9, 0xc4, 0x04, 0xdc, 0xcd, 0x80, 0x00,
+ 0xdc, 0xc1, 0x00, 0xdc, 0xc1, 0x00, 0xdc, 0xc2,
+ 0x02, 0xdc, 0x42, 0x1b, 0xc2, 0x00, 0xdc, 0xc1,
+ 0x01, 0xdc, 0xc4, 0xb0, 0x0b, 0x00, 0x07, 0x8f,
+ 0x00, 0x09, 0x82, 0xc0, 0x00, 0xdc, 0xc1, 0xb0,
+ 0x36, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xaf, 0xc0,
+ 0xb0, 0x0c, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0,
+ 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3d,
+ 0x00, 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x4e, 0x00,
+ 0x09, 0xb0, 0x3d, 0x00, 0x07, 0x8f, 0x00, 0x09,
+ 0x86, 0x00, 0x54, 0x00, 0x5b, 0xb0, 0x34, 0x00,
+ 0x07, 0x8f, 0x00, 0x09, 0xb0, 0x3c, 0x01, 0x09,
+ 0x8f, 0x00, 0x09, 0xb0, 0x4b, 0x00, 0x09, 0xb0,
+ 0x3c, 0x01, 0x67, 0x00, 0x09, 0x8c, 0x03, 0x6b,
+ 0xb0, 0x3b, 0x01, 0x76, 0x00, 0x09, 0x8c, 0x03,
+ 0x7a, 0xb0, 0x1b, 0x01, 0xdc, 0x9a, 0x00, 0xdc,
+ 0x80, 0x00, 0xdc, 0x80, 0x00, 0xd8, 0xb0, 0x06,
+ 0x41, 0x81, 0x80, 0x00, 0x84, 0x84, 0x03, 0x82,
+ 0x81, 0x00, 0x82, 0x80, 0xc1, 0x00, 0x09, 0x80,
+ 0xc1, 0xb0, 0x0d, 0x00, 0xdc, 0xb0, 0x3f, 0x00,
+ 0x07, 0x80, 0x01, 0x09, 0xb0, 0x21, 0x00, 0xdc,
+ 0xb2, 0x9e, 0xc2, 0xb3, 0x83, 0x01, 0x09, 0x9d,
+ 0x00, 0x09, 0xb0, 0x6c, 0x00, 0x09, 0x89, 0xc0,
+ 0xb0, 0x9a, 0x00, 0xe4, 0xb0, 0x5e, 0x00, 0xde,
+ 0xc0, 0x00, 0xdc, 0xb0, 0xaa, 0xc0, 0x00, 0xdc,
+ 0xb0, 0x16, 0x00, 0x09, 0x93, 0xc7, 0x81, 0x00,
+ 0xdc, 0xaf, 0xc4, 0x05, 0xdc, 0xc1, 0x00, 0xdc,
+ 0x80, 0x01, 0xdc, 0xc1, 0x01, 0xdc, 0xc4, 0x00,
+ 0xdc, 0xc3, 0xb0, 0x34, 0x00, 0x07, 0x8e, 0x00,
+ 0x09, 0xa5, 0xc0, 0x00, 0xdc, 0xc6, 0xb0, 0x05,
+ 0x01, 0x09, 0xb0, 0x09, 0x00, 0x07, 0x8a, 0x01,
+ 0x09, 0xb0, 0x12, 0x00, 0x07, 0xb0, 0x67, 0xc2,
+ 0x41, 0x00, 0x04, 0xdc, 0xc1, 0x03, 0xdc, 0xc0,
+ 0x41, 0x00, 0x05, 0x01, 0x83, 0x00, 0xdc, 0x85,
+ 0xc0, 0x82, 0xc1, 0xb0, 0x95, 0xc1, 0x00, 0xdc,
+ 0xc6, 0x00, 0xdc, 0xc1, 0x00, 0xea, 0x00, 0xd6,
+ 0x00, 0xdc, 0x00, 0xca, 0xe4, 0x00, 0xe8, 0x01,
+ 0xe4, 0x00, 0xdc, 0x00, 0xda, 0xc0, 0x00, 0xe9,
+ 0x00, 0xdc, 0xc0, 0x00, 0xdc, 0xb2, 0x9f, 0xc1,
+ 0x01, 0x01, 0xc3, 0x02, 0x01, 0xc1, 0x83, 0xc0,
+ 0x82, 0x01, 0x01, 0xc0, 0x00, 0xdc, 0xc0, 0x01,
+ 0x01, 0x03, 0xdc, 0xc0, 0xb8, 0x03, 0xcd, 0xc2,
+ 0xb0, 0x5c, 0x00, 0x09, 0xb0, 0x2f, 0xdf, 0xb1,
+ 0xf9, 0x00, 0xda, 0x00, 0xe4, 0x00, 0xe8, 0x00,
+ 0xde, 0x01, 0xe0, 0xb0, 0x38, 0x01, 0x08, 0xb8,
+ 0x6d, 0xa3, 0xc0, 0x83, 0xc9, 0x9f, 0xc1, 0xb0,
+ 0x1f, 0xc1, 0xb0, 0xe3, 0x00, 0x09, 0xa4, 0x00,
+ 0x09, 0xb0, 0x66, 0x00, 0x09, 0x9a, 0xd1, 0xb0,
+ 0x08, 0x02, 0xdc, 0xa4, 0x00, 0x09, 0xb0, 0x2e,
+ 0x00, 0x07, 0x8b, 0x00, 0x09, 0xb0, 0xbe, 0xc0,
+ 0x80, 0xc1, 0x00, 0xdc, 0x81, 0xc1, 0x84, 0xc1,
+ 0x80, 0xc0, 0xb0, 0x03, 0x00, 0x09, 0xb0, 0xc5,
+ 0x00, 0x09, 0xb8, 0x46, 0xff, 0x00, 0x1a, 0xb2,
+ 0xd0, 0xc6, 0x06, 0xdc, 0xc1, 0xb3, 0x9c, 0x00,
+ 0xdc, 0xb0, 0xb1, 0x00, 0xdc, 0xb0, 0x64, 0xc4,
+ 0xb6, 0x61, 0x00, 0xdc, 0x80, 0xc0, 0xa7, 0xc0,
+ 0x00, 0x01, 0x00, 0xdc, 0x83, 0x00, 0x09, 0xb0,
+ 0x74, 0xc0, 0x00, 0xdc, 0xb2, 0x0c, 0xc3, 0xb1,
+ 0x52, 0xc1, 0xb0, 0x68, 0x01, 0xdc, 0xc2, 0x00,
+ 0xdc, 0xc0, 0x03, 0xdc, 0xb0, 0x00, 0xc0, 0x00,
+ 0xdc, 0xc0, 0x00, 0xdc, 0xb0, 0x8f, 0x00, 0x09,
+ 0xa8, 0x00, 0x09, 0x8d, 0x00, 0x09, 0xb0, 0x08,
+ 0x00, 0x09, 0x00, 0x07, 0xb0, 0x14, 0xc2, 0xaf,
+ 0x01, 0x09, 0xb0, 0x0d, 0x00, 0x07, 0xb0, 0x1b,
+ 0x00, 0x09, 0x88, 0x00, 0x07, 0xb0, 0x39, 0x00,
+ 0x09, 0x00, 0x07, 0xb0, 0x81, 0x00, 0x07, 0x00,
+ 0x09, 0xb0, 0x1f, 0x01, 0x07, 0x8f, 0x00, 0x09,
+ 0x97, 0xc6, 0x82, 0xc4, 0xb0, 0x9c, 0x00, 0x09,
+ 0x82, 0x00, 0x07, 0x96, 0xc0, 0xb0, 0x32, 0x00,
+ 0x09, 0x00, 0x07, 0xb0, 0xca, 0x00, 0x09, 0x00,
+ 0x07, 0xb0, 0x4d, 0x00, 0x09, 0xb0, 0x45, 0x00,
+ 0x09, 0x00, 0x07, 0xb0, 0x42, 0x00, 0x09, 0xb0,
+ 0xdc, 0x00, 0x09, 0x00, 0x07, 0xb0, 0xd1, 0x01,
+ 0x09, 0x83, 0x00, 0x07, 0xb0, 0x6b, 0x00, 0x09,
+ 0xb0, 0x22, 0x00, 0x09, 0x91, 0x00, 0x09, 0xb0,
+ 0x20, 0x00, 0x09, 0xb1, 0x74, 0x00, 0x09, 0xb0,
+ 0xd1, 0x00, 0x07, 0x80, 0x01, 0x09, 0xb0, 0x20,
+ 0x00, 0x09, 0xb8, 0x45, 0x27, 0x04, 0x01, 0xb0,
+ 0x0a, 0xc6, 0xb4, 0x88, 0x01, 0x06, 0xb8, 0x44,
+ 0x7b, 0x00, 0x01, 0xb8, 0x0c, 0x95, 0x01, 0xd8,
+ 0x02, 0x01, 0x82, 0x00, 0xe2, 0x04, 0xd8, 0x87,
+ 0x07, 0xdc, 0x81, 0xc4, 0x01, 0xdc, 0x9d, 0xc3,
+ 0xb0, 0x63, 0xc2, 0xb8, 0x05, 0x8a, 0xc6, 0x80,
+ 0xd0, 0x81, 0xc6, 0x80, 0xc1, 0x80, 0xc4, 0xb0,
+ 0xd4, 0xc6, 0xb1, 0x46, 0xc0, 0xb0, 0x0c, 0xc3,
+ 0xb5, 0xaf, 0x06, 0xdc, 0xb0, 0x3c, 0xc5, 0x00,
+ 0x07,
+};
+
+static const uint8_t unicode_cc_index[84] = {
+ 0x4d, 0x03, 0x00, 0x97, 0x05, 0x20, 0xc6, 0x05,
+ 0x00, 0xe7, 0x06, 0x00, 0x45, 0x07, 0x00, 0x9c,
+ 0x08, 0x00, 0x4d, 0x09, 0x00, 0x3c, 0x0b, 0x00,
+ 0x3d, 0x0d, 0x00, 0x36, 0x0f, 0x00, 0x38, 0x10,
+ 0x20, 0x3a, 0x19, 0x00, 0xcb, 0x1a, 0x20, 0xd3,
+ 0x1c, 0x00, 0xcf, 0x1d, 0x00, 0xe2, 0x20, 0x00,
+ 0x2e, 0x30, 0x20, 0x2b, 0xa9, 0x20, 0xed, 0xab,
+ 0x00, 0x39, 0x0a, 0x01, 0x84, 0x0f, 0x21, 0xc0,
+ 0x11, 0x01, 0x43, 0x14, 0x01, 0x39, 0x18, 0x21,
+ 0x42, 0x1d, 0x21, 0x67, 0xd1, 0x01, 0x30, 0xe1,
+ 0x21, 0x4b, 0xe9, 0x01,
+};
+
+static const uint32_t unicode_decomp_table1[693] = {
+ 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097,
+ 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097,
+ 0x002e4115, 0x002f0199, 0x00302016, 0x00400842,
+ 0x00448a42, 0x004a0442, 0x004c0096, 0x004c8117,
+ 0x004d0242, 0x004e4342, 0x004fc12f, 0x0050c342,
+ 0x005240bf, 0x00530342, 0x00550942, 0x005a0842,
+ 0x005e0096, 0x005e4342, 0x005fc081, 0x00680142,
+ 0x006bc142, 0x00710185, 0x0071c317, 0x00734844,
+ 0x00778344, 0x00798342, 0x007b02be, 0x007c4197,
+ 0x007d0142, 0x007e0444, 0x00800e42, 0x00878142,
+ 0x00898744, 0x00ac0483, 0x00b60317, 0x00b80283,
+ 0x00d00214, 0x00d10096, 0x00dd0080, 0x00de8097,
+ 0x00df8080, 0x00e10097, 0x00e1413e, 0x00e1c080,
+ 0x00e204be, 0x00ea83ae, 0x00f282ae, 0x00f401ad,
+ 0x00f4c12e, 0x00f54103, 0x00fc0303, 0x00fe4081,
+ 0x0100023e, 0x0101c0be, 0x010301be, 0x010640be,
+ 0x010e40be, 0x0114023e, 0x0115c0be, 0x011701be,
+ 0x011d8144, 0x01304144, 0x01340244, 0x01358144,
+ 0x01368344, 0x01388344, 0x013a8644, 0x013e0144,
+ 0x0161c085, 0x018882ae, 0x019d422f, 0x01b00184,
+ 0x01b4c084, 0x024a4084, 0x024c4084, 0x024d0084,
+ 0x0256042e, 0x0272c12e, 0x02770120, 0x0277c084,
+ 0x028cc084, 0x028d8084, 0x029641ae, 0x02978084,
+ 0x02d20084, 0x02d2c12e, 0x02d70120, 0x02e50084,
+ 0x02f281ae, 0x03120084, 0x03300084, 0x0331c122,
+ 0x0332812e, 0x035281ae, 0x03768084, 0x037701ae,
+ 0x038cc085, 0x03acc085, 0x03b7012f, 0x03c30081,
+ 0x03d0c084, 0x03d34084, 0x03d48084, 0x03d5c084,
+ 0x03d70084, 0x03da4084, 0x03dcc084, 0x03dd412e,
+ 0x03ddc085, 0x03de0084, 0x03de4085, 0x03e04084,
+ 0x03e4c084, 0x03e74084, 0x03e88084, 0x03e9c084,
+ 0x03eb0084, 0x03ee4084, 0x04098084, 0x043f0081,
+ 0x06c18484, 0x06c48084, 0x06cec184, 0x06d00120,
+ 0x06d0c084, 0x074b0383, 0x074cc41f, 0x074f1783,
+ 0x075e0081, 0x0766d283, 0x07801d44, 0x078e8942,
+ 0x07931844, 0x079f0d42, 0x07a58216, 0x07a68085,
+ 0x07a6c0be, 0x07a80d44, 0x07aea044, 0x07c00122,
+ 0x07c08344, 0x07c20122, 0x07c28344, 0x07c40122,
+ 0x07c48244, 0x07c60122, 0x07c68244, 0x07c8113e,
+ 0x07d08244, 0x07d20122, 0x07d28244, 0x07d40122,
+ 0x07d48344, 0x07d64c3e, 0x07dc4080, 0x07dc80be,
+ 0x07dcc080, 0x07dd00be, 0x07dd4080, 0x07dd80be,
+ 0x07ddc080, 0x07de00be, 0x07de4080, 0x07de80be,
+ 0x07dec080, 0x07df00be, 0x07df4080, 0x07e00820,
+ 0x07e40820, 0x07e80820, 0x07ec05be, 0x07eec080,
+ 0x07ef00be, 0x07ef4097, 0x07ef8080, 0x07efc117,
+ 0x07f0443e, 0x07f24080, 0x07f280be, 0x07f2c080,
+ 0x07f303be, 0x07f4c080, 0x07f582ae, 0x07f6c080,
+ 0x07f7433e, 0x07f8c080, 0x07f903ae, 0x07fac080,
+ 0x07fb013e, 0x07fb8102, 0x07fc83be, 0x07fe4080,
+ 0x07fe80be, 0x07fec080, 0x07ff00be, 0x07ff4080,
+ 0x07ff8097, 0x0800011e, 0x08008495, 0x08044081,
+ 0x0805c097, 0x08090081, 0x08094097, 0x08098099,
+ 0x080bc081, 0x080cc085, 0x080d00b1, 0x080d8085,
+ 0x080dc0b1, 0x080f0197, 0x0811c197, 0x0815c0b3,
+ 0x0817c081, 0x081c0595, 0x081ec081, 0x081f0215,
+ 0x0820051f, 0x08228583, 0x08254415, 0x082a0097,
+ 0x08400119, 0x08408081, 0x0840c0bf, 0x08414119,
+ 0x0841c081, 0x084240bf, 0x0842852d, 0x08454081,
+ 0x08458097, 0x08464295, 0x08480097, 0x08484099,
+ 0x08488097, 0x08490081, 0x08498080, 0x084a0081,
+ 0x084a8102, 0x084b0495, 0x084d421f, 0x084e4081,
+ 0x084ec099, 0x084f0283, 0x08514295, 0x08540119,
+ 0x0854809b, 0x0854c619, 0x0857c097, 0x08580081,
+ 0x08584097, 0x08588099, 0x0858c097, 0x08590081,
+ 0x08594097, 0x08598099, 0x0859c09b, 0x085a0097,
+ 0x085a4081, 0x085a8097, 0x085ac099, 0x085b0295,
+ 0x085c4097, 0x085c8099, 0x085cc097, 0x085d0081,
+ 0x085d4097, 0x085d8099, 0x085dc09b, 0x085e0097,
+ 0x085e4081, 0x085e8097, 0x085ec099, 0x085f0215,
+ 0x08624099, 0x0866813e, 0x086b80be, 0x087341be,
+ 0x088100be, 0x088240be, 0x088300be, 0x088901be,
+ 0x088b0085, 0x088b40b1, 0x088bc085, 0x088c00b1,
+ 0x089040be, 0x089100be, 0x0891c1be, 0x089801be,
+ 0x089b42be, 0x089d0144, 0x089e0144, 0x08a00144,
+ 0x08a10144, 0x08a20144, 0x08ab023e, 0x08b80244,
+ 0x08ba8220, 0x08ca411e, 0x0918049f, 0x091a4523,
+ 0x091cc097, 0x091d04a5, 0x091f452b, 0x0921c09b,
+ 0x092204a1, 0x09244525, 0x0926c099, 0x09270d25,
+ 0x092d8d1f, 0x09340d1f, 0x093a8081, 0x0a8300b3,
+ 0x0a9d0099, 0x0a9d4097, 0x0a9d8099, 0x0ab700be,
+ 0x0b1f0115, 0x0b5bc081, 0x0ba7c081, 0x0bbcc081,
+ 0x0bc004ad, 0x0bc244ad, 0x0bc484ad, 0x0bc6f383,
+ 0x0be0852d, 0x0be31d03, 0x0bf1882d, 0x0c000081,
+ 0x0c0d8283, 0x0c130b84, 0x0c194284, 0x0c1c0122,
+ 0x0c1cc122, 0x0c1d8122, 0x0c1e4122, 0x0c1f0122,
+ 0x0c250084, 0x0c26c123, 0x0c278084, 0x0c27c085,
+ 0x0c2b0b84, 0x0c314284, 0x0c340122, 0x0c34c122,
+ 0x0c358122, 0x0c364122, 0x0c370122, 0x0c3d0084,
+ 0x0c3dc220, 0x0c3f8084, 0x0c3fc085, 0x0c4c4a2d,
+ 0x0c51451f, 0x0c53ca9f, 0x0c5915ad, 0x0c648703,
+ 0x0c800741, 0x0c838089, 0x0c83c129, 0x0c8441a9,
+ 0x0c850089, 0x0c854129, 0x0c85c2a9, 0x0c870089,
+ 0x0c87408f, 0x0c87808d, 0x0c881241, 0x0c910203,
+ 0x0c940099, 0x0c9444a3, 0x0c968323, 0x0c98072d,
+ 0x0c9b84af, 0x0c9dc2a1, 0x0c9f00b5, 0x0c9f40b3,
+ 0x0c9f8085, 0x0ca01883, 0x0cac4223, 0x0cad4523,
+ 0x0cafc097, 0x0cb004a1, 0x0cb241a5, 0x0cb30097,
+ 0x0cb34099, 0x0cb38097, 0x0cb3c099, 0x0cb417ad,
+ 0x0cbfc085, 0x0cc001b3, 0x0cc0c0b1, 0x0cc100b3,
+ 0x0cc14131, 0x0cc1c0b5, 0x0cc200b3, 0x0cc241b1,
+ 0x0cc30133, 0x0cc38131, 0x0cc40085, 0x0cc440b1,
+ 0x0cc48133, 0x0cc50085, 0x0cc540b5, 0x0cc580b7,
+ 0x0cc5c0b5, 0x0cc600b1, 0x0cc64135, 0x0cc6c0b3,
+ 0x0cc701b1, 0x0cc7c0b3, 0x0cc800b5, 0x0cc840b3,
+ 0x0cc881b1, 0x0cc9422f, 0x0cca4131, 0x0ccac0b5,
+ 0x0ccb00b1, 0x0ccb40b3, 0x0ccb80b5, 0x0ccbc0b1,
+ 0x0ccc012f, 0x0ccc80b5, 0x0cccc0b3, 0x0ccd00b5,
+ 0x0ccd40b1, 0x0ccd80b5, 0x0ccdc085, 0x0cce02b1,
+ 0x0ccf40b3, 0x0ccf80b1, 0x0ccfc085, 0x0cd001b1,
+ 0x0cd0c0b3, 0x0cd101b1, 0x0cd1c0b5, 0x0cd200b3,
+ 0x0cd24085, 0x0cd280b5, 0x0cd2c085, 0x0cd30133,
+ 0x0cd381b1, 0x0cd440b3, 0x0cd48085, 0x0cd4c0b1,
+ 0x0cd500b3, 0x0cd54085, 0x0cd580b5, 0x0cd5c0b1,
+ 0x0cd60521, 0x0cd88525, 0x0cdb02a5, 0x0cdc4099,
+ 0x0cdc8117, 0x0cdd0099, 0x0cdd4197, 0x0cde0127,
+ 0x0cde8285, 0x0cdfc089, 0x0ce0043f, 0x0ce20099,
+ 0x0ce2409b, 0x0ce283bf, 0x0ce44219, 0x0ce54205,
+ 0x0ce6433f, 0x0ce7c131, 0x0ce84085, 0x0ce881b1,
+ 0x0ce94085, 0x0ce98107, 0x0cea0089, 0x0cea4097,
+ 0x0cea8219, 0x0ceb809d, 0x0cebc08d, 0x0cec083f,
+ 0x0cf00105, 0x0cf0809b, 0x0cf0c197, 0x0cf1809b,
+ 0x0cf1c099, 0x0cf20517, 0x0cf48099, 0x0cf4c117,
+ 0x0cf54119, 0x0cf5c097, 0x0cf6009b, 0x0cf64099,
+ 0x0cf68217, 0x0cf78119, 0x0cf804a1, 0x0cfa4525,
+ 0x0cfcc525, 0x0cff4125, 0x0cffc099, 0x29a70103,
+ 0x29dc0081, 0x29fc8195, 0x29fe0103, 0x2ad70203,
+ 0x2ada4081, 0x3e401482, 0x3e4a7f82, 0x3e6a3f82,
+ 0x3e8aa102, 0x3e9b0110, 0x3e9c2f82, 0x3eb3c590,
+ 0x3ec00197, 0x3ec0c119, 0x3ec1413f, 0x3ec4c2af,
+ 0x3ec74184, 0x3ec804ad, 0x3eca4081, 0x3eca8304,
+ 0x3ecc03a0, 0x3ece02a0, 0x3ecf8084, 0x3ed00120,
+ 0x3ed0c120, 0x3ed184ae, 0x3ed3c085, 0x3ed4312d,
+ 0x3ef4cbad, 0x3efa892f, 0x3eff022d, 0x3f002f2f,
+ 0x3f1782a5, 0x3f18c0b1, 0x3f1907af, 0x3f1cffaf,
+ 0x3f3c81a5, 0x3f3d64af, 0x3f542031, 0x3f649b31,
+ 0x3f7c0131, 0x3f7c83b3, 0x3f7e40b1, 0x3f7e80bd,
+ 0x3f7ec0bb, 0x3f7f00b3, 0x3f840503, 0x3f8c01ad,
+ 0x3f8cc315, 0x3f8e462d, 0x3f91cc03, 0x3f97c695,
+ 0x3f9c01af, 0x3f9d0085, 0x3f9d852f, 0x3fa03aad,
+ 0x3fbd442f, 0x3fc06f1f, 0x3fd7c11f, 0x3fd85fad,
+ 0x3fe80081, 0x3fe84f1f, 0x3ff0831f, 0x3ff2831f,
+ 0x3ff4831f, 0x3ff6819f, 0x3ff80783, 0x41e04d83,
+ 0x41e70f91, 0x44268192, 0x442ac092, 0x444b8112,
+ 0x44d2c112, 0x452ec212, 0x456e8112, 0x464e0092,
+ 0x74578392, 0x746ec312, 0x75000d1f, 0x75068d1f,
+ 0x750d0d1f, 0x7513839f, 0x7515891f, 0x751a0d1f,
+ 0x75208d1f, 0x75271015, 0x752f439f, 0x7531459f,
+ 0x75340d1f, 0x753a8d1f, 0x75410395, 0x7543441f,
+ 0x7545839f, 0x75478d1f, 0x754e0795, 0x7552839f,
+ 0x75548d1f, 0x755b0d1f, 0x75618d1f, 0x75680d1f,
+ 0x756e8d1f, 0x75750d1f, 0x757b8d1f, 0x75820d1f,
+ 0x75888d1f, 0x758f0d1f, 0x75958d1f, 0x759c0d1f,
+ 0x75a28d1f, 0x75a90103, 0x75aa089f, 0x75ae4081,
+ 0x75ae839f, 0x75b04081, 0x75b08c9f, 0x75b6c081,
+ 0x75b7032d, 0x75b8889f, 0x75bcc081, 0x75bd039f,
+ 0x75bec081, 0x75bf0c9f, 0x75c54081, 0x75c5832d,
+ 0x75c7089f, 0x75cb4081, 0x75cb839f, 0x75cd4081,
+ 0x75cd8c9f, 0x75d3c081, 0x75d4032d, 0x75d5889f,
+ 0x75d9c081, 0x75da039f, 0x75dbc081, 0x75dc0c9f,
+ 0x75e24081, 0x75e2832d, 0x75e4089f, 0x75e84081,
+ 0x75e8839f, 0x75ea4081, 0x75ea8c9f, 0x75f0c081,
+ 0x75f1042d, 0x75f3851f, 0x75f6051f, 0x75f8851f,
+ 0x75fb051f, 0x75fd851f, 0x7b80022d, 0x7b814dad,
+ 0x7b884203, 0x7b89c081, 0x7b8a452d, 0x7b8d0403,
+ 0x7b908081, 0x7b91dc03, 0x7ba0052d, 0x7ba2c8ad,
+ 0x7ba84483, 0x7baac8ad, 0x7c400097, 0x7c404521,
+ 0x7c440d25, 0x7c4a8087, 0x7c4ac115, 0x7c4b4117,
+ 0x7c4c0d1f, 0x7c528217, 0x7c538099, 0x7c53c097,
+ 0x7c5a8197, 0x7c640097, 0x7c80012f, 0x7c808081,
+ 0x7c841603, 0x7c9004c1, 0x7c940103, 0x7efc051f,
+ 0xbe0001ac, 0xbe00d110, 0xbe0947ac, 0xbe0d3910,
+ 0xbe29872c, 0xbe2d022c, 0xbe2e3790, 0xbe49ff90,
+ 0xbe69bc10,
+};
+
+static const uint16_t unicode_decomp_table2[693] = {
+ 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008,
+ 0x000a, 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3,
+ 0x00c7, 0x00cb, 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8,
+ 0x0108, 0x010a, 0x0073, 0x0110, 0x0112, 0x0114, 0x0120, 0x012c,
+ 0x0144, 0x014d, 0x0153, 0x0162, 0x0168, 0x016a, 0x0176, 0x0192,
+ 0x0194, 0x01a9, 0x01bb, 0x01c7, 0x01d1, 0x01d5, 0x02b9, 0x01d7,
+ 0x003b, 0x01d9, 0x01db, 0x00b7, 0x01e1, 0x01fc, 0x020c, 0x0218,
+ 0x021d, 0x0223, 0x0227, 0x03a3, 0x0233, 0x023f, 0x0242, 0x024b,
+ 0x024e, 0x0251, 0x025d, 0x0260, 0x0269, 0x026c, 0x026f, 0x0275,
+ 0x0278, 0x0281, 0x028a, 0x029c, 0x029f, 0x02a3, 0x02af, 0x02b9,
+ 0x02c5, 0x02c9, 0x02cd, 0x02d1, 0x02d5, 0x02e7, 0x02ed, 0x02f1,
+ 0x02f5, 0x02f9, 0x02fd, 0x0305, 0x0309, 0x030d, 0x0313, 0x0317,
+ 0x031b, 0x0323, 0x0327, 0x032b, 0x032f, 0x0335, 0x033d, 0x0341,
+ 0x0349, 0x034d, 0x0351, 0x0f0b, 0x0357, 0x035b, 0x035f, 0x0363,
+ 0x0367, 0x036b, 0x036f, 0x0373, 0x0379, 0x037d, 0x0381, 0x0385,
+ 0x0389, 0x038d, 0x0391, 0x0395, 0x0399, 0x039d, 0x03a1, 0x10dc,
+ 0x03a5, 0x03c9, 0x03cd, 0x03d9, 0x03dd, 0x03e1, 0x03ef, 0x03f1,
+ 0x043d, 0x044f, 0x0499, 0x04f0, 0x0502, 0x054a, 0x0564, 0x056c,
+ 0x0570, 0x0573, 0x059a, 0x05fa, 0x05fe, 0x0607, 0x060b, 0x0614,
+ 0x0618, 0x061e, 0x0622, 0x0628, 0x068e, 0x0694, 0x0698, 0x069e,
+ 0x06a2, 0x06ab, 0x03ac, 0x06f3, 0x03ad, 0x06f6, 0x03ae, 0x06f9,
+ 0x03af, 0x06fc, 0x03cc, 0x06ff, 0x03cd, 0x0702, 0x03ce, 0x0705,
+ 0x0709, 0x070d, 0x0711, 0x0386, 0x0732, 0x0735, 0x03b9, 0x0737,
+ 0x073b, 0x0388, 0x0753, 0x0389, 0x0756, 0x0390, 0x076b, 0x038a,
+ 0x0777, 0x03b0, 0x0789, 0x038e, 0x0799, 0x079f, 0x07a3, 0x038c,
+ 0x07b8, 0x038f, 0x07bb, 0x00b4, 0x07be, 0x07c0, 0x07c2, 0x2010,
+ 0x07cb, 0x002e, 0x07cd, 0x07cf, 0x0020, 0x07d2, 0x07d6, 0x07db,
+ 0x07df, 0x07e4, 0x07ea, 0x07f0, 0x0020, 0x07f6, 0x2212, 0x0801,
+ 0x0805, 0x0807, 0x081d, 0x0825, 0x0827, 0x0043, 0x082d, 0x0830,
+ 0x0190, 0x0836, 0x0839, 0x004e, 0x0845, 0x0847, 0x084c, 0x084e,
+ 0x0851, 0x005a, 0x03a9, 0x005a, 0x0853, 0x0857, 0x0860, 0x0069,
+ 0x0862, 0x0865, 0x086f, 0x0874, 0x087a, 0x087e, 0x08a2, 0x0049,
+ 0x08a4, 0x08a6, 0x08a9, 0x0056, 0x08ab, 0x08ad, 0x08b0, 0x08b4,
+ 0x0058, 0x08b6, 0x08b8, 0x08bb, 0x08c0, 0x08c2, 0x08c5, 0x0076,
+ 0x08c7, 0x08c9, 0x08cc, 0x08d0, 0x0078, 0x08d2, 0x08d4, 0x08d7,
+ 0x08db, 0x08de, 0x08e4, 0x08e7, 0x08f0, 0x08f3, 0x08f6, 0x08f9,
+ 0x0902, 0x0906, 0x090b, 0x090f, 0x0914, 0x0917, 0x091a, 0x0923,
+ 0x092c, 0x093b, 0x093e, 0x0941, 0x0944, 0x0947, 0x094a, 0x0956,
+ 0x095c, 0x0960, 0x0962, 0x0964, 0x0968, 0x096a, 0x0970, 0x0978,
+ 0x097c, 0x0980, 0x0986, 0x0989, 0x098f, 0x0991, 0x0030, 0x0993,
+ 0x0999, 0x099c, 0x099e, 0x09a1, 0x09a4, 0x2d61, 0x6bcd, 0x9f9f,
+ 0x09a6, 0x09b1, 0x09bc, 0x09c7, 0x0a95, 0x0aa1, 0x0b15, 0x0020,
+ 0x0b27, 0x0b31, 0x0b8d, 0x0ba1, 0x0ba5, 0x0ba9, 0x0bad, 0x0bb1,
+ 0x0bb5, 0x0bb9, 0x0bbd, 0x0bc1, 0x0bc5, 0x0c21, 0x0c35, 0x0c39,
+ 0x0c3d, 0x0c41, 0x0c45, 0x0c49, 0x0c4d, 0x0c51, 0x0c55, 0x0c59,
+ 0x0c6f, 0x0c71, 0x0c73, 0x0ca0, 0x0cbc, 0x0cdc, 0x0ce4, 0x0cec,
+ 0x0cf4, 0x0cfc, 0x0d04, 0x0d0c, 0x0d14, 0x0d22, 0x0d2e, 0x0d7a,
+ 0x0d82, 0x0d85, 0x0d89, 0x0d8d, 0x0d9d, 0x0db1, 0x0db5, 0x0dbc,
+ 0x0dc2, 0x0dc6, 0x0e28, 0x0e2c, 0x0e30, 0x0e32, 0x0e36, 0x0e3c,
+ 0x0e3e, 0x0e41, 0x0e43, 0x0e46, 0x0e77, 0x0e7b, 0x0e89, 0x0e8e,
+ 0x0e94, 0x0e9c, 0x0ea3, 0x0ea9, 0x0eb4, 0x0ebe, 0x0ec6, 0x0eca,
+ 0x0ecf, 0x0ed9, 0x0edd, 0x0ee4, 0x0eec, 0x0ef3, 0x0ef8, 0x0f04,
+ 0x0f0a, 0x0f15, 0x0f1b, 0x0f22, 0x0f28, 0x0f33, 0x0f3d, 0x0f45,
+ 0x0f4c, 0x0f51, 0x0f57, 0x0f5e, 0x0f63, 0x0f69, 0x0f70, 0x0f76,
+ 0x0f7d, 0x0f82, 0x0f89, 0x0f8d, 0x0f9e, 0x0fa4, 0x0fa9, 0x0fad,
+ 0x0fb8, 0x0fbe, 0x0fc9, 0x0fd0, 0x0fd6, 0x0fda, 0x0fe1, 0x0fe5,
+ 0x0fef, 0x0ffa, 0x1000, 0x1004, 0x1009, 0x100f, 0x1013, 0x101a,
+ 0x101f, 0x1023, 0x1029, 0x102f, 0x1032, 0x1036, 0x1039, 0x103f,
+ 0x1045, 0x1059, 0x1061, 0x1079, 0x107c, 0x1080, 0x1095, 0x10a1,
+ 0x10b1, 0x10c3, 0x10cb, 0x10cf, 0x10da, 0x10de, 0x10ea, 0x10f2,
+ 0x10f4, 0x1100, 0x1105, 0x1111, 0x1141, 0x1149, 0x114d, 0x1153,
+ 0x1157, 0x115a, 0x116e, 0x1171, 0x1175, 0x117b, 0x117d, 0x1181,
+ 0x1184, 0x118c, 0x1192, 0x1196, 0x119c, 0x11a2, 0x11a8, 0x11ab,
+ 0xa76f, 0x11af, 0x11b2, 0x11b6, 0x028d, 0x11be, 0x1210, 0x130e,
+ 0x140c, 0x1490, 0x1495, 0x1553, 0x156c, 0x1572, 0x1578, 0x157e,
+ 0x158a, 0x1596, 0x002b, 0x15a1, 0x15b9, 0x15bd, 0x15c1, 0x15c5,
+ 0x15c9, 0x15cd, 0x15e1, 0x15e5, 0x1649, 0x1662, 0x1688, 0x168e,
+ 0x174c, 0x1752, 0x1757, 0x1777, 0x1877, 0x187d, 0x1911, 0x19d3,
+ 0x1a77, 0x1a7f, 0x1a9d, 0x1aa2, 0x1ab6, 0x1ac0, 0x1ac6, 0x1ada,
+ 0x1adf, 0x1ae5, 0x1af3, 0x1b23, 0x1b30, 0x1b38, 0x1b3c, 0x1b52,
+ 0x1bc9, 0x1bdb, 0x1bdd, 0x1bdf, 0x3164, 0x1c20, 0x1c22, 0x1c24,
+ 0x1c26, 0x1c28, 0x1c2a, 0x1c48, 0x1c7e, 0x1cc4, 0x1cd2, 0x1cd7,
+ 0x1ce0, 0x1ce9, 0x1cfb, 0x1d04, 0x1d09, 0x1d29, 0x1d44, 0x1d46,
+ 0x1d48, 0x1d4a, 0x1d4c, 0x1d4e, 0x1d50, 0x1d52, 0x1d72, 0x1d74,
+ 0x1d76, 0x1d78, 0x1d7a, 0x1d81, 0x1d83, 0x1d85, 0x1d87, 0x1d96,
+ 0x1d98, 0x1d9a, 0x1d9c, 0x1d9e, 0x1da0, 0x1da2, 0x1da4, 0x1da6,
+ 0x1da8, 0x1daa, 0x1dac, 0x1dae, 0x1db0, 0x1db2, 0x1db6, 0x03f4,
+ 0x1db8, 0x2207, 0x1dba, 0x2202, 0x1dbc, 0x1dc4, 0x03f4, 0x1dc6,
+ 0x2207, 0x1dc8, 0x2202, 0x1dca, 0x1dd2, 0x03f4, 0x1dd4, 0x2207,
+ 0x1dd6, 0x2202, 0x1dd8, 0x1de0, 0x03f4, 0x1de2, 0x2207, 0x1de4,
+ 0x2202, 0x1de6, 0x1dee, 0x03f4, 0x1df0, 0x2207, 0x1df2, 0x2202,
+ 0x1df4, 0x1dfe, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0e,
+ 0x1e2b, 0x062d, 0x1e33, 0x1e3f, 0x062c, 0x1e4f, 0x1ebf, 0x1ecb,
+ 0x1ede, 0x1ef0, 0x1f03, 0x1f05, 0x1f09, 0x1f0f, 0x1f15, 0x1f17,
+ 0x1f1b, 0x1f1d, 0x1f25, 0x1f28, 0x1f2a, 0x1f30, 0x1f32, 0x30b5,
+ 0x1f38, 0x1f90, 0x1fa6, 0x1faa, 0x1fac, 0x1fb1, 0x1ffe, 0x200f,
+ 0x2110, 0x2120, 0x2126, 0x2220, 0x233e,
+};
+
+static const uint8_t unicode_decomp_data[9292] = {
+ 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81,
+ 0x20, 0xa7, 0x31, 0x6f, 0x31, 0xd0, 0x34, 0x31,
+ 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41,
+ 0x81, 0x41, 0x82, 0x41, 0x83, 0x41, 0x88, 0x41,
+ 0x8a, 0x00, 0x00, 0x43, 0xa7, 0x45, 0x80, 0x45,
+ 0x81, 0x45, 0x82, 0x45, 0x88, 0x49, 0x80, 0x49,
+ 0x81, 0x49, 0x82, 0x49, 0x88, 0x00, 0x00, 0x4e,
+ 0x83, 0x4f, 0x80, 0x4f, 0x81, 0x4f, 0x82, 0x4f,
+ 0x83, 0x4f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x55,
+ 0x80, 0x55, 0x81, 0x55, 0x82, 0x55, 0x88, 0x59,
+ 0x81, 0x00, 0x00, 0x00, 0x00, 0x61, 0x80, 0x61,
+ 0x81, 0x61, 0x82, 0x61, 0x83, 0x61, 0x88, 0x61,
+ 0x8a, 0x00, 0x00, 0x63, 0xa7, 0x65, 0x80, 0x65,
+ 0x81, 0x65, 0x82, 0x65, 0x88, 0x69, 0x80, 0x69,
+ 0x81, 0x69, 0x82, 0x69, 0x88, 0x00, 0x00, 0x6e,
+ 0x83, 0x6f, 0x80, 0x6f, 0x81, 0x6f, 0x82, 0x6f,
+ 0x83, 0x6f, 0x88, 0x00, 0x00, 0x00, 0x00, 0x75,
+ 0x80, 0x75, 0x81, 0x75, 0x82, 0x75, 0x88, 0x79,
+ 0x81, 0x00, 0x00, 0x79, 0x88, 0x41, 0x84, 0x41,
+ 0x86, 0x41, 0xa8, 0x43, 0x81, 0x43, 0x82, 0x43,
+ 0x87, 0x43, 0x8c, 0x44, 0x8c, 0x45, 0x84, 0x45,
+ 0x86, 0x45, 0x87, 0x45, 0xa8, 0x45, 0x8c, 0x47,
+ 0x82, 0x47, 0x86, 0x47, 0x87, 0x47, 0xa7, 0x48,
+ 0x82, 0x49, 0x83, 0x49, 0x84, 0x49, 0x86, 0x49,
+ 0xa8, 0x49, 0x87, 0x49, 0x4a, 0x69, 0x6a, 0x4a,
+ 0x82, 0x4b, 0xa7, 0x4c, 0x81, 0x4c, 0xa7, 0x4c,
+ 0x8c, 0x4c, 0x00, 0x00, 0x6b, 0x20, 0x6b, 0x4e,
+ 0x81, 0x4e, 0xa7, 0x4e, 0x8c, 0xbc, 0x02, 0x6e,
+ 0x4f, 0x84, 0x4f, 0x86, 0x4f, 0x8b, 0x52, 0x81,
+ 0x52, 0xa7, 0x52, 0x8c, 0x53, 0x81, 0x53, 0x82,
+ 0x53, 0xa7, 0x53, 0x8c, 0x54, 0xa7, 0x54, 0x8c,
+ 0x55, 0x83, 0x55, 0x84, 0x55, 0x86, 0x55, 0x8a,
+ 0x55, 0x8b, 0x55, 0xa8, 0x57, 0x82, 0x59, 0x82,
+ 0x59, 0x88, 0x5a, 0x81, 0x5a, 0x87, 0x5a, 0x8c,
+ 0x4f, 0x9b, 0x55, 0x9b, 0x44, 0x00, 0x7d, 0x01,
+ 0x44, 0x00, 0x7e, 0x01, 0x64, 0x00, 0x7e, 0x01,
+ 0x4c, 0x4a, 0x4c, 0x6a, 0x6c, 0x6a, 0x4e, 0x4a,
+ 0x4e, 0x6a, 0x6e, 0x6a, 0x41, 0x00, 0x8c, 0x49,
+ 0x00, 0x8c, 0x4f, 0x00, 0x8c, 0x55, 0x00, 0x8c,
+ 0xdc, 0x00, 0x84, 0xdc, 0x00, 0x81, 0xdc, 0x00,
+ 0x8c, 0xdc, 0x00, 0x80, 0xc4, 0x00, 0x84, 0x26,
+ 0x02, 0x84, 0xc6, 0x00, 0x84, 0x47, 0x8c, 0x4b,
+ 0x8c, 0x4f, 0xa8, 0xea, 0x01, 0x84, 0xeb, 0x01,
+ 0x84, 0xb7, 0x01, 0x8c, 0x92, 0x02, 0x8c, 0x6a,
+ 0x00, 0x8c, 0x44, 0x5a, 0x44, 0x7a, 0x64, 0x7a,
+ 0x47, 0x81, 0x4e, 0x00, 0x80, 0xc5, 0x00, 0x81,
+ 0xc6, 0x00, 0x81, 0xd8, 0x00, 0x81, 0x41, 0x8f,
+ 0x41, 0x91, 0x45, 0x8f, 0x45, 0x91, 0x49, 0x8f,
+ 0x49, 0x91, 0x4f, 0x8f, 0x4f, 0x91, 0x52, 0x8f,
+ 0x52, 0x91, 0x55, 0x8f, 0x55, 0x91, 0x53, 0xa6,
+ 0x54, 0xa6, 0x48, 0x8c, 0x41, 0x00, 0x87, 0x45,
+ 0x00, 0xa7, 0xd6, 0x00, 0x84, 0xd5, 0x00, 0x84,
+ 0x4f, 0x00, 0x87, 0x2e, 0x02, 0x84, 0x59, 0x00,
+ 0x84, 0x68, 0x00, 0x66, 0x02, 0x6a, 0x00, 0x72,
+ 0x00, 0x79, 0x02, 0x7b, 0x02, 0x81, 0x02, 0x77,
+ 0x00, 0x79, 0x00, 0x20, 0x86, 0x20, 0x87, 0x20,
+ 0x8a, 0x20, 0xa8, 0x20, 0x83, 0x20, 0x8b, 0x63,
+ 0x02, 0x6c, 0x00, 0x73, 0x00, 0x78, 0x00, 0x95,
+ 0x02, 0x80, 0x81, 0x00, 0x93, 0x88, 0x81, 0x20,
+ 0xc5, 0x20, 0x81, 0xa8, 0x00, 0x81, 0x91, 0x03,
+ 0x81, 0x95, 0x03, 0x81, 0x97, 0x03, 0x81, 0x99,
+ 0x03, 0x81, 0x00, 0x00, 0x00, 0x9f, 0x03, 0x81,
+ 0x00, 0x00, 0x00, 0xa5, 0x03, 0x81, 0xa9, 0x03,
+ 0x81, 0xca, 0x03, 0x81, 0x01, 0x03, 0x98, 0x07,
+ 0xa4, 0x07, 0xb0, 0x00, 0xb4, 0x00, 0xb6, 0x00,
+ 0xb8, 0x00, 0xca, 0x00, 0x01, 0x03, 0xb8, 0x07,
+ 0xc4, 0x07, 0xbe, 0x00, 0xc4, 0x00, 0xc8, 0x00,
+ 0xa5, 0x03, 0x0d, 0x13, 0x00, 0x01, 0x03, 0xd1,
+ 0x00, 0xd1, 0x07, 0xc6, 0x03, 0xc0, 0x03, 0xba,
+ 0x03, 0xc1, 0x03, 0xc2, 0x03, 0x00, 0x00, 0x98,
+ 0x03, 0xb5, 0x03, 0x15, 0x04, 0x80, 0x15, 0x04,
+ 0x88, 0x00, 0x00, 0x00, 0x13, 0x04, 0x81, 0x06,
+ 0x04, 0x88, 0x1a, 0x04, 0x81, 0x18, 0x04, 0x80,
+ 0x23, 0x04, 0x86, 0x18, 0x04, 0x86, 0x38, 0x04,
+ 0x86, 0x35, 0x04, 0x80, 0x35, 0x04, 0x88, 0x00,
+ 0x00, 0x00, 0x33, 0x04, 0x81, 0x56, 0x04, 0x88,
+ 0x3a, 0x04, 0x81, 0x38, 0x04, 0x80, 0x43, 0x04,
+ 0x86, 0x74, 0x04, 0x8f, 0x16, 0x04, 0x86, 0x10,
+ 0x04, 0x86, 0x10, 0x04, 0x88, 0x15, 0x04, 0x86,
+ 0xd8, 0x04, 0x88, 0x16, 0x04, 0x88, 0x17, 0x04,
+ 0x88, 0x18, 0x04, 0x84, 0x18, 0x04, 0x88, 0x1e,
+ 0x04, 0x88, 0xe8, 0x04, 0x88, 0x2d, 0x04, 0x88,
+ 0x23, 0x04, 0x84, 0x23, 0x04, 0x88, 0x23, 0x04,
+ 0x8b, 0x27, 0x04, 0x88, 0x2b, 0x04, 0x88, 0x65,
+ 0x05, 0x82, 0x05, 0x27, 0x06, 0x00, 0x2c, 0x00,
+ 0x2d, 0x21, 0x2d, 0x00, 0x2e, 0x23, 0x2d, 0x27,
+ 0x06, 0x00, 0x4d, 0x21, 0x4d, 0xa0, 0x4d, 0x23,
+ 0x4d, 0xd5, 0x06, 0x54, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0xc1, 0x06, 0x54, 0x06, 0xd2, 0x06, 0x54,
+ 0x06, 0x28, 0x09, 0x3c, 0x09, 0x30, 0x09, 0x3c,
+ 0x09, 0x33, 0x09, 0x3c, 0x09, 0x15, 0x09, 0x00,
+ 0x27, 0x01, 0x27, 0x02, 0x27, 0x07, 0x27, 0x0c,
+ 0x27, 0x0d, 0x27, 0x16, 0x27, 0x1a, 0x27, 0xbe,
+ 0x09, 0x09, 0x00, 0x09, 0x19, 0xa1, 0x09, 0xbc,
+ 0x09, 0xaf, 0x09, 0xbc, 0x09, 0x32, 0x0a, 0x3c,
+ 0x0a, 0x38, 0x0a, 0x3c, 0x0a, 0x16, 0x0a, 0x00,
+ 0x26, 0x01, 0x26, 0x06, 0x26, 0x2b, 0x0a, 0x3c,
+ 0x0a, 0x47, 0x0b, 0x56, 0x0b, 0x3e, 0x0b, 0x09,
+ 0x00, 0x09, 0x19, 0x21, 0x0b, 0x3c, 0x0b, 0x92,
+ 0x0b, 0xd7, 0x0b, 0xbe, 0x0b, 0x08, 0x00, 0x09,
+ 0x00, 0x08, 0x19, 0x46, 0x0c, 0x56, 0x0c, 0xbf,
+ 0x0c, 0xd5, 0x0c, 0xc6, 0x0c, 0xd5, 0x0c, 0xc2,
+ 0x0c, 0x04, 0x00, 0x08, 0x13, 0x3e, 0x0d, 0x08,
+ 0x00, 0x09, 0x00, 0x08, 0x19, 0xd9, 0x0d, 0xca,
+ 0x0d, 0xca, 0x0d, 0x0f, 0x05, 0x12, 0x00, 0x0f,
+ 0x15, 0x4d, 0x0e, 0x32, 0x0e, 0xcd, 0x0e, 0xb2,
+ 0x0e, 0x99, 0x0e, 0x12, 0x00, 0x12, 0x08, 0x42,
+ 0x0f, 0xb7, 0x0f, 0x4c, 0x0f, 0xb7, 0x0f, 0x51,
+ 0x0f, 0xb7, 0x0f, 0x56, 0x0f, 0xb7, 0x0f, 0x5b,
+ 0x0f, 0xb7, 0x0f, 0x40, 0x0f, 0xb5, 0x0f, 0x71,
+ 0x0f, 0x72, 0x0f, 0x71, 0x0f, 0x00, 0x03, 0x41,
+ 0x0f, 0xb2, 0x0f, 0x81, 0x0f, 0xb3, 0x0f, 0x80,
+ 0x0f, 0xb3, 0x0f, 0x81, 0x0f, 0x71, 0x0f, 0x80,
+ 0x0f, 0x92, 0x0f, 0xb7, 0x0f, 0x9c, 0x0f, 0xb7,
+ 0x0f, 0xa1, 0x0f, 0xb7, 0x0f, 0xa6, 0x0f, 0xb7,
+ 0x0f, 0xab, 0x0f, 0xb7, 0x0f, 0x90, 0x0f, 0xb5,
+ 0x0f, 0x25, 0x10, 0x2e, 0x10, 0x05, 0x1b, 0x35,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x07, 0x1b, 0x35,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x09, 0x1b, 0x35,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x35,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x1b, 0x35,
+ 0x1b, 0x11, 0x1b, 0x35, 0x1b, 0x3a, 0x1b, 0x35,
+ 0x1b, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x1b, 0x35,
+ 0x1b, 0x3e, 0x1b, 0x35, 0x1b, 0x42, 0x1b, 0x35,
+ 0x1b, 0x41, 0x00, 0xc6, 0x00, 0x42, 0x00, 0x00,
+ 0x00, 0x44, 0x00, 0x45, 0x00, 0x8e, 0x01, 0x47,
+ 0x00, 0x4f, 0x00, 0x22, 0x02, 0x50, 0x00, 0x52,
+ 0x00, 0x54, 0x00, 0x55, 0x00, 0x57, 0x00, 0x61,
+ 0x00, 0x50, 0x02, 0x51, 0x02, 0x02, 0x1d, 0x62,
+ 0x00, 0x64, 0x00, 0x65, 0x00, 0x59, 0x02, 0x5b,
+ 0x02, 0x5c, 0x02, 0x67, 0x00, 0x00, 0x00, 0x6b,
+ 0x00, 0x6d, 0x00, 0x4b, 0x01, 0x6f, 0x00, 0x54,
+ 0x02, 0x16, 0x1d, 0x17, 0x1d, 0x70, 0x00, 0x74,
+ 0x00, 0x75, 0x00, 0x1d, 0x1d, 0x6f, 0x02, 0x76,
+ 0x00, 0x25, 0x1d, 0xb2, 0x03, 0xb3, 0x03, 0xb4,
+ 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x69, 0x00, 0x72,
+ 0x00, 0x75, 0x00, 0x76, 0x00, 0xb2, 0x03, 0xb3,
+ 0x03, 0xc1, 0x03, 0xc6, 0x03, 0xc7, 0x03, 0x52,
+ 0x02, 0x63, 0x00, 0x55, 0x02, 0xf0, 0x00, 0x5c,
+ 0x02, 0x66, 0x00, 0x5f, 0x02, 0x61, 0x02, 0x65,
+ 0x02, 0x68, 0x02, 0x69, 0x02, 0x6a, 0x02, 0x7b,
+ 0x1d, 0x9d, 0x02, 0x6d, 0x02, 0x85, 0x1d, 0x9f,
+ 0x02, 0x71, 0x02, 0x70, 0x02, 0x72, 0x02, 0x73,
+ 0x02, 0x74, 0x02, 0x75, 0x02, 0x78, 0x02, 0x82,
+ 0x02, 0x83, 0x02, 0xab, 0x01, 0x89, 0x02, 0x8a,
+ 0x02, 0x1c, 0x1d, 0x8b, 0x02, 0x8c, 0x02, 0x7a,
+ 0x00, 0x90, 0x02, 0x91, 0x02, 0x92, 0x02, 0xb8,
+ 0x03, 0x41, 0x00, 0xa5, 0x42, 0x00, 0x87, 0x42,
+ 0x00, 0xa3, 0x42, 0x00, 0xb1, 0xc7, 0x00, 0x81,
+ 0x44, 0x00, 0x87, 0x44, 0x00, 0xa3, 0x44, 0x00,
+ 0xb1, 0x44, 0x00, 0xa7, 0x44, 0x00, 0xad, 0x12,
+ 0x01, 0x80, 0x12, 0x01, 0x81, 0x45, 0x00, 0xad,
+ 0x45, 0x00, 0xb0, 0x28, 0x02, 0x86, 0x46, 0x00,
+ 0x87, 0x47, 0x00, 0x84, 0x48, 0x00, 0x87, 0x48,
+ 0x00, 0xa3, 0x48, 0x00, 0x88, 0x48, 0x00, 0xa7,
+ 0x48, 0x00, 0xae, 0x49, 0x00, 0xb0, 0xcf, 0x00,
+ 0x81, 0x4b, 0x00, 0x81, 0x4b, 0x00, 0xa3, 0x4b,
+ 0x00, 0xb1, 0x4c, 0x00, 0xa3, 0x36, 0x1e, 0x84,
+ 0x4c, 0xb1, 0x4c, 0xad, 0x4d, 0x81, 0x4d, 0x87,
+ 0x4d, 0xa3, 0x4e, 0x87, 0x4e, 0xa3, 0x4e, 0xb1,
+ 0x4e, 0xad, 0xd5, 0x00, 0x81, 0xd5, 0x00, 0x88,
+ 0x4c, 0x01, 0x80, 0x4c, 0x01, 0x81, 0x50, 0x00,
+ 0x81, 0x50, 0x00, 0x87, 0x52, 0x00, 0x87, 0x52,
+ 0x00, 0xa3, 0x5a, 0x1e, 0x84, 0x52, 0x00, 0xb1,
+ 0x53, 0x00, 0x87, 0x53, 0x00, 0xa3, 0x5a, 0x01,
+ 0x87, 0x60, 0x01, 0x87, 0x62, 0x1e, 0x87, 0x54,
+ 0x00, 0x87, 0x54, 0x00, 0xa3, 0x54, 0x00, 0xb1,
+ 0x54, 0x00, 0xad, 0x55, 0x00, 0xa4, 0x55, 0x00,
+ 0xb0, 0x55, 0x00, 0xad, 0x68, 0x01, 0x81, 0x6a,
+ 0x01, 0x88, 0x56, 0x83, 0x56, 0xa3, 0x57, 0x80,
+ 0x57, 0x81, 0x57, 0x88, 0x57, 0x87, 0x57, 0xa3,
+ 0x58, 0x87, 0x58, 0x88, 0x59, 0x87, 0x5a, 0x82,
+ 0x5a, 0xa3, 0x5a, 0xb1, 0x68, 0xb1, 0x74, 0x88,
+ 0x77, 0x8a, 0x79, 0x8a, 0x61, 0x00, 0xbe, 0x02,
+ 0x7f, 0x01, 0x87, 0x41, 0x00, 0xa3, 0x41, 0x00,
+ 0x89, 0xc2, 0x00, 0x81, 0xc2, 0x00, 0x80, 0xc2,
+ 0x00, 0x89, 0xc2, 0x00, 0x83, 0xa0, 0x1e, 0x82,
+ 0x02, 0x01, 0x81, 0x02, 0x01, 0x80, 0x02, 0x01,
+ 0x89, 0x02, 0x01, 0x83, 0xa0, 0x1e, 0x86, 0x45,
+ 0x00, 0xa3, 0x45, 0x00, 0x89, 0x45, 0x00, 0x83,
+ 0xca, 0x00, 0x81, 0xca, 0x00, 0x80, 0xca, 0x00,
+ 0x89, 0xca, 0x00, 0x83, 0xb8, 0x1e, 0x82, 0x49,
+ 0x00, 0x89, 0x49, 0x00, 0xa3, 0x4f, 0x00, 0xa3,
+ 0x4f, 0x00, 0x89, 0xd4, 0x00, 0x81, 0xd4, 0x00,
+ 0x80, 0xd4, 0x00, 0x89, 0xd4, 0x00, 0x83, 0xcc,
+ 0x1e, 0x82, 0xa0, 0x01, 0x81, 0xa0, 0x01, 0x80,
+ 0xa0, 0x01, 0x89, 0xa0, 0x01, 0x83, 0xa0, 0x01,
+ 0xa3, 0x55, 0x00, 0xa3, 0x55, 0x00, 0x89, 0xaf,
+ 0x01, 0x81, 0xaf, 0x01, 0x80, 0xaf, 0x01, 0x89,
+ 0xaf, 0x01, 0x83, 0xaf, 0x01, 0xa3, 0x59, 0x00,
+ 0x80, 0x59, 0x00, 0xa3, 0x59, 0x00, 0x89, 0x59,
+ 0x00, 0x83, 0xb1, 0x03, 0x13, 0x03, 0x00, 0x1f,
+ 0x80, 0x00, 0x1f, 0x81, 0x00, 0x1f, 0xc2, 0x91,
+ 0x03, 0x13, 0x03, 0x08, 0x1f, 0x80, 0x08, 0x1f,
+ 0x81, 0x08, 0x1f, 0xc2, 0xb5, 0x03, 0x13, 0x03,
+ 0x10, 0x1f, 0x80, 0x10, 0x1f, 0x81, 0x95, 0x03,
+ 0x13, 0x03, 0x18, 0x1f, 0x80, 0x18, 0x1f, 0x81,
+ 0xb7, 0x03, 0x93, 0xb7, 0x03, 0x94, 0x20, 0x1f,
+ 0x80, 0x21, 0x1f, 0x80, 0x20, 0x1f, 0x81, 0x21,
+ 0x1f, 0x81, 0x20, 0x1f, 0xc2, 0x21, 0x1f, 0xc2,
+ 0x97, 0x03, 0x93, 0x97, 0x03, 0x94, 0x28, 0x1f,
+ 0x80, 0x29, 0x1f, 0x80, 0x28, 0x1f, 0x81, 0x29,
+ 0x1f, 0x81, 0x28, 0x1f, 0xc2, 0x29, 0x1f, 0xc2,
+ 0xb9, 0x03, 0x93, 0xb9, 0x03, 0x94, 0x30, 0x1f,
+ 0x80, 0x31, 0x1f, 0x80, 0x30, 0x1f, 0x81, 0x31,
+ 0x1f, 0x81, 0x30, 0x1f, 0xc2, 0x31, 0x1f, 0xc2,
+ 0x99, 0x03, 0x93, 0x99, 0x03, 0x94, 0x38, 0x1f,
+ 0x80, 0x39, 0x1f, 0x80, 0x38, 0x1f, 0x81, 0x39,
+ 0x1f, 0x81, 0x38, 0x1f, 0xc2, 0x39, 0x1f, 0xc2,
+ 0xbf, 0x03, 0x93, 0xbf, 0x03, 0x94, 0x40, 0x1f,
+ 0x80, 0x40, 0x1f, 0x81, 0x9f, 0x03, 0x13, 0x03,
+ 0x48, 0x1f, 0x80, 0x48, 0x1f, 0x81, 0xc5, 0x03,
+ 0x13, 0x03, 0x50, 0x1f, 0x80, 0x50, 0x1f, 0x81,
+ 0x50, 0x1f, 0xc2, 0xa5, 0x03, 0x94, 0x00, 0x00,
+ 0x00, 0x59, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x59,
+ 0x1f, 0x81, 0x00, 0x00, 0x00, 0x59, 0x1f, 0xc2,
+ 0xc9, 0x03, 0x93, 0xc9, 0x03, 0x94, 0x60, 0x1f,
+ 0x80, 0x61, 0x1f, 0x80, 0x60, 0x1f, 0x81, 0x61,
+ 0x1f, 0x81, 0x60, 0x1f, 0xc2, 0x61, 0x1f, 0xc2,
+ 0xa9, 0x03, 0x93, 0xa9, 0x03, 0x94, 0x68, 0x1f,
+ 0x80, 0x69, 0x1f, 0x80, 0x68, 0x1f, 0x81, 0x69,
+ 0x1f, 0x81, 0x68, 0x1f, 0xc2, 0x69, 0x1f, 0xc2,
+ 0xb1, 0x03, 0x80, 0xb5, 0x03, 0x80, 0xb7, 0x03,
+ 0x80, 0xb9, 0x03, 0x80, 0xbf, 0x03, 0x80, 0xc5,
+ 0x03, 0x80, 0xc9, 0x03, 0x80, 0x00, 0x1f, 0x45,
+ 0x03, 0x20, 0x1f, 0x45, 0x03, 0x60, 0x1f, 0x45,
+ 0x03, 0xb1, 0x03, 0x86, 0xb1, 0x03, 0x84, 0x70,
+ 0x1f, 0xc5, 0xb1, 0x03, 0xc5, 0xac, 0x03, 0xc5,
+ 0x00, 0x00, 0x00, 0xb1, 0x03, 0xc2, 0xb6, 0x1f,
+ 0xc5, 0x91, 0x03, 0x86, 0x91, 0x03, 0x84, 0x91,
+ 0x03, 0x80, 0x91, 0x03, 0xc5, 0x20, 0x93, 0x20,
+ 0x93, 0x20, 0xc2, 0xa8, 0x00, 0xc2, 0x74, 0x1f,
+ 0xc5, 0xb7, 0x03, 0xc5, 0xae, 0x03, 0xc5, 0x00,
+ 0x00, 0x00, 0xb7, 0x03, 0xc2, 0xc6, 0x1f, 0xc5,
+ 0x95, 0x03, 0x80, 0x97, 0x03, 0x80, 0x97, 0x03,
+ 0xc5, 0xbf, 0x1f, 0x80, 0xbf, 0x1f, 0x81, 0xbf,
+ 0x1f, 0xc2, 0xb9, 0x03, 0x86, 0xb9, 0x03, 0x84,
+ 0xca, 0x03, 0x80, 0x00, 0x03, 0xb9, 0x42, 0xca,
+ 0x42, 0x99, 0x06, 0x99, 0x04, 0x99, 0x00, 0xfe,
+ 0x1f, 0x80, 0xfe, 0x1f, 0x81, 0xfe, 0x1f, 0xc2,
+ 0xc5, 0x03, 0x86, 0xc5, 0x03, 0x84, 0xcb, 0x03,
+ 0x80, 0x00, 0x03, 0xc1, 0x13, 0xc1, 0x14, 0xc5,
+ 0x42, 0xcb, 0x42, 0xa5, 0x06, 0xa5, 0x04, 0xa5,
+ 0x00, 0xa1, 0x03, 0x94, 0xa8, 0x00, 0x80, 0x85,
+ 0x03, 0x60, 0x00, 0x7c, 0x1f, 0xc5, 0xc9, 0x03,
+ 0xc5, 0xce, 0x03, 0xc5, 0x00, 0x00, 0x00, 0xc9,
+ 0x03, 0xc2, 0xf6, 0x1f, 0xc5, 0x9f, 0x03, 0x80,
+ 0xa9, 0x03, 0x80, 0xa9, 0x03, 0xc5, 0x20, 0x94,
+ 0x02, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0xb3, 0x2e, 0x2e, 0x2e,
+ 0x2e, 0x2e, 0x32, 0x20, 0x32, 0x20, 0x32, 0x20,
+ 0x00, 0x00, 0x00, 0x35, 0x20, 0x35, 0x20, 0x35,
+ 0x20, 0x00, 0x00, 0x00, 0x21, 0x21, 0x00, 0x00,
+ 0x20, 0x85, 0x3f, 0x3f, 0x3f, 0x21, 0x21, 0x3f,
+ 0x32, 0x20, 0x00, 0x00, 0x00, 0x00, 0x30, 0x69,
+ 0x00, 0x00, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x2b, 0x3d, 0x28, 0x29, 0x6e, 0x30, 0x00, 0x2b,
+ 0x00, 0x12, 0x22, 0x3d, 0x00, 0x28, 0x00, 0x29,
+ 0x00, 0x00, 0x00, 0x61, 0x00, 0x65, 0x00, 0x6f,
+ 0x00, 0x78, 0x00, 0x59, 0x02, 0x68, 0x6b, 0x6c,
+ 0x6d, 0x6e, 0x70, 0x73, 0x74, 0x52, 0x73, 0x61,
+ 0x2f, 0x63, 0x61, 0x2f, 0x73, 0xb0, 0x00, 0x43,
+ 0x63, 0x2f, 0x6f, 0x63, 0x2f, 0x75, 0xb0, 0x00,
+ 0x46, 0x48, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x20,
+ 0xdf, 0x01, 0x01, 0x04, 0x24, 0x4e, 0x6f, 0x50,
+ 0x51, 0x52, 0x52, 0x52, 0x53, 0x4d, 0x54, 0x45,
+ 0x4c, 0x54, 0x4d, 0x4b, 0x00, 0xc5, 0x00, 0x42,
+ 0x43, 0x00, 0x65, 0x45, 0x46, 0x00, 0x4d, 0x6f,
+ 0xd0, 0x05, 0x46, 0x41, 0x58, 0xc0, 0x03, 0xb3,
+ 0x03, 0x93, 0x03, 0xa0, 0x03, 0x11, 0x22, 0x44,
+ 0x64, 0x65, 0x69, 0x6a, 0x31, 0xd0, 0x37, 0x31,
+ 0xd0, 0x39, 0x31, 0xd0, 0x31, 0x30, 0x31, 0xd0,
+ 0x33, 0x32, 0xd0, 0x33, 0x31, 0xd0, 0x35, 0x32,
+ 0xd0, 0x35, 0x33, 0xd0, 0x35, 0x34, 0xd0, 0x35,
+ 0x31, 0xd0, 0x36, 0x35, 0xd0, 0x36, 0x31, 0xd0,
+ 0x38, 0x33, 0xd0, 0x38, 0x35, 0xd0, 0x38, 0x37,
+ 0xd0, 0x38, 0x31, 0xd0, 0x49, 0x49, 0x49, 0x49,
+ 0x49, 0x49, 0x56, 0x56, 0x49, 0x56, 0x49, 0x49,
+ 0x56, 0x49, 0x49, 0x49, 0x49, 0x58, 0x58, 0x49,
+ 0x58, 0x49, 0x49, 0x4c, 0x43, 0x44, 0x4d, 0x69,
+ 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x76, 0x76,
+ 0x69, 0x76, 0x69, 0x69, 0x76, 0x69, 0x69, 0x69,
+ 0x69, 0x78, 0x78, 0x69, 0x78, 0x69, 0x69, 0x6c,
+ 0x63, 0x64, 0x6d, 0x30, 0xd0, 0x33, 0x90, 0x21,
+ 0xb8, 0x92, 0x21, 0xb8, 0x94, 0x21, 0xb8, 0xd0,
+ 0x21, 0xb8, 0xd4, 0x21, 0xb8, 0xd2, 0x21, 0xb8,
+ 0x03, 0x22, 0xb8, 0x08, 0x22, 0xb8, 0x0b, 0x22,
+ 0xb8, 0x23, 0x22, 0xb8, 0x00, 0x00, 0x00, 0x25,
+ 0x22, 0xb8, 0x2b, 0x22, 0x2b, 0x22, 0x2b, 0x22,
+ 0x00, 0x00, 0x00, 0x2e, 0x22, 0x2e, 0x22, 0x2e,
+ 0x22, 0x00, 0x00, 0x00, 0x3c, 0x22, 0xb8, 0x43,
+ 0x22, 0xb8, 0x45, 0x22, 0xb8, 0x00, 0x00, 0x00,
+ 0x48, 0x22, 0xb8, 0x3d, 0x00, 0xb8, 0x00, 0x00,
+ 0x00, 0x61, 0x22, 0xb8, 0x4d, 0x22, 0xb8, 0x3c,
+ 0x00, 0xb8, 0x3e, 0x00, 0xb8, 0x64, 0x22, 0xb8,
+ 0x65, 0x22, 0xb8, 0x72, 0x22, 0xb8, 0x76, 0x22,
+ 0xb8, 0x7a, 0x22, 0xb8, 0x82, 0x22, 0xb8, 0x86,
+ 0x22, 0xb8, 0xa2, 0x22, 0xb8, 0xa8, 0x22, 0xb8,
+ 0xa9, 0x22, 0xb8, 0xab, 0x22, 0xb8, 0x7c, 0x22,
+ 0xb8, 0x91, 0x22, 0xb8, 0xb2, 0x22, 0x38, 0x03,
+ 0x08, 0x30, 0x31, 0x00, 0x31, 0x00, 0x30, 0x00,
+ 0x32, 0x30, 0x28, 0x00, 0x31, 0x00, 0x29, 0x00,
+ 0x28, 0x00, 0x31, 0x00, 0x30, 0x00, 0x29, 0x00,
+ 0x28, 0x32, 0x30, 0x29, 0x31, 0x00, 0x2e, 0x00,
+ 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x32, 0x30,
+ 0x2e, 0x28, 0x00, 0x61, 0x00, 0x29, 0x00, 0x41,
+ 0x00, 0x61, 0x00, 0x2b, 0x22, 0x00, 0x00, 0x00,
+ 0x00, 0x3a, 0x3a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
+ 0x3d, 0xdd, 0x2a, 0xb8, 0x6a, 0x56, 0x00, 0x4e,
+ 0x00, 0x28, 0x36, 0x3f, 0x59, 0x85, 0x8c, 0xa0,
+ 0xba, 0x3f, 0x51, 0x00, 0x26, 0x2c, 0x43, 0x57,
+ 0x6c, 0xa1, 0xb6, 0xc1, 0x9b, 0x52, 0x00, 0x5e,
+ 0x7a, 0x7f, 0x9d, 0xa6, 0xc1, 0xce, 0xe7, 0xb6,
+ 0x53, 0xc8, 0x53, 0xe3, 0x53, 0xd7, 0x56, 0x1f,
+ 0x57, 0xeb, 0x58, 0x02, 0x59, 0x0a, 0x59, 0x15,
+ 0x59, 0x27, 0x59, 0x73, 0x59, 0x50, 0x5b, 0x80,
+ 0x5b, 0xf8, 0x5b, 0x0f, 0x5c, 0x22, 0x5c, 0x38,
+ 0x5c, 0x6e, 0x5c, 0x71, 0x5c, 0xdb, 0x5d, 0xe5,
+ 0x5d, 0xf1, 0x5d, 0xfe, 0x5d, 0x72, 0x5e, 0x7a,
+ 0x5e, 0x7f, 0x5e, 0xf4, 0x5e, 0xfe, 0x5e, 0x0b,
+ 0x5f, 0x13, 0x5f, 0x50, 0x5f, 0x61, 0x5f, 0x73,
+ 0x5f, 0xc3, 0x5f, 0x08, 0x62, 0x36, 0x62, 0x4b,
+ 0x62, 0x2f, 0x65, 0x34, 0x65, 0x87, 0x65, 0x97,
+ 0x65, 0xa4, 0x65, 0xb9, 0x65, 0xe0, 0x65, 0xe5,
+ 0x65, 0xf0, 0x66, 0x08, 0x67, 0x28, 0x67, 0x20,
+ 0x6b, 0x62, 0x6b, 0x79, 0x6b, 0xb3, 0x6b, 0xcb,
+ 0x6b, 0xd4, 0x6b, 0xdb, 0x6b, 0x0f, 0x6c, 0x14,
+ 0x6c, 0x34, 0x6c, 0x6b, 0x70, 0x2a, 0x72, 0x36,
+ 0x72, 0x3b, 0x72, 0x3f, 0x72, 0x47, 0x72, 0x59,
+ 0x72, 0x5b, 0x72, 0xac, 0x72, 0x84, 0x73, 0x89,
+ 0x73, 0xdc, 0x74, 0xe6, 0x74, 0x18, 0x75, 0x1f,
+ 0x75, 0x28, 0x75, 0x30, 0x75, 0x8b, 0x75, 0x92,
+ 0x75, 0x76, 0x76, 0x7d, 0x76, 0xae, 0x76, 0xbf,
+ 0x76, 0xee, 0x76, 0xdb, 0x77, 0xe2, 0x77, 0xf3,
+ 0x77, 0x3a, 0x79, 0xb8, 0x79, 0xbe, 0x79, 0x74,
+ 0x7a, 0xcb, 0x7a, 0xf9, 0x7a, 0x73, 0x7c, 0xf8,
+ 0x7c, 0x36, 0x7f, 0x51, 0x7f, 0x8a, 0x7f, 0xbd,
+ 0x7f, 0x01, 0x80, 0x0c, 0x80, 0x12, 0x80, 0x33,
+ 0x80, 0x7f, 0x80, 0x89, 0x80, 0xe3, 0x81, 0x00,
+ 0x07, 0x10, 0x19, 0x29, 0x38, 0x3c, 0x8b, 0x8f,
+ 0x95, 0x4d, 0x86, 0x6b, 0x86, 0x40, 0x88, 0x4c,
+ 0x88, 0x63, 0x88, 0x7e, 0x89, 0x8b, 0x89, 0xd2,
+ 0x89, 0x00, 0x8a, 0x37, 0x8c, 0x46, 0x8c, 0x55,
+ 0x8c, 0x78, 0x8c, 0x9d, 0x8c, 0x64, 0x8d, 0x70,
+ 0x8d, 0xb3, 0x8d, 0xab, 0x8e, 0xca, 0x8e, 0x9b,
+ 0x8f, 0xb0, 0x8f, 0xb5, 0x8f, 0x91, 0x90, 0x49,
+ 0x91, 0xc6, 0x91, 0xcc, 0x91, 0xd1, 0x91, 0x77,
+ 0x95, 0x80, 0x95, 0x1c, 0x96, 0xb6, 0x96, 0xb9,
+ 0x96, 0xe8, 0x96, 0x51, 0x97, 0x5e, 0x97, 0x62,
+ 0x97, 0x69, 0x97, 0xcb, 0x97, 0xed, 0x97, 0xf3,
+ 0x97, 0x01, 0x98, 0xa8, 0x98, 0xdb, 0x98, 0xdf,
+ 0x98, 0x96, 0x99, 0x99, 0x99, 0xac, 0x99, 0xa8,
+ 0x9a, 0xd8, 0x9a, 0xdf, 0x9a, 0x25, 0x9b, 0x2f,
+ 0x9b, 0x32, 0x9b, 0x3c, 0x9b, 0x5a, 0x9b, 0xe5,
+ 0x9c, 0x75, 0x9e, 0x7f, 0x9e, 0xa5, 0x9e, 0x00,
+ 0x16, 0x1e, 0x28, 0x2c, 0x54, 0x58, 0x69, 0x6e,
+ 0x7b, 0x96, 0xa5, 0xad, 0xe8, 0xf7, 0xfb, 0x12,
+ 0x30, 0x00, 0x00, 0x41, 0x53, 0x44, 0x53, 0x45,
+ 0x53, 0x4b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x4d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x4f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x51, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x53, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x55, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x57, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x59, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x5b, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x5d, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x5f, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x61, 0x30, 0x99, 0x30, 0x64, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x66, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x68, 0x30, 0x99,
+ 0x30, 0x6f, 0x30, 0x99, 0x30, 0x72, 0x30, 0x99,
+ 0x30, 0x75, 0x30, 0x99, 0x30, 0x78, 0x30, 0x99,
+ 0x30, 0x7b, 0x30, 0x99, 0x30, 0x46, 0x30, 0x99,
+ 0x30, 0x20, 0x00, 0x99, 0x30, 0x9d, 0x30, 0x99,
+ 0x30, 0x88, 0x30, 0x8a, 0x30, 0xab, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xad, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xaf, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x30, 0x99,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0xc1, 0x30, 0x99,
+ 0x30, 0xc4, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0xc6, 0x30, 0x99, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0xc8, 0x30, 0x99, 0x30, 0xcf, 0x30, 0x99,
+ 0x30, 0xd2, 0x30, 0x99, 0x30, 0xd5, 0x30, 0x99,
+ 0x30, 0xd8, 0x30, 0x99, 0x30, 0xdb, 0x30, 0x99,
+ 0x30, 0xa6, 0x30, 0x99, 0x30, 0xef, 0x30, 0x99,
+ 0x30, 0xfd, 0x30, 0x99, 0x30, 0xb3, 0x30, 0xc8,
+ 0x30, 0x00, 0x11, 0x00, 0x01, 0xaa, 0x02, 0xac,
+ 0xad, 0x03, 0x04, 0x05, 0xb0, 0xb1, 0xb2, 0xb3,
+ 0xb4, 0xb5, 0x1a, 0x06, 0x07, 0x08, 0x21, 0x09,
+ 0x11, 0x61, 0x11, 0x14, 0x11, 0x4c, 0x00, 0x01,
+ 0xb3, 0xb4, 0xb8, 0xba, 0xbf, 0xc3, 0xc5, 0x08,
+ 0xc9, 0xcb, 0x09, 0x0a, 0x0c, 0x0e, 0x0f, 0x13,
+ 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1e, 0x22,
+ 0x2c, 0x33, 0x38, 0xdd, 0xde, 0x43, 0x44, 0x45,
+ 0x70, 0x71, 0x74, 0x7d, 0x7e, 0x80, 0x8a, 0x8d,
+ 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56,
+ 0x0a, 0x4e, 0x2d, 0x4e, 0x0b, 0x4e, 0x32, 0x75,
+ 0x59, 0x4e, 0x19, 0x4e, 0x01, 0x4e, 0x29, 0x59,
+ 0x30, 0x57, 0xba, 0x4e, 0x28, 0x00, 0x29, 0x00,
+ 0x00, 0x11, 0x02, 0x11, 0x03, 0x11, 0x05, 0x11,
+ 0x06, 0x11, 0x07, 0x11, 0x09, 0x11, 0x0b, 0x11,
+ 0x0c, 0x11, 0x0e, 0x11, 0x0f, 0x11, 0x10, 0x11,
+ 0x11, 0x11, 0x12, 0x11, 0x28, 0x00, 0x00, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x02, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x05, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x09, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0e, 0x11,
+ 0x61, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0c, 0x11,
+ 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11,
+ 0x69, 0x11, 0x0c, 0x11, 0x65, 0x11, 0xab, 0x11,
+ 0x29, 0x00, 0x28, 0x00, 0x0b, 0x11, 0x69, 0x11,
+ 0x12, 0x11, 0x6e, 0x11, 0x29, 0x00, 0x28, 0x00,
+ 0x29, 0x00, 0x00, 0x4e, 0x8c, 0x4e, 0x09, 0x4e,
+ 0xdb, 0x56, 0x94, 0x4e, 0x6d, 0x51, 0x03, 0x4e,
+ 0x6b, 0x51, 0x5d, 0x4e, 0x41, 0x53, 0x08, 0x67,
+ 0x6b, 0x70, 0x34, 0x6c, 0x28, 0x67, 0xd1, 0x91,
+ 0x1f, 0x57, 0xe5, 0x65, 0x2a, 0x68, 0x09, 0x67,
+ 0x3e, 0x79, 0x0d, 0x54, 0x79, 0x72, 0xa1, 0x8c,
+ 0x5d, 0x79, 0xb4, 0x52, 0xe3, 0x4e, 0x7c, 0x54,
+ 0x66, 0x5b, 0xe3, 0x76, 0x01, 0x4f, 0xc7, 0x8c,
+ 0x54, 0x53, 0x6d, 0x79, 0x11, 0x4f, 0xea, 0x81,
+ 0xf3, 0x81, 0x4f, 0x55, 0x7c, 0x5e, 0x87, 0x65,
+ 0x8f, 0x7b, 0x50, 0x54, 0x45, 0x32, 0x00, 0x31,
+ 0x00, 0x33, 0x00, 0x30, 0x00, 0x00, 0x11, 0x00,
+ 0x02, 0x03, 0x05, 0x06, 0x07, 0x09, 0x0b, 0x0c,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x00, 0x11, 0x00,
+ 0x61, 0x02, 0x61, 0x03, 0x61, 0x05, 0x61, 0x06,
+ 0x61, 0x07, 0x61, 0x09, 0x61, 0x0b, 0x61, 0x0c,
+ 0x61, 0x0e, 0x11, 0x61, 0x11, 0x00, 0x11, 0x0e,
+ 0x61, 0xb7, 0x00, 0x69, 0x0b, 0x11, 0x01, 0x63,
+ 0x00, 0x69, 0x0b, 0x11, 0x6e, 0x11, 0x00, 0x4e,
+ 0x8c, 0x4e, 0x09, 0x4e, 0xdb, 0x56, 0x94, 0x4e,
+ 0x6d, 0x51, 0x03, 0x4e, 0x6b, 0x51, 0x5d, 0x4e,
+ 0x41, 0x53, 0x08, 0x67, 0x6b, 0x70, 0x34, 0x6c,
+ 0x28, 0x67, 0xd1, 0x91, 0x1f, 0x57, 0xe5, 0x65,
+ 0x2a, 0x68, 0x09, 0x67, 0x3e, 0x79, 0x0d, 0x54,
+ 0x79, 0x72, 0xa1, 0x8c, 0x5d, 0x79, 0xb4, 0x52,
+ 0xd8, 0x79, 0x37, 0x75, 0x73, 0x59, 0x69, 0x90,
+ 0x2a, 0x51, 0x70, 0x53, 0xe8, 0x6c, 0x05, 0x98,
+ 0x11, 0x4f, 0x99, 0x51, 0x63, 0x6b, 0x0a, 0x4e,
+ 0x2d, 0x4e, 0x0b, 0x4e, 0xe6, 0x5d, 0xf3, 0x53,
+ 0x3b, 0x53, 0x97, 0x5b, 0x66, 0x5b, 0xe3, 0x76,
+ 0x01, 0x4f, 0xc7, 0x8c, 0x54, 0x53, 0x1c, 0x59,
+ 0x33, 0x00, 0x36, 0x00, 0x34, 0x00, 0x30, 0x00,
+ 0x35, 0x30, 0x31, 0x00, 0x08, 0x67, 0x31, 0x00,
+ 0x30, 0x00, 0x08, 0x67, 0x48, 0x67, 0x65, 0x72,
+ 0x67, 0x65, 0x56, 0x4c, 0x54, 0x44, 0xa2, 0x30,
+ 0x00, 0x02, 0x04, 0x06, 0x08, 0x09, 0x0b, 0x0d,
+ 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d,
+ 0x1f, 0x22, 0x24, 0x26, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x30, 0x33, 0x36, 0x39, 0x3c, 0x3d,
+ 0x3e, 0x3f, 0x40, 0x42, 0x44, 0x46, 0x47, 0x48,
+ 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0xe4,
+ 0x4e, 0x8c, 0x54, 0xa1, 0x30, 0x01, 0x30, 0x5b,
+ 0x27, 0x01, 0x4a, 0x34, 0x00, 0x01, 0x52, 0x39,
+ 0x01, 0xa2, 0x30, 0x00, 0x5a, 0x49, 0xa4, 0x30,
+ 0x00, 0x27, 0x4f, 0x0c, 0xa4, 0x30, 0x00, 0x4f,
+ 0x1d, 0x02, 0x05, 0x4f, 0xa8, 0x30, 0x00, 0x11,
+ 0x07, 0x54, 0x21, 0xa8, 0x30, 0x00, 0x54, 0x03,
+ 0x54, 0xa4, 0x30, 0x06, 0x4f, 0x15, 0x06, 0x58,
+ 0x3c, 0x07, 0x00, 0x46, 0xab, 0x30, 0x00, 0x3e,
+ 0x18, 0x1d, 0x00, 0x42, 0x3f, 0x51, 0xac, 0x30,
+ 0x00, 0x41, 0x47, 0x00, 0x47, 0x32, 0xae, 0x30,
+ 0xac, 0x30, 0xae, 0x30, 0x00, 0x1d, 0x4e, 0xad,
+ 0x30, 0x00, 0x38, 0x3d, 0x4f, 0x01, 0x3e, 0x13,
+ 0x4f, 0xad, 0x30, 0xed, 0x30, 0xad, 0x30, 0x00,
+ 0x40, 0x03, 0x3c, 0x33, 0xad, 0x30, 0x00, 0x40,
+ 0x34, 0x4f, 0x1b, 0x3e, 0xad, 0x30, 0x00, 0x40,
+ 0x42, 0x16, 0x1b, 0xb0, 0x30, 0x00, 0x39, 0x30,
+ 0xa4, 0x30, 0x0c, 0x45, 0x3c, 0x24, 0x4f, 0x0b,
+ 0x47, 0x18, 0x00, 0x49, 0xaf, 0x30, 0x00, 0x3e,
+ 0x4d, 0x1e, 0xb1, 0x30, 0x00, 0x4b, 0x08, 0x02,
+ 0x3a, 0x19, 0x02, 0x4b, 0x2c, 0xa4, 0x30, 0x11,
+ 0x00, 0x0b, 0x47, 0xb5, 0x30, 0x00, 0x3e, 0x0c,
+ 0x47, 0x2b, 0xb0, 0x30, 0x07, 0x3a, 0x43, 0x00,
+ 0xb9, 0x30, 0x02, 0x3a, 0x08, 0x02, 0x3a, 0x0f,
+ 0x07, 0x43, 0x00, 0xb7, 0x30, 0x10, 0x00, 0x12,
+ 0x34, 0x11, 0x3c, 0x13, 0x17, 0xa4, 0x30, 0x2a,
+ 0x1f, 0x24, 0x2b, 0x00, 0x20, 0xbb, 0x30, 0x16,
+ 0x41, 0x00, 0x38, 0x0d, 0xc4, 0x30, 0x0d, 0x38,
+ 0x00, 0xd0, 0x30, 0x00, 0x2c, 0x1c, 0x1b, 0xa2,
+ 0x30, 0x32, 0x00, 0x17, 0x26, 0x49, 0xaf, 0x30,
+ 0x25, 0x00, 0x3c, 0xb3, 0x30, 0x21, 0x00, 0x20,
+ 0x38, 0xa1, 0x30, 0x34, 0x00, 0x48, 0x22, 0x28,
+ 0xa3, 0x30, 0x32, 0x00, 0x59, 0x25, 0xa7, 0x30,
+ 0x2f, 0x1c, 0x10, 0x00, 0x44, 0xd5, 0x30, 0x00,
+ 0x14, 0x1e, 0xaf, 0x30, 0x29, 0x00, 0x10, 0x4d,
+ 0x3c, 0xda, 0x30, 0xbd, 0x30, 0xb8, 0x30, 0x22,
+ 0x13, 0x1a, 0x20, 0x33, 0x0c, 0x22, 0x3b, 0x01,
+ 0x22, 0x44, 0x00, 0x21, 0x44, 0x07, 0xa4, 0x30,
+ 0x39, 0x00, 0x4f, 0x24, 0xc8, 0x30, 0x14, 0x23,
+ 0x00, 0xdb, 0x30, 0xf3, 0x30, 0xc9, 0x30, 0x14,
+ 0x2a, 0x00, 0x12, 0x33, 0x22, 0x12, 0x33, 0x2a,
+ 0xa4, 0x30, 0x3a, 0x00, 0x0b, 0x49, 0xa4, 0x30,
+ 0x3a, 0x00, 0x47, 0x3a, 0x1f, 0x2b, 0x3a, 0x47,
+ 0x0b, 0xb7, 0x30, 0x27, 0x3c, 0x00, 0x30, 0x3c,
+ 0xaf, 0x30, 0x30, 0x00, 0x3e, 0x44, 0xdf, 0x30,
+ 0xea, 0x30, 0xd0, 0x30, 0x0f, 0x1a, 0x00, 0x2c,
+ 0x1b, 0xe1, 0x30, 0xac, 0x30, 0xac, 0x30, 0x35,
+ 0x00, 0x1c, 0x47, 0x35, 0x50, 0x1c, 0x3f, 0xa2,
+ 0x30, 0x42, 0x5a, 0x27, 0x42, 0x5a, 0x49, 0x44,
+ 0x00, 0x51, 0xc3, 0x30, 0x27, 0x00, 0x05, 0x28,
+ 0xea, 0x30, 0xe9, 0x30, 0xd4, 0x30, 0x17, 0x00,
+ 0x28, 0xd6, 0x30, 0x15, 0x26, 0x00, 0x15, 0xec,
+ 0x30, 0xe0, 0x30, 0xb2, 0x30, 0x3a, 0x41, 0x16,
+ 0x00, 0x41, 0xc3, 0x30, 0x2c, 0x00, 0x05, 0x30,
+ 0x00, 0xb9, 0x70, 0x31, 0x00, 0x30, 0x00, 0xb9,
+ 0x70, 0x32, 0x00, 0x30, 0x00, 0xb9, 0x70, 0x68,
+ 0x50, 0x61, 0x64, 0x61, 0x41, 0x55, 0x62, 0x61,
+ 0x72, 0x6f, 0x56, 0x70, 0x63, 0x64, 0x6d, 0x64,
+ 0x00, 0x6d, 0x00, 0xb2, 0x00, 0x49, 0x00, 0x55,
+ 0x00, 0x73, 0x5e, 0x10, 0x62, 0x2d, 0x66, 0x8c,
+ 0x54, 0x27, 0x59, 0x63, 0x6b, 0x0e, 0x66, 0xbb,
+ 0x6c, 0x2a, 0x68, 0x0f, 0x5f, 0x1a, 0x4f, 0x3e,
+ 0x79, 0x70, 0x00, 0x41, 0x6e, 0x00, 0x41, 0xbc,
+ 0x03, 0x41, 0x6d, 0x00, 0x41, 0x6b, 0x00, 0x41,
+ 0x4b, 0x00, 0x42, 0x4d, 0x00, 0x42, 0x47, 0x00,
+ 0x42, 0x63, 0x61, 0x6c, 0x6b, 0x63, 0x61, 0x6c,
+ 0x70, 0x00, 0x46, 0x6e, 0x00, 0x46, 0xbc, 0x03,
+ 0x46, 0xbc, 0x03, 0x67, 0x6d, 0x00, 0x67, 0x6b,
+ 0x00, 0x67, 0x48, 0x00, 0x7a, 0x6b, 0x48, 0x7a,
+ 0x4d, 0x48, 0x7a, 0x47, 0x48, 0x7a, 0x54, 0x48,
+ 0x7a, 0xbc, 0x03, 0x13, 0x21, 0x6d, 0x00, 0x13,
+ 0x21, 0x64, 0x00, 0x13, 0x21, 0x6b, 0x00, 0x13,
+ 0x21, 0x66, 0x00, 0x6d, 0x6e, 0x00, 0x6d, 0xbc,
+ 0x03, 0x6d, 0x6d, 0x00, 0x6d, 0x63, 0x00, 0x6d,
+ 0x6b, 0x00, 0x6d, 0x63, 0x00, 0x0a, 0x0a, 0x4f,
+ 0x00, 0x0a, 0x4f, 0x6d, 0x00, 0xb2, 0x00, 0x63,
+ 0x00, 0x08, 0x0a, 0x4f, 0x0a, 0x0a, 0x50, 0x00,
+ 0x0a, 0x50, 0x6d, 0x00, 0xb3, 0x00, 0x6b, 0x00,
+ 0x6d, 0x00, 0xb3, 0x00, 0x6d, 0x00, 0x15, 0x22,
+ 0x73, 0x00, 0x6d, 0x00, 0x15, 0x22, 0x73, 0x00,
+ 0xb2, 0x00, 0x50, 0x61, 0x6b, 0x50, 0x61, 0x4d,
+ 0x50, 0x61, 0x47, 0x50, 0x61, 0x72, 0x61, 0x64,
+ 0x72, 0x61, 0x64, 0xd1, 0x73, 0x72, 0x00, 0x61,
+ 0x00, 0x64, 0x00, 0x15, 0x22, 0x73, 0x00, 0xb2,
+ 0x00, 0x70, 0x00, 0x73, 0x6e, 0x00, 0x73, 0xbc,
+ 0x03, 0x73, 0x6d, 0x00, 0x73, 0x70, 0x00, 0x56,
+ 0x6e, 0x00, 0x56, 0xbc, 0x03, 0x56, 0x6d, 0x00,
+ 0x56, 0x6b, 0x00, 0x56, 0x4d, 0x00, 0x56, 0x70,
+ 0x00, 0x57, 0x6e, 0x00, 0x57, 0xbc, 0x03, 0x57,
+ 0x6d, 0x00, 0x57, 0x6b, 0x00, 0x57, 0x4d, 0x00,
+ 0x57, 0x6b, 0x00, 0xa9, 0x03, 0x4d, 0x00, 0xa9,
+ 0x03, 0x61, 0x2e, 0x6d, 0x2e, 0x42, 0x71, 0x63,
+ 0x63, 0x63, 0x64, 0x43, 0xd1, 0x6b, 0x67, 0x43,
+ 0x6f, 0x2e, 0x64, 0x42, 0x47, 0x79, 0x68, 0x61,
+ 0x48, 0x50, 0x69, 0x6e, 0x4b, 0x4b, 0x4b, 0x4d,
+ 0x6b, 0x74, 0x6c, 0x6d, 0x6c, 0x6e, 0x6c, 0x6f,
+ 0x67, 0x6c, 0x78, 0x6d, 0x62, 0x6d, 0x69, 0x6c,
+ 0x6d, 0x6f, 0x6c, 0x50, 0x48, 0x70, 0x2e, 0x6d,
+ 0x2e, 0x50, 0x50, 0x4d, 0x50, 0x52, 0x73, 0x72,
+ 0x53, 0x76, 0x57, 0x62, 0x56, 0xd1, 0x6d, 0x41,
+ 0xd1, 0x6d, 0x31, 0x00, 0xe5, 0x65, 0x31, 0x00,
+ 0x30, 0x00, 0xe5, 0x65, 0x32, 0x00, 0x30, 0x00,
+ 0xe5, 0x65, 0x33, 0x00, 0x30, 0x00, 0xe5, 0x65,
+ 0x67, 0x61, 0x6c, 0x4a, 0x04, 0x4c, 0x04, 0x43,
+ 0x46, 0x51, 0x26, 0x01, 0x53, 0x01, 0x27, 0xa7,
+ 0x37, 0xab, 0x6b, 0x02, 0x52, 0xab, 0x48, 0x8c,
+ 0xf4, 0x66, 0xca, 0x8e, 0xc8, 0x8c, 0xd1, 0x6e,
+ 0x32, 0x4e, 0xe5, 0x53, 0x9c, 0x9f, 0x9c, 0x9f,
+ 0x51, 0x59, 0xd1, 0x91, 0x87, 0x55, 0x48, 0x59,
+ 0xf6, 0x61, 0x69, 0x76, 0x85, 0x7f, 0x3f, 0x86,
+ 0xba, 0x87, 0xf8, 0x88, 0x8f, 0x90, 0x02, 0x6a,
+ 0x1b, 0x6d, 0xd9, 0x70, 0xde, 0x73, 0x3d, 0x84,
+ 0x6a, 0x91, 0xf1, 0x99, 0x82, 0x4e, 0x75, 0x53,
+ 0x04, 0x6b, 0x1b, 0x72, 0x2d, 0x86, 0x1e, 0x9e,
+ 0x50, 0x5d, 0xeb, 0x6f, 0xcd, 0x85, 0x64, 0x89,
+ 0xc9, 0x62, 0xd8, 0x81, 0x1f, 0x88, 0xca, 0x5e,
+ 0x17, 0x67, 0x6a, 0x6d, 0xfc, 0x72, 0xce, 0x90,
+ 0x86, 0x4f, 0xb7, 0x51, 0xde, 0x52, 0xc4, 0x64,
+ 0xd3, 0x6a, 0x10, 0x72, 0xe7, 0x76, 0x01, 0x80,
+ 0x06, 0x86, 0x5c, 0x86, 0xef, 0x8d, 0x32, 0x97,
+ 0x6f, 0x9b, 0xfa, 0x9d, 0x8c, 0x78, 0x7f, 0x79,
+ 0xa0, 0x7d, 0xc9, 0x83, 0x04, 0x93, 0x7f, 0x9e,
+ 0xd6, 0x8a, 0xdf, 0x58, 0x04, 0x5f, 0x60, 0x7c,
+ 0x7e, 0x80, 0x62, 0x72, 0xca, 0x78, 0xc2, 0x8c,
+ 0xf7, 0x96, 0xd8, 0x58, 0x62, 0x5c, 0x13, 0x6a,
+ 0xda, 0x6d, 0x0f, 0x6f, 0x2f, 0x7d, 0x37, 0x7e,
+ 0x4b, 0x96, 0xd2, 0x52, 0x8b, 0x80, 0xdc, 0x51,
+ 0xcc, 0x51, 0x1c, 0x7a, 0xbe, 0x7d, 0xf1, 0x83,
+ 0x75, 0x96, 0x80, 0x8b, 0xcf, 0x62, 0x02, 0x6a,
+ 0xfe, 0x8a, 0x39, 0x4e, 0xe7, 0x5b, 0x12, 0x60,
+ 0x87, 0x73, 0x70, 0x75, 0x17, 0x53, 0xfb, 0x78,
+ 0xbf, 0x4f, 0xa9, 0x5f, 0x0d, 0x4e, 0xcc, 0x6c,
+ 0x78, 0x65, 0x22, 0x7d, 0xc3, 0x53, 0x5e, 0x58,
+ 0x01, 0x77, 0x49, 0x84, 0xaa, 0x8a, 0xba, 0x6b,
+ 0xb0, 0x8f, 0x88, 0x6c, 0xfe, 0x62, 0xe5, 0x82,
+ 0xa0, 0x63, 0x65, 0x75, 0xae, 0x4e, 0x69, 0x51,
+ 0xc9, 0x51, 0x81, 0x68, 0xe7, 0x7c, 0x6f, 0x82,
+ 0xd2, 0x8a, 0xcf, 0x91, 0xf5, 0x52, 0x42, 0x54,
+ 0x73, 0x59, 0xec, 0x5e, 0xc5, 0x65, 0xfe, 0x6f,
+ 0x2a, 0x79, 0xad, 0x95, 0x6a, 0x9a, 0x97, 0x9e,
+ 0xce, 0x9e, 0x9b, 0x52, 0xc6, 0x66, 0x77, 0x6b,
+ 0x62, 0x8f, 0x74, 0x5e, 0x90, 0x61, 0x00, 0x62,
+ 0x9a, 0x64, 0x23, 0x6f, 0x49, 0x71, 0x89, 0x74,
+ 0xca, 0x79, 0xf4, 0x7d, 0x6f, 0x80, 0x26, 0x8f,
+ 0xee, 0x84, 0x23, 0x90, 0x4a, 0x93, 0x17, 0x52,
+ 0xa3, 0x52, 0xbd, 0x54, 0xc8, 0x70, 0xc2, 0x88,
+ 0xaa, 0x8a, 0xc9, 0x5e, 0xf5, 0x5f, 0x7b, 0x63,
+ 0xae, 0x6b, 0x3e, 0x7c, 0x75, 0x73, 0xe4, 0x4e,
+ 0xf9, 0x56, 0xe7, 0x5b, 0xba, 0x5d, 0x1c, 0x60,
+ 0xb2, 0x73, 0x69, 0x74, 0x9a, 0x7f, 0x46, 0x80,
+ 0x34, 0x92, 0xf6, 0x96, 0x48, 0x97, 0x18, 0x98,
+ 0x8b, 0x4f, 0xae, 0x79, 0xb4, 0x91, 0xb8, 0x96,
+ 0xe1, 0x60, 0x86, 0x4e, 0xda, 0x50, 0xee, 0x5b,
+ 0x3f, 0x5c, 0x99, 0x65, 0x02, 0x6a, 0xce, 0x71,
+ 0x42, 0x76, 0xfc, 0x84, 0x7c, 0x90, 0x8d, 0x9f,
+ 0x88, 0x66, 0x2e, 0x96, 0x89, 0x52, 0x7b, 0x67,
+ 0xf3, 0x67, 0x41, 0x6d, 0x9c, 0x6e, 0x09, 0x74,
+ 0x59, 0x75, 0x6b, 0x78, 0x10, 0x7d, 0x5e, 0x98,
+ 0x6d, 0x51, 0x2e, 0x62, 0x78, 0x96, 0x2b, 0x50,
+ 0x19, 0x5d, 0xea, 0x6d, 0x2a, 0x8f, 0x8b, 0x5f,
+ 0x44, 0x61, 0x17, 0x68, 0x87, 0x73, 0x86, 0x96,
+ 0x29, 0x52, 0x0f, 0x54, 0x65, 0x5c, 0x13, 0x66,
+ 0x4e, 0x67, 0xa8, 0x68, 0xe5, 0x6c, 0x06, 0x74,
+ 0xe2, 0x75, 0x79, 0x7f, 0xcf, 0x88, 0xe1, 0x88,
+ 0xcc, 0x91, 0xe2, 0x96, 0x3f, 0x53, 0xba, 0x6e,
+ 0x1d, 0x54, 0xd0, 0x71, 0x98, 0x74, 0xfa, 0x85,
+ 0xa3, 0x96, 0x57, 0x9c, 0x9f, 0x9e, 0x97, 0x67,
+ 0xcb, 0x6d, 0xe8, 0x81, 0xcb, 0x7a, 0x20, 0x7b,
+ 0x92, 0x7c, 0xc0, 0x72, 0x99, 0x70, 0x58, 0x8b,
+ 0xc0, 0x4e, 0x36, 0x83, 0x3a, 0x52, 0x07, 0x52,
+ 0xa6, 0x5e, 0xd3, 0x62, 0xd6, 0x7c, 0x85, 0x5b,
+ 0x1e, 0x6d, 0xb4, 0x66, 0x3b, 0x8f, 0x4c, 0x88,
+ 0x4d, 0x96, 0x8b, 0x89, 0xd3, 0x5e, 0x40, 0x51,
+ 0xc0, 0x55, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x58,
+ 0x00, 0x00, 0x74, 0x66, 0x00, 0x00, 0x00, 0x00,
+ 0xde, 0x51, 0x2a, 0x73, 0xca, 0x76, 0x3c, 0x79,
+ 0x5e, 0x79, 0x65, 0x79, 0x8f, 0x79, 0x56, 0x97,
+ 0xbe, 0x7c, 0xbd, 0x7f, 0x00, 0x00, 0x12, 0x86,
+ 0x00, 0x00, 0xf8, 0x8a, 0x00, 0x00, 0x00, 0x00,
+ 0x38, 0x90, 0xfd, 0x90, 0xef, 0x98, 0xfc, 0x98,
+ 0x28, 0x99, 0xb4, 0x9d, 0xde, 0x90, 0xb7, 0x96,
+ 0xae, 0x4f, 0xe7, 0x50, 0x4d, 0x51, 0xc9, 0x52,
+ 0xe4, 0x52, 0x51, 0x53, 0x9d, 0x55, 0x06, 0x56,
+ 0x68, 0x56, 0x40, 0x58, 0xa8, 0x58, 0x64, 0x5c,
+ 0x6e, 0x5c, 0x94, 0x60, 0x68, 0x61, 0x8e, 0x61,
+ 0xf2, 0x61, 0x4f, 0x65, 0xe2, 0x65, 0x91, 0x66,
+ 0x85, 0x68, 0x77, 0x6d, 0x1a, 0x6e, 0x22, 0x6f,
+ 0x6e, 0x71, 0x2b, 0x72, 0x22, 0x74, 0x91, 0x78,
+ 0x3e, 0x79, 0x49, 0x79, 0x48, 0x79, 0x50, 0x79,
+ 0x56, 0x79, 0x5d, 0x79, 0x8d, 0x79, 0x8e, 0x79,
+ 0x40, 0x7a, 0x81, 0x7a, 0xc0, 0x7b, 0xf4, 0x7d,
+ 0x09, 0x7e, 0x41, 0x7e, 0x72, 0x7f, 0x05, 0x80,
+ 0xed, 0x81, 0x79, 0x82, 0x79, 0x82, 0x57, 0x84,
+ 0x10, 0x89, 0x96, 0x89, 0x01, 0x8b, 0x39, 0x8b,
+ 0xd3, 0x8c, 0x08, 0x8d, 0xb6, 0x8f, 0x38, 0x90,
+ 0xe3, 0x96, 0xff, 0x97, 0x3b, 0x98, 0x75, 0x60,
+ 0xee, 0x42, 0x18, 0x82, 0x02, 0x26, 0x4e, 0xb5,
+ 0x51, 0x68, 0x51, 0x80, 0x4f, 0x45, 0x51, 0x80,
+ 0x51, 0xc7, 0x52, 0xfa, 0x52, 0x9d, 0x55, 0x55,
+ 0x55, 0x99, 0x55, 0xe2, 0x55, 0x5a, 0x58, 0xb3,
+ 0x58, 0x44, 0x59, 0x54, 0x59, 0x62, 0x5a, 0x28,
+ 0x5b, 0xd2, 0x5e, 0xd9, 0x5e, 0x69, 0x5f, 0xad,
+ 0x5f, 0xd8, 0x60, 0x4e, 0x61, 0x08, 0x61, 0x8e,
+ 0x61, 0x60, 0x61, 0xf2, 0x61, 0x34, 0x62, 0xc4,
+ 0x63, 0x1c, 0x64, 0x52, 0x64, 0x56, 0x65, 0x74,
+ 0x66, 0x17, 0x67, 0x1b, 0x67, 0x56, 0x67, 0x79,
+ 0x6b, 0xba, 0x6b, 0x41, 0x6d, 0xdb, 0x6e, 0xcb,
+ 0x6e, 0x22, 0x6f, 0x1e, 0x70, 0x6e, 0x71, 0xa7,
+ 0x77, 0x35, 0x72, 0xaf, 0x72, 0x2a, 0x73, 0x71,
+ 0x74, 0x06, 0x75, 0x3b, 0x75, 0x1d, 0x76, 0x1f,
+ 0x76, 0xca, 0x76, 0xdb, 0x76, 0xf4, 0x76, 0x4a,
+ 0x77, 0x40, 0x77, 0xcc, 0x78, 0xb1, 0x7a, 0xc0,
+ 0x7b, 0x7b, 0x7c, 0x5b, 0x7d, 0xf4, 0x7d, 0x3e,
+ 0x7f, 0x05, 0x80, 0x52, 0x83, 0xef, 0x83, 0x79,
+ 0x87, 0x41, 0x89, 0x86, 0x89, 0x96, 0x89, 0xbf,
+ 0x8a, 0xf8, 0x8a, 0xcb, 0x8a, 0x01, 0x8b, 0xfe,
+ 0x8a, 0xed, 0x8a, 0x39, 0x8b, 0x8a, 0x8b, 0x08,
+ 0x8d, 0x38, 0x8f, 0x72, 0x90, 0x99, 0x91, 0x76,
+ 0x92, 0x7c, 0x96, 0xe3, 0x96, 0x56, 0x97, 0xdb,
+ 0x97, 0xff, 0x97, 0x0b, 0x98, 0x3b, 0x98, 0x12,
+ 0x9b, 0x9c, 0x9f, 0x4a, 0x28, 0x44, 0x28, 0xd5,
+ 0x33, 0x9d, 0x3b, 0x18, 0x40, 0x39, 0x40, 0x49,
+ 0x52, 0xd0, 0x5c, 0xd3, 0x7e, 0x43, 0x9f, 0x8e,
+ 0x9f, 0x2a, 0xa0, 0x02, 0x66, 0x66, 0x66, 0x69,
+ 0x66, 0x6c, 0x66, 0x66, 0x69, 0x66, 0x66, 0x6c,
+ 0x7f, 0x01, 0x74, 0x73, 0x00, 0x74, 0x65, 0x05,
+ 0x0f, 0x11, 0x0f, 0x00, 0x0f, 0x06, 0x19, 0x11,
+ 0x0f, 0x08, 0xd9, 0x05, 0xb4, 0x05, 0x00, 0x00,
+ 0x00, 0x00, 0xf2, 0x05, 0xb7, 0x05, 0xd0, 0x05,
+ 0x12, 0x00, 0x03, 0x04, 0x0b, 0x0c, 0x0d, 0x18,
+ 0x1a, 0xe9, 0x05, 0xc1, 0x05, 0xe9, 0x05, 0xc2,
+ 0x05, 0x49, 0xfb, 0xc1, 0x05, 0x49, 0xfb, 0xc2,
+ 0x05, 0xd0, 0x05, 0xb7, 0x05, 0xd0, 0x05, 0xb8,
+ 0x05, 0xd0, 0x05, 0xbc, 0x05, 0xd8, 0x05, 0xbc,
+ 0x05, 0xde, 0x05, 0xbc, 0x05, 0xe0, 0x05, 0xbc,
+ 0x05, 0xe3, 0x05, 0xbc, 0x05, 0xb9, 0x05, 0x2d,
+ 0x03, 0x2e, 0x03, 0x2f, 0x03, 0x30, 0x03, 0x31,
+ 0x03, 0x1c, 0x00, 0x18, 0x06, 0x22, 0x06, 0x2b,
+ 0x06, 0xd0, 0x05, 0xdc, 0x05, 0x71, 0x06, 0x00,
+ 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0f, 0x0f, 0x0f, 0x0f, 0x09, 0x09, 0x09,
+ 0x09, 0x0e, 0x0e, 0x0e, 0x0e, 0x08, 0x08, 0x08,
+ 0x08, 0x33, 0x33, 0x33, 0x33, 0x35, 0x35, 0x35,
+ 0x35, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12,
+ 0x12, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16,
+ 0x16, 0x1c, 0x1c, 0x1b, 0x1b, 0x1d, 0x1d, 0x17,
+ 0x17, 0x27, 0x27, 0x20, 0x20, 0x38, 0x38, 0x38,
+ 0x38, 0x3e, 0x3e, 0x3e, 0x3e, 0x42, 0x42, 0x42,
+ 0x42, 0x40, 0x40, 0x40, 0x40, 0x49, 0x49, 0x4a,
+ 0x4a, 0x4a, 0x4a, 0x4f, 0x4f, 0x50, 0x50, 0x50,
+ 0x50, 0x4d, 0x4d, 0x4d, 0x4d, 0x61, 0x61, 0x62,
+ 0x62, 0x49, 0x06, 0x64, 0x64, 0x64, 0x64, 0x7e,
+ 0x7e, 0x7d, 0x7d, 0x7f, 0x7f, 0x2e, 0x82, 0x82,
+ 0x7c, 0x7c, 0x80, 0x80, 0x87, 0x87, 0x87, 0x87,
+ 0x00, 0x00, 0x26, 0x06, 0x00, 0x01, 0x00, 0x01,
+ 0x00, 0xaf, 0x00, 0xaf, 0x00, 0x22, 0x00, 0x22,
+ 0x00, 0xa1, 0x00, 0xa1, 0x00, 0xa0, 0x00, 0xa0,
+ 0x00, 0xa2, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xaa,
+ 0x00, 0xaa, 0x00, 0x23, 0x00, 0x23, 0x00, 0x23,
+ 0xcc, 0x06, 0x00, 0x00, 0x00, 0x00, 0x26, 0x06,
+ 0x00, 0x06, 0x00, 0x07, 0x00, 0x1f, 0x00, 0x23,
+ 0x00, 0x24, 0x02, 0x06, 0x02, 0x07, 0x02, 0x08,
+ 0x02, 0x1f, 0x02, 0x23, 0x02, 0x24, 0x04, 0x06,
+ 0x04, 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x23,
+ 0x04, 0x24, 0x05, 0x06, 0x05, 0x1f, 0x05, 0x23,
+ 0x05, 0x24, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06,
+ 0x07, 0x1f, 0x08, 0x06, 0x08, 0x07, 0x08, 0x1f,
+ 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x08, 0x0d, 0x1f,
+ 0x0f, 0x07, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07,
+ 0x10, 0x08, 0x10, 0x1f, 0x11, 0x07, 0x11, 0x1f,
+ 0x12, 0x1f, 0x13, 0x06, 0x13, 0x1f, 0x14, 0x06,
+ 0x14, 0x1f, 0x1b, 0x06, 0x1b, 0x07, 0x1b, 0x08,
+ 0x1b, 0x1f, 0x1b, 0x23, 0x1b, 0x24, 0x1c, 0x07,
+ 0x1c, 0x1f, 0x1c, 0x23, 0x1c, 0x24, 0x1d, 0x01,
+ 0x1d, 0x06, 0x1d, 0x07, 0x1d, 0x08, 0x1d, 0x1e,
+ 0x1d, 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x06,
+ 0x1e, 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x23,
+ 0x1e, 0x24, 0x1f, 0x06, 0x1f, 0x07, 0x1f, 0x08,
+ 0x1f, 0x1f, 0x1f, 0x23, 0x1f, 0x24, 0x20, 0x06,
+ 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20, 0x23,
+ 0x20, 0x24, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x23,
+ 0x21, 0x24, 0x24, 0x06, 0x24, 0x07, 0x24, 0x08,
+ 0x24, 0x1f, 0x24, 0x23, 0x24, 0x24, 0x0a, 0x4a,
+ 0x0b, 0x4a, 0x23, 0x4a, 0x20, 0x00, 0x4c, 0x06,
+ 0x51, 0x06, 0x51, 0x06, 0xff, 0x00, 0x1f, 0x26,
+ 0x06, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x1f, 0x00,
+ 0x20, 0x00, 0x23, 0x00, 0x24, 0x02, 0x0b, 0x02,
+ 0x0c, 0x02, 0x1f, 0x02, 0x20, 0x02, 0x23, 0x02,
+ 0x24, 0x04, 0x0b, 0x04, 0x0c, 0x04, 0x1f, 0x26,
+ 0x06, 0x04, 0x20, 0x04, 0x23, 0x04, 0x24, 0x05,
+ 0x0b, 0x05, 0x0c, 0x05, 0x1f, 0x05, 0x20, 0x05,
+ 0x23, 0x05, 0x24, 0x1b, 0x23, 0x1b, 0x24, 0x1c,
+ 0x23, 0x1c, 0x24, 0x1d, 0x01, 0x1d, 0x1e, 0x1d,
+ 0x1f, 0x1d, 0x23, 0x1d, 0x24, 0x1e, 0x1f, 0x1e,
+ 0x23, 0x1e, 0x24, 0x1f, 0x01, 0x1f, 0x1f, 0x20,
+ 0x0b, 0x20, 0x0c, 0x20, 0x1f, 0x20, 0x20, 0x20,
+ 0x23, 0x20, 0x24, 0x23, 0x4a, 0x24, 0x0b, 0x24,
+ 0x0c, 0x24, 0x1f, 0x24, 0x20, 0x24, 0x23, 0x24,
+ 0x24, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00,
+ 0x1f, 0x00, 0x21, 0x02, 0x06, 0x02, 0x07, 0x02,
+ 0x08, 0x02, 0x1f, 0x02, 0x21, 0x04, 0x06, 0x04,
+ 0x07, 0x04, 0x08, 0x04, 0x1f, 0x04, 0x21, 0x05,
+ 0x1f, 0x06, 0x07, 0x06, 0x1f, 0x07, 0x06, 0x07,
+ 0x1f, 0x08, 0x06, 0x08, 0x1f, 0x0d, 0x06, 0x0d,
+ 0x07, 0x0d, 0x08, 0x0d, 0x1f, 0x0f, 0x07, 0x0f,
+ 0x08, 0x0f, 0x1f, 0x10, 0x06, 0x10, 0x07, 0x10,
+ 0x08, 0x10, 0x1f, 0x11, 0x07, 0x12, 0x1f, 0x13,
+ 0x06, 0x13, 0x1f, 0x14, 0x06, 0x14, 0x1f, 0x1b,
+ 0x06, 0x1b, 0x07, 0x1b, 0x08, 0x1b, 0x1f, 0x1c,
+ 0x07, 0x1c, 0x1f, 0x1d, 0x06, 0x1d, 0x07, 0x1d,
+ 0x08, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x06, 0x1e,
+ 0x07, 0x1e, 0x08, 0x1e, 0x1f, 0x1e, 0x21, 0x1f,
+ 0x06, 0x1f, 0x07, 0x1f, 0x08, 0x1f, 0x1f, 0x20,
+ 0x06, 0x20, 0x07, 0x20, 0x08, 0x20, 0x1f, 0x20,
+ 0x21, 0x21, 0x06, 0x21, 0x1f, 0x21, 0x4a, 0x24,
+ 0x06, 0x24, 0x07, 0x24, 0x08, 0x24, 0x1f, 0x24,
+ 0x21, 0x00, 0x1f, 0x00, 0x21, 0x02, 0x1f, 0x02,
+ 0x21, 0x04, 0x1f, 0x04, 0x21, 0x05, 0x1f, 0x05,
+ 0x21, 0x0d, 0x1f, 0x0d, 0x21, 0x0e, 0x1f, 0x0e,
+ 0x21, 0x1d, 0x1e, 0x1d, 0x1f, 0x1e, 0x1f, 0x20,
+ 0x1f, 0x20, 0x21, 0x24, 0x1f, 0x24, 0x21, 0x40,
+ 0x06, 0x4e, 0x06, 0x51, 0x06, 0x27, 0x06, 0x10,
+ 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13,
+ 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d,
+ 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05,
+ 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e,
+ 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d,
+ 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d,
+ 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x10,
+ 0x22, 0x10, 0x23, 0x12, 0x22, 0x12, 0x23, 0x13,
+ 0x22, 0x13, 0x23, 0x0c, 0x22, 0x0c, 0x23, 0x0d,
+ 0x22, 0x0d, 0x23, 0x06, 0x22, 0x06, 0x23, 0x05,
+ 0x22, 0x05, 0x23, 0x07, 0x22, 0x07, 0x23, 0x0e,
+ 0x22, 0x0e, 0x23, 0x0f, 0x22, 0x0f, 0x23, 0x0d,
+ 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0d,
+ 0x0a, 0x0c, 0x0a, 0x0e, 0x0a, 0x0f, 0x0a, 0x0d,
+ 0x05, 0x0d, 0x06, 0x0d, 0x07, 0x0d, 0x1e, 0x0c,
+ 0x20, 0x0d, 0x20, 0x10, 0x1e, 0x0c, 0x05, 0x0c,
+ 0x06, 0x0c, 0x07, 0x0d, 0x05, 0x0d, 0x06, 0x0d,
+ 0x07, 0x10, 0x1e, 0x11, 0x1e, 0x00, 0x24, 0x00,
+ 0x24, 0x2a, 0x06, 0x00, 0x02, 0x1b, 0x00, 0x03,
+ 0x02, 0x00, 0x03, 0x02, 0x00, 0x03, 0x1b, 0x00,
+ 0x04, 0x1b, 0x00, 0x1b, 0x02, 0x00, 0x1b, 0x03,
+ 0x00, 0x1b, 0x04, 0x02, 0x1b, 0x03, 0x02, 0x1b,
+ 0x03, 0x03, 0x1b, 0x20, 0x03, 0x1b, 0x1f, 0x09,
+ 0x03, 0x02, 0x09, 0x02, 0x03, 0x09, 0x02, 0x1f,
+ 0x09, 0x1b, 0x03, 0x09, 0x1b, 0x03, 0x09, 0x1b,
+ 0x02, 0x09, 0x1b, 0x1b, 0x09, 0x1b, 0x1b, 0x0b,
+ 0x03, 0x03, 0x0b, 0x03, 0x03, 0x0b, 0x1b, 0x1b,
+ 0x0a, 0x03, 0x1b, 0x0a, 0x03, 0x1b, 0x0a, 0x02,
+ 0x20, 0x0a, 0x1b, 0x04, 0x0a, 0x1b, 0x04, 0x0a,
+ 0x1b, 0x1b, 0x0a, 0x1b, 0x1b, 0x0c, 0x03, 0x1f,
+ 0x0c, 0x04, 0x1b, 0x0c, 0x04, 0x1b, 0x0d, 0x1b,
+ 0x03, 0x0d, 0x1b, 0x03, 0x0d, 0x1b, 0x1b, 0x0d,
+ 0x1b, 0x20, 0x0f, 0x02, 0x1b, 0x0f, 0x1b, 0x1b,
+ 0x0f, 0x1b, 0x1b, 0x0f, 0x1b, 0x1f, 0x10, 0x1b,
+ 0x1b, 0x10, 0x1b, 0x20, 0x10, 0x1b, 0x1f, 0x17,
+ 0x04, 0x1b, 0x17, 0x04, 0x1b, 0x18, 0x1b, 0x03,
+ 0x18, 0x1b, 0x1b, 0x1a, 0x03, 0x1b, 0x1a, 0x03,
+ 0x20, 0x1a, 0x03, 0x1f, 0x1a, 0x02, 0x02, 0x1a,
+ 0x02, 0x02, 0x1a, 0x04, 0x1b, 0x1a, 0x04, 0x1b,
+ 0x1a, 0x1b, 0x03, 0x1a, 0x1b, 0x03, 0x1b, 0x03,
+ 0x02, 0x1b, 0x03, 0x1b, 0x1b, 0x03, 0x20, 0x1b,
+ 0x02, 0x03, 0x1b, 0x02, 0x1b, 0x1b, 0x04, 0x02,
+ 0x1b, 0x04, 0x1b, 0x28, 0x06, 0x1d, 0x04, 0x06,
+ 0x1f, 0x1d, 0x04, 0x1f, 0x1d, 0x1d, 0x1e, 0x05,
+ 0x1d, 0x1e, 0x05, 0x21, 0x1e, 0x04, 0x1d, 0x1e,
+ 0x04, 0x1d, 0x1e, 0x04, 0x21, 0x1e, 0x1d, 0x22,
+ 0x1e, 0x1d, 0x21, 0x22, 0x1d, 0x1d, 0x22, 0x1d,
+ 0x1d, 0x00, 0x06, 0x22, 0x02, 0x04, 0x22, 0x02,
+ 0x04, 0x21, 0x02, 0x06, 0x22, 0x02, 0x06, 0x21,
+ 0x02, 0x1d, 0x22, 0x02, 0x1d, 0x21, 0x04, 0x1d,
+ 0x22, 0x04, 0x05, 0x21, 0x04, 0x1d, 0x21, 0x0b,
+ 0x06, 0x21, 0x0d, 0x05, 0x22, 0x0c, 0x05, 0x22,
+ 0x0e, 0x05, 0x22, 0x1c, 0x04, 0x22, 0x1c, 0x1d,
+ 0x22, 0x22, 0x05, 0x22, 0x22, 0x04, 0x22, 0x22,
+ 0x1d, 0x22, 0x1d, 0x1d, 0x22, 0x1a, 0x1d, 0x22,
+ 0x1e, 0x05, 0x22, 0x1a, 0x1d, 0x05, 0x1c, 0x05,
+ 0x1d, 0x11, 0x1d, 0x22, 0x1b, 0x1d, 0x22, 0x1e,
+ 0x04, 0x05, 0x1d, 0x06, 0x22, 0x1c, 0x04, 0x1d,
+ 0x1b, 0x1d, 0x1d, 0x1c, 0x04, 0x1d, 0x1e, 0x04,
+ 0x05, 0x04, 0x05, 0x22, 0x05, 0x04, 0x22, 0x1d,
+ 0x04, 0x22, 0x19, 0x1d, 0x22, 0x00, 0x05, 0x22,
+ 0x1b, 0x1d, 0x1d, 0x11, 0x04, 0x1d, 0x0d, 0x1d,
+ 0x1d, 0x0b, 0x06, 0x22, 0x1e, 0x04, 0x22, 0x35,
+ 0x06, 0x00, 0x0f, 0x9d, 0x0d, 0x0f, 0x9d, 0x27,
+ 0x06, 0x00, 0x1d, 0x1d, 0x20, 0x00, 0x1c, 0x01,
+ 0x0a, 0x1e, 0x06, 0x1e, 0x08, 0x0e, 0x1d, 0x12,
+ 0x1e, 0x0a, 0x0c, 0x21, 0x1d, 0x12, 0x1d, 0x23,
+ 0x20, 0x21, 0x0c, 0x1d, 0x1e, 0x35, 0x06, 0x00,
+ 0x0f, 0x14, 0x27, 0x06, 0x0e, 0x1d, 0x22, 0xff,
+ 0x00, 0x1d, 0x1d, 0x20, 0xff, 0x12, 0x1d, 0x23,
+ 0x20, 0xff, 0x21, 0x0c, 0x1d, 0x1e, 0x27, 0x06,
+ 0x05, 0x1d, 0xff, 0x05, 0x1d, 0x00, 0x1d, 0x20,
+ 0x27, 0x06, 0x0a, 0xa5, 0x00, 0x1d, 0x2c, 0x00,
+ 0x01, 0x30, 0x02, 0x30, 0x3a, 0x00, 0x3b, 0x00,
+ 0x21, 0x00, 0x3f, 0x00, 0x16, 0x30, 0x17, 0x30,
+ 0x26, 0x20, 0x13, 0x20, 0x12, 0x01, 0x00, 0x5f,
+ 0x5f, 0x28, 0x29, 0x7b, 0x7d, 0x08, 0x30, 0x0c,
+ 0x0d, 0x08, 0x09, 0x02, 0x03, 0x00, 0x01, 0x04,
+ 0x05, 0x06, 0x07, 0x5b, 0x00, 0x5d, 0x00, 0x3e,
+ 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x3e, 0x20, 0x5f,
+ 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x2c, 0x00, 0x01,
+ 0x30, 0x2e, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x3a,
+ 0x00, 0x3f, 0x00, 0x21, 0x00, 0x14, 0x20, 0x28,
+ 0x00, 0x29, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x14,
+ 0x30, 0x15, 0x30, 0x23, 0x26, 0x2a, 0x2b, 0x2d,
+ 0x3c, 0x3e, 0x3d, 0x00, 0x5c, 0x24, 0x25, 0x40,
+ 0x40, 0x06, 0xff, 0x0b, 0x00, 0x0b, 0xff, 0x0c,
+ 0x20, 0x00, 0x4d, 0x06, 0x40, 0x06, 0xff, 0x0e,
+ 0x00, 0x0e, 0xff, 0x0f, 0x00, 0x0f, 0xff, 0x10,
+ 0x00, 0x10, 0xff, 0x11, 0x00, 0x11, 0xff, 0x12,
+ 0x00, 0x12, 0x21, 0x06, 0x00, 0x01, 0x01, 0x02,
+ 0x02, 0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x05,
+ 0x05, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x08,
+ 0x08, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0f,
+ 0x0f, 0x10, 0x10, 0x11, 0x11, 0x12, 0x12, 0x12,
+ 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14,
+ 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16,
+ 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18,
+ 0x18, 0x19, 0x19, 0x19, 0x19, 0x20, 0x20, 0x20,
+ 0x20, 0x21, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22,
+ 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24,
+ 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26,
+ 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x29,
+ 0x29, 0x22, 0x06, 0x22, 0x00, 0x22, 0x00, 0x22,
+ 0x01, 0x22, 0x01, 0x22, 0x03, 0x22, 0x03, 0x22,
+ 0x05, 0x22, 0x05, 0x21, 0x00, 0x85, 0x29, 0x01,
+ 0x30, 0x01, 0x0b, 0x0c, 0x00, 0xfa, 0xf1, 0xa0,
+ 0xa2, 0xa4, 0xa6, 0xa8, 0xe2, 0xe4, 0xe6, 0xc2,
+ 0xfb, 0xa1, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac,
+ 0xae, 0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc,
+ 0xbe, 0xc0, 0xc3, 0xc5, 0xc7, 0xc9, 0xca, 0xcb,
+ 0xcc, 0xcd, 0xce, 0xd1, 0xd4, 0xd7, 0xda, 0xdd,
+ 0xde, 0xdf, 0xe0, 0xe1, 0xe3, 0xe5, 0xe7, 0xe8,
+ 0xe9, 0xea, 0xeb, 0xec, 0xee, 0xf2, 0x98, 0x99,
+ 0x31, 0x31, 0x4f, 0x31, 0x55, 0x31, 0x5b, 0x31,
+ 0x61, 0x31, 0xa2, 0x00, 0xa3, 0x00, 0xac, 0x00,
+ 0xaf, 0x00, 0xa6, 0x00, 0xa5, 0x00, 0xa9, 0x20,
+ 0x00, 0x00, 0x02, 0x25, 0x90, 0x21, 0x91, 0x21,
+ 0x92, 0x21, 0x93, 0x21, 0xa0, 0x25, 0xcb, 0x25,
+ 0xd0, 0x02, 0xd1, 0x02, 0xe6, 0x00, 0x99, 0x02,
+ 0x53, 0x02, 0x00, 0x00, 0xa3, 0x02, 0x66, 0xab,
+ 0xa5, 0x02, 0xa4, 0x02, 0x56, 0x02, 0x57, 0x02,
+ 0x91, 0x1d, 0x58, 0x02, 0x5e, 0x02, 0xa9, 0x02,
+ 0x64, 0x02, 0x62, 0x02, 0x60, 0x02, 0x9b, 0x02,
+ 0x27, 0x01, 0x9c, 0x02, 0x67, 0x02, 0x84, 0x02,
+ 0xaa, 0x02, 0xab, 0x02, 0x6c, 0x02, 0x04, 0xdf,
+ 0x8e, 0xa7, 0x6e, 0x02, 0x05, 0xdf, 0x8e, 0x02,
+ 0x06, 0xdf, 0xf8, 0x00, 0x76, 0x02, 0x77, 0x02,
+ 0x71, 0x00, 0x7a, 0x02, 0x08, 0xdf, 0x7d, 0x02,
+ 0x7e, 0x02, 0x80, 0x02, 0xa8, 0x02, 0xa6, 0x02,
+ 0x67, 0xab, 0xa7, 0x02, 0x88, 0x02, 0x71, 0x2c,
+ 0x00, 0x00, 0x8f, 0x02, 0xa1, 0x02, 0xa2, 0x02,
+ 0x98, 0x02, 0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01,
+ 0x0a, 0xdf, 0x1e, 0xdf, 0x41, 0x04, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x14, 0x99, 0x10, 0xba, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x9b, 0x10, 0xba, 0x10,
+ 0x05, 0x05, 0xa5, 0x10, 0xba, 0x10, 0x05, 0x31,
+ 0x11, 0x27, 0x11, 0x32, 0x11, 0x27, 0x11, 0x55,
+ 0x47, 0x13, 0x3e, 0x13, 0x47, 0x13, 0x57, 0x13,
+ 0x55, 0xb9, 0x14, 0xba, 0x14, 0xb9, 0x14, 0xb0,
+ 0x14, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x14, 0xbd,
+ 0x14, 0x55, 0x50, 0xb8, 0x15, 0xaf, 0x15, 0xb9,
+ 0x15, 0xaf, 0x15, 0x55, 0x35, 0x19, 0x30, 0x19,
+ 0x05, 0x57, 0xd1, 0x65, 0xd1, 0x58, 0xd1, 0x65,
+ 0xd1, 0x5f, 0xd1, 0x6e, 0xd1, 0x5f, 0xd1, 0x6f,
+ 0xd1, 0x5f, 0xd1, 0x70, 0xd1, 0x5f, 0xd1, 0x71,
+ 0xd1, 0x5f, 0xd1, 0x72, 0xd1, 0x55, 0x55, 0x55,
+ 0x05, 0xb9, 0xd1, 0x65, 0xd1, 0xba, 0xd1, 0x65,
+ 0xd1, 0xbb, 0xd1, 0x6e, 0xd1, 0xbc, 0xd1, 0x6e,
+ 0xd1, 0xbb, 0xd1, 0x6f, 0xd1, 0xbc, 0xd1, 0x6f,
+ 0xd1, 0x55, 0x55, 0x55, 0x41, 0x00, 0x61, 0x00,
+ 0x41, 0x00, 0x61, 0x00, 0x69, 0x00, 0x41, 0x00,
+ 0x61, 0x00, 0x41, 0x00, 0x43, 0x44, 0x00, 0x00,
+ 0x47, 0x00, 0x00, 0x4a, 0x4b, 0x00, 0x00, 0x4e,
+ 0x4f, 0x50, 0x51, 0x00, 0x53, 0x54, 0x55, 0x56,
+ 0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64,
+ 0x00, 0x66, 0x68, 0x00, 0x70, 0x00, 0x41, 0x00,
+ 0x61, 0x00, 0x41, 0x42, 0x00, 0x44, 0x45, 0x46,
+ 0x47, 0x4a, 0x00, 0x53, 0x00, 0x61, 0x00, 0x41,
+ 0x42, 0x00, 0x44, 0x45, 0x46, 0x47, 0x00, 0x49,
+ 0x4a, 0x4b, 0x4c, 0x4d, 0x00, 0x4f, 0x53, 0x00,
+ 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00,
+ 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00,
+ 0x61, 0x00, 0x41, 0x00, 0x61, 0x00, 0x41, 0x00,
+ 0x61, 0x00, 0x31, 0x01, 0x37, 0x02, 0x91, 0x03,
+ 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00,
+ 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03,
+ 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04,
+ 0x20, 0x05, 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03,
+ 0xd1, 0x03, 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05,
+ 0x91, 0x03, 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03,
+ 0x24, 0x00, 0x1f, 0x04, 0x20, 0x05, 0x91, 0x03,
+ 0xa3, 0x03, 0xb1, 0x03, 0xd1, 0x03, 0x24, 0x00,
+ 0x1f, 0x04, 0x20, 0x05, 0x0b, 0x0c, 0x30, 0x00,
+ 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00,
+ 0x27, 0x06, 0x00, 0x01, 0x05, 0x08, 0x2a, 0x06,
+ 0x1e, 0x08, 0x03, 0x0d, 0x20, 0x19, 0x1a, 0x1b,
+ 0x1c, 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a,
+ 0x00, 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x44,
+ 0x90, 0x77, 0x45, 0x28, 0x06, 0x2c, 0x06, 0x00,
+ 0x00, 0x47, 0x06, 0x33, 0x06, 0x17, 0x10, 0x11,
+ 0x12, 0x13, 0x00, 0x06, 0x0e, 0x02, 0x0f, 0x34,
+ 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e, 0x06, 0x00,
+ 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a, 0x06, 0x2d,
+ 0x06, 0x00, 0x00, 0x4a, 0x06, 0x00, 0x00, 0x44,
+ 0x06, 0x00, 0x00, 0x46, 0x06, 0x33, 0x06, 0x39,
+ 0x06, 0x00, 0x00, 0x35, 0x06, 0x42, 0x06, 0x00,
+ 0x00, 0x34, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2e,
+ 0x06, 0x00, 0x00, 0x36, 0x06, 0x00, 0x00, 0x3a,
+ 0x06, 0x00, 0x00, 0xba, 0x06, 0x00, 0x00, 0x6f,
+ 0x06, 0x00, 0x00, 0x28, 0x06, 0x2c, 0x06, 0x00,
+ 0x00, 0x47, 0x06, 0x00, 0x00, 0x00, 0x00, 0x2d,
+ 0x06, 0x37, 0x06, 0x4a, 0x06, 0x43, 0x06, 0x00,
+ 0x00, 0x45, 0x06, 0x46, 0x06, 0x33, 0x06, 0x39,
+ 0x06, 0x41, 0x06, 0x35, 0x06, 0x42, 0x06, 0x00,
+ 0x00, 0x34, 0x06, 0x2a, 0x06, 0x2b, 0x06, 0x2e,
+ 0x06, 0x00, 0x00, 0x36, 0x06, 0x38, 0x06, 0x3a,
+ 0x06, 0x6e, 0x06, 0x00, 0x00, 0xa1, 0x06, 0x27,
+ 0x06, 0x00, 0x01, 0x05, 0x08, 0x20, 0x21, 0x0b,
+ 0x06, 0x10, 0x23, 0x2a, 0x06, 0x1a, 0x1b, 0x1c,
+ 0x09, 0x0f, 0x17, 0x0b, 0x18, 0x07, 0x0a, 0x00,
+ 0x01, 0x04, 0x06, 0x0c, 0x0e, 0x10, 0x28, 0x06,
+ 0x2c, 0x06, 0x2f, 0x06, 0x00, 0x00, 0x48, 0x06,
+ 0x32, 0x06, 0x2d, 0x06, 0x37, 0x06, 0x4a, 0x06,
+ 0x2a, 0x06, 0x1a, 0x1b, 0x1c, 0x09, 0x0f, 0x17,
+ 0x0b, 0x18, 0x07, 0x0a, 0x00, 0x01, 0x04, 0x06,
+ 0x0c, 0x0e, 0x10, 0x30, 0x2e, 0x30, 0x00, 0x2c,
+ 0x00, 0x28, 0x00, 0x41, 0x00, 0x29, 0x00, 0x14,
+ 0x30, 0x53, 0x00, 0x15, 0x30, 0x43, 0x52, 0x43,
+ 0x44, 0x57, 0x5a, 0x41, 0x00, 0x48, 0x56, 0x4d,
+ 0x56, 0x53, 0x44, 0x53, 0x53, 0x50, 0x50, 0x56,
+ 0x57, 0x43, 0x4d, 0x43, 0x4d, 0x44, 0x4d, 0x52,
+ 0x44, 0x4a, 0x4b, 0x30, 0x30, 0x00, 0x68, 0x68,
+ 0x4b, 0x62, 0x57, 0x5b, 0xcc, 0x53, 0xc7, 0x30,
+ 0x8c, 0x4e, 0x1a, 0x59, 0xe3, 0x89, 0x29, 0x59,
+ 0xa4, 0x4e, 0x20, 0x66, 0x21, 0x71, 0x99, 0x65,
+ 0x4d, 0x52, 0x8c, 0x5f, 0x8d, 0x51, 0xb0, 0x65,
+ 0x1d, 0x52, 0x42, 0x7d, 0x1f, 0x75, 0xa9, 0x8c,
+ 0xf0, 0x58, 0x39, 0x54, 0x14, 0x6f, 0x95, 0x62,
+ 0x55, 0x63, 0x00, 0x4e, 0x09, 0x4e, 0x4a, 0x90,
+ 0xe6, 0x5d, 0x2d, 0x4e, 0xf3, 0x53, 0x07, 0x63,
+ 0x70, 0x8d, 0x53, 0x62, 0x81, 0x79, 0x7a, 0x7a,
+ 0x08, 0x54, 0x80, 0x6e, 0x09, 0x67, 0x08, 0x67,
+ 0x33, 0x75, 0x72, 0x52, 0xb6, 0x55, 0x4d, 0x91,
+ 0x14, 0x30, 0x15, 0x30, 0x2c, 0x67, 0x09, 0x4e,
+ 0x8c, 0x4e, 0x89, 0x5b, 0xb9, 0x70, 0x53, 0x62,
+ 0xd7, 0x76, 0xdd, 0x52, 0x57, 0x65, 0x97, 0x5f,
+ 0xef, 0x53, 0x30, 0x00, 0x38, 0x4e, 0x05, 0x00,
+ 0x09, 0x22, 0x01, 0x60, 0x4f, 0xae, 0x4f, 0xbb,
+ 0x4f, 0x02, 0x50, 0x7a, 0x50, 0x99, 0x50, 0xe7,
+ 0x50, 0xcf, 0x50, 0x9e, 0x34, 0x3a, 0x06, 0x4d,
+ 0x51, 0x54, 0x51, 0x64, 0x51, 0x77, 0x51, 0x1c,
+ 0x05, 0xb9, 0x34, 0x67, 0x51, 0x8d, 0x51, 0x4b,
+ 0x05, 0x97, 0x51, 0xa4, 0x51, 0xcc, 0x4e, 0xac,
+ 0x51, 0xb5, 0x51, 0xdf, 0x91, 0xf5, 0x51, 0x03,
+ 0x52, 0xdf, 0x34, 0x3b, 0x52, 0x46, 0x52, 0x72,
+ 0x52, 0x77, 0x52, 0x15, 0x35, 0x02, 0x00, 0x20,
+ 0x80, 0x80, 0x00, 0x08, 0x00, 0x00, 0xc7, 0x52,
+ 0x00, 0x02, 0x1d, 0x33, 0x3e, 0x3f, 0x50, 0x82,
+ 0x8a, 0x93, 0xac, 0xb6, 0xb8, 0xb8, 0xb8, 0x2c,
+ 0x0a, 0x70, 0x70, 0xca, 0x53, 0xdf, 0x53, 0x63,
+ 0x0b, 0xeb, 0x53, 0xf1, 0x53, 0x06, 0x54, 0x9e,
+ 0x54, 0x38, 0x54, 0x48, 0x54, 0x68, 0x54, 0xa2,
+ 0x54, 0xf6, 0x54, 0x10, 0x55, 0x53, 0x55, 0x63,
+ 0x55, 0x84, 0x55, 0x84, 0x55, 0x99, 0x55, 0xab,
+ 0x55, 0xb3, 0x55, 0xc2, 0x55, 0x16, 0x57, 0x06,
+ 0x56, 0x17, 0x57, 0x51, 0x56, 0x74, 0x56, 0x07,
+ 0x52, 0xee, 0x58, 0xce, 0x57, 0xf4, 0x57, 0x0d,
+ 0x58, 0x8b, 0x57, 0x32, 0x58, 0x31, 0x58, 0xac,
+ 0x58, 0xe4, 0x14, 0xf2, 0x58, 0xf7, 0x58, 0x06,
+ 0x59, 0x1a, 0x59, 0x22, 0x59, 0x62, 0x59, 0xa8,
+ 0x16, 0xea, 0x16, 0xec, 0x59, 0x1b, 0x5a, 0x27,
+ 0x5a, 0xd8, 0x59, 0x66, 0x5a, 0xee, 0x36, 0xfc,
+ 0x36, 0x08, 0x5b, 0x3e, 0x5b, 0x3e, 0x5b, 0xc8,
+ 0x19, 0xc3, 0x5b, 0xd8, 0x5b, 0xe7, 0x5b, 0xf3,
+ 0x5b, 0x18, 0x1b, 0xff, 0x5b, 0x06, 0x5c, 0x53,
+ 0x5f, 0x22, 0x5c, 0x81, 0x37, 0x60, 0x5c, 0x6e,
+ 0x5c, 0xc0, 0x5c, 0x8d, 0x5c, 0xe4, 0x1d, 0x43,
+ 0x5d, 0xe6, 0x1d, 0x6e, 0x5d, 0x6b, 0x5d, 0x7c,
+ 0x5d, 0xe1, 0x5d, 0xe2, 0x5d, 0x2f, 0x38, 0xfd,
+ 0x5d, 0x28, 0x5e, 0x3d, 0x5e, 0x69, 0x5e, 0x62,
+ 0x38, 0x83, 0x21, 0x7c, 0x38, 0xb0, 0x5e, 0xb3,
+ 0x5e, 0xb6, 0x5e, 0xca, 0x5e, 0x92, 0xa3, 0xfe,
+ 0x5e, 0x31, 0x23, 0x31, 0x23, 0x01, 0x82, 0x22,
+ 0x5f, 0x22, 0x5f, 0xc7, 0x38, 0xb8, 0x32, 0xda,
+ 0x61, 0x62, 0x5f, 0x6b, 0x5f, 0xe3, 0x38, 0x9a,
+ 0x5f, 0xcd, 0x5f, 0xd7, 0x5f, 0xf9, 0x5f, 0x81,
+ 0x60, 0x3a, 0x39, 0x1c, 0x39, 0x94, 0x60, 0xd4,
+ 0x26, 0xc7, 0x60, 0x02, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0a, 0x00,
+ 0x00, 0x02, 0x08, 0x00, 0x80, 0x08, 0x00, 0x00,
+ 0x08, 0x80, 0x28, 0x80, 0x02, 0x00, 0x00, 0x02,
+ 0x48, 0x61, 0x00, 0x04, 0x06, 0x04, 0x32, 0x46,
+ 0x6a, 0x5c, 0x67, 0x96, 0xaa, 0xae, 0xc8, 0xd3,
+ 0x5d, 0x62, 0x00, 0x54, 0x77, 0xf3, 0x0c, 0x2b,
+ 0x3d, 0x63, 0xfc, 0x62, 0x68, 0x63, 0x83, 0x63,
+ 0xe4, 0x63, 0xf1, 0x2b, 0x22, 0x64, 0xc5, 0x63,
+ 0xa9, 0x63, 0x2e, 0x3a, 0x69, 0x64, 0x7e, 0x64,
+ 0x9d, 0x64, 0x77, 0x64, 0x6c, 0x3a, 0x4f, 0x65,
+ 0x6c, 0x65, 0x0a, 0x30, 0xe3, 0x65, 0xf8, 0x66,
+ 0x49, 0x66, 0x19, 0x3b, 0x91, 0x66, 0x08, 0x3b,
+ 0xe4, 0x3a, 0x92, 0x51, 0x95, 0x51, 0x00, 0x67,
+ 0x9c, 0x66, 0xad, 0x80, 0xd9, 0x43, 0x17, 0x67,
+ 0x1b, 0x67, 0x21, 0x67, 0x5e, 0x67, 0x53, 0x67,
+ 0xc3, 0x33, 0x49, 0x3b, 0xfa, 0x67, 0x85, 0x67,
+ 0x52, 0x68, 0x85, 0x68, 0x6d, 0x34, 0x8e, 0x68,
+ 0x1f, 0x68, 0x14, 0x69, 0x9d, 0x3b, 0x42, 0x69,
+ 0xa3, 0x69, 0xea, 0x69, 0xa8, 0x6a, 0xa3, 0x36,
+ 0xdb, 0x6a, 0x18, 0x3c, 0x21, 0x6b, 0xa7, 0x38,
+ 0x54, 0x6b, 0x4e, 0x3c, 0x72, 0x6b, 0x9f, 0x6b,
+ 0xba, 0x6b, 0xbb, 0x6b, 0x8d, 0x3a, 0x0b, 0x1d,
+ 0xfa, 0x3a, 0x4e, 0x6c, 0xbc, 0x3c, 0xbf, 0x6c,
+ 0xcd, 0x6c, 0x67, 0x6c, 0x16, 0x6d, 0x3e, 0x6d,
+ 0x77, 0x6d, 0x41, 0x6d, 0x69, 0x6d, 0x78, 0x6d,
+ 0x85, 0x6d, 0x1e, 0x3d, 0x34, 0x6d, 0x2f, 0x6e,
+ 0x6e, 0x6e, 0x33, 0x3d, 0xcb, 0x6e, 0xc7, 0x6e,
+ 0xd1, 0x3e, 0xf9, 0x6d, 0x6e, 0x6f, 0x5e, 0x3f,
+ 0x8e, 0x3f, 0xc6, 0x6f, 0x39, 0x70, 0x1e, 0x70,
+ 0x1b, 0x70, 0x96, 0x3d, 0x4a, 0x70, 0x7d, 0x70,
+ 0x77, 0x70, 0xad, 0x70, 0x25, 0x05, 0x45, 0x71,
+ 0x63, 0x42, 0x9c, 0x71, 0xab, 0x43, 0x28, 0x72,
+ 0x35, 0x72, 0x50, 0x72, 0x08, 0x46, 0x80, 0x72,
+ 0x95, 0x72, 0x35, 0x47, 0x02, 0x20, 0x00, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, 0x00,
+ 0x00, 0x02, 0x02, 0x80, 0x8a, 0x00, 0x00, 0x20,
+ 0x00, 0x08, 0x0a, 0x00, 0x80, 0x88, 0x80, 0x20,
+ 0x14, 0x48, 0x7a, 0x73, 0x8b, 0x73, 0xac, 0x3e,
+ 0xa5, 0x73, 0xb8, 0x3e, 0xb8, 0x3e, 0x47, 0x74,
+ 0x5c, 0x74, 0x71, 0x74, 0x85, 0x74, 0xca, 0x74,
+ 0x1b, 0x3f, 0x24, 0x75, 0x36, 0x4c, 0x3e, 0x75,
+ 0x92, 0x4c, 0x70, 0x75, 0x9f, 0x21, 0x10, 0x76,
+ 0xa1, 0x4f, 0xb8, 0x4f, 0x44, 0x50, 0xfc, 0x3f,
+ 0x08, 0x40, 0xf4, 0x76, 0xf3, 0x50, 0xf2, 0x50,
+ 0x19, 0x51, 0x33, 0x51, 0x1e, 0x77, 0x1f, 0x77,
+ 0x1f, 0x77, 0x4a, 0x77, 0x39, 0x40, 0x8b, 0x77,
+ 0x46, 0x40, 0x96, 0x40, 0x1d, 0x54, 0x4e, 0x78,
+ 0x8c, 0x78, 0xcc, 0x78, 0xe3, 0x40, 0x26, 0x56,
+ 0x56, 0x79, 0x9a, 0x56, 0xc5, 0x56, 0x8f, 0x79,
+ 0xeb, 0x79, 0x2f, 0x41, 0x40, 0x7a, 0x4a, 0x7a,
+ 0x4f, 0x7a, 0x7c, 0x59, 0xa7, 0x5a, 0xa7, 0x5a,
+ 0xee, 0x7a, 0x02, 0x42, 0xab, 0x5b, 0xc6, 0x7b,
+ 0xc9, 0x7b, 0x27, 0x42, 0x80, 0x5c, 0xd2, 0x7c,
+ 0xa0, 0x42, 0xe8, 0x7c, 0xe3, 0x7c, 0x00, 0x7d,
+ 0x86, 0x5f, 0x63, 0x7d, 0x01, 0x43, 0xc7, 0x7d,
+ 0x02, 0x7e, 0x45, 0x7e, 0x34, 0x43, 0x28, 0x62,
+ 0x47, 0x62, 0x59, 0x43, 0xd9, 0x62, 0x7a, 0x7f,
+ 0x3e, 0x63, 0x95, 0x7f, 0xfa, 0x7f, 0x05, 0x80,
+ 0xda, 0x64, 0x23, 0x65, 0x60, 0x80, 0xa8, 0x65,
+ 0x70, 0x80, 0x5f, 0x33, 0xd5, 0x43, 0xb2, 0x80,
+ 0x03, 0x81, 0x0b, 0x44, 0x3e, 0x81, 0xb5, 0x5a,
+ 0xa7, 0x67, 0xb5, 0x67, 0x93, 0x33, 0x9c, 0x33,
+ 0x01, 0x82, 0x04, 0x82, 0x9e, 0x8f, 0x6b, 0x44,
+ 0x91, 0x82, 0x8b, 0x82, 0x9d, 0x82, 0xb3, 0x52,
+ 0xb1, 0x82, 0xb3, 0x82, 0xbd, 0x82, 0xe6, 0x82,
+ 0x3c, 0x6b, 0xe5, 0x82, 0x1d, 0x83, 0x63, 0x83,
+ 0xad, 0x83, 0x23, 0x83, 0xbd, 0x83, 0xe7, 0x83,
+ 0x57, 0x84, 0x53, 0x83, 0xca, 0x83, 0xcc, 0x83,
+ 0xdc, 0x83, 0x36, 0x6c, 0x6b, 0x6d, 0x02, 0x00,
+ 0x00, 0x20, 0x22, 0x2a, 0xa0, 0x0a, 0x00, 0x20,
+ 0x80, 0x28, 0x00, 0xa8, 0x20, 0x20, 0x00, 0x02,
+ 0x80, 0x22, 0x02, 0x8a, 0x08, 0x00, 0xaa, 0x00,
+ 0x00, 0x00, 0x02, 0x00, 0x00, 0x28, 0xd5, 0x6c,
+ 0x2b, 0x45, 0xf1, 0x84, 0xf3, 0x84, 0x16, 0x85,
+ 0xca, 0x73, 0x64, 0x85, 0x2c, 0x6f, 0x5d, 0x45,
+ 0x61, 0x45, 0xb1, 0x6f, 0xd2, 0x70, 0x6b, 0x45,
+ 0x50, 0x86, 0x5c, 0x86, 0x67, 0x86, 0x69, 0x86,
+ 0xa9, 0x86, 0x88, 0x86, 0x0e, 0x87, 0xe2, 0x86,
+ 0x79, 0x87, 0x28, 0x87, 0x6b, 0x87, 0x86, 0x87,
+ 0xd7, 0x45, 0xe1, 0x87, 0x01, 0x88, 0xf9, 0x45,
+ 0x60, 0x88, 0x63, 0x88, 0x67, 0x76, 0xd7, 0x88,
+ 0xde, 0x88, 0x35, 0x46, 0xfa, 0x88, 0xbb, 0x34,
+ 0xae, 0x78, 0x66, 0x79, 0xbe, 0x46, 0xc7, 0x46,
+ 0xa0, 0x8a, 0xed, 0x8a, 0x8a, 0x8b, 0x55, 0x8c,
+ 0xa8, 0x7c, 0xab, 0x8c, 0xc1, 0x8c, 0x1b, 0x8d,
+ 0x77, 0x8d, 0x2f, 0x7f, 0x04, 0x08, 0xcb, 0x8d,
+ 0xbc, 0x8d, 0xf0, 0x8d, 0xde, 0x08, 0xd4, 0x8e,
+ 0x38, 0x8f, 0xd2, 0x85, 0xed, 0x85, 0x94, 0x90,
+ 0xf1, 0x90, 0x11, 0x91, 0x2e, 0x87, 0x1b, 0x91,
+ 0x38, 0x92, 0xd7, 0x92, 0xd8, 0x92, 0x7c, 0x92,
+ 0xf9, 0x93, 0x15, 0x94, 0xfa, 0x8b, 0x8b, 0x95,
+ 0x95, 0x49, 0xb7, 0x95, 0x77, 0x8d, 0xe6, 0x49,
+ 0xc3, 0x96, 0xb2, 0x5d, 0x23, 0x97, 0x45, 0x91,
+ 0x1a, 0x92, 0x6e, 0x4a, 0x76, 0x4a, 0xe0, 0x97,
+ 0x0a, 0x94, 0xb2, 0x4a, 0x96, 0x94, 0x0b, 0x98,
+ 0x0b, 0x98, 0x29, 0x98, 0xb6, 0x95, 0xe2, 0x98,
+ 0x33, 0x4b, 0x29, 0x99, 0xa7, 0x99, 0xc2, 0x99,
+ 0xfe, 0x99, 0xce, 0x4b, 0x30, 0x9b, 0x12, 0x9b,
+ 0x40, 0x9c, 0xfd, 0x9c, 0xce, 0x4c, 0xed, 0x4c,
+ 0x67, 0x9d, 0xce, 0xa0, 0xf8, 0x4c, 0x05, 0xa1,
+ 0x0e, 0xa2, 0x91, 0xa2, 0xbb, 0x9e, 0x56, 0x4d,
+ 0xf9, 0x9e, 0xfe, 0x9e, 0x05, 0x9f, 0x0f, 0x9f,
+ 0x16, 0x9f, 0x3b, 0x9f, 0x00, 0xa6, 0x02, 0x88,
+ 0xa0, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x28,
+ 0x00, 0x08, 0xa0, 0x80, 0xa0, 0x80, 0x00, 0x80,
+ 0x80, 0x00, 0x0a, 0x88, 0x80, 0x00, 0x80, 0x00,
+ 0x20, 0x2a, 0x00, 0x80,
+};
+
+static const uint16_t unicode_comp_table[945] = {
+ 0x4a01, 0x49c0, 0x4a02, 0x0280, 0x0281, 0x0282, 0x0283, 0x02c0,
+ 0x02c2, 0x0a00, 0x0284, 0x2442, 0x0285, 0x07c0, 0x0980, 0x0982,
+ 0x2440, 0x2280, 0x02c4, 0x2282, 0x2284, 0x2286, 0x02c6, 0x02c8,
+ 0x02ca, 0x02cc, 0x0287, 0x228a, 0x02ce, 0x228c, 0x2290, 0x2292,
+ 0x228e, 0x0288, 0x0289, 0x028a, 0x2482, 0x0300, 0x0302, 0x0304,
+ 0x028b, 0x2480, 0x0308, 0x0984, 0x0986, 0x2458, 0x0a02, 0x0306,
+ 0x2298, 0x229a, 0x229e, 0x0900, 0x030a, 0x22a0, 0x030c, 0x030e,
+ 0x0840, 0x0310, 0x0312, 0x22a2, 0x22a6, 0x09c0, 0x22a4, 0x22a8,
+ 0x22aa, 0x028c, 0x028d, 0x028e, 0x0340, 0x0342, 0x0344, 0x0380,
+ 0x028f, 0x248e, 0x07c2, 0x0988, 0x098a, 0x2490, 0x0346, 0x22ac,
+ 0x0400, 0x22b0, 0x0842, 0x22b2, 0x0402, 0x22b4, 0x0440, 0x0444,
+ 0x22b6, 0x0442, 0x22c2, 0x22c0, 0x22c4, 0x22c6, 0x22c8, 0x0940,
+ 0x04c0, 0x0291, 0x22ca, 0x04c4, 0x22cc, 0x04c2, 0x22d0, 0x22ce,
+ 0x0292, 0x0293, 0x0294, 0x0295, 0x0540, 0x0542, 0x0a08, 0x0296,
+ 0x2494, 0x0544, 0x07c4, 0x098c, 0x098e, 0x06c0, 0x2492, 0x0844,
+ 0x2308, 0x230a, 0x0580, 0x230c, 0x0584, 0x0990, 0x0992, 0x230e,
+ 0x0582, 0x2312, 0x0586, 0x0588, 0x2314, 0x058c, 0x2316, 0x0998,
+ 0x058a, 0x231e, 0x0590, 0x2320, 0x099a, 0x058e, 0x2324, 0x2322,
+ 0x0299, 0x029a, 0x029b, 0x05c0, 0x05c2, 0x05c4, 0x029c, 0x24ac,
+ 0x05c6, 0x05c8, 0x07c6, 0x0994, 0x0996, 0x0700, 0x24aa, 0x2326,
+ 0x05ca, 0x232a, 0x2328, 0x2340, 0x2342, 0x2344, 0x2346, 0x05cc,
+ 0x234a, 0x2348, 0x234c, 0x234e, 0x2350, 0x24b8, 0x029d, 0x05ce,
+ 0x24be, 0x0a0c, 0x2352, 0x0600, 0x24bc, 0x24ba, 0x0640, 0x2354,
+ 0x0642, 0x0644, 0x2356, 0x2358, 0x02a0, 0x02a1, 0x02a2, 0x02a3,
+ 0x02c1, 0x02c3, 0x0a01, 0x02a4, 0x2443, 0x02a5, 0x07c1, 0x0981,
+ 0x0983, 0x2441, 0x2281, 0x02c5, 0x2283, 0x2285, 0x2287, 0x02c7,
+ 0x02c9, 0x02cb, 0x02cd, 0x02a7, 0x228b, 0x02cf, 0x228d, 0x2291,
+ 0x2293, 0x228f, 0x02a8, 0x02a9, 0x02aa, 0x2483, 0x0301, 0x0303,
+ 0x0305, 0x02ab, 0x2481, 0x0309, 0x0985, 0x0987, 0x2459, 0x0a03,
+ 0x0307, 0x2299, 0x229b, 0x229f, 0x0901, 0x030b, 0x22a1, 0x030d,
+ 0x030f, 0x0841, 0x0311, 0x0313, 0x22a3, 0x22a7, 0x09c1, 0x22a5,
+ 0x22a9, 0x22ab, 0x2380, 0x02ac, 0x02ad, 0x02ae, 0x0341, 0x0343,
+ 0x0345, 0x02af, 0x248f, 0x07c3, 0x0989, 0x098b, 0x2491, 0x0347,
+ 0x22ad, 0x0401, 0x0884, 0x22b1, 0x0843, 0x22b3, 0x0403, 0x22b5,
+ 0x0441, 0x0445, 0x22b7, 0x0443, 0x22c3, 0x22c1, 0x22c5, 0x22c7,
+ 0x22c9, 0x0941, 0x04c1, 0x02b1, 0x22cb, 0x04c5, 0x22cd, 0x04c3,
+ 0x22d1, 0x22cf, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x0541, 0x0543,
+ 0x0a09, 0x02b6, 0x2495, 0x0545, 0x07c5, 0x098d, 0x098f, 0x06c1,
+ 0x2493, 0x0845, 0x2309, 0x230b, 0x0581, 0x230d, 0x0585, 0x0991,
+ 0x0993, 0x230f, 0x0583, 0x2313, 0x0587, 0x0589, 0x2315, 0x058d,
+ 0x2317, 0x0999, 0x058b, 0x231f, 0x2381, 0x0591, 0x2321, 0x099b,
+ 0x058f, 0x2325, 0x2323, 0x02b9, 0x02ba, 0x02bb, 0x05c1, 0x05c3,
+ 0x05c5, 0x02bc, 0x24ad, 0x05c7, 0x05c9, 0x07c7, 0x0995, 0x0997,
+ 0x0701, 0x24ab, 0x2327, 0x05cb, 0x232b, 0x2329, 0x2341, 0x2343,
+ 0x2345, 0x2347, 0x05cd, 0x234b, 0x2349, 0x2382, 0x234d, 0x234f,
+ 0x2351, 0x24b9, 0x02bd, 0x05cf, 0x24bf, 0x0a0d, 0x2353, 0x02bf,
+ 0x24bd, 0x2383, 0x24bb, 0x0641, 0x2355, 0x0643, 0x0645, 0x2357,
+ 0x2359, 0x3101, 0x0c80, 0x2e00, 0x2446, 0x2444, 0x244a, 0x2448,
+ 0x0800, 0x0942, 0x0944, 0x0804, 0x2288, 0x2486, 0x2484, 0x248a,
+ 0x2488, 0x22ae, 0x2498, 0x2496, 0x249c, 0x249a, 0x2300, 0x0a06,
+ 0x2302, 0x0a04, 0x0946, 0x07ce, 0x07ca, 0x07c8, 0x07cc, 0x2447,
+ 0x2445, 0x244b, 0x2449, 0x0801, 0x0943, 0x0945, 0x0805, 0x2289,
+ 0x2487, 0x2485, 0x248b, 0x2489, 0x22af, 0x2499, 0x2497, 0x249d,
+ 0x249b, 0x2301, 0x0a07, 0x2303, 0x0a05, 0x0947, 0x07cf, 0x07cb,
+ 0x07c9, 0x07cd, 0x2450, 0x244e, 0x2454, 0x2452, 0x2451, 0x244f,
+ 0x2455, 0x2453, 0x2294, 0x2296, 0x2295, 0x2297, 0x2304, 0x2306,
+ 0x2305, 0x2307, 0x2318, 0x2319, 0x231a, 0x231b, 0x232c, 0x232d,
+ 0x232e, 0x232f, 0x2400, 0x24a2, 0x24a0, 0x24a6, 0x24a4, 0x24a8,
+ 0x24a3, 0x24a1, 0x24a7, 0x24a5, 0x24a9, 0x24b0, 0x24ae, 0x24b4,
+ 0x24b2, 0x24b6, 0x24b1, 0x24af, 0x24b5, 0x24b3, 0x24b7, 0x0882,
+ 0x0880, 0x0881, 0x0802, 0x0803, 0x229c, 0x229d, 0x0a0a, 0x0a0b,
+ 0x0883, 0x0b40, 0x2c8a, 0x0c81, 0x2c89, 0x2c88, 0x2540, 0x2541,
+ 0x2d00, 0x2e07, 0x0d00, 0x2640, 0x2641, 0x2e80, 0x0d01, 0x26c8,
+ 0x26c9, 0x2f00, 0x2f84, 0x0d02, 0x2f83, 0x2f82, 0x0d40, 0x26d8,
+ 0x26d9, 0x3186, 0x0d04, 0x2740, 0x2741, 0x3100, 0x3086, 0x0d06,
+ 0x3085, 0x3084, 0x0d41, 0x2840, 0x3200, 0x0d07, 0x284f, 0x2850,
+ 0x3280, 0x2c84, 0x2e03, 0x2857, 0x0d42, 0x2c81, 0x2c80, 0x24c0,
+ 0x24c1, 0x2c86, 0x2c83, 0x28c0, 0x0d43, 0x25c0, 0x25c1, 0x2940,
+ 0x0d44, 0x26c0, 0x26c1, 0x2e05, 0x2e02, 0x29c0, 0x0d45, 0x2f05,
+ 0x2f04, 0x0d80, 0x26d0, 0x26d1, 0x2f80, 0x2a40, 0x0d82, 0x26e0,
+ 0x26e1, 0x3080, 0x3081, 0x2ac0, 0x0d83, 0x3004, 0x3003, 0x0d81,
+ 0x27c0, 0x27c1, 0x3082, 0x2b40, 0x0d84, 0x2847, 0x2848, 0x3184,
+ 0x3181, 0x2f06, 0x0d08, 0x2f81, 0x3005, 0x0d46, 0x3083, 0x3182,
+ 0x0e00, 0x0e01, 0x0f40, 0x1180, 0x1182, 0x0f03, 0x0f00, 0x11c0,
+ 0x0f01, 0x1140, 0x1202, 0x1204, 0x0f81, 0x1240, 0x0fc0, 0x1242,
+ 0x0f80, 0x1244, 0x1284, 0x0f82, 0x1286, 0x1288, 0x128a, 0x12c0,
+ 0x1282, 0x1181, 0x1183, 0x1043, 0x1040, 0x11c1, 0x1041, 0x1141,
+ 0x1203, 0x1205, 0x10c1, 0x1241, 0x1000, 0x1243, 0x10c0, 0x1245,
+ 0x1285, 0x10c2, 0x1287, 0x1289, 0x128b, 0x12c1, 0x1283, 0x1080,
+ 0x1100, 0x1101, 0x1200, 0x1201, 0x1280, 0x1281, 0x1340, 0x1341,
+ 0x1343, 0x1342, 0x1344, 0x13c2, 0x1400, 0x13c0, 0x1440, 0x1480,
+ 0x14c0, 0x1540, 0x1541, 0x1740, 0x1700, 0x1741, 0x17c0, 0x1800,
+ 0x1802, 0x1801, 0x1840, 0x1880, 0x1900, 0x18c0, 0x18c1, 0x1901,
+ 0x1940, 0x1942, 0x1941, 0x1980, 0x19c0, 0x19c2, 0x19c1, 0x1c80,
+ 0x1cc0, 0x1dc0, 0x1f80, 0x2000, 0x2002, 0x2004, 0x2006, 0x2008,
+ 0x2040, 0x2080, 0x2082, 0x20c0, 0x20c1, 0x2100, 0x22b8, 0x22b9,
+ 0x2310, 0x2311, 0x231c, 0x231d, 0x244c, 0x2456, 0x244d, 0x2457,
+ 0x248c, 0x248d, 0x249e, 0x249f, 0x2500, 0x2502, 0x2504, 0x2bc0,
+ 0x2501, 0x2503, 0x2505, 0x2bc1, 0x2bc2, 0x2bc3, 0x2bc4, 0x2bc5,
+ 0x2bc6, 0x2bc7, 0x2580, 0x2582, 0x2584, 0x2bc8, 0x2581, 0x2583,
+ 0x2585, 0x2bc9, 0x2bca, 0x2bcb, 0x2bcc, 0x2bcd, 0x2bce, 0x2bcf,
+ 0x2600, 0x2602, 0x2601, 0x2603, 0x2680, 0x2682, 0x2681, 0x2683,
+ 0x26c2, 0x26c4, 0x26c6, 0x2c00, 0x26c3, 0x26c5, 0x26c7, 0x2c01,
+ 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x26ca, 0x26cc,
+ 0x26ce, 0x2c08, 0x26cb, 0x26cd, 0x26cf, 0x2c09, 0x2c0a, 0x2c0b,
+ 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x26d2, 0x26d4, 0x26d6, 0x26d3,
+ 0x26d5, 0x26d7, 0x26da, 0x26dc, 0x26de, 0x26db, 0x26dd, 0x26df,
+ 0x2700, 0x2702, 0x2701, 0x2703, 0x2780, 0x2782, 0x2781, 0x2783,
+ 0x2800, 0x2802, 0x2804, 0x2801, 0x2803, 0x2805, 0x2842, 0x2844,
+ 0x2846, 0x2849, 0x284b, 0x284d, 0x2c40, 0x284a, 0x284c, 0x284e,
+ 0x2c41, 0x2c42, 0x2c43, 0x2c44, 0x2c45, 0x2c46, 0x2c47, 0x2851,
+ 0x2853, 0x2855, 0x2c48, 0x2852, 0x2854, 0x2856, 0x2c49, 0x2c4a,
+ 0x2c4b, 0x2c4c, 0x2c4d, 0x2c4e, 0x2c4f, 0x2c82, 0x2e01, 0x3180,
+ 0x2c87, 0x2f01, 0x2f02, 0x2f03, 0x2e06, 0x3185, 0x3000, 0x3001,
+ 0x3002, 0x4640, 0x4641, 0x4680, 0x46c0, 0x46c2, 0x46c1, 0x4700,
+ 0x4740, 0x4780, 0x47c0, 0x47c2, 0x4900, 0x4940, 0x4980, 0x4982,
+ 0x4a00, 0x49c2, 0x4a03, 0x4a04, 0x4a40, 0x4a41, 0x4a80, 0x4a81,
+ 0x4ac0, 0x4ac1, 0x4bc0, 0x4bc1, 0x4b00, 0x4b01, 0x4b40, 0x4b41,
+ 0x4bc2, 0x4bc3, 0x4b80, 0x4b81, 0x4b82, 0x4b83, 0x4c00, 0x4c01,
+ 0x4c02, 0x4c03, 0x5600, 0x5440, 0x5442, 0x5444, 0x5446, 0x5448,
+ 0x544a, 0x544c, 0x544e, 0x5450, 0x5452, 0x5454, 0x5456, 0x5480,
+ 0x5482, 0x5484, 0x54c0, 0x54c1, 0x5500, 0x5501, 0x5540, 0x5541,
+ 0x5580, 0x5581, 0x55c0, 0x55c1, 0x5680, 0x58c0, 0x5700, 0x5702,
+ 0x5704, 0x5706, 0x5708, 0x570a, 0x570c, 0x570e, 0x5710, 0x5712,
+ 0x5714, 0x5716, 0x5740, 0x5742, 0x5744, 0x5780, 0x5781, 0x57c0,
+ 0x57c1, 0x5800, 0x5801, 0x5840, 0x5841, 0x5880, 0x5881, 0x5900,
+ 0x5901, 0x5902, 0x5903, 0x5940, 0x8f40, 0x8f42, 0x8f80, 0x8fc0,
+ 0x8fc1, 0x9000, 0x9001, 0x9041, 0x9040, 0x9043, 0x9080, 0x9081,
+ 0x90c0,
+};
+
+typedef enum {
+ UNICODE_GC_Cn,
+ UNICODE_GC_Lu,
+ UNICODE_GC_Ll,
+ UNICODE_GC_Lt,
+ UNICODE_GC_Lm,
+ UNICODE_GC_Lo,
+ UNICODE_GC_Mn,
+ UNICODE_GC_Mc,
+ UNICODE_GC_Me,
+ UNICODE_GC_Nd,
+ UNICODE_GC_Nl,
+ UNICODE_GC_No,
+ UNICODE_GC_Sm,
+ UNICODE_GC_Sc,
+ UNICODE_GC_Sk,
+ UNICODE_GC_So,
+ UNICODE_GC_Pc,
+ UNICODE_GC_Pd,
+ UNICODE_GC_Ps,
+ UNICODE_GC_Pe,
+ UNICODE_GC_Pi,
+ UNICODE_GC_Pf,
+ UNICODE_GC_Po,
+ UNICODE_GC_Zs,
+ UNICODE_GC_Zl,
+ UNICODE_GC_Zp,
+ UNICODE_GC_Cc,
+ UNICODE_GC_Cf,
+ UNICODE_GC_Cs,
+ UNICODE_GC_Co,
+ UNICODE_GC_LC,
+ UNICODE_GC_L,
+ UNICODE_GC_M,
+ UNICODE_GC_N,
+ UNICODE_GC_S,
+ UNICODE_GC_P,
+ UNICODE_GC_Z,
+ UNICODE_GC_C,
+ UNICODE_GC_COUNT,
+} UnicodeGCEnum;
+
+static const char unicode_gc_name_table[] =
+ "Cn,Unassigned" "\0"
+ "Lu,Uppercase_Letter" "\0"
+ "Ll,Lowercase_Letter" "\0"
+ "Lt,Titlecase_Letter" "\0"
+ "Lm,Modifier_Letter" "\0"
+ "Lo,Other_Letter" "\0"
+ "Mn,Nonspacing_Mark" "\0"
+ "Mc,Spacing_Mark" "\0"
+ "Me,Enclosing_Mark" "\0"
+ "Nd,Decimal_Number,digit" "\0"
+ "Nl,Letter_Number" "\0"
+ "No,Other_Number" "\0"
+ "Sm,Math_Symbol" "\0"
+ "Sc,Currency_Symbol" "\0"
+ "Sk,Modifier_Symbol" "\0"
+ "So,Other_Symbol" "\0"
+ "Pc,Connector_Punctuation" "\0"
+ "Pd,Dash_Punctuation" "\0"
+ "Ps,Open_Punctuation" "\0"
+ "Pe,Close_Punctuation" "\0"
+ "Pi,Initial_Punctuation" "\0"
+ "Pf,Final_Punctuation" "\0"
+ "Po,Other_Punctuation" "\0"
+ "Zs,Space_Separator" "\0"
+ "Zl,Line_Separator" "\0"
+ "Zp,Paragraph_Separator" "\0"
+ "Cc,Control,cntrl" "\0"
+ "Cf,Format" "\0"
+ "Cs,Surrogate" "\0"
+ "Co,Private_Use" "\0"
+ "LC,Cased_Letter" "\0"
+ "L,Letter" "\0"
+ "M,Mark,Combining_Mark" "\0"
+ "N,Number" "\0"
+ "S,Symbol" "\0"
+ "P,Punctuation,punct" "\0"
+ "Z,Separator" "\0"
+ "C,Other" "\0"
+;
+
+static const uint8_t unicode_gc_table[3897] = {
+ 0xfa, 0x18, 0x17, 0x56, 0x0d, 0x56, 0x12, 0x13,
+ 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36,
+ 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e,
+ 0x10, 0x0e, 0xe2, 0x12, 0x12, 0x0c, 0x13, 0x0c,
+ 0xfa, 0x19, 0x17, 0x16, 0x6d, 0x0f, 0x16, 0x0e,
+ 0x0f, 0x05, 0x14, 0x0c, 0x1b, 0x0f, 0x0e, 0x0f,
+ 0x0c, 0x2b, 0x0e, 0x02, 0x36, 0x0e, 0x0b, 0x05,
+ 0x15, 0x4b, 0x16, 0xe1, 0x0f, 0x0c, 0xc1, 0xe2,
+ 0x10, 0x0c, 0xe2, 0x00, 0xff, 0x30, 0x02, 0xff,
+ 0x08, 0x02, 0xff, 0x27, 0xbf, 0x22, 0x21, 0x02,
+ 0x5f, 0x5f, 0x21, 0x22, 0x61, 0x02, 0x21, 0x02,
+ 0x41, 0x42, 0x21, 0x02, 0x21, 0x02, 0x9f, 0x7f,
+ 0x02, 0x5f, 0x5f, 0x21, 0x02, 0x5f, 0x3f, 0x02,
+ 0x05, 0x3f, 0x22, 0x65, 0x01, 0x03, 0x02, 0x01,
+ 0x03, 0x02, 0x01, 0x03, 0x02, 0xff, 0x08, 0x02,
+ 0xff, 0x0a, 0x02, 0x01, 0x03, 0x02, 0x5f, 0x21,
+ 0x02, 0xff, 0x32, 0xa2, 0x21, 0x02, 0x21, 0x22,
+ 0x5f, 0x41, 0x02, 0xff, 0x00, 0xe2, 0x3c, 0x05,
+ 0xe2, 0x13, 0xe4, 0x0a, 0x6e, 0xe4, 0x04, 0xee,
+ 0x06, 0x84, 0xce, 0x04, 0x0e, 0x04, 0xee, 0x09,
+ 0xe6, 0x68, 0x7f, 0x04, 0x0e, 0x3f, 0x20, 0x04,
+ 0x42, 0x16, 0x01, 0x60, 0x2e, 0x01, 0x16, 0x41,
+ 0x00, 0x01, 0x00, 0x21, 0x02, 0xe1, 0x09, 0x00,
+ 0xe1, 0x01, 0xe2, 0x1b, 0x3f, 0x02, 0x41, 0x42,
+ 0xff, 0x10, 0x62, 0x3f, 0x0c, 0x5f, 0x3f, 0x02,
+ 0xe1, 0x2b, 0xe2, 0x28, 0xff, 0x1a, 0x0f, 0x86,
+ 0x28, 0xff, 0x2f, 0xff, 0x06, 0x02, 0xff, 0x58,
+ 0x00, 0xe1, 0x1e, 0x20, 0x04, 0xb6, 0xe2, 0x21,
+ 0x16, 0x11, 0x20, 0x2f, 0x0d, 0x00, 0xe6, 0x25,
+ 0x11, 0x06, 0x16, 0x26, 0x16, 0x26, 0x16, 0x06,
+ 0xe0, 0x00, 0xe5, 0x13, 0x60, 0x65, 0x36, 0xe0,
+ 0x03, 0xbb, 0x4c, 0x36, 0x0d, 0x36, 0x2f, 0xe6,
+ 0x03, 0x16, 0x1b, 0x56, 0xe5, 0x18, 0x04, 0xe5,
+ 0x02, 0xe6, 0x0d, 0xe9, 0x02, 0x76, 0x25, 0x06,
+ 0xe5, 0x5b, 0x16, 0x05, 0xc6, 0x1b, 0x0f, 0xa6,
+ 0x24, 0x26, 0x0f, 0x66, 0x25, 0xe9, 0x02, 0x45,
+ 0x2f, 0x05, 0xf6, 0x06, 0x00, 0x1b, 0x05, 0x06,
+ 0xe5, 0x16, 0xe6, 0x13, 0x20, 0xe5, 0x51, 0xe6,
+ 0x03, 0x05, 0xe0, 0x06, 0xe9, 0x02, 0xe5, 0x19,
+ 0xe6, 0x01, 0x24, 0x0f, 0x56, 0x04, 0x20, 0x06,
+ 0x2d, 0xe5, 0x0e, 0x66, 0x04, 0xe6, 0x01, 0x04,
+ 0x46, 0x04, 0x86, 0x20, 0xf6, 0x07, 0x00, 0xe5,
+ 0x11, 0x46, 0x20, 0x16, 0x00, 0xe5, 0x03, 0x80,
+ 0xe5, 0x10, 0x0e, 0xa5, 0x00, 0x3b, 0xa0, 0xe6,
+ 0x00, 0xe5, 0x21, 0x04, 0xe6, 0x10, 0x1b, 0xe6,
+ 0x18, 0x07, 0xe5, 0x2e, 0x06, 0x07, 0x06, 0x05,
+ 0x47, 0xe6, 0x00, 0x67, 0x06, 0x27, 0x05, 0xc6,
+ 0xe5, 0x02, 0x26, 0x36, 0xe9, 0x02, 0x16, 0x04,
+ 0xe5, 0x07, 0x06, 0x27, 0x00, 0xe5, 0x00, 0x20,
+ 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x05,
+ 0x40, 0x65, 0x20, 0x06, 0x05, 0x47, 0x66, 0x20,
+ 0x27, 0x20, 0x27, 0x06, 0x05, 0xe0, 0x00, 0x07,
+ 0x60, 0x25, 0x00, 0x45, 0x26, 0x20, 0xe9, 0x02,
+ 0x25, 0x2d, 0xab, 0x0f, 0x0d, 0x05, 0x16, 0x06,
+ 0x20, 0x26, 0x07, 0x00, 0xa5, 0x60, 0x25, 0x20,
+ 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25, 0x00, 0x25,
+ 0x00, 0x25, 0x20, 0x06, 0x00, 0x47, 0x26, 0x60,
+ 0x26, 0x20, 0x46, 0x40, 0x06, 0xc0, 0x65, 0x00,
+ 0x05, 0xc0, 0xe9, 0x02, 0x26, 0x45, 0x06, 0x16,
+ 0xe0, 0x02, 0x26, 0x07, 0x00, 0xe5, 0x01, 0x00,
+ 0x45, 0x00, 0xe5, 0x0e, 0x00, 0xc5, 0x00, 0x25,
+ 0x00, 0x85, 0x20, 0x06, 0x05, 0x47, 0x86, 0x00,
+ 0x26, 0x07, 0x00, 0x27, 0x06, 0x20, 0x05, 0xe0,
+ 0x07, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x16, 0x0d,
+ 0xc0, 0x05, 0xa6, 0x00, 0x06, 0x27, 0x00, 0xe5,
+ 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5,
+ 0x00, 0x25, 0x00, 0x85, 0x20, 0x06, 0x05, 0x07,
+ 0x06, 0x07, 0x66, 0x20, 0x27, 0x20, 0x27, 0x06,
+ 0xc0, 0x26, 0x07, 0x60, 0x25, 0x00, 0x45, 0x26,
+ 0x20, 0xe9, 0x02, 0x0f, 0x05, 0xab, 0xe0, 0x02,
+ 0x06, 0x05, 0x00, 0xa5, 0x40, 0x45, 0x00, 0x65,
+ 0x40, 0x25, 0x00, 0x05, 0x00, 0x25, 0x40, 0x25,
+ 0x40, 0x45, 0x40, 0xe5, 0x04, 0x60, 0x27, 0x06,
+ 0x27, 0x40, 0x47, 0x00, 0x47, 0x06, 0x20, 0x05,
+ 0xa0, 0x07, 0xe0, 0x06, 0xe9, 0x02, 0x4b, 0xaf,
+ 0x0d, 0x0f, 0x80, 0x06, 0x47, 0x06, 0xe5, 0x00,
+ 0x00, 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x08,
+ 0x20, 0x06, 0x05, 0x46, 0x67, 0x00, 0x46, 0x00,
+ 0x66, 0xc0, 0x26, 0x00, 0x45, 0x20, 0x05, 0x20,
+ 0x25, 0x26, 0x20, 0xe9, 0x02, 0xc0, 0x16, 0xcb,
+ 0x0f, 0x05, 0x06, 0x27, 0x16, 0xe5, 0x00, 0x00,
+ 0x45, 0x00, 0xe5, 0x0f, 0x00, 0xe5, 0x02, 0x00,
+ 0x85, 0x20, 0x06, 0x05, 0x07, 0x06, 0x87, 0x00,
+ 0x06, 0x27, 0x00, 0x27, 0x26, 0xc0, 0x27, 0xa0,
+ 0x25, 0x00, 0x25, 0x26, 0x20, 0xe9, 0x02, 0x00,
+ 0x25, 0xe0, 0x05, 0x26, 0x27, 0xe5, 0x01, 0x00,
+ 0x45, 0x00, 0xe5, 0x21, 0x26, 0x05, 0x47, 0x66,
+ 0x00, 0x47, 0x00, 0x47, 0x06, 0x05, 0x0f, 0x60,
+ 0x45, 0x07, 0xcb, 0x45, 0x26, 0x20, 0xe9, 0x02,
+ 0xeb, 0x01, 0x0f, 0xa5, 0x00, 0x06, 0x27, 0x00,
+ 0xe5, 0x0a, 0x40, 0xe5, 0x10, 0x00, 0xe5, 0x01,
+ 0x00, 0x05, 0x20, 0xc5, 0x40, 0x06, 0x60, 0x47,
+ 0x46, 0x00, 0x06, 0x00, 0xe7, 0x00, 0xa0, 0xe9,
+ 0x02, 0x20, 0x27, 0x16, 0xe0, 0x04, 0xe5, 0x28,
+ 0x06, 0x25, 0xc6, 0x60, 0x0d, 0xa5, 0x04, 0xe6,
+ 0x00, 0x16, 0xe9, 0x02, 0x36, 0xe0, 0x1d, 0x25,
+ 0x00, 0x05, 0x00, 0x85, 0x00, 0xe5, 0x10, 0x00,
+ 0x05, 0x00, 0xe5, 0x02, 0x06, 0x25, 0xe6, 0x01,
+ 0x05, 0x20, 0x85, 0x00, 0x04, 0x00, 0xa6, 0x20,
+ 0xe9, 0x02, 0x20, 0x65, 0xe0, 0x18, 0x05, 0x4f,
+ 0xf6, 0x07, 0x0f, 0x16, 0x4f, 0x26, 0xaf, 0xe9,
+ 0x02, 0xeb, 0x02, 0x0f, 0x06, 0x0f, 0x06, 0x0f,
+ 0x06, 0x12, 0x13, 0x12, 0x13, 0x27, 0xe5, 0x00,
+ 0x00, 0xe5, 0x1c, 0x60, 0xe6, 0x06, 0x07, 0x86,
+ 0x16, 0x26, 0x85, 0xe6, 0x03, 0x00, 0xe6, 0x1c,
+ 0x00, 0xef, 0x00, 0x06, 0xaf, 0x00, 0x2f, 0x96,
+ 0x6f, 0x36, 0xe0, 0x1d, 0xe5, 0x23, 0x27, 0x66,
+ 0x07, 0xa6, 0x07, 0x26, 0x27, 0x26, 0x05, 0xe9,
+ 0x02, 0xb6, 0xa5, 0x27, 0x26, 0x65, 0x46, 0x05,
+ 0x47, 0x25, 0xc7, 0x45, 0x66, 0xe5, 0x05, 0x06,
+ 0x27, 0x26, 0xa7, 0x06, 0x05, 0x07, 0xe9, 0x02,
+ 0x47, 0x06, 0x2f, 0xe1, 0x1e, 0x00, 0x01, 0x80,
+ 0x01, 0x20, 0xe2, 0x23, 0x16, 0x04, 0x42, 0xe5,
+ 0x80, 0xc1, 0x00, 0x65, 0x20, 0xc5, 0x00, 0x05,
+ 0x00, 0x65, 0x20, 0xe5, 0x21, 0x00, 0x65, 0x20,
+ 0xe5, 0x19, 0x00, 0x65, 0x20, 0xc5, 0x00, 0x05,
+ 0x00, 0x65, 0x20, 0xe5, 0x07, 0x00, 0xe5, 0x31,
+ 0x00, 0x65, 0x20, 0xe5, 0x3b, 0x20, 0x46, 0xf6,
+ 0x01, 0xeb, 0x0c, 0x40, 0xe5, 0x08, 0xef, 0x02,
+ 0xa0, 0xe1, 0x4e, 0x20, 0xa2, 0x20, 0x11, 0xe5,
+ 0x81, 0xe4, 0x0f, 0x16, 0xe5, 0x09, 0x17, 0xe5,
+ 0x12, 0x12, 0x13, 0x40, 0xe5, 0x43, 0x56, 0x4a,
+ 0xe5, 0x00, 0xc0, 0xe5, 0x0a, 0x46, 0x07, 0xe0,
+ 0x01, 0xe5, 0x0b, 0x26, 0x07, 0x36, 0xe0, 0x01,
+ 0xe5, 0x0a, 0x26, 0xe0, 0x04, 0xe5, 0x05, 0x00,
+ 0x45, 0x00, 0x26, 0xe0, 0x04, 0xe5, 0x2c, 0x26,
+ 0x07, 0xc6, 0xe7, 0x00, 0x06, 0x27, 0xe6, 0x03,
+ 0x56, 0x04, 0x56, 0x0d, 0x05, 0x06, 0x20, 0xe9,
+ 0x02, 0xa0, 0xeb, 0x02, 0xa0, 0xb6, 0x11, 0x76,
+ 0x46, 0x1b, 0x06, 0xe9, 0x02, 0xa0, 0xe5, 0x1b,
+ 0x04, 0xe5, 0x2d, 0xc0, 0x85, 0x26, 0xe5, 0x1a,
+ 0x06, 0x05, 0x80, 0xe5, 0x3e, 0xe0, 0x02, 0xe5,
+ 0x17, 0x00, 0x46, 0x67, 0x26, 0x47, 0x60, 0x27,
+ 0x06, 0xa7, 0x46, 0x60, 0x0f, 0x40, 0x36, 0xe9,
+ 0x02, 0xe5, 0x16, 0x20, 0x85, 0xe0, 0x03, 0xe5,
+ 0x24, 0x60, 0xe5, 0x12, 0xa0, 0xe9, 0x02, 0x0b,
+ 0x40, 0xef, 0x1a, 0xe5, 0x0f, 0x26, 0x27, 0x06,
+ 0x20, 0x36, 0xe5, 0x2d, 0x07, 0x06, 0x07, 0xc6,
+ 0x00, 0x06, 0x07, 0x06, 0x27, 0xe6, 0x00, 0xa7,
+ 0xe6, 0x02, 0x20, 0x06, 0xe9, 0x02, 0xa0, 0xe9,
+ 0x02, 0xa0, 0xd6, 0x04, 0xb6, 0x20, 0xe6, 0x06,
+ 0x08, 0xe6, 0x08, 0xe0, 0x29, 0x66, 0x07, 0xe5,
+ 0x27, 0x06, 0x07, 0x86, 0x07, 0x06, 0x87, 0x06,
+ 0x27, 0xe5, 0x00, 0x40, 0xe9, 0x02, 0xd6, 0xef,
+ 0x02, 0xe6, 0x01, 0xef, 0x01, 0x36, 0x00, 0x26,
+ 0x07, 0xe5, 0x16, 0x07, 0x66, 0x27, 0x26, 0x07,
+ 0x46, 0x25, 0xe9, 0x02, 0xe5, 0x24, 0x06, 0x07,
+ 0x26, 0x47, 0x06, 0x07, 0x46, 0x27, 0xe0, 0x00,
+ 0x76, 0xe5, 0x1c, 0xe7, 0x00, 0xe6, 0x00, 0x27,
+ 0x26, 0x40, 0x96, 0xe9, 0x02, 0x40, 0x45, 0xe9,
+ 0x02, 0xe5, 0x16, 0xa4, 0x36, 0xe2, 0x01, 0xc0,
+ 0xe1, 0x23, 0x20, 0x41, 0xf6, 0x00, 0xe0, 0x00,
+ 0x46, 0x16, 0xe6, 0x05, 0x07, 0xc6, 0x65, 0x06,
+ 0xa5, 0x06, 0x25, 0x07, 0x26, 0x05, 0x80, 0xe2,
+ 0x24, 0xe4, 0x37, 0xe2, 0x05, 0x04, 0xe2, 0x1a,
+ 0xe4, 0x1d, 0xe6, 0x38, 0xff, 0x80, 0x0e, 0xe2,
+ 0x00, 0xff, 0x5a, 0xe2, 0x00, 0xe1, 0x00, 0xa2,
+ 0x20, 0xa1, 0x20, 0xe2, 0x00, 0xe1, 0x00, 0xe2,
+ 0x00, 0xe1, 0x00, 0xa2, 0x20, 0xa1, 0x20, 0xe2,
+ 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x3f, 0xc2, 0xe1, 0x00, 0xe2, 0x06, 0x20, 0xe2,
+ 0x00, 0xe3, 0x00, 0xe2, 0x00, 0xe3, 0x00, 0xe2,
+ 0x00, 0xe3, 0x00, 0x82, 0x00, 0x22, 0x61, 0x03,
+ 0x0e, 0x02, 0x4e, 0x42, 0x00, 0x22, 0x61, 0x03,
+ 0x4e, 0x62, 0x20, 0x22, 0x61, 0x00, 0x4e, 0xe2,
+ 0x00, 0x81, 0x4e, 0x20, 0x42, 0x00, 0x22, 0x61,
+ 0x03, 0x2e, 0x00, 0xf7, 0x03, 0x9b, 0xb1, 0x36,
+ 0x14, 0x15, 0x12, 0x34, 0x15, 0x12, 0x14, 0xf6,
+ 0x00, 0x18, 0x19, 0x9b, 0x17, 0xf6, 0x01, 0x14,
+ 0x15, 0x76, 0x30, 0x56, 0x0c, 0x12, 0x13, 0xf6,
+ 0x03, 0x0c, 0x16, 0x10, 0xf6, 0x02, 0x17, 0x9b,
+ 0x00, 0xfb, 0x02, 0x0b, 0x04, 0x20, 0xab, 0x4c,
+ 0x12, 0x13, 0x04, 0xeb, 0x02, 0x4c, 0x12, 0x13,
+ 0x00, 0xe4, 0x05, 0x40, 0xed, 0x19, 0xe0, 0x07,
+ 0xe6, 0x05, 0x68, 0x06, 0x48, 0xe6, 0x04, 0xe0,
+ 0x07, 0x2f, 0x01, 0x6f, 0x01, 0x2f, 0x02, 0x41,
+ 0x22, 0x41, 0x02, 0x0f, 0x01, 0x2f, 0x0c, 0x81,
+ 0xaf, 0x01, 0x0f, 0x01, 0x0f, 0x01, 0x0f, 0x61,
+ 0x0f, 0x02, 0x61, 0x02, 0x65, 0x02, 0x2f, 0x22,
+ 0x21, 0x8c, 0x3f, 0x42, 0x0f, 0x0c, 0x2f, 0x02,
+ 0x0f, 0xeb, 0x08, 0xea, 0x1b, 0x3f, 0x6a, 0x0b,
+ 0x2f, 0x60, 0x8c, 0x8f, 0x2c, 0x6f, 0x0c, 0x2f,
+ 0x0c, 0x2f, 0x0c, 0xcf, 0x0c, 0xef, 0x17, 0x2c,
+ 0x2f, 0x0c, 0x0f, 0x0c, 0xef, 0x17, 0xec, 0x80,
+ 0x84, 0xef, 0x00, 0x12, 0x13, 0x12, 0x13, 0xef,
+ 0x0c, 0x2c, 0xcf, 0x12, 0x13, 0xef, 0x49, 0x0c,
+ 0xef, 0x16, 0xec, 0x11, 0xef, 0x20, 0xac, 0xef,
+ 0x3d, 0xe0, 0x11, 0xef, 0x03, 0xe0, 0x0d, 0xeb,
+ 0x34, 0xef, 0x46, 0xeb, 0x0e, 0xef, 0x80, 0x2f,
+ 0x0c, 0xef, 0x01, 0x0c, 0xef, 0x2e, 0xec, 0x00,
+ 0xef, 0x67, 0x0c, 0xef, 0x80, 0x70, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0xeb, 0x16, 0xef, 0x24,
+ 0x8c, 0x12, 0x13, 0xec, 0x17, 0x12, 0x13, 0x12,
+ 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0xec,
+ 0x08, 0xef, 0x80, 0x78, 0xec, 0x7b, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0xec, 0x37, 0x12, 0x13,
+ 0x12, 0x13, 0xec, 0x18, 0x12, 0x13, 0xec, 0x80,
+ 0x7a, 0xef, 0x28, 0xec, 0x0d, 0x2f, 0xac, 0xef,
+ 0x1f, 0x20, 0xef, 0x18, 0x00, 0xef, 0x61, 0xe1,
+ 0x28, 0xe2, 0x28, 0x5f, 0x21, 0x22, 0xdf, 0x41,
+ 0x02, 0x3f, 0x02, 0x3f, 0x82, 0x24, 0x41, 0x02,
+ 0xff, 0x5a, 0x02, 0xaf, 0x7f, 0x46, 0x3f, 0x80,
+ 0x76, 0x0b, 0x36, 0xe2, 0x1e, 0x00, 0x02, 0x80,
+ 0x02, 0x20, 0xe5, 0x30, 0xc0, 0x04, 0x16, 0xe0,
+ 0x06, 0x06, 0xe5, 0x0f, 0xe0, 0x01, 0xc5, 0x00,
+ 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00,
+ 0xc5, 0x00, 0xc5, 0x00, 0xc5, 0x00, 0xe6, 0x18,
+ 0x36, 0x14, 0x15, 0x14, 0x15, 0x56, 0x14, 0x15,
+ 0x16, 0x14, 0x15, 0xf6, 0x01, 0x11, 0x36, 0x11,
+ 0x16, 0x14, 0x15, 0x36, 0x14, 0x15, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x96, 0x04,
+ 0xf6, 0x02, 0x31, 0x76, 0x11, 0x16, 0x12, 0xf6,
+ 0x05, 0x2f, 0x56, 0x12, 0x13, 0x12, 0x13, 0x12,
+ 0x13, 0x12, 0x13, 0x11, 0xe0, 0x1a, 0xef, 0x12,
+ 0x00, 0xef, 0x51, 0xe0, 0x04, 0xef, 0x80, 0x4e,
+ 0xe0, 0x12, 0xef, 0x04, 0x60, 0x17, 0x56, 0x0f,
+ 0x04, 0x05, 0x0a, 0x12, 0x13, 0x12, 0x13, 0x12,
+ 0x13, 0x12, 0x13, 0x12, 0x13, 0x2f, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x11, 0x12,
+ 0x33, 0x0f, 0xea, 0x01, 0x66, 0x27, 0x11, 0x84,
+ 0x2f, 0x4a, 0x04, 0x05, 0x16, 0x2f, 0x00, 0xe5,
+ 0x4e, 0x20, 0x26, 0x2e, 0x24, 0x05, 0x11, 0xe5,
+ 0x52, 0x16, 0x44, 0x05, 0x80, 0xe5, 0x23, 0x00,
+ 0xe5, 0x56, 0x00, 0x2f, 0x6b, 0xef, 0x02, 0xe5,
+ 0x18, 0xef, 0x1c, 0xe0, 0x04, 0xe5, 0x08, 0xef,
+ 0x17, 0x00, 0xeb, 0x02, 0xef, 0x16, 0xeb, 0x00,
+ 0x0f, 0xeb, 0x07, 0xef, 0x18, 0xeb, 0x02, 0xef,
+ 0x1f, 0xeb, 0x07, 0xef, 0x80, 0xb8, 0xe5, 0x99,
+ 0x38, 0xef, 0x38, 0xe5, 0xc0, 0x11, 0x8d, 0x04,
+ 0xe5, 0x83, 0xef, 0x40, 0xef, 0x2f, 0xe0, 0x01,
+ 0xe5, 0x20, 0xa4, 0x36, 0xe5, 0x80, 0x84, 0x04,
+ 0x56, 0xe5, 0x08, 0xe9, 0x02, 0x25, 0xe0, 0x0c,
+ 0xff, 0x26, 0x05, 0x06, 0x48, 0x16, 0xe6, 0x02,
+ 0x16, 0x04, 0xff, 0x14, 0x24, 0x26, 0xe5, 0x3e,
+ 0xea, 0x02, 0x26, 0xb6, 0xe0, 0x00, 0xee, 0x0f,
+ 0xe4, 0x01, 0x2e, 0xff, 0x06, 0x22, 0xff, 0x36,
+ 0x04, 0xe2, 0x00, 0x9f, 0xff, 0x02, 0x04, 0x2e,
+ 0x7f, 0x05, 0x7f, 0x22, 0xff, 0x0d, 0x61, 0x02,
+ 0x81, 0x02, 0xff, 0x07, 0x41, 0x02, 0x3f, 0x80,
+ 0x3f, 0x00, 0x02, 0x00, 0x02, 0x7f, 0xe0, 0x10,
+ 0x44, 0x3f, 0x05, 0x24, 0x02, 0xc5, 0x06, 0x45,
+ 0x06, 0x65, 0x06, 0xe5, 0x0f, 0x27, 0x26, 0x07,
+ 0x6f, 0x06, 0x40, 0xab, 0x2f, 0x0d, 0x0f, 0xa0,
+ 0xe5, 0x2c, 0x76, 0xe0, 0x00, 0x27, 0xe5, 0x2a,
+ 0xe7, 0x08, 0x26, 0xe0, 0x00, 0x36, 0xe9, 0x02,
+ 0xa0, 0xe6, 0x0a, 0xa5, 0x56, 0x05, 0x16, 0x25,
+ 0x06, 0xe9, 0x02, 0xe5, 0x14, 0xe6, 0x00, 0x36,
+ 0xe5, 0x0f, 0xe6, 0x03, 0x27, 0xe0, 0x03, 0x16,
+ 0xe5, 0x15, 0x40, 0x46, 0x07, 0xe5, 0x27, 0x06,
+ 0x27, 0x66, 0x27, 0x26, 0x47, 0xf6, 0x05, 0x00,
+ 0x04, 0xe9, 0x02, 0x60, 0x36, 0x85, 0x06, 0x04,
+ 0xe5, 0x01, 0xe9, 0x02, 0x85, 0x00, 0xe5, 0x21,
+ 0xa6, 0x27, 0x26, 0x27, 0x26, 0xe0, 0x01, 0x45,
+ 0x06, 0xe5, 0x00, 0x06, 0x07, 0x20, 0xe9, 0x02,
+ 0x20, 0x76, 0xe5, 0x08, 0x04, 0xa5, 0x4f, 0x05,
+ 0x07, 0x06, 0x07, 0xe5, 0x2a, 0x06, 0x05, 0x46,
+ 0x25, 0x26, 0x85, 0x26, 0x05, 0x06, 0x05, 0xe0,
+ 0x10, 0x25, 0x04, 0x36, 0xe5, 0x03, 0x07, 0x26,
+ 0x27, 0x36, 0x05, 0x24, 0x07, 0x06, 0xe0, 0x02,
+ 0xa5, 0x20, 0xa5, 0x20, 0xa5, 0xe0, 0x01, 0xc5,
+ 0x00, 0xc5, 0x00, 0xe2, 0x23, 0x0e, 0x64, 0xe2,
+ 0x01, 0x04, 0x2e, 0x60, 0xe2, 0x48, 0xe5, 0x1b,
+ 0x27, 0x06, 0x27, 0x06, 0x27, 0x16, 0x07, 0x06,
+ 0x20, 0xe9, 0x02, 0xa0, 0xe5, 0xab, 0x1c, 0xe0,
+ 0x04, 0xe5, 0x0f, 0x60, 0xe5, 0x29, 0x60, 0xfc,
+ 0x87, 0x78, 0xfd, 0x98, 0x78, 0xe5, 0x80, 0xe6,
+ 0x20, 0xe5, 0x62, 0xe0, 0x1e, 0xc2, 0xe0, 0x04,
+ 0x82, 0x80, 0x05, 0x06, 0xe5, 0x02, 0x0c, 0xe5,
+ 0x05, 0x00, 0x85, 0x00, 0x05, 0x00, 0x25, 0x00,
+ 0x25, 0x00, 0xe5, 0x64, 0xee, 0x09, 0xe0, 0x08,
+ 0xe5, 0x80, 0xe3, 0x13, 0x12, 0xef, 0x08, 0xe5,
+ 0x38, 0x20, 0xe5, 0x2e, 0xc0, 0x0f, 0xe0, 0x18,
+ 0xe5, 0x04, 0x0d, 0x4f, 0xe6, 0x08, 0xd6, 0x12,
+ 0x13, 0x16, 0xa0, 0xe6, 0x08, 0x16, 0x31, 0x30,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13,
+ 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13,
+ 0x36, 0x12, 0x13, 0x76, 0x50, 0x56, 0x00, 0x76,
+ 0x11, 0x12, 0x13, 0x12, 0x13, 0x12, 0x13, 0x56,
+ 0x0c, 0x11, 0x4c, 0x00, 0x16, 0x0d, 0x36, 0x60,
+ 0x85, 0x00, 0xe5, 0x7f, 0x20, 0x1b, 0x00, 0x56,
+ 0x0d, 0x56, 0x12, 0x13, 0x16, 0x0c, 0x16, 0x11,
+ 0x36, 0xe9, 0x02, 0x36, 0x4c, 0x36, 0xe1, 0x12,
+ 0x12, 0x16, 0x13, 0x0e, 0x10, 0x0e, 0xe2, 0x12,
+ 0x12, 0x0c, 0x13, 0x0c, 0x12, 0x13, 0x16, 0x12,
+ 0x13, 0x36, 0xe5, 0x02, 0x04, 0xe5, 0x25, 0x24,
+ 0xe5, 0x17, 0x40, 0xa5, 0x20, 0xa5, 0x20, 0xa5,
+ 0x20, 0x45, 0x40, 0x2d, 0x0c, 0x0e, 0x0f, 0x2d,
+ 0x00, 0x0f, 0x6c, 0x2f, 0xe0, 0x02, 0x5b, 0x2f,
+ 0x20, 0xe5, 0x04, 0x00, 0xe5, 0x12, 0x00, 0xe5,
+ 0x0b, 0x00, 0x25, 0x00, 0xe5, 0x07, 0x20, 0xe5,
+ 0x06, 0xe0, 0x1a, 0xe5, 0x73, 0x80, 0x56, 0x60,
+ 0xeb, 0x25, 0x40, 0xef, 0x01, 0xea, 0x2d, 0x6b,
+ 0xef, 0x09, 0x2b, 0x4f, 0x00, 0xef, 0x05, 0x40,
+ 0x0f, 0xe0, 0x27, 0xef, 0x25, 0x06, 0xe0, 0x7a,
+ 0xe5, 0x15, 0x40, 0xe5, 0x29, 0xe0, 0x07, 0x06,
+ 0xeb, 0x13, 0x60, 0xe5, 0x18, 0x6b, 0xe0, 0x01,
+ 0xe5, 0x0c, 0x0a, 0xe5, 0x00, 0x0a, 0x80, 0xe5,
+ 0x1e, 0x86, 0x80, 0xe5, 0x16, 0x00, 0x16, 0xe5,
+ 0x1c, 0x60, 0xe5, 0x00, 0x16, 0x8a, 0xe0, 0x22,
+ 0xe1, 0x20, 0xe2, 0x20, 0xe5, 0x46, 0x20, 0xe9,
+ 0x02, 0xa0, 0xe1, 0x1c, 0x60, 0xe2, 0x1c, 0x60,
+ 0xe5, 0x20, 0xe0, 0x00, 0xe5, 0x2c, 0xe0, 0x03,
+ 0x16, 0xe1, 0x03, 0x00, 0xe1, 0x07, 0x00, 0xc1,
+ 0x00, 0x21, 0x00, 0xe2, 0x03, 0x00, 0xe2, 0x07,
+ 0x00, 0xc2, 0x00, 0x22, 0xe0, 0x3b, 0xe5, 0x80,
+ 0xaf, 0xe0, 0x01, 0xe5, 0x0e, 0xe0, 0x02, 0xe5,
+ 0x00, 0xe0, 0x10, 0xa4, 0x00, 0xe4, 0x22, 0x00,
+ 0xe4, 0x01, 0xe0, 0x3d, 0xa5, 0x20, 0x05, 0x00,
+ 0xe5, 0x24, 0x00, 0x25, 0x40, 0x05, 0x20, 0xe5,
+ 0x0f, 0x00, 0x16, 0xeb, 0x00, 0xe5, 0x0f, 0x2f,
+ 0xcb, 0xe5, 0x17, 0xe0, 0x00, 0xeb, 0x01, 0xe0,
+ 0x28, 0xe5, 0x0b, 0x00, 0x25, 0x80, 0x8b, 0xe5,
+ 0x0e, 0xab, 0x40, 0x16, 0xe5, 0x12, 0x80, 0x16,
+ 0xe0, 0x38, 0xe5, 0x30, 0x60, 0x2b, 0x25, 0xeb,
+ 0x08, 0x20, 0xeb, 0x26, 0x05, 0x46, 0x00, 0x26,
+ 0x80, 0x66, 0x65, 0x00, 0x45, 0x00, 0xe5, 0x15,
+ 0x20, 0x46, 0x60, 0x06, 0xeb, 0x01, 0xc0, 0xf6,
+ 0x01, 0xc0, 0xe5, 0x15, 0x2b, 0x16, 0xe5, 0x15,
+ 0x4b, 0xe0, 0x18, 0xe5, 0x00, 0x0f, 0xe5, 0x14,
+ 0x26, 0x60, 0x8b, 0xd6, 0xe0, 0x01, 0xe5, 0x2e,
+ 0x40, 0xd6, 0xe5, 0x0e, 0x20, 0xeb, 0x00, 0xe5,
+ 0x0b, 0x80, 0xeb, 0x00, 0xe5, 0x0a, 0xc0, 0x76,
+ 0xe0, 0x04, 0xcb, 0xe0, 0x48, 0xe5, 0x41, 0xe0,
+ 0x2f, 0xe1, 0x2b, 0xe0, 0x05, 0xe2, 0x2b, 0xc0,
+ 0xab, 0xe5, 0x1c, 0x66, 0xe0, 0x00, 0xe9, 0x02,
+ 0xe0, 0x80, 0x9e, 0xeb, 0x17, 0x00, 0xe5, 0x22,
+ 0x00, 0x26, 0x11, 0x20, 0x25, 0xe0, 0x46, 0xe5,
+ 0x15, 0xeb, 0x02, 0x05, 0xe0, 0x00, 0xe5, 0x0e,
+ 0xe6, 0x03, 0x6b, 0x96, 0xe0, 0x0e, 0xe5, 0x0a,
+ 0x66, 0x76, 0xe0, 0x1e, 0xe5, 0x0d, 0xcb, 0xe0,
+ 0x0c, 0xe5, 0x0f, 0xe0, 0x01, 0x07, 0x06, 0x07,
+ 0xe5, 0x2d, 0xe6, 0x07, 0xd6, 0x60, 0xeb, 0x0c,
+ 0xe9, 0x02, 0x06, 0x25, 0x26, 0x05, 0xe0, 0x01,
+ 0x46, 0x07, 0xe5, 0x25, 0x47, 0x66, 0x27, 0x26,
+ 0x36, 0x1b, 0x76, 0x06, 0xe0, 0x02, 0x1b, 0x20,
+ 0xe5, 0x11, 0xc0, 0xe9, 0x02, 0xa0, 0x46, 0xe5,
+ 0x1c, 0x86, 0x07, 0xe6, 0x00, 0x00, 0xe9, 0x02,
+ 0x76, 0x05, 0x27, 0x05, 0xe0, 0x00, 0xe5, 0x1b,
+ 0x06, 0x36, 0x05, 0xe0, 0x01, 0x26, 0x07, 0xe5,
+ 0x28, 0x47, 0xe6, 0x01, 0x27, 0x65, 0x76, 0x66,
+ 0x16, 0x07, 0x06, 0xe9, 0x02, 0x05, 0x16, 0x05,
+ 0x56, 0x00, 0xeb, 0x0c, 0xe0, 0x03, 0xe5, 0x0a,
+ 0x00, 0xe5, 0x11, 0x47, 0x46, 0x27, 0x06, 0x07,
+ 0x26, 0xb6, 0x06, 0xe0, 0x39, 0xc5, 0x00, 0x05,
+ 0x00, 0x65, 0x00, 0xe5, 0x07, 0x00, 0xe5, 0x02,
+ 0x16, 0xa0, 0xe5, 0x27, 0x06, 0x47, 0xe6, 0x00,
+ 0x80, 0xe9, 0x02, 0xa0, 0x26, 0x27, 0x00, 0xe5,
+ 0x00, 0x20, 0x25, 0x20, 0xe5, 0x0e, 0x00, 0xc5,
+ 0x00, 0x25, 0x00, 0x85, 0x00, 0x26, 0x05, 0x27,
+ 0x06, 0x67, 0x20, 0x27, 0x20, 0x47, 0x20, 0x05,
+ 0xa0, 0x07, 0x80, 0x85, 0x27, 0x20, 0xc6, 0x40,
+ 0x86, 0xe0, 0x80, 0x03, 0xe5, 0x2d, 0x47, 0xe6,
+ 0x00, 0x27, 0x46, 0x07, 0x06, 0x65, 0x96, 0xe9,
+ 0x02, 0x36, 0x00, 0x16, 0x06, 0x45, 0xe0, 0x16,
+ 0xe5, 0x28, 0x47, 0xa6, 0x07, 0x06, 0x67, 0x26,
+ 0x07, 0x26, 0x25, 0x16, 0x05, 0xe0, 0x00, 0xe9,
+ 0x02, 0xe0, 0x80, 0x1e, 0xe5, 0x27, 0x47, 0x66,
+ 0x20, 0x67, 0x26, 0x07, 0x26, 0xf6, 0x0f, 0x65,
+ 0x26, 0xe0, 0x1a, 0xe5, 0x28, 0x47, 0xe6, 0x00,
+ 0x27, 0x06, 0x07, 0x26, 0x56, 0x05, 0xe0, 0x03,
+ 0xe9, 0x02, 0xa0, 0xf6, 0x05, 0xe0, 0x0b, 0xe5,
+ 0x23, 0x06, 0x07, 0x06, 0x27, 0xa6, 0x07, 0x06,
+ 0x05, 0x16, 0xa0, 0xe9, 0x02, 0xe0, 0x2e, 0xe5,
+ 0x13, 0x20, 0x46, 0x27, 0x66, 0x07, 0x86, 0x60,
+ 0xe9, 0x02, 0x2b, 0x56, 0x0f, 0xc5, 0xe0, 0x80,
+ 0x31, 0xe5, 0x24, 0x47, 0xe6, 0x01, 0x07, 0x26,
+ 0x16, 0xe0, 0x5c, 0xe1, 0x18, 0xe2, 0x18, 0xe9,
+ 0x02, 0xeb, 0x01, 0xe0, 0x04, 0xe5, 0x00, 0x20,
+ 0x05, 0x20, 0xe5, 0x00, 0x00, 0x25, 0x00, 0xe5,
+ 0x10, 0xa7, 0x00, 0x27, 0x20, 0x26, 0x07, 0x06,
+ 0x05, 0x07, 0x05, 0x07, 0x06, 0x56, 0xe0, 0x01,
+ 0xe9, 0x02, 0xe0, 0x3e, 0xe5, 0x00, 0x20, 0xe5,
+ 0x1f, 0x47, 0x66, 0x20, 0x26, 0x67, 0x06, 0x05,
+ 0x16, 0x05, 0x07, 0xe0, 0x13, 0x05, 0xe6, 0x02,
+ 0xe5, 0x20, 0xa6, 0x07, 0x05, 0x66, 0xf6, 0x00,
+ 0x06, 0xe0, 0x00, 0x05, 0xa6, 0x27, 0x46, 0xe5,
+ 0x26, 0xe6, 0x05, 0x07, 0x26, 0x56, 0x05, 0x96,
+ 0xe0, 0x05, 0xe5, 0x41, 0xe0, 0x80, 0x7f, 0xe5,
+ 0x01, 0x00, 0xe5, 0x1d, 0x07, 0xc6, 0x00, 0xa6,
+ 0x07, 0x06, 0x05, 0x96, 0xe0, 0x02, 0xe9, 0x02,
+ 0xeb, 0x0b, 0x40, 0x36, 0xe5, 0x16, 0x20, 0xe6,
+ 0x0e, 0x00, 0x07, 0xc6, 0x07, 0x26, 0x07, 0x26,
+ 0xe0, 0x41, 0xc5, 0x00, 0x25, 0x00, 0xe5, 0x1e,
+ 0xa6, 0x40, 0x06, 0x00, 0x26, 0x00, 0xc6, 0x05,
+ 0x06, 0xe0, 0x00, 0xe9, 0x02, 0xa0, 0xa5, 0x00,
+ 0x25, 0x00, 0xe5, 0x18, 0x87, 0x00, 0x26, 0x00,
+ 0x27, 0x06, 0x07, 0x06, 0x05, 0xc0, 0xe9, 0x02,
+ 0xe0, 0x80, 0xae, 0xe5, 0x0b, 0x26, 0x27, 0x36,
+ 0xe0, 0x80, 0x2f, 0x05, 0xe0, 0x07, 0xeb, 0x0d,
+ 0xef, 0x00, 0x6d, 0xef, 0x09, 0xe0, 0x05, 0x16,
+ 0xe5, 0x83, 0x12, 0xe0, 0x5e, 0xea, 0x67, 0x00,
+ 0x96, 0xe0, 0x03, 0xe5, 0x80, 0x3c, 0xe0, 0x89,
+ 0xc4, 0xe5, 0x59, 0x36, 0xe0, 0x05, 0xe5, 0x83,
+ 0xa7, 0x00, 0xfb, 0x01, 0xe0, 0x8f, 0x3f, 0xe5,
+ 0x81, 0xbf, 0xe0, 0xa1, 0x31, 0xe5, 0x81, 0xb1,
+ 0xc0, 0xe5, 0x17, 0x00, 0xe9, 0x02, 0x60, 0x36,
+ 0xe5, 0x47, 0x00, 0xe9, 0x02, 0xa0, 0xe5, 0x16,
+ 0x20, 0x86, 0x16, 0xe0, 0x02, 0xe5, 0x28, 0xc6,
+ 0x96, 0x6f, 0x64, 0x16, 0x0f, 0xe0, 0x02, 0xe9,
+ 0x02, 0x00, 0xcb, 0x00, 0xe5, 0x0d, 0x80, 0xe5,
+ 0x0b, 0xe0, 0x82, 0x28, 0xe1, 0x18, 0xe2, 0x18,
+ 0xeb, 0x0f, 0x76, 0xe0, 0x5d, 0xe5, 0x43, 0x60,
+ 0x06, 0x05, 0xe7, 0x2f, 0xc0, 0x66, 0xe4, 0x05,
+ 0xe0, 0x38, 0x24, 0x16, 0x04, 0x06, 0xe0, 0x03,
+ 0x27, 0xe0, 0x06, 0xe5, 0x97, 0x70, 0xe0, 0x00,
+ 0xe5, 0x84, 0x4e, 0xe0, 0x22, 0xe5, 0x01, 0xe0,
+ 0xa2, 0x5f, 0x64, 0x00, 0xc4, 0x00, 0x24, 0x00,
+ 0xe5, 0x80, 0x9b, 0xe0, 0x25, 0x45, 0xe0, 0x09,
+ 0x65, 0xe0, 0x00, 0xe5, 0x81, 0x04, 0xe0, 0x88,
+ 0x7c, 0xe5, 0x63, 0x80, 0xe5, 0x05, 0x40, 0xe5,
+ 0x01, 0xc0, 0xe5, 0x02, 0x20, 0x0f, 0x26, 0x16,
+ 0x7b, 0xe0, 0x91, 0xd4, 0xe6, 0x26, 0x20, 0xe6,
+ 0x0f, 0xe0, 0x01, 0xef, 0x6c, 0xe0, 0x34, 0xef,
+ 0x80, 0x6e, 0xe0, 0x02, 0xef, 0x1f, 0x20, 0xef,
+ 0x34, 0x27, 0x46, 0x4f, 0xa7, 0xfb, 0x00, 0xe6,
+ 0x00, 0x2f, 0xc6, 0xef, 0x16, 0x66, 0xef, 0x35,
+ 0xe0, 0x0d, 0xef, 0x3a, 0x46, 0x0f, 0xe0, 0x80,
+ 0x12, 0xeb, 0x0c, 0xe0, 0x04, 0xef, 0x4f, 0xe0,
+ 0x01, 0xeb, 0x11, 0xe0, 0x7f, 0xe1, 0x12, 0xe2,
+ 0x12, 0xe1, 0x12, 0xc2, 0x00, 0xe2, 0x0a, 0xe1,
+ 0x12, 0xe2, 0x12, 0x01, 0x00, 0x21, 0x20, 0x01,
+ 0x20, 0x21, 0x20, 0x61, 0x00, 0xe1, 0x00, 0x62,
+ 0x00, 0x02, 0x00, 0xc2, 0x00, 0xe2, 0x03, 0xe1,
+ 0x12, 0xe2, 0x12, 0x21, 0x00, 0x61, 0x20, 0xe1,
+ 0x00, 0x00, 0xc1, 0x00, 0xe2, 0x12, 0x21, 0x00,
+ 0x61, 0x00, 0x81, 0x00, 0x01, 0x40, 0xc1, 0x00,
+ 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12,
+ 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12,
+ 0xe2, 0x12, 0xe1, 0x12, 0xe2, 0x12, 0xe1, 0x12,
+ 0xe2, 0x14, 0x20, 0xe1, 0x11, 0x0c, 0xe2, 0x11,
+ 0x0c, 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c,
+ 0xa2, 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2,
+ 0xe1, 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0xe1,
+ 0x11, 0x0c, 0xe2, 0x11, 0x0c, 0xa2, 0x3f, 0x20,
+ 0xe9, 0x2a, 0xef, 0x81, 0x78, 0xe6, 0x2f, 0x6f,
+ 0xe6, 0x2a, 0xef, 0x00, 0x06, 0xef, 0x06, 0x06,
+ 0x2f, 0x96, 0xe0, 0x07, 0x86, 0x00, 0xe6, 0x07,
+ 0xe0, 0x83, 0xc8, 0xe2, 0x02, 0x05, 0xe2, 0x0c,
+ 0xe0, 0x80, 0x59, 0xc6, 0x00, 0xe6, 0x09, 0x20,
+ 0xc6, 0x00, 0x26, 0x00, 0x86, 0xe0, 0x80, 0x4d,
+ 0xe5, 0x25, 0x40, 0xc6, 0xc4, 0x20, 0xe9, 0x02,
+ 0x60, 0x05, 0x0f, 0xe0, 0x80, 0xb8, 0xe5, 0x16,
+ 0x06, 0xe0, 0x09, 0xe5, 0x24, 0x66, 0xe9, 0x02,
+ 0x80, 0x0d, 0xe0, 0x84, 0x58, 0xc5, 0x00, 0x65,
+ 0x00, 0x25, 0x00, 0xe5, 0x07, 0x00, 0xe5, 0x80,
+ 0x3d, 0x20, 0xeb, 0x01, 0xc6, 0xe0, 0x21, 0xe1,
+ 0x1a, 0xe2, 0x1a, 0xc6, 0x04, 0x60, 0xe9, 0x02,
+ 0x60, 0x36, 0xe0, 0x82, 0x89, 0xeb, 0x33, 0x0f,
+ 0x4b, 0x0d, 0x6b, 0xe0, 0x44, 0xeb, 0x25, 0x0f,
+ 0xeb, 0x07, 0xe0, 0x80, 0x3a, 0x65, 0x00, 0xe5,
+ 0x13, 0x00, 0x25, 0x00, 0x05, 0x20, 0x05, 0x00,
+ 0xe5, 0x02, 0x00, 0x65, 0x00, 0x05, 0x00, 0x05,
+ 0xa0, 0x05, 0x60, 0x05, 0x00, 0x05, 0x00, 0x05,
+ 0x00, 0x45, 0x00, 0x25, 0x00, 0x05, 0x20, 0x05,
+ 0x00, 0x05, 0x00, 0x05, 0x00, 0x05, 0x00, 0x05,
+ 0x00, 0x25, 0x00, 0x05, 0x20, 0x65, 0x00, 0xc5,
+ 0x00, 0x65, 0x00, 0x65, 0x00, 0x05, 0x00, 0xe5,
+ 0x02, 0x00, 0xe5, 0x09, 0x80, 0x45, 0x00, 0x85,
+ 0x00, 0xe5, 0x09, 0xe0, 0x2c, 0x2c, 0xe0, 0x80,
+ 0x86, 0xef, 0x24, 0x60, 0xef, 0x5c, 0xe0, 0x04,
+ 0xef, 0x07, 0x20, 0xef, 0x07, 0x00, 0xef, 0x07,
+ 0x00, 0xef, 0x1d, 0xe0, 0x02, 0xeb, 0x05, 0xef,
+ 0x80, 0x19, 0xe0, 0x30, 0xef, 0x15, 0xe0, 0x05,
+ 0xef, 0x24, 0x60, 0xef, 0x01, 0xc0, 0x2f, 0xe0,
+ 0x06, 0xaf, 0xe0, 0x80, 0x12, 0xef, 0x80, 0x73,
+ 0x8e, 0xef, 0x82, 0x50, 0x80, 0xef, 0x08, 0x40,
+ 0xef, 0x05, 0x40, 0xef, 0x6c, 0xe0, 0x04, 0xef,
+ 0x51, 0xc0, 0xef, 0x04, 0x60, 0x0f, 0xe0, 0x07,
+ 0xef, 0x04, 0x60, 0xef, 0x30, 0xe0, 0x00, 0xef,
+ 0x02, 0xa0, 0xef, 0x20, 0xe0, 0x00, 0xef, 0x16,
+ 0x20, 0x2f, 0xe0, 0x46, 0xef, 0x80, 0xcc, 0xe0,
+ 0x04, 0xef, 0x06, 0x20, 0x8f, 0x40, 0x8f, 0x40,
+ 0xcf, 0xe0, 0x01, 0xef, 0x15, 0x40, 0xef, 0x03,
+ 0x80, 0xaf, 0xe0, 0x02, 0xef, 0x02, 0xa0, 0xef,
+ 0x00, 0xe0, 0x00, 0xcf, 0xe0, 0x01, 0xef, 0x80,
+ 0x0b, 0x00, 0xef, 0x2f, 0xe0, 0x1d, 0xe9, 0x02,
+ 0xe0, 0x83, 0x7e, 0xe5, 0xc0, 0x66, 0x58, 0xe0,
+ 0x18, 0xe5, 0x8f, 0xb1, 0xc0, 0xe5, 0x80, 0x56,
+ 0x20, 0xe5, 0x95, 0xfa, 0xe0, 0x06, 0xe5, 0x9c,
+ 0xa9, 0xe0, 0x8b, 0x97, 0xe5, 0x81, 0x96, 0xe0,
+ 0x85, 0x5a, 0xe5, 0x92, 0xc3, 0xe0, 0xca, 0xac,
+ 0x2e, 0x1b, 0xe0, 0x16, 0xfb, 0x58, 0xe0, 0x78,
+ 0xe6, 0x80, 0x68, 0xe0, 0xc0, 0xbd, 0x88, 0xfd,
+ 0xc0, 0xbf, 0x76, 0x20, 0xfd, 0xc0, 0xbf, 0x76,
+ 0x20,
+};
+
+typedef enum {
+ UNICODE_SCRIPT_Unknown,
+ UNICODE_SCRIPT_Adlam,
+ UNICODE_SCRIPT_Ahom,
+ UNICODE_SCRIPT_Anatolian_Hieroglyphs,
+ UNICODE_SCRIPT_Arabic,
+ UNICODE_SCRIPT_Armenian,
+ UNICODE_SCRIPT_Avestan,
+ UNICODE_SCRIPT_Balinese,
+ UNICODE_SCRIPT_Bamum,
+ UNICODE_SCRIPT_Bassa_Vah,
+ UNICODE_SCRIPT_Batak,
+ UNICODE_SCRIPT_Bengali,
+ UNICODE_SCRIPT_Bhaiksuki,
+ UNICODE_SCRIPT_Bopomofo,
+ UNICODE_SCRIPT_Brahmi,
+ UNICODE_SCRIPT_Braille,
+ UNICODE_SCRIPT_Buginese,
+ UNICODE_SCRIPT_Buhid,
+ UNICODE_SCRIPT_Canadian_Aboriginal,
+ UNICODE_SCRIPT_Carian,
+ UNICODE_SCRIPT_Caucasian_Albanian,
+ UNICODE_SCRIPT_Chakma,
+ UNICODE_SCRIPT_Cham,
+ UNICODE_SCRIPT_Cherokee,
+ UNICODE_SCRIPT_Chorasmian,
+ UNICODE_SCRIPT_Common,
+ UNICODE_SCRIPT_Coptic,
+ UNICODE_SCRIPT_Cuneiform,
+ UNICODE_SCRIPT_Cypriot,
+ UNICODE_SCRIPT_Cyrillic,
+ UNICODE_SCRIPT_Cypro_Minoan,
+ UNICODE_SCRIPT_Deseret,
+ UNICODE_SCRIPT_Devanagari,
+ UNICODE_SCRIPT_Dives_Akuru,
+ UNICODE_SCRIPT_Dogra,
+ UNICODE_SCRIPT_Duployan,
+ UNICODE_SCRIPT_Egyptian_Hieroglyphs,
+ UNICODE_SCRIPT_Elbasan,
+ UNICODE_SCRIPT_Elymaic,
+ UNICODE_SCRIPT_Ethiopic,
+ UNICODE_SCRIPT_Georgian,
+ UNICODE_SCRIPT_Glagolitic,
+ UNICODE_SCRIPT_Gothic,
+ UNICODE_SCRIPT_Grantha,
+ UNICODE_SCRIPT_Greek,
+ UNICODE_SCRIPT_Gujarati,
+ UNICODE_SCRIPT_Gunjala_Gondi,
+ UNICODE_SCRIPT_Gurmukhi,
+ UNICODE_SCRIPT_Han,
+ UNICODE_SCRIPT_Hangul,
+ UNICODE_SCRIPT_Hanifi_Rohingya,
+ UNICODE_SCRIPT_Hanunoo,
+ UNICODE_SCRIPT_Hatran,
+ UNICODE_SCRIPT_Hebrew,
+ UNICODE_SCRIPT_Hiragana,
+ UNICODE_SCRIPT_Imperial_Aramaic,
+ UNICODE_SCRIPT_Inherited,
+ UNICODE_SCRIPT_Inscriptional_Pahlavi,
+ UNICODE_SCRIPT_Inscriptional_Parthian,
+ UNICODE_SCRIPT_Javanese,
+ UNICODE_SCRIPT_Kaithi,
+ UNICODE_SCRIPT_Kannada,
+ UNICODE_SCRIPT_Katakana,
+ UNICODE_SCRIPT_Kayah_Li,
+ UNICODE_SCRIPT_Kharoshthi,
+ UNICODE_SCRIPT_Khmer,
+ UNICODE_SCRIPT_Khojki,
+ UNICODE_SCRIPT_Khitan_Small_Script,
+ UNICODE_SCRIPT_Khudawadi,
+ UNICODE_SCRIPT_Lao,
+ UNICODE_SCRIPT_Latin,
+ UNICODE_SCRIPT_Lepcha,
+ UNICODE_SCRIPT_Limbu,
+ UNICODE_SCRIPT_Linear_A,
+ UNICODE_SCRIPT_Linear_B,
+ UNICODE_SCRIPT_Lisu,
+ UNICODE_SCRIPT_Lycian,
+ UNICODE_SCRIPT_Lydian,
+ UNICODE_SCRIPT_Makasar,
+ UNICODE_SCRIPT_Mahajani,
+ UNICODE_SCRIPT_Malayalam,
+ UNICODE_SCRIPT_Mandaic,
+ UNICODE_SCRIPT_Manichaean,
+ UNICODE_SCRIPT_Marchen,
+ UNICODE_SCRIPT_Masaram_Gondi,
+ UNICODE_SCRIPT_Medefaidrin,
+ UNICODE_SCRIPT_Meetei_Mayek,
+ UNICODE_SCRIPT_Mende_Kikakui,
+ UNICODE_SCRIPT_Meroitic_Cursive,
+ UNICODE_SCRIPT_Meroitic_Hieroglyphs,
+ UNICODE_SCRIPT_Miao,
+ UNICODE_SCRIPT_Modi,
+ UNICODE_SCRIPT_Mongolian,
+ UNICODE_SCRIPT_Mro,
+ UNICODE_SCRIPT_Multani,
+ UNICODE_SCRIPT_Myanmar,
+ UNICODE_SCRIPT_Nabataean,
+ UNICODE_SCRIPT_Nandinagari,
+ UNICODE_SCRIPT_New_Tai_Lue,
+ UNICODE_SCRIPT_Newa,
+ UNICODE_SCRIPT_Nko,
+ UNICODE_SCRIPT_Nushu,
+ UNICODE_SCRIPT_Nyiakeng_Puachue_Hmong,
+ UNICODE_SCRIPT_Ogham,
+ UNICODE_SCRIPT_Ol_Chiki,
+ UNICODE_SCRIPT_Old_Hungarian,
+ UNICODE_SCRIPT_Old_Italic,
+ UNICODE_SCRIPT_Old_North_Arabian,
+ UNICODE_SCRIPT_Old_Permic,
+ UNICODE_SCRIPT_Old_Persian,
+ UNICODE_SCRIPT_Old_Sogdian,
+ UNICODE_SCRIPT_Old_South_Arabian,
+ UNICODE_SCRIPT_Old_Turkic,
+ UNICODE_SCRIPT_Old_Uyghur,
+ UNICODE_SCRIPT_Oriya,
+ UNICODE_SCRIPT_Osage,
+ UNICODE_SCRIPT_Osmanya,
+ UNICODE_SCRIPT_Pahawh_Hmong,
+ UNICODE_SCRIPT_Palmyrene,
+ UNICODE_SCRIPT_Pau_Cin_Hau,
+ UNICODE_SCRIPT_Phags_Pa,
+ UNICODE_SCRIPT_Phoenician,
+ UNICODE_SCRIPT_Psalter_Pahlavi,
+ UNICODE_SCRIPT_Rejang,
+ UNICODE_SCRIPT_Runic,
+ UNICODE_SCRIPT_Samaritan,
+ UNICODE_SCRIPT_Saurashtra,
+ UNICODE_SCRIPT_Sharada,
+ UNICODE_SCRIPT_Shavian,
+ UNICODE_SCRIPT_Siddham,
+ UNICODE_SCRIPT_SignWriting,
+ UNICODE_SCRIPT_Sinhala,
+ UNICODE_SCRIPT_Sogdian,
+ UNICODE_SCRIPT_Sora_Sompeng,
+ UNICODE_SCRIPT_Soyombo,
+ UNICODE_SCRIPT_Sundanese,
+ UNICODE_SCRIPT_Syloti_Nagri,
+ UNICODE_SCRIPT_Syriac,
+ UNICODE_SCRIPT_Tagalog,
+ UNICODE_SCRIPT_Tagbanwa,
+ UNICODE_SCRIPT_Tai_Le,
+ UNICODE_SCRIPT_Tai_Tham,
+ UNICODE_SCRIPT_Tai_Viet,
+ UNICODE_SCRIPT_Takri,
+ UNICODE_SCRIPT_Tamil,
+ UNICODE_SCRIPT_Tangut,
+ UNICODE_SCRIPT_Telugu,
+ UNICODE_SCRIPT_Thaana,
+ UNICODE_SCRIPT_Thai,
+ UNICODE_SCRIPT_Tibetan,
+ UNICODE_SCRIPT_Tifinagh,
+ UNICODE_SCRIPT_Tirhuta,
+ UNICODE_SCRIPT_Tangsa,
+ UNICODE_SCRIPT_Toto,
+ UNICODE_SCRIPT_Ugaritic,
+ UNICODE_SCRIPT_Vai,
+ UNICODE_SCRIPT_Vithkuqi,
+ UNICODE_SCRIPT_Wancho,
+ UNICODE_SCRIPT_Warang_Citi,
+ UNICODE_SCRIPT_Yezidi,
+ UNICODE_SCRIPT_Yi,
+ UNICODE_SCRIPT_Zanabazar_Square,
+ UNICODE_SCRIPT_COUNT,
+} UnicodeScriptEnum;
+
+static const char unicode_script_name_table[] =
+ "Adlam,Adlm" "\0"
+ "Ahom,Ahom" "\0"
+ "Anatolian_Hieroglyphs,Hluw" "\0"
+ "Arabic,Arab" "\0"
+ "Armenian,Armn" "\0"
+ "Avestan,Avst" "\0"
+ "Balinese,Bali" "\0"
+ "Bamum,Bamu" "\0"
+ "Bassa_Vah,Bass" "\0"
+ "Batak,Batk" "\0"
+ "Bengali,Beng" "\0"
+ "Bhaiksuki,Bhks" "\0"
+ "Bopomofo,Bopo" "\0"
+ "Brahmi,Brah" "\0"
+ "Braille,Brai" "\0"
+ "Buginese,Bugi" "\0"
+ "Buhid,Buhd" "\0"
+ "Canadian_Aboriginal,Cans" "\0"
+ "Carian,Cari" "\0"
+ "Caucasian_Albanian,Aghb" "\0"
+ "Chakma,Cakm" "\0"
+ "Cham,Cham" "\0"
+ "Cherokee,Cher" "\0"
+ "Chorasmian,Chrs" "\0"
+ "Common,Zyyy" "\0"
+ "Coptic,Copt,Qaac" "\0"
+ "Cuneiform,Xsux" "\0"
+ "Cypriot,Cprt" "\0"
+ "Cyrillic,Cyrl" "\0"
+ "Cypro_Minoan,Cpmn" "\0"
+ "Deseret,Dsrt" "\0"
+ "Devanagari,Deva" "\0"
+ "Dives_Akuru,Diak" "\0"
+ "Dogra,Dogr" "\0"
+ "Duployan,Dupl" "\0"
+ "Egyptian_Hieroglyphs,Egyp" "\0"
+ "Elbasan,Elba" "\0"
+ "Elymaic,Elym" "\0"
+ "Ethiopic,Ethi" "\0"
+ "Georgian,Geor" "\0"
+ "Glagolitic,Glag" "\0"
+ "Gothic,Goth" "\0"
+ "Grantha,Gran" "\0"
+ "Greek,Grek" "\0"
+ "Gujarati,Gujr" "\0"
+ "Gunjala_Gondi,Gong" "\0"
+ "Gurmukhi,Guru" "\0"
+ "Han,Hani" "\0"
+ "Hangul,Hang" "\0"
+ "Hanifi_Rohingya,Rohg" "\0"
+ "Hanunoo,Hano" "\0"
+ "Hatran,Hatr" "\0"
+ "Hebrew,Hebr" "\0"
+ "Hiragana,Hira" "\0"
+ "Imperial_Aramaic,Armi" "\0"
+ "Inherited,Zinh,Qaai" "\0"
+ "Inscriptional_Pahlavi,Phli" "\0"
+ "Inscriptional_Parthian,Prti" "\0"
+ "Javanese,Java" "\0"
+ "Kaithi,Kthi" "\0"
+ "Kannada,Knda" "\0"
+ "Katakana,Kana" "\0"
+ "Kayah_Li,Kali" "\0"
+ "Kharoshthi,Khar" "\0"
+ "Khmer,Khmr" "\0"
+ "Khojki,Khoj" "\0"
+ "Khitan_Small_Script,Kits" "\0"
+ "Khudawadi,Sind" "\0"
+ "Lao,Laoo" "\0"
+ "Latin,Latn" "\0"
+ "Lepcha,Lepc" "\0"
+ "Limbu,Limb" "\0"
+ "Linear_A,Lina" "\0"
+ "Linear_B,Linb" "\0"
+ "Lisu,Lisu" "\0"
+ "Lycian,Lyci" "\0"
+ "Lydian,Lydi" "\0"
+ "Makasar,Maka" "\0"
+ "Mahajani,Mahj" "\0"
+ "Malayalam,Mlym" "\0"
+ "Mandaic,Mand" "\0"
+ "Manichaean,Mani" "\0"
+ "Marchen,Marc" "\0"
+ "Masaram_Gondi,Gonm" "\0"
+ "Medefaidrin,Medf" "\0"
+ "Meetei_Mayek,Mtei" "\0"
+ "Mende_Kikakui,Mend" "\0"
+ "Meroitic_Cursive,Merc" "\0"
+ "Meroitic_Hieroglyphs,Mero" "\0"
+ "Miao,Plrd" "\0"
+ "Modi,Modi" "\0"
+ "Mongolian,Mong" "\0"
+ "Mro,Mroo" "\0"
+ "Multani,Mult" "\0"
+ "Myanmar,Mymr" "\0"
+ "Nabataean,Nbat" "\0"
+ "Nandinagari,Nand" "\0"
+ "New_Tai_Lue,Talu" "\0"
+ "Newa,Newa" "\0"
+ "Nko,Nkoo" "\0"
+ "Nushu,Nshu" "\0"
+ "Nyiakeng_Puachue_Hmong,Hmnp" "\0"
+ "Ogham,Ogam" "\0"
+ "Ol_Chiki,Olck" "\0"
+ "Old_Hungarian,Hung" "\0"
+ "Old_Italic,Ital" "\0"
+ "Old_North_Arabian,Narb" "\0"
+ "Old_Permic,Perm" "\0"
+ "Old_Persian,Xpeo" "\0"
+ "Old_Sogdian,Sogo" "\0"
+ "Old_South_Arabian,Sarb" "\0"
+ "Old_Turkic,Orkh" "\0"
+ "Old_Uyghur,Ougr" "\0"
+ "Oriya,Orya" "\0"
+ "Osage,Osge" "\0"
+ "Osmanya,Osma" "\0"
+ "Pahawh_Hmong,Hmng" "\0"
+ "Palmyrene,Palm" "\0"
+ "Pau_Cin_Hau,Pauc" "\0"
+ "Phags_Pa,Phag" "\0"
+ "Phoenician,Phnx" "\0"
+ "Psalter_Pahlavi,Phlp" "\0"
+ "Rejang,Rjng" "\0"
+ "Runic,Runr" "\0"
+ "Samaritan,Samr" "\0"
+ "Saurashtra,Saur" "\0"
+ "Sharada,Shrd" "\0"
+ "Shavian,Shaw" "\0"
+ "Siddham,Sidd" "\0"
+ "SignWriting,Sgnw" "\0"
+ "Sinhala,Sinh" "\0"
+ "Sogdian,Sogd" "\0"
+ "Sora_Sompeng,Sora" "\0"
+ "Soyombo,Soyo" "\0"
+ "Sundanese,Sund" "\0"
+ "Syloti_Nagri,Sylo" "\0"
+ "Syriac,Syrc" "\0"
+ "Tagalog,Tglg" "\0"
+ "Tagbanwa,Tagb" "\0"
+ "Tai_Le,Tale" "\0"
+ "Tai_Tham,Lana" "\0"
+ "Tai_Viet,Tavt" "\0"
+ "Takri,Takr" "\0"
+ "Tamil,Taml" "\0"
+ "Tangut,Tang" "\0"
+ "Telugu,Telu" "\0"
+ "Thaana,Thaa" "\0"
+ "Thai,Thai" "\0"
+ "Tibetan,Tibt" "\0"
+ "Tifinagh,Tfng" "\0"
+ "Tirhuta,Tirh" "\0"
+ "Tangsa,Tnsa" "\0"
+ "Toto,Toto" "\0"
+ "Ugaritic,Ugar" "\0"
+ "Vai,Vaii" "\0"
+ "Vithkuqi,Vith" "\0"
+ "Wancho,Wcho" "\0"
+ "Warang_Citi,Wara" "\0"
+ "Yezidi,Yezi" "\0"
+ "Yi,Yiii" "\0"
+ "Zanabazar_Square,Zanb" "\0"
+;
+
+static const uint8_t unicode_script_table[2690] = {
+ 0xc0, 0x19, 0x99, 0x46, 0x85, 0x19, 0x99, 0x46,
+ 0xae, 0x19, 0x80, 0x46, 0x8e, 0x19, 0x80, 0x46,
+ 0x84, 0x19, 0x96, 0x46, 0x80, 0x19, 0x9e, 0x46,
+ 0x80, 0x19, 0xe1, 0x60, 0x46, 0xa6, 0x19, 0x84,
+ 0x46, 0x84, 0x19, 0x81, 0x0d, 0x93, 0x19, 0xe0,
+ 0x0f, 0x38, 0x83, 0x2c, 0x80, 0x19, 0x82, 0x2c,
+ 0x01, 0x83, 0x2c, 0x80, 0x19, 0x80, 0x2c, 0x03,
+ 0x80, 0x2c, 0x80, 0x19, 0x80, 0x2c, 0x80, 0x19,
+ 0x82, 0x2c, 0x00, 0x80, 0x2c, 0x00, 0x93, 0x2c,
+ 0x00, 0xbe, 0x2c, 0x8d, 0x1a, 0x8f, 0x2c, 0xe0,
+ 0x24, 0x1d, 0x81, 0x38, 0xe0, 0x48, 0x1d, 0x00,
+ 0xa5, 0x05, 0x01, 0xb1, 0x05, 0x01, 0x82, 0x05,
+ 0x00, 0xb6, 0x35, 0x07, 0x9a, 0x35, 0x03, 0x85,
+ 0x35, 0x0a, 0x84, 0x04, 0x80, 0x19, 0x85, 0x04,
+ 0x80, 0x19, 0x8d, 0x04, 0x80, 0x19, 0x82, 0x04,
+ 0x80, 0x19, 0x9f, 0x04, 0x80, 0x19, 0x89, 0x04,
+ 0x8a, 0x38, 0x99, 0x04, 0x80, 0x38, 0xe0, 0x0b,
+ 0x04, 0x80, 0x19, 0xa1, 0x04, 0x8d, 0x89, 0x00,
+ 0xbb, 0x89, 0x01, 0x82, 0x89, 0xaf, 0x04, 0xb1,
+ 0x93, 0x0d, 0xba, 0x64, 0x01, 0x82, 0x64, 0xad,
+ 0x7d, 0x01, 0x8e, 0x7d, 0x00, 0x9b, 0x51, 0x01,
+ 0x80, 0x51, 0x00, 0x8a, 0x89, 0x04, 0x9e, 0x04,
+ 0x00, 0x81, 0x04, 0x05, 0xc9, 0x04, 0x80, 0x19,
+ 0x9c, 0x04, 0xd0, 0x20, 0x83, 0x38, 0x8e, 0x20,
+ 0x81, 0x19, 0x99, 0x20, 0x83, 0x0b, 0x00, 0x87,
+ 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x95, 0x0b, 0x00,
+ 0x86, 0x0b, 0x00, 0x80, 0x0b, 0x02, 0x83, 0x0b,
+ 0x01, 0x88, 0x0b, 0x01, 0x81, 0x0b, 0x01, 0x83,
+ 0x0b, 0x07, 0x80, 0x0b, 0x03, 0x81, 0x0b, 0x00,
+ 0x84, 0x0b, 0x01, 0x98, 0x0b, 0x01, 0x82, 0x2f,
+ 0x00, 0x85, 0x2f, 0x03, 0x81, 0x2f, 0x01, 0x95,
+ 0x2f, 0x00, 0x86, 0x2f, 0x00, 0x81, 0x2f, 0x00,
+ 0x81, 0x2f, 0x00, 0x81, 0x2f, 0x01, 0x80, 0x2f,
+ 0x00, 0x84, 0x2f, 0x03, 0x81, 0x2f, 0x01, 0x82,
+ 0x2f, 0x02, 0x80, 0x2f, 0x06, 0x83, 0x2f, 0x00,
+ 0x80, 0x2f, 0x06, 0x90, 0x2f, 0x09, 0x82, 0x2d,
+ 0x00, 0x88, 0x2d, 0x00, 0x82, 0x2d, 0x00, 0x95,
+ 0x2d, 0x00, 0x86, 0x2d, 0x00, 0x81, 0x2d, 0x00,
+ 0x84, 0x2d, 0x01, 0x89, 0x2d, 0x00, 0x82, 0x2d,
+ 0x00, 0x82, 0x2d, 0x01, 0x80, 0x2d, 0x0e, 0x83,
+ 0x2d, 0x01, 0x8b, 0x2d, 0x06, 0x86, 0x2d, 0x00,
+ 0x82, 0x72, 0x00, 0x87, 0x72, 0x01, 0x81, 0x72,
+ 0x01, 0x95, 0x72, 0x00, 0x86, 0x72, 0x00, 0x81,
+ 0x72, 0x00, 0x84, 0x72, 0x01, 0x88, 0x72, 0x01,
+ 0x81, 0x72, 0x01, 0x82, 0x72, 0x06, 0x82, 0x72,
+ 0x03, 0x81, 0x72, 0x00, 0x84, 0x72, 0x01, 0x91,
+ 0x72, 0x09, 0x81, 0x90, 0x00, 0x85, 0x90, 0x02,
+ 0x82, 0x90, 0x00, 0x83, 0x90, 0x02, 0x81, 0x90,
+ 0x00, 0x80, 0x90, 0x00, 0x81, 0x90, 0x02, 0x81,
+ 0x90, 0x02, 0x82, 0x90, 0x02, 0x8b, 0x90, 0x03,
+ 0x84, 0x90, 0x02, 0x82, 0x90, 0x00, 0x83, 0x90,
+ 0x01, 0x80, 0x90, 0x05, 0x80, 0x90, 0x0d, 0x94,
+ 0x90, 0x04, 0x8c, 0x92, 0x00, 0x82, 0x92, 0x00,
+ 0x96, 0x92, 0x00, 0x8f, 0x92, 0x01, 0x88, 0x92,
+ 0x00, 0x82, 0x92, 0x00, 0x83, 0x92, 0x06, 0x81,
+ 0x92, 0x00, 0x82, 0x92, 0x01, 0x80, 0x92, 0x01,
+ 0x83, 0x92, 0x01, 0x89, 0x92, 0x06, 0x88, 0x92,
+ 0x8c, 0x3d, 0x00, 0x82, 0x3d, 0x00, 0x96, 0x3d,
+ 0x00, 0x89, 0x3d, 0x00, 0x84, 0x3d, 0x01, 0x88,
+ 0x3d, 0x00, 0x82, 0x3d, 0x00, 0x83, 0x3d, 0x06,
+ 0x81, 0x3d, 0x05, 0x81, 0x3d, 0x00, 0x83, 0x3d,
+ 0x01, 0x89, 0x3d, 0x00, 0x81, 0x3d, 0x0c, 0x8c,
+ 0x50, 0x00, 0x82, 0x50, 0x00, 0xb2, 0x50, 0x00,
+ 0x82, 0x50, 0x00, 0x85, 0x50, 0x03, 0x8f, 0x50,
+ 0x01, 0x99, 0x50, 0x00, 0x82, 0x83, 0x00, 0x91,
+ 0x83, 0x02, 0x97, 0x83, 0x00, 0x88, 0x83, 0x00,
+ 0x80, 0x83, 0x01, 0x86, 0x83, 0x02, 0x80, 0x83,
+ 0x03, 0x85, 0x83, 0x00, 0x80, 0x83, 0x00, 0x87,
+ 0x83, 0x05, 0x89, 0x83, 0x01, 0x82, 0x83, 0x0b,
+ 0xb9, 0x94, 0x03, 0x80, 0x19, 0x9b, 0x94, 0x24,
+ 0x81, 0x45, 0x00, 0x80, 0x45, 0x00, 0x84, 0x45,
+ 0x00, 0x97, 0x45, 0x00, 0x80, 0x45, 0x00, 0x96,
+ 0x45, 0x01, 0x84, 0x45, 0x00, 0x80, 0x45, 0x00,
+ 0x85, 0x45, 0x01, 0x89, 0x45, 0x01, 0x83, 0x45,
+ 0x1f, 0xc7, 0x95, 0x00, 0xa3, 0x95, 0x03, 0xa6,
+ 0x95, 0x00, 0xa3, 0x95, 0x00, 0x8e, 0x95, 0x00,
+ 0x86, 0x95, 0x83, 0x19, 0x81, 0x95, 0x24, 0xe0,
+ 0x3f, 0x5f, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04,
+ 0x80, 0x28, 0x01, 0xaa, 0x28, 0x80, 0x19, 0x83,
+ 0x28, 0xe0, 0x9f, 0x31, 0xc8, 0x27, 0x00, 0x83,
+ 0x27, 0x01, 0x86, 0x27, 0x00, 0x80, 0x27, 0x00,
+ 0x83, 0x27, 0x01, 0xa8, 0x27, 0x00, 0x83, 0x27,
+ 0x01, 0xa0, 0x27, 0x00, 0x83, 0x27, 0x01, 0x86,
+ 0x27, 0x00, 0x80, 0x27, 0x00, 0x83, 0x27, 0x01,
+ 0x8e, 0x27, 0x00, 0xb8, 0x27, 0x00, 0x83, 0x27,
+ 0x01, 0xc2, 0x27, 0x01, 0x9f, 0x27, 0x02, 0x99,
+ 0x27, 0x05, 0xd5, 0x17, 0x01, 0x85, 0x17, 0x01,
+ 0xe2, 0x1f, 0x12, 0x9c, 0x67, 0x02, 0xca, 0x7c,
+ 0x82, 0x19, 0x8a, 0x7c, 0x06, 0x95, 0x8a, 0x08,
+ 0x80, 0x8a, 0x94, 0x33, 0x81, 0x19, 0x08, 0x93,
+ 0x11, 0x0b, 0x8c, 0x8b, 0x00, 0x82, 0x8b, 0x00,
+ 0x81, 0x8b, 0x0b, 0xdd, 0x41, 0x01, 0x89, 0x41,
+ 0x05, 0x89, 0x41, 0x05, 0x81, 0x5c, 0x81, 0x19,
+ 0x80, 0x5c, 0x80, 0x19, 0x93, 0x5c, 0x05, 0xd8,
+ 0x5c, 0x06, 0xaa, 0x5c, 0x04, 0xc5, 0x12, 0x09,
+ 0x9e, 0x48, 0x00, 0x8b, 0x48, 0x03, 0x8b, 0x48,
+ 0x03, 0x80, 0x48, 0x02, 0x8b, 0x48, 0x9d, 0x8c,
+ 0x01, 0x84, 0x8c, 0x0a, 0xab, 0x62, 0x03, 0x99,
+ 0x62, 0x05, 0x8a, 0x62, 0x02, 0x81, 0x62, 0x9f,
+ 0x41, 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x8d,
+ 0x00, 0x9c, 0x8d, 0x01, 0x8a, 0x8d, 0x05, 0x89,
+ 0x8d, 0x05, 0x8d, 0x8d, 0x01, 0x9e, 0x38, 0x30,
+ 0xcc, 0x07, 0x02, 0xae, 0x07, 0x00, 0xbf, 0x87,
+ 0xb3, 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x47, 0x02,
+ 0x8e, 0x47, 0x02, 0x82, 0x47, 0xaf, 0x68, 0x88,
+ 0x1d, 0x06, 0xaa, 0x28, 0x01, 0x82, 0x28, 0x87,
+ 0x87, 0x07, 0x82, 0x38, 0x80, 0x19, 0x8c, 0x38,
+ 0x80, 0x19, 0x86, 0x38, 0x83, 0x19, 0x80, 0x38,
+ 0x85, 0x19, 0x80, 0x38, 0x82, 0x19, 0x81, 0x38,
+ 0x80, 0x19, 0x04, 0xa5, 0x46, 0x84, 0x2c, 0x80,
+ 0x1d, 0xb0, 0x46, 0x84, 0x2c, 0x83, 0x46, 0x84,
+ 0x2c, 0x8c, 0x46, 0x80, 0x1d, 0xc5, 0x46, 0x80,
+ 0x2c, 0xbf, 0x38, 0xe0, 0x9f, 0x46, 0x95, 0x2c,
+ 0x01, 0x85, 0x2c, 0x01, 0xa5, 0x2c, 0x01, 0x85,
+ 0x2c, 0x01, 0x87, 0x2c, 0x00, 0x80, 0x2c, 0x00,
+ 0x80, 0x2c, 0x00, 0x80, 0x2c, 0x00, 0x9e, 0x2c,
+ 0x01, 0xb4, 0x2c, 0x00, 0x8e, 0x2c, 0x00, 0x8d,
+ 0x2c, 0x01, 0x85, 0x2c, 0x00, 0x92, 0x2c, 0x01,
+ 0x82, 0x2c, 0x00, 0x88, 0x2c, 0x00, 0x8b, 0x19,
+ 0x81, 0x38, 0xd6, 0x19, 0x00, 0x8a, 0x19, 0x80,
+ 0x46, 0x01, 0x8a, 0x19, 0x80, 0x46, 0x8e, 0x19,
+ 0x00, 0x8c, 0x46, 0x02, 0xa0, 0x19, 0x0e, 0xa0,
+ 0x38, 0x0e, 0xa5, 0x19, 0x80, 0x2c, 0x82, 0x19,
+ 0x81, 0x46, 0x85, 0x19, 0x80, 0x46, 0x9a, 0x19,
+ 0x80, 0x46, 0x90, 0x19, 0xa8, 0x46, 0x82, 0x19,
+ 0x03, 0xe2, 0x36, 0x19, 0x18, 0x8a, 0x19, 0x14,
+ 0xe3, 0x3f, 0x19, 0xe0, 0x9f, 0x0f, 0xe2, 0x13,
+ 0x19, 0x01, 0x9f, 0x19, 0x00, 0xe0, 0x08, 0x19,
+ 0xdf, 0x29, 0x9f, 0x46, 0xe0, 0x13, 0x1a, 0x04,
+ 0x86, 0x1a, 0xa5, 0x28, 0x00, 0x80, 0x28, 0x04,
+ 0x80, 0x28, 0x01, 0xb7, 0x96, 0x06, 0x81, 0x96,
+ 0x0d, 0x80, 0x96, 0x96, 0x27, 0x08, 0x86, 0x27,
+ 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x86,
+ 0x27, 0x00, 0x86, 0x27, 0x00, 0x86, 0x27, 0x00,
+ 0x86, 0x27, 0x00, 0x86, 0x27, 0x00, 0x9f, 0x1d,
+ 0xdd, 0x19, 0x21, 0x99, 0x30, 0x00, 0xd8, 0x30,
+ 0x0b, 0xe0, 0x75, 0x30, 0x19, 0x8b, 0x19, 0x03,
+ 0x84, 0x19, 0x80, 0x30, 0x80, 0x19, 0x80, 0x30,
+ 0x98, 0x19, 0x88, 0x30, 0x83, 0x38, 0x81, 0x31,
+ 0x87, 0x19, 0x83, 0x30, 0x83, 0x19, 0x00, 0xd5,
+ 0x36, 0x01, 0x81, 0x38, 0x81, 0x19, 0x82, 0x36,
+ 0x80, 0x19, 0xd9, 0x3e, 0x81, 0x19, 0x82, 0x3e,
+ 0x04, 0xaa, 0x0d, 0x00, 0xdd, 0x31, 0x00, 0x8f,
+ 0x19, 0x9f, 0x0d, 0xa3, 0x19, 0x0b, 0x8f, 0x3e,
+ 0x9e, 0x31, 0x00, 0xbf, 0x19, 0x9e, 0x31, 0xd0,
+ 0x19, 0xae, 0x3e, 0x80, 0x19, 0xd7, 0x3e, 0xe0,
+ 0x47, 0x19, 0xf0, 0x09, 0x5f, 0x30, 0xbf, 0x19,
+ 0xf0, 0x41, 0x9f, 0x30, 0xe4, 0x2c, 0xa0, 0x02,
+ 0xb6, 0xa0, 0x08, 0xaf, 0x4b, 0xe0, 0xcb, 0x9b,
+ 0x13, 0xdf, 0x1d, 0xd7, 0x08, 0x07, 0xa1, 0x19,
+ 0xe0, 0x05, 0x46, 0x82, 0x19, 0xbf, 0x46, 0x04,
+ 0x81, 0x46, 0x00, 0x80, 0x46, 0x00, 0x84, 0x46,
+ 0x17, 0x8d, 0x46, 0xac, 0x88, 0x02, 0x89, 0x19,
+ 0x05, 0xb7, 0x78, 0x07, 0xc5, 0x7e, 0x07, 0x8b,
+ 0x7e, 0x05, 0x9f, 0x20, 0xad, 0x3f, 0x80, 0x19,
+ 0x80, 0x3f, 0xa3, 0x7b, 0x0a, 0x80, 0x7b, 0x9c,
+ 0x31, 0x02, 0xcd, 0x3b, 0x00, 0x80, 0x19, 0x89,
+ 0x3b, 0x03, 0x81, 0x3b, 0x9e, 0x5f, 0x00, 0xb6,
+ 0x16, 0x08, 0x8d, 0x16, 0x01, 0x89, 0x16, 0x01,
+ 0x83, 0x16, 0x9f, 0x5f, 0xc2, 0x8e, 0x17, 0x84,
+ 0x8e, 0x96, 0x56, 0x09, 0x85, 0x27, 0x01, 0x85,
+ 0x27, 0x01, 0x85, 0x27, 0x08, 0x86, 0x27, 0x00,
+ 0x86, 0x27, 0x00, 0xaa, 0x46, 0x80, 0x19, 0x88,
+ 0x46, 0x80, 0x2c, 0x83, 0x46, 0x81, 0x19, 0x03,
+ 0xcf, 0x17, 0xad, 0x56, 0x01, 0x89, 0x56, 0x05,
+ 0xf0, 0x1b, 0x43, 0x31, 0x0b, 0x96, 0x31, 0x03,
+ 0xb0, 0x31, 0x70, 0x10, 0xa3, 0xe1, 0x0d, 0x30,
+ 0x01, 0xe0, 0x09, 0x30, 0x25, 0x86, 0x46, 0x0b,
+ 0x84, 0x05, 0x04, 0x99, 0x35, 0x00, 0x84, 0x35,
+ 0x00, 0x80, 0x35, 0x00, 0x81, 0x35, 0x00, 0x81,
+ 0x35, 0x00, 0x89, 0x35, 0xe0, 0x12, 0x04, 0x0f,
+ 0xe1, 0x0a, 0x04, 0x81, 0x19, 0xcf, 0x04, 0x01,
+ 0xb5, 0x04, 0x06, 0x80, 0x04, 0x1f, 0x8f, 0x04,
+ 0x8f, 0x38, 0x89, 0x19, 0x05, 0x8d, 0x38, 0x81,
+ 0x1d, 0xa2, 0x19, 0x00, 0x92, 0x19, 0x00, 0x83,
+ 0x19, 0x03, 0x84, 0x04, 0x00, 0xe0, 0x26, 0x04,
+ 0x01, 0x80, 0x19, 0x00, 0x9f, 0x19, 0x99, 0x46,
+ 0x85, 0x19, 0x99, 0x46, 0x8a, 0x19, 0x89, 0x3e,
+ 0x80, 0x19, 0xac, 0x3e, 0x81, 0x19, 0x9e, 0x31,
+ 0x02, 0x85, 0x31, 0x01, 0x85, 0x31, 0x01, 0x85,
+ 0x31, 0x01, 0x82, 0x31, 0x02, 0x86, 0x19, 0x00,
+ 0x86, 0x19, 0x09, 0x84, 0x19, 0x01, 0x8b, 0x4a,
+ 0x00, 0x99, 0x4a, 0x00, 0x92, 0x4a, 0x00, 0x81,
+ 0x4a, 0x00, 0x8e, 0x4a, 0x01, 0x8d, 0x4a, 0x21,
+ 0xe0, 0x1a, 0x4a, 0x04, 0x82, 0x19, 0x03, 0xac,
+ 0x19, 0x02, 0x88, 0x19, 0xce, 0x2c, 0x00, 0x8c,
+ 0x19, 0x02, 0x80, 0x2c, 0x2e, 0xac, 0x19, 0x80,
+ 0x38, 0x60, 0x21, 0x9c, 0x4c, 0x02, 0xb0, 0x13,
+ 0x0e, 0x80, 0x38, 0x9a, 0x19, 0x03, 0xa3, 0x6a,
+ 0x08, 0x82, 0x6a, 0x9a, 0x2a, 0x04, 0xaa, 0x6c,
+ 0x04, 0x9d, 0x9a, 0x00, 0x80, 0x9a, 0xa3, 0x6d,
+ 0x03, 0x8d, 0x6d, 0x29, 0xcf, 0x1f, 0xaf, 0x80,
+ 0x9d, 0x74, 0x01, 0x89, 0x74, 0x05, 0xa3, 0x73,
+ 0x03, 0xa3, 0x73, 0x03, 0xa7, 0x25, 0x07, 0xb3,
+ 0x14, 0x0a, 0x80, 0x14, 0x8a, 0x9c, 0x00, 0x8e,
+ 0x9c, 0x00, 0x86, 0x9c, 0x00, 0x81, 0x9c, 0x00,
+ 0x8a, 0x9c, 0x00, 0x8e, 0x9c, 0x00, 0x86, 0x9c,
+ 0x00, 0x81, 0x9c, 0x42, 0xe0, 0xd6, 0x49, 0x08,
+ 0x95, 0x49, 0x09, 0x87, 0x49, 0x17, 0x85, 0x46,
+ 0x00, 0xa9, 0x46, 0x00, 0x88, 0x46, 0x44, 0x85,
+ 0x1c, 0x01, 0x80, 0x1c, 0x00, 0xab, 0x1c, 0x00,
+ 0x81, 0x1c, 0x02, 0x80, 0x1c, 0x01, 0x80, 0x1c,
+ 0x95, 0x37, 0x00, 0x88, 0x37, 0x9f, 0x76, 0x9e,
+ 0x60, 0x07, 0x88, 0x60, 0x2f, 0x92, 0x34, 0x00,
+ 0x81, 0x34, 0x04, 0x84, 0x34, 0x9b, 0x79, 0x02,
+ 0x80, 0x79, 0x99, 0x4d, 0x04, 0x80, 0x4d, 0x3f,
+ 0x9f, 0x59, 0x97, 0x58, 0x03, 0x93, 0x58, 0x01,
+ 0xad, 0x58, 0x83, 0x40, 0x00, 0x81, 0x40, 0x04,
+ 0x87, 0x40, 0x00, 0x82, 0x40, 0x00, 0x9c, 0x40,
+ 0x01, 0x82, 0x40, 0x03, 0x89, 0x40, 0x06, 0x88,
+ 0x40, 0x06, 0x9f, 0x6f, 0x9f, 0x6b, 0x1f, 0xa6,
+ 0x52, 0x03, 0x8b, 0x52, 0x08, 0xb5, 0x06, 0x02,
+ 0x86, 0x06, 0x95, 0x3a, 0x01, 0x87, 0x3a, 0x92,
+ 0x39, 0x04, 0x87, 0x39, 0x91, 0x7a, 0x06, 0x83,
+ 0x7a, 0x0b, 0x86, 0x7a, 0x4f, 0xc8, 0x70, 0x36,
+ 0xb2, 0x69, 0x0c, 0xb2, 0x69, 0x06, 0x85, 0x69,
+ 0xa7, 0x32, 0x07, 0x89, 0x32, 0x60, 0xc5, 0x9e,
+ 0x04, 0x00, 0xa9, 0x9f, 0x00, 0x82, 0x9f, 0x01,
+ 0x81, 0x9f, 0x4d, 0xa7, 0x6e, 0x07, 0xa9, 0x84,
+ 0x15, 0x99, 0x71, 0x25, 0x9b, 0x18, 0x13, 0x96,
+ 0x26, 0x08, 0xcd, 0x0e, 0x03, 0xa3, 0x0e, 0x08,
+ 0x80, 0x0e, 0xc2, 0x3c, 0x09, 0x80, 0x3c, 0x01,
+ 0x98, 0x85, 0x06, 0x89, 0x85, 0x05, 0xb4, 0x15,
+ 0x00, 0x91, 0x15, 0x07, 0xa6, 0x4f, 0x08, 0xdf,
+ 0x7f, 0x00, 0x93, 0x83, 0x0a, 0x91, 0x42, 0x00,
+ 0xab, 0x42, 0x40, 0x86, 0x5e, 0x00, 0x80, 0x5e,
+ 0x00, 0x83, 0x5e, 0x00, 0x8e, 0x5e, 0x00, 0x8a,
+ 0x5e, 0x05, 0xba, 0x44, 0x04, 0x89, 0x44, 0x05,
+ 0x83, 0x2b, 0x00, 0x87, 0x2b, 0x01, 0x81, 0x2b,
+ 0x01, 0x95, 0x2b, 0x00, 0x86, 0x2b, 0x00, 0x81,
+ 0x2b, 0x00, 0x84, 0x2b, 0x00, 0x80, 0x38, 0x88,
+ 0x2b, 0x01, 0x81, 0x2b, 0x01, 0x82, 0x2b, 0x01,
+ 0x80, 0x2b, 0x05, 0x80, 0x2b, 0x04, 0x86, 0x2b,
+ 0x01, 0x86, 0x2b, 0x02, 0x84, 0x2b, 0x60, 0x2a,
+ 0xdb, 0x63, 0x00, 0x84, 0x63, 0x1d, 0xc7, 0x97,
+ 0x07, 0x89, 0x97, 0x60, 0x45, 0xb5, 0x81, 0x01,
+ 0xa5, 0x81, 0x21, 0xc4, 0x5b, 0x0a, 0x89, 0x5b,
+ 0x05, 0x8c, 0x5c, 0x12, 0xb9, 0x8f, 0x05, 0x89,
+ 0x8f, 0x35, 0x9a, 0x02, 0x01, 0x8e, 0x02, 0x03,
+ 0x96, 0x02, 0x60, 0x58, 0xbb, 0x22, 0x60, 0x03,
+ 0xd2, 0x9e, 0x0b, 0x80, 0x9e, 0x86, 0x21, 0x01,
+ 0x80, 0x21, 0x01, 0x87, 0x21, 0x00, 0x81, 0x21,
+ 0x00, 0x9d, 0x21, 0x00, 0x81, 0x21, 0x01, 0x8b,
+ 0x21, 0x08, 0x89, 0x21, 0x45, 0x87, 0x61, 0x01,
+ 0xad, 0x61, 0x01, 0x8a, 0x61, 0x1a, 0xc7, 0xa1,
+ 0x07, 0xd2, 0x86, 0x0c, 0x8f, 0x12, 0xb8, 0x77,
+ 0x60, 0xa6, 0x88, 0x0c, 0x00, 0xac, 0x0c, 0x00,
+ 0x8d, 0x0c, 0x09, 0x9c, 0x0c, 0x02, 0x9f, 0x53,
+ 0x01, 0x95, 0x53, 0x00, 0x8d, 0x53, 0x48, 0x86,
+ 0x54, 0x00, 0x81, 0x54, 0x00, 0xab, 0x54, 0x02,
+ 0x80, 0x54, 0x00, 0x81, 0x54, 0x00, 0x88, 0x54,
+ 0x07, 0x89, 0x54, 0x05, 0x85, 0x2e, 0x00, 0x81,
+ 0x2e, 0x00, 0xa4, 0x2e, 0x00, 0x81, 0x2e, 0x00,
+ 0x85, 0x2e, 0x06, 0x89, 0x2e, 0x60, 0xd5, 0x98,
+ 0x4e, 0x60, 0x56, 0x80, 0x4b, 0x0e, 0xb1, 0x90,
+ 0x0c, 0x80, 0x90, 0xe3, 0x39, 0x1b, 0x60, 0x05,
+ 0xe0, 0x0e, 0x1b, 0x00, 0x84, 0x1b, 0x0a, 0xe0,
+ 0x63, 0x1b, 0x69, 0xeb, 0xe0, 0x02, 0x1e, 0x0c,
+ 0xe3, 0xce, 0x24, 0x00, 0x88, 0x24, 0x6f, 0x66,
+ 0xe1, 0xe6, 0x03, 0x70, 0x11, 0x58, 0xe1, 0xd8,
+ 0x08, 0x06, 0x9e, 0x5d, 0x00, 0x89, 0x5d, 0x03,
+ 0x81, 0x5d, 0xce, 0x98, 0x00, 0x89, 0x98, 0x05,
+ 0x9d, 0x09, 0x01, 0x85, 0x09, 0x09, 0xc5, 0x75,
+ 0x09, 0x89, 0x75, 0x00, 0x86, 0x75, 0x00, 0x94,
+ 0x75, 0x04, 0x92, 0x75, 0x62, 0x4f, 0xda, 0x55,
+ 0x60, 0x04, 0xca, 0x5a, 0x03, 0xb8, 0x5a, 0x06,
+ 0x90, 0x5a, 0x3f, 0x80, 0x91, 0x80, 0x65, 0x81,
+ 0x30, 0x80, 0x43, 0x0a, 0x81, 0x30, 0x0d, 0xf0,
+ 0x07, 0x97, 0x91, 0x07, 0xe2, 0x9f, 0x91, 0xe1,
+ 0x75, 0x43, 0x29, 0x88, 0x91, 0x70, 0x12, 0x86,
+ 0x83, 0x3e, 0x00, 0x86, 0x3e, 0x00, 0x81, 0x3e,
+ 0x00, 0x80, 0x3e, 0xe0, 0xbe, 0x36, 0x82, 0x3e,
+ 0x2c, 0x82, 0x36, 0x10, 0x83, 0x3e, 0x07, 0xe1,
+ 0x2b, 0x65, 0x68, 0xa3, 0xe0, 0x0a, 0x23, 0x04,
+ 0x8c, 0x23, 0x02, 0x88, 0x23, 0x06, 0x89, 0x23,
+ 0x01, 0x83, 0x23, 0x83, 0x19, 0x70, 0x01, 0xfb,
+ 0xad, 0x38, 0x01, 0x96, 0x38, 0x08, 0xe0, 0x13,
+ 0x19, 0x3b, 0xe0, 0x95, 0x19, 0x09, 0xa6, 0x19,
+ 0x01, 0xbd, 0x19, 0x82, 0x38, 0x90, 0x19, 0x87,
+ 0x38, 0x81, 0x19, 0x86, 0x38, 0x9d, 0x19, 0x83,
+ 0x38, 0xbc, 0x19, 0x14, 0xc5, 0x2c, 0x60, 0x39,
+ 0x93, 0x19, 0x0b, 0xd6, 0x19, 0x08, 0x98, 0x19,
+ 0x60, 0x26, 0xd4, 0x19, 0x00, 0xc6, 0x19, 0x00,
+ 0x81, 0x19, 0x01, 0x80, 0x19, 0x01, 0x81, 0x19,
+ 0x01, 0x83, 0x19, 0x00, 0x8b, 0x19, 0x00, 0x80,
+ 0x19, 0x00, 0x86, 0x19, 0x00, 0xc0, 0x19, 0x00,
+ 0x83, 0x19, 0x01, 0x87, 0x19, 0x00, 0x86, 0x19,
+ 0x00, 0x9b, 0x19, 0x00, 0x83, 0x19, 0x00, 0x84,
+ 0x19, 0x00, 0x80, 0x19, 0x02, 0x86, 0x19, 0x00,
+ 0xe0, 0xf3, 0x19, 0x01, 0xe0, 0xc3, 0x19, 0x01,
+ 0xb1, 0x19, 0xe2, 0x2b, 0x82, 0x0e, 0x84, 0x82,
+ 0x00, 0x8e, 0x82, 0x63, 0xef, 0x9e, 0x46, 0x60,
+ 0x80, 0x86, 0x29, 0x00, 0x90, 0x29, 0x01, 0x86,
+ 0x29, 0x00, 0x81, 0x29, 0x00, 0x84, 0x29, 0x60,
+ 0x74, 0xac, 0x66, 0x02, 0x8d, 0x66, 0x01, 0x89,
+ 0x66, 0x03, 0x81, 0x66, 0x60, 0xdf, 0x9e, 0x99,
+ 0x10, 0xb9, 0x9d, 0x04, 0x80, 0x9d, 0x64, 0x7f,
+ 0x86, 0x27, 0x00, 0x83, 0x27, 0x00, 0x81, 0x27,
+ 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x57, 0x01,
+ 0x8f, 0x57, 0x28, 0xcb, 0x01, 0x03, 0x89, 0x01,
+ 0x03, 0x81, 0x01, 0x62, 0xb0, 0xc3, 0x19, 0x4b,
+ 0xbc, 0x19, 0x60, 0x61, 0x83, 0x04, 0x00, 0x9a,
+ 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04, 0x01,
+ 0x80, 0x04, 0x00, 0x89, 0x04, 0x00, 0x83, 0x04,
+ 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x05, 0x80,
+ 0x04, 0x03, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00,
+ 0x80, 0x04, 0x00, 0x82, 0x04, 0x00, 0x81, 0x04,
+ 0x00, 0x80, 0x04, 0x01, 0x80, 0x04, 0x00, 0x80,
+ 0x04, 0x00, 0x80, 0x04, 0x00, 0x80, 0x04, 0x00,
+ 0x80, 0x04, 0x00, 0x81, 0x04, 0x00, 0x80, 0x04,
+ 0x01, 0x83, 0x04, 0x00, 0x86, 0x04, 0x00, 0x83,
+ 0x04, 0x00, 0x83, 0x04, 0x00, 0x80, 0x04, 0x00,
+ 0x89, 0x04, 0x00, 0x90, 0x04, 0x04, 0x82, 0x04,
+ 0x00, 0x84, 0x04, 0x00, 0x90, 0x04, 0x33, 0x81,
+ 0x04, 0x60, 0xad, 0xab, 0x19, 0x03, 0xe0, 0x03,
+ 0x19, 0x0b, 0x8e, 0x19, 0x01, 0x8e, 0x19, 0x00,
+ 0x8e, 0x19, 0x00, 0xa4, 0x19, 0x09, 0xe0, 0x4d,
+ 0x19, 0x37, 0x99, 0x19, 0x80, 0x36, 0x81, 0x19,
+ 0x0c, 0xab, 0x19, 0x03, 0x88, 0x19, 0x06, 0x81,
+ 0x19, 0x0d, 0x85, 0x19, 0x60, 0x39, 0xe3, 0x77,
+ 0x19, 0x04, 0x8f, 0x19, 0x02, 0x8c, 0x19, 0x02,
+ 0xe0, 0x13, 0x19, 0x0b, 0xd8, 0x19, 0x06, 0x8b,
+ 0x19, 0x03, 0x80, 0x19, 0x0e, 0x8b, 0x19, 0x03,
+ 0xb7, 0x19, 0x07, 0x89, 0x19, 0x05, 0xa7, 0x19,
+ 0x07, 0x9d, 0x19, 0x01, 0x81, 0x19, 0x4d, 0xe0,
+ 0xf3, 0x19, 0x0b, 0x8d, 0x19, 0x01, 0x84, 0x19,
+ 0x02, 0x84, 0x19, 0x02, 0x86, 0x19, 0x08, 0x9c,
+ 0x19, 0x02, 0x8a, 0x19, 0x04, 0x85, 0x19, 0x09,
+ 0x89, 0x19, 0x05, 0x87, 0x19, 0x07, 0x86, 0x19,
+ 0x08, 0xe0, 0x32, 0x19, 0x00, 0xb6, 0x19, 0x24,
+ 0x89, 0x19, 0x63, 0xa5, 0xf0, 0x96, 0x7f, 0x30,
+ 0x1f, 0xef, 0xd8, 0x30, 0x06, 0xe0, 0x7d, 0x30,
+ 0x01, 0xf0, 0x06, 0x21, 0x30, 0x0d, 0xf0, 0x0c,
+ 0xd0, 0x30, 0x6b, 0xbe, 0xe1, 0xbd, 0x30, 0x65,
+ 0x81, 0xf0, 0x02, 0xea, 0x30, 0x7a, 0xdc, 0x55,
+ 0x80, 0x19, 0x1d, 0xdf, 0x19, 0x60, 0x1f, 0xe0,
+ 0x8f, 0x38,
+};
+
+static const uint8_t unicode_script_ext_table[828] = {
+ 0x82, 0xc1, 0x00, 0x00, 0x01, 0x2c, 0x01, 0x00,
+ 0x00, 0x01, 0x2c, 0x1c, 0x00, 0x0c, 0x01, 0x46,
+ 0x80, 0x92, 0x00, 0x00, 0x02, 0x1d, 0x6c, 0x00,
+ 0x02, 0x1d, 0x29, 0x01, 0x02, 0x1d, 0x46, 0x00,
+ 0x02, 0x1d, 0x29, 0x81, 0x03, 0x00, 0x00, 0x06,
+ 0x04, 0x64, 0x32, 0x89, 0x93, 0x9f, 0x0d, 0x00,
+ 0x00, 0x06, 0x04, 0x64, 0x32, 0x89, 0x93, 0x9f,
+ 0x00, 0x03, 0x04, 0x89, 0x93, 0x01, 0x00, 0x00,
+ 0x07, 0x01, 0x04, 0x64, 0x32, 0x89, 0x93, 0x9f,
+ 0x1f, 0x00, 0x00, 0x09, 0x01, 0x04, 0x51, 0x52,
+ 0x71, 0x7a, 0x32, 0x84, 0x89, 0x09, 0x00, 0x0a,
+ 0x02, 0x04, 0x89, 0x09, 0x00, 0x09, 0x03, 0x04,
+ 0x93, 0x9f, 0x05, 0x00, 0x00, 0x02, 0x04, 0x89,
+ 0x62, 0x00, 0x00, 0x02, 0x04, 0x32, 0x81, 0xfb,
+ 0x00, 0x00, 0x0d, 0x0b, 0x20, 0x2b, 0x2d, 0x2f,
+ 0x3d, 0x46, 0x50, 0x72, 0x7f, 0x90, 0x92, 0x97,
+ 0x00, 0x0c, 0x0b, 0x20, 0x2b, 0x2d, 0x2f, 0x3d,
+ 0x46, 0x50, 0x72, 0x90, 0x92, 0x97, 0x10, 0x00,
+ 0x00, 0x14, 0x0b, 0x20, 0x22, 0x2e, 0x54, 0x2b,
+ 0x2d, 0x2f, 0x3d, 0x4f, 0x50, 0x61, 0x72, 0x44,
+ 0x83, 0x88, 0x8f, 0x90, 0x92, 0x97, 0x00, 0x15,
+ 0x0b, 0x20, 0x22, 0x2e, 0x54, 0x2b, 0x2d, 0x2f,
+ 0x3d, 0x48, 0x4f, 0x50, 0x61, 0x72, 0x44, 0x83,
+ 0x88, 0x8f, 0x90, 0x92, 0x97, 0x09, 0x04, 0x20,
+ 0x22, 0x3c, 0x4f, 0x75, 0x00, 0x09, 0x03, 0x0b,
+ 0x15, 0x88, 0x75, 0x00, 0x09, 0x02, 0x2f, 0x5e,
+ 0x75, 0x00, 0x09, 0x02, 0x2d, 0x42, 0x80, 0x75,
+ 0x00, 0x0d, 0x02, 0x2b, 0x90, 0x80, 0x71, 0x00,
+ 0x09, 0x02, 0x3d, 0x61, 0x82, 0xcf, 0x00, 0x09,
+ 0x03, 0x15, 0x5f, 0x8c, 0x80, 0x30, 0x00, 0x00,
+ 0x02, 0x28, 0x46, 0x85, 0xb8, 0x00, 0x01, 0x04,
+ 0x11, 0x33, 0x8b, 0x8a, 0x80, 0x4a, 0x00, 0x01,
+ 0x02, 0x5c, 0x78, 0x00, 0x00, 0x00, 0x02, 0x5c,
+ 0x78, 0x84, 0x49, 0x00, 0x00, 0x04, 0x0b, 0x20,
+ 0x2b, 0x3d, 0x00, 0x01, 0x20, 0x00, 0x04, 0x0b,
+ 0x20, 0x2b, 0x3d, 0x00, 0x02, 0x20, 0x2b, 0x00,
+ 0x01, 0x20, 0x01, 0x02, 0x0b, 0x20, 0x00, 0x02,
+ 0x20, 0x7f, 0x00, 0x02, 0x0b, 0x20, 0x00, 0x02,
+ 0x20, 0x7f, 0x00, 0x06, 0x20, 0x3d, 0x50, 0x72,
+ 0x90, 0x92, 0x00, 0x01, 0x20, 0x01, 0x02, 0x20,
+ 0x7f, 0x01, 0x01, 0x20, 0x00, 0x02, 0x20, 0x7f,
+ 0x00, 0x02, 0x0b, 0x20, 0x06, 0x01, 0x20, 0x00,
+ 0x02, 0x20, 0x61, 0x00, 0x02, 0x0b, 0x20, 0x01,
+ 0x01, 0x20, 0x00, 0x02, 0x0b, 0x20, 0x03, 0x01,
+ 0x20, 0x00, 0x08, 0x0b, 0x20, 0x2b, 0x3d, 0x61,
+ 0x72, 0x92, 0x97, 0x00, 0x02, 0x20, 0x2b, 0x00,
+ 0x03, 0x20, 0x2b, 0x3d, 0x01, 0x02, 0x0b, 0x20,
+ 0x00, 0x01, 0x0b, 0x01, 0x02, 0x20, 0x2b, 0x00,
+ 0x01, 0x61, 0x80, 0x44, 0x00, 0x01, 0x01, 0x2c,
+ 0x35, 0x00, 0x00, 0x02, 0x1d, 0x89, 0x00, 0x00,
+ 0x00, 0x01, 0x89, 0x81, 0xb3, 0x00, 0x00, 0x02,
+ 0x46, 0x5c, 0x80, 0x3f, 0x00, 0x00, 0x03, 0x20,
+ 0x2b, 0x46, 0x8c, 0xd1, 0x00, 0x00, 0x02, 0x1d,
+ 0x29, 0x81, 0x3c, 0x00, 0x01, 0x06, 0x0d, 0x31,
+ 0x30, 0x36, 0x3e, 0xa0, 0x00, 0x05, 0x0d, 0x31,
+ 0x30, 0x36, 0x3e, 0x01, 0x00, 0x00, 0x01, 0x30,
+ 0x00, 0x00, 0x09, 0x06, 0x0d, 0x31, 0x30, 0x36,
+ 0x3e, 0xa0, 0x00, 0x00, 0x00, 0x05, 0x0d, 0x31,
+ 0x30, 0x36, 0x3e, 0x07, 0x06, 0x0d, 0x31, 0x30,
+ 0x36, 0x3e, 0xa0, 0x03, 0x05, 0x0d, 0x31, 0x30,
+ 0x36, 0x3e, 0x09, 0x00, 0x03, 0x02, 0x0d, 0x30,
+ 0x01, 0x00, 0x00, 0x05, 0x0d, 0x31, 0x30, 0x36,
+ 0x3e, 0x04, 0x02, 0x36, 0x3e, 0x00, 0x00, 0x00,
+ 0x05, 0x0d, 0x31, 0x30, 0x36, 0x3e, 0x03, 0x00,
+ 0x01, 0x03, 0x30, 0x36, 0x3e, 0x01, 0x01, 0x30,
+ 0x58, 0x00, 0x03, 0x02, 0x36, 0x3e, 0x02, 0x00,
+ 0x00, 0x02, 0x36, 0x3e, 0x59, 0x00, 0x00, 0x06,
+ 0x0d, 0x31, 0x30, 0x36, 0x3e, 0xa0, 0x00, 0x02,
+ 0x36, 0x3e, 0x80, 0x12, 0x00, 0x0f, 0x01, 0x30,
+ 0x1f, 0x00, 0x23, 0x01, 0x30, 0x3b, 0x00, 0x27,
+ 0x01, 0x30, 0x37, 0x00, 0x30, 0x01, 0x30, 0x0e,
+ 0x00, 0x0b, 0x01, 0x30, 0x32, 0x00, 0x00, 0x01,
+ 0x30, 0x57, 0x00, 0x18, 0x01, 0x30, 0x09, 0x00,
+ 0x04, 0x01, 0x30, 0x5f, 0x00, 0x1e, 0x01, 0x30,
+ 0xc0, 0x31, 0xef, 0x00, 0x00, 0x02, 0x1d, 0x29,
+ 0x80, 0x0f, 0x00, 0x07, 0x02, 0x30, 0x46, 0x80,
+ 0xa7, 0x00, 0x02, 0x0e, 0x20, 0x22, 0x2d, 0x2f,
+ 0x42, 0x3d, 0x3c, 0x4f, 0x50, 0x5b, 0x61, 0x44,
+ 0x8f, 0x97, 0x02, 0x0d, 0x20, 0x22, 0x2d, 0x2f,
+ 0x42, 0x3d, 0x3c, 0x4f, 0x5b, 0x61, 0x44, 0x8f,
+ 0x97, 0x03, 0x0b, 0x20, 0x22, 0x2d, 0x2f, 0x42,
+ 0x3c, 0x4f, 0x5b, 0x44, 0x8f, 0x97, 0x80, 0x36,
+ 0x00, 0x00, 0x02, 0x0b, 0x20, 0x00, 0x00, 0x00,
+ 0x02, 0x20, 0x90, 0x39, 0x00, 0x00, 0x03, 0x3f,
+ 0x46, 0x5f, 0x80, 0x1f, 0x00, 0x00, 0x02, 0x10,
+ 0x3b, 0xc0, 0x12, 0xed, 0x00, 0x01, 0x02, 0x04,
+ 0x64, 0x80, 0x31, 0x00, 0x00, 0x02, 0x04, 0x93,
+ 0x09, 0x00, 0x00, 0x02, 0x04, 0x93, 0x46, 0x00,
+ 0x01, 0x05, 0x0d, 0x31, 0x30, 0x36, 0x3e, 0x80,
+ 0x99, 0x00, 0x04, 0x06, 0x0d, 0x31, 0x30, 0x36,
+ 0x3e, 0xa0, 0x09, 0x00, 0x00, 0x02, 0x36, 0x3e,
+ 0x2c, 0x00, 0x01, 0x02, 0x36, 0x3e, 0x80, 0xdf,
+ 0x00, 0x01, 0x03, 0x1e, 0x1c, 0x4a, 0x00, 0x02,
+ 0x1c, 0x4a, 0x03, 0x00, 0x2c, 0x03, 0x1c, 0x49,
+ 0x4a, 0x02, 0x00, 0x08, 0x02, 0x1c, 0x4a, 0x81,
+ 0x1f, 0x00, 0x1b, 0x02, 0x04, 0x1a, 0x87, 0x75,
+ 0x00, 0x00, 0x02, 0x52, 0x71, 0x87, 0x8d, 0x00,
+ 0x00, 0x02, 0x2b, 0x90, 0x00, 0x00, 0x00, 0x02,
+ 0x2b, 0x90, 0x36, 0x00, 0x01, 0x02, 0x2b, 0x90,
+ 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2b, 0x90, 0x00,
+ 0x00, 0x00, 0x02, 0x2b, 0x90, 0xc0, 0x5c, 0x4b,
+ 0x00, 0x03, 0x01, 0x23, 0x96, 0x3b, 0x00, 0x11,
+ 0x01, 0x30, 0x9e, 0x5d, 0x00, 0x01, 0x01, 0x30,
+ 0xce, 0xcd, 0x2d, 0x00,
+};
+
+static const uint8_t unicode_prop_Hyphen_table[28] = {
+ 0xac, 0x80, 0xfe, 0x80, 0x44, 0xdb, 0x80, 0x52,
+ 0x7a, 0x80, 0x48, 0x08, 0x81, 0x4e, 0x04, 0x80,
+ 0x42, 0xe2, 0x80, 0x60, 0xcd, 0x66, 0x80, 0x40,
+ 0xa8, 0x80, 0xd6, 0x80,
+};
+
+static const uint8_t unicode_prop_Other_Math_table[200] = {
+ 0xdd, 0x80, 0x43, 0x70, 0x11, 0x80, 0x99, 0x09,
+ 0x81, 0x5c, 0x1f, 0x80, 0x9a, 0x82, 0x8a, 0x80,
+ 0x9f, 0x83, 0x97, 0x81, 0x8d, 0x81, 0xc0, 0x8c,
+ 0x18, 0x11, 0x1c, 0x91, 0x03, 0x01, 0x89, 0x00,
+ 0x14, 0x28, 0x11, 0x09, 0x02, 0x05, 0x13, 0x24,
+ 0xca, 0x21, 0x18, 0x08, 0x08, 0x00, 0x21, 0x0b,
+ 0x0b, 0x91, 0x09, 0x00, 0x06, 0x00, 0x29, 0x41,
+ 0x21, 0x83, 0x40, 0xa7, 0x08, 0x80, 0x97, 0x80,
+ 0x90, 0x80, 0x41, 0xbc, 0x81, 0x8b, 0x88, 0x24,
+ 0x21, 0x09, 0x14, 0x8d, 0x00, 0x01, 0x85, 0x97,
+ 0x81, 0xb8, 0x00, 0x80, 0x9c, 0x83, 0x88, 0x81,
+ 0x41, 0x55, 0x81, 0x9e, 0x89, 0x41, 0x92, 0x95,
+ 0xbe, 0x83, 0x9f, 0x81, 0x60, 0xd4, 0x62, 0x00,
+ 0x03, 0x80, 0x40, 0xd2, 0x00, 0x80, 0x60, 0xd4,
+ 0xc0, 0xd4, 0x80, 0xc6, 0x01, 0x08, 0x09, 0x0b,
+ 0x80, 0x8b, 0x00, 0x06, 0x80, 0xc0, 0x03, 0x0f,
+ 0x06, 0x80, 0x9b, 0x03, 0x04, 0x00, 0x16, 0x80,
+ 0x41, 0x53, 0x81, 0x98, 0x80, 0x98, 0x80, 0x9e,
+ 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x80, 0x9e,
+ 0x80, 0x98, 0x80, 0x9e, 0x80, 0x98, 0x07, 0x81,
+ 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00, 0x08,
+ 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18, 0x00,
+ 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03, 0x00,
+ 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90,
+};
+
+static const uint8_t unicode_prop_Other_Alphabetic_table[417] = {
+ 0x43, 0x44, 0x80, 0x42, 0x69, 0x8d, 0x00, 0x01,
+ 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x8c, 0x06, 0x8f,
+ 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, 0xa2, 0x80,
+ 0x9d, 0x8f, 0xe5, 0x8a, 0xe4, 0x0a, 0x88, 0x02,
+ 0x03, 0x40, 0xa6, 0x8b, 0x16, 0x85, 0x93, 0xb5,
+ 0x09, 0x8e, 0x01, 0x22, 0x89, 0x81, 0x9c, 0x82,
+ 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80, 0x89, 0x81,
+ 0x9c, 0x82, 0xb9, 0x23, 0x09, 0x0b, 0x80, 0x9d,
+ 0x0a, 0x80, 0x8a, 0x82, 0xb9, 0x38, 0x10, 0x81,
+ 0x94, 0x81, 0x95, 0x13, 0x82, 0xb9, 0x31, 0x09,
+ 0x81, 0x88, 0x81, 0x89, 0x81, 0x9d, 0x80, 0xba,
+ 0x22, 0x10, 0x82, 0x89, 0x80, 0xa7, 0x83, 0xb9,
+ 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9c, 0x82,
+ 0xb9, 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x9b,
+ 0x83, 0xb9, 0x30, 0x10, 0x82, 0x89, 0x80, 0x89,
+ 0x81, 0x9c, 0x82, 0xca, 0x28, 0x00, 0x87, 0x91,
+ 0x81, 0xbc, 0x01, 0x86, 0x91, 0x80, 0xe2, 0x01,
+ 0x28, 0x81, 0x8f, 0x80, 0x40, 0xa2, 0x90, 0x8a,
+ 0x8a, 0x80, 0xa3, 0xed, 0x8b, 0x00, 0x0b, 0x96,
+ 0x1b, 0x10, 0x11, 0x32, 0x83, 0x8c, 0x8b, 0x00,
+ 0x89, 0x83, 0x46, 0x73, 0x81, 0x9d, 0x81, 0x9d,
+ 0x81, 0x9d, 0x81, 0xc1, 0x92, 0x40, 0xbb, 0x81,
+ 0xa1, 0x80, 0xf5, 0x8b, 0x83, 0x88, 0x40, 0xdd,
+ 0x84, 0xb8, 0x89, 0x81, 0x93, 0xc9, 0x81, 0x8a,
+ 0x82, 0xb0, 0x84, 0xaf, 0x8e, 0xbb, 0x82, 0x9d,
+ 0x88, 0x09, 0xb8, 0x8a, 0xb1, 0x92, 0x41, 0xaf,
+ 0x8d, 0x46, 0xc0, 0xb3, 0x48, 0xf5, 0x9f, 0x60,
+ 0x78, 0x73, 0x87, 0xa1, 0x81, 0x41, 0x61, 0x07,
+ 0x80, 0x96, 0x84, 0xd7, 0x81, 0xb1, 0x8f, 0x00,
+ 0xb8, 0x80, 0xa5, 0x84, 0x9b, 0x8b, 0xac, 0x83,
+ 0xaf, 0x8b, 0xa4, 0x80, 0xc2, 0x8d, 0x8b, 0x07,
+ 0x81, 0xac, 0x82, 0xb1, 0x00, 0x11, 0x0c, 0x80,
+ 0xab, 0x24, 0x80, 0x40, 0xec, 0x87, 0x60, 0x4f,
+ 0x32, 0x80, 0x48, 0x56, 0x84, 0x46, 0x85, 0x10,
+ 0x0c, 0x83, 0x43, 0x13, 0x83, 0x41, 0x82, 0x81,
+ 0x41, 0x52, 0x82, 0xb4, 0x8d, 0xac, 0x81, 0x8c,
+ 0x80, 0xac, 0x88, 0x88, 0x80, 0xbc, 0x82, 0xa3,
+ 0x8b, 0x91, 0x81, 0xb8, 0x82, 0xaf, 0x8c, 0x8d,
+ 0x81, 0xdb, 0x88, 0x08, 0x28, 0x40, 0x9f, 0x89,
+ 0x96, 0x83, 0xb9, 0x31, 0x09, 0x81, 0x89, 0x80,
+ 0x89, 0x81, 0x40, 0xd0, 0x8c, 0x02, 0xe9, 0x91,
+ 0x40, 0xec, 0x31, 0x86, 0x9c, 0x81, 0xd1, 0x8e,
+ 0x00, 0xe9, 0x8a, 0xe6, 0x8d, 0x41, 0x00, 0x8c,
+ 0x40, 0xf6, 0x28, 0x09, 0x0a, 0x00, 0x80, 0x40,
+ 0x8d, 0x31, 0x2b, 0x80, 0x9b, 0x89, 0xa9, 0x20,
+ 0x83, 0x91, 0x8a, 0xad, 0x8d, 0x41, 0x96, 0x38,
+ 0x86, 0xd2, 0x95, 0x80, 0x8d, 0xf9, 0x2a, 0x00,
+ 0x08, 0x10, 0x02, 0x80, 0xc1, 0x20, 0x08, 0x83,
+ 0x41, 0x5b, 0x83, 0x60, 0x50, 0x57, 0x00, 0xb6,
+ 0x33, 0xdc, 0x81, 0x60, 0x4c, 0xab, 0x80, 0x60,
+ 0x23, 0x60, 0x30, 0x90, 0x0e, 0x01, 0x04, 0x49,
+ 0x1b, 0x80, 0x47, 0xe7, 0x99, 0x85, 0x99, 0x85,
+ 0x99,
+};
+
+static const uint8_t unicode_prop_Other_Lowercase_table[59] = {
+ 0x40, 0xa9, 0x80, 0x8e, 0x80, 0x41, 0xf4, 0x88,
+ 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x59,
+ 0xb0, 0xbe, 0x8c, 0x80, 0xa1, 0xa4, 0x42, 0xb0,
+ 0x80, 0x8c, 0x80, 0x8f, 0x8c, 0x40, 0xd2, 0x8f,
+ 0x43, 0x4f, 0x99, 0x47, 0x91, 0x81, 0x60, 0x7a,
+ 0x1d, 0x81, 0x40, 0xd1, 0x80, 0x40, 0x86, 0x81,
+ 0x43, 0x61, 0x83, 0x60, 0x5c, 0x1f, 0x01, 0x10,
+ 0xa9, 0x80, 0x88,
+};
+
+static const uint8_t unicode_prop_Other_Uppercase_table[15] = {
+ 0x60, 0x21, 0x5f, 0x8f, 0x43, 0x45, 0x99, 0x61,
+ 0xcc, 0x5f, 0x99, 0x85, 0x99, 0x85, 0x99,
+};
+
+static const uint8_t unicode_prop_Other_Grapheme_Extend_table[65] = {
+ 0x49, 0xbd, 0x80, 0x97, 0x80, 0x41, 0x65, 0x80,
+ 0x97, 0x80, 0xe5, 0x80, 0x97, 0x80, 0x40, 0xe9,
+ 0x80, 0x91, 0x81, 0xe6, 0x80, 0x97, 0x80, 0xf6,
+ 0x80, 0x8e, 0x80, 0x4d, 0x54, 0x80, 0x44, 0xd5,
+ 0x80, 0x50, 0x20, 0x81, 0x60, 0xcf, 0x6d, 0x81,
+ 0x53, 0x9d, 0x80, 0x97, 0x80, 0x41, 0x57, 0x80,
+ 0x8b, 0x80, 0x40, 0xf0, 0x80, 0x43, 0x7f, 0x80,
+ 0x60, 0xb8, 0x33, 0x07, 0x84, 0x6c, 0x2e, 0xac,
+ 0xdf,
+};
+
+static const uint8_t unicode_prop_Other_Default_Ignorable_Code_Point_table[32] = {
+ 0x43, 0x4e, 0x80, 0x4e, 0x0e, 0x81, 0x46, 0x52,
+ 0x81, 0x48, 0xae, 0x80, 0x50, 0xfd, 0x80, 0x60,
+ 0xce, 0x3a, 0x80, 0xce, 0x88, 0x6d, 0x00, 0x06,
+ 0x00, 0x9d, 0xdf, 0xff, 0x40, 0xef, 0x4e, 0x0f,
+};
+
+static const uint8_t unicode_prop_Other_ID_Start_table[11] = {
+ 0x58, 0x84, 0x81, 0x48, 0x90, 0x80, 0x94, 0x80,
+ 0x4f, 0x6b, 0x81,
+};
+
+static const uint8_t unicode_prop_Other_ID_Continue_table[12] = {
+ 0x40, 0xb6, 0x80, 0x42, 0xce, 0x80, 0x4f, 0xe0,
+ 0x88, 0x46, 0x67, 0x80,
+};
+
+static const uint8_t unicode_prop_Prepended_Concatenation_Mark_table[19] = {
+ 0x45, 0xff, 0x85, 0x40, 0xd6, 0x80, 0xb0, 0x80,
+ 0x41, 0x7f, 0x81, 0xcf, 0x80, 0x61, 0x07, 0xd9,
+ 0x80, 0x8e, 0x80,
+};
+
+static const uint8_t unicode_prop_XID_Start1_table[31] = {
+ 0x43, 0x79, 0x80, 0x4a, 0xb7, 0x80, 0xfe, 0x80,
+ 0x60, 0x21, 0xe6, 0x81, 0x60, 0xcb, 0xc0, 0x85,
+ 0x41, 0x95, 0x81, 0xf3, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0x41, 0x1e, 0x81,
+};
+
+static const uint8_t unicode_prop_XID_Continue1_table[23] = {
+ 0x43, 0x79, 0x80, 0x60, 0x2d, 0x1f, 0x81, 0x60,
+ 0xcb, 0xc0, 0x85, 0x41, 0x95, 0x81, 0xf3, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+};
+
+static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = {
+ 0x41, 0xc3, 0x08, 0x08, 0x81, 0xa4, 0x81, 0x4e,
+ 0xdc, 0xaa, 0x0a, 0x4e, 0x87, 0x3f, 0x3f, 0x87,
+ 0x8b, 0x80, 0x8e, 0x80, 0xae, 0x80,
+};
+
+static const uint8_t unicode_prop_Changes_When_Casefolded1_table[33] = {
+ 0x40, 0xde, 0x80, 0xcf, 0x80, 0x97, 0x80, 0x44,
+ 0x3c, 0x80, 0x59, 0x11, 0x80, 0x40, 0xe4, 0x3f,
+ 0x3f, 0x87, 0x89, 0x11, 0x05, 0x02, 0x11, 0x80,
+ 0xa9, 0x11, 0x80, 0x60, 0xdb, 0x07, 0x86, 0x8b,
+ 0x84,
+};
+
+static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[448] = {
+ 0x40, 0x9f, 0x06, 0x00, 0x01, 0x00, 0x01, 0x12,
+ 0x10, 0x82, 0x9f, 0x80, 0xcf, 0x01, 0x80, 0x8b,
+ 0x07, 0x80, 0xfb, 0x01, 0x01, 0x80, 0xa5, 0x80,
+ 0x40, 0xbb, 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08,
+ 0x81, 0x89, 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08,
+ 0x80, 0xc9, 0x82, 0x9c, 0x80, 0x41, 0x93, 0x80,
+ 0x40, 0x93, 0x80, 0xd7, 0x83, 0x42, 0xde, 0x87,
+ 0xfb, 0x08, 0x80, 0xd2, 0x01, 0x80, 0xa1, 0x11,
+ 0x80, 0x40, 0xfc, 0x81, 0x42, 0xd4, 0x80, 0xfe,
+ 0x80, 0xa7, 0x81, 0xad, 0x80, 0xb5, 0x80, 0x88,
+ 0x03, 0x03, 0x03, 0x80, 0x8b, 0x80, 0x88, 0x00,
+ 0x26, 0x80, 0x90, 0x80, 0x88, 0x03, 0x03, 0x03,
+ 0x80, 0x8b, 0x80, 0x41, 0x41, 0x80, 0xe1, 0x81,
+ 0x46, 0x52, 0x81, 0xd4, 0x84, 0x45, 0x1b, 0x10,
+ 0x8a, 0x80, 0x91, 0x80, 0x9b, 0x8c, 0x80, 0xa1,
+ 0xa4, 0x40, 0xd9, 0x80, 0x40, 0xd5, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x3f, 0x3f, 0x87,
+ 0x89, 0x11, 0x04, 0x00, 0x29, 0x04, 0x12, 0x80,
+ 0x88, 0x12, 0x80, 0x88, 0x11, 0x11, 0x04, 0x08,
+ 0x8f, 0x00, 0x20, 0x8b, 0x12, 0x2a, 0x08, 0x0b,
+ 0x00, 0x07, 0x82, 0x8c, 0x06, 0x92, 0x81, 0x9a,
+ 0x80, 0x8c, 0x8a, 0x80, 0xd6, 0x18, 0x10, 0x8a,
+ 0x01, 0x0c, 0x0a, 0x00, 0x10, 0x11, 0x02, 0x06,
+ 0x05, 0x1c, 0x85, 0x8f, 0x8f, 0x8f, 0x88, 0x80,
+ 0x40, 0xa1, 0x08, 0x81, 0x40, 0xf7, 0x81, 0x41,
+ 0x34, 0xd5, 0x99, 0x9a, 0x45, 0x20, 0x80, 0xe6,
+ 0x82, 0xe4, 0x80, 0x41, 0x9e, 0x81, 0x40, 0xf0,
+ 0x80, 0x41, 0x2e, 0x80, 0xd2, 0x80, 0x8b, 0x40,
+ 0xd5, 0xa9, 0x80, 0xb4, 0x00, 0x82, 0xdf, 0x09,
+ 0x80, 0xde, 0x80, 0xb0, 0xdd, 0x82, 0x8d, 0xdf,
+ 0x9e, 0x80, 0xa7, 0x87, 0xae, 0x80, 0x41, 0x7f,
+ 0x60, 0x72, 0x9b, 0x81, 0x40, 0xd1, 0x80, 0x40,
+ 0x80, 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, 0x80,
+ 0x60, 0x4d, 0x95, 0x41, 0x0d, 0x08, 0x00, 0x81,
+ 0x89, 0x00, 0x00, 0x09, 0x82, 0xc3, 0x81, 0xe9,
+ 0xa5, 0x86, 0x8b, 0x24, 0x00, 0x97, 0x04, 0x00,
+ 0x01, 0x01, 0x80, 0xeb, 0xa0, 0x41, 0x6a, 0x91,
+ 0xbf, 0x81, 0xb5, 0xa7, 0x8c, 0x82, 0x99, 0x95,
+ 0x94, 0x81, 0x8b, 0x80, 0x92, 0x03, 0x1a, 0x00,
+ 0x80, 0x40, 0x86, 0x08, 0x80, 0x9f, 0x99, 0x40,
+ 0x83, 0x15, 0x0d, 0x0d, 0x0a, 0x16, 0x06, 0x80,
+ 0x88, 0x47, 0x87, 0x20, 0xa9, 0x80, 0x88, 0x60,
+ 0xb4, 0xe4, 0x83, 0x54, 0xb9, 0x86, 0x8d, 0x87,
+ 0xbf, 0x85, 0x42, 0x3e, 0xd4, 0x80, 0xc6, 0x01,
+ 0x08, 0x09, 0x0b, 0x80, 0x8b, 0x00, 0x06, 0x80,
+ 0xc0, 0x03, 0x0f, 0x06, 0x80, 0x9b, 0x03, 0x04,
+ 0x00, 0x16, 0x80, 0x41, 0x53, 0x81, 0x41, 0x23,
+ 0x81, 0xb1, 0x55, 0xff, 0x18, 0x9a, 0x01, 0x00,
+ 0x08, 0x80, 0x89, 0x03, 0x00, 0x00, 0x28, 0x18,
+ 0x00, 0x00, 0x02, 0x01, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x0b, 0x06, 0x03, 0x03,
+ 0x00, 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80,
+ 0x90, 0x42, 0x43, 0x8a, 0x84, 0x9e, 0x80, 0x9f,
+ 0x99, 0x82, 0xa2, 0x80, 0xee, 0x82, 0x8c, 0xab,
+ 0x83, 0x88, 0x31, 0x49, 0x9d, 0x89, 0x60, 0xfc,
+ 0x05, 0x42, 0x1d, 0x6b, 0x05, 0xe1, 0x4f, 0xff,
+};
+
+static const uint8_t unicode_prop_ASCII_Hex_Digit_table[5] = {
+ 0xaf, 0x89, 0x35, 0x99, 0x85,
+};
+
+static const uint8_t unicode_prop_Bidi_Control_table[10] = {
+ 0x46, 0x1b, 0x80, 0x59, 0xf0, 0x81, 0x99, 0x84,
+ 0xb6, 0x83,
+};
+
+static const uint8_t unicode_prop_Dash_table[55] = {
+ 0xac, 0x80, 0x45, 0x5b, 0x80, 0xb2, 0x80, 0x4e,
+ 0x40, 0x80, 0x44, 0x04, 0x80, 0x48, 0x08, 0x85,
+ 0xbc, 0x80, 0xa6, 0x80, 0x8e, 0x80, 0x41, 0x85,
+ 0x80, 0x4c, 0x03, 0x01, 0x80, 0x9e, 0x0b, 0x80,
+ 0x9b, 0x80, 0x41, 0xbd, 0x80, 0x92, 0x80, 0xee,
+ 0x80, 0x60, 0xcd, 0x8f, 0x81, 0xa4, 0x80, 0x89,
+ 0x80, 0x40, 0xa8, 0x80, 0x4f, 0x9e, 0x80,
+};
+
+static const uint8_t unicode_prop_Deprecated_table[23] = {
+ 0x41, 0x48, 0x80, 0x45, 0x28, 0x80, 0x49, 0x02,
+ 0x00, 0x80, 0x48, 0x28, 0x81, 0x48, 0xc4, 0x85,
+ 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80,
+};
+
+static const uint8_t unicode_prop_Diacritic_table[391] = {
+ 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81,
+ 0x41, 0xf6, 0x40, 0x9e, 0x07, 0x25, 0x90, 0x0b,
+ 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0,
+ 0x80, 0xb6, 0x90, 0x80, 0x9a, 0x00, 0x01, 0x00,
+ 0x40, 0x85, 0x3b, 0x81, 0x40, 0x85, 0x0b, 0x0a,
+ 0x82, 0xc2, 0x9a, 0xda, 0x8a, 0xb9, 0x8a, 0xa1,
+ 0x81, 0xfd, 0x87, 0xa8, 0x89, 0x8f, 0x9b, 0xbc,
+ 0x80, 0x8f, 0x02, 0x83, 0x9b, 0x80, 0xc9, 0x80,
+ 0x8f, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed, 0x80,
+ 0x8f, 0x80, 0xae, 0x82, 0xbb, 0x80, 0x8f, 0x06,
+ 0x80, 0xf6, 0x80, 0xed, 0x80, 0x8f, 0x80, 0xed,
+ 0x80, 0x8f, 0x80, 0xec, 0x81, 0x8f, 0x80, 0xfb,
+ 0x80, 0xfb, 0x28, 0x80, 0xea, 0x80, 0x8c, 0x84,
+ 0xca, 0x81, 0x9a, 0x00, 0x00, 0x03, 0x81, 0xc1,
+ 0x10, 0x81, 0xbd, 0x80, 0xef, 0x00, 0x81, 0xa7,
+ 0x0b, 0x84, 0x98, 0x30, 0x80, 0x89, 0x81, 0x42,
+ 0xc0, 0x82, 0x43, 0xb3, 0x81, 0x40, 0xb2, 0x8a,
+ 0x88, 0x80, 0x41, 0x5a, 0x82, 0x41, 0x38, 0x39,
+ 0x80, 0xaf, 0x8e, 0x81, 0x8a, 0xe7, 0x80, 0x8e,
+ 0x80, 0xa5, 0x88, 0xb5, 0x81, 0x40, 0x89, 0x81,
+ 0xbf, 0x85, 0xd1, 0x98, 0x18, 0x28, 0x0a, 0xb1,
+ 0xbe, 0xd8, 0x8b, 0xa4, 0x8a, 0x41, 0xbc, 0x00,
+ 0x82, 0x8a, 0x82, 0x8c, 0x82, 0x8c, 0x82, 0x8c,
+ 0x81, 0x4c, 0xef, 0x82, 0x41, 0x3c, 0x80, 0x41,
+ 0xf9, 0x85, 0xe8, 0x83, 0xde, 0x80, 0x60, 0x75,
+ 0x71, 0x80, 0x8b, 0x08, 0x80, 0x9b, 0x81, 0xd1,
+ 0x81, 0x8d, 0xa1, 0xe5, 0x82, 0xec, 0x81, 0x40,
+ 0xc9, 0x80, 0x9a, 0x91, 0xb8, 0x83, 0xa3, 0x80,
+ 0xde, 0x80, 0x8b, 0x80, 0xa3, 0x80, 0x40, 0x94,
+ 0x82, 0xc0, 0x83, 0xb2, 0x80, 0xe3, 0x84, 0x88,
+ 0x82, 0xff, 0x81, 0x60, 0x4f, 0x2f, 0x80, 0x43,
+ 0x00, 0x8f, 0x41, 0x0d, 0x00, 0x80, 0xae, 0x80,
+ 0xac, 0x81, 0xc2, 0x80, 0x42, 0xfb, 0x80, 0x44,
+ 0x9e, 0x28, 0xa9, 0x80, 0x88, 0x43, 0x29, 0x81,
+ 0x42, 0x3a, 0x85, 0x42, 0x1d, 0x8a, 0xb0, 0x83,
+ 0x40, 0xbf, 0x80, 0xa8, 0x80, 0xc7, 0x81, 0xf7,
+ 0x81, 0xbd, 0x80, 0xcb, 0x80, 0x88, 0x82, 0xe7,
+ 0x81, 0x40, 0xb1, 0x81, 0xd0, 0x80, 0x8f, 0x80,
+ 0x97, 0x32, 0x84, 0x40, 0xcc, 0x02, 0x80, 0xfa,
+ 0x81, 0x40, 0xfa, 0x81, 0xfd, 0x80, 0xf5, 0x81,
+ 0xf2, 0x80, 0x41, 0x0c, 0x81, 0x41, 0x01, 0x0b,
+ 0x80, 0x40, 0x9b, 0x80, 0xd2, 0x80, 0x91, 0x80,
+ 0xd0, 0x80, 0x41, 0xa4, 0x80, 0x41, 0x01, 0x00,
+ 0x81, 0xd0, 0x80, 0x60, 0x4d, 0x57, 0x84, 0xba,
+ 0x86, 0x44, 0x57, 0x90, 0xcf, 0x81, 0x60, 0x3f,
+ 0xfd, 0x18, 0x30, 0x81, 0x5f, 0x00, 0xad, 0x81,
+ 0x96, 0x42, 0x1f, 0x12, 0x2f, 0x39, 0x86, 0x9d,
+ 0x83, 0x4f, 0x81, 0x86, 0x41, 0x76, 0x80, 0xbc,
+ 0x83, 0x45, 0xdf, 0x86, 0xec, 0x10, 0x82,
+};
+
+static const uint8_t unicode_prop_Extender_table[92] = {
+ 0x40, 0xb6, 0x80, 0x42, 0x17, 0x81, 0x43, 0x6d,
+ 0x80, 0x41, 0xb8, 0x80, 0x43, 0x59, 0x80, 0x42,
+ 0xef, 0x80, 0xfe, 0x80, 0x49, 0x42, 0x80, 0xb7,
+ 0x80, 0x42, 0x62, 0x80, 0x41, 0x8d, 0x80, 0xc3,
+ 0x80, 0x53, 0x88, 0x80, 0xaa, 0x84, 0xe6, 0x81,
+ 0xdc, 0x82, 0x60, 0x6f, 0x15, 0x80, 0x45, 0xf5,
+ 0x80, 0x43, 0xc1, 0x80, 0x95, 0x80, 0x40, 0x88,
+ 0x80, 0xeb, 0x80, 0x94, 0x81, 0x60, 0x54, 0x7a,
+ 0x80, 0x48, 0x0f, 0x81, 0x4b, 0xd9, 0x80, 0x42,
+ 0x67, 0x82, 0x44, 0xce, 0x80, 0x60, 0x50, 0xa8,
+ 0x81, 0x44, 0x9b, 0x08, 0x80, 0x60, 0x71, 0x57,
+ 0x81, 0x48, 0x05, 0x82,
+};
+
+static const uint8_t unicode_prop_Hex_Digit_table[12] = {
+ 0xaf, 0x89, 0x35, 0x99, 0x85, 0x60, 0xfe, 0xa8,
+ 0x89, 0x35, 0x99, 0x85,
+};
+
+static const uint8_t unicode_prop_IDS_Binary_Operator_table[5] = {
+ 0x60, 0x2f, 0xef, 0x09, 0x87,
+};
+
+static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = {
+ 0x60, 0x2f, 0xf1, 0x81,
+};
+
+static const uint8_t unicode_prop_Ideographic_table[66] = {
+ 0x60, 0x30, 0x05, 0x81, 0x98, 0x88, 0x8d, 0x82,
+ 0x43, 0xc4, 0x59, 0xbf, 0xbf, 0x60, 0x51, 0xff,
+ 0x60, 0x58, 0xff, 0x41, 0x6d, 0x81, 0xe9, 0x60,
+ 0x75, 0x09, 0x80, 0x9a, 0x57, 0xf7, 0x87, 0x44,
+ 0xd5, 0xa9, 0x88, 0x60, 0x24, 0x66, 0x41, 0x8b,
+ 0x60, 0x4d, 0x03, 0x60, 0xa6, 0xdf, 0x9f, 0x50,
+ 0x38, 0x86, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d,
+ 0x5d, 0x30, 0x4c, 0x1e, 0x42, 0x1d, 0x45, 0xe1,
+ 0x53, 0x4a,
+};
+
+static const uint8_t unicode_prop_Join_Control_table[4] = {
+ 0x60, 0x20, 0x0b, 0x81,
+};
+
+static const uint8_t unicode_prop_Logical_Order_Exception_table[15] = {
+ 0x4e, 0x3f, 0x84, 0xfa, 0x84, 0x4a, 0xef, 0x11,
+ 0x80, 0x60, 0x90, 0xf9, 0x09, 0x00, 0x81,
+};
+
+static const uint8_t unicode_prop_Noncharacter_Code_Point_table[71] = {
+ 0x60, 0xfd, 0xcf, 0x9f, 0x42, 0x0d, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81, 0x60,
+ 0xff, 0xfd, 0x81, 0x60, 0xff, 0xfd, 0x81,
+};
+
+static const uint8_t unicode_prop_Pattern_Syntax_table[58] = {
+ 0xa0, 0x8e, 0x89, 0x86, 0x99, 0x18, 0x80, 0x99,
+ 0x83, 0xa1, 0x30, 0x00, 0x08, 0x00, 0x0b, 0x03,
+ 0x02, 0x80, 0x96, 0x80, 0x9e, 0x80, 0x5f, 0x17,
+ 0x97, 0x87, 0x8e, 0x81, 0x92, 0x80, 0x89, 0x41,
+ 0x30, 0x42, 0xcf, 0x40, 0x9f, 0x42, 0x75, 0x9d,
+ 0x44, 0x6b, 0x41, 0xff, 0xff, 0x41, 0x80, 0x13,
+ 0x98, 0x8e, 0x80, 0x60, 0xcd, 0x0c, 0x81, 0x41,
+ 0x04, 0x81,
+};
+
+static const uint8_t unicode_prop_Pattern_White_Space_table[11] = {
+ 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x5f, 0x87,
+ 0x81, 0x97, 0x81,
+};
+
+static const uint8_t unicode_prop_Quotation_Mark_table[31] = {
+ 0xa1, 0x03, 0x80, 0x40, 0x82, 0x80, 0x8e, 0x80,
+ 0x5f, 0x5b, 0x87, 0x98, 0x81, 0x4e, 0x06, 0x80,
+ 0x41, 0xc8, 0x83, 0x8c, 0x82, 0x60, 0xce, 0x20,
+ 0x83, 0x40, 0xbc, 0x03, 0x80, 0xd9, 0x81,
+};
+
+static const uint8_t unicode_prop_Radical_table[9] = {
+ 0x60, 0x2e, 0x7f, 0x99, 0x80, 0xd8, 0x8b, 0x40,
+ 0xd5,
+};
+
+static const uint8_t unicode_prop_Regional_Indicator_table[4] = {
+ 0x61, 0xf1, 0xe5, 0x99,
+};
+
+static const uint8_t unicode_prop_Sentence_Terminal_table[194] = {
+ 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48,
+ 0x80, 0x40, 0x92, 0x82, 0x40, 0xb3, 0x80, 0xaa,
+ 0x82, 0x40, 0xf5, 0x80, 0xbc, 0x00, 0x02, 0x81,
+ 0x41, 0x24, 0x81, 0x46, 0xe3, 0x81, 0x43, 0x15,
+ 0x03, 0x81, 0x43, 0x04, 0x80, 0x40, 0xc5, 0x81,
+ 0x40, 0xcb, 0x04, 0x80, 0x41, 0x39, 0x81, 0x41,
+ 0x61, 0x83, 0x40, 0xad, 0x09, 0x81, 0x9c, 0x81,
+ 0x40, 0xbb, 0x81, 0xc0, 0x81, 0x43, 0xbb, 0x81,
+ 0x88, 0x82, 0x4d, 0xe3, 0x80, 0x8c, 0x80, 0x95,
+ 0x81, 0x41, 0xac, 0x80, 0x60, 0x74, 0xfb, 0x80,
+ 0x41, 0x0d, 0x81, 0x40, 0xe2, 0x02, 0x80, 0x41,
+ 0x7d, 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x97,
+ 0x81, 0x40, 0x92, 0x82, 0x40, 0x8f, 0x81, 0x40,
+ 0xf8, 0x80, 0x60, 0x52, 0x65, 0x02, 0x81, 0x40,
+ 0xa8, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0xc0, 0x80,
+ 0x4a, 0xf3, 0x81, 0x44, 0xfc, 0x84, 0xab, 0x83,
+ 0x40, 0xbc, 0x81, 0xf4, 0x83, 0xfe, 0x82, 0x40,
+ 0x80, 0x0d, 0x80, 0x8f, 0x81, 0xd7, 0x08, 0x81,
+ 0xeb, 0x80, 0x41, 0xa0, 0x81, 0x41, 0x74, 0x0c,
+ 0x8e, 0xe8, 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04,
+ 0x00, 0x80, 0x40, 0xfa, 0x81, 0xd6, 0x81, 0x41,
+ 0xa3, 0x81, 0x42, 0xb3, 0x81, 0x60, 0x4b, 0x74,
+ 0x81, 0x40, 0x84, 0x80, 0xc0, 0x81, 0x8a, 0x80,
+ 0x43, 0x52, 0x80, 0x60, 0x4e, 0x05, 0x80, 0x5d,
+ 0xe7, 0x80,
+};
+
+static const uint8_t unicode_prop_Soft_Dotted_table[74] = {
+ 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80,
+ 0x9d, 0x80, 0xb3, 0x80, 0x93, 0x80, 0x41, 0x3f,
+ 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2,
+ 0x80, 0x8c, 0x02, 0x80, 0x40, 0x83, 0x80, 0x40,
+ 0x9c, 0x80, 0x41, 0xa4, 0x80, 0x40, 0xd5, 0x81,
+ 0x4b, 0x31, 0x80, 0x61, 0xa7, 0xa4, 0x81, 0xb1,
+ 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1,
+ 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1,
+ 0x81, 0xb1, 0x81, 0xb1, 0x81, 0xb1, 0x81, 0x48,
+ 0x85, 0x80,
+};
+
+static const uint8_t unicode_prop_Terminal_Punctuation_table[246] = {
+ 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80,
+ 0x43, 0x3d, 0x07, 0x80, 0x42, 0x00, 0x80, 0xb8,
+ 0x80, 0xc7, 0x80, 0x8d, 0x00, 0x82, 0x40, 0xb3,
+ 0x80, 0xaa, 0x8a, 0x00, 0x40, 0xea, 0x81, 0xb5,
+ 0x8e, 0x9e, 0x80, 0x41, 0x04, 0x81, 0x44, 0xf3,
+ 0x81, 0x40, 0xab, 0x03, 0x85, 0x41, 0x36, 0x81,
+ 0x43, 0x14, 0x87, 0x43, 0x04, 0x80, 0xfb, 0x82,
+ 0xc6, 0x81, 0x40, 0x9c, 0x12, 0x80, 0xa6, 0x19,
+ 0x81, 0x41, 0x39, 0x81, 0x41, 0x61, 0x83, 0x40,
+ 0xad, 0x08, 0x82, 0x9c, 0x81, 0x40, 0xbb, 0x84,
+ 0xbd, 0x81, 0x43, 0xbb, 0x81, 0x88, 0x82, 0x4d,
+ 0xe3, 0x80, 0x8c, 0x03, 0x80, 0x89, 0x00, 0x0a,
+ 0x81, 0x41, 0xab, 0x81, 0x60, 0x74, 0xfa, 0x81,
+ 0x41, 0x0c, 0x82, 0x40, 0xe2, 0x84, 0x41, 0x7d,
+ 0x81, 0xd5, 0x81, 0xde, 0x80, 0x40, 0x96, 0x82,
+ 0x40, 0x92, 0x82, 0xfe, 0x80, 0x8f, 0x81, 0x40,
+ 0xf8, 0x80, 0x60, 0x52, 0x63, 0x10, 0x83, 0x40,
+ 0xa8, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80,
+ 0xc0, 0x01, 0x80, 0x44, 0x39, 0x80, 0xaf, 0x80,
+ 0x44, 0x85, 0x80, 0x40, 0xc6, 0x80, 0x41, 0x35,
+ 0x81, 0x40, 0x97, 0x85, 0xc3, 0x85, 0xd8, 0x83,
+ 0x43, 0xb7, 0x84, 0xab, 0x83, 0x40, 0xbc, 0x86,
+ 0xef, 0x83, 0xfe, 0x82, 0x40, 0x80, 0x0d, 0x80,
+ 0x8f, 0x81, 0xd7, 0x84, 0xeb, 0x80, 0x41, 0xa0,
+ 0x82, 0x8b, 0x81, 0x41, 0x65, 0x1a, 0x8e, 0xe8,
+ 0x81, 0x40, 0xf8, 0x82, 0x42, 0x04, 0x00, 0x80,
+ 0x40, 0xfa, 0x81, 0xd6, 0x0b, 0x81, 0x41, 0x9d,
+ 0x82, 0xac, 0x80, 0x42, 0x84, 0x81, 0x45, 0x76,
+ 0x84, 0x60, 0x45, 0xf8, 0x81, 0x40, 0x84, 0x80,
+ 0xc0, 0x82, 0x89, 0x80, 0x43, 0x51, 0x81, 0x60,
+ 0x4e, 0x05, 0x80, 0x5d, 0xe6, 0x83,
+};
+
+static const uint8_t unicode_prop_Unified_Ideograph_table[42] = {
+ 0x60, 0x33, 0xff, 0x59, 0xbf, 0xbf, 0x60, 0x51,
+ 0xff, 0x60, 0x5a, 0x0d, 0x08, 0x00, 0x81, 0x89,
+ 0x00, 0x00, 0x09, 0x82, 0x61, 0x05, 0xd5, 0x60,
+ 0xa6, 0xdf, 0x9f, 0x50, 0x38, 0x86, 0x40, 0xdd,
+ 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x54, 0x1e,
+ 0x53, 0x4a,
+};
+
+static const uint8_t unicode_prop_Variation_Selector_table[13] = {
+ 0x58, 0x0a, 0x10, 0x80, 0x60, 0xe5, 0xef, 0x8f,
+ 0x6d, 0x02, 0xef, 0x40, 0xef,
+};
+
+static const uint8_t unicode_prop_White_Space_table[22] = {
+ 0x88, 0x84, 0x91, 0x80, 0xe3, 0x80, 0x99, 0x80,
+ 0x55, 0xde, 0x80, 0x49, 0x7e, 0x8a, 0x9c, 0x0c,
+ 0x80, 0xae, 0x80, 0x4f, 0x9f, 0x80,
+};
+
+static const uint8_t unicode_prop_Bidi_Mirrored_table[173] = {
+ 0xa7, 0x81, 0x91, 0x00, 0x80, 0x9b, 0x00, 0x80,
+ 0x9c, 0x00, 0x80, 0xac, 0x80, 0x8e, 0x80, 0x4e,
+ 0x7d, 0x83, 0x47, 0x5c, 0x81, 0x49, 0x9b, 0x81,
+ 0x89, 0x81, 0xb5, 0x81, 0x8d, 0x81, 0x40, 0xb0,
+ 0x80, 0x40, 0xbf, 0x1a, 0x2a, 0x02, 0x0a, 0x18,
+ 0x18, 0x00, 0x03, 0x88, 0x20, 0x80, 0x91, 0x23,
+ 0x88, 0x08, 0x00, 0x39, 0x9e, 0x0b, 0x20, 0x88,
+ 0x09, 0x92, 0x21, 0x88, 0x21, 0x0b, 0x97, 0x81,
+ 0x8f, 0x3b, 0x93, 0x0e, 0x81, 0x44, 0x3c, 0x8d,
+ 0xc9, 0x01, 0x18, 0x08, 0x14, 0x1c, 0x12, 0x8d,
+ 0x41, 0x92, 0x95, 0x0d, 0x80, 0x8d, 0x38, 0x35,
+ 0x10, 0x1c, 0x01, 0x0c, 0x18, 0x02, 0x09, 0x89,
+ 0x29, 0x81, 0x8b, 0x92, 0x03, 0x08, 0x00, 0x08,
+ 0x03, 0x21, 0x2a, 0x97, 0x81, 0x8a, 0x0b, 0x18,
+ 0x09, 0x0b, 0xaa, 0x0f, 0x80, 0xa7, 0x20, 0x00,
+ 0x14, 0x22, 0x18, 0x14, 0x00, 0x40, 0xff, 0x80,
+ 0x42, 0x02, 0x1a, 0x08, 0x81, 0x8d, 0x09, 0x89,
+ 0xaa, 0x87, 0x41, 0xaa, 0x89, 0x0f, 0x60, 0xce,
+ 0x3c, 0x2c, 0x81, 0x40, 0xa1, 0x81, 0x91, 0x00,
+ 0x80, 0x9b, 0x00, 0x80, 0x9c, 0x00, 0x00, 0x08,
+ 0x81, 0x60, 0xd7, 0x76, 0x80, 0xb8, 0x80, 0xb8,
+ 0x80, 0xb8, 0x80, 0xb8, 0x80,
+};
+
+static const uint8_t unicode_prop_Emoji_table[239] = {
+ 0xa2, 0x05, 0x04, 0x89, 0xee, 0x03, 0x80, 0x5f,
+ 0x8c, 0x80, 0x8b, 0x80, 0x40, 0xd7, 0x80, 0x95,
+ 0x80, 0xd9, 0x85, 0x8e, 0x81, 0x41, 0x6e, 0x81,
+ 0x8b, 0x80, 0x40, 0xa5, 0x80, 0x98, 0x8a, 0x1a,
+ 0x40, 0xc6, 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80,
+ 0x88, 0x80, 0xb9, 0x18, 0x84, 0x88, 0x01, 0x01,
+ 0x09, 0x03, 0x01, 0x00, 0x09, 0x02, 0x02, 0x0f,
+ 0x14, 0x00, 0x04, 0x8b, 0x8a, 0x09, 0x00, 0x08,
+ 0x80, 0x91, 0x01, 0x81, 0x91, 0x28, 0x00, 0x0a,
+ 0x0c, 0x01, 0x0b, 0x81, 0x8a, 0x0c, 0x09, 0x04,
+ 0x08, 0x00, 0x81, 0x93, 0x0c, 0x28, 0x19, 0x03,
+ 0x01, 0x01, 0x28, 0x01, 0x00, 0x00, 0x05, 0x02,
+ 0x05, 0x80, 0x89, 0x81, 0x8e, 0x01, 0x03, 0x00,
+ 0x03, 0x10, 0x80, 0x8a, 0x81, 0xaf, 0x82, 0x88,
+ 0x80, 0x8d, 0x80, 0x8d, 0x80, 0x41, 0x73, 0x81,
+ 0x41, 0xce, 0x82, 0x92, 0x81, 0xb2, 0x03, 0x80,
+ 0x44, 0xd9, 0x80, 0x8b, 0x80, 0x42, 0x58, 0x00,
+ 0x80, 0x61, 0xbd, 0x69, 0x80, 0x40, 0xc9, 0x80,
+ 0x40, 0x9f, 0x81, 0x8b, 0x81, 0x8d, 0x01, 0x89,
+ 0xca, 0x99, 0x01, 0x96, 0x80, 0x93, 0x01, 0x88,
+ 0x94, 0x81, 0x40, 0xad, 0xa1, 0x81, 0xef, 0x09,
+ 0x02, 0x81, 0xd2, 0x0a, 0x80, 0x41, 0x06, 0x80,
+ 0xbe, 0x8a, 0x28, 0x97, 0x31, 0x0f, 0x8b, 0x01,
+ 0x19, 0x03, 0x81, 0x8c, 0x09, 0x07, 0x81, 0x88,
+ 0x04, 0x82, 0x8b, 0x17, 0x11, 0x00, 0x03, 0x05,
+ 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0a, 0x84,
+ 0x88, 0x10, 0x01, 0x10, 0x81, 0x89, 0x40, 0xe2,
+ 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80, 0x89, 0x80,
+ 0x40, 0xb8, 0xef, 0x22, 0x22, 0x86, 0x88, 0x9c,
+ 0x82, 0x8a, 0x25, 0x89, 0x89, 0x2f, 0x3e,
+};
+
+static const uint8_t unicode_prop_Emoji_Component_table[28] = {
+ 0xa2, 0x05, 0x04, 0x89, 0x5f, 0xd2, 0x80, 0x40,
+ 0xd4, 0x80, 0x60, 0xdd, 0x2a, 0x80, 0x60, 0xf3,
+ 0xd5, 0x99, 0x41, 0xfa, 0x84, 0x45, 0xaf, 0x83,
+ 0x6c, 0x06, 0x6b, 0xdf,
+};
+
+static const uint8_t unicode_prop_Emoji_Modifier_table[4] = {
+ 0x61, 0xf3, 0xfa, 0x84,
+};
+
+static const uint8_t unicode_prop_Emoji_Modifier_Base_table[71] = {
+ 0x60, 0x26, 0x1c, 0x80, 0x40, 0xda, 0x80, 0x8f,
+ 0x83, 0x61, 0xcc, 0x76, 0x80, 0xbb, 0x11, 0x01,
+ 0x82, 0xf4, 0x09, 0x8a, 0x94, 0x92, 0x10, 0x1a,
+ 0x02, 0x30, 0x00, 0x97, 0x80, 0x40, 0xc8, 0x0b,
+ 0x80, 0x94, 0x03, 0x81, 0x40, 0xad, 0x12, 0x84,
+ 0xd2, 0x80, 0x8f, 0x82, 0x88, 0x80, 0x8a, 0x80,
+ 0x42, 0x3e, 0x01, 0x07, 0x3d, 0x80, 0x88, 0x89,
+ 0x0a, 0xb7, 0x80, 0xbc, 0x08, 0x08, 0x80, 0x90,
+ 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, 0x86,
+};
+
+static const uint8_t unicode_prop_Emoji_Presentation_table[145] = {
+ 0x60, 0x23, 0x19, 0x81, 0x40, 0xcc, 0x1a, 0x01,
+ 0x80, 0x42, 0x08, 0x81, 0x94, 0x81, 0xb1, 0x8b,
+ 0xaa, 0x80, 0x92, 0x80, 0x8c, 0x07, 0x81, 0x90,
+ 0x0c, 0x0f, 0x04, 0x80, 0x94, 0x06, 0x08, 0x03,
+ 0x01, 0x06, 0x03, 0x81, 0x9b, 0x80, 0xa2, 0x00,
+ 0x03, 0x10, 0x80, 0xbc, 0x82, 0x97, 0x80, 0x8d,
+ 0x80, 0x43, 0x5a, 0x81, 0xb2, 0x03, 0x80, 0x61,
+ 0xc4, 0xad, 0x80, 0x40, 0xc9, 0x80, 0x40, 0xbd,
+ 0x01, 0x89, 0xca, 0x99, 0x00, 0x97, 0x80, 0x93,
+ 0x01, 0x20, 0x82, 0x94, 0x81, 0x40, 0xad, 0xa0,
+ 0x8b, 0x88, 0x80, 0xc5, 0x80, 0x95, 0x8b, 0xaa,
+ 0x1c, 0x8b, 0x90, 0x10, 0x82, 0xc6, 0x00, 0x80,
+ 0x40, 0xba, 0x81, 0xbe, 0x8c, 0x18, 0x97, 0x91,
+ 0x80, 0x99, 0x81, 0x8c, 0x80, 0xd5, 0xd4, 0xaf,
+ 0xc5, 0x28, 0x12, 0x0a, 0x22, 0x8a, 0x0e, 0x88,
+ 0x40, 0xe2, 0x8b, 0x18, 0x41, 0x1a, 0xae, 0x80,
+ 0x89, 0x80, 0x40, 0xb8, 0xef, 0x22, 0x22, 0x86,
+ 0x88, 0x9c, 0x82, 0x8a, 0x25, 0x89, 0x89, 0x2f,
+ 0x3e,
+};
+
+static const uint8_t unicode_prop_Extended_Pictographic_table[156] = {
+ 0x40, 0xa8, 0x03, 0x80, 0x5f, 0x8c, 0x80, 0x8b,
+ 0x80, 0x40, 0xd7, 0x80, 0x95, 0x80, 0xd9, 0x85,
+ 0x8e, 0x81, 0x41, 0x6e, 0x81, 0x8b, 0x80, 0xde,
+ 0x80, 0xc5, 0x80, 0x98, 0x8a, 0x1a, 0x40, 0xc6,
+ 0x80, 0x40, 0xe6, 0x81, 0x89, 0x80, 0x88, 0x80,
+ 0xb9, 0x18, 0x28, 0x8b, 0x80, 0xf1, 0x89, 0xf5,
+ 0x81, 0x8a, 0x00, 0x00, 0x28, 0x10, 0x28, 0x89,
+ 0x81, 0x8e, 0x01, 0x03, 0x00, 0x03, 0x10, 0x80,
+ 0x8a, 0x84, 0xac, 0x82, 0x88, 0x80, 0x8d, 0x80,
+ 0x8d, 0x80, 0x41, 0x73, 0x81, 0x41, 0xce, 0x82,
+ 0x92, 0x81, 0xb2, 0x03, 0x80, 0x44, 0xd9, 0x80,
+ 0x8b, 0x80, 0x42, 0x58, 0x00, 0x80, 0x61, 0xbd,
+ 0x65, 0x40, 0xff, 0x8c, 0x82, 0x9e, 0x80, 0xbb,
+ 0x85, 0x8b, 0x81, 0x8d, 0x01, 0x89, 0x91, 0xb8,
+ 0x9a, 0x8e, 0x89, 0x80, 0x93, 0x01, 0x88, 0x03,
+ 0x88, 0x41, 0xb1, 0x84, 0x41, 0x3d, 0x87, 0x41,
+ 0x09, 0xaf, 0xff, 0xf3, 0x8b, 0xd4, 0xaa, 0x8b,
+ 0x83, 0xb7, 0x87, 0x89, 0x85, 0xa7, 0x87, 0x9d,
+ 0xd1, 0x8b, 0xae, 0x80, 0x89, 0x80, 0x41, 0xb8,
+ 0x40, 0xff, 0x43, 0xfd,
+};
+
+static const uint8_t unicode_prop_Default_Ignorable_Code_Point_table[51] = {
+ 0x40, 0xac, 0x80, 0x42, 0xa0, 0x80, 0x42, 0xcb,
+ 0x80, 0x4b, 0x41, 0x81, 0x46, 0x52, 0x81, 0xd4,
+ 0x84, 0x47, 0xfa, 0x84, 0x99, 0x84, 0xb0, 0x8f,
+ 0x50, 0xf3, 0x80, 0x60, 0xcc, 0x9a, 0x8f, 0x40,
+ 0xee, 0x80, 0x40, 0x9f, 0x80, 0xce, 0x88, 0x60,
+ 0xbc, 0xa6, 0x83, 0x54, 0xce, 0x87, 0x6c, 0x2e,
+ 0x84, 0x4f, 0xff,
+};
+
+typedef enum {
+ UNICODE_PROP_Hyphen,
+ UNICODE_PROP_Other_Math,
+ UNICODE_PROP_Other_Alphabetic,
+ UNICODE_PROP_Other_Lowercase,
+ UNICODE_PROP_Other_Uppercase,
+ UNICODE_PROP_Other_Grapheme_Extend,
+ UNICODE_PROP_Other_Default_Ignorable_Code_Point,
+ UNICODE_PROP_Other_ID_Start,
+ UNICODE_PROP_Other_ID_Continue,
+ UNICODE_PROP_Prepended_Concatenation_Mark,
+ UNICODE_PROP_ID_Continue1,
+ UNICODE_PROP_XID_Start1,
+ UNICODE_PROP_XID_Continue1,
+ UNICODE_PROP_Changes_When_Titlecased1,
+ UNICODE_PROP_Changes_When_Casefolded1,
+ UNICODE_PROP_Changes_When_NFKC_Casefolded1,
+ UNICODE_PROP_ASCII_Hex_Digit,
+ UNICODE_PROP_Bidi_Control,
+ UNICODE_PROP_Dash,
+ UNICODE_PROP_Deprecated,
+ UNICODE_PROP_Diacritic,
+ UNICODE_PROP_Extender,
+ UNICODE_PROP_Hex_Digit,
+ UNICODE_PROP_IDS_Binary_Operator,
+ UNICODE_PROP_IDS_Trinary_Operator,
+ UNICODE_PROP_Ideographic,
+ UNICODE_PROP_Join_Control,
+ UNICODE_PROP_Logical_Order_Exception,
+ UNICODE_PROP_Noncharacter_Code_Point,
+ UNICODE_PROP_Pattern_Syntax,
+ UNICODE_PROP_Pattern_White_Space,
+ UNICODE_PROP_Quotation_Mark,
+ UNICODE_PROP_Radical,
+ UNICODE_PROP_Regional_Indicator,
+ UNICODE_PROP_Sentence_Terminal,
+ UNICODE_PROP_Soft_Dotted,
+ UNICODE_PROP_Terminal_Punctuation,
+ UNICODE_PROP_Unified_Ideograph,
+ UNICODE_PROP_Variation_Selector,
+ UNICODE_PROP_White_Space,
+ UNICODE_PROP_Bidi_Mirrored,
+ UNICODE_PROP_Emoji,
+ UNICODE_PROP_Emoji_Component,
+ UNICODE_PROP_Emoji_Modifier,
+ UNICODE_PROP_Emoji_Modifier_Base,
+ UNICODE_PROP_Emoji_Presentation,
+ UNICODE_PROP_Extended_Pictographic,
+ UNICODE_PROP_Default_Ignorable_Code_Point,
+ UNICODE_PROP_ID_Start,
+ UNICODE_PROP_Case_Ignorable,
+ UNICODE_PROP_ASCII,
+ UNICODE_PROP_Alphabetic,
+ UNICODE_PROP_Any,
+ UNICODE_PROP_Assigned,
+ UNICODE_PROP_Cased,
+ UNICODE_PROP_Changes_When_Casefolded,
+ UNICODE_PROP_Changes_When_Casemapped,
+ UNICODE_PROP_Changes_When_Lowercased,
+ UNICODE_PROP_Changes_When_NFKC_Casefolded,
+ UNICODE_PROP_Changes_When_Titlecased,
+ UNICODE_PROP_Changes_When_Uppercased,
+ UNICODE_PROP_Grapheme_Base,
+ UNICODE_PROP_Grapheme_Extend,
+ UNICODE_PROP_ID_Continue,
+ UNICODE_PROP_Lowercase,
+ UNICODE_PROP_Math,
+ UNICODE_PROP_Uppercase,
+ UNICODE_PROP_XID_Continue,
+ UNICODE_PROP_XID_Start,
+ UNICODE_PROP_Cased1,
+ UNICODE_PROP_COUNT,
+} UnicodePropertyEnum;
+
+static const char unicode_prop_name_table[] =
+ "ASCII_Hex_Digit,AHex" "\0"
+ "Bidi_Control,Bidi_C" "\0"
+ "Dash" "\0"
+ "Deprecated,Dep" "\0"
+ "Diacritic,Dia" "\0"
+ "Extender,Ext" "\0"
+ "Hex_Digit,Hex" "\0"
+ "IDS_Binary_Operator,IDSB" "\0"
+ "IDS_Trinary_Operator,IDST" "\0"
+ "Ideographic,Ideo" "\0"
+ "Join_Control,Join_C" "\0"
+ "Logical_Order_Exception,LOE" "\0"
+ "Noncharacter_Code_Point,NChar" "\0"
+ "Pattern_Syntax,Pat_Syn" "\0"
+ "Pattern_White_Space,Pat_WS" "\0"
+ "Quotation_Mark,QMark" "\0"
+ "Radical" "\0"
+ "Regional_Indicator,RI" "\0"
+ "Sentence_Terminal,STerm" "\0"
+ "Soft_Dotted,SD" "\0"
+ "Terminal_Punctuation,Term" "\0"
+ "Unified_Ideograph,UIdeo" "\0"
+ "Variation_Selector,VS" "\0"
+ "White_Space,space" "\0"
+ "Bidi_Mirrored,Bidi_M" "\0"
+ "Emoji" "\0"
+ "Emoji_Component,EComp" "\0"
+ "Emoji_Modifier,EMod" "\0"
+ "Emoji_Modifier_Base,EBase" "\0"
+ "Emoji_Presentation,EPres" "\0"
+ "Extended_Pictographic,ExtPict" "\0"
+ "Default_Ignorable_Code_Point,DI" "\0"
+ "ID_Start,IDS" "\0"
+ "Case_Ignorable,CI" "\0"
+ "ASCII" "\0"
+ "Alphabetic,Alpha" "\0"
+ "Any" "\0"
+ "Assigned" "\0"
+ "Cased" "\0"
+ "Changes_When_Casefolded,CWCF" "\0"
+ "Changes_When_Casemapped,CWCM" "\0"
+ "Changes_When_Lowercased,CWL" "\0"
+ "Changes_When_NFKC_Casefolded,CWKCF" "\0"
+ "Changes_When_Titlecased,CWT" "\0"
+ "Changes_When_Uppercased,CWU" "\0"
+ "Grapheme_Base,Gr_Base" "\0"
+ "Grapheme_Extend,Gr_Ext" "\0"
+ "ID_Continue,IDC" "\0"
+ "Lowercase,Lower" "\0"
+ "Math" "\0"
+ "Uppercase,Upper" "\0"
+ "XID_Continue,XIDC" "\0"
+ "XID_Start,XIDS" "\0"
+;
+
+static const uint8_t * const unicode_prop_table[] = {
+ unicode_prop_Hyphen_table,
+ unicode_prop_Other_Math_table,
+ unicode_prop_Other_Alphabetic_table,
+ unicode_prop_Other_Lowercase_table,
+ unicode_prop_Other_Uppercase_table,
+ unicode_prop_Other_Grapheme_Extend_table,
+ unicode_prop_Other_Default_Ignorable_Code_Point_table,
+ unicode_prop_Other_ID_Start_table,
+ unicode_prop_Other_ID_Continue_table,
+ unicode_prop_Prepended_Concatenation_Mark_table,
+ unicode_prop_ID_Continue1_table,
+ unicode_prop_XID_Start1_table,
+ unicode_prop_XID_Continue1_table,
+ unicode_prop_Changes_When_Titlecased1_table,
+ unicode_prop_Changes_When_Casefolded1_table,
+ unicode_prop_Changes_When_NFKC_Casefolded1_table,
+ unicode_prop_ASCII_Hex_Digit_table,
+ unicode_prop_Bidi_Control_table,
+ unicode_prop_Dash_table,
+ unicode_prop_Deprecated_table,
+ unicode_prop_Diacritic_table,
+ unicode_prop_Extender_table,
+ unicode_prop_Hex_Digit_table,
+ unicode_prop_IDS_Binary_Operator_table,
+ unicode_prop_IDS_Trinary_Operator_table,
+ unicode_prop_Ideographic_table,
+ unicode_prop_Join_Control_table,
+ unicode_prop_Logical_Order_Exception_table,
+ unicode_prop_Noncharacter_Code_Point_table,
+ unicode_prop_Pattern_Syntax_table,
+ unicode_prop_Pattern_White_Space_table,
+ unicode_prop_Quotation_Mark_table,
+ unicode_prop_Radical_table,
+ unicode_prop_Regional_Indicator_table,
+ unicode_prop_Sentence_Terminal_table,
+ unicode_prop_Soft_Dotted_table,
+ unicode_prop_Terminal_Punctuation_table,
+ unicode_prop_Unified_Ideograph_table,
+ unicode_prop_Variation_Selector_table,
+ unicode_prop_White_Space_table,
+ unicode_prop_Bidi_Mirrored_table,
+ unicode_prop_Emoji_table,
+ unicode_prop_Emoji_Component_table,
+ unicode_prop_Emoji_Modifier_table,
+ unicode_prop_Emoji_Modifier_Base_table,
+ unicode_prop_Emoji_Presentation_table,
+ unicode_prop_Extended_Pictographic_table,
+ unicode_prop_Default_Ignorable_Code_Point_table,
+ unicode_prop_ID_Start_table,
+ unicode_prop_Case_Ignorable_table,
+};
+
+static const uint16_t unicode_prop_len_table[] = {
+ countof(unicode_prop_Hyphen_table),
+ countof(unicode_prop_Other_Math_table),
+ countof(unicode_prop_Other_Alphabetic_table),
+ countof(unicode_prop_Other_Lowercase_table),
+ countof(unicode_prop_Other_Uppercase_table),
+ countof(unicode_prop_Other_Grapheme_Extend_table),
+ countof(unicode_prop_Other_Default_Ignorable_Code_Point_table),
+ countof(unicode_prop_Other_ID_Start_table),
+ countof(unicode_prop_Other_ID_Continue_table),
+ countof(unicode_prop_Prepended_Concatenation_Mark_table),
+ countof(unicode_prop_ID_Continue1_table),
+ countof(unicode_prop_XID_Start1_table),
+ countof(unicode_prop_XID_Continue1_table),
+ countof(unicode_prop_Changes_When_Titlecased1_table),
+ countof(unicode_prop_Changes_When_Casefolded1_table),
+ countof(unicode_prop_Changes_When_NFKC_Casefolded1_table),
+ countof(unicode_prop_ASCII_Hex_Digit_table),
+ countof(unicode_prop_Bidi_Control_table),
+ countof(unicode_prop_Dash_table),
+ countof(unicode_prop_Deprecated_table),
+ countof(unicode_prop_Diacritic_table),
+ countof(unicode_prop_Extender_table),
+ countof(unicode_prop_Hex_Digit_table),
+ countof(unicode_prop_IDS_Binary_Operator_table),
+ countof(unicode_prop_IDS_Trinary_Operator_table),
+ countof(unicode_prop_Ideographic_table),
+ countof(unicode_prop_Join_Control_table),
+ countof(unicode_prop_Logical_Order_Exception_table),
+ countof(unicode_prop_Noncharacter_Code_Point_table),
+ countof(unicode_prop_Pattern_Syntax_table),
+ countof(unicode_prop_Pattern_White_Space_table),
+ countof(unicode_prop_Quotation_Mark_table),
+ countof(unicode_prop_Radical_table),
+ countof(unicode_prop_Regional_Indicator_table),
+ countof(unicode_prop_Sentence_Terminal_table),
+ countof(unicode_prop_Soft_Dotted_table),
+ countof(unicode_prop_Terminal_Punctuation_table),
+ countof(unicode_prop_Unified_Ideograph_table),
+ countof(unicode_prop_Variation_Selector_table),
+ countof(unicode_prop_White_Space_table),
+ countof(unicode_prop_Bidi_Mirrored_table),
+ countof(unicode_prop_Emoji_table),
+ countof(unicode_prop_Emoji_Component_table),
+ countof(unicode_prop_Emoji_Modifier_table),
+ countof(unicode_prop_Emoji_Modifier_Base_table),
+ countof(unicode_prop_Emoji_Presentation_table),
+ countof(unicode_prop_Extended_Pictographic_table),
+ countof(unicode_prop_Default_Ignorable_Code_Point_table),
+ countof(unicode_prop_ID_Start_table),
+ countof(unicode_prop_Case_Ignorable_table),
+};
+
+#endif /* CONFIG_ALL_UNICODE */
diff --git a/src/shared/quickjs/libunicode.c b/src/shared/quickjs/libunicode.c
new file mode 100644
index 000000000..112da72da
--- /dev/null
+++ b/src/shared/quickjs/libunicode.c
@@ -0,0 +1,1558 @@
+/*
+ * Unicode utilities
+ *
+ * Copyright (c) 2017-2018 Fabrice Bellard
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <assert.h>
+
+#include "cutils.h"
+#include "libunicode.h"
+#include "libunicode-table.h"
+
+enum {
+ RUN_TYPE_U,
+ RUN_TYPE_L,
+ RUN_TYPE_UF,
+ RUN_TYPE_LF,
+ RUN_TYPE_UL,
+ RUN_TYPE_LSU,
+ RUN_TYPE_U2L_399_EXT2,
+ RUN_TYPE_UF_D20,
+ RUN_TYPE_UF_D1_EXT,
+ RUN_TYPE_U_EXT,
+ RUN_TYPE_LF_EXT,
+ RUN_TYPE_U_EXT2,
+ RUN_TYPE_L_EXT2,
+ RUN_TYPE_U_EXT3,
+};
+
+/* conv_type:
+ 0 = to upper
+ 1 = to lower
+ 2 = case folding (= to lower with modifications)
+*/
+int lre_case_conv(uint32_t *res, uint32_t c, int conv_type)
+{
+ if (c < 128) {
+ if (conv_type) {
+ if (c >= 'A' && c <= 'Z') {
+ c = c - 'A' + 'a';
+ }
+ } else {
+ if (c >= 'a' && c <= 'z') {
+ c = c - 'a' + 'A';
+ }
+ }
+ } else {
+ uint32_t v, code, data, type, len, a, is_lower;
+ int idx, idx_min, idx_max;
+
+ is_lower = (conv_type != 0);
+ idx_min = 0;
+ idx_max = countof(case_conv_table1) - 1;
+ while (idx_min <= idx_max) {
+ idx = (unsigned)(idx_max + idx_min) / 2;
+ v = case_conv_table1[idx];
+ code = v >> (32 - 17);
+ len = (v >> (32 - 17 - 7)) & 0x7f;
+ if (c < code) {
+ idx_max = idx - 1;
+ } else if (c >= code + len) {
+ idx_min = idx + 1;
+ } else {
+ type = (v >> (32 - 17 - 7 - 4)) & 0xf;
+ data = ((v & 0xf) << 8) | case_conv_table2[idx];
+ switch(type) {
+ case RUN_TYPE_U:
+ case RUN_TYPE_L:
+ case RUN_TYPE_UF:
+ case RUN_TYPE_LF:
+ if (conv_type == (type & 1) ||
+ (type >= RUN_TYPE_UF && conv_type == 2)) {
+ c = c - code + (case_conv_table1[data] >> (32 - 17));
+ }
+ break;
+ case RUN_TYPE_UL:
+ a = c - code;
+ if ((a & 1) != (1 - is_lower))
+ break;
+ c = (a ^ 1) + code;
+ break;
+ case RUN_TYPE_LSU:
+ a = c - code;
+ if (a == 1) {
+ c += 2 * is_lower - 1;
+ } else if (a == (1 - is_lower) * 2) {
+ c += (2 * is_lower - 1) * 2;
+ }
+ break;
+ case RUN_TYPE_U2L_399_EXT2:
+ if (!is_lower) {
+ res[0] = c - code + case_conv_ext[data >> 6];
+ res[1] = 0x399;
+ return 2;
+ } else {
+ c = c - code + case_conv_ext[data & 0x3f];
+ }
+ break;
+ case RUN_TYPE_UF_D20:
+ if (conv_type == 1)
+ break;
+ c = data + (conv_type == 2) * 0x20;
+ break;
+ case RUN_TYPE_UF_D1_EXT:
+ if (conv_type == 1)
+ break;
+ c = case_conv_ext[data] + (conv_type == 2);
+ break;
+ case RUN_TYPE_U_EXT:
+ case RUN_TYPE_LF_EXT:
+ if (is_lower != (type - RUN_TYPE_U_EXT))
+ break;
+ c = case_conv_ext[data];
+ break;
+ case RUN_TYPE_U_EXT2:
+ case RUN_TYPE_L_EXT2:
+ if (conv_type != (type - RUN_TYPE_U_EXT2))
+ break;
+ res[0] = c - code + case_conv_ext[data >> 6];
+ res[1] = case_conv_ext[data & 0x3f];
+ return 2;
+ default:
+ case RUN_TYPE_U_EXT3:
+ if (conv_type != 0)
+ break;
+ res[0] = case_conv_ext[data >> 8];
+ res[1] = case_conv_ext[(data >> 4) & 0xf];
+ res[2] = case_conv_ext[data & 0xf];
+ return 3;
+ }
+ break;
+ }
+ }
+ }
+ res[0] = c;
+ return 1;
+}
+
+static uint32_t get_le24(const uint8_t *ptr)
+{
+#if defined(__x86__) || defined(__x86_64__)
+ return *(uint16_t *)ptr | (ptr[2] << 16);
+#else
+ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16);
+#endif
+}
+
+#define UNICODE_INDEX_BLOCK_LEN 32
+
+/* return -1 if not in table, otherwise the offset in the block */
+static int get_index_pos(uint32_t *pcode, uint32_t c,
+ const uint8_t *index_table, int index_table_len)
+{
+ uint32_t code, v;
+ int idx_min, idx_max, idx;
+
+ idx_min = 0;
+ v = get_le24(index_table);
+ code = v & ((1 << 21) - 1);
+ if (c < code) {
+ *pcode = 0;
+ return 0;
+ }
+ idx_max = index_table_len - 1;
+ code = get_le24(index_table + idx_max * 3);
+ if (c >= code)
+ return -1;
+ /* invariant: tab[idx_min] <= c < tab2[idx_max] */
+ while ((idx_max - idx_min) > 1) {
+ idx = (idx_max + idx_min) / 2;
+ v = get_le24(index_table + idx * 3);
+ code = v & ((1 << 21) - 1);
+ if (c < code) {
+ idx_max = idx;
+ } else {
+ idx_min = idx;
+ }
+ }
+ v = get_le24(index_table + idx_min * 3);
+ *pcode = v & ((1 << 21) - 1);
+ return (idx_min + 1) * UNICODE_INDEX_BLOCK_LEN + (v >> 21);
+}
+
+static BOOL lre_is_in_table(uint32_t c, const uint8_t *table,
+ const uint8_t *index_table, int index_table_len)
+{
+ uint32_t code, b, bit;
+ int pos;
+ const uint8_t *p;
+
+ pos = get_index_pos(&code, c, index_table, index_table_len);
+ if (pos < 0)
+ return FALSE; /* outside the table */
+ p = table + pos;
+ bit = 0;
+ for(;;) {
+ b = *p++;
+ if (b < 64) {
+ code += (b >> 3) + 1;
+ if (c < code)
+ return bit;
+ bit ^= 1;
+ code += (b & 7) + 1;
+ } else if (b >= 0x80) {
+ code += b - 0x80 + 1;
+ } else if (b < 0x60) {
+ code += (((b - 0x40) << 8) | p[0]) + 1;
+ p++;
+ } else {
+ code += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1;
+ p += 2;
+ }
+ if (c < code)
+ return bit;
+ bit ^= 1;
+ }
+}
+
+BOOL lre_is_cased(uint32_t c)
+{
+ uint32_t v, code, len;
+ int idx, idx_min, idx_max;
+
+ idx_min = 0;
+ idx_max = countof(case_conv_table1) - 1;
+ while (idx_min <= idx_max) {
+ idx = (unsigned)(idx_max + idx_min) / 2;
+ v = case_conv_table1[idx];
+ code = v >> (32 - 17);
+ len = (v >> (32 - 17 - 7)) & 0x7f;
+ if (c < code) {
+ idx_max = idx - 1;
+ } else if (c >= code + len) {
+ idx_min = idx + 1;
+ } else {
+ return TRUE;
+ }
+ }
+ return lre_is_in_table(c, unicode_prop_Cased1_table,
+ unicode_prop_Cased1_index,
+ sizeof(unicode_prop_Cased1_index) / 3);
+}
+
+BOOL lre_is_case_ignorable(uint32_t c)
+{
+ return lre_is_in_table(c, unicode_prop_Case_Ignorable_table,
+ unicode_prop_Case_Ignorable_index,
+ sizeof(unicode_prop_Case_Ignorable_index) / 3);
+}
+
+/* character range */
+
+static maybe_unused void cr_dump(CharRange *cr)
+{
+ int i;
+ for(i = 0; i < cr->len; i++)
+ printf("%d: 0x%04x\n", i, cr->points[i]);
+}
+
+static void *cr_default_realloc(void *opaque, void *ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+void cr_init(CharRange *cr, void *mem_opaque, DynBufReallocFunc *realloc_func)
+{
+ cr->len = cr->size = 0;
+ cr->points = NULL;
+ cr->mem_opaque = mem_opaque;
+ cr->realloc_func = realloc_func ? realloc_func : cr_default_realloc;
+}
+
+void cr_free(CharRange *cr)
+{
+ cr->realloc_func(cr->mem_opaque, cr->points, 0);
+}
+
+int cr_realloc(CharRange *cr, int size)
+{
+ int new_size;
+ uint32_t *new_buf;
+
+ if (size > cr->size) {
+ new_size = max_int(size, cr->size * 3 / 2);
+ new_buf = cr->realloc_func(cr->mem_opaque, cr->points,
+ new_size * sizeof(cr->points[0]));
+ if (!new_buf)
+ return -1;
+ cr->points = new_buf;
+ cr->size = new_size;
+ }
+ return 0;
+}
+
+int cr_copy(CharRange *cr, const CharRange *cr1)
+{
+ if (cr_realloc(cr, cr1->len))
+ return -1;
+ memcpy(cr->points, cr1->points, sizeof(cr->points[0]) * cr1->len);
+ cr->len = cr1->len;
+ return 0;
+}
+
+/* merge consecutive intervals and remove empty intervals */
+static void cr_compress(CharRange *cr)
+{
+ int i, j, k, len;
+ uint32_t *pt;
+
+ pt = cr->points;
+ len = cr->len;
+ i = 0;
+ j = 0;
+ k = 0;
+ while ((i + 1) < len) {
+ if (pt[i] == pt[i + 1]) {
+ /* empty interval */
+ i += 2;
+ } else {
+ j = i;
+ while ((j + 3) < len && pt[j + 1] == pt[j + 2])
+ j += 2;
+ /* just copy */
+ pt[k] = pt[i];
+ pt[k + 1] = pt[j + 1];
+ k += 2;
+ i = j + 2;
+ }
+ }
+ cr->len = k;
+}
+
+/* union or intersection */
+int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
+ const uint32_t *b_pt, int b_len, int op)
+{
+ int a_idx, b_idx, is_in;
+ uint32_t v;
+
+ a_idx = 0;
+ b_idx = 0;
+ for(;;) {
+ /* get one more point from a or b in increasing order */
+ if (a_idx < a_len && b_idx < b_len) {
+ if (a_pt[a_idx] < b_pt[b_idx]) {
+ goto a_add;
+ } else if (a_pt[a_idx] == b_pt[b_idx]) {
+ v = a_pt[a_idx];
+ a_idx++;
+ b_idx++;
+ } else {
+ goto b_add;
+ }
+ } else if (a_idx < a_len) {
+ a_add:
+ v = a_pt[a_idx++];
+ } else if (b_idx < b_len) {
+ b_add:
+ v = b_pt[b_idx++];
+ } else {
+ break;
+ }
+ /* add the point if the in/out status changes */
+ switch(op) {
+ case CR_OP_UNION:
+ is_in = (a_idx & 1) | (b_idx & 1);
+ break;
+ case CR_OP_INTER:
+ is_in = (a_idx & 1) & (b_idx & 1);
+ break;
+ case CR_OP_XOR:
+ is_in = (a_idx & 1) ^ (b_idx & 1);
+ break;
+ default:
+ abort();
+ }
+ if (is_in != (cr->len & 1)) {
+ if (cr_add_point(cr, v))
+ return -1;
+ }
+ }
+ cr_compress(cr);
+ return 0;
+}
+
+int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len)
+{
+ CharRange a = *cr;
+ int ret;
+ cr->len = 0;
+ cr->size = 0;
+ cr->points = NULL;
+ ret = cr_op(cr, a.points, a.len, b_pt, b_len, CR_OP_UNION);
+ cr_free(&a);
+ return ret;
+}
+
+int cr_invert(CharRange *cr)
+{
+ int len;
+ len = cr->len;
+ if (cr_realloc(cr, len + 2))
+ return -1;
+ memmove(cr->points + 1, cr->points, len * sizeof(cr->points[0]));
+ cr->points[0] = 0;
+ cr->points[len + 1] = UINT32_MAX;
+ cr->len = len + 2;
+ cr_compress(cr);
+ return 0;
+}
+
+#ifdef CONFIG_ALL_UNICODE
+
+BOOL lre_is_id_start(uint32_t c)
+{
+ return lre_is_in_table(c, unicode_prop_ID_Start_table,
+ unicode_prop_ID_Start_index,
+ sizeof(unicode_prop_ID_Start_index) / 3);
+}
+
+BOOL lre_is_id_continue(uint32_t c)
+{
+ return lre_is_id_start(c) ||
+ lre_is_in_table(c, unicode_prop_ID_Continue1_table,
+ unicode_prop_ID_Continue1_index,
+ sizeof(unicode_prop_ID_Continue1_index) / 3);
+}
+
+#define UNICODE_DECOMP_LEN_MAX 18
+
+typedef enum {
+ DECOMP_TYPE_C1, /* 16 bit char */
+ DECOMP_TYPE_L1, /* 16 bit char table */
+ DECOMP_TYPE_L2,
+ DECOMP_TYPE_L3,
+ DECOMP_TYPE_L4,
+ DECOMP_TYPE_L5, /* XXX: not used */
+ DECOMP_TYPE_L6, /* XXX: could remove */
+ DECOMP_TYPE_L7, /* XXX: could remove */
+ DECOMP_TYPE_LL1, /* 18 bit char table */
+ DECOMP_TYPE_LL2,
+ DECOMP_TYPE_S1, /* 8 bit char table */
+ DECOMP_TYPE_S2,
+ DECOMP_TYPE_S3,
+ DECOMP_TYPE_S4,
+ DECOMP_TYPE_S5,
+ DECOMP_TYPE_I1, /* increment 16 bit char value */
+ DECOMP_TYPE_I2_0,
+ DECOMP_TYPE_I2_1,
+ DECOMP_TYPE_I3_1,
+ DECOMP_TYPE_I3_2,
+ DECOMP_TYPE_I4_1,
+ DECOMP_TYPE_I4_2,
+ DECOMP_TYPE_B1, /* 16 bit base + 8 bit offset */
+ DECOMP_TYPE_B2,
+ DECOMP_TYPE_B3,
+ DECOMP_TYPE_B4,
+ DECOMP_TYPE_B5,
+ DECOMP_TYPE_B6,
+ DECOMP_TYPE_B7,
+ DECOMP_TYPE_B8,
+ DECOMP_TYPE_B18,
+ DECOMP_TYPE_LS2,
+ DECOMP_TYPE_PAT3,
+ DECOMP_TYPE_S2_UL,
+ DECOMP_TYPE_LS2_UL,
+} DecompTypeEnum;
+
+static uint32_t unicode_get_short_code(uint32_t c)
+{
+ static const uint16_t unicode_short_table[2] = { 0x2044, 0x2215 };
+
+ if (c < 0x80)
+ return c;
+ else if (c < 0x80 + 0x50)
+ return c - 0x80 + 0x300;
+ else
+ return unicode_short_table[c - 0x80 - 0x50];
+}
+
+static uint32_t unicode_get_lower_simple(uint32_t c)
+{
+ if (c < 0x100 || (c >= 0x410 && c <= 0x42f))
+ c += 0x20;
+ else
+ c++;
+ return c;
+}
+
+static uint16_t unicode_get16(const uint8_t *p)
+{
+ return p[0] | (p[1] << 8);
+}
+
+static int unicode_decomp_entry(uint32_t *res, uint32_t c,
+ int idx, uint32_t code, uint32_t len,
+ uint32_t type)
+{
+ uint32_t c1;
+ int l, i, p;
+ const uint8_t *d;
+
+ if (type == DECOMP_TYPE_C1) {
+ res[0] = unicode_decomp_table2[idx];
+ return 1;
+ } else {
+ d = unicode_decomp_data + unicode_decomp_table2[idx];
+ switch(type) {
+ case DECOMP_TYPE_L1:
+ case DECOMP_TYPE_L2:
+ case DECOMP_TYPE_L3:
+ case DECOMP_TYPE_L4:
+ case DECOMP_TYPE_L5:
+ case DECOMP_TYPE_L6:
+ case DECOMP_TYPE_L7:
+ l = type - DECOMP_TYPE_L1 + 1;
+ d += (c - code) * l * 2;
+ for(i = 0; i < l; i++) {
+ if ((res[i] = unicode_get16(d + 2 * i)) == 0)
+ return 0;
+ }
+ return l;
+ case DECOMP_TYPE_LL1:
+ case DECOMP_TYPE_LL2:
+ {
+ uint32_t k, p;
+ l = type - DECOMP_TYPE_LL1 + 1;
+ k = (c - code) * l;
+ p = len * l * 2;
+ for(i = 0; i < l; i++) {
+ c1 = unicode_get16(d + 2 * k) |
+ (((d[p + (k / 4)] >> ((k % 4) * 2)) & 3) << 16);
+ if (!c1)
+ return 0;
+ res[i] = c1;
+ k++;
+ }
+ }
+ return l;
+ case DECOMP_TYPE_S1:
+ case DECOMP_TYPE_S2:
+ case DECOMP_TYPE_S3:
+ case DECOMP_TYPE_S4:
+ case DECOMP_TYPE_S5:
+ l = type - DECOMP_TYPE_S1 + 1;
+ d += (c - code) * l;
+ for(i = 0; i < l; i++) {
+ if ((res[i] = unicode_get_short_code(d[i])) == 0)
+ return 0;
+ }
+ return l;
+ case DECOMP_TYPE_I1:
+ l = 1;
+ p = 0;
+ goto decomp_type_i;
+ case DECOMP_TYPE_I2_0:
+ case DECOMP_TYPE_I2_1:
+ case DECOMP_TYPE_I3_1:
+ case DECOMP_TYPE_I3_2:
+ case DECOMP_TYPE_I4_1:
+ case DECOMP_TYPE_I4_2:
+ l = 2 + ((type - DECOMP_TYPE_I2_0) >> 1);
+ p = ((type - DECOMP_TYPE_I2_0) & 1) + (l > 2);
+ decomp_type_i:
+ for(i = 0; i < l; i++) {
+ c1 = unicode_get16(d + 2 * i);
+ if (i == p)
+ c1 += c - code;
+ res[i] = c1;
+ }
+ return l;
+ case DECOMP_TYPE_B18:
+ l = 18;
+ goto decomp_type_b;
+ case DECOMP_TYPE_B1:
+ case DECOMP_TYPE_B2:
+ case DECOMP_TYPE_B3:
+ case DECOMP_TYPE_B4:
+ case DECOMP_TYPE_B5:
+ case DECOMP_TYPE_B6:
+ case DECOMP_TYPE_B7:
+ case DECOMP_TYPE_B8:
+ l = type - DECOMP_TYPE_B1 + 1;
+ decomp_type_b:
+ {
+ uint32_t c_min;
+ c_min = unicode_get16(d);
+ d += 2 + (c - code) * l;
+ for(i = 0; i < l; i++) {
+ c1 = d[i];
+ if (c1 == 0xff)
+ c1 = 0x20;
+ else
+ c1 += c_min;
+ res[i] = c1;
+ }
+ }
+ return l;
+ case DECOMP_TYPE_LS2:
+ d += (c - code) * 3;
+ if (!(res[0] = unicode_get16(d)))
+ return 0;
+ res[1] = unicode_get_short_code(d[2]);
+ return 2;
+ case DECOMP_TYPE_PAT3:
+ res[0] = unicode_get16(d);
+ res[2] = unicode_get16(d + 2);
+ d += 4 + (c - code) * 2;
+ res[1] = unicode_get16(d);
+ return 3;
+ case DECOMP_TYPE_S2_UL:
+ case DECOMP_TYPE_LS2_UL:
+ c1 = c - code;
+ if (type == DECOMP_TYPE_S2_UL) {
+ d += c1 & ~1;
+ c = unicode_get_short_code(*d);
+ d++;
+ } else {
+ d += (c1 >> 1) * 3;
+ c = unicode_get16(d);
+ d += 2;
+ }
+ if (c1 & 1)
+ c = unicode_get_lower_simple(c);
+ res[0] = c;
+ res[1] = unicode_get_short_code(*d);
+ return 2;
+ }
+ }
+ return 0;
+}
+
+
+/* return the length of the decomposition (length <=
+ UNICODE_DECOMP_LEN_MAX) or 0 if no decomposition */
+static int unicode_decomp_char(uint32_t *res, uint32_t c, BOOL is_compat1)
+{
+ uint32_t v, type, is_compat, code, len;
+ int idx_min, idx_max, idx;
+
+ idx_min = 0;
+ idx_max = countof(unicode_decomp_table1) - 1;
+ while (idx_min <= idx_max) {
+ idx = (idx_max + idx_min) / 2;
+ v = unicode_decomp_table1[idx];
+ code = v >> (32 - 18);
+ len = (v >> (32 - 18 - 7)) & 0x7f;
+ // printf("idx=%d code=%05x len=%d\n", idx, code, len);
+ if (c < code) {
+ idx_max = idx - 1;
+ } else if (c >= code + len) {
+ idx_min = idx + 1;
+ } else {
+ is_compat = v & 1;
+ if (is_compat1 < is_compat)
+ break;
+ type = (v >> (32 - 18 - 7 - 6)) & 0x3f;
+ return unicode_decomp_entry(res, c, idx, code, len, type);
+ }
+ }
+ return 0;
+}
+
+/* return 0 if no pair found */
+static int unicode_compose_pair(uint32_t c0, uint32_t c1)
+{
+ uint32_t code, len, type, v, idx1, d_idx, d_offset, ch;
+ int idx_min, idx_max, idx, d;
+ uint32_t pair[2];
+
+ idx_min = 0;
+ idx_max = countof(unicode_comp_table) - 1;
+ while (idx_min <= idx_max) {
+ idx = (idx_max + idx_min) / 2;
+ idx1 = unicode_comp_table[idx];
+
+ /* idx1 represent an entry of the decomposition table */
+ d_idx = idx1 >> 6;
+ d_offset = idx1 & 0x3f;
+ v = unicode_decomp_table1[d_idx];
+ code = v >> (32 - 18);
+ len = (v >> (32 - 18 - 7)) & 0x7f;
+ type = (v >> (32 - 18 - 7 - 6)) & 0x3f;
+ ch = code + d_offset;
+ unicode_decomp_entry(pair, ch, d_idx, code, len, type);
+ d = c0 - pair[0];
+ if (d == 0)
+ d = c1 - pair[1];
+ if (d < 0) {
+ idx_max = idx - 1;
+ } else if (d > 0) {
+ idx_min = idx + 1;
+ } else {
+ return ch;
+ }
+ }
+ return 0;
+}
+
+/* return the combining class of character c (between 0 and 255) */
+static int unicode_get_cc(uint32_t c)
+{
+ uint32_t code, n, type, cc, c1, b;
+ int pos;
+ const uint8_t *p;
+
+ pos = get_index_pos(&code, c,
+ unicode_cc_index, sizeof(unicode_cc_index) / 3);
+ if (pos < 0)
+ return 0;
+ p = unicode_cc_table + pos;
+ for(;;) {
+ b = *p++;
+ type = b >> 6;
+ n = b & 0x3f;
+ if (n < 48) {
+ } else if (n < 56) {
+ n = (n - 48) << 8;
+ n |= *p++;
+ n += 48;
+ } else {
+ n = (n - 56) << 8;
+ n |= *p++ << 8;
+ n |= *p++;
+ n += 48 + (1 << 11);
+ }
+ if (type <= 1)
+ p++;
+ c1 = code + n + 1;
+ if (c < c1) {
+ switch(type) {
+ case 0:
+ cc = p[-1];
+ break;
+ case 1:
+ cc = p[-1] + c - code;
+ break;
+ case 2:
+ cc = 0;
+ break;
+ default:
+ case 3:
+ cc = 230;
+ break;
+ }
+ return cc;
+ }
+ code = c1;
+ }
+}
+
+static void sort_cc(int *buf, int len)
+{
+ int i, j, k, cc, cc1, start, ch1;
+
+ for(i = 0; i < len; i++) {
+ cc = unicode_get_cc(buf[i]);
+ if (cc != 0) {
+ start = i;
+ j = i + 1;
+ while (j < len) {
+ ch1 = buf[j];
+ cc1 = unicode_get_cc(ch1);
+ if (cc1 == 0)
+ break;
+ k = j - 1;
+ while (k >= start) {
+ if (unicode_get_cc(buf[k]) <= cc1)
+ break;
+ buf[k + 1] = buf[k];
+ k--;
+ }
+ buf[k + 1] = ch1;
+ j++;
+ }
+#if 0
+ printf("cc:");
+ for(k = start; k < j; k++) {
+ printf(" %3d", unicode_get_cc(buf[k]));
+ }
+ printf("\n");
+#endif
+ i = j;
+ }
+ }
+}
+
+static void to_nfd_rec(DynBuf *dbuf,
+ const int *src, int src_len, int is_compat)
+{
+ uint32_t c, v;
+ int i, l;
+ uint32_t res[UNICODE_DECOMP_LEN_MAX];
+
+ for(i = 0; i < src_len; i++) {
+ c = src[i];
+ if (c >= 0xac00 && c < 0xd7a4) {
+ /* Hangul decomposition */
+ c -= 0xac00;
+ dbuf_put_u32(dbuf, 0x1100 + c / 588);
+ dbuf_put_u32(dbuf, 0x1161 + (c % 588) / 28);
+ v = c % 28;
+ if (v != 0)
+ dbuf_put_u32(dbuf, 0x11a7 + v);
+ } else {
+ l = unicode_decomp_char(res, c, is_compat);
+ if (l) {
+ to_nfd_rec(dbuf, (int *)res, l, is_compat);
+ } else {
+ dbuf_put_u32(dbuf, c);
+ }
+ }
+ }
+}
+
+/* return 0 if not found */
+static int compose_pair(uint32_t c0, uint32_t c1)
+{
+ /* Hangul composition */
+ if (c0 >= 0x1100 && c0 < 0x1100 + 19 &&
+ c1 >= 0x1161 && c1 < 0x1161 + 21) {
+ return 0xac00 + (c0 - 0x1100) * 588 + (c1 - 0x1161) * 28;
+ } else if (c0 >= 0xac00 && c0 < 0xac00 + 11172 &&
+ (c0 - 0xac00) % 28 == 0 &&
+ c1 >= 0x11a7 && c1 < 0x11a7 + 28) {
+ return c0 + c1 - 0x11a7;
+ } else {
+ return unicode_compose_pair(c0, c1);
+ }
+}
+
+int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
+ UnicodeNormalizationEnum n_type,
+ void *opaque, DynBufReallocFunc *realloc_func)
+{
+ int *buf, buf_len, i, p, starter_pos, cc, last_cc, out_len;
+ BOOL is_compat;
+ DynBuf dbuf_s, *dbuf = &dbuf_s;
+
+ is_compat = n_type >> 1;
+
+ dbuf_init2(dbuf, opaque, realloc_func);
+ if (dbuf_realloc(dbuf, sizeof(int) * src_len))
+ goto fail;
+
+ /* common case: latin1 is unaffected by NFC */
+ if (n_type == UNICODE_NFC) {
+ for(i = 0; i < src_len; i++) {
+ if (src[i] >= 0x100)
+ goto not_latin1;
+ }
+ buf = (int *)dbuf->buf;
+ memcpy(buf, src, src_len * sizeof(int));
+ *pdst = (uint32_t *)buf;
+ return src_len;
+ not_latin1: ;
+ }
+
+ to_nfd_rec(dbuf, (const int *)src, src_len, is_compat);
+ if (dbuf_error(dbuf)) {
+ fail:
+ *pdst = NULL;
+ return -1;
+ }
+ buf = (int *)dbuf->buf;
+ buf_len = dbuf->size / sizeof(int);
+
+ sort_cc(buf, buf_len);
+
+ if (buf_len <= 1 || (n_type & 1) != 0) {
+ /* NFD / NFKD */
+ *pdst = (uint32_t *)buf;
+ return buf_len;
+ }
+
+ i = 1;
+ out_len = 1;
+ while (i < buf_len) {
+ /* find the starter character and test if it is blocked from
+ the character at 'i' */
+ last_cc = unicode_get_cc(buf[i]);
+ starter_pos = out_len - 1;
+ while (starter_pos >= 0) {
+ cc = unicode_get_cc(buf[starter_pos]);
+ if (cc == 0)
+ break;
+ if (cc >= last_cc)
+ goto next;
+ last_cc = 256;
+ starter_pos--;
+ }
+ if (starter_pos >= 0 &&
+ (p = compose_pair(buf[starter_pos], buf[i])) != 0) {
+ buf[starter_pos] = p;
+ i++;
+ } else {
+ next:
+ buf[out_len++] = buf[i++];
+ }
+ }
+ *pdst = (uint32_t *)buf;
+ return out_len;
+}
+
+/* char ranges for various unicode properties */
+
+static int unicode_find_name(const char *name_table, const char *name)
+{
+ const char *p, *r;
+ int pos;
+ size_t name_len, len;
+
+ p = name_table;
+ pos = 0;
+ name_len = strlen(name);
+ while (*p) {
+ for(;;) {
+ r = strchr(p, ',');
+ if (!r)
+ len = strlen(p);
+ else
+ len = r - p;
+ if (len == name_len && !memcmp(p, name, name_len))
+ return pos;
+ p += len + 1;
+ if (!r)
+ break;
+ }
+ pos++;
+ }
+ return -1;
+}
+
+/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2
+ if not found */
+int unicode_script(CharRange *cr,
+ const char *script_name, BOOL is_ext)
+{
+ int script_idx;
+ const uint8_t *p, *p_end;
+ uint32_t c, c1, b, n, v, v_len, i, type;
+ CharRange cr1_s, *cr1;
+ CharRange cr2_s, *cr2 = &cr2_s;
+ BOOL is_common;
+
+ script_idx = unicode_find_name(unicode_script_name_table, script_name);
+ if (script_idx < 0)
+ return -2;
+ /* Note: we remove the "Unknown" Script */
+ script_idx += UNICODE_SCRIPT_Unknown + 1;
+
+ is_common = (script_idx == UNICODE_SCRIPT_Common ||
+ script_idx == UNICODE_SCRIPT_Inherited);
+ if (is_ext) {
+ cr1 = &cr1_s;
+ cr_init(cr1, cr->mem_opaque, cr->realloc_func);
+ cr_init(cr2, cr->mem_opaque, cr->realloc_func);
+ } else {
+ cr1 = cr;
+ }
+
+ p = unicode_script_table;
+ p_end = unicode_script_table + countof(unicode_script_table);
+ c = 0;
+ while (p < p_end) {
+ b = *p++;
+ type = b >> 7;
+ n = b & 0x7f;
+ if (n < 96) {
+ } else if (n < 112) {
+ n = (n - 96) << 8;
+ n |= *p++;
+ n += 96;
+ } else {
+ n = (n - 112) << 16;
+ n |= *p++ << 8;
+ n |= *p++;
+ n += 96 + (1 << 12);
+ }
+ if (type == 0)
+ v = 0;
+ else
+ v = *p++;
+ c1 = c + n + 1;
+ if (v == script_idx) {
+ if (cr_add_interval(cr1, c, c1))
+ goto fail;
+ }
+ c = c1;
+ }
+
+ if (is_ext) {
+ /* add the script extensions */
+ p = unicode_script_ext_table;
+ p_end = unicode_script_ext_table + countof(unicode_script_ext_table);
+ c = 0;
+ while (p < p_end) {
+ b = *p++;
+ if (b < 128) {
+ n = b;
+ } else if (b < 128 + 64) {
+ n = (b - 128) << 8;
+ n |= *p++;
+ n += 128;
+ } else {
+ n = (b - 128 - 64) << 16;
+ n |= *p++ << 8;
+ n |= *p++;
+ n += 128 + (1 << 14);
+ }
+ c1 = c + n + 1;
+ v_len = *p++;
+ if (is_common) {
+ if (v_len != 0) {
+ if (cr_add_interval(cr2, c, c1))
+ goto fail;
+ }
+ } else {
+ for(i = 0; i < v_len; i++) {
+ if (p[i] == script_idx) {
+ if (cr_add_interval(cr2, c, c1))
+ goto fail;
+ break;
+ }
+ }
+ }
+ p += v_len;
+ c = c1;
+ }
+ if (is_common) {
+ /* remove all the characters with script extensions */
+ if (cr_invert(cr2))
+ goto fail;
+ if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len,
+ CR_OP_INTER))
+ goto fail;
+ } else {
+ if (cr_op(cr, cr1->points, cr1->len, cr2->points, cr2->len,
+ CR_OP_UNION))
+ goto fail;
+ }
+ cr_free(cr1);
+ cr_free(cr2);
+ }
+ return 0;
+ fail:
+ if (is_ext) {
+ cr_free(cr1);
+ cr_free(cr2);
+ }
+ goto fail;
+}
+
+#define M(id) (1U << UNICODE_GC_ ## id)
+
+static int unicode_general_category1(CharRange *cr, uint32_t gc_mask)
+{
+ const uint8_t *p, *p_end;
+ uint32_t c, c0, b, n, v;
+
+ p = unicode_gc_table;
+ p_end = unicode_gc_table + countof(unicode_gc_table);
+ c = 0;
+ while (p < p_end) {
+ b = *p++;
+ n = b >> 5;
+ v = b & 0x1f;
+ if (n == 7) {
+ n = *p++;
+ if (n < 128) {
+ n += 7;
+ } else if (n < 128 + 64) {
+ n = (n - 128) << 8;
+ n |= *p++;
+ n += 7 + 128;
+ } else {
+ n = (n - 128 - 64) << 16;
+ n |= *p++ << 8;
+ n |= *p++;
+ n += 7 + 128 + (1 << 14);
+ }
+ }
+ c0 = c;
+ c += n + 1;
+ if (v == 31) {
+ /* run of Lu / Ll */
+ b = gc_mask & (M(Lu) | M(Ll));
+ if (b != 0) {
+ if (b == (M(Lu) | M(Ll))) {
+ goto add_range;
+ } else {
+ c0 += ((gc_mask & M(Ll)) != 0);
+ for(; c0 < c; c0 += 2) {
+ if (cr_add_interval(cr, c0, c0 + 1))
+ return -1;
+ }
+ }
+ }
+ } else if ((gc_mask >> v) & 1) {
+ add_range:
+ if (cr_add_interval(cr, c0, c))
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int unicode_prop1(CharRange *cr, int prop_idx)
+{
+ const uint8_t *p, *p_end;
+ uint32_t c, c0, b, bit;
+
+ p = unicode_prop_table[prop_idx];
+ p_end = p + unicode_prop_len_table[prop_idx];
+ c = 0;
+ bit = 0;
+ while (p < p_end) {
+ c0 = c;
+ b = *p++;
+ if (b < 64) {
+ c += (b >> 3) + 1;
+ if (bit) {
+ if (cr_add_interval(cr, c0, c))
+ return -1;
+ }
+ bit ^= 1;
+ c0 = c;
+ c += (b & 7) + 1;
+ } else if (b >= 0x80) {
+ c += b - 0x80 + 1;
+ } else if (b < 0x60) {
+ c += (((b - 0x40) << 8) | p[0]) + 1;
+ p++;
+ } else {
+ c += (((b - 0x60) << 16) | (p[0] << 8) | p[1]) + 1;
+ p += 2;
+ }
+ if (bit) {
+ if (cr_add_interval(cr, c0, c))
+ return -1;
+ }
+ bit ^= 1;
+ }
+ return 0;
+}
+
+#define CASE_U (1 << 0)
+#define CASE_L (1 << 1)
+#define CASE_F (1 << 2)
+
+/* use the case conversion table to generate range of characters.
+ CASE_U: set char if modified by uppercasing,
+ CASE_L: set char if modified by lowercasing,
+ CASE_F: set char if modified by case folding,
+ */
+static int unicode_case1(CharRange *cr, int case_mask)
+{
+#define MR(x) (1 << RUN_TYPE_ ## x)
+ const uint32_t tab_run_mask[3] = {
+ MR(U) | MR(UF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(UF_D20) |
+ MR(UF_D1_EXT) | MR(U_EXT) | MR(U_EXT2) | MR(U_EXT3),
+
+ MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(L_EXT2),
+
+ MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT),
+ };
+#undef MR
+ uint32_t mask, v, code, type, len, i, idx;
+
+ if (case_mask == 0)
+ return 0;
+ mask = 0;
+ for(i = 0; i < 3; i++) {
+ if ((case_mask >> i) & 1)
+ mask |= tab_run_mask[i];
+ }
+ for(idx = 0; idx < countof(case_conv_table1); idx++) {
+ v = case_conv_table1[idx];
+ type = (v >> (32 - 17 - 7 - 4)) & 0xf;
+ code = v >> (32 - 17);
+ len = (v >> (32 - 17 - 7)) & 0x7f;
+ if ((mask >> type) & 1) {
+ // printf("%d: type=%d %04x %04x\n", idx, type, code, code + len - 1);
+ switch(type) {
+ case RUN_TYPE_UL:
+ if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F)))
+ goto def_case;
+ code += ((case_mask & CASE_U) != 0);
+ for(i = 0; i < len; i += 2) {
+ if (cr_add_interval(cr, code + i, code + i + 1))
+ return -1;
+ }
+ break;
+ case RUN_TYPE_LSU:
+ if ((case_mask & CASE_U) && (case_mask & (CASE_L | CASE_F)))
+ goto def_case;
+ if (!(case_mask & CASE_U)) {
+ if (cr_add_interval(cr, code, code + 1))
+ return -1;
+ }
+ if (cr_add_interval(cr, code + 1, code + 2))
+ return -1;
+ if (case_mask & CASE_U) {
+ if (cr_add_interval(cr, code + 2, code + 3))
+ return -1;
+ }
+ break;
+ default:
+ def_case:
+ if (cr_add_interval(cr, code, code + len))
+ return -1;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+typedef enum {
+ POP_GC,
+ POP_PROP,
+ POP_CASE,
+ POP_UNION,
+ POP_INTER,
+ POP_XOR,
+ POP_INVERT,
+ POP_END,
+} PropOPEnum;
+
+#define POP_STACK_LEN_MAX 4
+
+static int unicode_prop_ops(CharRange *cr, ...)
+{
+ va_list ap;
+ CharRange stack[POP_STACK_LEN_MAX];
+ int stack_len, op, ret, i;
+ uint32_t a;
+
+ va_start(ap, cr);
+ stack_len = 0;
+ for(;;) {
+ op = va_arg(ap, int);
+ switch(op) {
+ case POP_GC:
+ assert(stack_len < POP_STACK_LEN_MAX);
+ a = va_arg(ap, int);
+ cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func);
+ if (unicode_general_category1(&stack[stack_len - 1], a))
+ goto fail;
+ break;
+ case POP_PROP:
+ assert(stack_len < POP_STACK_LEN_MAX);
+ a = va_arg(ap, int);
+ cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func);
+ if (unicode_prop1(&stack[stack_len - 1], a))
+ goto fail;
+ break;
+ case POP_CASE:
+ assert(stack_len < POP_STACK_LEN_MAX);
+ a = va_arg(ap, int);
+ cr_init(&stack[stack_len++], cr->mem_opaque, cr->realloc_func);
+ if (unicode_case1(&stack[stack_len - 1], a))
+ goto fail;
+ break;
+ case POP_UNION:
+ case POP_INTER:
+ case POP_XOR:
+ {
+ CharRange *cr1, *cr2, *cr3;
+ assert(stack_len >= 2);
+ assert(stack_len < POP_STACK_LEN_MAX);
+ cr1 = &stack[stack_len - 2];
+ cr2 = &stack[stack_len - 1];
+ cr3 = &stack[stack_len++];
+ cr_init(cr3, cr->mem_opaque, cr->realloc_func);
+ if (cr_op(cr3, cr1->points, cr1->len,
+ cr2->points, cr2->len, op - POP_UNION + CR_OP_UNION))
+ goto fail;
+ cr_free(cr1);
+ cr_free(cr2);
+ *cr1 = *cr3;
+ stack_len -= 2;
+ }
+ break;
+ case POP_INVERT:
+ assert(stack_len >= 1);
+ if (cr_invert(&stack[stack_len - 1]))
+ goto fail;
+ break;
+ case POP_END:
+ goto done;
+ default:
+ abort();
+ }
+ }
+ done:
+ va_end(ap);
+ assert(stack_len == 1);
+ ret = cr_copy(cr, &stack[0]);
+ cr_free(&stack[0]);
+ return ret;
+ fail:
+ va_end(ap);
+ for(i = 0; i < stack_len; i++)
+ cr_free(&stack[i]);
+ return -1;
+}
+
+static const uint32_t unicode_gc_mask_table[] = {
+ M(Lu) | M(Ll) | M(Lt), /* LC */
+ M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo), /* L */
+ M(Mn) | M(Mc) | M(Me), /* M */
+ M(Nd) | M(Nl) | M(No), /* N */
+ M(Sm) | M(Sc) | M(Sk) | M(So), /* S */
+ M(Pc) | M(Pd) | M(Ps) | M(Pe) | M(Pi) | M(Pf) | M(Po), /* P */
+ M(Zs) | M(Zl) | M(Zp), /* Z */
+ M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn), /* C */
+};
+
+/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2
+ if not found */
+int unicode_general_category(CharRange *cr, const char *gc_name)
+{
+ int gc_idx;
+ uint32_t gc_mask;
+
+ gc_idx = unicode_find_name(unicode_gc_name_table, gc_name);
+ if (gc_idx < 0)
+ return -2;
+ if (gc_idx <= UNICODE_GC_Co) {
+ gc_mask = (uint64_t)1 << gc_idx;
+ } else {
+ gc_mask = unicode_gc_mask_table[gc_idx - UNICODE_GC_LC];
+ }
+ return unicode_general_category1(cr, gc_mask);
+}
+
+
+/* 'cr' must be initialized and empty. Return 0 if OK, -1 if error, -2
+ if not found */
+int unicode_prop(CharRange *cr, const char *prop_name)
+{
+ int prop_idx, ret;
+
+ prop_idx = unicode_find_name(unicode_prop_name_table, prop_name);
+ if (prop_idx < 0)
+ return -2;
+ prop_idx += UNICODE_PROP_ASCII_Hex_Digit;
+
+ ret = 0;
+ switch(prop_idx) {
+ case UNICODE_PROP_ASCII:
+ if (cr_add_interval(cr, 0x00, 0x7f + 1))
+ return -1;
+ break;
+ case UNICODE_PROP_Any:
+ if (cr_add_interval(cr, 0x00000, 0x10ffff + 1))
+ return -1;
+ break;
+ case UNICODE_PROP_Assigned:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Cn),
+ POP_INVERT,
+ POP_END);
+ break;
+ case UNICODE_PROP_Math:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Sm),
+ POP_PROP, UNICODE_PROP_Other_Math,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_Lowercase:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Ll),
+ POP_PROP, UNICODE_PROP_Other_Lowercase,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_Uppercase:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu),
+ POP_PROP, UNICODE_PROP_Other_Uppercase,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_Cased:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt),
+ POP_PROP, UNICODE_PROP_Other_Uppercase,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Other_Lowercase,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_Alphabetic:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl),
+ POP_PROP, UNICODE_PROP_Other_Uppercase,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Other_Lowercase,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Other_Alphabetic,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_Grapheme_Base:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Cc) | M(Cf) | M(Cs) | M(Co) | M(Cn) | M(Zl) | M(Zp) | M(Me) | M(Mn),
+ POP_PROP, UNICODE_PROP_Other_Grapheme_Extend,
+ POP_UNION,
+ POP_INVERT,
+ POP_END);
+ break;
+ case UNICODE_PROP_Grapheme_Extend:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Me) | M(Mn),
+ POP_PROP, UNICODE_PROP_Other_Grapheme_Extend,
+ POP_UNION,
+ POP_END);
+ break;
+ case UNICODE_PROP_XID_Start:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl),
+ POP_PROP, UNICODE_PROP_Other_ID_Start,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Pattern_Syntax,
+ POP_PROP, UNICODE_PROP_Pattern_White_Space,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_XID_Start1,
+ POP_UNION,
+ POP_INVERT,
+ POP_INTER,
+ POP_END);
+ break;
+ case UNICODE_PROP_XID_Continue:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) |
+ M(Mn) | M(Mc) | M(Nd) | M(Pc),
+ POP_PROP, UNICODE_PROP_Other_ID_Start,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Other_ID_Continue,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Pattern_Syntax,
+ POP_PROP, UNICODE_PROP_Pattern_White_Space,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_XID_Continue1,
+ POP_UNION,
+ POP_INVERT,
+ POP_INTER,
+ POP_END);
+ break;
+ case UNICODE_PROP_Changes_When_Uppercased:
+ ret = unicode_case1(cr, CASE_U);
+ break;
+ case UNICODE_PROP_Changes_When_Lowercased:
+ ret = unicode_case1(cr, CASE_L);
+ break;
+ case UNICODE_PROP_Changes_When_Casemapped:
+ ret = unicode_case1(cr, CASE_U | CASE_L | CASE_F);
+ break;
+ case UNICODE_PROP_Changes_When_Titlecased:
+ ret = unicode_prop_ops(cr,
+ POP_CASE, CASE_U,
+ POP_PROP, UNICODE_PROP_Changes_When_Titlecased1,
+ POP_XOR,
+ POP_END);
+ break;
+ case UNICODE_PROP_Changes_When_Casefolded:
+ ret = unicode_prop_ops(cr,
+ POP_CASE, CASE_F,
+ POP_PROP, UNICODE_PROP_Changes_When_Casefolded1,
+ POP_XOR,
+ POP_END);
+ break;
+ case UNICODE_PROP_Changes_When_NFKC_Casefolded:
+ ret = unicode_prop_ops(cr,
+ POP_CASE, CASE_F,
+ POP_PROP, UNICODE_PROP_Changes_When_NFKC_Casefolded1,
+ POP_XOR,
+ POP_END);
+ break;
+#if 0
+ case UNICODE_PROP_ID_Start:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl),
+ POP_PROP, UNICODE_PROP_Other_ID_Start,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Pattern_Syntax,
+ POP_PROP, UNICODE_PROP_Pattern_White_Space,
+ POP_UNION,
+ POP_INVERT,
+ POP_INTER,
+ POP_END);
+ break;
+ case UNICODE_PROP_ID_Continue:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Lu) | M(Ll) | M(Lt) | M(Lm) | M(Lo) | M(Nl) |
+ M(Mn) | M(Mc) | M(Nd) | M(Pc),
+ POP_PROP, UNICODE_PROP_Other_ID_Start,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Other_ID_Continue,
+ POP_UNION,
+ POP_PROP, UNICODE_PROP_Pattern_Syntax,
+ POP_PROP, UNICODE_PROP_Pattern_White_Space,
+ POP_UNION,
+ POP_INVERT,
+ POP_INTER,
+ POP_END);
+ break;
+ case UNICODE_PROP_Case_Ignorable:
+ ret = unicode_prop_ops(cr,
+ POP_GC, M(Mn) | M(Cf) | M(Lm) | M(Sk),
+ POP_PROP, UNICODE_PROP_Case_Ignorable1,
+ POP_XOR,
+ POP_END);
+ break;
+#else
+ /* we use the existing tables */
+ case UNICODE_PROP_ID_Continue:
+ ret = unicode_prop_ops(cr,
+ POP_PROP, UNICODE_PROP_ID_Start,
+ POP_PROP, UNICODE_PROP_ID_Continue1,
+ POP_XOR,
+ POP_END);
+ break;
+#endif
+ default:
+ if (prop_idx >= countof(unicode_prop_table))
+ return -2;
+ ret = unicode_prop1(cr, prop_idx);
+ break;
+ }
+ return ret;
+}
+
+#endif /* CONFIG_ALL_UNICODE */
diff --git a/src/shared/quickjs/libunicode.h b/src/shared/quickjs/libunicode.h
new file mode 100644
index 000000000..cfa600a50
--- /dev/null
+++ b/src/shared/quickjs/libunicode.h
@@ -0,0 +1,124 @@
+/*
+ * Unicode utilities
+ *
+ * Copyright (c) 2017-2018 Fabrice Bellard
+ *
+ * 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 LIBUNICODE_H
+#define LIBUNICODE_H
+
+#include <inttypes.h>
+
+#define LRE_BOOL int /* for documentation purposes */
+
+/* define it to include all the unicode tables (40KB larger) */
+#define CONFIG_ALL_UNICODE
+
+#define LRE_CC_RES_LEN_MAX 3
+
+typedef enum {
+ UNICODE_NFC,
+ UNICODE_NFD,
+ UNICODE_NFKC,
+ UNICODE_NFKD,
+} UnicodeNormalizationEnum;
+
+int lre_case_conv(uint32_t *res, uint32_t c, int conv_type);
+LRE_BOOL lre_is_cased(uint32_t c);
+LRE_BOOL lre_is_case_ignorable(uint32_t c);
+
+/* char ranges */
+
+typedef struct {
+ int len; /* in points, always even */
+ int size;
+ uint32_t *points; /* points sorted by increasing value */
+ void *mem_opaque;
+ void *(*realloc_func)(void *opaque, void *ptr, size_t size);
+} CharRange;
+
+typedef enum {
+ CR_OP_UNION,
+ CR_OP_INTER,
+ CR_OP_XOR,
+} CharRangeOpEnum;
+
+void cr_init(CharRange *cr, void *mem_opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
+void cr_free(CharRange *cr);
+int cr_realloc(CharRange *cr, int size);
+int cr_copy(CharRange *cr, const CharRange *cr1);
+
+static inline int cr_add_point(CharRange *cr, uint32_t v)
+{
+ if (cr->len >= cr->size) {
+ if (cr_realloc(cr, cr->len + 1))
+ return -1;
+ }
+ cr->points[cr->len++] = v;
+ return 0;
+}
+
+static inline int cr_add_interval(CharRange *cr, uint32_t c1, uint32_t c2)
+{
+ if ((cr->len + 2) > cr->size) {
+ if (cr_realloc(cr, cr->len + 2))
+ return -1;
+ }
+ cr->points[cr->len++] = c1;
+ cr->points[cr->len++] = c2;
+ return 0;
+}
+
+int cr_union1(CharRange *cr, const uint32_t *b_pt, int b_len);
+
+static inline int cr_union_interval(CharRange *cr, uint32_t c1, uint32_t c2)
+{
+ uint32_t b_pt[2];
+ b_pt[0] = c1;
+ b_pt[1] = c2 + 1;
+ return cr_union1(cr, b_pt, 2);
+}
+
+int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len,
+ const uint32_t *b_pt, int b_len, int op);
+
+int cr_invert(CharRange *cr);
+
+#ifdef CONFIG_ALL_UNICODE
+
+LRE_BOOL lre_is_id_start(uint32_t c);
+LRE_BOOL lre_is_id_continue(uint32_t c);
+
+int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len,
+ UnicodeNormalizationEnum n_type,
+ void *opaque, void *(*realloc_func)(void *opaque, void *ptr, size_t size));
+
+/* Unicode character range functions */
+
+int unicode_script(CharRange *cr,
+ const char *script_name, LRE_BOOL is_ext);
+int unicode_general_category(CharRange *cr, const char *gc_name);
+int unicode_prop(CharRange *cr, const char *prop_name);
+
+#endif /* CONFIG_ALL_UNICODE */
+
+#undef LRE_BOOL
+
+#endif /* LIBUNICODE_H */
diff --git a/src/shared/quickjs/list.h b/src/shared/quickjs/list.h
new file mode 100644
index 000000000..e7f51a9d9
--- /dev/null
+++ b/src/shared/quickjs/list.h
@@ -0,0 +1,100 @@
+/*
+ * Linux klist like system
+ *
+ * Copyright (c) 2016-2017 Fabrice Bellard
+ *
+ * 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 LIST_H
+#define LIST_H
+
+#ifndef NULL
+#include <stddef.h>
+#endif
+
+struct list_head {
+ struct list_head *prev;
+ struct list_head *next;
+};
+
+#define LIST_HEAD_INIT(el) { &(el), &(el) }
+
+/* return the pointer of type 'type *' containing 'el' as field 'member' */
+#define list_entry(el, type, member) \
+ ((type *)((uint8_t *)(el) - offsetof(type, member)))
+
+static inline void init_list_head(struct list_head *head)
+{
+ head->prev = head;
+ head->next = head;
+}
+
+/* insert 'el' between 'prev' and 'next' */
+static inline void list_add_impl(struct list_head *el,
+ struct list_head *prev, struct list_head *next)
+{
+ prev->next = el;
+ el->prev = prev;
+ el->next = next;
+ next->prev = el;
+}
+
+/* add 'el' at the head of the list 'head' (= after element head) */
+static inline void list_add(struct list_head *el, struct list_head *head)
+{
+ list_add_impl(el, head, head->next);
+}
+
+/* add 'el' at the end of the list 'head' (= before element head) */
+static inline void list_add_tail(struct list_head *el, struct list_head *head)
+{
+ list_add_impl(el, head->prev, head);
+}
+
+static inline void list_del(struct list_head *el)
+{
+ struct list_head *prev, *next;
+ prev = el->prev;
+ next = el->next;
+ prev->next = next;
+ next->prev = prev;
+ el->prev = NULL; /* fail safe */
+ el->next = NULL; /* fail safe */
+}
+
+static inline int list_empty(struct list_head *el)
+{
+ return el->next == el;
+}
+
+#define list_for_each(el, head) \
+ for(el = (head)->next; el != (head); el = el->next)
+
+#define list_for_each_safe(el, el1, head) \
+ for(el = (head)->next, el1 = el->next; el != (head); \
+ el = el1, el1 = el->next)
+
+#define list_for_each_prev(el, head) \
+ for(el = (head)->prev; el != (head); el = el->prev)
+
+#define list_for_each_prev_safe(el, el1, head) \
+ for(el = (head)->prev, el1 = el->prev; el != (head); \
+ el = el1, el1 = el->prev)
+
+#endif /* LIST_H */
diff --git a/src/shared/quickjs/quickjs-atom.h b/src/shared/quickjs/quickjs-atom.h
new file mode 100644
index 000000000..4c2279452
--- /dev/null
+++ b/src/shared/quickjs/quickjs-atom.h
@@ -0,0 +1,273 @@
+/*
+ * QuickJS atom definitions
+ *
+ * Copyright (c) 2017-2018 Fabrice Bellard
+ * Copyright (c) 2017-2018 Charlie Gordon
+ *
+ * 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.
+ */
+
+#ifdef DEF
+
+/* Note: first atoms are considered as keywords in the parser */
+DEF(null, "null") /* must be first */
+DEF(false, "false")
+DEF(true, "true")
+DEF(if, "if")
+DEF(else, "else")
+DEF(return, "return")
+DEF(var, "var")
+DEF(this, "this")
+DEF(delete, "delete")
+DEF(void, "void")
+DEF(typeof, "typeof")
+DEF(new, "new")
+DEF(in, "in")
+DEF(instanceof, "instanceof")
+DEF(do, "do")
+DEF(while, "while")
+DEF(for, "for")
+DEF(break, "break")
+DEF(continue, "continue")
+DEF(switch, "switch")
+DEF(case, "case")
+DEF(default, "default")
+DEF(throw, "throw")
+DEF(try, "try")
+DEF(catch, "catch")
+DEF(finally, "finally")
+DEF(function, "function")
+DEF(debugger, "debugger")
+DEF(with, "with")
+/* FutureReservedWord */
+DEF(class, "class")
+DEF(const, "const")
+DEF(enum, "enum")
+DEF(export, "export")
+DEF(extends, "extends")
+DEF(import, "import")
+DEF(super, "super")
+/* FutureReservedWords when parsing strict mode code */
+DEF(implements, "implements")
+DEF(interface, "interface")
+DEF(let, "let")
+DEF(package, "package")
+DEF(private, "private")
+DEF(protected, "protected")
+DEF(public, "public")
+DEF(static, "static")
+DEF(yield, "yield")
+DEF(await, "await")
+
+/* empty string */
+DEF(empty_string, "")
+/* identifiers */
+DEF(length, "length")
+DEF(fileName, "fileName")
+DEF(lineNumber, "lineNumber")
+DEF(message, "message")
+DEF(errors, "errors")
+DEF(stack, "stack")
+DEF(name, "name")
+DEF(toString, "toString")
+DEF(toLocaleString, "toLocaleString")
+DEF(valueOf, "valueOf")
+DEF(eval, "eval")
+DEF(prototype, "prototype")
+DEF(constructor, "constructor")
+DEF(configurable, "configurable")
+DEF(writable, "writable")
+DEF(enumerable, "enumerable")
+DEF(value, "value")
+DEF(get, "get")
+DEF(set, "set")
+DEF(of, "of")
+DEF(__proto__, "__proto__")
+DEF(undefined, "undefined")
+DEF(number, "number")
+DEF(boolean, "boolean")
+DEF(string, "string")
+DEF(object, "object")
+DEF(symbol, "symbol")
+DEF(integer, "integer")
+DEF(unknown, "unknown")
+DEF(arguments, "arguments")
+DEF(callee, "callee")
+DEF(caller, "caller")
+DEF(_eval_, "<eval>")
+DEF(_ret_, "<ret>")
+DEF(_var_, "<var>")
+DEF(_arg_var_, "<arg_var>")
+DEF(_with_, "<with>")
+DEF(lastIndex, "lastIndex")
+DEF(target, "target")
+DEF(index, "index")
+DEF(input, "input")
+DEF(defineProperties, "defineProperties")
+DEF(apply, "apply")
+DEF(join, "join")
+DEF(concat, "concat")
+DEF(split, "split")
+DEF(construct, "construct")
+DEF(getPrototypeOf, "getPrototypeOf")
+DEF(setPrototypeOf, "setPrototypeOf")
+DEF(isExtensible, "isExtensible")
+DEF(preventExtensions, "preventExtensions")
+DEF(has, "has")
+DEF(deleteProperty, "deleteProperty")
+DEF(defineProperty, "defineProperty")
+DEF(getOwnPropertyDescriptor, "getOwnPropertyDescriptor")
+DEF(ownKeys, "ownKeys")
+DEF(add, "add")
+DEF(done, "done")
+DEF(next, "next")
+DEF(values, "values")
+DEF(source, "source")
+DEF(flags, "flags")
+DEF(global, "global")
+DEF(unicode, "unicode")
+DEF(raw, "raw")
+DEF(new_target, "new.target")
+DEF(this_active_func, "this.active_func")
+DEF(home_object, "<home_object>")
+DEF(computed_field, "<computed_field>")
+DEF(static_computed_field, "<static_computed_field>") /* must come after computed_fields */
+DEF(class_fields_init, "<class_fields_init>")
+DEF(brand, "<brand>")
+DEF(hash_constructor, "#constructor")
+DEF(as, "as")
+DEF(from, "from")
+DEF(meta, "meta")
+DEF(_default_, "*default*")
+DEF(_star_, "*")
+DEF(Module, "Module")
+DEF(then, "then")
+DEF(resolve, "resolve")
+DEF(reject, "reject")
+DEF(promise, "promise")
+DEF(proxy, "proxy")
+DEF(revoke, "revoke")
+DEF(async, "async")
+DEF(exec, "exec")
+DEF(groups, "groups")
+DEF(status, "status")
+DEF(reason, "reason")
+DEF(globalThis, "globalThis")
+#ifdef CONFIG_BIGNUM
+DEF(bigint, "bigint")
+DEF(bigfloat, "bigfloat")
+DEF(bigdecimal, "bigdecimal")
+DEF(roundingMode, "roundingMode")
+DEF(maximumSignificantDigits, "maximumSignificantDigits")
+DEF(maximumFractionDigits, "maximumFractionDigits")
+#endif
+#ifdef CONFIG_ATOMICS
+DEF(not_equal, "not-equal")
+DEF(timed_out, "timed-out")
+DEF(ok, "ok")
+#endif
+DEF(toJSON, "toJSON")
+/* class names */
+DEF(Object, "Object")
+DEF(Array, "Array")
+DEF(Error, "Error")
+DEF(Number, "Number")
+DEF(String, "String")
+DEF(Boolean, "Boolean")
+DEF(Symbol, "Symbol")
+DEF(Arguments, "Arguments")
+DEF(Math, "Math")
+DEF(JSON, "JSON")
+DEF(Date, "Date")
+DEF(Function, "Function")
+DEF(GeneratorFunction, "GeneratorFunction")
+DEF(ForInIterator, "ForInIterator")
+DEF(RegExp, "RegExp")
+DEF(ArrayBuffer, "ArrayBuffer")
+DEF(SharedArrayBuffer, "SharedArrayBuffer")
+/* must keep same order as class IDs for typed arrays */
+DEF(Uint8ClampedArray, "Uint8ClampedArray")
+DEF(Int8Array, "Int8Array")
+DEF(Uint8Array, "Uint8Array")
+DEF(Int16Array, "Int16Array")
+DEF(Uint16Array, "Uint16Array")
+DEF(Int32Array, "Int32Array")
+DEF(Uint32Array, "Uint32Array")
+#ifdef CONFIG_BIGNUM
+DEF(BigInt64Array, "BigInt64Array")
+DEF(BigUint64Array, "BigUint64Array")
+#endif
+DEF(Float32Array, "Float32Array")
+DEF(Float64Array, "Float64Array")
+DEF(DataView, "DataView")
+#ifdef CONFIG_BIGNUM
+DEF(BigInt, "BigInt")
+DEF(BigFloat, "BigFloat")
+DEF(BigFloatEnv, "BigFloatEnv")
+DEF(BigDecimal, "BigDecimal")
+DEF(OperatorSet, "OperatorSet")
+DEF(Operators, "Operators")
+#endif
+DEF(Map, "Map")
+DEF(Set, "Set") /* Map + 1 */
+DEF(WeakMap, "WeakMap") /* Map + 2 */
+DEF(WeakSet, "WeakSet") /* Map + 3 */
+DEF(Map_Iterator, "Map Iterator")
+DEF(Set_Iterator, "Set Iterator")
+DEF(Array_Iterator, "Array Iterator")
+DEF(String_Iterator, "String Iterator")
+DEF(RegExp_String_Iterator, "RegExp String Iterator")
+DEF(Generator, "Generator")
+DEF(Proxy, "Proxy")
+DEF(Promise, "Promise")
+DEF(PromiseResolveFunction, "PromiseResolveFunction")
+DEF(PromiseRejectFunction, "PromiseRejectFunction")
+DEF(AsyncFunction, "AsyncFunction")
+DEF(AsyncFunctionResolve, "AsyncFunctionResolve")
+DEF(AsyncFunctionReject, "AsyncFunctionReject")
+DEF(AsyncGeneratorFunction, "AsyncGeneratorFunction")
+DEF(AsyncGenerator, "AsyncGenerator")
+DEF(EvalError, "EvalError")
+DEF(RangeError, "RangeError")
+DEF(ReferenceError, "ReferenceError")
+DEF(SyntaxError, "SyntaxError")
+DEF(TypeError, "TypeError")
+DEF(URIError, "URIError")
+DEF(InternalError, "InternalError")
+/* private symbols */
+DEF(Private_brand, "<brand>")
+/* symbols */
+DEF(Symbol_toPrimitive, "Symbol.toPrimitive")
+DEF(Symbol_iterator, "Symbol.iterator")
+DEF(Symbol_match, "Symbol.match")
+DEF(Symbol_matchAll, "Symbol.matchAll")
+DEF(Symbol_replace, "Symbol.replace")
+DEF(Symbol_search, "Symbol.search")
+DEF(Symbol_split, "Symbol.split")
+DEF(Symbol_toStringTag, "Symbol.toStringTag")
+DEF(Symbol_isConcatSpreadable, "Symbol.isConcatSpreadable")
+DEF(Symbol_hasInstance, "Symbol.hasInstance")
+DEF(Symbol_species, "Symbol.species")
+DEF(Symbol_unscopables, "Symbol.unscopables")
+DEF(Symbol_asyncIterator, "Symbol.asyncIterator")
+#ifdef CONFIG_BIGNUM
+DEF(Symbol_operatorSet, "Symbol.operatorSet")
+#endif
+
+#endif /* DEF */
diff --git a/src/shared/quickjs/quickjs-opcode.h b/src/shared/quickjs/quickjs-opcode.h
new file mode 100644
index 000000000..c731a14a9
--- /dev/null
+++ b/src/shared/quickjs/quickjs-opcode.h
@@ -0,0 +1,365 @@
+/*
+ * QuickJS opcode definitions
+ *
+ * Copyright (c) 2017-2018 Fabrice Bellard
+ * Copyright (c) 2017-2018 Charlie Gordon
+ *
+ * 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.
+ */
+
+#ifdef FMT
+FMT(none)
+FMT(none_int)
+FMT(none_loc)
+FMT(none_arg)
+FMT(none_var_ref)
+FMT(u8)
+FMT(i8)
+FMT(loc8)
+FMT(const8)
+FMT(label8)
+FMT(u16)
+FMT(i16)
+FMT(label16)
+FMT(npop)
+FMT(npopx)
+FMT(npop_u16)
+FMT(loc)
+FMT(arg)
+FMT(var_ref)
+FMT(u32)
+FMT(i32)
+FMT(const)
+FMT(label)
+FMT(atom)
+FMT(atom_u8)
+FMT(atom_u16)
+FMT(atom_label_u8)
+FMT(atom_label_u16)
+FMT(label_u16)
+#undef FMT
+#endif /* FMT */
+
+#ifdef DEF
+
+#ifndef def
+#define def(id, size, n_pop, n_push, f) DEF(id, size, n_pop, n_push, f)
+#endif
+
+DEF(invalid, 1, 0, 0, none) /* never emitted */
+
+/* push values */
+DEF( push_i32, 5, 0, 1, i32)
+DEF( push_const, 5, 0, 1, const)
+DEF( fclosure, 5, 0, 1, const) /* must follow push_const */
+DEF(push_atom_value, 5, 0, 1, atom)
+DEF( private_symbol, 5, 0, 1, atom)
+DEF( undefined, 1, 0, 1, none)
+DEF( null, 1, 0, 1, none)
+DEF( push_this, 1, 0, 1, none) /* only used at the start of a function */
+DEF( push_false, 1, 0, 1, none)
+DEF( push_true, 1, 0, 1, none)
+DEF( object, 1, 0, 1, none)
+DEF( special_object, 2, 0, 1, u8) /* only used at the start of a function */
+DEF( rest, 3, 0, 1, u16) /* only used at the start of a function */
+
+DEF( drop, 1, 1, 0, none) /* a -> */
+DEF( nip, 1, 2, 1, none) /* a b -> b */
+DEF( nip1, 1, 3, 2, none) /* a b c -> b c */
+DEF( dup, 1, 1, 2, none) /* a -> a a */
+DEF( dup1, 1, 2, 3, none) /* a b -> a a b */
+DEF( dup2, 1, 2, 4, none) /* a b -> a b a b */
+DEF( dup3, 1, 3, 6, none) /* a b c -> a b c a b c */
+DEF( insert2, 1, 2, 3, none) /* obj a -> a obj a (dup_x1) */
+DEF( insert3, 1, 3, 4, none) /* obj prop a -> a obj prop a (dup_x2) */
+DEF( insert4, 1, 4, 5, none) /* this obj prop a -> a this obj prop a */
+DEF( perm3, 1, 3, 3, none) /* obj a b -> a obj b */
+DEF( perm4, 1, 4, 4, none) /* obj prop a b -> a obj prop b */
+DEF( perm5, 1, 5, 5, none) /* this obj prop a b -> a this obj prop b */
+DEF( swap, 1, 2, 2, none) /* a b -> b a */
+DEF( swap2, 1, 4, 4, none) /* a b c d -> c d a b */
+DEF( rot3l, 1, 3, 3, none) /* x a b -> a b x */
+DEF( rot3r, 1, 3, 3, none) /* a b x -> x a b */
+DEF( rot4l, 1, 4, 4, none) /* x a b c -> a b c x */
+DEF( rot5l, 1, 5, 5, none) /* x a b c d -> a b c d x */
+
+DEF(call_constructor, 3, 2, 1, npop) /* func new.target args -> ret. arguments are not counted in n_pop */
+DEF( call, 3, 1, 1, npop) /* arguments are not counted in n_pop */
+DEF( tail_call, 3, 1, 0, npop) /* arguments are not counted in n_pop */
+DEF( call_method, 3, 2, 1, npop) /* arguments are not counted in n_pop */
+DEF(tail_call_method, 3, 2, 0, npop) /* arguments are not counted in n_pop */
+DEF( array_from, 3, 0, 1, npop) /* arguments are not counted in n_pop */
+DEF( apply, 3, 3, 1, u16)
+DEF( return, 1, 1, 0, none)
+DEF( return_undef, 1, 0, 0, none)
+DEF(check_ctor_return, 1, 1, 2, none)
+DEF( check_ctor, 1, 0, 0, none)
+DEF( check_brand, 1, 2, 2, none) /* this_obj func -> this_obj func */
+DEF( add_brand, 1, 2, 0, none) /* this_obj home_obj -> */
+DEF( return_async, 1, 1, 0, none)
+DEF( throw, 1, 1, 0, none)
+DEF( throw_error, 6, 0, 0, atom_u8)
+DEF( eval, 5, 1, 1, npop_u16) /* func args... -> ret_val */
+DEF( apply_eval, 3, 2, 1, u16) /* func array -> ret_eval */
+DEF( regexp, 1, 2, 1, none) /* create a RegExp object from the pattern and a
+ bytecode string */
+DEF( get_super, 1, 1, 1, none)
+DEF( import, 1, 1, 1, none) /* dynamic module import */
+
+DEF( check_var, 5, 0, 1, atom) /* check if a variable exists */
+DEF( get_var_undef, 5, 0, 1, atom) /* push undefined if the variable does not exist */
+DEF( get_var, 5, 0, 1, atom) /* throw an exception if the variable does not exist */
+DEF( put_var, 5, 1, 0, atom) /* must come after get_var */
+DEF( put_var_init, 5, 1, 0, atom) /* must come after put_var. Used to initialize a global lexical variable */
+DEF( put_var_strict, 5, 2, 0, atom) /* for strict mode variable write */
+
+DEF( get_ref_value, 1, 2, 3, none)
+DEF( put_ref_value, 1, 3, 0, none)
+
+DEF( define_var, 6, 0, 0, atom_u8)
+DEF(check_define_var, 6, 0, 0, atom_u8)
+DEF( define_func, 6, 1, 0, atom_u8)
+DEF( get_field, 5, 1, 1, atom)
+DEF( get_field2, 5, 1, 2, atom)
+DEF( put_field, 5, 2, 0, atom)
+DEF( get_private_field, 1, 2, 1, none) /* obj prop -> value */
+DEF( put_private_field, 1, 3, 0, none) /* obj value prop -> */
+DEF(define_private_field, 1, 3, 1, none) /* obj prop value -> obj */
+DEF( get_array_el, 1, 2, 1, none)
+DEF( get_array_el2, 1, 2, 2, none) /* obj prop -> obj value */
+DEF( put_array_el, 1, 3, 0, none)
+DEF(get_super_value, 1, 3, 1, none) /* this obj prop -> value */
+DEF(put_super_value, 1, 4, 0, none) /* this obj prop value -> */
+DEF( define_field, 5, 2, 1, atom)
+DEF( set_name, 5, 1, 1, atom)
+DEF(set_name_computed, 1, 2, 2, none)
+DEF( set_proto, 1, 2, 1, none)
+DEF(set_home_object, 1, 2, 2, none)
+DEF(define_array_el, 1, 3, 2, none)
+DEF( append, 1, 3, 2, none) /* append enumerated object, update length */
+DEF(copy_data_properties, 2, 3, 3, u8)
+DEF( define_method, 6, 2, 1, atom_u8)
+DEF(define_method_computed, 2, 3, 1, u8) /* must come after define_method */
+DEF( define_class, 6, 2, 2, atom_u8) /* parent ctor -> ctor proto */
+DEF( define_class_computed, 6, 3, 3, atom_u8) /* field_name parent ctor -> field_name ctor proto (class with computed name) */
+
+DEF( get_loc, 3, 0, 1, loc)
+DEF( put_loc, 3, 1, 0, loc) /* must come after get_loc */
+DEF( set_loc, 3, 1, 1, loc) /* must come after put_loc */
+DEF( get_arg, 3, 0, 1, arg)
+DEF( put_arg, 3, 1, 0, arg) /* must come after get_arg */
+DEF( set_arg, 3, 1, 1, arg) /* must come after put_arg */
+DEF( get_var_ref, 3, 0, 1, var_ref)
+DEF( put_var_ref, 3, 1, 0, var_ref) /* must come after get_var_ref */
+DEF( set_var_ref, 3, 1, 1, var_ref) /* must come after put_var_ref */
+DEF(set_loc_uninitialized, 3, 0, 0, loc)
+DEF( get_loc_check, 3, 0, 1, loc)
+DEF( put_loc_check, 3, 1, 0, loc) /* must come after get_loc_check */
+DEF( put_loc_check_init, 3, 1, 0, loc)
+DEF(get_var_ref_check, 3, 0, 1, var_ref)
+DEF(put_var_ref_check, 3, 1, 0, var_ref) /* must come after get_var_ref_check */
+DEF(put_var_ref_check_init, 3, 1, 0, var_ref)
+DEF( close_loc, 3, 0, 0, loc)
+DEF( if_false, 5, 1, 0, label)
+DEF( if_true, 5, 1, 0, label) /* must come after if_false */
+DEF( goto, 5, 0, 0, label) /* must come after if_true */
+DEF( catch, 5, 0, 1, label)
+DEF( gosub, 5, 0, 0, label) /* used to execute the finally block */
+DEF( ret, 1, 1, 0, none) /* used to return from the finally block */
+
+DEF( to_object, 1, 1, 1, none)
+//DEF( to_string, 1, 1, 1, none)
+DEF( to_propkey, 1, 1, 1, none)
+DEF( to_propkey2, 1, 2, 2, none)
+
+DEF( with_get_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
+DEF( with_put_var, 10, 2, 1, atom_label_u8) /* must be in the same order as scope_xxx */
+DEF(with_delete_var, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
+DEF( with_make_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
+DEF( with_get_ref, 10, 1, 0, atom_label_u8) /* must be in the same order as scope_xxx */
+DEF(with_get_ref_undef, 10, 1, 0, atom_label_u8)
+
+DEF( make_loc_ref, 7, 0, 2, atom_u16)
+DEF( make_arg_ref, 7, 0, 2, atom_u16)
+DEF(make_var_ref_ref, 7, 0, 2, atom_u16)
+DEF( make_var_ref, 5, 0, 2, atom)
+
+DEF( for_in_start, 1, 1, 1, none)
+DEF( for_of_start, 1, 1, 3, none)
+DEF(for_await_of_start, 1, 1, 3, none)
+DEF( for_in_next, 1, 1, 3, none)
+DEF( for_of_next, 2, 3, 5, u8)
+DEF(iterator_check_object, 1, 1, 1, none)
+DEF(iterator_get_value_done, 1, 1, 2, none)
+DEF( iterator_close, 1, 3, 0, none)
+DEF(iterator_close_return, 1, 4, 4, none)
+DEF( iterator_next, 1, 4, 4, none)
+DEF( iterator_call, 2, 4, 5, u8)
+DEF( initial_yield, 1, 0, 0, none)
+DEF( yield, 1, 1, 2, none)
+DEF( yield_star, 1, 1, 2, none)
+DEF(async_yield_star, 1, 1, 2, none)
+DEF( await, 1, 1, 1, none)
+
+/* arithmetic/logic operations */
+DEF( neg, 1, 1, 1, none)
+DEF( plus, 1, 1, 1, none)
+DEF( dec, 1, 1, 1, none)
+DEF( inc, 1, 1, 1, none)
+DEF( post_dec, 1, 1, 2, none)
+DEF( post_inc, 1, 1, 2, none)
+DEF( dec_loc, 2, 0, 0, loc8)
+DEF( inc_loc, 2, 0, 0, loc8)
+DEF( add_loc, 2, 1, 0, loc8)
+DEF( not, 1, 1, 1, none)
+DEF( lnot, 1, 1, 1, none)
+DEF( typeof, 1, 1, 1, none)
+DEF( delete, 1, 2, 1, none)
+DEF( delete_var, 5, 0, 1, atom)
+
+DEF( mul, 1, 2, 1, none)
+DEF( div, 1, 2, 1, none)
+DEF( mod, 1, 2, 1, none)
+DEF( add, 1, 2, 1, none)
+DEF( sub, 1, 2, 1, none)
+DEF( pow, 1, 2, 1, none)
+DEF( shl, 1, 2, 1, none)
+DEF( sar, 1, 2, 1, none)
+DEF( shr, 1, 2, 1, none)
+DEF( lt, 1, 2, 1, none)
+DEF( lte, 1, 2, 1, none)
+DEF( gt, 1, 2, 1, none)
+DEF( gte, 1, 2, 1, none)
+DEF( instanceof, 1, 2, 1, none)
+DEF( in, 1, 2, 1, none)
+DEF( eq, 1, 2, 1, none)
+DEF( neq, 1, 2, 1, none)
+DEF( strict_eq, 1, 2, 1, none)
+DEF( strict_neq, 1, 2, 1, none)
+DEF( and, 1, 2, 1, none)
+DEF( xor, 1, 2, 1, none)
+DEF( or, 1, 2, 1, none)
+DEF(is_undefined_or_null, 1, 1, 1, none)
+#ifdef CONFIG_BIGNUM
+DEF( mul_pow10, 1, 2, 1, none)
+DEF( math_mod, 1, 2, 1, none)
+#endif
+/* must be the last non short and non temporary opcode */
+DEF( nop, 1, 0, 0, none)
+
+/* temporary opcodes: never emitted in the final bytecode */
+
+def( enter_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
+def( leave_scope, 3, 0, 0, u16) /* emitted in phase 1, removed in phase 2 */
+
+def( label, 5, 0, 0, label) /* emitted in phase 1, removed in phase 3 */
+
+def(scope_get_var_undef, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
+def( scope_get_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
+def( scope_put_var, 7, 1, 0, atom_u16) /* emitted in phase 1, removed in phase 2 */
+def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2 */
+def( scope_make_ref, 11, 0, 2, atom_label_u16) /* emitted in phase 1, removed in phase 2 */
+def( scope_get_ref, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
+def(scope_put_var_init, 7, 0, 2, atom_u16) /* emitted in phase 1, removed in phase 2 */
+def(scope_get_private_field, 7, 1, 1, atom_u16) /* obj -> value, emitted in phase 1, removed in phase 2 */
+def(scope_get_private_field2, 7, 1, 2, atom_u16) /* obj -> obj value, emitted in phase 1, removed in phase 2 */
+def(scope_put_private_field, 7, 1, 1, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */
+
+def( set_class_name, 5, 1, 1, u32) /* emitted in phase 1, removed in phase 2 */
+
+def( line_num, 5, 0, 0, u32) /* emitted in phase 1, removed in phase 3 */
+
+#if SHORT_OPCODES
+DEF( push_minus1, 1, 0, 1, none_int)
+DEF( push_0, 1, 0, 1, none_int)
+DEF( push_1, 1, 0, 1, none_int)
+DEF( push_2, 1, 0, 1, none_int)
+DEF( push_3, 1, 0, 1, none_int)
+DEF( push_4, 1, 0, 1, none_int)
+DEF( push_5, 1, 0, 1, none_int)
+DEF( push_6, 1, 0, 1, none_int)
+DEF( push_7, 1, 0, 1, none_int)
+DEF( push_i8, 2, 0, 1, i8)
+DEF( push_i16, 3, 0, 1, i16)
+DEF( push_const8, 2, 0, 1, const8)
+DEF( fclosure8, 2, 0, 1, const8) /* must follow push_const8 */
+DEF(push_empty_string, 1, 0, 1, none)
+
+DEF( get_loc8, 2, 0, 1, loc8)
+DEF( put_loc8, 2, 1, 0, loc8)
+DEF( set_loc8, 2, 1, 1, loc8)
+
+DEF( get_loc0, 1, 0, 1, none_loc)
+DEF( get_loc1, 1, 0, 1, none_loc)
+DEF( get_loc2, 1, 0, 1, none_loc)
+DEF( get_loc3, 1, 0, 1, none_loc)
+DEF( put_loc0, 1, 1, 0, none_loc)
+DEF( put_loc1, 1, 1, 0, none_loc)
+DEF( put_loc2, 1, 1, 0, none_loc)
+DEF( put_loc3, 1, 1, 0, none_loc)
+DEF( set_loc0, 1, 1, 1, none_loc)
+DEF( set_loc1, 1, 1, 1, none_loc)
+DEF( set_loc2, 1, 1, 1, none_loc)
+DEF( set_loc3, 1, 1, 1, none_loc)
+DEF( get_arg0, 1, 0, 1, none_arg)
+DEF( get_arg1, 1, 0, 1, none_arg)
+DEF( get_arg2, 1, 0, 1, none_arg)
+DEF( get_arg3, 1, 0, 1, none_arg)
+DEF( put_arg0, 1, 1, 0, none_arg)
+DEF( put_arg1, 1, 1, 0, none_arg)
+DEF( put_arg2, 1, 1, 0, none_arg)
+DEF( put_arg3, 1, 1, 0, none_arg)
+DEF( set_arg0, 1, 1, 1, none_arg)
+DEF( set_arg1, 1, 1, 1, none_arg)
+DEF( set_arg2, 1, 1, 1, none_arg)
+DEF( set_arg3, 1, 1, 1, none_arg)
+DEF( get_var_ref0, 1, 0, 1, none_var_ref)
+DEF( get_var_ref1, 1, 0, 1, none_var_ref)
+DEF( get_var_ref2, 1, 0, 1, none_var_ref)
+DEF( get_var_ref3, 1, 0, 1, none_var_ref)
+DEF( put_var_ref0, 1, 1, 0, none_var_ref)
+DEF( put_var_ref1, 1, 1, 0, none_var_ref)
+DEF( put_var_ref2, 1, 1, 0, none_var_ref)
+DEF( put_var_ref3, 1, 1, 0, none_var_ref)
+DEF( set_var_ref0, 1, 1, 1, none_var_ref)
+DEF( set_var_ref1, 1, 1, 1, none_var_ref)
+DEF( set_var_ref2, 1, 1, 1, none_var_ref)
+DEF( set_var_ref3, 1, 1, 1, none_var_ref)
+
+DEF( get_length, 1, 1, 1, none)
+
+DEF( if_false8, 2, 1, 0, label8)
+DEF( if_true8, 2, 1, 0, label8) /* must come after if_false8 */
+DEF( goto8, 2, 0, 0, label8) /* must come after if_true8 */
+DEF( goto16, 3, 0, 0, label16)
+
+DEF( call0, 1, 1, 1, npopx)
+DEF( call1, 1, 1, 1, npopx)
+DEF( call2, 1, 1, 1, npopx)
+DEF( call3, 1, 1, 1, npopx)
+
+DEF( is_undefined, 1, 1, 1, none)
+DEF( is_null, 1, 1, 1, none)
+DEF(typeof_is_undefined, 1, 1, 1, none)
+DEF( typeof_is_function, 1, 1, 1, none)
+#endif
+
+#undef DEF
+#undef def
+#endif /* DEF */
diff --git a/src/shared/quickjs/quickjs.c b/src/shared/quickjs/quickjs.c
new file mode 100644
index 000000000..1e3b97d51
--- /dev/null
+++ b/src/shared/quickjs/quickjs.c
@@ -0,0 +1,54367 @@
+/*
+ * QuickJS Javascript Engine
+ *
+ * Copyright (c) 2017-2021 Fabrice Bellard
+ * Copyright (c) 2017-2021 Charlie Gordon
+ *
+ * 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 <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+#ifndef _MSC_VER
+#include <sys/time.h>
+#endif
+#include <time.h>
+#include <fenv.h>
+#include <math.h>
+#if defined(__APPLE__)
+#include <malloc/malloc.h>
+#elif defined(__linux__)
+#include <malloc.h>
+#elif defined(__FreeBSD__)
+#include <malloc_np.h>
+#endif
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#include <windows.h>
+#endif
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
+ || defined(__APPLE__)
+#include <xlocale.h>
+#else
+#include <locale.h>
+#endif
+
+#include "cutils.h"
+#include "list.h"
+#include "quickjs.h"
+#include "libregexp.h"
+#ifdef CONFIG_BIGNUM
+#include "libbf.h"
+#endif
+
+#define OPTIMIZE 0
+#define SHORT_OPCODES 1
+#if defined(EMSCRIPTEN) || defined(_MSC_VER)
+#define DIRECT_DISPATCH 0
+#else
+#define DIRECT_DISPATCH 1
+#endif
+
+#if defined(__APPLE__)
+#define MALLOC_OVERHEAD 0
+#else
+#define MALLOC_OVERHEAD 8
+#endif
+
+#if !defined(_WIN32)
+/* define it if printf uses the RNDN rounding mode instead of RNDNA */
+#define CONFIG_PRINTF_RNDN
+#endif
+
+/* define to include Atomics.* operations which depend on the OS
+ threads */
+#if !defined(EMSCRIPTEN) && !defined(_MSC_VER)
+#define CONFIG_ATOMICS
+#endif
+
+#if !defined(EMSCRIPTEN)
+/* enable stack limitation */
+#define CONFIG_STACK_CHECK
+#endif
+
+/* dump object free */
+//#define DUMP_FREE
+//#define DUMP_CLOSURE
+/* dump the bytecode of the compiled functions: combination of bits
+ 1: dump pass 3 final byte code
+ 2: dump pass 2 code
+ 4: dump pass 1 code
+ 8: dump stdlib functions
+ 16: dump bytecode in hex
+ 32: dump line number table
+ */
+//#define DUMP_BYTECODE (1)
+/* dump the occurence of the automatic GC */
+//#define DUMP_GC
+/* dump objects freed by the garbage collector */
+//#define DUMP_GC_FREE
+/* dump objects leaking when freeing the runtime */
+//#define DUMP_LEAKS 1
+/* dump memory usage before running the garbage collector */
+//#define DUMP_MEM
+//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */
+//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */
+//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */
+//#define DUMP_MODULE_RESOLVE
+//#define DUMP_PROMISE
+//#define DUMP_READ_OBJECT
+
+/* test the GC by forcing it before each object allocation */
+//#define FORCE_GC_AT_MALLOC
+
+#ifdef CONFIG_ATOMICS
+#include <pthread.h>
+#include <stdatomic.h>
+#include <errno.h>
+#endif
+
+static double safe_strtod(const char *restrict nptr, char **restrict endptr)
+{
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
+ setlocale(LC_NUMERIC, "C");
+#else
+ const locale_t tempLoc = newlocale(LC_NUMERIC_MASK, "C", 0);
+ uselocale(tempLoc);
+#endif
+ double d = strtod(nptr, endptr);
+#if defined(_MSC_VER) || defined(__MINGW32__)
+ _configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
+#else
+ uselocale(LC_GLOBAL_LOCALE);
+ freelocale(tempLoc);
+#endif
+ return d;
+}
+
+enum {
+ /* classid tag */ /* union usage | properties */
+ JS_CLASS_OBJECT = 1, /* must be first */
+ JS_CLASS_ARRAY, /* u.array | length */
+ JS_CLASS_ERROR,
+ JS_CLASS_NUMBER, /* u.object_data */
+ JS_CLASS_STRING, /* u.object_data */
+ JS_CLASS_BOOLEAN, /* u.object_data */
+ JS_CLASS_SYMBOL, /* u.object_data */
+ JS_CLASS_ARGUMENTS, /* u.array | length */
+ JS_CLASS_MAPPED_ARGUMENTS, /* | length */
+ JS_CLASS_DATE, /* u.object_data */
+ JS_CLASS_MODULE_NS,
+ JS_CLASS_C_FUNCTION, /* u.cfunc */
+ JS_CLASS_BYTECODE_FUNCTION, /* u.func */
+ JS_CLASS_BOUND_FUNCTION, /* u.bound_function */
+ JS_CLASS_C_FUNCTION_DATA, /* u.c_function_data_record */
+ JS_CLASS_GENERATOR_FUNCTION, /* u.func */
+ JS_CLASS_FOR_IN_ITERATOR, /* u.for_in_iterator */
+ JS_CLASS_REGEXP, /* u.regexp */
+ JS_CLASS_ARRAY_BUFFER, /* u.array_buffer */
+ JS_CLASS_SHARED_ARRAY_BUFFER, /* u.array_buffer */
+ JS_CLASS_UINT8C_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_INT8_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_UINT8_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_INT16_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_UINT16_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_INT32_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_UINT32_ARRAY, /* u.array (typed_array) */
+#ifdef CONFIG_BIGNUM
+ JS_CLASS_BIG_INT64_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_BIG_UINT64_ARRAY, /* u.array (typed_array) */
+#endif
+ JS_CLASS_FLOAT32_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_FLOAT64_ARRAY, /* u.array (typed_array) */
+ JS_CLASS_DATAVIEW, /* u.typed_array */
+#ifdef CONFIG_BIGNUM
+ JS_CLASS_BIG_INT, /* u.object_data */
+ JS_CLASS_BIG_FLOAT, /* u.object_data */
+ JS_CLASS_FLOAT_ENV, /* u.float_env */
+ JS_CLASS_BIG_DECIMAL, /* u.object_data */
+ JS_CLASS_OPERATOR_SET, /* u.operator_set */
+#endif
+ JS_CLASS_MAP, /* u.map_state */
+ JS_CLASS_SET, /* u.map_state */
+ JS_CLASS_WEAKMAP, /* u.map_state */
+ JS_CLASS_WEAKSET, /* u.map_state */
+ JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */
+ JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */
+ JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */
+ JS_CLASS_STRING_ITERATOR, /* u.array_iterator_data */
+ JS_CLASS_REGEXP_STRING_ITERATOR, /* u.regexp_string_iterator_data */
+ JS_CLASS_GENERATOR, /* u.generator_data */
+ JS_CLASS_PROXY, /* u.proxy_data */
+ JS_CLASS_PROMISE, /* u.promise_data */
+ JS_CLASS_PROMISE_RESOLVE_FUNCTION, /* u.promise_function_data */
+ JS_CLASS_PROMISE_REJECT_FUNCTION, /* u.promise_function_data */
+ JS_CLASS_ASYNC_FUNCTION, /* u.func */
+ JS_CLASS_ASYNC_FUNCTION_RESOLVE, /* u.async_function_data */
+ JS_CLASS_ASYNC_FUNCTION_REJECT, /* u.async_function_data */
+ JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */
+ JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */
+ JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
+
+ JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
+};
+
+/* number of typed array types */
+#define JS_TYPED_ARRAY_COUNT (JS_CLASS_FLOAT64_ARRAY - JS_CLASS_UINT8C_ARRAY + 1)
+static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT];
+#define typed_array_size_log2(classid) (typed_array_size_log2[(classid)- JS_CLASS_UINT8C_ARRAY])
+
+typedef enum JSErrorEnum {
+ JS_EVAL_ERROR,
+ JS_RANGE_ERROR,
+ JS_REFERENCE_ERROR,
+ JS_SYNTAX_ERROR,
+ JS_TYPE_ERROR,
+ JS_URI_ERROR,
+ JS_INTERNAL_ERROR,
+ JS_AGGREGATE_ERROR,
+
+ JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */
+} JSErrorEnum;
+
+#define JS_MAX_LOCAL_VARS 65536
+#define JS_STACK_SIZE_MAX 65534
+#define JS_STRING_LEN_MAX ((1 << 30) - 1)
+
+#ifdef __GNUC__
+#define warn_unused __attribute__((warn_unused_result))
+#else
+#define warn_unused
+#endif
+
+typedef struct JSShape JSShape;
+typedef struct JSString JSString;
+typedef struct JSString JSAtomStruct;
+
+typedef enum {
+ JS_GC_PHASE_NONE,
+ JS_GC_PHASE_DECREF,
+ JS_GC_PHASE_REMOVE_CYCLES,
+} JSGCPhaseEnum;
+
+typedef enum OPCodeEnum OPCodeEnum;
+
+#ifdef CONFIG_BIGNUM
+/* function pointers are used for numeric operations so that it is
+ possible to remove some numeric types */
+typedef struct {
+ JSValue (*to_string)(JSContext *ctx, JSValueConst val);
+ JSValue (*from_string)(JSContext *ctx, const char *buf,
+ int radix, int flags, slimb_t *pexponent);
+ int (*unary_arith)(JSContext *ctx,
+ JSValue *pres, OPCodeEnum op, JSValue op1);
+ int (*binary_arith)(JSContext *ctx, OPCodeEnum op,
+ JSValue *pres, JSValue op1, JSValue op2);
+ int (*compare)(JSContext *ctx, OPCodeEnum op,
+ JSValue op1, JSValue op2);
+ /* only for bigfloat: */
+ JSValue (*mul_pow10_to_float64)(JSContext *ctx, const bf_t *a,
+ int64_t exponent);
+ int (*mul_pow10)(JSContext *ctx, JSValue *sp);
+} JSNumericOperations;
+#endif
+
+struct JSRuntime {
+ JSMallocFunctions mf;
+ JSMallocState malloc_state;
+ const char *rt_info;
+
+ int atom_hash_size; /* power of two */
+ int atom_count;
+ int atom_size;
+ int atom_count_resize; /* resize hash table at this count */
+ uint32_t *atom_hash;
+ JSAtomStruct **atom_array;
+ int atom_free_index; /* 0 = none */
+
+ int class_count; /* size of class_array */
+ JSClass *class_array;
+
+ struct list_head context_list; /* list of JSContext.link */
+ /* list of JSGCObjectHeader.link. List of allocated GC objects (used
+ by the garbage collector) */
+ struct list_head gc_obj_list;
+ /* list of JSGCObjectHeader.link. Used during JS_FreeValueRT() */
+ struct list_head gc_zero_ref_count_list;
+ struct list_head tmp_obj_list; /* used during GC */
+ JSGCPhaseEnum gc_phase : 8;
+ size_t malloc_gc_threshold;
+#ifdef DUMP_LEAKS
+ struct list_head string_list; /* list of JSString.link */
+#endif
+ /* stack limitation */
+ uintptr_t stack_size; /* in bytes, 0 if no limit */
+ uintptr_t stack_top;
+ uintptr_t stack_limit; /* lower stack limit */
+
+ JSValue current_exception;
+ /* true if inside an out of memory error, to avoid recursing */
+ BOOL in_out_of_memory : 8;
+
+ struct JSStackFrame *current_stack_frame;
+
+ JSInterruptHandler *interrupt_handler;
+ void *interrupt_opaque;
+
+ JSHostPromiseRejectionTracker *host_promise_rejection_tracker;
+ void *host_promise_rejection_tracker_opaque;
+
+ struct list_head job_list; /* list of JSJobEntry.link */
+
+ JSModuleNormalizeFunc *module_normalize_func;
+ JSModuleLoaderFunc *module_loader_func;
+ void *module_loader_opaque;
+
+ BOOL can_block : 8; /* TRUE if Atomics.wait can block */
+ /* used to allocate, free and clone SharedArrayBuffers */
+ JSSharedArrayBufferFunctions sab_funcs;
+
+ /* Shape hash table */
+ int shape_hash_bits;
+ int shape_hash_size;
+ int shape_hash_count; /* number of hashed shapes */
+ JSShape **shape_hash;
+#ifdef CONFIG_BIGNUM
+ bf_context_t bf_ctx;
+ JSNumericOperations bigint_ops;
+ JSNumericOperations bigfloat_ops;
+ JSNumericOperations bigdecimal_ops;
+ uint32_t operator_count;
+#endif
+ void *user_opaque;
+};
+
+struct JSClass {
+ uint32_t class_id; /* 0 means free entry */
+ JSAtom class_name;
+ JSClassFinalizer *finalizer;
+ JSClassGCMark *gc_mark;
+ JSClassCall *call;
+ /* pointers for exotic behavior, can be NULL if none are present */
+ const JSClassExoticMethods *exotic;
+};
+
+#define JS_MODE_STRICT (1 << 0)
+#define JS_MODE_STRIP (1 << 1)
+#define JS_MODE_MATH (1 << 2)
+
+typedef struct JSStackFrame {
+ struct JSStackFrame *prev_frame; /* NULL if first stack frame */
+ JSValue cur_func; /* current function, JS_UNDEFINED if the frame is detached */
+ JSValue *arg_buf; /* arguments */
+ JSValue *var_buf; /* variables */
+ struct list_head var_ref_list; /* list of JSVarRef.link */
+ const uint8_t *cur_pc; /* only used in bytecode functions : PC of the
+ instruction after the call */
+ int arg_count;
+ int js_mode; /* 0 or JS_MODE_MATH for C functions */
+ /* only used in generators. Current stack pointer value. NULL if
+ the function is running. */
+ JSValue *cur_sp;
+} JSStackFrame;
+
+typedef enum {
+ JS_GC_OBJ_TYPE_JS_OBJECT,
+ JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
+ JS_GC_OBJ_TYPE_SHAPE,
+ JS_GC_OBJ_TYPE_VAR_REF,
+ JS_GC_OBJ_TYPE_ASYNC_FUNCTION,
+ JS_GC_OBJ_TYPE_JS_CONTEXT,
+} JSGCObjectTypeEnum;
+
+/* header for GC objects. GC objects are C data structures with a
+ reference count that can reference other GC objects. JS Objects are
+ a particular type of GC object. */
+struct JSGCObjectHeader {
+ int ref_count; /* must come first, 32-bit */
+ JSGCObjectTypeEnum gc_obj_type : 4;
+ uint8_t mark : 4; /* used by the GC */
+ uint8_t dummy1; /* not used by the GC */
+ uint16_t dummy2; /* not used by the GC */
+ struct list_head link;
+};
+
+typedef struct JSVarRef {
+ union {
+ JSGCObjectHeader header; /* must come first */
+ struct {
+ int _gc_ref_count; /* corresponds to header.ref_count */
+ uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */
+
+ /* 0 : the JSVarRef is on the stack. header.link is an element
+ of JSStackFrame.var_ref_list.
+ 1 : the JSVarRef is detached. header.link has the normal meanning
+ */
+ uint8_t is_detached : 1;
+ uint8_t is_arg : 1;
+ uint16_t var_idx; /* index of the corresponding function variable on
+ the stack */
+ };
+ };
+ JSValue *pvalue; /* pointer to the value, either on the stack or
+ to 'value' */
+ JSValue value; /* used when the variable is no longer on the stack */
+} JSVarRef;
+
+#ifdef CONFIG_BIGNUM
+typedef struct JSFloatEnv {
+ limb_t prec;
+ bf_flags_t flags;
+ unsigned int status;
+} JSFloatEnv;
+
+/* the same structure is used for big integers and big floats. Big
+ integers are never infinite or NaNs */
+typedef struct JSBigFloat {
+ JSRefCountHeader header; /* must come first, 32-bit */
+ bf_t num;
+} JSBigFloat;
+
+typedef struct JSBigDecimal {
+ JSRefCountHeader header; /* must come first, 32-bit */
+ bfdec_t num;
+} JSBigDecimal;
+#endif
+
+typedef enum {
+ JS_AUTOINIT_ID_PROTOTYPE,
+ JS_AUTOINIT_ID_MODULE_NS,
+ JS_AUTOINIT_ID_PROP,
+} JSAutoInitIDEnum;
+
+/* must be large enough to have a negligible runtime cost and small
+ enough to call the interrupt callback often. */
+#define JS_INTERRUPT_COUNTER_INIT 10000
+
+struct JSContext {
+ JSGCObjectHeader header; /* must come first */
+ JSRuntime *rt;
+ struct list_head link;
+
+ uint16_t binary_object_count;
+ int binary_object_size;
+
+ JSShape *array_shape; /* initial shape for Array objects */
+
+ JSValue *class_proto;
+ JSValue function_proto;
+ JSValue function_ctor;
+ JSValue array_ctor;
+ JSValue regexp_ctor;
+ JSValue promise_ctor;
+ JSValue native_error_proto[JS_NATIVE_ERROR_COUNT];
+ JSValue iterator_proto;
+ JSValue async_iterator_proto;
+ JSValue array_proto_values;
+ JSValue throw_type_error;
+ JSValue eval_obj;
+
+ JSValue global_obj; /* global object */
+ JSValue global_var_obj; /* contains the global let/const definitions */
+
+ uint64_t random_state;
+#ifdef CONFIG_BIGNUM
+ bf_context_t *bf_ctx; /* points to rt->bf_ctx, shared by all contexts */
+ JSFloatEnv fp_env; /* global FP environment */
+ BOOL bignum_ext : 8; /* enable math mode */
+ BOOL allow_operator_overloading : 8;
+#endif
+ /* when the counter reaches zero, JSRutime.interrupt_handler is called */
+ int interrupt_counter;
+ BOOL is_error_property_enabled;
+
+ struct list_head loaded_modules; /* list of JSModuleDef.link */
+
+ /* if NULL, RegExp compilation is not supported */
+ JSValue (*compile_regexp)(JSContext *ctx, JSValueConst pattern,
+ JSValueConst flags);
+ /* if NULL, eval is not supported */
+ JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int flags, int scope_idx);
+ void *user_opaque;
+ ScopeLookup *scopeLookup;
+ FoundUndefinedHandler *handleUndefined;
+ FunctionEnteredHandler *handleFunctionEntered;
+ FunctionExitedHandler *handleFunctionExited;
+};
+
+typedef union JSFloat64Union {
+ double d;
+ uint64_t u64;
+ uint32_t u32[2];
+} JSFloat64Union;
+
+enum {
+ JS_ATOM_TYPE_STRING = 1,
+ JS_ATOM_TYPE_GLOBAL_SYMBOL,
+ JS_ATOM_TYPE_SYMBOL,
+ JS_ATOM_TYPE_PRIVATE,
+};
+
+enum {
+ JS_ATOM_HASH_SYMBOL,
+ JS_ATOM_HASH_PRIVATE,
+};
+
+typedef enum {
+ JS_ATOM_KIND_STRING,
+ JS_ATOM_KIND_SYMBOL,
+ JS_ATOM_KIND_PRIVATE,
+} JSAtomKindEnum;
+
+#define JS_ATOM_HASH_MASK ((1 << 30) - 1)
+
+struct JSString {
+ JSRefCountHeader header; /* must come first, 32-bit */
+ uint32_t len : 31;
+ uint8_t is_wide_char : 1; /* 0 = 8 bits, 1 = 16 bits characters */
+ /* for JS_ATOM_TYPE_SYMBOL: hash = 0, atom_type = 3,
+ for JS_ATOM_TYPE_PRIVATE: hash = 1, atom_type = 3
+ XXX: could change encoding to have one more bit in hash */
+ uint32_t hash : 30;
+ uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */
+ uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */
+#ifdef DUMP_LEAKS
+ struct list_head link; /* string list */
+#endif
+ union {
+ uint8_t str8[0]; /* 8 bit strings will get an extra null terminator */
+ uint16_t str16[0];
+ } u;
+};
+
+typedef struct JSClosureVar {
+ uint8_t is_local : 1;
+ uint8_t is_arg : 1;
+ uint8_t is_const : 1;
+ uint8_t is_lexical : 1;
+ uint8_t var_kind : 4; /* see JSVarKindEnum */
+ /* 8 bits available */
+ uint16_t var_idx; /* is_local = TRUE: index to a normal variable of the
+ parent function. otherwise: index to a closure
+ variable of the parent function */
+ JSAtom var_name;
+} JSClosureVar;
+
+#define ARG_SCOPE_INDEX 1
+#define ARG_SCOPE_END (-2)
+
+typedef struct JSVarScope {
+ int parent; /* index into fd->scopes of the enclosing scope */
+ int first; /* index into fd->vars of the last variable in this scope */
+} JSVarScope;
+
+typedef enum {
+ /* XXX: add more variable kinds here instead of using bit fields */
+ JS_VAR_NORMAL,
+ JS_VAR_FUNCTION_DECL, /* lexical var with function declaration */
+ JS_VAR_NEW_FUNCTION_DECL, /* lexical var with async/generator
+ function declaration */
+ JS_VAR_CATCH,
+ JS_VAR_FUNCTION_NAME, /* function expression name */
+ JS_VAR_PRIVATE_FIELD,
+ JS_VAR_PRIVATE_METHOD,
+ JS_VAR_PRIVATE_GETTER,
+ JS_VAR_PRIVATE_SETTER, /* must come after JS_VAR_PRIVATE_GETTER */
+ JS_VAR_PRIVATE_GETTER_SETTER, /* must come after JS_VAR_PRIVATE_SETTER */
+} JSVarKindEnum;
+
+/* XXX: could use a different structure in bytecode functions to save
+ memory */
+typedef struct JSVarDef {
+ JSAtom var_name;
+ /* index into fd->scopes of this variable lexical scope */
+ int scope_level;
+ /* during compilation:
+ - if scope_level = 0: scope in which the variable is defined
+ - if scope_level != 0: index into fd->vars of the next
+ variable in the same or enclosing lexical scope
+ in a bytecode function:
+ index into fd->vars of the next
+ variable in the same or enclosing lexical scope
+ */
+ int scope_next;
+ uint8_t is_const : 1;
+ uint8_t is_lexical : 1;
+ uint8_t is_captured : 1;
+ uint8_t var_kind : 4; /* see JSVarKindEnum */
+ /* only used during compilation: function pool index for lexical
+ variables with var_kind =
+ JS_VAR_FUNCTION_DECL/JS_VAR_NEW_FUNCTION_DECL or scope level of
+ the definition of the 'var' variables (they have scope_level =
+ 0) */
+ int func_pool_idx : 24; /* only used during compilation : index in
+ the constant pool for hoisted function
+ definition */
+} JSVarDef;
+
+/* for the encoding of the pc2line table */
+#define PC2LINE_BASE (-1)
+#define PC2LINE_RANGE 5
+#define PC2LINE_OP_FIRST 1
+#define PC2LINE_DIFF_PC_MAX ((255 - PC2LINE_OP_FIRST) / PC2LINE_RANGE)
+
+typedef enum JSFunctionKindEnum {
+ JS_FUNC_NORMAL = 0,
+ JS_FUNC_GENERATOR = (1 << 0),
+ JS_FUNC_ASYNC = (1 << 1),
+ JS_FUNC_ASYNC_GENERATOR = (JS_FUNC_GENERATOR | JS_FUNC_ASYNC),
+} JSFunctionKindEnum;
+
+typedef struct JSFunctionBytecode {
+ JSGCObjectHeader header; /* must come first */
+ uint8_t js_mode;
+ uint8_t has_prototype : 1; /* true if a prototype field is necessary */
+ uint8_t has_simple_parameter_list : 1;
+ uint8_t is_derived_class_constructor : 1;
+ /* true if home_object needs to be initialized */
+ uint8_t need_home_object : 1;
+ uint8_t func_kind : 2;
+ uint8_t new_target_allowed : 1;
+ uint8_t super_call_allowed : 1;
+ uint8_t super_allowed : 1;
+ uint8_t arguments_allowed : 1;
+ uint8_t has_debug : 1;
+ uint8_t backtrace_barrier : 1; /* stop backtrace on this function */
+ uint8_t read_only_bytecode : 1;
+ /* XXX: 4 bits available */
+ uint8_t *byte_code_buf; /* (self pointer) */
+ int byte_code_len;
+ JSAtom func_name;
+ JSVarDef *vardefs; /* arguments + local variables (arg_count + var_count) (self pointer) */
+ JSClosureVar *closure_var; /* list of variables in the closure (self pointer) */
+ uint16_t arg_count;
+ uint16_t var_count;
+ uint16_t defined_arg_count; /* for length function property */
+ uint16_t stack_size; /* maximum stack size */
+ JSContext *realm; /* function realm */
+ JSValue *cpool; /* constant pool (self pointer) */
+ int cpool_count;
+ int closure_var_count;
+ struct {
+ /* debug info, move to separate structure to save memory? */
+ JSAtom filename;
+ int line_num;
+ int source_len;
+ int pc2line_len;
+ uint8_t *pc2line_buf;
+ char *source;
+ } debug;
+} JSFunctionBytecode;
+
+typedef struct JSBoundFunction {
+ JSValue func_obj;
+ JSValue this_val;
+ int argc;
+ JSValue argv[0];
+} JSBoundFunction;
+
+typedef enum JSIteratorKindEnum {
+ JS_ITERATOR_KIND_KEY,
+ JS_ITERATOR_KIND_VALUE,
+ JS_ITERATOR_KIND_KEY_AND_VALUE,
+} JSIteratorKindEnum;
+
+typedef struct JSForInIterator {
+ JSValue obj;
+ BOOL is_array;
+ uint32_t array_length;
+ uint32_t idx;
+} JSForInIterator;
+
+typedef struct JSRegExp {
+ JSString *pattern;
+ JSString *bytecode; /* also contains the flags */
+} JSRegExp;
+
+typedef struct JSProxyData {
+ JSValue target;
+ JSValue handler;
+ uint8_t is_func;
+ uint8_t is_revoked;
+} JSProxyData;
+
+typedef struct JSArrayBuffer {
+ int byte_length; /* 0 if detached */
+ uint8_t detached;
+ uint8_t shared; /* if shared, the array buffer cannot be detached */
+ uint8_t *data; /* NULL if detached */
+ struct list_head array_list;
+ void *opaque;
+ JSFreeArrayBufferDataFunc *free_func;
+} JSArrayBuffer;
+
+typedef struct JSTypedArray {
+ struct list_head link; /* link to arraybuffer */
+ JSObject *obj; /* back pointer to the TypedArray/DataView object */
+ JSObject *buffer; /* based array buffer */
+ uint32_t offset; /* offset in the array buffer */
+ uint32_t length; /* length in the array buffer */
+} JSTypedArray;
+
+typedef struct JSAsyncFunctionState {
+ JSValue this_val; /* 'this' generator argument */
+ int argc; /* number of function arguments */
+ BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */
+ JSStackFrame frame;
+} JSAsyncFunctionState;
+
+/* XXX: could use an object instead to avoid the
+ JS_TAG_ASYNC_FUNCTION tag for the GC */
+typedef struct JSAsyncFunctionData {
+ JSGCObjectHeader header; /* must come first */
+ JSValue resolving_funcs[2];
+ BOOL is_active; /* true if the async function state is valid */
+ JSAsyncFunctionState func_state;
+} JSAsyncFunctionData;
+
+typedef enum {
+ /* binary operators */
+ JS_OVOP_ADD,
+ JS_OVOP_SUB,
+ JS_OVOP_MUL,
+ JS_OVOP_DIV,
+ JS_OVOP_MOD,
+ JS_OVOP_POW,
+ JS_OVOP_OR,
+ JS_OVOP_AND,
+ JS_OVOP_XOR,
+ JS_OVOP_SHL,
+ JS_OVOP_SAR,
+ JS_OVOP_SHR,
+ JS_OVOP_EQ,
+ JS_OVOP_LESS,
+
+ JS_OVOP_BINARY_COUNT,
+ /* unary operators */
+ JS_OVOP_POS = JS_OVOP_BINARY_COUNT,
+ JS_OVOP_NEG,
+ JS_OVOP_INC,
+ JS_OVOP_DEC,
+ JS_OVOP_NOT,
+
+ JS_OVOP_COUNT,
+} JSOverloadableOperatorEnum;
+
+typedef struct {
+ uint32_t operator_index;
+ JSObject *ops[JS_OVOP_BINARY_COUNT]; /* self operators */
+} JSBinaryOperatorDefEntry;
+
+typedef struct {
+ int count;
+ JSBinaryOperatorDefEntry *tab;
+} JSBinaryOperatorDef;
+
+typedef struct {
+ uint32_t operator_counter;
+ BOOL is_primitive; /* OperatorSet for a primitive type */
+ /* NULL if no operator is defined */
+ JSObject *self_ops[JS_OVOP_COUNT]; /* self operators */
+ JSBinaryOperatorDef left;
+ JSBinaryOperatorDef right;
+} JSOperatorSetData;
+
+typedef struct JSReqModuleEntry {
+ JSAtom module_name;
+ JSModuleDef *module; /* used using resolution */
+} JSReqModuleEntry;
+
+typedef enum JSExportTypeEnum {
+ JS_EXPORT_TYPE_LOCAL,
+ JS_EXPORT_TYPE_INDIRECT,
+} JSExportTypeEnum;
+
+typedef struct JSExportEntry {
+ union {
+ struct {
+ int var_idx; /* closure variable index */
+ JSVarRef *var_ref; /* if != NULL, reference to the variable */
+ } local; /* for local export */
+ int req_module_idx; /* module for indirect export */
+ } u;
+ JSExportTypeEnum export_type;
+ JSAtom local_name; /* '*' if export ns from. not used for local
+ export after compilation */
+ JSAtom export_name; /* exported variable name */
+} JSExportEntry;
+
+typedef struct JSStarExportEntry {
+ int req_module_idx; /* in req_module_entries */
+} JSStarExportEntry;
+
+typedef struct JSImportEntry {
+ int var_idx; /* closure variable index */
+ JSAtom import_name;
+ int req_module_idx; /* in req_module_entries */
+} JSImportEntry;
+
+struct JSModuleDef {
+ JSRefCountHeader header; /* must come first, 32-bit */
+ JSAtom module_name;
+ struct list_head link;
+
+ JSReqModuleEntry *req_module_entries;
+ int req_module_entries_count;
+ int req_module_entries_size;
+
+ JSExportEntry *export_entries;
+ int export_entries_count;
+ int export_entries_size;
+
+ JSStarExportEntry *star_export_entries;
+ int star_export_entries_count;
+ int star_export_entries_size;
+
+ JSImportEntry *import_entries;
+ int import_entries_count;
+ int import_entries_size;
+
+ JSValue module_ns;
+ JSValue func_obj; /* only used for JS modules */
+ JSModuleInitFunc *init_func; /* only used for C modules */
+ BOOL resolved : 8;
+ BOOL func_created : 8;
+ BOOL instantiated : 8;
+ BOOL evaluated : 8;
+ BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */
+ /* true if evaluation yielded an exception. It is saved in
+ eval_exception */
+ BOOL eval_has_exception : 8;
+ JSValue eval_exception;
+ JSValue meta_obj; /* for import.meta */
+};
+
+typedef struct JSJobEntry {
+ struct list_head link;
+ JSContext *ctx;
+ JSJobFunc *job_func;
+ int argc;
+ JSValue argv[0];
+} JSJobEntry;
+
+typedef struct JSProperty {
+ union {
+ JSValue value; /* JS_PROP_NORMAL */
+ struct { /* JS_PROP_GETSET */
+ JSObject *getter; /* NULL if undefined */
+ JSObject *setter; /* NULL if undefined */
+ } getset;
+ JSVarRef *var_ref; /* JS_PROP_VARREF */
+ struct { /* JS_PROP_AUTOINIT */
+ /* in order to use only 2 pointers, we compress the realm
+ and the init function pointer */
+ uintptr_t realm_and_id; /* realm and init_id (JS_AUTOINIT_ID_x)
+ in the 2 low bits */
+ void *opaque;
+ } init;
+ } u;
+} JSProperty;
+
+#define JS_PROP_INITIAL_SIZE 2
+#define JS_PROP_INITIAL_HASH_SIZE 4 /* must be a power of two */
+#define JS_ARRAY_INITIAL_SIZE 2
+
+typedef struct JSShapeProperty {
+ uint32_t hash_next : 26; /* 0 if last in list */
+ uint32_t flags : 6; /* JS_PROP_XXX */
+ JSAtom atom; /* JS_ATOM_NULL = free property entry */
+} JSShapeProperty;
+
+struct JSShape {
+ /* hash table of size hash_mask + 1 before the start of the
+ structure (see prop_hash_end()). */
+ JSGCObjectHeader header;
+ /* true if the shape is inserted in the shape hash table. If not,
+ JSShape.hash is not valid */
+ uint8_t is_hashed;
+ /* If true, the shape may have small array index properties 'n' with 0
+ <= n <= 2^31-1. If false, the shape is guaranteed not to have
+ small array index properties */
+ uint8_t has_small_array_index;
+ uint32_t hash; /* current hash value */
+ uint32_t prop_hash_mask;
+ int prop_size; /* allocated properties */
+ int prop_count; /* include deleted properties */
+ int deleted_prop_count;
+ JSShape *shape_hash_next; /* in JSRuntime.shape_hash[h] list */
+ JSObject *proto;
+ JSShapeProperty prop[0]; /* prop_size elements */
+};
+
+struct JSObject {
+ union {
+ JSGCObjectHeader header;
+ struct {
+ int _gc_ref_count; /* corresponds to header.ref_count */
+ uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */
+
+ uint8_t extensible : 1;
+ uint8_t free_mark : 1; /* only used when freeing objects with cycles */
+ uint8_t is_exotic : 1; /* TRUE if object has exotic property handlers */
+ uint8_t fast_array : 1; /* TRUE if u.array is used for get/put (for JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS and typed arrays) */
+ uint8_t is_constructor : 1; /* TRUE if object is a constructor function */
+ uint8_t is_uncatchable_error : 1; /* if TRUE, error is not catchable */
+ uint8_t tmp_mark : 1; /* used in JS_WriteObjectRec() */
+ uint8_t is_HTMLDDA : 1; /* specific annex B IsHtmlDDA behavior */
+ uint16_t class_id; /* see JS_CLASS_x */
+ };
+ };
+ /* byte offsets: 16/24 */
+ JSShape *shape; /* prototype and property names + flag */
+ JSProperty *prop; /* array of properties */
+ /* byte offsets: 24/40 */
+ struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */
+ /* byte offsets: 28/48 */
+ union {
+ void *opaque;
+ struct JSBoundFunction *bound_function; /* JS_CLASS_BOUND_FUNCTION */
+ struct JSCFunctionDataRecord *c_function_data_record; /* JS_CLASS_C_FUNCTION_DATA */
+ struct JSForInIterator *for_in_iterator; /* JS_CLASS_FOR_IN_ITERATOR */
+ struct JSArrayBuffer *array_buffer; /* JS_CLASS_ARRAY_BUFFER, JS_CLASS_SHARED_ARRAY_BUFFER */
+ struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_DATAVIEW */
+#ifdef CONFIG_BIGNUM
+ struct JSFloatEnv *float_env; /* JS_CLASS_FLOAT_ENV */
+ struct JSOperatorSetData *operator_set; /* JS_CLASS_OPERATOR_SET */
+#endif
+ struct JSMapState *map_state; /* JS_CLASS_MAP..JS_CLASS_WEAKSET */
+ struct JSMapIteratorData *map_iterator_data; /* JS_CLASS_MAP_ITERATOR, JS_CLASS_SET_ITERATOR */
+ struct JSArrayIteratorData *array_iterator_data; /* JS_CLASS_ARRAY_ITERATOR, JS_CLASS_STRING_ITERATOR */
+ struct JSRegExpStringIteratorData *regexp_string_iterator_data; /* JS_CLASS_REGEXP_STRING_ITERATOR */
+ struct JSGeneratorData *generator_data; /* JS_CLASS_GENERATOR */
+ struct JSProxyData *proxy_data; /* JS_CLASS_PROXY */
+ struct JSPromiseData *promise_data; /* JS_CLASS_PROMISE */
+ struct JSPromiseFunctionData *promise_function_data; /* JS_CLASS_PROMISE_RESOLVE_FUNCTION, JS_CLASS_PROMISE_REJECT_FUNCTION */
+ struct JSAsyncFunctionData *async_function_data; /* JS_CLASS_ASYNC_FUNCTION_RESOLVE, JS_CLASS_ASYNC_FUNCTION_REJECT */
+ struct JSAsyncFromSyncIteratorData *async_from_sync_iterator_data; /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
+ struct JSAsyncGeneratorData *async_generator_data; /* JS_CLASS_ASYNC_GENERATOR */
+ struct { /* JS_CLASS_BYTECODE_FUNCTION: 12/24 bytes */
+ /* also used by JS_CLASS_GENERATOR_FUNCTION, JS_CLASS_ASYNC_FUNCTION and JS_CLASS_ASYNC_GENERATOR_FUNCTION */
+ struct JSFunctionBytecode *function_bytecode;
+ JSVarRef **var_refs;
+ JSObject *home_object; /* for 'super' access */
+ } func;
+ struct { /* JS_CLASS_C_FUNCTION: 12/20 bytes */
+ JSContext *realm;
+ JSCFunctionType c_function;
+ uint8_t length;
+ uint8_t cproto;
+ int16_t magic;
+ } cfunc;
+ /* array part for fast arrays and typed arrays */
+ struct { /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS, JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
+ union {
+ uint32_t size; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
+ struct JSTypedArray *typed_array; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
+ } u1;
+ union {
+ JSValue *values; /* JS_CLASS_ARRAY, JS_CLASS_ARGUMENTS */
+ void *ptr; /* JS_CLASS_UINT8C_ARRAY..JS_CLASS_FLOAT64_ARRAY */
+ int8_t *int8_ptr; /* JS_CLASS_INT8_ARRAY */
+ uint8_t *uint8_ptr; /* JS_CLASS_UINT8_ARRAY, JS_CLASS_UINT8C_ARRAY */
+ int16_t *int16_ptr; /* JS_CLASS_INT16_ARRAY */
+ uint16_t *uint16_ptr; /* JS_CLASS_UINT16_ARRAY */
+ int32_t *int32_ptr; /* JS_CLASS_INT32_ARRAY */
+ uint32_t *uint32_ptr; /* JS_CLASS_UINT32_ARRAY */
+ int64_t *int64_ptr; /* JS_CLASS_INT64_ARRAY */
+ uint64_t *uint64_ptr; /* JS_CLASS_UINT64_ARRAY */
+ float *float_ptr; /* JS_CLASS_FLOAT32_ARRAY */
+ double *double_ptr; /* JS_CLASS_FLOAT64_ARRAY */
+ } u;
+ uint32_t count; /* <= 2^31-1. 0 for a detached typed array */
+ } array; /* 12/20 bytes */
+ JSRegExp regexp; /* JS_CLASS_REGEXP: 8/16 bytes */
+ JSValue object_data; /* for JS_SetObjectData(): 8/16/16 bytes */
+ } u;
+ /* byte sizes: 40/48/72 */
+};
+enum {
+ JS_ATOM_NULL_ = JS_ATOM_NULL,
+#define DEF(name, str) JS_ATOM_ ## name,
+#include "quickjs-atom.h"
+#undef DEF
+ JS_ATOM_END,
+};
+#define JS_ATOM_LAST_KEYWORD JS_ATOM_super
+#define JS_ATOM_LAST_STRICT_KEYWORD JS_ATOM_yield
+
+static const char js_atom_init[] =
+#define DEF(name, str) str "\0"
+#include "quickjs-atom.h"
+#undef DEF
+;
+
+typedef enum OPCodeFormat {
+#define FMT(f) OP_FMT_ ## f,
+#define DEF(id, size, n_pop, n_push, f)
+#include "quickjs-opcode.h"
+#undef DEF
+#undef FMT
+} OPCodeFormat;
+
+enum OPCodeEnum {
+#define FMT(f)
+#define DEF(id, size, n_pop, n_push, f) OP_ ## id,
+#define def(id, size, n_pop, n_push, f)
+#include "quickjs-opcode.h"
+#undef def
+#undef DEF
+#undef FMT
+ OP_COUNT, /* excluding temporary opcodes */
+ /* temporary opcodes : overlap with the short opcodes */
+ OP_TEMP_START = OP_nop + 1,
+ OP___dummy = OP_TEMP_START - 1,
+#define FMT(f)
+#define DEF(id, size, n_pop, n_push, f)
+#define def(id, size, n_pop, n_push, f) OP_ ## id,
+#include "quickjs-opcode.h"
+#undef def
+#undef DEF
+#undef FMT
+ OP_TEMP_END,
+};
+
+static int JS_InitAtoms(JSRuntime *rt);
+static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len,
+ int atom_type);
+static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);
+static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);
+static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags);
+static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags);
+static JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj, JSValueConst new_target,
+ int argc, JSValue *argv, int flags);
+static JSValue JS_CallConstructorInternal(JSContext *ctx,
+ JSValueConst func_obj,
+ JSValueConst new_target,
+ int argc, JSValue *argv, int flags);
+static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv);
+static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
+ int argc, JSValueConst *argv);
+static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
+ JSValue val, BOOL is_array_ctor);
+static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
+ JSValueConst val, int flags, int scope_idx);
+static maybe_unused void JS_DumpAtoms(JSRuntime *rt);
+static maybe_unused void JS_DumpString(JSRuntime *rt,
+ const JSString *p);
+static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
+static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
+static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
+static maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ JSValueConst val);
+static maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
+static maybe_unused void JS_PrintValue(JSContext *ctx,
+ const char *str,
+ JSValueConst val);
+static maybe_unused void JS_DumpShapes(JSRuntime *rt);
+static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic);
+static void js_array_finalizer(JSRuntime *rt, JSValue val);
+static void js_array_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_object_data_finalizer(JSRuntime *rt, JSValue val);
+static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_c_function_finalizer(JSRuntime *rt, JSValue val);
+static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val);
+static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_bound_function_finalizer(JSRuntime *rt, JSValue val);
+static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val);
+static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_regexp_finalizer(JSRuntime *rt, JSValue val);
+static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val);
+static void js_typed_array_finalizer(JSRuntime *rt, JSValue val);
+static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_proxy_finalizer(JSRuntime *rt, JSValue val);
+static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_map_finalizer(JSRuntime *rt, JSValue val);
+static void js_map_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val);
+static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val);
+static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val);
+static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_generator_finalizer(JSRuntime *rt, JSValue obj);
+static void js_generator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_promise_finalizer(JSRuntime *rt, JSValue val);
+static void js_promise_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
+static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+#ifdef CONFIG_BIGNUM
+static void js_operator_set_finalizer(JSRuntime *rt, JSValue val);
+static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+#endif
+static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
+static int JS_ToBoolFree(JSContext *ctx, JSValue val);
+static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
+static int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val);
+static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val);
+static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
+ JSValueConst flags);
+static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
+ JSValue pattern, JSValue bc);
+static void gc_decref(JSRuntime *rt);
+static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
+ const JSClassDef *class_def, JSAtom name);
+
+typedef enum JSStrictEqModeEnum {
+ JS_EQ_STRICT,
+ JS_EQ_SAME_VALUE,
+ JS_EQ_SAME_VALUE_ZERO,
+} JSStrictEqModeEnum;
+
+static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
+ JSStrictEqModeEnum eq_mode);
+static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2);
+static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2);
+static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2);
+static JSValue JS_ToObject(JSContext *ctx, JSValueConst val);
+static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val);
+static JSProperty *add_property(JSContext *ctx,
+ JSObject *p, JSAtom prop, int prop_flags);
+#ifdef CONFIG_BIGNUM
+static void js_float_env_finalizer(JSRuntime *rt, JSValue val);
+static JSValue JS_NewBigFloat(JSContext *ctx);
+static inline bf_t *JS_GetBigFloat(JSValueConst val)
+{
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ return &p->num;
+}
+static JSValue JS_NewBigDecimal(JSContext *ctx);
+static inline bfdec_t *JS_GetBigDecimal(JSValueConst val)
+{
+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);
+ return &p->num;
+}
+static JSValue JS_NewBigInt(JSContext *ctx);
+static inline bf_t *JS_GetBigInt(JSValueConst val)
+{
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ return &p->num;
+}
+static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
+ BOOL convert_to_safe_integer);
+static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val);
+static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val);
+static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val);
+static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf);
+static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val);
+static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
+ BOOL allow_null_or_undefined);
+static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
+#endif
+static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
+static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
+static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
+ JSValueConst proto_val, BOOL throw_flag);
+static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj);
+static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj);
+static int js_proxy_isArray(JSContext *ctx, JSValueConst obj);
+static int JS_CreateProperty(JSContext *ctx, JSObject *p,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter,
+ int flags);
+static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);
+static void reset_weak_ref(JSRuntime *rt, JSObject *p);
+static JSValue js_array_buffer_constructor3(JSContext *ctx,
+ JSValueConst new_target,
+ uint64_t len, JSClassID class_id,
+ uint8_t *buf,
+ JSFreeArrayBufferDataFunc *free_func,
+ void *opaque, BOOL alloc_flag);
+static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj);
+static JSValue js_typed_array_constructor(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int classid);
+static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p);
+static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p);
+static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx);
+static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, int var_idx,
+ BOOL is_arg);
+static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int flags);
+static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val);
+static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int flags, int scope_idx);
+static void js_free_module_def(JSContext *ctx, JSModuleDef *m);
+static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
+ JS_MarkFunc *mark_func);
+static JSValue js_import_meta(JSContext *ctx);
+static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier);
+static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
+static JSValue js_new_promise_capability(JSContext *ctx,
+ JSValue *resolving_funcs,
+ JSValueConst ctor);
+static warn_unused int perform_promise_then(JSContext *ctx,
+ JSValueConst promise,
+ JSValueConst *resolve_reject,
+ JSValueConst *cap_resolving_funcs);
+static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic);
+static int js_string_compare(JSContext *ctx,
+ const JSString *p1, const JSString *p2);
+static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val);
+static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSValue prop, JSValue val, int flags);
+static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val);
+static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val);
+static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val);
+static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSObject *p, JSAtom prop);
+static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc);
+static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
+ JS_MarkFunc *mark_func);
+static void JS_AddIntrinsicBasicObjects(JSContext *ctx);
+static void js_free_shape(JSRuntime *rt, JSShape *sh);
+static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
+static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
+ JSShapeProperty **pprs);
+static int init_shape_hash(JSRuntime *rt);
+static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres,
+ JSValueConst obj);
+static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres,
+ JSValueConst obj);
+static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);
+static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
+ JSValueConst array_arg);
+static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
+ JSValue **arrpp, uint32_t *countp);
+static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
+ JSValueConst sync_iter);
+static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val);
+static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int flags);
+static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val);
+static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
+ JSGCObjectTypeEnum type);
+static void remove_gc_object(JSGCObjectHeader *h);
+static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s);
+static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
+static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
+ void *opaque);
+static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
+ JSAtom atom, void *opaque);
+void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag);
+
+static const JSClassExoticMethods js_arguments_exotic_methods;
+static const JSClassExoticMethods js_string_exotic_methods;
+static const JSClassExoticMethods js_proxy_exotic_methods;
+static const JSClassExoticMethods js_module_ns_exotic_methods;
+static JSClassID js_class_id_alloc = JS_CLASS_INIT_COUNT;
+
+static void js_trigger_gc(JSRuntime *rt, size_t size)
+{
+ BOOL force_gc;
+#ifdef FORCE_GC_AT_MALLOC
+ force_gc = TRUE;
+#else
+ force_gc = ((rt->malloc_state.malloc_size + size) >
+ rt->malloc_gc_threshold);
+#endif
+ if (force_gc) {
+#ifdef DUMP_GC
+ printf("GC: size=%" PRIu64 "\n",
+ (uint64_t)rt->malloc_state.malloc_size);
+#endif
+ JS_RunGC(rt);
+ rt->malloc_gc_threshold = rt->malloc_state.malloc_size +
+ (rt->malloc_state.malloc_size >> 1);
+ }
+}
+
+static size_t js_malloc_usable_size_unknown(const void *ptr)
+{
+ return 0;
+}
+
+void *js_malloc_rt(JSRuntime *rt, size_t size)
+{
+ return rt->mf.js_malloc(&rt->malloc_state, size);
+}
+
+void js_free_rt(JSRuntime *rt, void *ptr)
+{
+ rt->mf.js_free(&rt->malloc_state, ptr);
+}
+
+void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size)
+{
+ return rt->mf.js_realloc(&rt->malloc_state, ptr, size);
+}
+
+size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr)
+{
+ return rt->mf.js_malloc_usable_size(ptr);
+}
+
+void *js_mallocz_rt(JSRuntime *rt, size_t size)
+{
+ void *ptr;
+ ptr = js_malloc_rt(rt, size);
+ if (!ptr)
+ return NULL;
+ return memset(ptr, 0, size);
+}
+
+#ifdef CONFIG_BIGNUM
+/* called by libbf */
+static void *js_bf_realloc(void *opaque, void *ptr, size_t size)
+{
+ JSRuntime *rt = opaque;
+ return js_realloc_rt(rt, ptr, size);
+}
+#endif /* CONFIG_BIGNUM */
+
+/* Throw out of memory in case of error */
+void *js_malloc(JSContext *ctx, size_t size)
+{
+ void *ptr;
+ ptr = js_malloc_rt(ctx->rt, size);
+ if (unlikely(!ptr)) {
+ JS_ThrowOutOfMemory(ctx);
+ return NULL;
+ }
+ return ptr;
+}
+
+/* Throw out of memory in case of error */
+void *js_mallocz(JSContext *ctx, size_t size)
+{
+ void *ptr;
+ ptr = js_mallocz_rt(ctx->rt, size);
+ if (unlikely(!ptr)) {
+ JS_ThrowOutOfMemory(ctx);
+ return NULL;
+ }
+ return ptr;
+}
+
+void js_free(JSContext *ctx, void *ptr)
+{
+ js_free_rt(ctx->rt, ptr);
+}
+
+/* Throw out of memory in case of error */
+void *js_realloc(JSContext *ctx, void *ptr, size_t size)
+{
+ void *ret;
+ ret = js_realloc_rt(ctx->rt, ptr, size);
+ if (unlikely(!ret && size != 0)) {
+ JS_ThrowOutOfMemory(ctx);
+ return NULL;
+ }
+ return ret;
+}
+
+/* store extra allocated size in *pslack if successful */
+void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack)
+{
+ void *ret;
+ ret = js_realloc_rt(ctx->rt, ptr, size);
+ if (unlikely(!ret && size != 0)) {
+ JS_ThrowOutOfMemory(ctx);
+ return NULL;
+ }
+ if (pslack) {
+ size_t new_size = js_malloc_usable_size_rt(ctx->rt, ret);
+ *pslack = (new_size > size) ? new_size - size : 0;
+ }
+ return ret;
+}
+
+size_t js_malloc_usable_size(JSContext *ctx, const void *ptr)
+{
+ return js_malloc_usable_size_rt(ctx->rt, ptr);
+}
+
+/* Throw out of memory exception in case of error */
+char *js_strndup(JSContext *ctx, const char *s, size_t n)
+{
+ char *ptr;
+ ptr = js_malloc(ctx, n + 1);
+ if (ptr) {
+ memcpy(ptr, s, n);
+ ptr[n] = '\0';
+ }
+ return ptr;
+}
+
+char *js_strdup(JSContext *ctx, const char *str)
+{
+ return js_strndup(ctx, str, strlen(str));
+}
+
+static no_inline int js_realloc_array(JSContext *ctx, void **parray,
+ int elem_size, int *psize, int req_size)
+{
+ int new_size;
+ size_t slack;
+ void *new_array;
+ /* XXX: potential arithmetic overflow */
+ new_size = max_int(req_size, *psize * 3 / 2);
+ new_array = js_realloc2(ctx, *parray, new_size * elem_size, &slack);
+ if (!new_array)
+ return -1;
+ new_size += slack / elem_size;
+ *psize = new_size;
+ *parray = new_array;
+ return 0;
+}
+
+/* resize the array and update its size if req_size > *psize */
+static inline int js_resize_array(JSContext *ctx, void **parray, int elem_size,
+ int *psize, int req_size)
+{
+ if (unlikely(req_size > *psize))
+ return js_realloc_array(ctx, parray, elem_size, psize, req_size);
+ else
+ return 0;
+}
+
+static inline void js_dbuf_init(JSContext *ctx, DynBuf *s)
+{
+ dbuf_init2(s, ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
+}
+
+static inline int is_digit(int c) {
+ return c >= '0' && c <= '9';
+}
+
+typedef struct JSClassShortDef {
+ JSAtom class_name;
+ JSClassFinalizer *finalizer;
+ JSClassGCMark *gc_mark;
+} JSClassShortDef;
+
+static JSClassShortDef const js_std_class_def[] = {
+ { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_OBJECT */
+ { JS_ATOM_Array, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARRAY */
+ { JS_ATOM_Error, NULL, NULL }, /* JS_CLASS_ERROR */
+ { JS_ATOM_Number, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_NUMBER */
+ { JS_ATOM_String, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_STRING */
+ { JS_ATOM_Boolean, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BOOLEAN */
+ { JS_ATOM_Symbol, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_SYMBOL */
+ { JS_ATOM_Arguments, js_array_finalizer, js_array_mark }, /* JS_CLASS_ARGUMENTS */
+ { JS_ATOM_Arguments, NULL, NULL }, /* JS_CLASS_MAPPED_ARGUMENTS */
+ { JS_ATOM_Date, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_DATE */
+ { JS_ATOM_Object, NULL, NULL }, /* JS_CLASS_MODULE_NS */
+ { JS_ATOM_Function, js_c_function_finalizer, js_c_function_mark }, /* JS_CLASS_C_FUNCTION */
+ { JS_ATOM_Function, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_BYTECODE_FUNCTION */
+ { JS_ATOM_Function, js_bound_function_finalizer, js_bound_function_mark }, /* JS_CLASS_BOUND_FUNCTION */
+ { JS_ATOM_Function, js_c_function_data_finalizer, js_c_function_data_mark }, /* JS_CLASS_C_FUNCTION_DATA */
+ { JS_ATOM_GeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_GENERATOR_FUNCTION */
+ { JS_ATOM_ForInIterator, js_for_in_iterator_finalizer, js_for_in_iterator_mark }, /* JS_CLASS_FOR_IN_ITERATOR */
+ { JS_ATOM_RegExp, js_regexp_finalizer, NULL }, /* JS_CLASS_REGEXP */
+ { JS_ATOM_ArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_ARRAY_BUFFER */
+ { JS_ATOM_SharedArrayBuffer, js_array_buffer_finalizer, NULL }, /* JS_CLASS_SHARED_ARRAY_BUFFER */
+ { JS_ATOM_Uint8ClampedArray, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8C_ARRAY */
+ { JS_ATOM_Int8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT8_ARRAY */
+ { JS_ATOM_Uint8Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT8_ARRAY */
+ { JS_ATOM_Int16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT16_ARRAY */
+ { JS_ATOM_Uint16Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT16_ARRAY */
+ { JS_ATOM_Int32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_INT32_ARRAY */
+ { JS_ATOM_Uint32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_UINT32_ARRAY */
+#ifdef CONFIG_BIGNUM
+ { JS_ATOM_BigInt64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_INT64_ARRAY */
+ { JS_ATOM_BigUint64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_BIG_UINT64_ARRAY */
+#endif
+ { JS_ATOM_Float32Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT32_ARRAY */
+ { JS_ATOM_Float64Array, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_FLOAT64_ARRAY */
+ { JS_ATOM_DataView, js_typed_array_finalizer, js_typed_array_mark }, /* JS_CLASS_DATAVIEW */
+#ifdef CONFIG_BIGNUM
+ { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */
+ { JS_ATOM_BigFloat, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_FLOAT */
+ { JS_ATOM_BigFloatEnv, js_float_env_finalizer, NULL }, /* JS_CLASS_FLOAT_ENV */
+ { JS_ATOM_BigDecimal, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_DECIMAL */
+ { JS_ATOM_OperatorSet, js_operator_set_finalizer, js_operator_set_mark }, /* JS_CLASS_OPERATOR_SET */
+#endif
+ { JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */
+ { JS_ATOM_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */
+ { JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */
+ { JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */
+ { JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */
+ { JS_ATOM_Set_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_SET_ITERATOR */
+ { JS_ATOM_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */
+ { JS_ATOM_String_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_STRING_ITERATOR */
+ { JS_ATOM_RegExp_String_Iterator, js_regexp_string_iterator_finalizer, js_regexp_string_iterator_mark }, /* JS_CLASS_REGEXP_STRING_ITERATOR */
+ { JS_ATOM_Generator, js_generator_finalizer, js_generator_mark }, /* JS_CLASS_GENERATOR */
+};
+
+static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab,
+ int start, int count)
+{
+ JSClassDef cm_s, *cm = &cm_s;
+ int i, class_id;
+
+ for(i = 0; i < count; i++) {
+ class_id = i + start;
+ memset(cm, 0, sizeof(*cm));
+ cm->finalizer = tab[i].finalizer;
+ cm->gc_mark = tab[i].gc_mark;
+ if (JS_NewClass1(rt, class_id, cm, tab[i].class_name) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_BIGNUM
+static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "unsupported operation");
+}
+
+static JSValue invalid_to_string(JSContext *ctx, JSValueConst val)
+{
+ return JS_ThrowUnsupportedOperation(ctx);
+}
+
+static JSValue invalid_from_string(JSContext *ctx, const char *buf,
+ int radix, int flags, slimb_t *pexponent)
+{
+ return JS_NAN;
+}
+
+static int invalid_unary_arith(JSContext *ctx,
+ JSValue *pres, OPCodeEnum op, JSValue op1)
+{
+ JS_FreeValue(ctx, op1);
+ JS_ThrowUnsupportedOperation(ctx);
+ return -1;
+}
+
+static int invalid_binary_arith(JSContext *ctx, OPCodeEnum op,
+ JSValue *pres, JSValue op1, JSValue op2)
+{
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ JS_ThrowUnsupportedOperation(ctx);
+ return -1;
+}
+
+static JSValue invalid_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
+ int64_t exponent)
+{
+ return JS_ThrowUnsupportedOperation(ctx);
+}
+
+static int invalid_mul_pow10(JSContext *ctx, JSValue *sp)
+{
+ JS_ThrowUnsupportedOperation(ctx);
+ return -1;
+}
+
+static void set_dummy_numeric_ops(JSNumericOperations *ops)
+{
+ ops->to_string = invalid_to_string;
+ ops->from_string = invalid_from_string;
+ ops->unary_arith = invalid_unary_arith;
+ ops->binary_arith = invalid_binary_arith;
+ ops->mul_pow10_to_float64 = invalid_mul_pow10_to_float64;
+ ops->mul_pow10 = invalid_mul_pow10;
+}
+
+#endif /* CONFIG_BIGNUM */
+
+#if !defined(CONFIG_STACK_CHECK)
+/* no stack limitation */
+static inline uintptr_t js_get_stack_pointer(void)
+{
+ return 0;
+}
+
+static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
+{
+ return FALSE;
+}
+#else
+// Uses code from LLVM project.
+static inline uintptr_t js_get_stack_pointer(void)
+{
+#ifdef _MSC_VER
+ return (uintptr_t) _AddressOfReturnAddress();
+#elif defined __has_builtin
+#if __has_builtin(__builtin_frame_address)
+ return (uintptr_t) __builtin_frame_address(0);
+#endif
+#elif defined __GNUC__
+ return (uintptr_t) __builtin_frame_address(0);
+#else
+ char CharOnStack = 0;
+ // The volatile store here is intended to escape the local variable, to
+ // prevent the compiler from optimizing CharOnStack into anything other
+ // than a char on the stack.
+ //
+ // Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19.
+ char *volatile Ptr = &CharOnStack;
+ return (uintptr_t) Ptr;
+#endif
+}
+
+static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
+{
+ uintptr_t sp;
+ sp = js_get_stack_pointer() - alloca_size;
+ return unlikely(sp < rt->stack_limit);
+}
+#endif
+
+JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
+{
+ JSRuntime *rt;
+ JSMallocState ms;
+
+ memset(&ms, 0, sizeof(ms));
+ ms.opaque = opaque;
+ ms.malloc_limit = -1;
+
+ rt = mf->js_malloc(&ms, sizeof(JSRuntime));
+ if (!rt)
+ return NULL;
+ memset(rt, 0, sizeof(*rt));
+ rt->mf = *mf;
+ if (!rt->mf.js_malloc_usable_size) {
+ /* use dummy function if none provided */
+ rt->mf.js_malloc_usable_size = js_malloc_usable_size_unknown;
+ }
+ rt->malloc_state = ms;
+ rt->malloc_gc_threshold = 256 * 1024;
+
+#ifdef CONFIG_BIGNUM
+ bf_context_init(&rt->bf_ctx, js_bf_realloc, rt);
+ set_dummy_numeric_ops(&rt->bigint_ops);
+ set_dummy_numeric_ops(&rt->bigfloat_ops);
+ set_dummy_numeric_ops(&rt->bigdecimal_ops);
+#endif
+
+ init_list_head(&rt->context_list);
+ init_list_head(&rt->gc_obj_list);
+ init_list_head(&rt->gc_zero_ref_count_list);
+ rt->gc_phase = JS_GC_PHASE_NONE;
+
+#ifdef DUMP_LEAKS
+ init_list_head(&rt->string_list);
+#endif
+ init_list_head(&rt->job_list);
+
+ if (JS_InitAtoms(rt))
+ goto fail;
+
+ /* create the object, array and function classes */
+ if (init_class_range(rt, js_std_class_def, JS_CLASS_OBJECT,
+ countof(js_std_class_def)) < 0)
+ goto fail;
+ rt->class_array[JS_CLASS_ARGUMENTS].exotic = &js_arguments_exotic_methods;
+ rt->class_array[JS_CLASS_STRING].exotic = &js_string_exotic_methods;
+ rt->class_array[JS_CLASS_MODULE_NS].exotic = &js_module_ns_exotic_methods;
+
+ rt->class_array[JS_CLASS_C_FUNCTION].call = js_call_c_function;
+ rt->class_array[JS_CLASS_C_FUNCTION_DATA].call = js_c_function_data_call;
+ rt->class_array[JS_CLASS_BOUND_FUNCTION].call = js_call_bound_function;
+ rt->class_array[JS_CLASS_GENERATOR_FUNCTION].call = js_generator_function_call;
+ if (init_shape_hash(rt))
+ goto fail;
+
+ rt->stack_size = JS_DEFAULT_STACK_SIZE;
+ JS_UpdateStackTop(rt);
+
+ rt->current_exception = JS_NULL;
+
+ return rt;
+ fail:
+ JS_FreeRuntime(rt);
+ return NULL;
+}
+
+void *JS_GetRuntimeOpaque(JSRuntime *rt)
+{
+ return rt->user_opaque;
+}
+
+void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque)
+{
+ rt->user_opaque = opaque;
+}
+
+/* default memory allocation functions with memory limitation */
+static inline size_t js_def_malloc_usable_size(void *ptr)
+{
+#if defined(__APPLE__)
+ return malloc_size(ptr);
+#elif defined(_WIN32)
+ return _msize(ptr);
+#elif defined(EMSCRIPTEN)
+ return 0;
+#elif defined(__linux__)
+ return malloc_usable_size(ptr);
+#else
+ /* change this to `return 0;` if compilation fails */
+ return malloc_usable_size(ptr);
+#endif
+}
+
+static void *js_def_malloc(JSMallocState *s, size_t size)
+{
+ void *ptr;
+
+ /* Do not allocate zero bytes: behavior is platform dependent */
+ assert(size != 0);
+
+ if (unlikely(s->malloc_size + size > s->malloc_limit))
+ return NULL;
+
+ ptr = malloc(size);
+ if (!ptr)
+ return NULL;
+
+ s->malloc_count++;
+ s->malloc_size += js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
+ return ptr;
+}
+
+static void js_def_free(JSMallocState *s, void *ptr)
+{
+ if (!ptr)
+ return;
+
+ s->malloc_count--;
+ s->malloc_size -= js_def_malloc_usable_size(ptr) + MALLOC_OVERHEAD;
+ free(ptr);
+}
+
+static void *js_def_realloc(JSMallocState *s, void *ptr, size_t size)
+{
+ size_t old_size;
+
+ if (!ptr) {
+ if (size == 0)
+ return NULL;
+ return js_def_malloc(s, size);
+ }
+ old_size = js_def_malloc_usable_size(ptr);
+ if (size == 0) {
+ s->malloc_count--;
+ s->malloc_size -= old_size + MALLOC_OVERHEAD;
+ free(ptr);
+ return NULL;
+ }
+ if (s->malloc_size + size - old_size > s->malloc_limit)
+ return NULL;
+
+ ptr = realloc(ptr, size);
+ if (!ptr)
+ return NULL;
+
+ s->malloc_size += js_def_malloc_usable_size(ptr) - old_size;
+ return ptr;
+}
+
+static const JSMallocFunctions def_malloc_funcs = {
+ js_def_malloc,
+ js_def_free,
+ js_def_realloc,
+#if defined(__APPLE__)
+ malloc_size,
+#elif defined(_WIN32)
+ (size_t (*)(const void *))_msize,
+#elif defined(EMSCRIPTEN)
+ NULL,
+#elif defined(__linux__)
+ (size_t (*)(const void *))malloc_usable_size,
+#else
+ /* change this to `NULL,` if compilation fails */
+ malloc_usable_size,
+#endif
+};
+
+JSRuntime *JS_NewRuntime(void)
+{
+ return JS_NewRuntime2(&def_malloc_funcs, NULL);
+}
+
+void JS_SetMemoryLimit(JSRuntime *rt, size_t limit)
+{
+ rt->malloc_state.malloc_limit = limit;
+}
+
+/* use -1 to disable automatic GC */
+void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold)
+{
+ rt->malloc_gc_threshold = gc_threshold;
+}
+
+#define malloc(s) malloc_is_forbidden(s)
+#define free(p) free_is_forbidden(p)
+#define realloc(p,s) realloc_is_forbidden(p,s)
+
+void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque)
+{
+ rt->interrupt_handler = cb;
+ rt->interrupt_opaque = opaque;
+}
+
+void JS_SetCanBlock(JSRuntime *rt, BOOL can_block)
+{
+ rt->can_block = can_block;
+}
+
+void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
+ const JSSharedArrayBufferFunctions *sf)
+{
+ rt->sab_funcs = *sf;
+}
+
+/* return 0 if OK, < 0 if exception */
+int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
+ int argc, JSValueConst *argv)
+{
+ JSRuntime *rt = ctx->rt;
+ JSJobEntry *e;
+ int i;
+
+ e = js_malloc(ctx, sizeof(*e) + argc * sizeof(JSValue));
+ if (!e)
+ return -1;
+ e->ctx = ctx;
+ e->job_func = job_func;
+ e->argc = argc;
+ for(i = 0; i < argc; i++) {
+ e->argv[i] = JS_DupValue(ctx, argv[i]);
+ }
+ list_add_tail(&e->link, &rt->job_list);
+ return 0;
+}
+
+BOOL JS_IsJobPending(JSRuntime *rt)
+{
+ return !list_empty(&rt->job_list);
+}
+
+/* return < 0 if exception, 0 if no job pending, 1 if a job was
+ executed successfully. the context of the job is stored in '*pctx' */
+int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx)
+{
+ JSContext *ctx;
+ JSJobEntry *e;
+ JSValue res;
+ int i, ret;
+
+ if (list_empty(&rt->job_list)) {
+ *pctx = NULL;
+ return 0;
+ }
+
+ /* get the first pending job and execute it */
+ e = list_entry(rt->job_list.next, JSJobEntry, link);
+ list_del(&e->link);
+ ctx = e->ctx;
+ res = e->job_func(e->ctx, e->argc, (JSValueConst *)e->argv);
+ for(i = 0; i < e->argc; i++)
+ JS_FreeValue(ctx, e->argv[i]);
+ if (JS_IsException(res))
+ ret = -1;
+ else
+ ret = 1;
+ JS_FreeValue(ctx, res);
+ js_free(ctx, e);
+ *pctx = ctx;
+ return ret;
+}
+
+static inline uint32_t atom_get_free(const JSAtomStruct *p)
+{
+ return (uintptr_t)p >> 1;
+}
+
+static inline BOOL atom_is_free(const JSAtomStruct *p)
+{
+ return (uintptr_t)p & 1;
+}
+
+static inline JSAtomStruct *atom_set_free(uint32_t v)
+{
+ return (JSAtomStruct *)(((uintptr_t)v << 1) | 1);
+}
+
+/* Note: the string contents are uninitialized */
+static JSString *js_alloc_string_rt(JSRuntime *rt, int max_len, int is_wide_char)
+{
+ JSString *str;
+ str = js_malloc_rt(rt, sizeof(JSString) + (max_len << is_wide_char) + 1 - is_wide_char);
+ if (unlikely(!str))
+ return NULL;
+ str->header.ref_count = 1;
+ str->is_wide_char = is_wide_char;
+ str->len = max_len;
+ str->atom_type = 0;
+ str->hash = 0; /* optional but costless */
+ str->hash_next = 0; /* optional */
+#ifdef DUMP_LEAKS
+ list_add_tail(&str->link, &rt->string_list);
+#endif
+ return str;
+}
+
+static JSString *js_alloc_string(JSContext *ctx, int max_len, int is_wide_char)
+{
+ JSString *p;
+ p = js_alloc_string_rt(ctx->rt, max_len, is_wide_char);
+ if (unlikely(!p)) {
+ JS_ThrowOutOfMemory(ctx);
+ return NULL;
+ }
+ return p;
+}
+
+/* same as JS_FreeValueRT() but faster */
+static inline void js_free_string(JSRuntime *rt, JSString *str)
+{
+ if (--str->header.ref_count <= 0) {
+ if (str->atom_type) {
+ JS_FreeAtomStruct(rt, str);
+ } else {
+#ifdef DUMP_LEAKS
+ list_del(&str->link);
+#endif
+ js_free_rt(rt, str);
+ }
+ }
+}
+
+void JS_SetRuntimeInfo(JSRuntime *rt, const char *s)
+{
+ if (rt)
+ rt->rt_info = s;
+}
+
+void JS_FreeRuntime(JSRuntime *rt)
+{
+ struct list_head *el, *el1;
+ int i;
+
+ JS_FreeValueRT(rt, rt->current_exception);
+
+ list_for_each_safe(el, el1, &rt->job_list) {
+ JSJobEntry *e = list_entry(el, JSJobEntry, link);
+ for(i = 0; i < e->argc; i++)
+ JS_FreeValueRT(rt, e->argv[i]);
+ js_free_rt(rt, e);
+ }
+ init_list_head(&rt->job_list);
+
+ JS_RunGC(rt);
+
+#ifdef DUMP_LEAKS
+ /* leaking objects */
+ {
+ BOOL header_done;
+ JSGCObjectHeader *p;
+ int count;
+
+ /* remove the internal refcounts to display only the object
+ referenced externally */
+ list_for_each(el, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ p->mark = 0;
+ }
+ gc_decref(rt);
+
+ header_done = FALSE;
+ list_for_each(el, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ if (p->ref_count != 0) {
+ if (!header_done) {
+ printf("Object leaks:\n");
+ JS_DumpObjectHeader(rt);
+ header_done = TRUE;
+ }
+ JS_DumpGCObject(rt, p);
+ }
+ }
+
+ count = 0;
+ list_for_each(el, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ if (p->ref_count == 0) {
+ count++;
+ }
+ }
+ if (count != 0)
+ printf("Secondary object leaks: %d\n", count);
+ }
+#endif
+ fflush(stdout);
+ assert(list_empty(&rt->gc_obj_list));
+
+ /* free the classes */
+ for(i = 0; i < rt->class_count; i++) {
+ JSClass *cl = &rt->class_array[i];
+ if (cl->class_id != 0) {
+ JS_FreeAtomRT(rt, cl->class_name);
+ }
+ }
+ js_free_rt(rt, rt->class_array);
+
+#ifdef CONFIG_BIGNUM
+ bf_context_end(&rt->bf_ctx);
+#endif
+
+#ifdef DUMP_LEAKS
+ /* only the atoms defined in JS_InitAtoms() should be left */
+ {
+ BOOL header_done = FALSE;
+
+ for(i = 0; i < rt->atom_size; i++) {
+ JSAtomStruct *p = rt->atom_array[i];
+ if (!atom_is_free(p) /* && p->str*/) {
+ if (i >= JS_ATOM_END || p->header.ref_count != 1) {
+ if (!header_done) {
+ header_done = TRUE;
+ if (rt->rt_info) {
+ printf("%s:1: atom leakage:", rt->rt_info);
+ } else {
+ printf("Atom leaks:\n"
+ " %6s %6s %s\n",
+ "ID", "REFCNT", "NAME");
+ }
+ }
+ if (rt->rt_info) {
+ printf(" ");
+ } else {
+ printf(" %6u %6u ", i, p->header.ref_count);
+ }
+ switch (p->atom_type) {
+ case JS_ATOM_TYPE_STRING:
+ JS_DumpString(rt, p);
+ break;
+ case JS_ATOM_TYPE_GLOBAL_SYMBOL:
+ printf("Symbol.for(");
+ JS_DumpString(rt, p);
+ printf(")");
+ break;
+ case JS_ATOM_TYPE_SYMBOL:
+ if (p->hash == JS_ATOM_HASH_SYMBOL) {
+ printf("Symbol(");
+ JS_DumpString(rt, p);
+ printf(")");
+ } else {
+ printf("Private(");
+ JS_DumpString(rt, p);
+ printf(")");
+ }
+ break;
+ }
+ if (rt->rt_info) {
+ printf(":%u", p->header.ref_count);
+ } else {
+ printf("\n");
+ }
+ }
+ }
+ }
+ if (rt->rt_info && header_done)
+ printf("\n");
+ }
+#endif
+
+ /* free the atoms */
+ for(i = 0; i < rt->atom_size; i++) {
+ JSAtomStruct *p = rt->atom_array[i];
+ if (!atom_is_free(p)) {
+#ifdef DUMP_LEAKS
+ list_del(&p->link);
+#endif
+ js_free_rt(rt, p);
+ }
+ }
+ js_free_rt(rt, rt->atom_array);
+ js_free_rt(rt, rt->atom_hash);
+ js_free_rt(rt, rt->shape_hash);
+#ifdef DUMP_LEAKS
+ if (!list_empty(&rt->string_list)) {
+ if (rt->rt_info) {
+ printf("%s:1: string leakage:", rt->rt_info);
+ } else {
+ printf("String leaks:\n"
+ " %6s %s\n",
+ "REFCNT", "VALUE");
+ }
+ list_for_each_safe(el, el1, &rt->string_list) {
+ JSString *str = list_entry(el, JSString, link);
+ if (rt->rt_info) {
+ printf(" ");
+ } else {
+ printf(" %6u ", str->header.ref_count);
+ }
+ JS_DumpString(rt, str);
+ if (rt->rt_info) {
+ printf(":%u", str->header.ref_count);
+ } else {
+ printf("\n");
+ }
+ list_del(&str->link);
+ js_free_rt(rt, str);
+ }
+ if (rt->rt_info)
+ printf("\n");
+ }
+ {
+ JSMallocState *s = &rt->malloc_state;
+ if (s->malloc_count > 1) {
+ if (rt->rt_info)
+ printf("%s:1: ", rt->rt_info);
+ printf("Memory leak: %"PRIu64" bytes lost in %"PRIu64" block%s\n",
+ (uint64_t)(s->malloc_size - sizeof(JSRuntime)),
+ (uint64_t)(s->malloc_count - 1), &"s"[s->malloc_count == 2]);
+ }
+ }
+#endif
+
+ {
+ JSMallocState ms = rt->malloc_state;
+ rt->mf.js_free(&ms, rt);
+ }
+}
+
+JSContext *JS_NewContextRaw(JSRuntime *rt)
+{
+ JSContext *ctx;
+ int i;
+
+ ctx = js_mallocz_rt(rt, sizeof(JSContext));
+ if (!ctx)
+ return NULL;
+ ctx->header.ref_count = 1;
+ add_gc_object(rt, &ctx->header, JS_GC_OBJ_TYPE_JS_CONTEXT);
+
+ ctx->class_proto = js_malloc_rt(rt, sizeof(ctx->class_proto[0]) *
+ rt->class_count);
+ if (!ctx->class_proto) {
+ js_free_rt(rt, ctx);
+ return NULL;
+ }
+ ctx->rt = rt;
+ list_add_tail(&ctx->link, &rt->context_list);
+#ifdef CONFIG_BIGNUM
+ ctx->bf_ctx = &rt->bf_ctx;
+ ctx->fp_env.prec = 113;
+ ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL;
+#endif
+ for(i = 0; i < rt->class_count; i++)
+ ctx->class_proto[i] = JS_NULL;
+ ctx->array_ctor = JS_NULL;
+ ctx->regexp_ctor = JS_NULL;
+ ctx->promise_ctor = JS_NULL;
+ init_list_head(&ctx->loaded_modules);
+
+ JS_AddIntrinsicBasicObjects(ctx);
+ return ctx;
+}
+
+JSContext *JS_NewContext(JSRuntime *rt)
+{
+ JSContext *ctx;
+
+ ctx = JS_NewContextRaw(rt);
+ if (!ctx)
+ return NULL;
+
+ JS_AddIntrinsicBaseObjects(ctx);
+ JS_AddIntrinsicDate(ctx);
+ JS_AddIntrinsicEval(ctx);
+ JS_AddIntrinsicStringNormalize(ctx);
+ JS_AddIntrinsicRegExp(ctx);
+ JS_AddIntrinsicJSON(ctx);
+ JS_AddIntrinsicProxy(ctx);
+ JS_AddIntrinsicMapSet(ctx);
+ JS_AddIntrinsicTypedArrays(ctx);
+ JS_AddIntrinsicPromise(ctx);
+#ifdef CONFIG_BIGNUM
+ JS_AddIntrinsicBigInt(ctx);
+#endif
+ return ctx;
+}
+
+void *JS_GetContextOpaque(JSContext *ctx)
+{
+ return ctx->user_opaque;
+}
+
+void JS_SetContextOpaque(JSContext *ctx, void *opaque)
+{
+ ctx->user_opaque = opaque;
+}
+
+/* set the new value and free the old value after (freeing the value
+ can reallocate the object data) */
+static inline void set_value(JSContext *ctx, JSValue *pval, JSValue new_val)
+{
+ JSValue old_val;
+ old_val = *pval;
+ *pval = new_val;
+ JS_FreeValue(ctx, old_val);
+}
+
+void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj)
+{
+ JSRuntime *rt = ctx->rt;
+ assert(class_id < rt->class_count);
+ set_value(ctx, &ctx->class_proto[class_id], obj);
+}
+
+JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id)
+{
+ JSRuntime *rt = ctx->rt;
+ assert(class_id < rt->class_count);
+ return JS_DupValue(ctx, ctx->class_proto[class_id]);
+}
+
+typedef enum JSFreeModuleEnum {
+ JS_FREE_MODULE_ALL,
+ JS_FREE_MODULE_NOT_RESOLVED,
+ JS_FREE_MODULE_NOT_EVALUATED,
+} JSFreeModuleEnum;
+
+/* XXX: would be more efficient with separate module lists */
+static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag)
+{
+ struct list_head *el, *el1;
+ list_for_each_safe(el, el1, &ctx->loaded_modules) {
+ JSModuleDef *m = list_entry(el, JSModuleDef, link);
+ if (flag == JS_FREE_MODULE_ALL ||
+ (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved) ||
+ (flag == JS_FREE_MODULE_NOT_EVALUATED && !m->evaluated)) {
+ js_free_module_def(ctx, m);
+ }
+ }
+}
+
+JSContext *JS_DupContext(JSContext *ctx)
+{
+ ctx->header.ref_count++;
+ return ctx;
+}
+
+/* used by the GC */
+static void JS_MarkContext(JSRuntime *rt, JSContext *ctx,
+ JS_MarkFunc *mark_func)
+{
+ int i;
+ struct list_head *el;
+
+ /* modules are not seen by the GC, so we directly mark the objects
+ referenced by each module */
+ list_for_each(el, &ctx->loaded_modules) {
+ JSModuleDef *m = list_entry(el, JSModuleDef, link);
+ js_mark_module_def(rt, m, mark_func);
+ }
+
+ JS_MarkValue(rt, ctx->global_obj, mark_func);
+ JS_MarkValue(rt, ctx->global_var_obj, mark_func);
+
+ JS_MarkValue(rt, ctx->throw_type_error, mark_func);
+ JS_MarkValue(rt, ctx->eval_obj, mark_func);
+
+ JS_MarkValue(rt, ctx->array_proto_values, mark_func);
+ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
+ JS_MarkValue(rt, ctx->native_error_proto[i], mark_func);
+ }
+ for(i = 0; i < rt->class_count; i++) {
+ JS_MarkValue(rt, ctx->class_proto[i], mark_func);
+ }
+ JS_MarkValue(rt, ctx->iterator_proto, mark_func);
+ JS_MarkValue(rt, ctx->async_iterator_proto, mark_func);
+ JS_MarkValue(rt, ctx->promise_ctor, mark_func);
+ JS_MarkValue(rt, ctx->array_ctor, mark_func);
+ JS_MarkValue(rt, ctx->regexp_ctor, mark_func);
+ JS_MarkValue(rt, ctx->function_ctor, mark_func);
+ JS_MarkValue(rt, ctx->function_proto, mark_func);
+
+ if (ctx->array_shape)
+ mark_func(rt, &ctx->array_shape->header);
+}
+
+void JS_FreeContext(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ int i;
+
+ if (--ctx->header.ref_count > 0)
+ return;
+ assert(ctx->header.ref_count == 0);
+
+#ifdef DUMP_ATOMS
+ JS_DumpAtoms(ctx->rt);
+#endif
+#ifdef DUMP_SHAPES
+ JS_DumpShapes(ctx->rt);
+#endif
+#ifdef DUMP_OBJECTS
+ {
+ struct list_head *el;
+ JSGCObjectHeader *p;
+ printf("JSObjects: {\n");
+ JS_DumpObjectHeader(ctx->rt);
+ list_for_each(el, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ JS_DumpGCObject(rt, p);
+ }
+ printf("}\n");
+ }
+#endif
+#ifdef DUMP_MEM
+ {
+ JSMemoryUsage stats;
+ JS_ComputeMemoryUsage(rt, &stats);
+ JS_DumpMemoryUsage(stdout, &stats, rt);
+ }
+#endif
+
+ js_free_modules(ctx, JS_FREE_MODULE_ALL);
+
+ JS_FreeValue(ctx, ctx->global_obj);
+ JS_FreeValue(ctx, ctx->global_var_obj);
+
+ JS_FreeValue(ctx, ctx->throw_type_error);
+ JS_FreeValue(ctx, ctx->eval_obj);
+
+ JS_FreeValue(ctx, ctx->array_proto_values);
+ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
+ JS_FreeValue(ctx, ctx->native_error_proto[i]);
+ }
+ for(i = 0; i < rt->class_count; i++) {
+ JS_FreeValue(ctx, ctx->class_proto[i]);
+ }
+ js_free_rt(rt, ctx->class_proto);
+ JS_FreeValue(ctx, ctx->iterator_proto);
+ JS_FreeValue(ctx, ctx->async_iterator_proto);
+ JS_FreeValue(ctx, ctx->promise_ctor);
+ JS_FreeValue(ctx, ctx->array_ctor);
+ JS_FreeValue(ctx, ctx->regexp_ctor);
+ JS_FreeValue(ctx, ctx->function_ctor);
+ JS_FreeValue(ctx, ctx->function_proto);
+
+ js_free_shape_null(ctx->rt, ctx->array_shape);
+
+ list_del(&ctx->link);
+ remove_gc_object(&ctx->header);
+ js_free_rt(ctx->rt, ctx);
+}
+
+JSRuntime *JS_GetRuntime(JSContext *ctx)
+{
+ return ctx->rt;
+}
+
+static void update_stack_limit(JSRuntime *rt)
+{
+ if (rt->stack_size == 0) {
+ rt->stack_limit = 0; /* no limit */
+ } else {
+ rt->stack_limit = rt->stack_top - rt->stack_size;
+ }
+}
+
+void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size)
+{
+ rt->stack_size = stack_size;
+ update_stack_limit(rt);
+}
+
+void JS_UpdateStackTop(JSRuntime *rt)
+{
+ rt->stack_top = js_get_stack_pointer();
+ update_stack_limit(rt);
+}
+
+static inline BOOL is_strict_mode(JSContext *ctx)
+{
+ JSStackFrame *sf = ctx->rt->current_stack_frame;
+ return (sf && (sf->js_mode & JS_MODE_STRICT));
+}
+
+#ifdef CONFIG_BIGNUM
+static inline BOOL is_math_mode(JSContext *ctx)
+{
+ JSStackFrame *sf = ctx->rt->current_stack_frame;
+ return (sf && (sf->js_mode & JS_MODE_MATH));
+}
+#endif
+
+/* JSAtom support */
+
+#define JS_ATOM_TAG_INT (1U << 31)
+#define JS_ATOM_MAX_INT (JS_ATOM_TAG_INT - 1)
+#define JS_ATOM_MAX ((1U << 30) - 1)
+
+/* return the max count from the hash size */
+#define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)
+
+static inline BOOL JS_AtomIsConst(JSAtom v)
+{
+#if defined(DUMP_LEAKS) && DUMP_LEAKS > 1
+ return (int32_t)v <= 0;
+#else
+ return (int32_t)v < JS_ATOM_END;
+#endif
+}
+
+static inline BOOL JS_AtomIsTaggedInt(JSAtom v)
+{
+ return (v & JS_ATOM_TAG_INT) != 0;
+}
+
+static inline JSAtom JS_AtomFromUInt32(uint32_t v)
+{
+ return v | JS_ATOM_TAG_INT;
+}
+
+static inline uint32_t JS_AtomToUInt32(JSAtom atom)
+{
+ return atom & ~JS_ATOM_TAG_INT;
+}
+
+static inline int is_num(int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+/* return TRUE if the string is a number n with 0 <= n <= 2^32-1 */
+static inline BOOL is_num_string(uint32_t *pval, const JSString *p)
+{
+ uint32_t n;
+ uint64_t n64;
+ int c, i, len;
+
+ len = p->len;
+ if (len == 0 || len > 10)
+ return FALSE;
+ if (p->is_wide_char)
+ c = p->u.str16[0];
+ else
+ c = p->u.str8[0];
+ if (is_num(c)) {
+ if (c == '0') {
+ if (len != 1)
+ return FALSE;
+ n = 0;
+ } else {
+ n = c - '0';
+ for(i = 1; i < len; i++) {
+ if (p->is_wide_char)
+ c = p->u.str16[i];
+ else
+ c = p->u.str8[i];
+ if (!is_num(c))
+ return FALSE;
+ n64 = (uint64_t)n * 10 + (c - '0');
+ if ((n64 >> 32) != 0)
+ return FALSE;
+ n = n64;
+ }
+ }
+ *pval = n;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/* XXX: could use faster version ? */
+static inline uint32_t hash_string8(const uint8_t *str, size_t len, uint32_t h)
+{
+ size_t i;
+
+ for(i = 0; i < len; i++)
+ h = h * 263 + str[i];
+ return h;
+}
+
+static inline uint32_t hash_string16(const uint16_t *str,
+ size_t len, uint32_t h)
+{
+ size_t i;
+
+ for(i = 0; i < len; i++)
+ h = h * 263 + str[i];
+ return h;
+}
+
+static uint32_t hash_string(const JSString *str, uint32_t h)
+{
+ if (str->is_wide_char)
+ h = hash_string16(str->u.str16, str->len, h);
+ else
+ h = hash_string8(str->u.str8, str->len, h);
+ return h;
+}
+
+static maybe_unused void JS_DumpString(JSRuntime *rt,
+ const JSString *p)
+{
+ int i, c, sep;
+
+ if (p == NULL) {
+ printf("<null>");
+ return;
+ }
+ printf("%d", p->header.ref_count);
+ sep = (p->header.ref_count == 1) ? '\"' : '\'';
+ putchar(sep);
+ for(i = 0; i < p->len; i++) {
+ if (p->is_wide_char)
+ c = p->u.str16[i];
+ else
+ c = p->u.str8[i];
+ if (c == sep || c == '\\') {
+ putchar('\\');
+ putchar(c);
+ } else if (c >= ' ' && c <= 126) {
+ putchar(c);
+ } else if (c == '\n') {
+ putchar('\\');
+ putchar('n');
+ } else {
+ printf("\\u%04x", c);
+ }
+ }
+ putchar(sep);
+}
+
+static maybe_unused void JS_DumpAtoms(JSRuntime *rt)
+{
+ JSAtomStruct *p;
+ int h, i;
+ /* This only dumps hashed atoms, not JS_ATOM_TYPE_SYMBOL atoms */
+ printf("JSAtom count=%d size=%d hash_size=%d:\n",
+ rt->atom_count, rt->atom_size, rt->atom_hash_size);
+ printf("JSAtom hash table: {\n");
+ for(i = 0; i < rt->atom_hash_size; i++) {
+ h = rt->atom_hash[i];
+ if (h) {
+ printf(" %d:", i);
+ while (h) {
+ p = rt->atom_array[h];
+ printf(" ");
+ JS_DumpString(rt, p);
+ h = p->hash_next;
+ }
+ printf("\n");
+ }
+ }
+ printf("}\n");
+ printf("JSAtom table: {\n");
+ for(i = 0; i < rt->atom_size; i++) {
+ p = rt->atom_array[i];
+ if (!atom_is_free(p)) {
+ printf(" %d: { %d %08x ", i, p->atom_type, p->hash);
+ if (!(p->len == 0 && p->is_wide_char != 0))
+ JS_DumpString(rt, p);
+ printf(" %d }\n", p->hash_next);
+ }
+ }
+ printf("}\n");
+}
+
+static int JS_ResizeAtomHash(JSRuntime *rt, int new_hash_size)
+{
+ JSAtomStruct *p;
+ uint32_t new_hash_mask, h, i, hash_next1, j, *new_hash;
+
+ assert((new_hash_size & (new_hash_size - 1)) == 0); /* power of two */
+ new_hash_mask = new_hash_size - 1;
+ new_hash = js_mallocz_rt(rt, sizeof(rt->atom_hash[0]) * new_hash_size);
+ if (!new_hash)
+ return -1;
+ for(i = 0; i < rt->atom_hash_size; i++) {
+ h = rt->atom_hash[i];
+ while (h != 0) {
+ p = rt->atom_array[h];
+ hash_next1 = p->hash_next;
+ /* add in new hash table */
+ j = p->hash & new_hash_mask;
+ p->hash_next = new_hash[j];
+ new_hash[j] = h;
+ h = hash_next1;
+ }
+ }
+ js_free_rt(rt, rt->atom_hash);
+ rt->atom_hash = new_hash;
+ rt->atom_hash_size = new_hash_size;
+ rt->atom_count_resize = JS_ATOM_COUNT_RESIZE(new_hash_size);
+ // JS_DumpAtoms(rt);
+ return 0;
+}
+
+static int JS_InitAtoms(JSRuntime *rt)
+{
+ int i, len, atom_type;
+ const char *p;
+
+ rt->atom_hash_size = 0;
+ rt->atom_hash = NULL;
+ rt->atom_count = 0;
+ rt->atom_size = 0;
+ rt->atom_free_index = 0;
+ if (JS_ResizeAtomHash(rt, 256)) /* there are at least 195 predefined atoms */
+ return -1;
+
+ p = js_atom_init;
+ for(i = 1; i < JS_ATOM_END; i++) {
+ if (i == JS_ATOM_Private_brand)
+ atom_type = JS_ATOM_TYPE_PRIVATE;
+ else if (i >= JS_ATOM_Symbol_toPrimitive)
+ atom_type = JS_ATOM_TYPE_SYMBOL;
+ else
+ atom_type = JS_ATOM_TYPE_STRING;
+ len = strlen(p);
+ if (JS_NewAtomInitImpl(rt, p, len, atom_type) == JS_ATOM_NULL)
+ return -1;
+ p = p + len + 1;
+ }
+ return 0;
+}
+
+static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
+{
+ JSAtomStruct *p;
+
+ if (!JS_AtomIsConst(v)) {
+ p = rt->atom_array[v];
+ p->header.ref_count++;
+ }
+ return v;
+}
+
+JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)
+{
+ JSRuntime *rt;
+ JSAtomStruct *p;
+
+ if (!JS_AtomIsConst(v)) {
+ rt = ctx->rt;
+ p = rt->atom_array[v];
+ p->header.ref_count++;
+ }
+ return v;
+}
+
+static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)
+{
+ JSRuntime *rt;
+ JSAtomStruct *p;
+
+ rt = ctx->rt;
+ if (JS_AtomIsTaggedInt(v))
+ return JS_ATOM_KIND_STRING;
+ p = rt->atom_array[v];
+ switch(p->atom_type) {
+ case JS_ATOM_TYPE_STRING:
+ return JS_ATOM_KIND_STRING;
+ case JS_ATOM_TYPE_GLOBAL_SYMBOL:
+ return JS_ATOM_KIND_SYMBOL;
+ case JS_ATOM_TYPE_SYMBOL:
+ switch(p->hash) {
+ case JS_ATOM_HASH_SYMBOL:
+ return JS_ATOM_KIND_SYMBOL;
+ case JS_ATOM_HASH_PRIVATE:
+ return JS_ATOM_KIND_PRIVATE;
+ default:
+ abort();
+ }
+ default:
+ abort();
+ }
+}
+
+static BOOL JS_AtomIsString(JSContext *ctx, JSAtom v)
+{
+ return JS_AtomGetKind(ctx, v) == JS_ATOM_KIND_STRING;
+}
+
+static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
+{
+ uint32_t i = p->hash_next; /* atom_index */
+ if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
+ JSAtomStruct *p1;
+
+ i = rt->atom_hash[p->hash & (rt->atom_hash_size - 1)];
+ p1 = rt->atom_array[i];
+ while (p1 != p) {
+ assert(i != 0);
+ i = p1->hash_next;
+ p1 = rt->atom_array[i];
+ }
+ }
+ return i;
+}
+
+/* string case (internal). Return JS_ATOM_NULL if error. 'str' is
+ freed. */
+static JSAtom JS_NewAtomImpl(JSRuntime *rt, JSString *str, int atom_type)
+{
+ uint32_t h, h1, i;
+ JSAtomStruct *p;
+ int len;
+
+#if 0
+ printf("__JS_NewAtom: "); JS_DumpString(rt, str); printf("\n");
+#endif
+ if (atom_type < JS_ATOM_TYPE_SYMBOL) {
+ /* str is not NULL */
+ if (str->atom_type == atom_type) {
+ /* str is the atom, return its index */
+ i = js_get_atom_index(rt, str);
+ /* reduce string refcount and increase atom's unless constant */
+ if (JS_AtomIsConst(i))
+ str->header.ref_count--;
+ return i;
+ }
+ /* try and locate an already registered atom */
+ len = str->len;
+ h = hash_string(str, atom_type);
+ h &= JS_ATOM_HASH_MASK;
+ h1 = h & (rt->atom_hash_size - 1);
+ i = rt->atom_hash[h1];
+ while (i != 0) {
+ p = rt->atom_array[i];
+ if (p->hash == h &&
+ p->atom_type == atom_type &&
+ p->len == len &&
+ js_string_memcmp(p, str, len) == 0) {
+ if (!JS_AtomIsConst(i))
+ p->header.ref_count++;
+ goto done;
+ }
+ i = p->hash_next;
+ }
+ } else {
+ h1 = 0; /* avoid warning */
+ if (atom_type == JS_ATOM_TYPE_SYMBOL) {
+ h = JS_ATOM_HASH_SYMBOL;
+ } else {
+ h = JS_ATOM_HASH_PRIVATE;
+ atom_type = JS_ATOM_TYPE_SYMBOL;
+ }
+ }
+
+ if (rt->atom_free_index == 0) {
+ /* allow new atom entries */
+ uint32_t new_size, start;
+ JSAtomStruct **new_array;
+
+ /* alloc new with size progression 3/2:
+ 4 6 9 13 19 28 42 63 94 141 211 316 474 711 1066 1599 2398 3597 5395 8092
+ preallocating space for predefined atoms (at least 195).
+ */
+ new_size = max_int(211, rt->atom_size * 3 / 2);
+ if (new_size > JS_ATOM_MAX)
+ goto fail;
+ /* XXX: should use realloc2 to use slack space */
+ new_array = js_realloc_rt(rt, rt->atom_array, sizeof(*new_array) * new_size);
+ if (!new_array)
+ goto fail;
+ /* Note: the atom 0 is not used */
+ start = rt->atom_size;
+ if (start == 0) {
+ /* JS_ATOM_NULL entry */
+ p = js_mallocz_rt(rt, sizeof(JSAtomStruct));
+ if (!p) {
+ js_free_rt(rt, new_array);
+ goto fail;
+ }
+ p->header.ref_count = 1; /* not refcounted */
+ p->atom_type = JS_ATOM_TYPE_SYMBOL;
+#ifdef DUMP_LEAKS
+ list_add_tail(&p->link, &rt->string_list);
+#endif
+ new_array[0] = p;
+ rt->atom_count++;
+ start = 1;
+ }
+ rt->atom_size = new_size;
+ rt->atom_array = new_array;
+ rt->atom_free_index = start;
+ for(i = start; i < new_size; i++) {
+ uint32_t next;
+ if (i == (new_size - 1))
+ next = 0;
+ else
+ next = i + 1;
+ rt->atom_array[i] = atom_set_free(next);
+ }
+ }
+
+ if (str) {
+ if (str->atom_type == 0) {
+ p = str;
+ p->atom_type = atom_type;
+ } else {
+ p = js_malloc_rt(rt, sizeof(JSString) +
+ (str->len << str->is_wide_char) +
+ 1 - str->is_wide_char);
+ if (unlikely(!p))
+ goto fail;
+ p->header.ref_count = 1;
+ p->is_wide_char = str->is_wide_char;
+ p->len = str->len;
+#ifdef DUMP_LEAKS
+ list_add_tail(&p->link, &rt->string_list);
+#endif
+ memcpy(p->u.str8, str->u.str8, (str->len << str->is_wide_char) +
+ 1 - str->is_wide_char);
+ js_free_string(rt, str);
+ }
+ } else {
+ p = js_malloc_rt(rt, sizeof(JSAtomStruct)); /* empty wide string */
+ if (!p)
+ return JS_ATOM_NULL;
+ p->header.ref_count = 1;
+ p->is_wide_char = 1; /* Hack to represent NULL as a JSString */
+ p->len = 0;
+#ifdef DUMP_LEAKS
+ list_add_tail(&p->link, &rt->string_list);
+#endif
+ }
+
+ /* use an already free entry */
+ i = rt->atom_free_index;
+ rt->atom_free_index = atom_get_free(rt->atom_array[i]);
+ rt->atom_array[i] = p;
+
+ p->hash = h;
+ p->hash_next = i; /* atom_index */
+ p->atom_type = atom_type;
+
+ rt->atom_count++;
+
+ if (atom_type != JS_ATOM_TYPE_SYMBOL) {
+ p->hash_next = rt->atom_hash[h1];
+ rt->atom_hash[h1] = i;
+ if (unlikely(rt->atom_count >= rt->atom_count_resize))
+ JS_ResizeAtomHash(rt, rt->atom_hash_size * 2);
+ }
+
+ // JS_DumpAtoms(rt);
+ return i;
+
+ fail:
+ i = JS_ATOM_NULL;
+ done:
+ if (str)
+ js_free_string(rt, str);
+ return i;
+}
+
+/* only works with zero terminated 8 bit strings */
+static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len,
+ int atom_type)
+{
+ JSString *p;
+ p = js_alloc_string_rt(rt, len, 0);
+ if (!p)
+ return JS_ATOM_NULL;
+ memcpy(p->u.str8, str, len);
+ p->u.str8[len] = '\0';
+ return JS_NewAtomImpl(rt, p, atom_type);
+}
+
+static JSAtom JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
+ int atom_type)
+{
+ uint32_t h, h1, i;
+ JSAtomStruct *p;
+
+ h = hash_string8((const uint8_t *)str, len, JS_ATOM_TYPE_STRING);
+ h &= JS_ATOM_HASH_MASK;
+ h1 = h & (rt->atom_hash_size - 1);
+ i = rt->atom_hash[h1];
+ while (i != 0) {
+ p = rt->atom_array[i];
+ if (p->hash == h &&
+ p->atom_type == JS_ATOM_TYPE_STRING &&
+ p->len == len &&
+ p->is_wide_char == 0 &&
+ memcmp(p->u.str8, str, len) == 0) {
+ if (!JS_AtomIsConst(i))
+ p->header.ref_count++;
+ return i;
+ }
+ i = p->hash_next;
+ }
+ return JS_ATOM_NULL;
+}
+
+static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
+{
+#if 0 /* JS_ATOM_NULL is not refcounted: __JS_AtomIsConst() includes 0 */
+ if (unlikely(i == JS_ATOM_NULL)) {
+ p->header.ref_count = INT32_MAX / 2;
+ return;
+ }
+#endif
+ uint32_t i = p->hash_next; /* atom_index */
+ if (p->atom_type != JS_ATOM_TYPE_SYMBOL) {
+ JSAtomStruct *p0, *p1;
+ uint32_t h0;
+
+ h0 = p->hash & (rt->atom_hash_size - 1);
+ i = rt->atom_hash[h0];
+ p1 = rt->atom_array[i];
+ if (p1 == p) {
+ rt->atom_hash[h0] = p1->hash_next;
+ } else {
+ for(;;) {
+ assert(i != 0);
+ p0 = p1;
+ i = p1->hash_next;
+ p1 = rt->atom_array[i];
+ if (p1 == p) {
+ p0->hash_next = p1->hash_next;
+ break;
+ }
+ }
+ }
+ }
+ /* insert in free atom list */
+ rt->atom_array[i] = atom_set_free(rt->atom_free_index);
+ rt->atom_free_index = i;
+ /* free the string structure */
+#ifdef DUMP_LEAKS
+ list_del(&p->link);
+#endif
+ js_free_rt(rt, p);
+ rt->atom_count--;
+ assert(rt->atom_count >= 0);
+}
+
+static void JS_FreeAtomImpl(JSRuntime *rt, uint32_t i)
+{
+ JSAtomStruct *p;
+
+ p = rt->atom_array[i];
+ if (--p->header.ref_count > 0)
+ return;
+ JS_FreeAtomStruct(rt, p);
+}
+
+/* Warning: 'p' is freed */
+static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
+{
+ JSRuntime *rt = ctx->rt;
+ uint32_t n;
+ if (is_num_string(&n, p)) {
+ if (n <= JS_ATOM_MAX_INT) {
+ js_free_string(rt, p);
+ return JS_AtomFromUInt32(n);
+ }
+ }
+ /* XXX: should generate an exception */
+ return JS_NewAtomImpl(rt, p, JS_ATOM_TYPE_STRING);
+}
+
+JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
+{
+ JSValue val;
+
+ if (len == 0 || !is_digit(*str)) {
+ JSAtom atom = JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
+ if (atom)
+ return atom;
+ }
+ val = JS_NewStringLen(ctx, str, len);
+ if (JS_IsException(val))
+ return JS_ATOM_NULL;
+ return JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(val));
+}
+
+JSAtom JS_NewAtom(JSContext *ctx, const char *str)
+{
+ return JS_NewAtomLen(ctx, str, strlen(str));
+}
+
+JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
+{
+ if (n <= JS_ATOM_MAX_INT) {
+ return JS_AtomFromUInt32(n);
+ } else {
+ char buf[11];
+ JSValue val;
+ snprintf(buf, sizeof(buf), "%u", n);
+ val = JS_NewString(ctx, buf);
+ if (JS_IsException(val))
+ return JS_ATOM_NULL;
+ return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val),
+ JS_ATOM_TYPE_STRING);
+ }
+}
+
+static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
+{
+ if ((uint64_t)n <= JS_ATOM_MAX_INT) {
+ return JS_AtomFromUInt32((uint32_t)n);
+ } else {
+ char buf[24];
+ JSValue val;
+ snprintf(buf, sizeof(buf), "%" PRId64 , n);
+ val = JS_NewString(ctx, buf);
+ if (JS_IsException(val))
+ return JS_ATOM_NULL;
+ return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val),
+ JS_ATOM_TYPE_STRING);
+ }
+}
+
+/* 'p' is freed */
+static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)
+{
+ JSRuntime *rt = ctx->rt;
+ JSAtom atom;
+ atom = JS_NewAtomImpl(rt, p, atom_type);
+ if (atom == JS_ATOM_NULL)
+ return JS_ThrowOutOfMemory(ctx);
+ return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);
+}
+
+/* descr must be a non-numeric string atom */
+static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
+ int atom_type)
+{
+ JSRuntime *rt = ctx->rt;
+ JSString *p;
+
+ assert(!JS_AtomIsTaggedInt(descr));
+ assert(descr < rt->atom_size);
+ p = rt->atom_array[descr];
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+ return JS_NewSymbol(ctx, p, atom_type);
+}
+
+#define ATOM_GET_STR_BUF_SIZE 64
+
+/* Should only be used for debug. */
+static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,
+ JSAtom atom)
+{
+ if (JS_AtomIsTaggedInt(atom)) {
+ snprintf(buf, buf_size, "%u", JS_AtomToUInt32(atom));
+ } else {
+ JSAtomStruct *p;
+ assert(atom < rt->atom_size);
+ if (atom == JS_ATOM_NULL) {
+ snprintf(buf, buf_size, "<null>");
+ } else {
+ int i, c;
+ char *q;
+ JSString *str;
+
+ q = buf;
+ p = rt->atom_array[atom];
+ assert(!atom_is_free(p));
+ str = p;
+ if (str) {
+ if (!str->is_wide_char) {
+ /* special case ASCII strings */
+ c = 0;
+ for(i = 0; i < str->len; i++) {
+ c |= str->u.str8[i];
+ }
+ if (c < 0x80)
+ return (const char *)str->u.str8;
+ }
+ for(i = 0; i < str->len; i++) {
+ if (str->is_wide_char)
+ c = str->u.str16[i];
+ else
+ c = str->u.str8[i];
+ if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX)
+ break;
+ if (c < 128) {
+ *q++ = c;
+ } else {
+ q += unicode_to_utf8((uint8_t *)q, c);
+ }
+ }
+ }
+ *q = '\0';
+ }
+ }
+ return buf;
+}
+
+static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom atom)
+{
+ return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
+}
+
+static JSValue JS_AtomToValueImpl(JSContext *ctx, JSAtom atom, BOOL force_string)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+
+ if (JS_AtomIsTaggedInt(atom)) {
+ snprintf(buf, sizeof(buf), "%u", JS_AtomToUInt32(atom));
+ return JS_NewString(ctx, buf);
+ } else {
+ JSRuntime *rt = ctx->rt;
+ JSAtomStruct *p;
+ assert(atom < rt->atom_size);
+ p = rt->atom_array[atom];
+ if (p->atom_type == JS_ATOM_TYPE_STRING) {
+ goto ret_string;
+ } else if (force_string) {
+ if (p->len == 0 && p->is_wide_char != 0) {
+ /* no description string */
+ p = rt->atom_array[JS_ATOM_empty_string];
+ }
+ ret_string:
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+ } else {
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_SYMBOL, p));
+ }
+ }
+}
+
+JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
+{
+ return JS_AtomToValueImpl(ctx, atom, FALSE);
+}
+
+JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
+{
+ return JS_AtomToValueImpl(ctx, atom, TRUE);
+}
+
+/* return TRUE if the atom is an array index (i.e. 0 <= index <=
+ 2^32-2 and return its value */
+static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
+{
+ if (JS_AtomIsTaggedInt(atom)) {
+ *pval = JS_AtomToUInt32(atom);
+ return TRUE;
+ } else {
+ JSRuntime *rt = ctx->rt;
+ JSAtomStruct *p;
+ uint32_t val;
+
+ assert(atom < rt->atom_size);
+ p = rt->atom_array[atom];
+ if (p->atom_type == JS_ATOM_TYPE_STRING &&
+ is_num_string(&val, p) && val != -1) {
+ *pval = val;
+ return TRUE;
+ } else {
+ *pval = 0;
+ return FALSE;
+ }
+ }
+}
+
+/* This test must be fast if atom is not a numeric index (e.g. a
+ method name). Return JS_UNDEFINED if not a numeric
+ index. JS_EXCEPTION can also be returned. */
+static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
+{
+ JSRuntime *rt = ctx->rt;
+ JSAtomStruct *p1;
+ JSString *p;
+ int c, len, ret;
+ JSValue num, str;
+
+ if (JS_AtomIsTaggedInt(atom))
+ return JS_NewInt32(ctx, JS_AtomToUInt32(atom));
+ assert(atom < rt->atom_size);
+ p1 = rt->atom_array[atom];
+ if (p1->atom_type != JS_ATOM_TYPE_STRING)
+ return JS_UNDEFINED;
+ p = p1;
+ len = p->len;
+ if (p->is_wide_char) {
+ const uint16_t *r = p->u.str16, *r_end = p->u.str16 + len;
+ if (r >= r_end)
+ return JS_UNDEFINED;
+ c = *r;
+ if (c == '-') {
+ if (r >= r_end)
+ return JS_UNDEFINED;
+ r++;
+ c = *r;
+ /* -0 case is specific */
+ if (c == '0' && len == 2)
+ goto minus_zero;
+ }
+ /* XXX: should test NaN, but the tests do not check it */
+ if (!is_num(c)) {
+ /* XXX: String should be normalized, therefore 8-bit only */
+ const uint16_t nfinity16[7] = { 'n', 'f', 'i', 'n', 'i', 't', 'y' };
+ if (!(c =='I' && (r_end - r) == 8 &&
+ !memcmp(r + 1, nfinity16, sizeof(nfinity16))))
+ return JS_UNDEFINED;
+ }
+ } else {
+ const uint8_t *r = p->u.str8, *r_end = p->u.str8 + len;
+ if (r >= r_end)
+ return JS_UNDEFINED;
+ c = *r;
+ if (c == '-') {
+ if (r >= r_end)
+ return JS_UNDEFINED;
+ r++;
+ c = *r;
+ /* -0 case is specific */
+ if (c == '0' && len == 2) {
+ minus_zero:
+ return JS_NewFloat64Impl(ctx, -0.0);
+ }
+ }
+ if (!is_num(c)) {
+ if (!(c =='I' && (r_end - r) == 8 &&
+ !memcmp(r + 1, "nfinity", 7)))
+ return JS_UNDEFINED;
+ }
+ }
+ /* XXX: bignum: would be better to only accept integer to avoid
+ relying on current floating point precision */
+ /* this is ECMA CanonicalNumericIndexString primitive */
+ num = JS_ToNumber(ctx, JS_MKPTR(JS_TAG_STRING, p));
+ if (JS_IsException(num))
+ return num;
+ str = JS_ToString(ctx, num);
+ if (JS_IsException(str)) {
+ JS_FreeValue(ctx, num);
+ return str;
+ }
+ ret = js_string_compare(ctx, p, JS_VALUE_GET_STRING(str));
+ JS_FreeValue(ctx, str);
+ if (ret == 0) {
+ return num;
+ } else {
+ JS_FreeValue(ctx, num);
+ return JS_UNDEFINED;
+ }
+}
+
+/* return -1 if exception or TRUE/FALSE */
+static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
+{
+ JSValue num;
+ num = JS_AtomIsNumericIndex1(ctx, atom);
+ if (likely(JS_IsUndefined(num)))
+ return FALSE;
+ if (JS_IsException(num))
+ return -1;
+ JS_FreeValue(ctx, num);
+ return TRUE;
+}
+
+void JS_FreeAtom(JSContext *ctx, JSAtom v)
+{
+ if (!JS_AtomIsConst(v))
+ JS_FreeAtomImpl(ctx->rt, v);
+}
+
+void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)
+{
+ if (!JS_AtomIsConst(v))
+ JS_FreeAtomImpl(rt, v);
+}
+
+/* return TRUE if 'v' is a symbol with a string description */
+static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
+{
+ JSRuntime *rt;
+ JSAtomStruct *p;
+
+ rt = ctx->rt;
+ if (JS_AtomIsTaggedInt(v))
+ return FALSE;
+ p = rt->atom_array[v];
+ return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&
+ p->hash == JS_ATOM_HASH_SYMBOL) ||
+ p->atom_type == JS_ATOM_TYPE_GLOBAL_SYMBOL) &&
+ !(p->len == 0 && p->is_wide_char != 0));
+}
+
+static maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ const char *p;
+ int i;
+
+ /* XXX: should handle embedded null characters */
+ /* XXX: should move encoding code to JS_AtomGetStr */
+ p = JS_AtomGetStr(ctx, buf, sizeof(buf), atom);
+ for (i = 0; p[i]; i++) {
+ int c = (unsigned char)p[i];
+ if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
+ (c == '_' || c == '$') || (c >= '0' && c <= '9' && i > 0)))
+ break;
+ }
+ if (i > 0 && p[i] == '\0') {
+ printf("%s", p);
+ } else {
+ putchar('"');
+ printf("%.*s", i, p);
+ for (; p[i]; i++) {
+ int c = (unsigned char)p[i];
+ if (c == '\"' || c == '\\') {
+ putchar('\\');
+ putchar(c);
+ } else if (c >= ' ' && c <= 126) {
+ putchar(c);
+ } else if (c == '\n') {
+ putchar('\\');
+ putchar('n');
+ } else {
+ printf("\\u%04x", c);
+ }
+ }
+ putchar('\"');
+ }
+}
+
+/* free with JS_FreeCString() */
+const char *JS_AtomToCString(JSContext *ctx, JSAtom atom)
+{
+ JSValue str;
+ const char *cstr;
+
+ str = JS_AtomToString(ctx, atom);
+ if (JS_IsException(str))
+ return NULL;
+ cstr = JS_ToCString(ctx, str);
+ JS_FreeValue(ctx, str);
+ return cstr;
+}
+
+/* return a string atom containing name concatenated with str1 */
+static JSAtom js_atom_concat_str(JSContext *ctx, JSAtom name, const char *str1)
+{
+ JSValue str;
+ JSAtom atom;
+ const char *cstr;
+ char *cstr2;
+ size_t len, len1;
+
+ str = JS_AtomToString(ctx, name);
+ if (JS_IsException(str))
+ return JS_ATOM_NULL;
+ cstr = JS_ToCStringLen(ctx, &len, str);
+ if (!cstr)
+ goto fail;
+ len1 = strlen(str1);
+ cstr2 = js_malloc(ctx, len + len1 + 1);
+ if (!cstr2)
+ goto fail;
+ memcpy(cstr2, cstr, len);
+ memcpy(cstr2 + len, str1, len1);
+ cstr2[len + len1] = '\0';
+ atom = JS_NewAtomLen(ctx, cstr2, len + len1);
+ js_free(ctx, cstr2);
+ JS_FreeCString(ctx, cstr);
+ JS_FreeValue(ctx, str);
+ return atom;
+ fail:
+ JS_FreeCString(ctx, cstr);
+ JS_FreeValue(ctx, str);
+ return JS_ATOM_NULL;
+}
+
+static JSAtom js_atom_concat_num(JSContext *ctx, JSAtom name, uint32_t n)
+{
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%u", n);
+ return js_atom_concat_str(ctx, name, buf);
+}
+
+static inline BOOL JS_IsEmptyString(JSValueConst v)
+{
+ return JS_VALUE_GET_TAG(v) == JS_TAG_STRING && JS_VALUE_GET_STRING(v)->len == 0;
+}
+
+/* JSClass support */
+
+/* a new class ID is allocated if *pclass_id != 0 */
+JSClassID JS_NewClassID(JSClassID *pclass_id)
+{
+ JSClassID class_id;
+ /* XXX: make it thread safe */
+ class_id = *pclass_id;
+ if (class_id == 0) {
+ class_id = js_class_id_alloc++;
+ *pclass_id = class_id;
+ }
+ return class_id;
+}
+
+BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id)
+{
+ return (class_id < rt->class_count &&
+ rt->class_array[class_id].class_id != 0);
+}
+
+/* create a new object internal class. Return -1 if error, 0 if
+ OK. The finalizer can be NULL if none is needed. */
+static int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
+ const JSClassDef *class_def, JSAtom name)
+{
+ int new_size, i;
+ JSClass *cl, *new_class_array;
+ struct list_head *el;
+
+ if (class_id >= (1 << 16))
+ return -1;
+ if (class_id < rt->class_count &&
+ rt->class_array[class_id].class_id != 0)
+ return -1;
+
+ if (class_id >= rt->class_count) {
+ new_size = max_int(JS_CLASS_INIT_COUNT,
+ max_int(class_id + 1, rt->class_count * 3 / 2));
+
+ /* reallocate the context class prototype array, if any */
+ list_for_each(el, &rt->context_list) {
+ JSContext *ctx = list_entry(el, JSContext, link);
+ JSValue *new_tab;
+ new_tab = js_realloc_rt(rt, ctx->class_proto,
+ sizeof(ctx->class_proto[0]) * new_size);
+ if (!new_tab)
+ return -1;
+ for(i = rt->class_count; i < new_size; i++)
+ new_tab[i] = JS_NULL;
+ ctx->class_proto = new_tab;
+ }
+ /* reallocate the class array */
+ new_class_array = js_realloc_rt(rt, rt->class_array,
+ sizeof(JSClass) * new_size);
+ if (!new_class_array)
+ return -1;
+ memset(new_class_array + rt->class_count, 0,
+ (new_size - rt->class_count) * sizeof(JSClass));
+ rt->class_array = new_class_array;
+ rt->class_count = new_size;
+ }
+ cl = &rt->class_array[class_id];
+ cl->class_id = class_id;
+ cl->class_name = JS_DupAtomRT(rt, name);
+ cl->finalizer = class_def->finalizer;
+ cl->gc_mark = class_def->gc_mark;
+ cl->call = class_def->call;
+ cl->exotic = class_def->exotic;
+ return 0;
+}
+
+int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)
+{
+ int ret, len;
+ JSAtom name;
+
+ len = strlen(class_def->class_name);
+ name = JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
+ if (name == JS_ATOM_NULL) {
+ name = JS_NewAtomInitImpl(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
+ if (name == JS_ATOM_NULL)
+ return -1;
+ }
+ ret = JS_NewClass1(rt, class_id, class_def, name);
+ JS_FreeAtomRT(rt, name);
+ return ret;
+}
+
+static JSValue js_new_string8(JSContext *ctx, const uint8_t *buf, int len)
+{
+ JSString *str;
+
+ if (len <= 0) {
+ return JS_AtomToString(ctx, JS_ATOM_empty_string);
+ }
+ str = js_alloc_string(ctx, len, 0);
+ if (!str)
+ return JS_EXCEPTION;
+ memcpy(str->u.str8, buf, len);
+ str->u.str8[len] = '\0';
+ return JS_MKPTR(JS_TAG_STRING, str);
+}
+
+static JSValue js_new_string16(JSContext *ctx, const uint16_t *buf, int len)
+{
+ JSString *str;
+ str = js_alloc_string(ctx, len, 1);
+ if (!str)
+ return JS_EXCEPTION;
+ memcpy(str->u.str16, buf, len * 2);
+ return JS_MKPTR(JS_TAG_STRING, str);
+}
+
+static JSValue js_new_string_char(JSContext *ctx, uint16_t c)
+{
+ if (c < 0x100) {
+ uint8_t ch8 = c;
+ return js_new_string8(ctx, &ch8, 1);
+ } else {
+ uint16_t ch16 = c;
+ return js_new_string16(ctx, &ch16, 1);
+ }
+}
+
+static JSValue js_sub_string(JSContext *ctx, JSString *p, int start, int end)
+{
+ int len = end - start;
+ if (start == 0 && end == p->len) {
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+ }
+ if (p->is_wide_char && len > 0) {
+ JSString *str;
+ int i;
+ uint16_t c = 0;
+ for (i = start; i < end; i++) {
+ c |= p->u.str16[i];
+ }
+ if (c > 0xFF)
+ return js_new_string16(ctx, p->u.str16 + start, len);
+
+ str = js_alloc_string(ctx, len, 0);
+ if (!str)
+ return JS_EXCEPTION;
+ for (i = 0; i < len; i++) {
+ str->u.str8[i] = p->u.str16[start + i];
+ }
+ str->u.str8[len] = '\0';
+ return JS_MKPTR(JS_TAG_STRING, str);
+ } else {
+ return js_new_string8(ctx, p->u.str8 + start, len);
+ }
+}
+
+typedef struct StringBuffer {
+ JSContext *ctx;
+ JSString *str;
+ int len;
+ int size;
+ int is_wide_char;
+ int error_status;
+} StringBuffer;
+
+/* It is valid to call string_buffer_end() and all string_buffer functions even
+ if string_buffer_init() or another string_buffer function returns an error.
+ If the error_status is set, string_buffer_end() returns JS_EXCEPTION.
+ */
+static int string_buffer_init2(JSContext *ctx, StringBuffer *s, int size,
+ int is_wide)
+{
+ s->ctx = ctx;
+ s->size = size;
+ s->len = 0;
+ s->is_wide_char = is_wide;
+ s->error_status = 0;
+ s->str = js_alloc_string(ctx, size, is_wide);
+ if (unlikely(!s->str)) {
+ s->size = 0;
+ return s->error_status = -1;
+ }
+#ifdef DUMP_LEAKS
+ /* the StringBuffer may reallocate the JSString, only link it at the end */
+ list_del(&s->str->link);
+#endif
+ return 0;
+}
+
+static inline int string_buffer_init(JSContext *ctx, StringBuffer *s, int size)
+{
+ return string_buffer_init2(ctx, s, size, 0);
+}
+
+static void string_buffer_free(StringBuffer *s)
+{
+ js_free(s->ctx, s->str);
+ s->str = NULL;
+}
+
+static int string_buffer_set_error(StringBuffer *s)
+{
+ js_free(s->ctx, s->str);
+ s->str = NULL;
+ s->size = 0;
+ s->len = 0;
+ return s->error_status = -1;
+}
+
+static no_inline int string_buffer_widen(StringBuffer *s, int size)
+{
+ JSString *str;
+ size_t slack;
+ int i;
+
+ if (s->error_status)
+ return -1;
+
+ str = js_realloc2(s->ctx, s->str, sizeof(JSString) + (size << 1), &slack);
+ if (!str)
+ return string_buffer_set_error(s);
+ size += slack >> 1;
+ for(i = s->len; i-- > 0;) {
+ str->u.str16[i] = str->u.str8[i];
+ }
+ s->is_wide_char = 1;
+ s->size = size;
+ s->str = str;
+ return 0;
+}
+
+static no_inline int string_buffer_realloc(StringBuffer *s, int new_len, int c)
+{
+ JSString *new_str;
+ int new_size;
+ size_t new_size_bytes, slack;
+
+ if (s->error_status)
+ return -1;
+
+ if (new_len > JS_STRING_LEN_MAX) {
+ JS_ThrowInternalError(s->ctx, "string too long");
+ return string_buffer_set_error(s);
+ }
+ new_size = min_int(max_int(new_len, s->size * 3 / 2), JS_STRING_LEN_MAX);
+ if (!s->is_wide_char && c >= 0x100) {
+ return string_buffer_widen(s, new_size);
+ }
+ new_size_bytes = sizeof(JSString) + (new_size << s->is_wide_char) + 1 - s->is_wide_char;
+ new_str = js_realloc2(s->ctx, s->str, new_size_bytes, &slack);
+ if (!new_str)
+ return string_buffer_set_error(s);
+ new_size = min_int(new_size + (slack >> s->is_wide_char), JS_STRING_LEN_MAX);
+ s->size = new_size;
+ s->str = new_str;
+ return 0;
+}
+
+static no_inline int string_buffer_putc_slow(StringBuffer *s, uint32_t c)
+{
+ if (unlikely(s->len >= s->size)) {
+ if (string_buffer_realloc(s, s->len + 1, c))
+ return -1;
+ }
+ if (s->is_wide_char) {
+ s->str->u.str16[s->len++] = c;
+ } else if (c < 0x100) {
+ s->str->u.str8[s->len++] = c;
+ } else {
+ if (string_buffer_widen(s, s->size))
+ return -1;
+ s->str->u.str16[s->len++] = c;
+ }
+ return 0;
+}
+
+/* 0 <= c <= 0xff */
+static int string_buffer_putc8(StringBuffer *s, uint32_t c)
+{
+ if (unlikely(s->len >= s->size)) {
+ if (string_buffer_realloc(s, s->len + 1, c))
+ return -1;
+ }
+ if (s->is_wide_char) {
+ s->str->u.str16[s->len++] = c;
+ } else {
+ s->str->u.str8[s->len++] = c;
+ }
+ return 0;
+}
+
+/* 0 <= c <= 0xffff */
+static int string_buffer_putc16(StringBuffer *s, uint32_t c)
+{
+ if (likely(s->len < s->size)) {
+ if (s->is_wide_char) {
+ s->str->u.str16[s->len++] = c;
+ return 0;
+ } else if (c < 0x100) {
+ s->str->u.str8[s->len++] = c;
+ return 0;
+ }
+ }
+ return string_buffer_putc_slow(s, c);
+}
+
+/* 0 <= c <= 0x10ffff */
+static int string_buffer_putc(StringBuffer *s, uint32_t c)
+{
+ if (unlikely(c >= 0x10000)) {
+ /* surrogate pair */
+ c -= 0x10000;
+ if (string_buffer_putc16(s, (c >> 10) + 0xd800))
+ return -1;
+ c = (c & 0x3ff) + 0xdc00;
+ }
+ return string_buffer_putc16(s, c);
+}
+
+static int string_get(const JSString *p, int idx) {
+ return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx];
+}
+
+static int string_getc(const JSString *p, int *pidx)
+{
+ int idx, c, c1;
+ idx = *pidx;
+ if (p->is_wide_char) {
+ c = p->u.str16[idx++];
+ if (c >= 0xd800 && c < 0xdc00 && idx < p->len) {
+ c1 = p->u.str16[idx];
+ if (c1 >= 0xdc00 && c1 < 0xe000) {
+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
+ idx++;
+ }
+ }
+ } else {
+ c = p->u.str8[idx++];
+ }
+ *pidx = idx;
+ return c;
+}
+
+static int string_buffer_write8(StringBuffer *s, const uint8_t *p, int len)
+{
+ int i;
+
+ if (s->len + len > s->size) {
+ if (string_buffer_realloc(s, s->len + len, 0))
+ return -1;
+ }
+ if (s->is_wide_char) {
+ for (i = 0; i < len; i++) {
+ s->str->u.str16[s->len + i] = p[i];
+ }
+ s->len += len;
+ } else {
+ memcpy(&s->str->u.str8[s->len], p, len);
+ s->len += len;
+ }
+ return 0;
+}
+
+static int string_buffer_write16(StringBuffer *s, const uint16_t *p, int len)
+{
+ int c = 0, i;
+
+ for (i = 0; i < len; i++) {
+ c |= p[i];
+ }
+ if (s->len + len > s->size) {
+ if (string_buffer_realloc(s, s->len + len, c))
+ return -1;
+ } else if (!s->is_wide_char && c >= 0x100) {
+ if (string_buffer_widen(s, s->size))
+ return -1;
+ }
+ if (s->is_wide_char) {
+ memcpy(&s->str->u.str16[s->len], p, len << 1);
+ s->len += len;
+ } else {
+ for (i = 0; i < len; i++) {
+ s->str->u.str8[s->len + i] = p[i];
+ }
+ s->len += len;
+ }
+ return 0;
+}
+
+/* appending an ASCII string */
+static int string_buffer_puts8(StringBuffer *s, const char *str)
+{
+ return string_buffer_write8(s, (const uint8_t *)str, strlen(str));
+}
+
+static int string_buffer_concat(StringBuffer *s, const JSString *p,
+ uint32_t from, uint32_t to)
+{
+ if (to <= from)
+ return 0;
+ if (p->is_wide_char)
+ return string_buffer_write16(s, p->u.str16 + from, to - from);
+ else
+ return string_buffer_write8(s, p->u.str8 + from, to - from);
+}
+
+static int string_buffer_concat_value(StringBuffer *s, JSValueConst v)
+{
+ JSString *p;
+ JSValue v1;
+ int res;
+
+ if (s->error_status) {
+ /* prevent exception overload */
+ return -1;
+ }
+ if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
+ v1 = JS_ToString(s->ctx, v);
+ if (JS_IsException(v1))
+ return string_buffer_set_error(s);
+ p = JS_VALUE_GET_STRING(v1);
+ res = string_buffer_concat(s, p, 0, p->len);
+ JS_FreeValue(s->ctx, v1);
+ return res;
+ }
+ p = JS_VALUE_GET_STRING(v);
+ return string_buffer_concat(s, p, 0, p->len);
+}
+
+static int string_buffer_concat_value_free(StringBuffer *s, JSValue v)
+{
+ JSString *p;
+ int res;
+
+ if (s->error_status) {
+ /* prevent exception overload */
+ JS_FreeValue(s->ctx, v);
+ return -1;
+ }
+ if (unlikely(JS_VALUE_GET_TAG(v) != JS_TAG_STRING)) {
+ v = JS_ToStringFree(s->ctx, v);
+ if (JS_IsException(v))
+ return string_buffer_set_error(s);
+ }
+ p = JS_VALUE_GET_STRING(v);
+ res = string_buffer_concat(s, p, 0, p->len);
+ JS_FreeValue(s->ctx, v);
+ return res;
+}
+
+static int string_buffer_fill(StringBuffer *s, int c, int count)
+{
+ /* XXX: optimize */
+ if (s->len + count > s->size) {
+ if (string_buffer_realloc(s, s->len + count, c))
+ return -1;
+ }
+ while (count-- > 0) {
+ if (string_buffer_putc16(s, c))
+ return -1;
+ }
+ return 0;
+}
+
+static JSValue string_buffer_end(StringBuffer *s)
+{
+ JSString *str;
+ str = s->str;
+ if (s->error_status)
+ return JS_EXCEPTION;
+ if (s->len == 0) {
+ js_free(s->ctx, str);
+ s->str = NULL;
+ return JS_AtomToString(s->ctx, JS_ATOM_empty_string);
+ }
+ if (s->len < s->size) {
+ /* smaller size so js_realloc should not fail, but OK if it does */
+ /* XXX: should add some slack to avoid unnecessary calls */
+ /* XXX: might need to use malloc+free to ensure smaller size */
+ str = js_realloc_rt(s->ctx->rt, str, sizeof(JSString) +
+ (s->len << s->is_wide_char) + 1 - s->is_wide_char);
+ if (str == NULL)
+ str = s->str;
+ s->str = str;
+ }
+ if (!s->is_wide_char)
+ str->u.str8[s->len] = 0;
+#ifdef DUMP_LEAKS
+ list_add_tail(&str->link, &s->ctx->rt->string_list);
+#endif
+ str->is_wide_char = s->is_wide_char;
+ str->len = s->len;
+ s->str = NULL;
+ return JS_MKPTR(JS_TAG_STRING, str);
+}
+
+/* create a string from a UTF-8 buffer */
+JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
+{
+ const uint8_t *p, *p_end, *p_start, *p_next;
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+ size_t len1;
+
+ p_start = (const uint8_t *)buf;
+ p_end = p_start + buf_len;
+ p = p_start;
+ while (p < p_end && *p < 128)
+ p++;
+ len1 = p - p_start;
+ if (len1 > JS_STRING_LEN_MAX)
+ return JS_ThrowInternalError(ctx, "string too long");
+ if (p == p_end) {
+ /* ASCII string */
+ return js_new_string8(ctx, (const uint8_t *)buf, buf_len);
+ } else {
+ if (string_buffer_init(ctx, b, buf_len))
+ goto fail;
+ string_buffer_write8(b, p_start, len1);
+ while (p < p_end) {
+ if (*p < 128) {
+ string_buffer_putc8(b, *p++);
+ } else {
+ /* parse utf-8 sequence, return 0xFFFFFFFF for error */
+ c = unicode_from_utf8(p, p_end - p, &p_next);
+ if (c < 0x10000) {
+ p = p_next;
+ } else if (c <= 0x10FFFF) {
+ p = p_next;
+ /* surrogate pair */
+ c -= 0x10000;
+ string_buffer_putc16(b, (c >> 10) + 0xd800);
+ c = (c & 0x3ff) + 0xdc00;
+ } else {
+ /* invalid char */
+ c = 0xfffd;
+ /* skip the invalid chars */
+ /* XXX: seems incorrect. Why not just use c = *p++; ? */
+ while (p < p_end && (*p >= 0x80 && *p < 0xc0))
+ p++;
+ if (p < p_end) {
+ p++;
+ while (p < p_end && (*p >= 0x80 && *p < 0xc0))
+ p++;
+ }
+ }
+ string_buffer_putc16(b, c);
+ }
+ }
+ }
+ return string_buffer_end(b);
+
+ fail:
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ConcatString3(JSContext *ctx, const char *str1,
+ JSValue str2, const char *str3)
+{
+ StringBuffer b_s, *b = &b_s;
+ int len1, len3;
+ JSString *p;
+
+ if (unlikely(JS_VALUE_GET_TAG(str2) != JS_TAG_STRING)) {
+ str2 = JS_ToStringFree(ctx, str2);
+ if (JS_IsException(str2))
+ goto fail;
+ }
+ p = JS_VALUE_GET_STRING(str2);
+ len1 = strlen(str1);
+ len3 = strlen(str3);
+
+ if (string_buffer_init2(ctx, b, len1 + p->len + len3, p->is_wide_char))
+ goto fail;
+
+ string_buffer_write8(b, (const uint8_t *)str1, len1);
+ string_buffer_concat(b, p, 0, p->len);
+ string_buffer_write8(b, (const uint8_t *)str3, len3);
+
+ JS_FreeValue(ctx, str2);
+ return string_buffer_end(b);
+
+ fail:
+ JS_FreeValue(ctx, str2);
+ return JS_EXCEPTION;
+}
+
+JSValue JS_NewString(JSContext *ctx, const char *str)
+{
+ return JS_NewStringLen(ctx, str, strlen(str));
+}
+
+JSValue JS_NewAtomString(JSContext *ctx, const char *str)
+{
+ JSAtom atom = JS_NewAtom(ctx, str);
+ if (atom == JS_ATOM_NULL)
+ return JS_EXCEPTION;
+ JSValue val = JS_AtomToString(ctx, atom);
+ JS_FreeAtom(ctx, atom);
+ return val;
+}
+
+/* return (NULL, 0) if exception. */
+/* return pointer into a JSString with a live ref_count */
+/* cesu8 determines if non-BMP1 codepoints are encoded as 1 or 2 utf-8 sequences */
+const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BOOL cesu8)
+{
+ JSValue val;
+ JSString *str, *str_new;
+ int pos, len, c, c1;
+ uint8_t *q;
+
+ if (JS_VALUE_GET_TAG(val1) != JS_TAG_STRING) {
+ val = JS_ToString(ctx, val1);
+ if (JS_IsException(val))
+ goto fail;
+ } else {
+ val = JS_DupValue(ctx, val1);
+ }
+
+ str = JS_VALUE_GET_STRING(val);
+ len = str->len;
+ if (!str->is_wide_char) {
+ const uint8_t *src = str->u.str8;
+ int count;
+
+ /* count the number of non-ASCII characters */
+ /* Scanning the whole string is required for ASCII strings,
+ and computing the number of non-ASCII bytes is less expensive
+ than testing each byte, hence this method is faster for ASCII
+ strings, which is the most common case.
+ */
+ count = 0;
+ for (pos = 0; pos < len; pos++) {
+ count += src[pos] >> 7;
+ }
+ if (count == 0) {
+ if (plen)
+ *plen = len;
+ return (const char *)src;
+ }
+ str_new = js_alloc_string(ctx, len + count, 0);
+ if (!str_new)
+ goto fail;
+ q = str_new->u.str8;
+ for (pos = 0; pos < len; pos++) {
+ c = src[pos];
+ if (c < 0x80) {
+ *q++ = c;
+ } else {
+ *q++ = (c >> 6) | 0xc0;
+ *q++ = (c & 0x3f) | 0x80;
+ }
+ }
+ } else {
+ const uint16_t *src = str->u.str16;
+ /* Allocate 3 bytes per 16 bit code point. Surrogate pairs may
+ produce 4 bytes but use 2 code points.
+ */
+ str_new = js_alloc_string(ctx, len * 3, 0);
+ if (!str_new)
+ goto fail;
+ q = str_new->u.str8;
+ pos = 0;
+ while (pos < len) {
+ c = src[pos++];
+ if (c < 0x80) {
+ *q++ = c;
+ } else {
+ if (c >= 0xd800 && c < 0xdc00) {
+ if (pos < len && !cesu8) {
+ c1 = src[pos];
+ if (c1 >= 0xdc00 && c1 < 0xe000) {
+ pos++;
+ /* surrogate pair */
+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
+ } else {
+ /* Keep unmatched surrogate code points */
+ /* c = 0xfffd; */ /* error */
+ }
+ } else {
+ /* Keep unmatched surrogate code points */
+ /* c = 0xfffd; */ /* error */
+ }
+ }
+ q += unicode_to_utf8(q, c);
+ }
+ }
+ }
+
+ *q = '\0';
+ str_new->len = q - str_new->u.str8;
+ JS_FreeValue(ctx, val);
+ if (plen)
+ *plen = str_new->len;
+ return (const char *)str_new->u.str8;
+ fail:
+ if (plen)
+ *plen = 0;
+ return NULL;
+}
+
+void JS_FreeCString(JSContext *ctx, const char *ptr)
+{
+ JSString *p;
+ if (!ptr)
+ return;
+ /* purposely removing constness */
+ p = (JSString *)(void *)(ptr - offsetof(JSString, u));
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+}
+
+static int memcmp16_8(const uint16_t *src1, const uint8_t *src2, int len)
+{
+ int c, i;
+ for(i = 0; i < len; i++) {
+ c = src1[i] - src2[i];
+ if (c != 0)
+ return c;
+ }
+ return 0;
+}
+
+static int memcmp16(const uint16_t *src1, const uint16_t *src2, int len)
+{
+ int c, i;
+ for(i = 0; i < len; i++) {
+ c = src1[i] - src2[i];
+ if (c != 0)
+ return c;
+ }
+ return 0;
+}
+
+static int js_string_memcmp(const JSString *p1, const JSString *p2, int len)
+{
+ int res;
+
+ if (likely(!p1->is_wide_char)) {
+ if (likely(!p2->is_wide_char))
+ res = memcmp(p1->u.str8, p2->u.str8, len);
+ else
+ res = -memcmp16_8(p2->u.str16, p1->u.str8, len);
+ } else {
+ if (!p2->is_wide_char)
+ res = memcmp16_8(p1->u.str16, p2->u.str8, len);
+ else
+ res = memcmp16(p1->u.str16, p2->u.str16, len);
+ }
+ return res;
+}
+
+/* return < 0, 0 or > 0 */
+static int js_string_compare(JSContext *ctx,
+ const JSString *p1, const JSString *p2)
+{
+ int res, len;
+ len = min_int(p1->len, p2->len);
+ res = js_string_memcmp(p1, p2, len);
+ if (res == 0) {
+ if (p1->len == p2->len)
+ res = 0;
+ else if (p1->len < p2->len)
+ res = -1;
+ else
+ res = 1;
+ }
+ return res;
+}
+
+static void copy_str16(uint16_t *dst, const JSString *p, int offset, int len)
+{
+ if (p->is_wide_char) {
+ memcpy(dst, p->u.str16 + offset, len * 2);
+ } else {
+ const uint8_t *src1 = p->u.str8 + offset;
+ int i;
+
+ for(i = 0; i < len; i++)
+ dst[i] = src1[i];
+ }
+}
+
+static JSValue JS_ConcatString1(JSContext *ctx,
+ const JSString *p1, const JSString *p2)
+{
+ JSString *p;
+ uint32_t len;
+ int is_wide_char;
+
+ len = p1->len + p2->len;
+ if (len > JS_STRING_LEN_MAX)
+ return JS_ThrowInternalError(ctx, "string too long");
+ is_wide_char = p1->is_wide_char | p2->is_wide_char;
+ p = js_alloc_string(ctx, len, is_wide_char);
+ if (!p)
+ return JS_EXCEPTION;
+ if (!is_wide_char) {
+ memcpy(p->u.str8, p1->u.str8, p1->len);
+ memcpy(p->u.str8 + p1->len, p2->u.str8, p2->len);
+ p->u.str8[len] = '\0';
+ } else {
+ copy_str16(p->u.str16, p1, 0, p1->len);
+ copy_str16(p->u.str16 + p1->len, p2, 0, p2->len);
+ }
+ return JS_MKPTR(JS_TAG_STRING, p);
+}
+
+/* op1 and op2 are converted to strings. For convience, op1 or op2 =
+ JS_EXCEPTION are accepted and return JS_EXCEPTION. */
+static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2)
+{
+ JSValue ret;
+ JSString *p1, *p2;
+
+ if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_STRING)) {
+ op1 = JS_ToStringFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ return JS_EXCEPTION;
+ }
+ }
+ if (unlikely(JS_VALUE_GET_TAG(op2) != JS_TAG_STRING)) {
+ op2 = JS_ToStringFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ return JS_EXCEPTION;
+ }
+ }
+ p1 = JS_VALUE_GET_STRING(op1);
+ p2 = JS_VALUE_GET_STRING(op2);
+
+ /* XXX: could also check if p1 is empty */
+ if (p2->len == 0) {
+ goto ret_op1;
+ }
+ if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char
+ && js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) {
+ /* Concatenate in place in available space at the end of p1 */
+ if (p1->is_wide_char) {
+ memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1);
+ p1->len += p2->len;
+ } else {
+ memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len);
+ p1->len += p2->len;
+ p1->u.str8[p1->len] = '\0';
+ }
+ ret_op1:
+ JS_FreeValue(ctx, op2);
+ return op1;
+ }
+ ret = JS_ConcatString1(ctx, p1, p2);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return ret;
+}
+
+/* Shape support */
+
+static inline size_t get_shape_size(size_t hash_size, size_t prop_size)
+{
+ return hash_size * sizeof(uint32_t) + sizeof(JSShape) +
+ prop_size * sizeof(JSShapeProperty);
+}
+
+static inline JSShape *get_shape_from_alloc(void *sh_alloc, size_t hash_size)
+{
+ return (JSShape *)(void *)((uint32_t *)sh_alloc + hash_size);
+}
+
+static inline uint32_t *prop_hash_end(JSShape *sh)
+{
+ return (uint32_t *)sh;
+}
+
+static inline void *get_alloc_from_shape(JSShape *sh)
+{
+ return prop_hash_end(sh) - ((intptr_t)sh->prop_hash_mask + 1);
+}
+
+static inline JSShapeProperty *get_shape_prop(JSShape *sh)
+{
+ return sh->prop;
+}
+
+static int init_shape_hash(JSRuntime *rt)
+{
+ rt->shape_hash_bits = 4; /* 16 shapes */
+ rt->shape_hash_size = 1 << rt->shape_hash_bits;
+ rt->shape_hash_count = 0;
+ rt->shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
+ rt->shape_hash_size);
+ if (!rt->shape_hash)
+ return -1;
+ return 0;
+}
+
+/* same magic hash multiplier as the Linux kernel */
+static uint32_t shape_hash(uint32_t h, uint32_t val)
+{
+ return (h + val) * 0x9e370001;
+}
+
+/* truncate the shape hash to 'hash_bits' bits */
+static uint32_t get_shape_hash(uint32_t h, int hash_bits)
+{
+ return h >> (32 - hash_bits);
+}
+
+static uint32_t shape_initial_hash(JSObject *proto)
+{
+ uint32_t h;
+ h = shape_hash(1, (uintptr_t)proto);
+ if (sizeof(proto) > 4)
+ h = shape_hash(h, (uint64_t)(uintptr_t)proto >> 32);
+ return h;
+}
+
+static int resize_shape_hash(JSRuntime *rt, int new_shape_hash_bits)
+{
+ int new_shape_hash_size, i;
+ uint32_t h;
+ JSShape **new_shape_hash, *sh, *sh_next;
+
+ new_shape_hash_size = 1 << new_shape_hash_bits;
+ new_shape_hash = js_mallocz_rt(rt, sizeof(rt->shape_hash[0]) *
+ new_shape_hash_size);
+ if (!new_shape_hash)
+ return -1;
+ for(i = 0; i < rt->shape_hash_size; i++) {
+ for(sh = rt->shape_hash[i]; sh != NULL; sh = sh_next) {
+ sh_next = sh->shape_hash_next;
+ h = get_shape_hash(sh->hash, new_shape_hash_bits);
+ sh->shape_hash_next = new_shape_hash[h];
+ new_shape_hash[h] = sh;
+ }
+ }
+ js_free_rt(rt, rt->shape_hash);
+ rt->shape_hash_bits = new_shape_hash_bits;
+ rt->shape_hash_size = new_shape_hash_size;
+ rt->shape_hash = new_shape_hash;
+ return 0;
+}
+
+static void js_shape_hash_link(JSRuntime *rt, JSShape *sh)
+{
+ uint32_t h;
+ h = get_shape_hash(sh->hash, rt->shape_hash_bits);
+ sh->shape_hash_next = rt->shape_hash[h];
+ rt->shape_hash[h] = sh;
+ rt->shape_hash_count++;
+}
+
+static void js_shape_hash_unlink(JSRuntime *rt, JSShape *sh)
+{
+ uint32_t h;
+ JSShape **psh;
+
+ h = get_shape_hash(sh->hash, rt->shape_hash_bits);
+ psh = &rt->shape_hash[h];
+ while (*psh != sh)
+ psh = &(*psh)->shape_hash_next;
+ *psh = sh->shape_hash_next;
+ rt->shape_hash_count--;
+}
+
+/* create a new empty shape with prototype 'proto' */
+static no_inline JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
+ int hash_size, int prop_size)
+{
+ JSRuntime *rt = ctx->rt;
+ void *sh_alloc;
+ JSShape *sh;
+
+ /* resize the shape hash table if necessary */
+ if (2 * (rt->shape_hash_count + 1) > rt->shape_hash_size) {
+ resize_shape_hash(rt, rt->shape_hash_bits + 1);
+ }
+
+ sh_alloc = js_malloc(ctx, get_shape_size(hash_size, prop_size));
+ if (!sh_alloc)
+ return NULL;
+ sh = get_shape_from_alloc(sh_alloc, hash_size);
+ sh->header.ref_count = 1;
+ add_gc_object(rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
+ if (proto)
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, proto));
+ sh->proto = proto;
+ memset(prop_hash_end(sh) - hash_size, 0, sizeof(prop_hash_end(sh)[0]) *
+ hash_size);
+ sh->prop_hash_mask = hash_size - 1;
+ sh->prop_size = prop_size;
+ sh->prop_count = 0;
+ sh->deleted_prop_count = 0;
+
+ /* insert in the hash table */
+ sh->hash = shape_initial_hash(proto);
+ sh->is_hashed = TRUE;
+ sh->has_small_array_index = FALSE;
+ js_shape_hash_link(ctx->rt, sh);
+ return sh;
+}
+
+static JSShape *js_new_shape(JSContext *ctx, JSObject *proto)
+{
+ return js_new_shape2(ctx, proto, JS_PROP_INITIAL_HASH_SIZE,
+ JS_PROP_INITIAL_SIZE);
+}
+
+/* The shape is cloned. The new shape is not inserted in the shape
+ hash table */
+static JSShape *js_clone_shape(JSContext *ctx, JSShape *sh1)
+{
+ JSShape *sh;
+ void *sh_alloc, *sh_alloc1;
+ size_t size;
+ JSShapeProperty *pr;
+ uint32_t i, hash_size;
+
+ hash_size = sh1->prop_hash_mask + 1;
+ size = get_shape_size(hash_size, sh1->prop_size);
+ sh_alloc = js_malloc(ctx, size);
+ if (!sh_alloc)
+ return NULL;
+ sh_alloc1 = get_alloc_from_shape(sh1);
+ memcpy(sh_alloc, sh_alloc1, size);
+ sh = get_shape_from_alloc(sh_alloc, hash_size);
+ sh->header.ref_count = 1;
+ add_gc_object(ctx->rt, &sh->header, JS_GC_OBJ_TYPE_SHAPE);
+ sh->is_hashed = FALSE;
+ if (sh->proto) {
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
+ }
+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
+ JS_DupAtom(ctx, pr->atom);
+ }
+ return sh;
+}
+
+static JSShape *js_dup_shape(JSShape *sh)
+{
+ sh->header.ref_count++;
+ return sh;
+}
+
+static void js_free_shape0(JSRuntime *rt, JSShape *sh)
+{
+ uint32_t i;
+ JSShapeProperty *pr;
+
+ assert(sh->header.ref_count == 0);
+ if (sh->is_hashed)
+ js_shape_hash_unlink(rt, sh);
+ if (sh->proto != NULL) {
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
+ }
+ pr = get_shape_prop(sh);
+ for(i = 0; i < sh->prop_count; i++) {
+ JS_FreeAtomRT(rt, pr->atom);
+ pr++;
+ }
+ remove_gc_object(&sh->header);
+ js_free_rt(rt, get_alloc_from_shape(sh));
+}
+
+static void js_free_shape(JSRuntime *rt, JSShape *sh)
+{
+ if (unlikely(--sh->header.ref_count <= 0)) {
+ js_free_shape0(rt, sh);
+ }
+}
+
+static void js_free_shape_null(JSRuntime *rt, JSShape *sh)
+{
+ if (sh)
+ js_free_shape(rt, sh);
+}
+
+/* make space to hold at least 'count' properties */
+static no_inline int resize_properties(JSContext *ctx, JSShape **psh,
+ JSObject *p, uint32_t count)
+{
+ JSShape *sh;
+ uint32_t new_size, new_hash_size, new_hash_mask, i;
+ JSShapeProperty *pr;
+ void *sh_alloc;
+ intptr_t h;
+
+ sh = *psh;
+ new_size = max_int(count, sh->prop_size * 3 / 2);
+ /* Reallocate prop array first to avoid crash or size inconsistency
+ in case of memory allocation failure */
+ if (p) {
+ JSProperty *new_prop;
+ new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
+ if (unlikely(!new_prop))
+ return -1;
+ p->prop = new_prop;
+ }
+ new_hash_size = sh->prop_hash_mask + 1;
+ while (new_hash_size < new_size)
+ new_hash_size = 2 * new_hash_size;
+ if (new_hash_size != (sh->prop_hash_mask + 1)) {
+ JSShape *old_sh;
+ /* resize the hash table and the properties */
+ old_sh = sh;
+ sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
+ if (!sh_alloc)
+ return -1;
+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);
+ list_del(&old_sh->header.link);
+ /* copy all the fields and the properties */
+ memcpy(sh, old_sh,
+ sizeof(JSShape) + sizeof(sh->prop[0]) * old_sh->prop_count);
+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+ new_hash_mask = new_hash_size - 1;
+ sh->prop_hash_mask = new_hash_mask;
+ memset(prop_hash_end(sh) - new_hash_size, 0,
+ sizeof(prop_hash_end(sh)[0]) * new_hash_size);
+ for(i = 0, pr = sh->prop; i < sh->prop_count; i++, pr++) {
+ if (pr->atom != JS_ATOM_NULL) {
+ h = ((uintptr_t)pr->atom & new_hash_mask);
+ pr->hash_next = prop_hash_end(sh)[-h - 1];
+ prop_hash_end(sh)[-h - 1] = i + 1;
+ }
+ }
+ js_free(ctx, get_alloc_from_shape(old_sh));
+ } else {
+ /* only resize the properties */
+ list_del(&sh->header.link);
+ sh_alloc = js_realloc(ctx, get_alloc_from_shape(sh),
+ get_shape_size(new_hash_size, new_size));
+ if (unlikely(!sh_alloc)) {
+ /* insert again in the GC list */
+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+ return -1;
+ }
+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);
+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+ }
+ *psh = sh;
+ sh->prop_size = new_size;
+ return 0;
+}
+
+/* remove the deleted properties. */
+static int compact_properties(JSContext *ctx, JSObject *p)
+{
+ JSShape *sh, *old_sh;
+ void *sh_alloc;
+ intptr_t h;
+ uint32_t new_hash_size, i, j, new_hash_mask, new_size;
+ JSShapeProperty *old_pr, *pr;
+ JSProperty *prop, *new_prop;
+
+ sh = p->shape;
+ assert(!sh->is_hashed);
+
+ new_size = max_int(JS_PROP_INITIAL_SIZE,
+ sh->prop_count - sh->deleted_prop_count);
+ assert(new_size <= sh->prop_size);
+
+ new_hash_size = sh->prop_hash_mask + 1;
+ while ((new_hash_size / 2) >= new_size)
+ new_hash_size = new_hash_size / 2;
+ new_hash_mask = new_hash_size - 1;
+
+ /* resize the hash table and the properties */
+ old_sh = sh;
+ sh_alloc = js_malloc(ctx, get_shape_size(new_hash_size, new_size));
+ if (!sh_alloc)
+ return -1;
+ sh = get_shape_from_alloc(sh_alloc, new_hash_size);
+ list_del(&old_sh->header.link);
+ memcpy(sh, old_sh, sizeof(JSShape));
+ list_add_tail(&sh->header.link, &ctx->rt->gc_obj_list);
+
+ memset(prop_hash_end(sh) - new_hash_size, 0,
+ sizeof(prop_hash_end(sh)[0]) * new_hash_size);
+
+ j = 0;
+ old_pr = old_sh->prop;
+ pr = sh->prop;
+ prop = p->prop;
+ for(i = 0; i < sh->prop_count; i++) {
+ if (old_pr->atom != JS_ATOM_NULL) {
+ pr->atom = old_pr->atom;
+ pr->flags = old_pr->flags;
+ h = ((uintptr_t)old_pr->atom & new_hash_mask);
+ pr->hash_next = prop_hash_end(sh)[-h - 1];
+ prop_hash_end(sh)[-h - 1] = j + 1;
+ prop[j] = prop[i];
+ j++;
+ pr++;
+ }
+ old_pr++;
+ }
+ assert(j == (sh->prop_count - sh->deleted_prop_count));
+ sh->prop_hash_mask = new_hash_mask;
+ sh->prop_size = new_size;
+ sh->deleted_prop_count = 0;
+ sh->prop_count = j;
+
+ p->shape = sh;
+ js_free(ctx, get_alloc_from_shape(old_sh));
+
+ /* reduce the size of the object properties */
+ new_prop = js_realloc(ctx, p->prop, sizeof(new_prop[0]) * new_size);
+ if (new_prop)
+ p->prop = new_prop;
+ return 0;
+}
+
+static int add_shape_property(JSContext *ctx, JSShape **psh,
+ JSObject *p, JSAtom atom, int prop_flags)
+{
+ JSRuntime *rt = ctx->rt;
+ JSShape *sh = *psh;
+ JSShapeProperty *pr, *prop;
+ uint32_t hash_mask, new_shape_hash = 0;
+ intptr_t h;
+
+ /* update the shape hash */
+ if (sh->is_hashed) {
+ js_shape_hash_unlink(rt, sh);
+ new_shape_hash = shape_hash(shape_hash(sh->hash, atom), prop_flags);
+ }
+
+ if (unlikely(sh->prop_count >= sh->prop_size)) {
+ if (resize_properties(ctx, psh, p, sh->prop_count + 1)) {
+ /* in case of error, reinsert in the hash table.
+ sh is still valid if resize_properties() failed */
+ if (sh->is_hashed)
+ js_shape_hash_link(rt, sh);
+ return -1;
+ }
+ sh = *psh;
+ }
+ if (sh->is_hashed) {
+ sh->hash = new_shape_hash;
+ js_shape_hash_link(rt, sh);
+ }
+ /* Initialize the new shape property.
+ The object property at p->prop[sh->prop_count] is uninitialized */
+ prop = get_shape_prop(sh);
+ pr = &prop[sh->prop_count++];
+ pr->atom = JS_DupAtom(ctx, atom);
+ pr->flags = prop_flags;
+ sh->has_small_array_index |= JS_AtomIsTaggedInt(atom);
+ /* add in hash table */
+ hash_mask = sh->prop_hash_mask;
+ h = atom & hash_mask;
+ pr->hash_next = prop_hash_end(sh)[-h - 1];
+ prop_hash_end(sh)[-h - 1] = sh->prop_count;
+ return 0;
+}
+
+/* find a hashed empty shape matching the prototype. Return NULL if
+ not found */
+static JSShape *find_hashed_shape_proto(JSRuntime *rt, JSObject *proto)
+{
+ JSShape *sh1;
+ uint32_t h, h1;
+
+ h = shape_initial_hash(proto);
+ h1 = get_shape_hash(h, rt->shape_hash_bits);
+ for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
+ if (sh1->hash == h &&
+ sh1->proto == proto &&
+ sh1->prop_count == 0) {
+ return sh1;
+ }
+ }
+ return NULL;
+}
+
+/* find a hashed shape matching sh + (prop, prop_flags). Return NULL if
+ not found */
+static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
+ JSAtom atom, int prop_flags)
+{
+ JSShape *sh1;
+ uint32_t h, h1, i, n;
+
+ h = sh->hash;
+ h = shape_hash(h, atom);
+ h = shape_hash(h, prop_flags);
+ h1 = get_shape_hash(h, rt->shape_hash_bits);
+ for(sh1 = rt->shape_hash[h1]; sh1 != NULL; sh1 = sh1->shape_hash_next) {
+ /* we test the hash first so that the rest is done only if the
+ shapes really match */
+ if (sh1->hash == h &&
+ sh1->proto == sh->proto &&
+ sh1->prop_count == ((n = sh->prop_count) + 1)) {
+ for(i = 0; i < n; i++) {
+ if (unlikely(sh1->prop[i].atom != sh->prop[i].atom) ||
+ unlikely(sh1->prop[i].flags != sh->prop[i].flags))
+ goto next;
+ }
+ if (unlikely(sh1->prop[n].atom != atom) ||
+ unlikely(sh1->prop[n].flags != prop_flags))
+ goto next;
+ return sh1;
+ }
+ next: ;
+ }
+ return NULL;
+}
+
+static maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
+{
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ int j;
+
+ /* XXX: should output readable class prototype */
+ printf("%5d %3d%c %14p %5d %5d", i,
+ sh->header.ref_count, " *"[sh->is_hashed],
+ (void *)sh->proto, sh->prop_size, sh->prop_count);
+ for(j = 0; j < sh->prop_count; j++) {
+ printf(" %s", JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf),
+ sh->prop[j].atom));
+ }
+ printf("\n");
+}
+
+static maybe_unused void JS_DumpShapes(JSRuntime *rt)
+{
+ int i;
+ JSShape *sh;
+ struct list_head *el;
+ JSObject *p;
+ JSGCObjectHeader *gp;
+
+ printf("JSShapes: {\n");
+ printf("%5s %4s %14s %5s %5s %s\n", "SLOT", "REFS", "PROTO", "SIZE", "COUNT", "PROPS");
+ for(i = 0; i < rt->shape_hash_size; i++) {
+ for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
+ JS_DumpShape(rt, i, sh);
+ assert(sh->is_hashed);
+ }
+ }
+ /* dump non-hashed shapes */
+ list_for_each(el, &rt->gc_obj_list) {
+ gp = list_entry(el, JSGCObjectHeader, link);
+ if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+ p = (JSObject *)gp;
+ if (!p->shape->is_hashed) {
+ JS_DumpShape(rt, -1, p->shape);
+ }
+ }
+ }
+ printf("}\n");
+}
+
+static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id)
+{
+ JSObject *p;
+
+ js_trigger_gc(ctx->rt, sizeof(JSObject));
+ p = js_malloc(ctx, sizeof(JSObject));
+ if (unlikely(!p))
+ goto fail;
+ p->class_id = class_id;
+ p->extensible = TRUE;
+ p->free_mark = 0;
+ p->is_exotic = 0;
+ p->fast_array = 0;
+ p->is_constructor = 0;
+ p->is_uncatchable_error = 0;
+ p->tmp_mark = 0;
+ p->is_HTMLDDA = 0;
+ p->first_weak_ref = NULL;
+ p->u.opaque = NULL;
+ p->shape = sh;
+ p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
+ if (unlikely(!p->prop)) {
+ js_free(ctx, p);
+ fail:
+ js_free_shape(ctx->rt, sh);
+ return JS_EXCEPTION;
+ }
+
+ switch(class_id) {
+ case JS_CLASS_OBJECT:
+ break;
+ case JS_CLASS_ARRAY:
+ {
+ JSProperty *pr;
+ p->is_exotic = 1;
+ p->fast_array = 1;
+ p->u.array.u.values = NULL;
+ p->u.array.count = 0;
+ p->u.array.u1.size = 0;
+ /* the length property is always the first one */
+ if (likely(sh == ctx->array_shape)) {
+ pr = &p->prop[0];
+ } else {
+ /* only used for the first array */
+ /* cannot fail */
+ pr = add_property(ctx, p, JS_ATOM_length,
+ JS_PROP_WRITABLE | JS_PROP_LENGTH);
+ }
+ pr->u.value = JS_NewInt32(ctx, 0);
+ }
+ break;
+ case JS_CLASS_C_FUNCTION:
+ p->prop[0].u.value = JS_UNDEFINED;
+ break;
+ case JS_CLASS_ARGUMENTS:
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ case JS_CLASS_FLOAT64_ARRAY:
+ p->is_exotic = 1;
+ p->fast_array = 1;
+ p->u.array.u.ptr = NULL;
+ p->u.array.count = 0;
+ break;
+ case JS_CLASS_DATAVIEW:
+ p->u.array.u.ptr = NULL;
+ p->u.array.count = 0;
+ break;
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_STRING:
+ case JS_CLASS_BOOLEAN:
+ case JS_CLASS_SYMBOL:
+ case JS_CLASS_DATE:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT:
+ case JS_CLASS_BIG_FLOAT:
+ case JS_CLASS_BIG_DECIMAL:
+#endif
+ p->u.object_data = JS_UNDEFINED;
+ goto set_exotic;
+ case JS_CLASS_REGEXP:
+ p->u.regexp.pattern = NULL;
+ p->u.regexp.bytecode = NULL;
+ goto set_exotic;
+ default:
+ set_exotic:
+ if (ctx->rt->class_array[class_id].exotic) {
+ p->is_exotic = 1;
+ }
+ break;
+ }
+ p->header.ref_count = 1;
+ add_gc_object(ctx->rt, &p->header, JS_GC_OBJ_TYPE_JS_OBJECT);
+ return JS_MKPTR(JS_TAG_OBJECT, p);
+}
+
+static JSObject *get_proto_obj(JSValueConst proto_val)
+{
+ if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT)
+ return NULL;
+ else
+ return JS_VALUE_GET_OBJ(proto_val);
+}
+
+/* WARNING: proto must be an object or JS_NULL */
+JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,
+ JSClassID class_id)
+{
+ JSShape *sh;
+ JSObject *proto;
+
+ proto = get_proto_obj(proto_val);
+ sh = find_hashed_shape_proto(ctx->rt, proto);
+ if (likely(sh)) {
+ sh = js_dup_shape(sh);
+ } else {
+ sh = js_new_shape(ctx, proto);
+ if (!sh)
+ return JS_EXCEPTION;
+ }
+ return JS_NewObjectFromShape(ctx, sh, class_id);
+}
+
+#if 0
+static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(obj);
+ switch(p->class_id) {
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_STRING:
+ case JS_CLASS_BOOLEAN:
+ case JS_CLASS_SYMBOL:
+ case JS_CLASS_DATE:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT:
+ case JS_CLASS_BIG_FLOAT:
+ case JS_CLASS_BIG_DECIMAL:
+#endif
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_UNDEFINED;
+}
+#endif
+
+static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val)
+{
+ JSObject *p;
+
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(obj);
+ switch(p->class_id) {
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_STRING:
+ case JS_CLASS_BOOLEAN:
+ case JS_CLASS_SYMBOL:
+ case JS_CLASS_DATE:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT:
+ case JS_CLASS_BIG_FLOAT:
+ case JS_CLASS_BIG_DECIMAL:
+#endif
+ JS_FreeValue(ctx, p->u.object_data);
+ p->u.object_data = val;
+ return 0;
+ }
+ }
+ JS_FreeValue(ctx, val);
+ if (!JS_IsException(obj))
+ JS_ThrowTypeError(ctx, "invalid object type");
+ return -1;
+}
+
+JSValue JS_NewObjectClass(JSContext *ctx, int class_id)
+{
+ return JS_NewObjectProtoClass(ctx, ctx->class_proto[class_id], class_id);
+}
+
+JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto)
+{
+ return JS_NewObjectProtoClass(ctx, proto, JS_CLASS_OBJECT);
+}
+
+JSValue JS_NewArray(JSContext *ctx)
+{
+ return JS_NewObjectFromShape(ctx, js_dup_shape(ctx->array_shape),
+ JS_CLASS_ARRAY);
+}
+
+JSValue JS_NewObject(JSContext *ctx)
+{
+ /* inline JS_NewObjectClass(ctx, JS_CLASS_OBJECT); */
+ return JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT], JS_CLASS_OBJECT);
+}
+
+static void js_function_set_properties(JSContext *ctx, JSValueConst func_obj,
+ JSAtom name, int len)
+{
+ /* ES6 feature non compatible with ES5.1: length is configurable */
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length, JS_NewInt32(ctx, len),
+ JS_PROP_CONFIGURABLE);
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name,
+ JS_AtomToString(ctx, name), JS_PROP_CONFIGURABLE);
+}
+
+static BOOL js_class_has_bytecode(JSClassID class_id)
+{
+ return (class_id == JS_CLASS_BYTECODE_FUNCTION ||
+ class_id == JS_CLASS_GENERATOR_FUNCTION ||
+ class_id == JS_CLASS_ASYNC_FUNCTION ||
+ class_id == JS_CLASS_ASYNC_GENERATOR_FUNCTION);
+}
+
+/* return NULL without exception if not a function or no bytecode */
+static JSFunctionBytecode *JS_GetFunctionBytecode(JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return NULL;
+ p = JS_VALUE_GET_OBJ(val);
+ if (!js_class_has_bytecode(p->class_id))
+ return NULL;
+ return p->u.func.function_bytecode;
+}
+
+static void js_method_set_home_object(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst home_obj)
+{
+ JSObject *p, *p1;
+ JSFunctionBytecode *b;
+
+ if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
+ return;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ if (!js_class_has_bytecode(p->class_id))
+ return;
+ b = p->u.func.function_bytecode;
+ if (b->need_home_object) {
+ p1 = p->u.func.home_object;
+ if (p1) {
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
+ }
+ if (JS_VALUE_GET_TAG(home_obj) == JS_TAG_OBJECT)
+ p1 = JS_VALUE_GET_OBJ(JS_DupValue(ctx, home_obj));
+ else
+ p1 = NULL;
+ p->u.func.home_object = p1;
+ }
+}
+
+static JSValue js_get_function_name(JSContext *ctx, JSAtom name)
+{
+ JSValue name_str;
+
+ name_str = JS_AtomToString(ctx, name);
+ if (JS_AtomSymbolHasDescription(ctx, name)) {
+ name_str = JS_ConcatString3(ctx, "[", name_str, "]");
+ }
+ return name_str;
+}
+
+/* Modify the name of a method according to the atom and
+ 'flags'. 'flags' is a bitmask of JS_PROP_HAS_GET and
+ JS_PROP_HAS_SET. Also set the home object of the method.
+ Return < 0 if exception. */
+static int js_method_set_properties(JSContext *ctx, JSValueConst func_obj,
+ JSAtom name, int flags, JSValueConst home_obj)
+{
+ JSValue name_str;
+
+ name_str = js_get_function_name(ctx, name);
+ if (flags & JS_PROP_HAS_GET) {
+ name_str = JS_ConcatString3(ctx, "get ", name_str, "");
+ } else if (flags & JS_PROP_HAS_SET) {
+ name_str = JS_ConcatString3(ctx, "set ", name_str, "");
+ }
+ if (JS_IsException(name_str))
+ return -1;
+ if (JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name_str,
+ JS_PROP_CONFIGURABLE) < 0)
+ return -1;
+ js_method_set_home_object(ctx, func_obj, home_obj);
+ return 0;
+}
+
+/* Note: at least 'length' arguments will be readable in 'argv' */
+static JSValue JS_NewCFunction3(JSContext *ctx, JSCFunction *func,
+ const char *name,
+ int length, JSCFunctionEnum cproto, int magic,
+ JSValueConst proto_val)
+{
+ JSValue func_obj;
+ JSObject *p;
+ JSAtom name_atom;
+
+ func_obj = JS_NewObjectProtoClass(ctx, proto_val, JS_CLASS_C_FUNCTION);
+ if (JS_IsException(func_obj))
+ return func_obj;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ p->u.cfunc.realm = JS_DupContext(ctx);
+ p->u.cfunc.c_function.generic = func;
+ p->u.cfunc.length = length;
+ p->u.cfunc.cproto = cproto;
+ p->u.cfunc.magic = magic;
+ p->is_constructor = (cproto == JS_CFUNC_constructor ||
+ cproto == JS_CFUNC_constructor_magic ||
+ cproto == JS_CFUNC_constructor_or_func ||
+ cproto == JS_CFUNC_constructor_or_func_magic);
+ if (!name)
+ name = "";
+ name_atom = JS_NewAtom(ctx, name);
+ js_function_set_properties(ctx, func_obj, name_atom, length);
+ JS_FreeAtom(ctx, name_atom);
+ return func_obj;
+}
+
+/* Note: at least 'length' arguments will be readable in 'argv' */
+JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,
+ const char *name,
+ int length, JSCFunctionEnum cproto, int magic)
+{
+ return JS_NewCFunction3(ctx, func, name, length, cproto, magic,
+ ctx->function_proto);
+}
+
+typedef struct JSCFunctionDataRecord {
+ JSCFunctionData *func;
+ uint8_t length;
+ uint8_t data_len;
+ uint16_t magic;
+ JSValue data[0];
+} JSCFunctionDataRecord;
+
+static void js_c_function_data_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
+ int i;
+
+ if (s) {
+ for(i = 0; i < s->data_len; i++) {
+ JS_FreeValueRT(rt, s->data[i]);
+ }
+ js_free_rt(rt, s);
+ }
+}
+
+static void js_c_function_data_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSCFunctionDataRecord *s = JS_GetOpaque(val, JS_CLASS_C_FUNCTION_DATA);
+ int i;
+
+ if (s) {
+ for(i = 0; i < s->data_len; i++) {
+ JS_MarkValue(rt, s->data[i], mark_func);
+ }
+ }
+}
+
+static JSValue js_c_function_data_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int flags)
+{
+ JSCFunctionDataRecord *s = JS_GetOpaque(func_obj, JS_CLASS_C_FUNCTION_DATA);
+ JSValueConst *arg_buf;
+ int i;
+
+ /* XXX: could add the function on the stack for debug */
+ if (unlikely(argc < s->length)) {
+ arg_buf = alloca(sizeof(arg_buf[0]) * s->length);
+ for(i = 0; i < argc; i++)
+ arg_buf[i] = argv[i];
+ for(i = argc; i < s->length; i++)
+ arg_buf[i] = JS_UNDEFINED;
+ } else {
+ arg_buf = argv;
+ }
+
+ return s->func(ctx, this_val, argc, arg_buf, s->magic, s->data);
+}
+
+JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
+ int length, int magic, int data_len,
+ JSValueConst *data)
+{
+ JSCFunctionDataRecord *s;
+ JSValue func_obj;
+ int i;
+
+ func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
+ JS_CLASS_C_FUNCTION_DATA);
+ if (JS_IsException(func_obj))
+ return func_obj;
+ s = js_malloc(ctx, sizeof(*s) + data_len * sizeof(JSValue));
+ if (!s) {
+ JS_FreeValue(ctx, func_obj);
+ return JS_EXCEPTION;
+ }
+ s->func = func;
+ s->length = length;
+ s->data_len = data_len;
+ s->magic = magic;
+ for(i = 0; i < data_len; i++)
+ s->data[i] = JS_DupValue(ctx, data[i]);
+ JS_SetOpaque(func_obj, s);
+ js_function_set_properties(ctx, func_obj,
+ JS_ATOM_empty_string, length);
+ return func_obj;
+}
+
+static JSContext *js_autoinit_get_realm(JSProperty *pr)
+{
+ return (JSContext *)(pr->u.init.realm_and_id & ~3);
+}
+
+static JSAutoInitIDEnum js_autoinit_get_id(JSProperty *pr)
+{
+ return pr->u.init.realm_and_id & 3;
+}
+
+static void js_autoinit_free(JSRuntime *rt, JSProperty *pr)
+{
+ JS_FreeContext(js_autoinit_get_realm(pr));
+}
+
+static void js_autoinit_mark(JSRuntime *rt, JSProperty *pr,
+ JS_MarkFunc *mark_func)
+{
+ mark_func(rt, &js_autoinit_get_realm(pr)->header);
+}
+
+static void free_property(JSRuntime *rt, JSProperty *pr, int prop_flags)
+{
+ if (unlikely(prop_flags & JS_PROP_TMASK)) {
+ if ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ if (pr->u.getset.getter)
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
+ if (pr->u.getset.setter)
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+ } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ free_var_ref(rt, pr->u.var_ref);
+ } else if ((prop_flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ js_autoinit_free(rt, pr);
+ }
+ } else {
+ JS_FreeValueRT(rt, pr->u.value);
+ }
+}
+
+static force_inline JSShapeProperty *find_own_property1(JSObject *p,
+ JSAtom atom)
+{
+ JSShape *sh;
+ JSShapeProperty *pr, *prop;
+ intptr_t h;
+ sh = p->shape;
+ h = (uintptr_t)atom & sh->prop_hash_mask;
+ h = prop_hash_end(sh)[-h - 1];
+ prop = get_shape_prop(sh);
+ while (h) {
+ pr = &prop[h - 1];
+ if (likely(pr->atom == atom)) {
+ return pr;
+ }
+ h = pr->hash_next;
+ }
+ return NULL;
+}
+
+static force_inline JSShapeProperty *find_own_property(JSProperty **ppr,
+ JSObject *p,
+ JSAtom atom)
+{
+ JSShape *sh;
+ JSShapeProperty *pr, *prop;
+ intptr_t h;
+ sh = p->shape;
+ h = (uintptr_t)atom & sh->prop_hash_mask;
+ h = prop_hash_end(sh)[-h - 1];
+ prop = get_shape_prop(sh);
+ while (h) {
+ pr = &prop[h - 1];
+ if (likely(pr->atom == atom)) {
+ *ppr = &p->prop[h - 1];
+ /* the compiler should be able to assume that pr != NULL here */
+ return pr;
+ }
+ h = pr->hash_next;
+ }
+ *ppr = NULL;
+ return NULL;
+}
+
+/* indicate that the object may be part of a function prototype cycle */
+static void set_cycle_flag(JSContext *ctx, JSValueConst obj)
+{
+}
+
+static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref)
+{
+ if (var_ref) {
+ assert(var_ref->header.ref_count > 0);
+ if (--var_ref->header.ref_count == 0) {
+ if (var_ref->is_detached) {
+ JS_FreeValueRT(rt, var_ref->value);
+ remove_gc_object(&var_ref->header);
+ } else {
+ list_del(&var_ref->header.link); /* still on the stack */
+ }
+ js_free_rt(rt, var_ref);
+ }
+ }
+}
+
+static void js_array_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ int i;
+
+ for(i = 0; i < p->u.array.count; i++) {
+ JS_FreeValueRT(rt, p->u.array.u.values[i]);
+ }
+ js_free_rt(rt, p->u.array.u.values);
+}
+
+static void js_array_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ int i;
+
+ for(i = 0; i < p->u.array.count; i++) {
+ JS_MarkValue(rt, p->u.array.u.values[i], mark_func);
+ }
+}
+
+static void js_object_data_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JS_FreeValueRT(rt, p->u.object_data);
+ p->u.object_data = JS_UNDEFINED;
+}
+
+static void js_object_data_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JS_MarkValue(rt, p->u.object_data, mark_func);
+}
+
+static void js_c_function_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+
+ if (p->u.cfunc.realm)
+ JS_FreeContext(p->u.cfunc.realm);
+}
+
+static void js_c_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+
+ if (p->u.cfunc.realm)
+ mark_func(rt, &p->u.cfunc.realm->header);
+}
+
+static void js_bytecode_function_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p1, *p = JS_VALUE_GET_OBJ(val);
+ JSFunctionBytecode *b;
+ JSVarRef **var_refs;
+ int i;
+
+ p1 = p->u.func.home_object;
+ if (p1) {
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, p1));
+ }
+ b = p->u.func.function_bytecode;
+ if (b) {
+ var_refs = p->u.func.var_refs;
+ if (var_refs) {
+ for(i = 0; i < b->closure_var_count; i++)
+ free_var_ref(rt, var_refs[i]);
+ js_free_rt(rt, var_refs);
+ }
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b));
+ }
+}
+
+static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSVarRef **var_refs = p->u.func.var_refs;
+ JSFunctionBytecode *b = p->u.func.function_bytecode;
+ int i;
+
+ if (p->u.func.home_object) {
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object),
+ mark_func);
+ }
+ if (b) {
+ if (var_refs) {
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSVarRef *var_ref = var_refs[i];
+ if (var_ref && var_ref->is_detached) {
+ mark_func(rt, &var_ref->header);
+ }
+ }
+ }
+ /* must mark the function bytecode because template objects may be
+ part of a cycle */
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b), mark_func);
+ }
+}
+
+static void js_bound_function_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSBoundFunction *bf = p->u.bound_function;
+ int i;
+
+ JS_FreeValueRT(rt, bf->func_obj);
+ JS_FreeValueRT(rt, bf->this_val);
+ for(i = 0; i < bf->argc; i++) {
+ JS_FreeValueRT(rt, bf->argv[i]);
+ }
+ js_free_rt(rt, bf);
+}
+
+static void js_bound_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSBoundFunction *bf = p->u.bound_function;
+ int i;
+
+ JS_MarkValue(rt, bf->func_obj, mark_func);
+ JS_MarkValue(rt, bf->this_val, mark_func);
+ for(i = 0; i < bf->argc; i++)
+ JS_MarkValue(rt, bf->argv[i], mark_func);
+}
+
+static void js_for_in_iterator_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSForInIterator *it = p->u.for_in_iterator;
+ JS_FreeValueRT(rt, it->obj);
+ js_free_rt(rt, it);
+}
+
+static void js_for_in_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSForInIterator *it = p->u.for_in_iterator;
+ JS_MarkValue(rt, it->obj, mark_func);
+}
+
+static void free_object(JSRuntime *rt, JSObject *p)
+{
+ int i;
+ JSClassFinalizer *finalizer;
+ JSShape *sh;
+ JSShapeProperty *pr;
+
+ p->free_mark = 1; /* used to tell the object is invalid when
+ freeing cycles */
+ /* free all the fields */
+ sh = p->shape;
+ pr = get_shape_prop(sh);
+ for(i = 0; i < sh->prop_count; i++) {
+ free_property(rt, &p->prop[i], pr->flags);
+ pr++;
+ }
+ js_free_rt(rt, p->prop);
+ /* as an optimization we destroy the shape immediately without
+ putting it in gc_zero_ref_count_list */
+ js_free_shape(rt, sh);
+
+ /* fail safe */
+ p->shape = NULL;
+ p->prop = NULL;
+
+ if (unlikely(p->first_weak_ref)) {
+ reset_weak_ref(rt, p);
+ }
+
+ finalizer = rt->class_array[p->class_id].finalizer;
+ if (finalizer)
+ (*finalizer)(rt, JS_MKPTR(JS_TAG_OBJECT, p));
+
+ /* fail safe */
+ p->class_id = 0;
+ p->u.opaque = NULL;
+ p->u.func.var_refs = NULL;
+ p->u.func.home_object = NULL;
+
+ remove_gc_object(&p->header);
+ if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && p->header.ref_count != 0) {
+ list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
+ } else {
+ js_free_rt(rt, p);
+ }
+}
+
+static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
+{
+ switch(gp->gc_obj_type) {
+ case JS_GC_OBJ_TYPE_JS_OBJECT:
+ free_object(rt, (JSObject *)gp);
+ break;
+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+ free_function_bytecode(rt, (JSFunctionBytecode *)gp);
+ break;
+ default:
+ abort();
+ }
+}
+
+static void free_zero_refcount(JSRuntime *rt)
+{
+ struct list_head *el;
+ JSGCObjectHeader *p;
+
+ rt->gc_phase = JS_GC_PHASE_DECREF;
+ for(;;) {
+ el = rt->gc_zero_ref_count_list.next;
+ if (el == &rt->gc_zero_ref_count_list)
+ break;
+ p = list_entry(el, JSGCObjectHeader, link);
+ assert(p->ref_count == 0);
+ free_gc_object(rt, p);
+ }
+ rt->gc_phase = JS_GC_PHASE_NONE;
+}
+
+/* called with the ref_count of 'v' reaches zero. */
+static void JS_FreeValueRTImpl(JSRuntime *rt, JSValue v)
+{
+ uint32_t tag = JS_VALUE_GET_TAG(v);
+
+#ifdef DUMP_FREE
+ {
+ printf("Freeing ");
+ if (tag == JS_TAG_OBJECT) {
+ JS_DumpObject(rt, JS_VALUE_GET_OBJ(v));
+ } else {
+ JS_DumpValueShort(rt, v);
+ printf("\n");
+ }
+ }
+#endif
+
+ switch(tag) {
+ case JS_TAG_STRING:
+ {
+ JSString *p = JS_VALUE_GET_STRING(v);
+ if (p->atom_type) {
+ JS_FreeAtomStruct(rt, p);
+ } else {
+#ifdef DUMP_LEAKS
+ list_del(&p->link);
+#endif
+ js_free_rt(rt, p);
+ }
+ }
+ break;
+ case JS_TAG_OBJECT:
+ case JS_TAG_FUNCTION_BYTECODE:
+ {
+ JSGCObjectHeader *p = JS_VALUE_GET_PTR(v);
+ if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) {
+ list_del(&p->link);
+ list_add(&p->link, &rt->gc_zero_ref_count_list);
+ if (rt->gc_phase == JS_GC_PHASE_NONE) {
+ free_zero_refcount(rt);
+ }
+ }
+ }
+ break;
+ case JS_TAG_MODULE:
+ abort(); /* never freed here */
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *bf = JS_VALUE_GET_PTR(v);
+ bf_delete(&bf->num);
+ js_free_rt(rt, bf);
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ {
+ JSBigDecimal *bf = JS_VALUE_GET_PTR(v);
+ bfdec_delete(&bf->num);
+ js_free_rt(rt, bf);
+ }
+ break;
+#endif
+ case JS_TAG_SYMBOL:
+ {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(v);
+ JS_FreeAtomStruct(rt, p);
+ }
+ break;
+ default:
+ printf("__JS_FreeValue: unknown tag=%d\n", tag);
+ abort();
+ }
+}
+
+static void JS_FreeValueImpl(JSContext *ctx, JSValue v)
+{
+ JS_FreeValueRTImpl(ctx->rt, v);
+}
+
+/* garbage collection */
+
+static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
+ JSGCObjectTypeEnum type)
+{
+ h->mark = 0;
+ h->gc_obj_type = type;
+ list_add_tail(&h->link, &rt->gc_obj_list);
+}
+
+static void remove_gc_object(JSGCObjectHeader *h)
+{
+ list_del(&h->link);
+}
+
+void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
+{
+ if (JS_VALUE_HAS_REF_COUNT(val)) {
+ switch(JS_VALUE_GET_TAG(val)) {
+ case JS_TAG_OBJECT:
+ case JS_TAG_FUNCTION_BYTECODE:
+ mark_func(rt, JS_VALUE_GET_PTR(val));
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
+ JS_MarkFunc *mark_func)
+{
+ switch(gp->gc_obj_type) {
+ case JS_GC_OBJ_TYPE_JS_OBJECT:
+ {
+ JSObject *p = (JSObject *)gp;
+ JSShapeProperty *prs;
+ JSShape *sh;
+ int i;
+ sh = p->shape;
+ mark_func(rt, &sh->header);
+ /* mark all the fields */
+ prs = get_shape_prop(sh);
+ for(i = 0; i < sh->prop_count; i++) {
+ JSProperty *pr = &p->prop[i];
+ if (prs->atom != JS_ATOM_NULL) {
+ if (prs->flags & JS_PROP_TMASK) {
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ if (pr->u.getset.getter)
+ mark_func(rt, &pr->u.getset.getter->header);
+ if (pr->u.getset.setter)
+ mark_func(rt, &pr->u.getset.setter->header);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ if (pr->u.var_ref->is_detached) {
+ /* Note: the tag does not matter
+ provided it is a GC object */
+ mark_func(rt, &pr->u.var_ref->header);
+ }
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ js_autoinit_mark(rt, pr, mark_func);
+ }
+ } else {
+ JS_MarkValue(rt, pr->u.value, mark_func);
+ }
+ }
+ prs++;
+ }
+
+ if (p->class_id != JS_CLASS_OBJECT) {
+ JSClassGCMark *gc_mark;
+ gc_mark = rt->class_array[p->class_id].gc_mark;
+ if (gc_mark)
+ gc_mark(rt, JS_MKPTR(JS_TAG_OBJECT, p), mark_func);
+ }
+ }
+ break;
+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+ /* the template objects can be part of a cycle */
+ {
+ JSFunctionBytecode *b = (JSFunctionBytecode *)gp;
+ int i;
+ for(i = 0; i < b->cpool_count; i++) {
+ JS_MarkValue(rt, b->cpool[i], mark_func);
+ }
+ if (b->realm)
+ mark_func(rt, &b->realm->header);
+ }
+ break;
+ case JS_GC_OBJ_TYPE_VAR_REF:
+ {
+ JSVarRef *var_ref = (JSVarRef *)gp;
+ /* only detached variable referenced are taken into account */
+ assert(var_ref->is_detached);
+ JS_MarkValue(rt, *var_ref->pvalue, mark_func);
+ }
+ break;
+ case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
+ {
+ JSAsyncFunctionData *s = (JSAsyncFunctionData *)gp;
+ if (s->is_active)
+ async_func_mark(rt, &s->func_state, mark_func);
+ JS_MarkValue(rt, s->resolving_funcs[0], mark_func);
+ JS_MarkValue(rt, s->resolving_funcs[1], mark_func);
+ }
+ break;
+ case JS_GC_OBJ_TYPE_SHAPE:
+ {
+ JSShape *sh = (JSShape *)gp;
+ if (sh->proto != NULL) {
+ mark_func(rt, &sh->proto->header);
+ }
+ }
+ break;
+ case JS_GC_OBJ_TYPE_JS_CONTEXT:
+ {
+ JSContext *ctx = (JSContext *)gp;
+ JS_MarkContext(rt, ctx, mark_func);
+ }
+ break;
+ default:
+ abort();
+ }
+}
+
+static void gc_decref_child(JSRuntime *rt, JSGCObjectHeader *p)
+{
+ assert(p->ref_count > 0);
+ p->ref_count--;
+ if (p->ref_count == 0 && p->mark == 1) {
+ list_del(&p->link);
+ list_add_tail(&p->link, &rt->tmp_obj_list);
+ }
+}
+
+static void gc_decref(JSRuntime *rt)
+{
+ struct list_head *el, *el1;
+ JSGCObjectHeader *p;
+
+ init_list_head(&rt->tmp_obj_list);
+
+ /* decrement the refcount of all the children of all the GC
+ objects and move the GC objects with zero refcount to
+ tmp_obj_list */
+ list_for_each_safe(el, el1, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ assert(p->mark == 0);
+ mark_children(rt, p, gc_decref_child);
+ p->mark = 1;
+ if (p->ref_count == 0) {
+ list_del(&p->link);
+ list_add_tail(&p->link, &rt->tmp_obj_list);
+ }
+ }
+}
+
+static void gc_scan_incref_child(JSRuntime *rt, JSGCObjectHeader *p)
+{
+ p->ref_count++;
+ if (p->ref_count == 1) {
+ /* ref_count was 0: remove from tmp_obj_list and add at the
+ end of gc_obj_list */
+ list_del(&p->link);
+ list_add_tail(&p->link, &rt->gc_obj_list);
+ p->mark = 0; /* reset the mark for the next GC call */
+ }
+}
+
+static void gc_scan_incref_child2(JSRuntime *rt, JSGCObjectHeader *p)
+{
+ p->ref_count++;
+}
+
+static void gc_scan(JSRuntime *rt)
+{
+ struct list_head *el;
+ JSGCObjectHeader *p;
+
+ /* keep the objects with a refcount > 0 and their children. */
+ list_for_each(el, &rt->gc_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ assert(p->ref_count > 0);
+ p->mark = 0; /* reset the mark for the next GC call */
+ mark_children(rt, p, gc_scan_incref_child);
+ }
+
+ /* restore the refcount of the objects to be deleted. */
+ list_for_each(el, &rt->tmp_obj_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ mark_children(rt, p, gc_scan_incref_child2);
+ }
+}
+
+static void gc_free_cycles(JSRuntime *rt)
+{
+ struct list_head *el, *el1;
+ JSGCObjectHeader *p;
+#ifdef DUMP_GC_FREE
+ BOOL header_done = FALSE;
+#endif
+
+ rt->gc_phase = JS_GC_PHASE_REMOVE_CYCLES;
+
+ for(;;) {
+ el = rt->tmp_obj_list.next;
+ if (el == &rt->tmp_obj_list)
+ break;
+ p = list_entry(el, JSGCObjectHeader, link);
+ /* Only need to free the GC object associated with JS
+ values. The rest will be automatically removed because they
+ must be referenced by them. */
+ switch(p->gc_obj_type) {
+ case JS_GC_OBJ_TYPE_JS_OBJECT:
+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+#ifdef DUMP_GC_FREE
+ if (!header_done) {
+ printf("Freeing cycles:\n");
+ JS_DumpObjectHeader(rt);
+ header_done = TRUE;
+ }
+ JS_DumpGCObject(rt, p);
+#endif
+ free_gc_object(rt, p);
+ break;
+ default:
+ list_del(&p->link);
+ list_add_tail(&p->link, &rt->gc_zero_ref_count_list);
+ break;
+ }
+ }
+ rt->gc_phase = JS_GC_PHASE_NONE;
+
+ list_for_each_safe(el, el1, &rt->gc_zero_ref_count_list) {
+ p = list_entry(el, JSGCObjectHeader, link);
+ assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
+ p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
+ js_free_rt(rt, p);
+ }
+
+ init_list_head(&rt->gc_zero_ref_count_list);
+}
+
+void JS_RunGC(JSRuntime *rt)
+{
+ /* decrement the reference of the children of each object. mark =
+ 1 after this pass. */
+ gc_decref(rt);
+
+ /* keep the GC objects with a non zero refcount and their childs */
+ gc_scan(rt);
+
+ /* free the GC objects in a cycle */
+ gc_free_cycles(rt);
+}
+
+/* Return false if not an object or if the object has already been
+ freed (zombie objects are visible in finalizers when freeing
+ cycles). */
+BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj)
+{
+ JSObject *p;
+ if (!JS_IsObject(obj))
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ return !p->free_mark;
+}
+
+/* Compute memory used by various object types */
+/* XXX: poor man's approach to handling multiply referenced objects */
+typedef struct JSMemoryUsage_helper {
+ double memory_used_count;
+ double str_count;
+ double str_size;
+ int64_t js_func_count;
+ double js_func_size;
+ int64_t js_func_code_size;
+ int64_t js_func_pc2line_count;
+ int64_t js_func_pc2line_size;
+} JSMemoryUsage_helper;
+
+static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp);
+
+static void compute_jsstring_size(JSString *str, JSMemoryUsage_helper *hp)
+{
+ if (!str->atom_type) { /* atoms are handled separately */
+ double s_ref_count = str->header.ref_count;
+ hp->str_count += 1 / s_ref_count;
+ hp->str_size += ((sizeof(*str) + (str->len << str->is_wide_char) +
+ 1 - str->is_wide_char) / s_ref_count);
+ }
+}
+
+static void compute_bytecode_size(JSFunctionBytecode *b, JSMemoryUsage_helper *hp)
+{
+ int memory_used_count, js_func_size, i;
+
+ memory_used_count = 0;
+ js_func_size = offsetof(JSFunctionBytecode, debug);
+ if (b->vardefs) {
+ js_func_size += (b->arg_count + b->var_count) * sizeof(*b->vardefs);
+ }
+ if (b->cpool) {
+ js_func_size += b->cpool_count * sizeof(*b->cpool);
+ for (i = 0; i < b->cpool_count; i++) {
+ JSValueConst val = b->cpool[i];
+ compute_value_size(val, hp);
+ }
+ }
+ if (b->closure_var) {
+ js_func_size += b->closure_var_count * sizeof(*b->closure_var);
+ }
+ if (!b->read_only_bytecode && b->byte_code_buf) {
+ hp->js_func_code_size += b->byte_code_len;
+ }
+ if (b->has_debug) {
+ js_func_size += sizeof(*b) - offsetof(JSFunctionBytecode, debug);
+ if (b->debug.source) {
+ memory_used_count++;
+ js_func_size += b->debug.source_len + 1;
+ }
+ if (b->debug.pc2line_len) {
+ memory_used_count++;
+ hp->js_func_pc2line_count += 1;
+ hp->js_func_pc2line_size += b->debug.pc2line_len;
+ }
+ }
+ hp->js_func_size += js_func_size;
+ hp->js_func_count += 1;
+ hp->memory_used_count += memory_used_count;
+}
+
+static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp)
+{
+ switch(JS_VALUE_GET_TAG(val)) {
+ case JS_TAG_STRING:
+ compute_jsstring_size(JS_VALUE_GET_STRING(val), hp);
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ case JS_TAG_BIG_DECIMAL:
+ /* should track JSBigFloat usage */
+ break;
+#endif
+ }
+}
+
+void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
+{
+ struct list_head *el, *el1;
+ int i;
+ JSMemoryUsage_helper mem = { 0 }, *hp = &mem;
+
+ memset(s, 0, sizeof(*s));
+ s->malloc_count = rt->malloc_state.malloc_count;
+ s->malloc_size = rt->malloc_state.malloc_size;
+ s->malloc_limit = rt->malloc_state.malloc_limit;
+
+ s->memory_used_count = 2; /* rt + rt->class_array */
+ s->memory_used_size = sizeof(JSRuntime) + sizeof(JSValue) * rt->class_count;
+
+ list_for_each(el, &rt->context_list) {
+ JSContext *ctx = list_entry(el, JSContext, link);
+ JSShape *sh = ctx->array_shape;
+ s->memory_used_count += 2; /* ctx + ctx->class_proto */
+ s->memory_used_size += sizeof(JSContext) +
+ sizeof(JSValue) * rt->class_count;
+ s->binary_object_count += ctx->binary_object_count;
+ s->binary_object_size += ctx->binary_object_size;
+
+ /* the hashed shapes are counted separately */
+ if (sh && !sh->is_hashed) {
+ int hash_size = sh->prop_hash_mask + 1;
+ s->shape_count++;
+ s->shape_size += get_shape_size(hash_size, sh->prop_size);
+ }
+ list_for_each(el1, &ctx->loaded_modules) {
+ JSModuleDef *m = list_entry(el1, JSModuleDef, link);
+ s->memory_used_count += 1;
+ s->memory_used_size += sizeof(*m);
+ if (m->req_module_entries) {
+ s->memory_used_count += 1;
+ s->memory_used_size += m->req_module_entries_count * sizeof(*m->req_module_entries);
+ }
+ if (m->export_entries) {
+ s->memory_used_count += 1;
+ s->memory_used_size += m->export_entries_count * sizeof(*m->export_entries);
+ for (i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL && me->u.local.var_ref) {
+ /* potential multiple count */
+ s->memory_used_count += 1;
+ compute_value_size(me->u.local.var_ref->value, hp);
+ }
+ }
+ }
+ if (m->star_export_entries) {
+ s->memory_used_count += 1;
+ s->memory_used_size += m->star_export_entries_count * sizeof(*m->star_export_entries);
+ }
+ if (m->import_entries) {
+ s->memory_used_count += 1;
+ s->memory_used_size += m->import_entries_count * sizeof(*m->import_entries);
+ }
+ compute_value_size(m->module_ns, hp);
+ compute_value_size(m->func_obj, hp);
+ }
+ }
+
+ list_for_each(el, &rt->gc_obj_list) {
+ JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
+ JSObject *p;
+ JSShape *sh;
+ JSShapeProperty *prs;
+
+ /* XXX: could count the other GC object types too */
+ if (gp->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE) {
+ compute_bytecode_size((JSFunctionBytecode *)gp, hp);
+ continue;
+ } else if (gp->gc_obj_type != JS_GC_OBJ_TYPE_JS_OBJECT) {
+ continue;
+ }
+ p = (JSObject *)gp;
+ sh = p->shape;
+ s->obj_count++;
+ if (p->prop) {
+ s->memory_used_count++;
+ s->prop_size += sh->prop_size * sizeof(*p->prop);
+ s->prop_count += sh->prop_count;
+ prs = get_shape_prop(sh);
+ for(i = 0; i < sh->prop_count; i++) {
+ JSProperty *pr = &p->prop[i];
+ if (prs->atom != JS_ATOM_NULL && !(prs->flags & JS_PROP_TMASK)) {
+ compute_value_size(pr->u.value, hp);
+ }
+ prs++;
+ }
+ }
+ /* the hashed shapes are counted separately */
+ if (!sh->is_hashed) {
+ int hash_size = sh->prop_hash_mask + 1;
+ s->shape_count++;
+ s->shape_size += get_shape_size(hash_size, sh->prop_size);
+ }
+
+ switch(p->class_id) {
+ case JS_CLASS_ARRAY: /* u.array | length */
+ case JS_CLASS_ARGUMENTS: /* u.array | length */
+ s->array_count++;
+ if (p->fast_array) {
+ s->fast_array_count++;
+ if (p->u.array.u.values) {
+ s->memory_used_count++;
+ s->memory_used_size += p->u.array.count *
+ sizeof(*p->u.array.u.values);
+ s->fast_array_elements += p->u.array.count;
+ for (i = 0; i < p->u.array.count; i++) {
+ compute_value_size(p->u.array.u.values[i], hp);
+ }
+ }
+ }
+ break;
+ case JS_CLASS_NUMBER: /* u.object_data */
+ case JS_CLASS_STRING: /* u.object_data */
+ case JS_CLASS_BOOLEAN: /* u.object_data */
+ case JS_CLASS_SYMBOL: /* u.object_data */
+ case JS_CLASS_DATE: /* u.object_data */
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT: /* u.object_data */
+ case JS_CLASS_BIG_FLOAT: /* u.object_data */
+ case JS_CLASS_BIG_DECIMAL: /* u.object_data */
+#endif
+ compute_value_size(p->u.object_data, hp);
+ break;
+ case JS_CLASS_C_FUNCTION: /* u.cfunc */
+ s->c_func_count++;
+ break;
+ case JS_CLASS_BYTECODE_FUNCTION: /* u.func */
+ {
+ JSFunctionBytecode *b = p->u.func.function_bytecode;
+ JSVarRef **var_refs = p->u.func.var_refs;
+ /* home_object: object will be accounted for in list scan */
+ if (var_refs) {
+ s->memory_used_count++;
+ s->js_func_size += b->closure_var_count * sizeof(*var_refs);
+ for (i = 0; i < b->closure_var_count; i++) {
+ if (var_refs[i]) {
+ double ref_count = var_refs[i]->header.ref_count;
+ s->memory_used_count += 1 / ref_count;
+ s->js_func_size += sizeof(*var_refs[i]) / ref_count;
+ /* handle non object closed values */
+ if (var_refs[i]->pvalue == &var_refs[i]->value) {
+ /* potential multiple count */
+ compute_value_size(var_refs[i]->value, hp);
+ }
+ }
+ }
+ }
+ }
+ break;
+ case JS_CLASS_BOUND_FUNCTION: /* u.bound_function */
+ {
+ JSBoundFunction *bf = p->u.bound_function;
+ /* func_obj and this_val are objects */
+ for (i = 0; i < bf->argc; i++) {
+ compute_value_size(bf->argv[i], hp);
+ }
+ s->memory_used_count += 1;
+ s->memory_used_size += sizeof(*bf) + bf->argc * sizeof(*bf->argv);
+ }
+ break;
+ case JS_CLASS_C_FUNCTION_DATA: /* u.c_function_data_record */
+ {
+ JSCFunctionDataRecord *fd = p->u.c_function_data_record;
+ if (fd) {
+ for (i = 0; i < fd->data_len; i++) {
+ compute_value_size(fd->data[i], hp);
+ }
+ s->memory_used_count += 1;
+ s->memory_used_size += sizeof(*fd) + fd->data_len * sizeof(*fd->data);
+ }
+ }
+ break;
+ case JS_CLASS_REGEXP: /* u.regexp */
+ compute_jsstring_size(p->u.regexp.pattern, hp);
+ compute_jsstring_size(p->u.regexp.bytecode, hp);
+ break;
+
+ case JS_CLASS_FOR_IN_ITERATOR: /* u.for_in_iterator */
+ {
+ JSForInIterator *it = p->u.for_in_iterator;
+ if (it) {
+ compute_value_size(it->obj, hp);
+ s->memory_used_count += 1;
+ s->memory_used_size += sizeof(*it);
+ }
+ }
+ break;
+ case JS_CLASS_ARRAY_BUFFER: /* u.array_buffer */
+ case JS_CLASS_SHARED_ARRAY_BUFFER: /* u.array_buffer */
+ {
+ JSArrayBuffer *abuf = p->u.array_buffer;
+ if (abuf) {
+ s->memory_used_count += 1;
+ s->memory_used_size += sizeof(*abuf);
+ if (abuf->data) {
+ s->memory_used_count += 1;
+ s->memory_used_size += abuf->byte_length;
+ }
+ }
+ }
+ break;
+ case JS_CLASS_GENERATOR: /* u.generator_data */
+ case JS_CLASS_UINT8C_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_INT8_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_UINT8_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_INT16_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_UINT16_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_INT32_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_UINT32_ARRAY: /* u.typed_array / u.array */
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_BIG_UINT64_ARRAY: /* u.typed_array / u.array */
+#endif
+ case JS_CLASS_FLOAT32_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_FLOAT64_ARRAY: /* u.typed_array / u.array */
+ case JS_CLASS_DATAVIEW: /* u.typed_array */
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_FLOAT_ENV: /* u.float_env */
+#endif
+ case JS_CLASS_MAP: /* u.map_state */
+ case JS_CLASS_SET: /* u.map_state */
+ case JS_CLASS_WEAKMAP: /* u.map_state */
+ case JS_CLASS_WEAKSET: /* u.map_state */
+ case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */
+ case JS_CLASS_SET_ITERATOR: /* u.map_iterator_data */
+ case JS_CLASS_ARRAY_ITERATOR: /* u.array_iterator_data */
+ case JS_CLASS_STRING_ITERATOR: /* u.array_iterator_data */
+ case JS_CLASS_PROXY: /* u.proxy_data */
+ case JS_CLASS_PROMISE: /* u.promise_data */
+ case JS_CLASS_PROMISE_RESOLVE_FUNCTION: /* u.promise_function_data */
+ case JS_CLASS_PROMISE_REJECT_FUNCTION: /* u.promise_function_data */
+ case JS_CLASS_ASYNC_FUNCTION_RESOLVE: /* u.async_function_data */
+ case JS_CLASS_ASYNC_FUNCTION_REJECT: /* u.async_function_data */
+ case JS_CLASS_ASYNC_FROM_SYNC_ITERATOR: /* u.async_from_sync_iterator_data */
+ case JS_CLASS_ASYNC_GENERATOR: /* u.async_generator_data */
+ /* TODO */
+ default:
+ /* XXX: class definition should have an opaque block size */
+ if (p->u.opaque) {
+ s->memory_used_count += 1;
+ }
+ break;
+ }
+ }
+ s->obj_size += s->obj_count * sizeof(JSObject);
+
+ /* hashed shapes */
+ s->memory_used_count++; /* rt->shape_hash */
+ s->memory_used_size += sizeof(rt->shape_hash[0]) * rt->shape_hash_size;
+ for(i = 0; i < rt->shape_hash_size; i++) {
+ JSShape *sh;
+ for(sh = rt->shape_hash[i]; sh != NULL; sh = sh->shape_hash_next) {
+ int hash_size = sh->prop_hash_mask + 1;
+ s->shape_count++;
+ s->shape_size += get_shape_size(hash_size, sh->prop_size);
+ }
+ }
+
+ /* atoms */
+ s->memory_used_count += 2; /* rt->atom_array, rt->atom_hash */
+ s->atom_count = rt->atom_count;
+ s->atom_size = sizeof(rt->atom_array[0]) * rt->atom_size +
+ sizeof(rt->atom_hash[0]) * rt->atom_hash_size;
+ for(i = 0; i < rt->atom_size; i++) {
+ JSAtomStruct *p = rt->atom_array[i];
+ if (!atom_is_free(p)) {
+ s->atom_size += (sizeof(*p) + (p->len << p->is_wide_char) +
+ 1 - p->is_wide_char);
+ }
+ }
+ s->str_count = round(mem.str_count);
+ s->str_size = round(mem.str_size);
+ s->js_func_count = mem.js_func_count;
+ s->js_func_size = round(mem.js_func_size);
+ s->js_func_code_size = mem.js_func_code_size;
+ s->js_func_pc2line_count = mem.js_func_pc2line_count;
+ s->js_func_pc2line_size = mem.js_func_pc2line_size;
+ s->memory_used_count += round(mem.memory_used_count) +
+ s->atom_count + s->str_count +
+ s->obj_count + s->shape_count +
+ s->js_func_count + s->js_func_pc2line_count;
+ s->memory_used_size += s->atom_size + s->str_size +
+ s->obj_size + s->prop_size + s->shape_size +
+ s->js_func_size + s->js_func_code_size + s->js_func_pc2line_size;
+}
+
+void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt)
+{
+ fprintf(fp, "QuickJS memory usage -- "
+#ifdef CONFIG_BIGNUM
+ "BigNum "
+#endif
+ CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n",
+ (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit);
+#if 1
+ if (rt) {
+ static const struct {
+ const char *name;
+ size_t size;
+ } object_types[] = {
+ { "JSRuntime", sizeof(JSRuntime) },
+ { "JSContext", sizeof(JSContext) },
+ { "JSObject", sizeof(JSObject) },
+ { "JSString", sizeof(JSString) },
+ { "JSFunctionBytecode", sizeof(JSFunctionBytecode) },
+ };
+ int i, usage_size_ok = 0;
+ for(i = 0; i < countof(object_types); i++) {
+ unsigned int size = object_types[i].size;
+ void *p = js_malloc_rt(rt, size);
+ if (p) {
+ unsigned int size1 = js_malloc_usable_size_rt(rt, p);
+ if (size1 >= size) {
+ usage_size_ok = 1;
+ fprintf(fp, " %3u + %-2u %s\n",
+ size, size1 - size, object_types[i].name);
+ }
+ js_free_rt(rt, p);
+ }
+ }
+ if (!usage_size_ok) {
+ fprintf(fp, " malloc_usable_size unavailable\n");
+ }
+ {
+ int obj_classes[JS_CLASS_INIT_COUNT + 1] = { 0 };
+ int class_id;
+ struct list_head *el;
+ list_for_each(el, &rt->gc_obj_list) {
+ JSGCObjectHeader *gp = list_entry(el, JSGCObjectHeader, link);
+ JSObject *p;
+ if (gp->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+ p = (JSObject *)gp;
+ obj_classes[min_uint32(p->class_id, JS_CLASS_INIT_COUNT)]++;
+ }
+ }
+ fprintf(fp, "\n" "JSObject classes\n");
+ if (obj_classes[0])
+ fprintf(fp, " %5d %2.0d %s\n", obj_classes[0], 0, "none");
+ for (class_id = 1; class_id < JS_CLASS_INIT_COUNT; class_id++) {
+ if (obj_classes[class_id]) {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ fprintf(fp, " %5d %2.0d %s\n", obj_classes[class_id], class_id,
+ JS_AtomGetStrRT(rt, buf, sizeof(buf), js_std_class_def[class_id - 1].class_name));
+ }
+ }
+ if (obj_classes[JS_CLASS_INIT_COUNT])
+ fprintf(fp, " %5d %2.0d %s\n", obj_classes[JS_CLASS_INIT_COUNT], 0, "other");
+ }
+ fprintf(fp, "\n");
+ }
+#endif
+ fprintf(fp, "%-20s %8s %8s\n", "NAME", "COUNT", "SIZE");
+
+ if (s->malloc_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per block)\n",
+ "memory allocated", s->malloc_count, s->malloc_size,
+ (double)s->malloc_size / s->malloc_count);
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%d overhead, %0.1f average slack)\n",
+ "memory used", s->memory_used_count, s->memory_used_size,
+ MALLOC_OVERHEAD, ((double)(s->malloc_size - s->memory_used_size) /
+ s->memory_used_count));
+ }
+ if (s->atom_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per atom)\n",
+ "atoms", s->atom_count, s->atom_size,
+ (double)s->atom_size / s->atom_count);
+ }
+ if (s->str_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per string)\n",
+ "strings", s->str_count, s->str_size,
+ (double)s->str_size / s->str_count);
+ }
+ if (s->obj_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
+ "objects", s->obj_count, s->obj_size,
+ (double)s->obj_size / s->obj_count);
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per object)\n",
+ " properties", s->prop_count, s->prop_size,
+ (double)s->prop_count / s->obj_count);
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per shape)\n",
+ " shapes", s->shape_count, s->shape_size,
+ (double)s->shape_size / s->shape_count);
+ }
+ if (s->js_func_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
+ "bytecode functions", s->js_func_count, s->js_func_size);
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
+ " bytecode", s->js_func_count, s->js_func_code_size,
+ (double)s->js_func_code_size / s->js_func_count);
+ if (s->js_func_pc2line_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per function)\n",
+ " pc2line", s->js_func_pc2line_count,
+ s->js_func_pc2line_size,
+ (double)s->js_func_pc2line_size / s->js_func_pc2line_count);
+ }
+ }
+ if (s->c_func_count) {
+ fprintf(fp, "%-20s %8"PRId64"\n", "C functions", s->c_func_count);
+ }
+ if (s->array_count) {
+ fprintf(fp, "%-20s %8"PRId64"\n", "arrays", s->array_count);
+ if (s->fast_array_count) {
+ fprintf(fp, "%-20s %8"PRId64"\n", " fast arrays", s->fast_array_count);
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64" (%0.1f per fast array)\n",
+ " elements", s->fast_array_elements,
+ s->fast_array_elements * (int)sizeof(JSValue),
+ (double)s->fast_array_elements / s->fast_array_count);
+ }
+ }
+ if (s->binary_object_count) {
+ fprintf(fp, "%-20s %8"PRId64" %8"PRId64"\n",
+ "binary objects", s->binary_object_count, s->binary_object_size);
+ }
+}
+
+JSValue JS_GetGlobalObject(JSContext *ctx)
+{
+ return JS_DupValue(ctx, ctx->global_obj);
+}
+
+/* WARNING: obj is freed */
+JSValue JS_Throw(JSContext *ctx, JSValue obj)
+{
+ JSRuntime *rt = ctx->rt;
+ JS_FreeValue(ctx, rt->current_exception);
+ rt->current_exception = obj;
+ return JS_EXCEPTION;
+}
+
+/* return the pending exception (cannot be called twice). */
+JSValue JS_GetException(JSContext *ctx)
+{
+ JSValue val;
+ JSRuntime *rt = ctx->rt;
+ val = rt->current_exception;
+ rt->current_exception = JS_NULL;
+ return val;
+}
+
+static void dbuf_put_leb128(DynBuf *s, uint32_t v)
+{
+ uint32_t a;
+ for(;;) {
+ a = v & 0x7f;
+ v >>= 7;
+ if (v != 0) {
+ dbuf_putc(s, a | 0x80);
+ } else {
+ dbuf_putc(s, a);
+ break;
+ }
+ }
+}
+
+static void dbuf_put_sleb128(DynBuf *s, int32_t v1)
+{
+ uint32_t v = v1;
+ dbuf_put_leb128(s, (2 * v) ^ -(v >> 31));
+}
+
+static int get_leb128(uint32_t *pval, const uint8_t *buf,
+ const uint8_t *buf_end)
+{
+ const uint8_t *ptr = buf;
+ uint32_t v, a, i;
+ v = 0;
+ for(i = 0; i < 5; i++) {
+ if (unlikely(ptr >= buf_end))
+ break;
+ a = *ptr++;
+ v |= (a & 0x7f) << (i * 7);
+ if (!(a & 0x80)) {
+ *pval = v;
+ return ptr - buf;
+ }
+ }
+ *pval = 0;
+ return -1;
+}
+
+static int get_sleb128(int32_t *pval, const uint8_t *buf,
+ const uint8_t *buf_end)
+{
+ int ret;
+ uint32_t val;
+ ret = get_leb128(&val, buf, buf_end);
+ if (ret < 0) {
+ *pval = 0;
+ return -1;
+ }
+ *pval = (val >> 1) ^ -(val & 1);
+ return ret;
+}
+
+static int find_line_num(JSContext *ctx, JSFunctionBytecode *b,
+ uint32_t pc_value)
+{
+ const uint8_t *p_end, *p;
+ int new_line_num, line_num, pc, v, ret;
+ unsigned int op;
+
+ if (!b->has_debug || !b->debug.pc2line_buf) {
+ /* function was stripped */
+ return -1;
+ }
+
+ p = b->debug.pc2line_buf;
+ p_end = p + b->debug.pc2line_len;
+ pc = 0;
+ line_num = b->debug.line_num;
+ while (p < p_end) {
+ op = *p++;
+ if (op == 0) {
+ uint32_t val;
+ ret = get_leb128(&val, p, p_end);
+ if (ret < 0)
+ goto fail;
+ pc += val;
+ p += ret;
+ ret = get_sleb128(&v, p, p_end);
+ if (ret < 0) {
+ fail:
+ /* should never happen */
+ return b->debug.line_num;
+ }
+ p += ret;
+ new_line_num = line_num + v;
+ } else {
+ op -= PC2LINE_OP_FIRST;
+ pc += (op / PC2LINE_RANGE);
+ new_line_num = line_num + (op % PC2LINE_RANGE) + PC2LINE_BASE;
+ }
+ if (pc_value < pc)
+ return line_num;
+ line_num = new_line_num;
+ }
+ return line_num;
+}
+
+/* in order to avoid executing arbitrary code during the stack trace
+ generation, we only look at simple 'name' properties containing a
+ string. */
+static const char *get_func_name(JSContext *ctx, JSValueConst func)
+{
+ JSProperty *pr;
+ JSShapeProperty *prs;
+ JSValueConst val;
+
+ if (JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)
+ return NULL;
+ prs = find_own_property(&pr, JS_VALUE_GET_OBJ(func), JS_ATOM_name);
+ if (!prs)
+ return NULL;
+ if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
+ return NULL;
+ val = pr->u.value;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
+ return NULL;
+ return JS_ToCString(ctx, val);
+}
+
+#define JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL (1 << 0)
+/* only taken into account if filename is provided */
+#define JS_BACKTRACE_FLAG_SINGLE_LEVEL (1 << 1)
+
+/* if filename != NULL, an additional level is added with the filename
+ and line number information (used for parse error). */
+void build_backtrace(JSContext *ctx, JSValueConst error_obj,
+ const char *filename, int line_num,
+ int backtrace_flags)
+{
+ JSStackFrame *sf;
+ JSValue str;
+ DynBuf dbuf;
+ const char *func_name_str;
+ const char *str1;
+ JSObject *p;
+ BOOL backtrace_barrier;
+
+ js_dbuf_init(ctx, &dbuf);
+ if (filename) {
+ dbuf_printf(&dbuf, " at %s", filename);
+ if (line_num != -1)
+ dbuf_printf(&dbuf, ":%d", line_num);
+ dbuf_putc(&dbuf, '\n');
+ str = JS_NewString(ctx, filename);
+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_fileName, str,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_lineNumber, JS_NewInt32(ctx, line_num),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ if (backtrace_flags & JS_BACKTRACE_FLAG_SINGLE_LEVEL)
+ goto done;
+ }
+ for(sf = ctx->rt->current_stack_frame; sf != NULL; sf = sf->prev_frame) {
+ if (backtrace_flags & JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL) {
+ backtrace_flags &= ~JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL;
+ continue;
+ }
+ func_name_str = get_func_name(ctx, sf->cur_func);
+ if (!func_name_str || func_name_str[0] == '\0')
+ str1 = "<anonymous>";
+ else
+ str1 = func_name_str;
+ dbuf_printf(&dbuf, " at %s", str1);
+ JS_FreeCString(ctx, func_name_str);
+
+ p = JS_VALUE_GET_OBJ(sf->cur_func);
+ backtrace_barrier = FALSE;
+ if (js_class_has_bytecode(p->class_id)) {
+ JSFunctionBytecode *b;
+ const char *atom_str;
+ int line_num1;
+
+ b = p->u.func.function_bytecode;
+ backtrace_barrier = b->backtrace_barrier;
+ if (b->has_debug) {
+ line_num1 = find_line_num(ctx, b,
+ sf->cur_pc - b->byte_code_buf - 1);
+ atom_str = JS_AtomToCString(ctx, b->debug.filename);
+ dbuf_printf(&dbuf, " (%s",
+ atom_str ? atom_str : "<null>");
+ JS_FreeCString(ctx, atom_str);
+ if (line_num1 != -1)
+ dbuf_printf(&dbuf, ":%d", line_num1);
+ dbuf_putc(&dbuf, ')');
+ }
+ } else {
+ dbuf_printf(&dbuf, " (native)");
+ }
+ dbuf_putc(&dbuf, '\n');
+ /* stop backtrace if JS_EVAL_FLAG_BACKTRACE_BARRIER was used */
+ if (backtrace_barrier)
+ break;
+ }
+ done:
+ dbuf_putc(&dbuf, '\0');
+ if (dbuf_error(&dbuf))
+ str = JS_NULL;
+ else
+ str = JS_NewString(ctx, (char *)dbuf.buf);
+ dbuf_free(&dbuf);
+ JS_DefinePropertyValue(ctx, error_obj, JS_ATOM_stack, str,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+}
+
+/* Note: it is important that no exception is returned by this function */
+static BOOL is_backtrace_needed(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id != JS_CLASS_ERROR)
+ return FALSE;
+ if (find_own_property1(p, JS_ATOM_stack))
+ return FALSE;
+ return TRUE;
+}
+
+JSValue JS_NewError(JSContext *ctx)
+{
+ return JS_NewObjectClass(ctx, JS_CLASS_ERROR);
+}
+
+static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,
+ const char *fmt, va_list ap, BOOL add_backtrace)
+{
+ char buf[8192];
+ JSValue obj, ret;
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ obj = JS_NewObjectProtoClass(ctx, ctx->native_error_proto[error_num],
+ JS_CLASS_ERROR);
+ if (unlikely(JS_IsException(obj))) {
+ /* out of memory: throw JS_NULL to avoid recursing */
+ obj = JS_NULL;
+ } else {
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_message,
+ JS_NewString(ctx, buf),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ }
+ if (add_backtrace) {
+ build_backtrace(ctx, obj, NULL, 0, 0);
+ }
+ ret = JS_Throw(ctx, obj);
+ return ret;
+}
+
+static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,
+ const char *fmt, va_list ap)
+{
+ JSRuntime *rt = ctx->rt;
+ JSStackFrame *sf;
+ BOOL add_backtrace;
+
+ /* the backtrace is added later if called from a bytecode function */
+ sf = rt->current_stack_frame;
+ add_backtrace = !rt->in_out_of_memory &&
+ (!sf || (JS_GetFunctionBytecode(sf->cur_func) == NULL));
+ return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);
+}
+
+JSValue FORMAT_ATTR(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
+{
+ JSValue val;
+ va_list ap;
+
+ va_start(ap, fmt);
+ val = JS_ThrowError(ctx, JS_SYNTAX_ERROR, fmt, ap);
+ va_end(ap);
+ return val;
+}
+
+JSValue FORMAT_ATTR(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
+{
+ JSValue val;
+ va_list ap;
+
+ va_start(ap, fmt);
+ val = JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
+ va_end(ap);
+ return val;
+}
+
+static int FORMAT_ATTR(3, 4) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
+{
+ va_list ap;
+
+ if ((flags & JS_PROP_THROW) ||
+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
+ va_start(ap, fmt);
+ JS_ThrowError(ctx, JS_TYPE_ERROR, fmt, ap);
+ va_end(ap);
+ return -1;
+ } else {
+ return FALSE;
+ }
+}
+
+/* never use it directly */
+static JSValue FORMAT_ATTR(3, 4) JS_ThrowTypeErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowTypeError(ctx, fmt,
+ JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
+}
+
+/* never use it directly */
+static JSValue FORMAT_ATTR(3, 4) JS_ThrowSyntaxErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowSyntaxError(ctx, fmt,
+ JS_AtomGetStr(ctx, buf, sizeof(buf), atom));
+}
+
+/* %s is replaced by 'atom'. The macro is used so that gcc can check
+ the format string. */
+#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) JS_ThrowTypeErrorAtomImpl(ctx, atom, fmt, "")
+#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) JS_ThrowSyntaxErrorAtomImpl(ctx, atom, fmt, "")
+
+static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
+{
+ if ((flags & JS_PROP_THROW) ||
+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
+ JS_ThrowTypeErrorAtom(ctx, "'%s' is read-only", atom);
+ return -1;
+ } else {
+ return FALSE;
+ }
+}
+
+JSValue FORMAT_ATTR(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
+{
+ JSValue val;
+ va_list ap;
+
+ va_start(ap, fmt);
+ val = JS_ThrowError(ctx, JS_REFERENCE_ERROR, fmt, ap);
+ va_end(ap);
+ return val;
+}
+
+JSValue FORMAT_ATTR(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
+{
+ JSValue val;
+ va_list ap;
+
+ va_start(ap, fmt);
+ val = JS_ThrowError(ctx, JS_RANGE_ERROR, fmt, ap);
+ va_end(ap);
+ return val;
+}
+
+JSValue FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
+{
+ JSValue val;
+ va_list ap;
+
+ va_start(ap, fmt);
+ val = JS_ThrowError(ctx, JS_INTERNAL_ERROR, fmt, ap);
+ va_end(ap);
+ return val;
+}
+
+JSValue JS_ThrowOutOfMemory(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ if (!rt->in_out_of_memory) {
+ rt->in_out_of_memory = TRUE;
+ JS_ThrowInternalError(ctx, "out of memory");
+ rt->in_out_of_memory = FALSE;
+ }
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ThrowStackOverflow(JSContext *ctx)
+{
+ return JS_ThrowInternalError(ctx, "stack overflow");
+}
+
+static JSValue JS_ThrowTypeErrorNotAnObject(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "not an object");
+}
+
+static JSValue JS_ThrowTypeErrorNotASymbol(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "not a symbol");
+}
+
+static JSValue JS_ThrowReferenceErrorNotDefined(JSContext *ctx, JSAtom name)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowReferenceError(ctx, "'%s' is not defined",
+ JS_AtomGetStr(ctx, buf, sizeof(buf), name));
+}
+
+static JSValue JS_ThrowReferenceErrorUninitialized(JSContext *ctx, JSAtom name)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowReferenceError(ctx, "%s is not initialized",
+ name == JS_ATOM_NULL ? "lexical variable" :
+ JS_AtomGetStr(ctx, buf, sizeof(buf), name));
+}
+
+static JSValue JS_ThrowReferenceErrorUninitialized2(JSContext *ctx,
+ JSFunctionBytecode *b,
+ int idx, BOOL is_ref)
+{
+ JSAtom atom = JS_ATOM_NULL;
+ if (is_ref) {
+ atom = b->closure_var[idx].var_name;
+ } else {
+ /* not present if the function is stripped and contains no eval() */
+ if (b->vardefs)
+ atom = b->vardefs[b->arg_count + idx].var_name;
+ }
+ return JS_ThrowReferenceErrorUninitialized(ctx, atom);
+}
+
+static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
+{
+ JSRuntime *rt = ctx->rt;
+ JSAtom name;
+ name = rt->class_array[class_id].class_name;
+ return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
+}
+
+static no_inline warn_unused int js_poll_interrupts_impl(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
+ if (rt->interrupt_handler) {
+ if (rt->interrupt_handler(rt, rt->interrupt_opaque)) {
+ /* XXX: should set a specific flag to avoid catching */
+ JS_ThrowInternalError(ctx, "interrupted");
+ JS_SetUncatchableError(ctx, ctx->rt->current_exception, TRUE);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static inline warn_unused int js_poll_interrupts(JSContext *ctx)
+{
+ if (unlikely(--ctx->interrupt_counter <= 0)) {
+ return js_poll_interrupts_impl(ctx);
+ } else {
+ return 0;
+ }
+}
+
+/* return -1 (exception) or TRUE/FALSE */
+static int JS_SetPrototypeInternal(JSContext *ctx, JSValueConst obj,
+ JSValueConst proto_val,
+ BOOL throw_flag)
+{
+ JSObject *proto, *p, *p1;
+ JSShape *sh;
+
+ if (throw_flag) {
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_NULL ||
+ JS_VALUE_GET_TAG(obj) == JS_TAG_UNDEFINED)
+ goto not_obj;
+ } else {
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ goto not_obj;
+ }
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_OBJECT) {
+ if (JS_VALUE_GET_TAG(proto_val) != JS_TAG_NULL) {
+ not_obj:
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ proto = NULL;
+ } else {
+ proto = JS_VALUE_GET_OBJ(proto_val);
+ }
+
+ if (throw_flag && JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return TRUE;
+
+ if (unlikely(p->class_id == JS_CLASS_PROXY))
+ return js_proxy_setPrototypeOf(ctx, obj, proto_val, throw_flag);
+ sh = p->shape;
+ if (sh->proto == proto)
+ return TRUE;
+ if (!p->extensible) {
+ if (throw_flag) {
+ JS_ThrowTypeError(ctx, "object is not extensible");
+ return -1;
+ } else {
+ return FALSE;
+ }
+ }
+ if (proto) {
+ /* check if there is a cycle */
+ p1 = proto;
+ do {
+ if (p1 == p) {
+ if (throw_flag) {
+ JS_ThrowTypeError(ctx, "circular prototype chain");
+ return -1;
+ } else {
+ return FALSE;
+ }
+ }
+ /* Note: for Proxy objects, proto is NULL */
+ p1 = p1->shape->proto;
+ } while (p1 != NULL);
+ JS_DupValue(ctx, proto_val);
+ }
+
+ if (js_shape_prepare_update(ctx, p, NULL))
+ return -1;
+ sh = p->shape;
+ if (sh->proto)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, sh->proto));
+ sh->proto = proto;
+ return TRUE;
+}
+
+/* return -1 (exception) or TRUE/FALSE */
+int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val)
+{
+ return JS_SetPrototypeInternal(ctx, obj, proto_val, TRUE);
+}
+
+/* Only works for primitive types, otherwise return JS_NULL. */
+static JSValueConst JS_GetPrototypePrimitive(JSContext *ctx, JSValueConst val)
+{
+ switch(JS_VALUE_GET_NORM_TAG(val)) {
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ val = ctx->class_proto[JS_CLASS_BIG_INT];
+ break;
+ case JS_TAG_BIG_FLOAT:
+ val = ctx->class_proto[JS_CLASS_BIG_FLOAT];
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ val = ctx->class_proto[JS_CLASS_BIG_DECIMAL];
+ break;
+#endif
+ case JS_TAG_INT:
+ case JS_TAG_FLOAT64:
+ val = ctx->class_proto[JS_CLASS_NUMBER];
+ break;
+ case JS_TAG_BOOL:
+ val = ctx->class_proto[JS_CLASS_BOOLEAN];
+ break;
+ case JS_TAG_STRING:
+ val = ctx->class_proto[JS_CLASS_STRING];
+ break;
+ case JS_TAG_SYMBOL:
+ val = ctx->class_proto[JS_CLASS_SYMBOL];
+ break;
+ case JS_TAG_OBJECT:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ default:
+ val = JS_NULL;
+ break;
+ }
+ return val;
+}
+
+/* Return an Object, JS_NULL or JS_EXCEPTION in case of Proxy object. */
+JSValue JS_GetPrototype(JSContext *ctx, JSValueConst obj)
+{
+ JSValue val;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ JSObject *p;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (unlikely(p->class_id == JS_CLASS_PROXY)) {
+ val = js_proxy_getPrototypeOf(ctx, obj);
+ } else {
+ p = p->shape->proto;
+ if (!p)
+ val = JS_NULL;
+ else
+ val = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ }
+ } else {
+ val = JS_DupValue(ctx, JS_GetPrototypePrimitive(ctx, obj));
+ }
+ return val;
+}
+
+static JSValue JS_GetPrototypeFree(JSContext *ctx, JSValue obj)
+{
+ JSValue obj1;
+ obj1 = JS_GetPrototype(ctx, obj);
+ JS_FreeValue(ctx, obj);
+ return obj1;
+}
+
+/* return TRUE, FALSE or (-1) in case of exception */
+static int JS_OrdinaryIsInstanceOf(JSContext *ctx, JSValueConst val,
+ JSValueConst obj)
+{
+ JSValue obj_proto;
+ JSObject *proto;
+ const JSObject *p, *proto1;
+ BOOL ret;
+
+ if (!JS_IsFunction(ctx, obj))
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id == JS_CLASS_BOUND_FUNCTION) {
+ JSBoundFunction *s = p->u.bound_function;
+ return JS_IsInstanceOf(ctx, val, s->func_obj);
+ }
+
+ /* Only explicitly boxed values are instances of constructors */
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ obj_proto = JS_GetProperty(ctx, obj, JS_ATOM_prototype);
+ if (JS_VALUE_GET_TAG(obj_proto) != JS_TAG_OBJECT) {
+ if (!JS_IsException(obj_proto))
+ JS_ThrowTypeError(ctx, "operand 'prototype' property is not an object");
+ ret = -1;
+ goto done;
+ }
+ proto = JS_VALUE_GET_OBJ(obj_proto);
+ p = JS_VALUE_GET_OBJ(val);
+ for(;;) {
+ proto1 = p->shape->proto;
+ if (!proto1) {
+ /* slow case if proxy in the prototype chain */
+ if (unlikely(p->class_id == JS_CLASS_PROXY)) {
+ JSValue obj1;
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, (JSObject *)p));
+ for(;;) {
+ obj1 = JS_GetPrototypeFree(ctx, obj1);
+ if (JS_IsException(obj1)) {
+ ret = -1;
+ break;
+ }
+ if (JS_IsNull(obj1)) {
+ ret = FALSE;
+ break;
+ }
+ if (proto == JS_VALUE_GET_OBJ(obj1)) {
+ JS_FreeValue(ctx, obj1);
+ ret = TRUE;
+ break;
+ }
+ /* must check for timeout to avoid infinite loop */
+ if (js_poll_interrupts(ctx)) {
+ JS_FreeValue(ctx, obj1);
+ ret = -1;
+ break;
+ }
+ }
+ } else {
+ ret = FALSE;
+ }
+ break;
+ }
+ p = proto1;
+ if (proto == p) {
+ ret = TRUE;
+ break;
+ }
+ }
+done:
+ JS_FreeValue(ctx, obj_proto);
+ return ret;
+}
+
+/* return TRUE, FALSE or (-1) in case of exception */
+int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj)
+{
+ JSValue method;
+
+ if (!JS_IsObject(obj))
+ goto fail;
+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_hasInstance);
+ if (JS_IsException(method))
+ return -1;
+ if (!JS_IsNull(method) && !JS_IsUndefined(method)) {
+ JSValue ret;
+ ret = JS_CallFree(ctx, method, obj, 1, &val);
+ return JS_ToBoolFree(ctx, ret);
+ }
+
+ /* legacy case */
+ if (!JS_IsFunction(ctx, obj)) {
+ fail:
+ JS_ThrowTypeError(ctx, "invalid 'instanceof' right operand");
+ return -1;
+ }
+ return JS_OrdinaryIsInstanceOf(ctx, val, obj);
+}
+
+/* return the value associated to the autoinit property or an exception */
+typedef JSValue JSAutoInitFunc(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque);
+
+static JSAutoInitFunc *js_autoinit_func_table[] = {
+ js_instantiate_prototype, /* JS_AUTOINIT_ID_PROTOTYPE */
+ js_module_ns_autoinit, /* JS_AUTOINIT_ID_MODULE_NS */
+ JS_InstantiateFunctionListItem2, /* JS_AUTOINIT_ID_PROP */
+};
+
+/* warning: 'prs' is reallocated after it */
+static int JS_AutoInitProperty(JSContext *ctx, JSObject *p, JSAtom prop,
+ JSProperty *pr, JSShapeProperty *prs)
+{
+ JSValue val;
+ JSContext *realm;
+ JSAutoInitFunc *func;
+
+ if (js_shape_prepare_update(ctx, p, &prs))
+ return -1;
+
+ realm = js_autoinit_get_realm(pr);
+ func = js_autoinit_func_table[js_autoinit_get_id(pr)];
+ /* 'func' shall not modify the object properties 'pr' */
+ val = func(realm, p, prop, pr->u.init.opaque);
+ js_autoinit_free(ctx->rt, pr);
+ prs->flags &= ~JS_PROP_TMASK;
+ pr->u.value = JS_UNDEFINED;
+ if (JS_IsException(val))
+ return -1;
+ pr->u.value = val;
+ return 0;
+}
+
+JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
+ JSAtom prop, JSValueConst this_obj,
+ BOOL throw_ref_error)
+{
+ JSObject *p;
+ JSProperty *pr;
+ JSShapeProperty *prs;
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_TAG(obj);
+ if (unlikely(tag != JS_TAG_OBJECT)) {
+ switch(tag) {
+ case JS_TAG_NULL:
+ return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of null", prop);
+ case JS_TAG_UNDEFINED:
+ return JS_ThrowTypeErrorAtom(ctx, "cannot read property '%s' of undefined", prop);
+ case JS_TAG_EXCEPTION:
+ return JS_EXCEPTION;
+ case JS_TAG_STRING:
+ {
+ JSString *p1 = JS_VALUE_GET_STRING(obj);
+ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx, ch;
+ idx = JS_AtomToUInt32(prop);
+ if (idx < p1->len) {
+ if (p1->is_wide_char)
+ ch = p1->u.str16[idx];
+ else
+ ch = p1->u.str8[idx];
+ return js_new_string_char(ctx, ch);
+ }
+ } else if (prop == JS_ATOM_length) {
+ return JS_NewInt32(ctx, p1->len);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ /* cannot raise an exception */
+ p = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, obj));
+ if (!p)
+ return JS_UNDEFINED;
+ } else {
+ p = JS_VALUE_GET_OBJ(obj);
+ }
+
+ for(;;) {
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ /* found */
+ if (unlikely(prs->flags & JS_PROP_TMASK)) {
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ if (unlikely(!pr->u.getset.getter)) {
+ return JS_UNDEFINED;
+ } else {
+ JSValue func = JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter);
+ /* Note: the field could be removed in the getter */
+ func = JS_DupValue(ctx, func);
+ return JS_CallFree(ctx, func, this_obj, 0, NULL);
+ }
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ JSValue val = *pr->u.var_ref->pvalue;
+ if (unlikely(JS_IsUninitialized(val)))
+ return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return JS_DupValue(ctx, val);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* Instantiate property and retry */
+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
+ return JS_EXCEPTION;
+ continue;
+ }
+ } else {
+ if (JS_IsUndefined(pr->u.value) && ctx->handleUndefined)
+ ctx->handleUndefined(ctx);
+ return JS_DupValue(ctx, pr->u.value);
+ }
+ }
+ if (unlikely(p->is_exotic)) {
+ /* exotic behaviors */
+ if (p->fast_array) {
+ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ /* we avoid duplicating the code */
+ return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ return JS_UNDEFINED;
+ }
+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ int ret;
+ ret = JS_AtomIsNumericIndex(ctx, prop);
+ if (ret != 0) {
+ if (ret < 0)
+ return JS_EXCEPTION;
+ return JS_UNDEFINED;
+ }
+ }
+ } else {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em) {
+ if (em->get_property) {
+ JSValue obj1, retval;
+ /* XXX: should pass throw_ref_error */
+ /* Note: if 'p' is a prototype, it can be
+ freed in the called function */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ retval = em->get_property(ctx, obj1, prop, this_obj);
+ JS_FreeValue(ctx, obj1);
+ return retval;
+ }
+ if (em->get_own_property) {
+ JSPropertyDescriptor desc;
+ int ret;
+ JSValue obj1;
+
+ /* Note: if 'p' is a prototype, it can be
+ freed in the called function */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = em->get_own_property(ctx, &desc, obj1, prop);
+ JS_FreeValue(ctx, obj1);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ if (ret) {
+ if (desc.flags & JS_PROP_GETSET) {
+ JS_FreeValue(ctx, desc.setter);
+ return JS_CallFree(ctx, desc.getter, this_obj, 0, NULL);
+ } else {
+ return desc.value;
+ }
+ }
+ }
+ }
+ }
+ }
+ p = p->shape->proto;
+ if (!p)
+ break;
+ }
+ if (unlikely(throw_ref_error)) {
+ return JS_ThrowReferenceErrorNotDefined(ctx, prop);
+ } else {
+ return JS_UNDEFINED;
+ }
+}
+
+static JSValue JS_ThrowTypeErrorPrivateNotFound(JSContext *ctx, JSAtom atom)
+{
+ return JS_ThrowTypeErrorAtom(ctx, "private class field '%s' does not exist",
+ atom);
+}
+
+/* Private fields can be added even on non extensible objects or
+ Proxies */
+static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
+ JSValueConst name, JSValue val)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSAtom prop;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ goto fail;
+ }
+ /* safety check */
+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
+ JS_ThrowTypeErrorNotASymbol(ctx);
+ goto fail;
+ }
+ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ JS_ThrowTypeErrorAtom(ctx, "private class field '%s' already exists",
+ prop);
+ goto fail;
+ }
+ pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
+ if (unlikely(!pr)) {
+ fail:
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ pr->u.value = val;
+ return 0;
+}
+
+static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,
+ JSValueConst name)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSAtom prop;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ /* safety check */
+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))
+ return JS_ThrowTypeErrorNotASymbol(ctx);
+ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (!prs) {
+ JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
+ return JS_EXCEPTION;
+ }
+ return JS_DupValue(ctx, pr->u.value);
+}
+
+static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
+ JSValueConst name, JSValue val)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSAtom prop;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ goto fail;
+ }
+ /* safety check */
+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) {
+ JS_ThrowTypeErrorNotASymbol(ctx);
+ goto fail;
+ }
+ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (!prs) {
+ JS_ThrowTypeErrorPrivateNotFound(ctx, prop);
+ fail:
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ set_value(ctx, &pr->u.value, val);
+ return 0;
+}
+
+static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj)
+{
+ JSObject *p, *p1;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSValue brand;
+ JSAtom brand_atom;
+
+ if (unlikely(JS_VALUE_GET_TAG(home_obj) != JS_TAG_OBJECT)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ p = JS_VALUE_GET_OBJ(home_obj);
+ prs = find_own_property(&pr, p, JS_ATOM_Private_brand);
+ if (!prs) {
+ brand = JS_NewSymbolFromAtom(ctx, JS_ATOM_brand, JS_ATOM_TYPE_PRIVATE);
+ if (JS_IsException(brand))
+ return -1;
+ /* if the brand is not present, add it */
+ pr = add_property(ctx, p, JS_ATOM_Private_brand, JS_PROP_C_W_E);
+ if (!pr) {
+ JS_FreeValue(ctx, brand);
+ return -1;
+ }
+ pr->u.value = JS_DupValue(ctx, brand);
+ } else {
+ brand = JS_DupValue(ctx, pr->u.value);
+ }
+ brand_atom = js_symbol_to_atom(ctx, brand);
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ JS_FreeAtom(ctx, brand_atom);
+ return -1;
+ }
+ p1 = JS_VALUE_GET_OBJ(obj);
+ pr = add_property(ctx, p1, brand_atom, JS_PROP_C_W_E);
+ JS_FreeAtom(ctx, brand_atom);
+ if (!pr)
+ return -1;
+ pr->u.value = JS_UNDEFINED;
+ return 0;
+}
+
+static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
+{
+ JSObject *p, *p1, *home_obj;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSValueConst brand;
+
+ /* get the home object of 'func' */
+ if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) {
+ not_obj:
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ p1 = JS_VALUE_GET_OBJ(func);
+ if (!js_class_has_bytecode(p1->class_id))
+ goto not_obj;
+ home_obj = p1->u.func.home_object;
+ if (!home_obj)
+ goto not_obj;
+ prs = find_own_property(&pr, home_obj, JS_ATOM_Private_brand);
+ if (!prs) {
+ JS_ThrowTypeError(ctx, "expecting <brand> private field");
+ return -1;
+ }
+ brand = pr->u.value;
+ /* safety check */
+ if (unlikely(JS_VALUE_GET_TAG(brand) != JS_TAG_SYMBOL))
+ goto not_obj;
+
+ /* get the brand array of 'obj' */
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ goto not_obj;
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand));
+ if (!prs) {
+ JS_ThrowTypeError(ctx, "invalid brand on object");
+ return -1;
+ }
+ return 0;
+}
+
+static uint32_t js_string_obj_get_length(JSContext *ctx,
+ JSValueConst obj)
+{
+ JSObject *p;
+ JSString *p1;
+ uint32_t len = 0;
+
+ /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
+ p1 = JS_VALUE_GET_STRING(p->u.object_data);
+ len = p1->len;
+ }
+ return len;
+}
+
+static int num_keys_cmp(const void *p1, const void *p2, void *opaque)
+{
+ JSContext *ctx = opaque;
+ JSAtom atom1 = ((const JSPropertyEnum *)p1)->atom;
+ JSAtom atom2 = ((const JSPropertyEnum *)p2)->atom;
+ uint32_t v1, v2;
+ BOOL atom1_is_integer, atom2_is_integer;
+
+ atom1_is_integer = JS_AtomIsArrayIndex(ctx, &v1, atom1);
+ atom2_is_integer = JS_AtomIsArrayIndex(ctx, &v2, atom2);
+ assert(atom1_is_integer && atom2_is_integer);
+ if (v1 < v2)
+ return -1;
+ else if (v1 == v2)
+ return 0;
+ else
+ return 1;
+}
+
+static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)
+{
+ uint32_t i;
+ if (tab) {
+ for(i = 0; i < len; i++)
+ JS_FreeAtom(ctx, tab[i].atom);
+ js_free(ctx, tab);
+ }
+}
+
+/* return < 0 in case if exception, 0 if OK. ptab and its atoms must
+ be freed by the user. */
+static int warn_unused JS_GetOwnPropertyNamesInternal(JSContext *ctx,
+ JSPropertyEnum **ptab,
+ uint32_t *plen,
+ JSObject *p, int flags)
+{
+ int i, j;
+ JSShape *sh;
+ JSShapeProperty *prs;
+ JSPropertyEnum *tab_atom, *tab_exotic;
+ JSAtom atom;
+ uint32_t num_keys_count, str_keys_count, sym_keys_count, atom_count;
+ uint32_t num_index, str_index, sym_index, exotic_count, exotic_keys_count;
+ BOOL is_enumerable, num_sorted;
+ uint32_t num_key;
+ JSAtomKindEnum kind;
+
+ /* clear pointer for consistency in case of failure */
+ *ptab = NULL;
+ *plen = 0;
+
+ /* compute the number of returned properties */
+ num_keys_count = 0;
+ str_keys_count = 0;
+ sym_keys_count = 0;
+ exotic_keys_count = 0;
+ exotic_count = 0;
+ tab_exotic = NULL;
+ sh = p->shape;
+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+ atom = prs->atom;
+ if (atom != JS_ATOM_NULL) {
+ is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
+ kind = JS_AtomGetKind(ctx, atom);
+ if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
+ ((flags >> kind) & 1) != 0) {
+ /* need to raise an exception in case of the module
+ name space (implicit GetOwnProperty) */
+ if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) &&
+ (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY))) {
+ JSVarRef *var_ref = p->prop[i].u.var_ref;
+ if (unlikely(JS_IsUninitialized(*var_ref->pvalue))) {
+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return -1;
+ }
+ }
+ if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
+ num_keys_count++;
+ } else if (kind == JS_ATOM_KIND_STRING) {
+ str_keys_count++;
+ } else {
+ sym_keys_count++;
+ }
+ }
+ }
+ }
+
+ if (p->is_exotic) {
+ if (p->fast_array) {
+ if (flags & JS_GPN_STRING_MASK) {
+ num_keys_count += p->u.array.count;
+ }
+ } else if (p->class_id == JS_CLASS_STRING) {
+ if (flags & JS_GPN_STRING_MASK) {
+ num_keys_count += js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ }
+ } else {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em && em->get_own_property_names) {
+ if (em->get_own_property_names(ctx, &tab_exotic, &exotic_count,
+ JS_MKPTR(JS_TAG_OBJECT, p)))
+ return -1;
+ for(i = 0; i < exotic_count; i++) {
+ atom = tab_exotic[i].atom;
+ kind = JS_AtomGetKind(ctx, atom);
+ if (((flags >> kind) & 1) != 0) {
+ is_enumerable = FALSE;
+ if (flags & (JS_GPN_SET_ENUM | JS_GPN_ENUM_ONLY)) {
+ JSPropertyDescriptor desc;
+ int res;
+ /* set the "is_enumerable" field if necessary */
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
+ if (res < 0) {
+ js_free_prop_enum(ctx, tab_exotic, exotic_count);
+ return -1;
+ }
+ if (res) {
+ is_enumerable =
+ ((desc.flags & JS_PROP_ENUMERABLE) != 0);
+ js_free_desc(ctx, &desc);
+ }
+ tab_exotic[i].is_enumerable = is_enumerable;
+ }
+ if (!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) {
+ exotic_keys_count++;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* fill them */
+
+ atom_count = num_keys_count + str_keys_count + sym_keys_count + exotic_keys_count;
+ /* avoid allocating 0 bytes */
+ tab_atom = js_malloc(ctx, sizeof(tab_atom[0]) * max_int(atom_count, 1));
+ if (!tab_atom) {
+ js_free_prop_enum(ctx, tab_exotic, exotic_count);
+ return -1;
+ }
+
+ num_index = 0;
+ str_index = num_keys_count;
+ sym_index = str_index + str_keys_count;
+
+ num_sorted = TRUE;
+ sh = p->shape;
+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+ atom = prs->atom;
+ if (atom != JS_ATOM_NULL) {
+ is_enumerable = ((prs->flags & JS_PROP_ENUMERABLE) != 0);
+ kind = JS_AtomGetKind(ctx, atom);
+ if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
+ ((flags >> kind) & 1) != 0) {
+ if (JS_AtomIsArrayIndex(ctx, &num_key, atom)) {
+ j = num_index++;
+ num_sorted = FALSE;
+ } else if (kind == JS_ATOM_KIND_STRING) {
+ j = str_index++;
+ } else {
+ j = sym_index++;
+ }
+ tab_atom[j].atom = JS_DupAtom(ctx, atom);
+ tab_atom[j].is_enumerable = is_enumerable;
+ }
+ }
+ }
+
+ if (p->is_exotic) {
+ int len;
+ if (p->fast_array) {
+ if (flags & JS_GPN_STRING_MASK) {
+ len = p->u.array.count;
+ goto add_array_keys;
+ }
+ } else if (p->class_id == JS_CLASS_STRING) {
+ if (flags & JS_GPN_STRING_MASK) {
+ len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ add_array_keys:
+ for(i = 0; i < len; i++) {
+ tab_atom[num_index].atom = JS_AtomFromUInt32(i);
+ if (tab_atom[num_index].atom == JS_ATOM_NULL) {
+ js_free_prop_enum(ctx, tab_atom, num_index);
+ return -1;
+ }
+ tab_atom[num_index].is_enumerable = TRUE;
+ num_index++;
+ }
+ }
+ } else {
+ /* Note: exotic keys are not reordered and comes after the object own properties. */
+ for(i = 0; i < exotic_count; i++) {
+ atom = tab_exotic[i].atom;
+ is_enumerable = tab_exotic[i].is_enumerable;
+ kind = JS_AtomGetKind(ctx, atom);
+ if ((!(flags & JS_GPN_ENUM_ONLY) || is_enumerable) &&
+ ((flags >> kind) & 1) != 0) {
+ tab_atom[sym_index].atom = atom;
+ tab_atom[sym_index].is_enumerable = is_enumerable;
+ sym_index++;
+ } else {
+ JS_FreeAtom(ctx, atom);
+ }
+ }
+ js_free(ctx, tab_exotic);
+ }
+ }
+
+ assert(num_index == num_keys_count);
+ assert(str_index == num_keys_count + str_keys_count);
+ assert(sym_index == atom_count);
+
+ if (num_keys_count != 0 && !num_sorted) {
+ rqsort(tab_atom, num_keys_count, sizeof(tab_atom[0]), num_keys_cmp,
+ ctx);
+ }
+ *ptab = tab_atom;
+ *plen = atom_count;
+ return 0;
+}
+
+int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
+ uint32_t *plen, JSValueConst obj, int flags)
+{
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
+ JS_VALUE_GET_OBJ(obj), flags);
+}
+
+/* Return -1 if exception,
+ FALSE if the property does not exist, TRUE if it exists. If TRUE is
+ returned, the property descriptor 'desc' is filled present. */
+static int JS_GetOwnPropertyInternal(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSObject *p, JSAtom prop)
+{
+ JSShapeProperty *prs;
+ JSProperty *pr;
+
+retry:
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ if (desc) {
+ desc->flags = prs->flags & JS_PROP_C_W_E;
+ desc->getter = JS_UNDEFINED;
+ desc->setter = JS_UNDEFINED;
+ desc->value = JS_UNDEFINED;
+ if (unlikely(prs->flags & JS_PROP_TMASK)) {
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ desc->flags |= JS_PROP_GETSET;
+ if (pr->u.getset.getter)
+ desc->getter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
+ if (pr->u.getset.setter)
+ desc->setter = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ JSValue val = *pr->u.var_ref->pvalue;
+ if (unlikely(JS_IsUninitialized(val))) {
+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return -1;
+ }
+ desc->value = JS_DupValue(ctx, val);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* Instantiate property and retry */
+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
+ return -1;
+ goto retry;
+ }
+ } else {
+ desc->value = JS_DupValue(ctx, pr->u.value);
+ }
+ } else {
+ /* for consistency, send the exception even if desc is NULL */
+ if (unlikely((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF)) {
+ if (unlikely(JS_IsUninitialized(*pr->u.var_ref->pvalue))) {
+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return -1;
+ }
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* nothing to do: delay instantiation until actual value and/or attributes are read */
+ }
+ }
+ return TRUE;
+ }
+ if (p->is_exotic) {
+ if (p->fast_array) {
+ /* specific case for fast arrays */
+ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx;
+ idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ if (desc) {
+ desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |
+ JS_PROP_CONFIGURABLE;
+ desc->getter = JS_UNDEFINED;
+ desc->setter = JS_UNDEFINED;
+ desc->value = JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
+ }
+ return TRUE;
+ }
+ }
+ } else {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em && em->get_own_property) {
+ return em->get_own_property(ctx, desc,
+ JS_MKPTR(JS_TAG_OBJECT, p), prop);
+ }
+ }
+ }
+ return FALSE;
+}
+
+int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop)
+{
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ return JS_GetOwnPropertyInternal(ctx, desc, JS_VALUE_GET_OBJ(obj), prop);
+}
+
+/* return -1 if exception (Proxy object only) or TRUE/FALSE */
+int JS_IsExtensible(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (unlikely(p->class_id == JS_CLASS_PROXY))
+ return js_proxy_isExtensible(ctx, obj);
+ else
+ return p->extensible;
+}
+
+/* return -1 if exception (Proxy object only) or TRUE/FALSE */
+int JS_PreventExtensions(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (unlikely(p->class_id == JS_CLASS_PROXY))
+ return js_proxy_preventExtensions(ctx, obj);
+ p->extensible = FALSE;
+ return TRUE;
+}
+
+/* return -1 if exception otherwise TRUE or FALSE */
+int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop)
+{
+ JSObject *p;
+ int ret;
+ JSValue obj1;
+
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ for(;;) {
+ if (p->is_exotic) {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em && em->has_property) {
+ /* has_property can free the prototype */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = em->has_property(ctx, obj1, prop);
+ JS_FreeValue(ctx, obj1);
+ return ret;
+ }
+ }
+ /* JS_GetOwnPropertyInternal can free the prototype */
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, prop);
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ if (ret != 0)
+ return ret;
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ ret = JS_AtomIsNumericIndex(ctx, prop);
+ if (ret != 0) {
+ if (ret < 0)
+ return -1;
+ return FALSE;
+ }
+ }
+ p = p->shape->proto;
+ if (!p)
+ break;
+ }
+ return FALSE;
+}
+
+/* val must be a symbol */
+static JSAtom js_symbol_to_atom(JSContext *ctx, JSValue val)
+{
+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);
+ return js_get_atom_index(ctx->rt, p);
+}
+
+/* return JS_ATOM_NULL in case of exception */
+JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
+{
+ JSAtom atom;
+ uint32_t tag;
+ tag = JS_VALUE_GET_TAG(val);
+ if (tag == JS_TAG_INT &&
+ (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {
+ /* fast path for integer values */
+ atom = JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
+ } else if (tag == JS_TAG_SYMBOL) {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);
+ atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));
+ } else {
+ JSValue str;
+ str = JS_ToPropertyKey(ctx, val);
+ if (JS_IsException(str))
+ return JS_ATOM_NULL;
+ if (JS_VALUE_GET_TAG(str) == JS_TAG_SYMBOL) {
+ atom = js_symbol_to_atom(ctx, str);
+ } else {
+ atom = JS_NewAtomStr(ctx, JS_VALUE_GET_STRING(str));
+ }
+ }
+ return atom;
+}
+
+static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSValue prop)
+{
+ JSAtom atom;
+ JSValue ret;
+
+ if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
+ JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
+ JSObject *p;
+ uint32_t idx, len;
+ /* fast path for array access */
+ p = JS_VALUE_GET_OBJ(this_obj);
+ idx = JS_VALUE_GET_INT(prop);
+ len = p->u.array.count;
+ if (unlikely(idx >= len))
+ goto slow_path;
+ switch(p->class_id) {
+ case JS_CLASS_ARRAY:
+ case JS_CLASS_ARGUMENTS:
+ return JS_DupValue(ctx, p->u.array.u.values[idx]);
+ case JS_CLASS_INT8_ARRAY:
+ return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]);
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]);
+ case JS_CLASS_INT16_ARRAY:
+ return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]);
+ case JS_CLASS_UINT16_ARRAY:
+ return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]);
+ case JS_CLASS_INT32_ARRAY:
+ return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]);
+ case JS_CLASS_UINT32_ARRAY:
+ return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]);
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]);
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ return JS_NewFloat64Impl(ctx, p->u.array.u.float_ptr[idx]);
+ case JS_CLASS_FLOAT64_ARRAY:
+ return JS_NewFloat64Impl(ctx, p->u.array.u.double_ptr[idx]);
+ default:
+ goto slow_path;
+ }
+ } else {
+ slow_path:
+ atom = JS_ValueToAtom(ctx, prop);
+ JS_FreeValue(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ ret = JS_GetProperty(ctx, this_obj, atom);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+ }
+}
+
+JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx)
+{
+ return JS_GetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx));
+}
+
+/* Check if an object has a generalized numeric property. Return value:
+ -1 for exception,
+ TRUE if property exists, stored into *pval,
+ FALSE if proprty does not exist.
+ */
+static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, JSValue *pval)
+{
+ JSValue val = JS_UNDEFINED;
+ JSAtom prop;
+ int present;
+
+ if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {
+ /* fast path */
+ present = JS_HasProperty(ctx, obj, JS_AtomFromUInt32(idx));
+ if (present > 0) {
+ val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
+ if (unlikely(JS_IsException(val)))
+ present = -1;
+ }
+ } else {
+ prop = JS_NewAtomInt64(ctx, idx);
+ present = -1;
+ if (likely(prop != JS_ATOM_NULL)) {
+ present = JS_HasProperty(ctx, obj, prop);
+ if (present > 0) {
+ val = JS_GetProperty(ctx, obj, prop);
+ if (unlikely(JS_IsException(val)))
+ present = -1;
+ }
+ JS_FreeAtom(ctx, prop);
+ }
+ }
+ *pval = val;
+ return present;
+}
+
+static JSValue JS_GetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx)
+{
+ JSAtom prop;
+ JSValue val;
+
+ if ((uint64_t)idx <= INT32_MAX) {
+ /* fast path for fast arrays */
+ return JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
+ }
+ prop = JS_NewAtomInt64(ctx, idx);
+ if (prop == JS_ATOM_NULL)
+ return JS_EXCEPTION;
+
+ val = JS_GetProperty(ctx, obj, prop);
+ JS_FreeAtom(ctx, prop);
+ return val;
+}
+
+JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
+ const char *prop)
+{
+ JSAtom atom;
+ JSValue ret;
+ atom = JS_NewAtom(ctx, prop);
+ ret = JS_GetProperty(ctx, this_obj, atom);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+}
+
+/* Note: the property value is not initialized. Return NULL if memory
+ error. */
+static JSProperty *add_property(JSContext *ctx,
+ JSObject *p, JSAtom prop, int prop_flags)
+{
+ JSShape *sh, *new_sh;
+
+ sh = p->shape;
+ if (sh->is_hashed) {
+ /* try to find an existing shape */
+ new_sh = find_hashed_shape_prop(ctx->rt, sh, prop, prop_flags);
+ if (new_sh) {
+ /* matching shape found: use it */
+ /* the property array may need to be resized */
+ if (new_sh->prop_size != sh->prop_size) {
+ JSProperty *new_prop;
+ new_prop = js_realloc(ctx, p->prop, sizeof(p->prop[0]) *
+ new_sh->prop_size);
+ if (!new_prop)
+ return NULL;
+ p->prop = new_prop;
+ }
+ p->shape = js_dup_shape(new_sh);
+ js_free_shape(ctx->rt, sh);
+ return &p->prop[new_sh->prop_count - 1];
+ } else if (sh->header.ref_count != 1) {
+ /* if the shape is shared, clone it */
+ new_sh = js_clone_shape(ctx, sh);
+ if (!new_sh)
+ return NULL;
+ /* hash the cloned shape */
+ new_sh->is_hashed = TRUE;
+ js_shape_hash_link(ctx->rt, new_sh);
+ js_free_shape(ctx->rt, p->shape);
+ p->shape = new_sh;
+ }
+ }
+ assert(p->shape->header.ref_count == 1);
+ if (add_shape_property(ctx, &p->shape, p, prop, prop_flags))
+ return NULL;
+ return &p->prop[p->shape->prop_count - 1];
+}
+
+/* can be called on Array or Arguments objects. return < 0 if
+ memory alloc error. */
+static no_inline warn_unused int convert_fast_array_to_array(JSContext *ctx,
+ JSObject *p)
+{
+ JSProperty *pr;
+ JSShape *sh;
+ JSValue *tab;
+ uint32_t i, len, new_count;
+
+ if (js_shape_prepare_update(ctx, p, NULL))
+ return -1;
+ len = p->u.array.count;
+ /* resize the properties once to simplify the error handling */
+ sh = p->shape;
+ new_count = sh->prop_count + len;
+ if (new_count > sh->prop_size) {
+ if (resize_properties(ctx, &p->shape, p, new_count))
+ return -1;
+ }
+
+ tab = p->u.array.u.values;
+ for(i = 0; i < len; i++) {
+ /* add_property cannot fail here but
+ JS_AtomFromUInt32(i) fails for i > INT32_MAX */
+ pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E);
+ pr->u.value = *tab++;
+ }
+ js_free(ctx, p->u.array.u.values);
+ p->u.array.count = 0;
+ p->u.array.u.values = NULL; /* fail safe */
+ p->u.array.u1.size = 0;
+ p->fast_array = 0;
+ return 0;
+}
+
+static int delete_property(JSContext *ctx, JSObject *p, JSAtom atom)
+{
+ JSShape *sh;
+ JSShapeProperty *pr, *lpr, *prop;
+ JSProperty *pr1;
+ uint32_t lpr_idx;
+ intptr_t h, h1;
+
+ redo:
+ sh = p->shape;
+ h1 = atom & sh->prop_hash_mask;
+ h = prop_hash_end(sh)[-h1 - 1];
+ prop = get_shape_prop(sh);
+ lpr = NULL;
+ lpr_idx = 0; /* prevent warning */
+ while (h != 0) {
+ pr = &prop[h - 1];
+ if (likely(pr->atom == atom)) {
+ /* found ! */
+ if (!(pr->flags & JS_PROP_CONFIGURABLE))
+ return FALSE;
+ /* realloc the shape if needed */
+ if (lpr)
+ lpr_idx = lpr - get_shape_prop(sh);
+ if (js_shape_prepare_update(ctx, p, &pr))
+ return -1;
+ sh = p->shape;
+ /* remove property */
+ if (lpr) {
+ lpr = get_shape_prop(sh) + lpr_idx;
+ lpr->hash_next = pr->hash_next;
+ } else {
+ prop_hash_end(sh)[-h1 - 1] = pr->hash_next;
+ }
+ sh->deleted_prop_count++;
+ /* free the entry */
+ pr1 = &p->prop[h - 1];
+ free_property(ctx->rt, pr1, pr->flags);
+ JS_FreeAtom(ctx, pr->atom);
+ /* put default values */
+ pr->flags = 0;
+ pr->atom = JS_ATOM_NULL;
+ pr1->u.value = JS_UNDEFINED;
+
+ /* compact the properties if too many deleted properties */
+ if (sh->deleted_prop_count >= 8 &&
+ sh->deleted_prop_count >= ((unsigned)sh->prop_count / 2)) {
+ compact_properties(ctx, p);
+ }
+ return TRUE;
+ }
+ lpr = pr;
+ h = pr->hash_next;
+ }
+
+ if (p->is_exotic) {
+ if (p->fast_array) {
+ uint32_t idx;
+ if (JS_AtomIsArrayIndex(ctx, &idx, atom) &&
+ idx < p->u.array.count) {
+ if (p->class_id == JS_CLASS_ARRAY ||
+ p->class_id == JS_CLASS_ARGUMENTS) {
+ /* Special case deleting the last element of a fast Array */
+ if (idx == p->u.array.count - 1) {
+ JS_FreeValue(ctx, p->u.array.u.values[idx]);
+ p->u.array.count = idx;
+ return TRUE;
+ }
+ if (convert_fast_array_to_array(ctx, p))
+ return -1;
+ goto redo;
+ } else {
+ return FALSE;
+ }
+ }
+ } else {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em && em->delete_property) {
+ return em->delete_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p), atom);
+ }
+ }
+ }
+ /* not found */
+ return TRUE;
+}
+
+static int call_setter(JSContext *ctx, JSObject *setter,
+ JSValueConst this_obj, JSValue val, int flags)
+{
+ JSValue ret, func;
+ if (likely(setter)) {
+ func = JS_MKPTR(JS_TAG_OBJECT, setter);
+ /* Note: the field could be removed in the setter */
+ func = JS_DupValue(ctx, func);
+ ret = JS_CallFree(ctx, func, this_obj, 1, &val);
+ JS_FreeValue(ctx, val);
+ if (JS_IsException(ret))
+ return -1;
+ JS_FreeValue(ctx, ret);
+ return TRUE;
+ } else {
+ JS_FreeValue(ctx, val);
+ if ((flags & JS_PROP_THROW) ||
+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
+ JS_ThrowTypeError(ctx, "no setter for property");
+ return -1;
+ }
+ return FALSE;
+ }
+}
+
+/* set the array length and remove the array elements if necessary. */
+static int set_array_length(JSContext *ctx, JSObject *p, JSValue val,
+ int flags)
+{
+ uint32_t len, idx, cur_len;
+ int i, ret;
+
+ /* Note: this call can reallocate the properties of 'p' */
+ ret = JS_ToArrayLengthFree(ctx, &len, val, FALSE);
+ if (ret)
+ return -1;
+ /* JS_ToArrayLengthFree() must be done before the read-only test */
+ if (unlikely(!(p->shape->prop[0].flags & JS_PROP_WRITABLE)))
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
+
+ if (likely(p->fast_array)) {
+ uint32_t old_len = p->u.array.count;
+ if (len < old_len) {
+ for(i = len; i < old_len; i++) {
+ JS_FreeValue(ctx, p->u.array.u.values[i]);
+ }
+ p->u.array.count = len;
+ }
+ p->prop[0].u.value = JS_NewUint32(ctx, len);
+ } else {
+ /* Note: length is always a uint32 because the object is an
+ array */
+ JS_ToUint32(ctx, &cur_len, p->prop[0].u.value);
+ if (len < cur_len) {
+ uint32_t d;
+ JSShape *sh;
+ JSShapeProperty *pr;
+
+ d = cur_len - len;
+ sh = p->shape;
+ if (d <= sh->prop_count) {
+ JSAtom atom;
+
+ /* faster to iterate */
+ while (cur_len > len) {
+ atom = JS_NewAtomUInt32(ctx, cur_len - 1);
+ ret = delete_property(ctx, p, atom);
+ JS_FreeAtom(ctx, atom);
+ if (unlikely(!ret)) {
+ /* unlikely case: property is not
+ configurable */
+ break;
+ }
+ cur_len--;
+ }
+ } else {
+ /* faster to iterate thru all the properties. Need two
+ passes in case one of the property is not
+ configurable */
+ cur_len = len;
+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
+ i++, pr++) {
+ if (pr->atom != JS_ATOM_NULL &&
+ JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
+ if (idx >= cur_len &&
+ !(pr->flags & JS_PROP_CONFIGURABLE)) {
+ cur_len = idx + 1;
+ }
+ }
+ }
+
+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count;
+ i++, pr++) {
+ if (pr->atom != JS_ATOM_NULL &&
+ JS_AtomIsArrayIndex(ctx, &idx, pr->atom)) {
+ if (idx >= cur_len) {
+ /* remove the property */
+ delete_property(ctx, p, pr->atom);
+ /* WARNING: the shape may have been modified */
+ sh = p->shape;
+ pr = get_shape_prop(sh) + i;
+ }
+ }
+ }
+ }
+ } else {
+ cur_len = len;
+ }
+ set_value(ctx, &p->prop[0].u.value, JS_NewUint32(ctx, cur_len));
+ if (unlikely(cur_len > len)) {
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "not configurable");
+ }
+ }
+ return TRUE;
+}
+
+/* return -1 if exception */
+static int expand_fast_array(JSContext *ctx, JSObject *p, uint32_t new_len)
+{
+ uint32_t new_size;
+ size_t slack;
+ JSValue *new_array_prop;
+ /* XXX: potential arithmetic overflow */
+ new_size = max_int(new_len, p->u.array.u1.size * 3 / 2);
+ new_array_prop = js_realloc2(ctx, p->u.array.u.values, sizeof(JSValue) * new_size, &slack);
+ if (!new_array_prop)
+ return -1;
+ new_size += slack / sizeof(*new_array_prop);
+ p->u.array.u.values = new_array_prop;
+ p->u.array.u1.size = new_size;
+ return 0;
+}
+
+/* Preconditions: 'p' must be of class JS_CLASS_ARRAY, p->fast_array =
+ TRUE and p->extensible = TRUE */
+static int add_fast_array_element(JSContext *ctx, JSObject *p,
+ JSValue val, int flags)
+{
+ uint32_t new_len, array_len;
+ /* extend the array by one */
+ /* XXX: convert to slow array if new_len > 2^31-1 elements */
+ new_len = p->u.array.count + 1;
+ /* update the length if necessary. We assume that if the length is
+ not an integer, then if it >= 2^31. */
+ if (likely(JS_VALUE_GET_TAG(p->prop[0].u.value) == JS_TAG_INT)) {
+ array_len = JS_VALUE_GET_INT(p->prop[0].u.value);
+ if (new_len > array_len) {
+ if (unlikely(!(get_shape_prop(p->shape)->flags & JS_PROP_WRITABLE))) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
+ }
+ p->prop[0].u.value = JS_NewInt32(ctx, new_len);
+ }
+ }
+ if (unlikely(new_len > p->u.array.u1.size)) {
+ if (expand_fast_array(ctx, p, new_len)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ }
+ p->u.array.u.values[new_len - 1] = val;
+ p->u.array.count = new_len;
+ return TRUE;
+}
+
+static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc)
+{
+ JS_FreeValue(ctx, desc->getter);
+ JS_FreeValue(ctx, desc->setter);
+ JS_FreeValue(ctx, desc->value);
+}
+
+/* generic (and slower) version of JS_SetProperty() for
+ * Reflect.set(). 'obj' must be an object. */
+static int JS_SetPropertyGeneric(JSContext *ctx,
+ JSValueConst obj, JSAtom prop,
+ JSValue val, JSValueConst this_obj,
+ int flags)
+{
+ int ret;
+ JSPropertyDescriptor desc;
+ JSValue obj1;
+ JSObject *p;
+
+ obj1 = JS_DupValue(ctx, obj);
+ for(;;) {
+ p = JS_VALUE_GET_OBJ(obj1);
+ if (p->is_exotic) {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em && em->set_property) {
+ ret = em->set_property(ctx, obj1, prop,
+ val, this_obj, flags);
+ JS_FreeValue(ctx, obj1);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ }
+
+ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (ret < 0) {
+ JS_FreeValue(ctx, obj1);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ if (ret) {
+ if (desc.flags & JS_PROP_GETSET) {
+ JSObject *setter;
+ if (JS_IsUndefined(desc.setter))
+ setter = NULL;
+ else
+ setter = JS_VALUE_GET_OBJ(desc.setter);
+ ret = call_setter(ctx, setter, this_obj, val, flags);
+ JS_FreeValue(ctx, desc.getter);
+ JS_FreeValue(ctx, desc.setter);
+ JS_FreeValue(ctx, obj1);
+ return ret;
+ } else {
+ JS_FreeValue(ctx, desc.value);
+ if (!(desc.flags & JS_PROP_WRITABLE)) {
+ JS_FreeValue(ctx, obj1);
+ goto read_only_error;
+ }
+ }
+ break;
+ }
+ /* Note: at this point 'obj1' cannot be a proxy. XXX: may have
+ to check recursion */
+ obj1 = JS_GetPrototypeFree(ctx, obj1);
+ if (JS_IsNull(obj1))
+ break;
+ }
+ JS_FreeValue(ctx, obj1);
+
+ if (!JS_IsObject(this_obj)) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "receiver is not an object");
+ }
+
+ p = JS_VALUE_GET_OBJ(this_obj);
+
+ /* modify the property in this_obj if it already exists */
+ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (ret < 0) {
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ if (ret) {
+ if (desc.flags & JS_PROP_GETSET) {
+ JS_FreeValue(ctx, desc.getter);
+ JS_FreeValue(ctx, desc.setter);
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "setter is forbidden");
+ } else {
+ JS_FreeValue(ctx, desc.value);
+ if (!(desc.flags & JS_PROP_WRITABLE) ||
+ p->class_id == JS_CLASS_MODULE_NS) {
+ read_only_error:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
+ }
+ }
+ ret = JS_DefineProperty(ctx, this_obj, prop, val,
+ JS_UNDEFINED, JS_UNDEFINED,
+ JS_PROP_HAS_VALUE);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+
+ ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
+ flags |
+ JS_PROP_HAS_VALUE |
+ JS_PROP_HAS_ENUMERABLE |
+ JS_PROP_HAS_WRITABLE |
+ JS_PROP_HAS_CONFIGURABLE |
+ JS_PROP_C_W_E);
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+/* return -1 in case of exception or TRUE or FALSE. Warning: 'val' is
+ freed by the function. 'flags' is a bitmask of JS_PROP_NO_ADD,
+ JS_PROP_THROW or JS_PROP_THROW_STRICT. If JS_PROP_NO_ADD is set,
+ the new property is not added and an error is raised. */
+int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValue val, int flags)
+{
+ JSObject *p, *p1;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ uint32_t tag;
+ JSPropertyDescriptor desc;
+ int ret;
+#if 0
+ printf("JS_SetPropertyInternal: "); print_atom(ctx, prop); printf("\n");
+#endif
+ tag = JS_VALUE_GET_TAG(this_obj);
+ if (unlikely(tag != JS_TAG_OBJECT)) {
+ switch(tag) {
+ case JS_TAG_NULL:
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of null", prop);
+ return -1;
+ case JS_TAG_UNDEFINED:
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeErrorAtom(ctx, "cannot set property '%s' of undefined", prop);
+ return -1;
+ default:
+ /* even on a primitive type we can have setters on the prototype */
+ p = NULL;
+ p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj));
+ goto prototype_lookup;
+ }
+ }
+ p = JS_VALUE_GET_OBJ(this_obj);
+retry:
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE |
+ JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) {
+ /* fast case */
+ set_value(ctx, &pr->u.value, val);
+ return TRUE;
+ } else if (prs->flags & JS_PROP_LENGTH) {
+ assert(p->class_id == JS_CLASS_ARRAY);
+ assert(prop == JS_ATOM_length);
+ return set_array_length(ctx, p, val, flags);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ /* JS_PROP_WRITABLE is always true for variable
+ references, but they are write protected in module name
+ spaces. */
+ if (p->class_id == JS_CLASS_MODULE_NS)
+ goto read_only_prop;
+ set_value(ctx, pr->u.var_ref->pvalue, val);
+ return TRUE;
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* Instantiate property and retry (potentially useless) */
+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ goto retry;
+ } else {
+ goto read_only_prop;
+ }
+ }
+
+ p1 = p;
+ for(;;) {
+ if (p1->is_exotic) {
+ if (p1->fast_array) {
+ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx < p1->u.array.count) {
+ if (unlikely(p == p1))
+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);
+ else
+ break;
+ } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ goto typed_array_oob;
+ }
+ } else if (p1->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p1->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ ret = JS_AtomIsNumericIndex(ctx, prop);
+ if (ret != 0) {
+ if (ret < 0) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ typed_array_oob:
+ val = JS_ToNumberFree(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (JS_IsException(val))
+ return -1;
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");
+ }
+ }
+ } else {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p1->class_id].exotic;
+ if (em) {
+ JSValue obj1;
+ if (em->set_property) {
+ /* set_property can free the prototype */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
+ ret = em->set_property(ctx, obj1, prop,
+ val, this_obj, flags);
+ JS_FreeValue(ctx, obj1);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ if (em->get_own_property) {
+ /* get_own_property can free the prototype */
+ obj1 = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
+ ret = em->get_own_property(ctx, &desc,
+ obj1, prop);
+ JS_FreeValue(ctx, obj1);
+ if (ret < 0) {
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ if (ret) {
+ if (desc.flags & JS_PROP_GETSET) {
+ JSObject *setter;
+ if (JS_IsUndefined(desc.setter))
+ setter = NULL;
+ else
+ setter = JS_VALUE_GET_OBJ(desc.setter);
+ ret = call_setter(ctx, setter, this_obj, val, flags);
+ JS_FreeValue(ctx, desc.getter);
+ JS_FreeValue(ctx, desc.setter);
+ return ret;
+ } else {
+ JS_FreeValue(ctx, desc.value);
+ if (!(desc.flags & JS_PROP_WRITABLE))
+ goto read_only_prop;
+ if (likely(p == p1)) {
+ ret = JS_DefineProperty(ctx, this_obj, prop, val,
+ JS_UNDEFINED, JS_UNDEFINED,
+ JS_PROP_HAS_VALUE);
+ JS_FreeValue(ctx, val);
+ return ret;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ p1 = p1->shape->proto;
+ prototype_lookup:
+ if (!p1)
+ break;
+
+ retry2:
+ prs = find_own_property(&pr, p1, prop);
+ if (prs) {
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ return call_setter(ctx, pr->u.getset.setter, this_obj, val, flags);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* Instantiate property and retry (potentially useless) */
+ if (JS_AutoInitProperty(ctx, p1, prop, pr, prs))
+ return -1;
+ goto retry2;
+ } else if (!(prs->flags & JS_PROP_WRITABLE)) {
+ read_only_prop:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, prop);
+ }
+ }
+ }
+
+ if (unlikely(flags & JS_PROP_NO_ADD)) {
+ JS_FreeValue(ctx, val);
+ JS_ThrowReferenceErrorNotDefined(ctx, prop);
+ return -1;
+ }
+
+ if (unlikely(!p)) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "not an object");
+ }
+
+ if (unlikely(!p->extensible)) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
+ }
+
+ if (p->is_exotic) {
+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
+ JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx == p->u.array.count) {
+ /* fast case */
+ return add_fast_array_element(ctx, p, val, flags);
+ } else {
+ goto generic_create_prop;
+ }
+ } else {
+ generic_create_prop:
+ ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED,
+ flags |
+ JS_PROP_HAS_VALUE |
+ JS_PROP_HAS_ENUMERABLE |
+ JS_PROP_HAS_WRITABLE |
+ JS_PROP_HAS_CONFIGURABLE |
+ JS_PROP_C_W_E);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ }
+
+ pr = add_property(ctx, p, prop, JS_PROP_C_W_E);
+ if (unlikely(!pr)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ pr->u.value = val;
+ return TRUE;
+}
+
+/* flags can be JS_PROP_THROW or JS_PROP_THROW_STRICT */
+static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSValue prop, JSValue val, int flags)
+{
+ if (likely(JS_VALUE_GET_TAG(this_obj) == JS_TAG_OBJECT &&
+ JS_VALUE_GET_TAG(prop) == JS_TAG_INT)) {
+ JSObject *p;
+ uint32_t idx;
+ double d;
+ int32_t v;
+
+ /* fast path for array access */
+ p = JS_VALUE_GET_OBJ(this_obj);
+ idx = JS_VALUE_GET_INT(prop);
+ switch(p->class_id) {
+ case JS_CLASS_ARRAY:
+ if (unlikely(idx >= (uint32_t)p->u.array.count)) {
+ JSObject *p1;
+ JSShape *sh1;
+
+ /* fast path to add an element to the array */
+ if (idx != p->u.array.count ||
+ !p->fast_array || !p->extensible)
+ goto slow_path;
+ /* check if prototype chain has a numeric property */
+ p1 = p->shape->proto;
+ while (p1 != NULL) {
+ sh1 = p1->shape;
+ if (p1->class_id == JS_CLASS_ARRAY) {
+ if (unlikely(!p1->fast_array))
+ goto slow_path;
+ } else if (p1->class_id == JS_CLASS_OBJECT) {
+ if (unlikely(sh1->has_small_array_index))
+ goto slow_path;
+ } else {
+ goto slow_path;
+ }
+ p1 = sh1->proto;
+ }
+ /* add element */
+ return add_fast_array_element(ctx, p, val, flags);
+ }
+ set_value(ctx, &p->u.array.u.values[idx], val);
+ break;
+ case JS_CLASS_ARGUMENTS:
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto slow_path;
+ set_value(ctx, &p->u.array.u.values[idx], val);
+ break;
+ case JS_CLASS_UINT8C_ARRAY:
+ if (JS_ToUint8ClampFree(ctx, &v, val))
+ return -1;
+ /* Note: the conversion can detach the typed array, so the
+ array bound check must be done after */
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.uint8_ptr[idx] = v;
+ break;
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ if (JS_ToInt32Free(ctx, &v, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.uint8_ptr[idx] = v;
+ break;
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ if (JS_ToInt32Free(ctx, &v, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.uint16_ptr[idx] = v;
+ break;
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+ if (JS_ToInt32Free(ctx, &v, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.uint32_ptr[idx] = v;
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ /* XXX: need specific conversion function */
+ {
+ int64_t v;
+ if (JS_ToBigInt64Free(ctx, &v, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.uint64_ptr[idx] = v;
+ }
+ break;
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count))
+ goto ta_out_of_bound;
+ p->u.array.u.float_ptr[idx] = d;
+ break;
+ case JS_CLASS_FLOAT64_ARRAY:
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return -1;
+ if (unlikely(idx >= (uint32_t)p->u.array.count)) {
+ ta_out_of_bound:
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound numeric index");
+ }
+ p->u.array.u.double_ptr[idx] = d;
+ break;
+ default:
+ goto slow_path;
+ }
+ return TRUE;
+ } else {
+ JSAtom atom;
+ int ret;
+ slow_path:
+ atom = JS_ValueToAtom(ctx, prop);
+ JS_FreeValue(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+ }
+}
+
+int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx, JSValue val)
+{
+ return JS_SetPropertyValue(ctx, this_obj, JS_NewUint32(ctx, idx), val,
+ JS_PROP_THROW);
+}
+
+int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,
+ int64_t idx, JSValue val)
+{
+ JSAtom prop;
+ int res;
+
+ if ((uint64_t)idx <= INT32_MAX) {
+ /* fast path for fast arrays */
+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val,
+ JS_PROP_THROW);
+ }
+ prop = JS_NewAtomInt64(ctx, idx);
+ if (prop == JS_ATOM_NULL) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ res = JS_SetProperty(ctx, this_obj, prop, val);
+ JS_FreeAtom(ctx, prop);
+ return res;
+}
+
+int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
+ const char *prop, JSValue val)
+{
+ JSAtom atom;
+ int ret;
+ atom = JS_NewAtom(ctx, prop);
+ ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+}
+
+/* compute the property flags. For each flag: (JS_PROP_HAS_x forces
+ it, otherwise def_flags is used)
+ Note: makes assumption about the bit pattern of the flags
+*/
+static int get_prop_flags(int flags, int def_flags)
+{
+ int mask;
+ mask = (flags >> JS_PROP_HAS_SHIFT) & JS_PROP_C_W_E;
+ return (flags & mask) | (def_flags & ~mask);
+}
+
+static int JS_CreateProperty(JSContext *ctx, JSObject *p,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter,
+ int flags)
+{
+ JSProperty *pr;
+ int ret, prop_flags;
+
+ /* add a new property or modify an existing exotic one */
+ if (p->is_exotic) {
+ if (p->class_id == JS_CLASS_ARRAY) {
+ uint32_t idx, len;
+
+ if (p->fast_array) {
+ if (JS_AtomIsTaggedInt(prop)) {
+ idx = JS_AtomToUInt32(prop);
+ if (idx == p->u.array.count) {
+ if (!p->extensible)
+ goto not_extensible;
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET))
+ goto convert_to_array;
+ prop_flags = get_prop_flags(flags, 0);
+ if (prop_flags != JS_PROP_C_W_E)
+ goto convert_to_array;
+ return add_fast_array_element(ctx, p,
+ JS_DupValue(ctx, val), flags);
+ } else {
+ goto convert_to_array;
+ }
+ } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
+ /* convert the fast array to normal array */
+ convert_to_array:
+ if (convert_fast_array_to_array(ctx, p))
+ return -1;
+ goto generic_array;
+ }
+ } else if (JS_AtomIsArrayIndex(ctx, &idx, prop)) {
+ JSProperty *plen;
+ JSShapeProperty *pslen;
+ generic_array:
+ /* update the length field */
+ plen = &p->prop[0];
+ JS_ToUint32(ctx, &len, plen->u.value);
+ if ((idx + 1) > len) {
+ pslen = get_shape_prop(p->shape);
+ if (unlikely(!(pslen->flags & JS_PROP_WRITABLE)))
+ return JS_ThrowTypeErrorReadOnly(ctx, flags, JS_ATOM_length);
+ /* XXX: should update the length after defining
+ the property */
+ len = idx + 1;
+ set_value(ctx, &plen->u.value, JS_NewUint32(ctx, len));
+ }
+ }
+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ ret = JS_AtomIsNumericIndex(ctx, prop);
+ if (ret != 0) {
+ if (ret < 0)
+ return -1;
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "cannot create numeric index in typed array");
+ }
+ } else if (!(flags & JS_PROP_NO_EXOTIC)) {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ if (em) {
+ if (em->define_own_property) {
+ return em->define_own_property(ctx, JS_MKPTR(JS_TAG_OBJECT, p),
+ prop, val, getter, setter, flags);
+ }
+ ret = JS_IsExtensible(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ if (ret < 0)
+ return -1;
+ if (!ret)
+ goto not_extensible;
+ }
+ }
+ }
+
+ if (!p->extensible) {
+ not_extensible:
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "object is not extensible");
+ }
+
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ prop_flags = (flags & (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
+ JS_PROP_GETSET;
+ } else {
+ prop_flags = flags & JS_PROP_C_W_E;
+ }
+ pr = add_property(ctx, p, prop, prop_flags);
+ if (unlikely(!pr))
+ return -1;
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ pr->u.getset.getter = NULL;
+ if ((flags & JS_PROP_HAS_GET) && JS_IsFunction(ctx, getter)) {
+ pr->u.getset.getter =
+ JS_VALUE_GET_OBJ(JS_DupValue(ctx, getter));
+ }
+ pr->u.getset.setter = NULL;
+ if ((flags & JS_PROP_HAS_SET) && JS_IsFunction(ctx, setter)) {
+ pr->u.getset.setter =
+ JS_VALUE_GET_OBJ(JS_DupValue(ctx, setter));
+ }
+ } else {
+ if (flags & JS_PROP_HAS_VALUE) {
+ pr->u.value = JS_DupValue(ctx, val);
+ } else {
+ pr->u.value = JS_UNDEFINED;
+ }
+ }
+ return TRUE;
+}
+
+/* return FALSE if not OK */
+static BOOL check_define_prop_flags(int prop_flags, int flags)
+{
+ BOOL has_accessor, is_getset;
+
+ if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
+ if ((flags & (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) ==
+ (JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE)) {
+ return FALSE;
+ }
+ if ((flags & JS_PROP_HAS_ENUMERABLE) &&
+ (flags & JS_PROP_ENUMERABLE) != (prop_flags & JS_PROP_ENUMERABLE))
+ return FALSE;
+ }
+ if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ if (!(prop_flags & JS_PROP_CONFIGURABLE)) {
+ has_accessor = ((flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) != 0);
+ is_getset = ((prop_flags & JS_PROP_TMASK) == JS_PROP_GETSET);
+ if (has_accessor != is_getset)
+ return FALSE;
+ if (!has_accessor && !is_getset && !(prop_flags & JS_PROP_WRITABLE)) {
+ /* not writable: cannot set the writable bit */
+ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
+ (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE))
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+/* ensure that the shape can be safely modified */
+static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
+ JSShapeProperty **pprs)
+{
+ JSShape *sh;
+ uint32_t idx = 0; /* prevent warning */
+
+ sh = p->shape;
+ if (sh->is_hashed) {
+ if (sh->header.ref_count != 1) {
+ if (pprs)
+ idx = *pprs - get_shape_prop(sh);
+ /* clone the shape (the resulting one is no longer hashed) */
+ sh = js_clone_shape(ctx, sh);
+ if (!sh)
+ return -1;
+ js_free_shape(ctx->rt, p->shape);
+ p->shape = sh;
+ if (pprs)
+ *pprs = get_shape_prop(sh) + idx;
+ } else {
+ js_shape_hash_unlink(ctx->rt, sh);
+ sh->is_hashed = FALSE;
+ }
+ }
+ return 0;
+}
+
+static int js_update_property_flags(JSContext *ctx, JSObject *p,
+ JSShapeProperty **pprs, int flags)
+{
+ if (flags != (*pprs)->flags) {
+ if (js_shape_prepare_update(ctx, p, pprs))
+ return -1;
+ (*pprs)->flags = flags;
+ }
+ return 0;
+}
+
+/* allowed flags:
+ JS_PROP_CONFIGURABLE, JS_PROP_WRITABLE, JS_PROP_ENUMERABLE
+ JS_PROP_HAS_GET, JS_PROP_HAS_SET, JS_PROP_HAS_VALUE,
+ JS_PROP_HAS_CONFIGURABLE, JS_PROP_HAS_WRITABLE, JS_PROP_HAS_ENUMERABLE,
+ JS_PROP_THROW, JS_PROP_NO_EXOTIC.
+ If JS_PROP_THROW is set, return an exception instead of FALSE.
+ if JS_PROP_NO_EXOTIC is set, do not call the exotic
+ define_own_property callback.
+ return -1 (exception), FALSE or TRUE.
+*/
+int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter, int flags)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ int mask, res;
+
+ if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ p = JS_VALUE_GET_OBJ(this_obj);
+
+ redo_prop_update:
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ /* the range of the Array length property is always tested before */
+ if ((prs->flags & JS_PROP_LENGTH) && (flags & JS_PROP_HAS_VALUE)) {
+ uint32_t array_length;
+ if (JS_ToArrayLengthFree(ctx, &array_length,
+ JS_DupValue(ctx, val), FALSE)) {
+ return -1;
+ }
+ /* this code relies on the fact that Uint32 are never allocated */
+ val = JS_NewUint32(ctx, array_length);
+ /* prs may have been modified */
+ prs = find_own_property(&pr, p, prop);
+ assert(prs != NULL);
+ }
+ /* property already exists */
+ if (!check_define_prop_flags(prs->flags, flags)) {
+ not_configurable:
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
+ }
+
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ /* Instantiate property and retry */
+ if (JS_AutoInitProperty(ctx, p, prop, pr, prs))
+ return -1;
+ goto redo_prop_update;
+ }
+
+ if (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE |
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ JSObject *new_getter, *new_setter;
+
+ if (JS_IsFunction(ctx, getter)) {
+ new_getter = JS_VALUE_GET_OBJ(getter);
+ } else {
+ new_getter = NULL;
+ }
+ if (JS_IsFunction(ctx, setter)) {
+ new_setter = JS_VALUE_GET_OBJ(setter);
+ } else {
+ new_setter = NULL;
+ }
+
+ if ((prs->flags & JS_PROP_TMASK) != JS_PROP_GETSET) {
+ if (js_shape_prepare_update(ctx, p, &prs))
+ return -1;
+ /* convert to getset */
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ free_var_ref(ctx->rt, pr->u.var_ref);
+ } else {
+ JS_FreeValue(ctx, pr->u.value);
+ }
+ prs->flags = (prs->flags &
+ (JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE)) |
+ JS_PROP_GETSET;
+ pr->u.getset.getter = NULL;
+ pr->u.getset.setter = NULL;
+ } else {
+ if (!(prs->flags & JS_PROP_CONFIGURABLE)) {
+ if ((flags & JS_PROP_HAS_GET) &&
+ new_getter != pr->u.getset.getter) {
+ goto not_configurable;
+ }
+ if ((flags & JS_PROP_HAS_SET) &&
+ new_setter != pr->u.getset.setter) {
+ goto not_configurable;
+ }
+ }
+ }
+ if (flags & JS_PROP_HAS_GET) {
+ if (pr->u.getset.getter)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
+ if (new_getter)
+ JS_DupValue(ctx, getter);
+ pr->u.getset.getter = new_getter;
+ }
+ if (flags & JS_PROP_HAS_SET) {
+ if (pr->u.getset.setter)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+ if (new_setter)
+ JS_DupValue(ctx, setter);
+ pr->u.getset.setter = new_setter;
+ }
+ } else {
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ /* convert to data descriptor */
+ if (js_shape_prepare_update(ctx, p, &prs))
+ return -1;
+ if (pr->u.getset.getter)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.getter));
+ if (pr->u.getset.setter)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, pr->u.getset.setter));
+ prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
+ pr->u.value = JS_UNDEFINED;
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ /* Note: JS_PROP_VARREF is always writable */
+ } else {
+ if ((prs->flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
+ (flags & JS_PROP_HAS_VALUE)) {
+ if (!js_same_value(ctx, val, pr->u.value)) {
+ goto not_configurable;
+ } else {
+ return TRUE;
+ }
+ }
+ }
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ if (flags & JS_PROP_HAS_VALUE) {
+ if (p->class_id == JS_CLASS_MODULE_NS) {
+ /* JS_PROP_WRITABLE is always true for variable
+ references, but they are write protected in module name
+ spaces. */
+ if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue))
+ goto not_configurable;
+ }
+ /* update the reference */
+ set_value(ctx, pr->u.var_ref->pvalue,
+ JS_DupValue(ctx, val));
+ }
+ /* if writable is set to false, no longer a
+ reference (for mapped arguments) */
+ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) == JS_PROP_HAS_WRITABLE) {
+ JSValue val1;
+ if (js_shape_prepare_update(ctx, p, &prs))
+ return -1;
+ val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue);
+ free_var_ref(ctx->rt, pr->u.var_ref);
+ pr->u.value = val1;
+ prs->flags &= ~(JS_PROP_TMASK | JS_PROP_WRITABLE);
+ }
+ } else if (prs->flags & JS_PROP_LENGTH) {
+ if (flags & JS_PROP_HAS_VALUE) {
+ /* Note: no JS code is executable because
+ 'val' is guaranted to be a Uint32 */
+ res = set_array_length(ctx, p, JS_DupValue(ctx, val),
+ flags);
+ } else {
+ res = TRUE;
+ }
+ /* still need to reset the writable flag if
+ needed. The JS_PROP_LENGTH is kept because the
+ Uint32 test is still done if the length
+ property is read-only. */
+ if ((flags & (JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE)) ==
+ JS_PROP_HAS_WRITABLE) {
+ prs = get_shape_prop(p->shape);
+ if (js_update_property_flags(ctx, p, &prs,
+ prs->flags & ~JS_PROP_WRITABLE))
+ return -1;
+ }
+ return res;
+ } else {
+ if (flags & JS_PROP_HAS_VALUE) {
+ JS_FreeValue(ctx, pr->u.value);
+ pr->u.value = JS_DupValue(ctx, val);
+ }
+ if (flags & JS_PROP_HAS_WRITABLE) {
+ if (js_update_property_flags(ctx, p, &prs,
+ (prs->flags & ~JS_PROP_WRITABLE) |
+ (flags & JS_PROP_WRITABLE)))
+ return -1;
+ }
+ }
+ }
+ }
+ mask = 0;
+ if (flags & JS_PROP_HAS_CONFIGURABLE)
+ mask |= JS_PROP_CONFIGURABLE;
+ if (flags & JS_PROP_HAS_ENUMERABLE)
+ mask |= JS_PROP_ENUMERABLE;
+ if (js_update_property_flags(ctx, p, &prs,
+ (prs->flags & ~mask) | (flags & mask)))
+ return -1;
+ return TRUE;
+ }
+
+ /* handle modification of fast array elements */
+ if (p->fast_array) {
+ uint32_t idx;
+ uint32_t prop_flags;
+ if (p->class_id == JS_CLASS_ARRAY) {
+ if (JS_AtomIsTaggedInt(prop)) {
+ idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);
+ if (prop_flags != JS_PROP_C_W_E)
+ goto convert_to_slow_array;
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ convert_to_slow_array:
+ if (convert_fast_array_to_array(ctx, p))
+ return -1;
+ else
+ goto redo_prop_update;
+ }
+ if (flags & JS_PROP_HAS_VALUE) {
+ set_value(ctx, &p->u.array.u.values[idx], JS_DupValue(ctx, val));
+ }
+ return TRUE;
+ }
+ }
+ } else if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ JSValue num;
+ int ret;
+
+ if (!JS_AtomIsTaggedInt(prop)) {
+ /* slow path with to handle all numeric indexes */
+ num = JS_AtomIsNumericIndex1(ctx, prop);
+ if (JS_IsUndefined(num))
+ goto typed_array_done;
+ if (JS_IsException(num))
+ return -1;
+ ret = JS_NumberIsInteger(ctx, num);
+ if (ret < 0) {
+ JS_FreeValue(ctx, num);
+ return -1;
+ }
+ if (!ret) {
+ JS_FreeValue(ctx, num);
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "non integer index in typed array");
+ }
+ ret = JS_NumberIsNegativeOrMinusZero(ctx, num);
+ JS_FreeValue(ctx, num);
+ if (ret) {
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");
+ }
+ if (!JS_AtomIsTaggedInt(prop))
+ goto typed_array_oob;
+ }
+ idx = JS_AtomToUInt32(prop);
+ /* if the typed array is detached, p->u.array.count = 0 */
+ if (idx >= typed_array_get_length(ctx, p)) {
+ typed_array_oob:
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array");
+ }
+ prop_flags = get_prop_flags(flags, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET) ||
+ prop_flags != (JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE)) {
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "invalid descriptor flags");
+ }
+ if (flags & JS_PROP_HAS_VALUE) {
+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), JS_DupValue(ctx, val), flags);
+ }
+ return TRUE;
+ typed_array_done: ;
+ }
+ }
+
+ return JS_CreateProperty(ctx, p, prop, val, getter, setter, flags);
+}
+
+static int JS_DefineAutoInitProperty(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSAutoInitIDEnum id,
+ void *opaque, int flags)
+{
+ JSObject *p;
+ JSProperty *pr;
+
+ if (JS_VALUE_GET_TAG(this_obj) != JS_TAG_OBJECT)
+ return FALSE;
+
+ p = JS_VALUE_GET_OBJ(this_obj);
+
+ if (find_own_property(&pr, p, prop)) {
+ /* property already exists */
+ abort();
+ return FALSE;
+ }
+
+ /* Specialized CreateProperty */
+ pr = add_property(ctx, p, prop, (flags & JS_PROP_C_W_E) | JS_PROP_AUTOINIT);
+ if (unlikely(!pr))
+ return -1;
+ pr->u.init.realm_and_id = (uintptr_t)JS_DupContext(ctx);
+ assert((pr->u.init.realm_and_id & 3) == 0);
+ assert(id <= 3);
+ pr->u.init.realm_and_id |= id;
+ pr->u.init.opaque = opaque;
+ return TRUE;
+}
+
+/* shortcut to add or redefine a new property value */
+int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValue val, int flags)
+{
+ int ret;
+ ret = JS_DefineProperty(ctx, this_obj, prop, val, JS_UNDEFINED, JS_UNDEFINED,
+ flags | JS_PROP_HAS_VALUE | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE);
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+int JS_DefinePropertyValueValue(JSContext *ctx, JSValueConst this_obj,
+ JSValue prop, JSValue val, int flags)
+{
+ JSAtom atom;
+ int ret;
+ atom = JS_ValueToAtom(ctx, prop);
+ JS_FreeValue(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+}
+
+int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx, JSValue val, int flags)
+{
+ return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewUint32(ctx, idx),
+ val, flags);
+}
+
+int JS_DefinePropertyValueInt64(JSContext *ctx, JSValueConst this_obj,
+ int64_t idx, JSValue val, int flags)
+{
+ return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
+ val, flags);
+}
+
+int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,
+ const char *prop, JSValue val, int flags)
+{
+ JSAtom atom;
+ int ret;
+ atom = JS_NewAtom(ctx, prop);
+ ret = JS_DefinePropertyValue(ctx, this_obj, atom, val, flags);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+}
+
+/* shortcut to add getter & setter */
+int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValue getter, JSValue setter,
+ int flags)
+{
+ int ret;
+ ret = JS_DefineProperty(ctx, this_obj, prop, JS_UNDEFINED, getter, setter,
+ flags | JS_PROP_HAS_GET | JS_PROP_HAS_SET |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE);
+ JS_FreeValue(ctx, getter);
+ JS_FreeValue(ctx, setter);
+ return ret;
+}
+
+static int JS_CreateDataPropertyUint32(JSContext *ctx, JSValueConst this_obj,
+ int64_t idx, JSValue val, int flags)
+{
+ return JS_DefinePropertyValueValue(ctx, this_obj, JS_NewInt64(ctx, idx),
+ val, flags | JS_PROP_CONFIGURABLE |
+ JS_PROP_ENUMERABLE | JS_PROP_WRITABLE);
+}
+
+
+/* return TRUE if 'obj' has a non empty 'name' string */
+static BOOL js_object_has_name(JSContext *ctx, JSValueConst obj)
+{
+ JSProperty *pr;
+ JSShapeProperty *prs;
+ JSValueConst val;
+ JSString *p;
+
+ prs = find_own_property(&pr, JS_VALUE_GET_OBJ(obj), JS_ATOM_name);
+ if (!prs)
+ return FALSE;
+ if ((prs->flags & JS_PROP_TMASK) != JS_PROP_NORMAL)
+ return TRUE;
+ val = pr->u.value;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
+ return TRUE;
+ p = JS_VALUE_GET_STRING(val);
+ return (p->len != 0);
+}
+
+static int JS_DefineObjectName(JSContext *ctx, JSValueConst obj,
+ JSAtom name, int flags)
+{
+ if (name != JS_ATOM_NULL
+ && JS_IsObject(obj)
+ && !js_object_has_name(ctx, obj)
+ && JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, JS_AtomToString(ctx, name), flags) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int JS_DefineObjectNameComputed(JSContext *ctx, JSValueConst obj,
+ JSValueConst str, int flags)
+{
+ if (JS_IsObject(obj) &&
+ !js_object_has_name(ctx, obj)) {
+ JSAtom prop;
+ JSValue name_str;
+ prop = JS_ValueToAtom(ctx, str);
+ if (prop == JS_ATOM_NULL)
+ return -1;
+ name_str = js_get_function_name(ctx, prop);
+ JS_FreeAtom(ctx, prop);
+ if (JS_IsException(name_str))
+ return -1;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_name, name_str, flags) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+#define DEFINE_GLOBAL_LEX_VAR (1 << 7)
+#define DEFINE_GLOBAL_FUNC_VAR (1 << 6)
+
+static JSValue JS_ThrowSyntaxErrorVarRedeclaration(JSContext *ctx, JSAtom prop)
+{
+ return JS_ThrowSyntaxErrorAtom(ctx, "redeclaration of '%s'", prop);
+}
+
+/* flags is 0, DEFINE_GLOBAL_LEX_VAR or DEFINE_GLOBAL_FUNC_VAR */
+/* XXX: could support exotic global object. */
+static int JS_CheckDefineGlobalVar(JSContext *ctx, JSAtom prop, int flags)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+
+ p = JS_VALUE_GET_OBJ(ctx->global_obj);
+ prs = find_own_property1(p, prop);
+ /* XXX: should handle JS_PROP_AUTOINIT */
+ if (flags & DEFINE_GLOBAL_LEX_VAR) {
+ if (prs && !(prs->flags & JS_PROP_CONFIGURABLE))
+ goto fail_redeclaration;
+ } else {
+ if (!prs && !p->extensible)
+ goto define_error;
+ if (flags & DEFINE_GLOBAL_FUNC_VAR) {
+ if (prs) {
+ if (!(prs->flags & JS_PROP_CONFIGURABLE) &&
+ ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET ||
+ ((prs->flags & (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)) !=
+ (JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)))) {
+ define_error:
+ JS_ThrowTypeErrorAtom(ctx, "cannot define variable '%s'",
+ prop);
+ return -1;
+ }
+ }
+ }
+ }
+ /* check if there already is a lexical declaration */
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property1(p, prop);
+ if (prs) {
+ fail_redeclaration:
+ JS_ThrowSyntaxErrorVarRedeclaration(ctx, prop);
+ return -1;
+ }
+ return 0;
+}
+
+/* def_flags is (0, DEFINE_GLOBAL_LEX_VAR) |
+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE */
+/* XXX: could support exotic global object. */
+static int JS_DefineGlobalVar(JSContext *ctx, JSAtom prop, int def_flags)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ JSValue val;
+ int flags;
+
+ if (def_flags & DEFINE_GLOBAL_LEX_VAR) {
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ flags = JS_PROP_ENUMERABLE | (def_flags & JS_PROP_WRITABLE) |
+ JS_PROP_CONFIGURABLE;
+ val = JS_UNINITIALIZED;
+ } else {
+ p = JS_VALUE_GET_OBJ(ctx->global_obj);
+ flags = JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
+ (def_flags & JS_PROP_CONFIGURABLE);
+ val = JS_UNDEFINED;
+ }
+ prs = find_own_property1(p, prop);
+ if (prs)
+ return 0;
+ if (!p->extensible)
+ return 0;
+ pr = add_property(ctx, p, prop, flags);
+ if (unlikely(!pr))
+ return -1;
+ pr->u.value = val;
+ return 0;
+}
+
+/* 'def_flags' is 0 or JS_PROP_CONFIGURABLE. */
+/* XXX: could support exotic global object. */
+static int JS_DefineGlobalFunction(JSContext *ctx, JSAtom prop,
+ JSValueConst func, int def_flags)
+{
+
+ JSObject *p;
+ JSShapeProperty *prs;
+ int flags;
+
+ p = JS_VALUE_GET_OBJ(ctx->global_obj);
+ prs = find_own_property1(p, prop);
+ flags = JS_PROP_HAS_VALUE | JS_PROP_THROW;
+ if (!prs || (prs->flags & JS_PROP_CONFIGURABLE)) {
+ flags |= JS_PROP_ENUMERABLE | JS_PROP_WRITABLE | def_flags |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_WRITABLE | JS_PROP_HAS_ENUMERABLE;
+ }
+ if (JS_DefineProperty(ctx, ctx->global_obj, prop, func,
+ JS_UNDEFINED, JS_UNDEFINED, flags) < 0)
+ return -1;
+ return 0;
+}
+
+static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
+ BOOL throw_ref_error)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+
+ /* no exotic behavior is possible in global_var_obj */
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ /* XXX: should handle JS_PROP_TMASK properties */
+ if (unlikely(JS_IsUninitialized(pr->u.value)))
+ return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return JS_DupValue(ctx, pr->u.value);
+ }
+ if (ctx->scopeLookup) {
+ struct LookupResult result = ctx->scopeLookup(ctx, prop);
+ if (result.useResult)
+ return result.value;
+ }
+ return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
+ ctx->global_obj, throw_ref_error);
+}
+
+/* construct a reference to a global variable */
+static int JS_GetGlobalVarRef(JSContext *ctx, JSAtom prop, JSValue *sp)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+
+ /* no exotic behavior is possible in global_var_obj */
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ /* XXX: should handle JS_PROP_AUTOINIT properties? */
+ /* XXX: conformance: do these tests in
+ OP_put_var_ref/OP_get_var_ref ? */
+ if (unlikely(JS_IsUninitialized(pr->u.value))) {
+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return -1;
+ }
+ if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
+ return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
+ }
+ sp[0] = JS_DupValue(ctx, ctx->global_var_obj);
+ } else {
+ int ret;
+ ret = JS_HasProperty(ctx, ctx->global_obj, prop);
+ if (ret < 0)
+ return -1;
+ if (ret) {
+ sp[0] = JS_DupValue(ctx, ctx->global_obj);
+ } else {
+ sp[0] = JS_UNDEFINED;
+ }
+ }
+ sp[1] = JS_AtomToValue(ctx, prop);
+ return 0;
+}
+
+/* use for strict variable access: test if the variable exists */
+static int JS_CheckGlobalVar(JSContext *ctx, JSAtom prop)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ int ret;
+
+ /* no exotic behavior is possible in global_var_obj */
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property1(p, prop);
+ if (prs) {
+ ret = TRUE;
+ } else {
+ ret = JS_HasProperty(ctx, ctx->global_obj, prop);
+ if (ret < 0)
+ return -1;
+ }
+ return ret;
+}
+
+/* flag = 0: normal variable write
+ flag = 1: initialize lexical variable
+ flag = 2: normal variable write, strict check was done before
+*/
+static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
+ int flag)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ int flags;
+
+ /* no exotic behavior is possible in global_var_obj */
+ p = JS_VALUE_GET_OBJ(ctx->global_var_obj);
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+ /* XXX: should handle JS_PROP_AUTOINIT properties? */
+ if (flag != 1) {
+ if (unlikely(JS_IsUninitialized(pr->u.value))) {
+ JS_FreeValue(ctx, val);
+ JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return -1;
+ }
+ if (unlikely(!(prs->flags & JS_PROP_WRITABLE))) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, prop);
+ }
+ }
+ set_value(ctx, &pr->u.value, val);
+ return 0;
+ }
+ flags = JS_PROP_THROW_STRICT;
+ if (is_strict_mode(ctx))
+ flags |= JS_PROP_NO_ADD;
+
+ if (ctx->scopeLookup) {
+ struct LookupResult result = ctx->scopeLookup(ctx, prop);
+ if (result.useResult) {
+ JS_FreeValue(ctx, result.value);
+ return JS_SetPropertyInternal(ctx, result.scope, prop, val, flags);
+ }
+ }
+
+ return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);
+}
+
+/* return -1, FALSE or TRUE. return FALSE if not configurable or
+ invalid object. return -1 in case of exception.
+ flags can be 0, JS_PROP_THROW or JS_PROP_THROW_STRICT */
+int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags)
+{
+ JSValue obj1;
+ JSObject *p;
+ int res;
+
+ obj1 = JS_ToObject(ctx, obj);
+ if (JS_IsException(obj1))
+ return -1;
+ p = JS_VALUE_GET_OBJ(obj1);
+ res = delete_property(ctx, p, prop);
+ JS_FreeValue(ctx, obj1);
+ if (res != FALSE)
+ return res;
+ if ((flags & JS_PROP_THROW) ||
+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
+ JS_ThrowTypeError(ctx, "could not delete property");
+ return -1;
+ }
+ return FALSE;
+}
+
+int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int flags)
+{
+ JSAtom prop;
+ int res;
+
+ if ((uint64_t)idx <= JS_ATOM_MAX_INT) {
+ /* fast path for fast arrays */
+ return JS_DeleteProperty(ctx, obj, JS_AtomFromUInt32(idx), flags);
+ }
+ prop = JS_NewAtomInt64(ctx, idx);
+ if (prop == JS_ATOM_NULL)
+ return -1;
+ res = JS_DeleteProperty(ctx, obj, prop, flags);
+ JS_FreeAtom(ctx, prop);
+ return res;
+}
+
+BOOL JS_IsFunction(JSContext *ctx, JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(val);
+ switch(p->class_id) {
+ case JS_CLASS_BYTECODE_FUNCTION:
+ return TRUE;
+ case JS_CLASS_PROXY:
+ return p->u.proxy_data->is_func;
+ default:
+ return (ctx->rt->class_array[p->class_id].call != NULL);
+ }
+}
+
+BOOL JS_IsCFunction(JSContext *ctx, JSValueConst val, JSCFunction *func, int magic)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(val);
+ if (p->class_id == JS_CLASS_C_FUNCTION)
+ return (p->u.cfunc.c_function.generic == func && p->u.cfunc.magic == magic);
+ else
+ return FALSE;
+}
+
+BOOL JS_IsConstructor(JSContext *ctx, JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(val);
+ return p->is_constructor;
+}
+
+BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, BOOL val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ p->is_constructor = val;
+ return TRUE;
+}
+
+JS_BOOL JS_IsArrayBuffer(JSValueConst v)
+{
+ if (!JS_IsObject(v))
+ return FALSE;
+ JSObject *p = JS_VALUE_GET_OBJ(v);
+ return p->class_id == JS_CLASS_ARRAY_BUFFER || p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER;
+}
+
+BOOL JS_IsError(JSContext *ctx, JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(val);
+ return (p->class_id == JS_CLASS_ERROR);
+}
+
+/* used to avoid catching interrupt exceptions */
+BOOL JS_IsUncatchableError(JSContext *ctx, JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(val);
+ return p->class_id == JS_CLASS_ERROR && p->is_uncatchable_error;
+}
+
+void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, BOOL flag)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return;
+ p = JS_VALUE_GET_OBJ(val);
+ if (p->class_id == JS_CLASS_ERROR)
+ p->is_uncatchable_error = flag;
+}
+
+void JS_ResetUncatchableError(JSContext *ctx)
+{
+ JS_SetUncatchableError(ctx, ctx->rt->current_exception, FALSE);
+}
+
+void JS_SetOpaque(JSValue obj, void *opaque)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(obj);
+ p->u.opaque = opaque;
+ }
+}
+
+/* return NULL if not an object of class class_id */
+void *JS_GetOpaque(JSValueConst obj, JSClassID class_id)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return NULL;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id != class_id)
+ return NULL;
+ return p->u.opaque;
+}
+
+void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id)
+{
+ void *p = JS_GetOpaque(obj, class_id);
+ if (unlikely(!p)) {
+ JS_ThrowTypeErrorInvalidClass(ctx, class_id);
+ }
+ return p;
+}
+
+#define HINT_STRING 0
+#define HINT_NUMBER 1
+#define HINT_NONE 2
+/* don't try Symbol.toPrimitive */
+#define HINT_FORCE_ORDINARY (1 << 4)
+
+static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
+{
+ int i;
+ BOOL force_ordinary;
+
+ JSAtom method_name;
+ JSValue method, ret;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return val;
+ force_ordinary = hint & HINT_FORCE_ORDINARY;
+ hint &= ~HINT_FORCE_ORDINARY;
+ if (!force_ordinary) {
+ method = JS_GetProperty(ctx, val, JS_ATOM_Symbol_toPrimitive);
+ if (JS_IsException(method))
+ goto exception;
+ /* ECMA says *If exoticToPrim is not undefined* but tests in
+ test262 use null as a non callable converter */
+ if (!JS_IsUndefined(method) && !JS_IsNull(method)) {
+ JSAtom atom;
+ JSValue arg;
+ switch(hint) {
+ case HINT_STRING:
+ atom = JS_ATOM_string;
+ break;
+ case HINT_NUMBER:
+ atom = JS_ATOM_number;
+ break;
+ default:
+ case HINT_NONE:
+ atom = JS_ATOM_default;
+ break;
+ }
+ arg = JS_AtomToString(ctx, atom);
+ ret = JS_CallFree(ctx, method, val, 1, &arg);
+ JS_FreeValue(ctx, arg);
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, val);
+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT)
+ return ret;
+ JS_FreeValue(ctx, ret);
+ return JS_ThrowTypeError(ctx, "toPrimitive");
+ }
+ }
+ if (hint != HINT_STRING)
+ hint = HINT_NUMBER;
+ for(i = 0; i < 2; i++) {
+ if ((i ^ hint) == 0) {
+ method_name = JS_ATOM_toString;
+ } else {
+ method_name = JS_ATOM_valueOf;
+ }
+ method = JS_GetProperty(ctx, val, method_name);
+ if (JS_IsException(method))
+ goto exception;
+ if (JS_IsFunction(ctx, method)) {
+ ret = JS_CallFree(ctx, method, val, 0, NULL);
+ if (JS_IsException(ret))
+ goto exception;
+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ JS_FreeValue(ctx, ret);
+ } else {
+ JS_FreeValue(ctx, method);
+ }
+ }
+ JS_ThrowTypeError(ctx, "toPrimitive");
+exception:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ToPrimitive(JSContext *ctx, JSValueConst val, int hint)
+{
+ return JS_ToPrimitiveFree(ctx, JS_DupValue(ctx, val), hint);
+}
+
+void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return;
+ p = JS_VALUE_GET_OBJ(obj);
+ p->is_HTMLDDA = TRUE;
+}
+
+static inline BOOL JS_IsHTMLDDA(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return FALSE;
+ p = JS_VALUE_GET_OBJ(obj);
+ return p->is_HTMLDDA;
+}
+
+static int JS_ToBoolFree(JSContext *ctx, JSValue val)
+{
+ uint32_t tag = JS_VALUE_GET_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ return JS_VALUE_GET_INT(val) != 0;
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ return JS_VALUE_GET_INT(val);
+ case JS_TAG_EXCEPTION:
+ return -1;
+ case JS_TAG_STRING:
+ {
+ BOOL ret = JS_VALUE_GET_STRING(val)->len != 0;
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ BOOL ret;
+ ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ case JS_TAG_BIG_DECIMAL:
+ {
+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);
+ BOOL ret;
+ ret = p->num.expn != BF_EXP_ZERO && p->num.expn != BF_EXP_NAN;
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+#endif
+ case JS_TAG_OBJECT:
+ {
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ BOOL ret;
+ ret = !p->is_HTMLDDA;
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+ break;
+ default:
+ if (JS_TAG_IS_FLOAT64(tag)) {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ return !isnan(d) && d != 0;
+ } else {
+ JS_FreeValue(ctx, val);
+ return TRUE;
+ }
+ }
+}
+
+int JS_ToBool(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToBoolFree(ctx, JS_DupValue(ctx, val));
+}
+
+static int skip_spaces(const char *pc)
+{
+ const uint8_t *p, *p_next, *p_start;
+ uint32_t c;
+
+ p = p_start = (const uint8_t *)pc;
+ for (;;) {
+ c = *p;
+ if (c < 128) {
+ if (!((c >= 0x09 && c <= 0x0d) || (c == 0x20)))
+ break;
+ p++;
+ } else {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
+ if (!lre_is_space(c))
+ break;
+ p = p_next;
+ }
+ }
+ return p - p_start;
+}
+
+static inline int to_digit(int c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'A' && c <= 'Z')
+ return c - 'A' + 10;
+ else if (c >= 'a' && c <= 'z')
+ return c - 'a' + 10;
+ else
+ return 36;
+}
+
+/* XXX: remove */
+static double js_strtod(const char *p, int radix, BOOL is_float)
+{
+ double d;
+ int c;
+
+ if (!is_float || radix != 10) {
+ uint64_t n_max, n;
+ int int_exp, is_neg;
+
+ is_neg = 0;
+ if (*p == '-') {
+ is_neg = 1;
+ p++;
+ }
+
+ /* skip leading zeros */
+ while (*p == '0')
+ p++;
+ n = 0;
+ if (radix == 10)
+ n_max = ((uint64_t)-1 - 9) / 10; /* most common case */
+ else
+ n_max = ((uint64_t)-1 - (radix - 1)) / radix;
+ /* XXX: could be more precise */
+ int_exp = 0;
+ while (*p != '\0') {
+ c = to_digit((uint8_t)*p);
+ if (c >= radix)
+ break;
+ if (n <= n_max) {
+ n = n * radix + c;
+ } else {
+ int_exp++;
+ }
+ p++;
+ }
+ d = n;
+ if (int_exp != 0) {
+ d *= pow(radix, int_exp);
+ }
+ if (is_neg)
+ d = -d;
+ } else {
+ d = safe_strtod(p, NULL);
+ }
+ return d;
+}
+
+#define ATOD_INT_ONLY (1 << 0)
+/* accept Oo and Ob prefixes in addition to 0x prefix if radix = 0 */
+#define ATOD_ACCEPT_BIN_OCT (1 << 2)
+/* accept O prefix as octal if radix == 0 and properly formed (Annex B) */
+#define ATOD_ACCEPT_LEGACY_OCTAL (1 << 4)
+/* accept _ between digits as a digit separator */
+#define ATOD_ACCEPT_UNDERSCORES (1 << 5)
+/* allow a suffix to override the type */
+#define ATOD_ACCEPT_SUFFIX (1 << 6)
+/* default type */
+#define ATOD_TYPE_MASK (3 << 7)
+#define ATOD_TYPE_FLOAT64 (0 << 7)
+#define ATOD_TYPE_BIG_INT (1 << 7)
+#define ATOD_TYPE_BIG_FLOAT (2 << 7)
+#define ATOD_TYPE_BIG_DECIMAL (3 << 7)
+/* assume bigint mode: floats are parsed as integers if no decimal
+ point nor exponent */
+#define ATOD_MODE_BIGINT (1 << 9)
+/* accept -0x1 */
+#define ATOD_ACCEPT_PREFIX_AFTER_SIGN (1 << 10)
+
+#ifdef CONFIG_BIGNUM
+static JSValue js_string_to_bigint(JSContext *ctx, const char *buf,
+ int radix, int flags, slimb_t *pexponent)
+{
+ bf_t a_s, *a = &a_s;
+ int ret;
+ JSValue val;
+ val = JS_NewBigInt(ctx);
+ if (JS_IsException(val))
+ return val;
+ a = JS_GetBigInt(val);
+ ret = bf_atof(a, buf, NULL, radix, BF_PREC_INF, BF_RNDZ);
+ if (ret & BF_ST_MEM_ERROR) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0);
+ return val;
+}
+
+static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf,
+ int radix, int flags, slimb_t *pexponent)
+{
+ bf_t *a;
+ int ret;
+ JSValue val;
+
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ return val;
+ a = JS_GetBigFloat(val);
+ if (flags & ATOD_ACCEPT_SUFFIX) {
+ /* return the exponent to get infinite precision */
+ ret = bf_atof2(a, pexponent, buf, NULL, radix, BF_PREC_INF,
+ BF_RNDZ | BF_ATOF_EXPONENT);
+ } else {
+ ret = bf_atof(a, buf, NULL, radix, ctx->fp_env.prec,
+ ctx->fp_env.flags);
+ }
+ if (ret & BF_ST_MEM_ERROR) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ return val;
+}
+
+static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf,
+ int radix, int flags, slimb_t *pexponent)
+{
+ bfdec_t *a;
+ int ret;
+ JSValue val;
+
+ val = JS_NewBigDecimal(ctx);
+ if (JS_IsException(val))
+ return val;
+ a = JS_GetBigDecimal(val);
+ ret = bfdec_atof(a, buf, NULL, BF_PREC_INF,
+ BF_RNDZ | BF_ATOF_NO_NAN_INF);
+ if (ret & BF_ST_MEM_ERROR) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ return val;
+}
+
+#endif
+
+/* return an exception in case of memory error. Return JS_NAN if
+ invalid syntax */
+#ifdef CONFIG_BIGNUM
+static JSValue js_atof2(JSContext *ctx, const char *str, const char **pp,
+ int radix, int flags, slimb_t *pexponent)
+#else
+static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
+ int radix, int flags)
+#endif
+{
+ const char *p, *p_start;
+ int sep, is_neg;
+ BOOL is_float, has_legacy_octal;
+ int atod_type = flags & ATOD_TYPE_MASK;
+ char buf1[64], *buf;
+ int i, j, len;
+ BOOL buf_allocated = FALSE;
+ JSValue val;
+
+ /* optional separator between digits */
+ sep = (flags & ATOD_ACCEPT_UNDERSCORES) ? '_' : 256;
+ has_legacy_octal = FALSE;
+
+ p = str;
+ p_start = p;
+ is_neg = 0;
+ if (p[0] == '+') {
+ p++;
+ p_start++;
+ if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
+ goto no_radix_prefix;
+ } else if (p[0] == '-') {
+ p++;
+ p_start++;
+ is_neg = 1;
+ if (!(flags & ATOD_ACCEPT_PREFIX_AFTER_SIGN))
+ goto no_radix_prefix;
+ }
+ if (p[0] == '0') {
+ if ((p[1] == 'x' || p[1] == 'X') &&
+ (radix == 0 || radix == 16)) {
+ p += 2;
+ radix = 16;
+ } else if ((p[1] == 'o' || p[1] == 'O') &&
+ radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
+ p += 2;
+ radix = 8;
+ } else if ((p[1] == 'b' || p[1] == 'B') &&
+ radix == 0 && (flags & ATOD_ACCEPT_BIN_OCT)) {
+ p += 2;
+ radix = 2;
+ } else if ((p[1] >= '0' && p[1] <= '9') &&
+ radix == 0 && (flags & ATOD_ACCEPT_LEGACY_OCTAL)) {
+ int i;
+ has_legacy_octal = TRUE;
+ sep = 256;
+ for (i = 1; (p[i] >= '0' && p[i] <= '7'); i++)
+ continue;
+ if (p[i] == '8' || p[i] == '9')
+ goto no_prefix;
+ p += 1;
+ radix = 8;
+ } else {
+ goto no_prefix;
+ }
+ /* there must be a digit after the prefix */
+ if (to_digit((uint8_t)*p) >= radix)
+ goto fail;
+ no_prefix: ;
+ } else {
+ no_radix_prefix:
+ if (!(flags & ATOD_INT_ONLY) &&
+ (atod_type == ATOD_TYPE_FLOAT64 ||
+ atod_type == ATOD_TYPE_BIG_FLOAT) &&
+ strstart(p, "Infinity", &p)) {
+#ifdef CONFIG_BIGNUM
+ if (atod_type == ATOD_TYPE_BIG_FLOAT) {
+ bf_t *a;
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ goto done;
+ a = JS_GetBigFloat(val);
+ bf_set_inf(a, is_neg);
+ } else
+#endif
+ {
+ double d = INFINITY;
+ if (is_neg)
+ d = -d;
+ val = JS_NewFloat64(ctx, d);
+ }
+ goto done;
+ }
+ }
+ if (radix == 0)
+ radix = 10;
+ is_float = FALSE;
+ p_start = p;
+ while (to_digit((uint8_t)*p) < radix
+ || (*p == sep && (radix != 10 ||
+ p != p_start + 1 || p[-1] != '0') &&
+ to_digit((uint8_t)p[1]) < radix)) {
+ p++;
+ }
+ if (!(flags & ATOD_INT_ONLY)) {
+ if (*p == '.' && (p > p_start || to_digit((uint8_t)p[1]) < radix)) {
+ is_float = TRUE;
+ p++;
+ if (*p == sep)
+ goto fail;
+ while (to_digit((uint8_t)*p) < radix ||
+ (*p == sep && to_digit((uint8_t)p[1]) < radix))
+ p++;
+ }
+ if (p > p_start &&
+ (((*p == 'e' || *p == 'E') && radix == 10) ||
+ ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
+ const char *p1 = p + 1;
+ is_float = TRUE;
+ if (*p1 == '+' || *p1 == '-')
+ p1++;
+ if (is_digit((uint8_t)*p1)) {
+ p = p1 + 1;
+ while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
+ p++;
+ }
+ }
+ }
+ if (p == p_start)
+ goto fail;
+
+ buf = buf1;
+ buf_allocated = FALSE;
+ len = p - p_start;
+ if (unlikely((len + 2) > sizeof(buf1))) {
+ buf = js_malloc_rt(ctx->rt, len + 2); /* no exception raised */
+ if (!buf)
+ goto mem_error;
+ buf_allocated = TRUE;
+ }
+ /* remove the separators and the radix prefixes */
+ j = 0;
+ if (is_neg)
+ buf[j++] = '-';
+ for (i = 0; i < len; i++) {
+ if (p_start[i] != '_')
+ buf[j++] = p_start[i];
+ }
+ buf[j] = '\0';
+
+#ifdef CONFIG_BIGNUM
+ if (flags & ATOD_ACCEPT_SUFFIX) {
+ if (*p == 'n') {
+ p++;
+ atod_type = ATOD_TYPE_BIG_INT;
+ } else if (*p == 'l') {
+ p++;
+ atod_type = ATOD_TYPE_BIG_FLOAT;
+ } else if (*p == 'm') {
+ p++;
+ atod_type = ATOD_TYPE_BIG_DECIMAL;
+ } else {
+ if (flags & ATOD_MODE_BIGINT) {
+ if (!is_float)
+ atod_type = ATOD_TYPE_BIG_INT;
+ if (has_legacy_octal)
+ goto fail;
+ } else {
+ if (is_float && radix != 10)
+ goto fail;
+ }
+ }
+ } else {
+ if (atod_type == ATOD_TYPE_FLOAT64) {
+ if (flags & ATOD_MODE_BIGINT) {
+ if (!is_float)
+ atod_type = ATOD_TYPE_BIG_INT;
+ if (has_legacy_octal)
+ goto fail;
+ } else {
+ if (is_float && radix != 10)
+ goto fail;
+ }
+ }
+ }
+
+ switch(atod_type) {
+ case ATOD_TYPE_FLOAT64:
+ {
+ double d;
+ d = js_strtod(buf, radix, is_float);
+ /* return int or float64 */
+ val = JS_NewFloat64(ctx, d);
+ }
+ break;
+ case ATOD_TYPE_BIG_INT:
+ if (has_legacy_octal || is_float)
+ goto fail;
+ val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL);
+ break;
+ case ATOD_TYPE_BIG_FLOAT:
+ if (has_legacy_octal)
+ goto fail;
+ val = ctx->rt->bigfloat_ops.from_string(ctx, buf, radix, flags,
+ pexponent);
+ break;
+ case ATOD_TYPE_BIG_DECIMAL:
+ if (radix != 10)
+ goto fail;
+ val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL);
+ break;
+ default:
+ abort();
+ }
+#else
+ {
+ double d;
+ (void)has_legacy_octal;
+ if (is_float && radix != 10)
+ goto fail;
+ d = js_strtod(buf, radix, is_float);
+ val = JS_NewFloat64(ctx, d);
+ }
+#endif
+
+done:
+ if (buf_allocated)
+ js_free_rt(ctx->rt, buf);
+ if (pp)
+ *pp = p;
+ return val;
+ fail:
+ val = JS_NAN;
+ goto done;
+ mem_error:
+ val = JS_ThrowOutOfMemory(ctx);
+ goto done;
+}
+
+#ifdef CONFIG_BIGNUM
+static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
+ int radix, int flags)
+{
+ return js_atof2(ctx, str, pp, radix, flags, NULL);
+}
+#endif
+
+typedef enum JSToNumberHintEnum {
+ TON_FLAG_NUMBER,
+ TON_FLAG_NUMERIC,
+} JSToNumberHintEnum;
+
+static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val,
+ JSToNumberHintEnum flag)
+{
+ uint32_t tag;
+ JSValue ret;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_DECIMAL:
+ if (flag != TON_FLAG_NUMERIC) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number");
+ }
+ ret = val;
+ break;
+ case JS_TAG_BIG_INT:
+ if (flag != TON_FLAG_NUMERIC) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert bigint to number");
+ }
+ ret = val;
+ break;
+ case JS_TAG_BIG_FLOAT:
+ if (flag != TON_FLAG_NUMERIC) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert bigfloat to number");
+ }
+ ret = val;
+ break;
+#endif
+ case JS_TAG_FLOAT64:
+ case JS_TAG_INT:
+ case JS_TAG_EXCEPTION:
+ ret = val;
+ break;
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_UNDEFINED:
+ ret = JS_NAN;
+ break;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+ goto redo;
+ case JS_TAG_STRING:
+ {
+ const char *str;
+ const char *p;
+ size_t len;
+
+ str = JS_ToCStringLen(ctx, &len, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ if ((p - str) == len) {
+ ret = JS_NewInt32(ctx, 0);
+ } else {
+ int flags = ATOD_ACCEPT_BIN_OCT;
+ ret = js_atof(ctx, p, &p, 0, flags);
+ if (!JS_IsException(ret)) {
+ p += skip_spaces(p);
+ if ((p - str) != len) {
+ JS_FreeValue(ctx, ret);
+ ret = JS_NAN;
+ }
+ }
+ }
+ JS_FreeCString(ctx, str);
+ }
+ break;
+ case JS_TAG_SYMBOL:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert symbol to number");
+ default:
+ JS_FreeValue(ctx, val);
+ ret = JS_NAN;
+ break;
+ }
+ return ret;
+}
+
+static JSValue JS_ToNumberFree(JSContext *ctx, JSValue val)
+{
+ return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMBER);
+}
+
+static JSValue JS_ToNumericFree(JSContext *ctx, JSValue val)
+{
+ return JS_ToNumberHintFree(ctx, val, TON_FLAG_NUMERIC);
+}
+
+static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
+}
+
+static warn_unused int JS_ToFloat64FreeImpl(JSContext *ctx, double *pres,
+ JSValue val)
+{
+ double d;
+ uint32_t tag;
+
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = JS_FLOAT64_NAN;
+ return -1;
+ }
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ d = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_FLOAT64:
+ d = JS_VALUE_GET_FLOAT64(val);
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ /* XXX: there can be a double rounding issue with some
+ primitives (such as JS_ToUint8ClampFree()), but it is
+ not critical to fix it. */
+ bf_get_float64(&p->num, &d, BF_RNDN);
+ JS_FreeValue(ctx, val);
+ }
+ break;
+#endif
+ default:
+ abort();
+ }
+ *pres = d;
+ return 0;
+}
+
+static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
+{
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_TAG(val);
+ if (tag <= JS_TAG_NULL) {
+ *pres = JS_VALUE_GET_INT(val);
+ return 0;
+ } else if (JS_TAG_IS_FLOAT64(tag)) {
+ *pres = JS_VALUE_GET_FLOAT64(val);
+ return 0;
+ } else {
+ return JS_ToFloat64FreeImpl(ctx, pres, val);
+ }
+}
+
+int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val)
+{
+ return JS_ToFloat64Free(ctx, pres, JS_DupValue(ctx, val));
+}
+
+static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToNumberFree(ctx, JS_DupValue(ctx, val));
+}
+
+/* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
+static maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
+{
+ uint32_t tag;
+ JSValue ret;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_NewInt32(ctx, JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ ret = JS_NewInt32(ctx, 0);
+ } else {
+ /* convert -0 to +0 */
+ d = trunc(d) + 0.0;
+ ret = JS_NewFloat64(ctx, d);
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ bf_t a_s, *a, r_s, *r = &r_s;
+ BOOL is_nan;
+
+ a = JS_ToBigFloat(ctx, &a_s, val);
+ if (!bf_is_finite(a)) {
+ is_nan = bf_is_nan(a);
+ if (is_nan)
+ ret = JS_NewInt32(ctx, 0);
+ else
+ ret = JS_DupValue(ctx, val);
+ } else {
+ ret = JS_NewBigInt(ctx);
+ if (!JS_IsException(ret)) {
+ r = JS_GetBigInt(ret);
+ bf_set(r, a);
+ bf_rint(r, BF_RNDZ);
+ ret = JS_CompactBigInt(ctx, ret);
+ }
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, val);
+ }
+ break;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return val;
+ goto redo;
+ }
+ return ret;
+}
+
+/* Note: the integer value is satured to 32 bits */
+static int JS_ToInt32SatFree(JSContext *ctx, int *pres, JSValue val)
+{
+ uint32_t tag;
+ int ret;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_EXCEPTION:
+ *pres = 0;
+ return -1;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ ret = 0;
+ } else {
+ if (d < INT32_MIN)
+ ret = INT32_MIN;
+ else if (d > INT32_MAX)
+ ret = INT32_MAX;
+ else
+ ret = (int)d;
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_get_int32(&ret, &p->num, 0);
+ JS_FreeValue(ctx, val);
+ }
+ break;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
+ }
+ *pres = ret;
+ return 0;
+}
+
+int JS_ToInt32Sat(JSContext *ctx, int *pres, JSValueConst val)
+{
+ return JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
+}
+
+int JS_ToInt32Clamp(JSContext *ctx, int *pres, JSValueConst val,
+ int min, int max, int min_offset)
+{
+ int res = JS_ToInt32SatFree(ctx, pres, JS_DupValue(ctx, val));
+ if (res == 0) {
+ if (*pres < min) {
+ *pres += min_offset;
+ if (*pres < min)
+ *pres = min;
+ } else {
+ if (*pres > max)
+ *pres = max;
+ }
+ }
+ return res;
+}
+
+static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val)
+{
+ uint32_t tag;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ *pres = JS_VALUE_GET_INT(val);
+ return 0;
+ case JS_TAG_EXCEPTION:
+ *pres = 0;
+ return -1;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ *pres = 0;
+ } else {
+ if (d < INT64_MIN)
+ *pres = INT64_MIN;
+ else if (d > INT64_MAX)
+ *pres = INT64_MAX;
+ else
+ *pres = (int64_t)d;
+ }
+ }
+ return 0;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_get_int64(pres, &p->num, 0);
+ JS_FreeValue(ctx, val);
+ }
+ return 0;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
+ }
+}
+
+int JS_ToInt64Sat(JSContext *ctx, int64_t *pres, JSValueConst val)
+{
+ return JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
+}
+
+int JS_ToInt64Clamp(JSContext *ctx, int64_t *pres, JSValueConst val,
+ int64_t min, int64_t max, int64_t neg_offset)
+{
+ int res = JS_ToInt64SatFree(ctx, pres, JS_DupValue(ctx, val));
+ if (res == 0) {
+ if (*pres < 0)
+ *pres += neg_offset;
+ if (*pres < min)
+ *pres = min;
+ else if (*pres > max)
+ *pres = max;
+ }
+ return res;
+}
+
+/* Same as JS_ToInt32Free() but with a 64 bit result. Return (<0, 0)
+ in case of exception */
+static int JS_ToInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
+{
+ uint32_t tag;
+ int64_t ret;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ double d;
+ int e;
+ d = JS_VALUE_GET_FLOAT64(val);
+ u.d = d;
+ /* we avoid doing fmod(x, 2^64) */
+ e = (u.u64 >> 52) & 0x7ff;
+ if (likely(e <= (1023 + 62))) {
+ /* fast case */
+ ret = (int64_t)d;
+ } else if (e <= (1023 + 62 + 53)) {
+ uint64_t v;
+ /* remainder modulo 2^64 */
+ v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
+ ret = v << ((e - 1023) - 52);
+ /* take the sign into account */
+ if (u.u64 >> 63)
+ ret = -ret;
+ } else {
+ ret = 0; /* also handles NaN and +inf */
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_get_int64(&ret, &p->num, BF_GET_INT_MOD);
+ JS_FreeValue(ctx, val);
+ }
+ break;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
+ }
+ *pres = ret;
+ return 0;
+}
+
+int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+{
+ return JS_ToInt64Free(ctx, pres, JS_DupValue(ctx, val));
+}
+
+int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val)
+{
+ if (JS_IsBigInt(ctx, val))
+ return JS_ToBigInt64(ctx, pres, val);
+ else
+ return JS_ToInt64(ctx, pres, val);
+}
+
+/* return (<0, 0) in case of exception */
+static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val)
+{
+ uint32_t tag;
+ int32_t ret;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_VALUE_GET_INT(val);
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ double d;
+ int e;
+ d = JS_VALUE_GET_FLOAT64(val);
+ u.d = d;
+ /* we avoid doing fmod(x, 2^32) */
+ e = (u.u64 >> 52) & 0x7ff;
+ if (likely(e <= (1023 + 30))) {
+ /* fast case */
+ ret = (int32_t)d;
+ } else if (e <= (1023 + 30 + 53)) {
+ uint64_t v;
+ /* remainder modulo 2^32 */
+ v = (u.u64 & (((uint64_t)1 << 52) - 1)) | ((uint64_t)1 << 52);
+ v = v << ((e - 1023) - 52 + 32);
+ ret = v >> 32;
+ /* take the sign into account */
+ if (u.u64 >> 63)
+ ret = -ret;
+ } else {
+ ret = 0; /* also handles NaN and +inf */
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_get_int32(&ret, &p->num, BF_GET_INT_MOD);
+ JS_FreeValue(ctx, val);
+ }
+ break;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
+ }
+ *pres = ret;
+ return 0;
+}
+
+int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val)
+{
+ return JS_ToInt32Free(ctx, pres, JS_DupValue(ctx, val));
+}
+
+static inline int JS_ToUint32Free(JSContext *ctx, uint32_t *pres, JSValue val)
+{
+ return JS_ToInt32Free(ctx, (int32_t *)pres, val);
+}
+
+static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
+{
+ uint32_t tag;
+ int res;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ res = JS_VALUE_GET_INT(val);
+#ifdef CONFIG_BIGNUM
+ int_clamp:
+#endif
+ res = max_int(0, min_int(255, res));
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (isnan(d)) {
+ res = 0;
+ } else {
+ if (d < 0)
+ res = 0;
+ else if (d > 255)
+ res = 255;
+ else
+ res = lrint(d);
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_t r_s, *r = &r_s;
+ bf_init(ctx->bf_ctx, r);
+ bf_set(r, &p->num);
+ bf_rint(r, BF_RNDN);
+ bf_get_int32(&res, r, 0);
+ bf_delete(r);
+ JS_FreeValue(ctx, val);
+ }
+ goto int_clamp;
+#endif
+ default:
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val)) {
+ *pres = 0;
+ return -1;
+ }
+ goto redo;
+ }
+ *pres = res;
+ return 0;
+}
+
+static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
+ JSValue val, BOOL is_array_ctor)
+{
+ uint32_t tag, len;
+
+ tag = JS_VALUE_GET_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ {
+ int v;
+ v = JS_VALUE_GET_INT(val);
+ if (v < 0)
+ goto fail;
+ len = v;
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ bf_t a;
+ BOOL res;
+ bf_get_int32((int32_t *)&len, &p->num, BF_GET_INT_MOD);
+ bf_init(ctx->bf_ctx, &a);
+ bf_set_ui(&a, len);
+ res = bf_cmp_eq(&a, &p->num);
+ bf_delete(&a);
+ JS_FreeValue(ctx, val);
+ if (!res)
+ goto fail;
+ }
+ break;
+#endif
+ default:
+ if (JS_TAG_IS_FLOAT64(tag)) {
+ double d;
+ d = JS_VALUE_GET_FLOAT64(val);
+ len = (uint32_t)d;
+ if (len != d)
+ goto fail;
+ } else {
+ uint32_t len1;
+
+ if (is_array_ctor) {
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return -1;
+ /* cannot recurse because val is a number */
+ if (JS_ToArrayLengthFree(ctx, &len, val, TRUE))
+ return -1;
+ } else {
+ /* legacy behavior: must do the conversion twice and compare */
+ if (JS_ToUint32(ctx, &len, val)) {
+ JS_FreeValue(ctx, val);
+ return -1;
+ }
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return -1;
+ /* cannot recurse because val is a number */
+ if (JS_ToArrayLengthFree(ctx, &len1, val, FALSE))
+ return -1;
+ if (len1 != len) {
+ fail:
+ JS_ThrowRangeError(ctx, "invalid array length");
+ return -1;
+ }
+ }
+ }
+ break;
+ }
+ *plen = len;
+ return 0;
+}
+
+#define MAX_SAFE_INTEGER (((int64_t)1 << 53) - 1)
+
+static BOOL is_safe_integer(double d)
+{
+ return isfinite(d) && floor(d) == d &&
+ fabs(d) <= (double)MAX_SAFE_INTEGER;
+}
+
+int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
+{
+ int64_t v;
+ if (JS_ToInt64Sat(ctx, &v, val))
+ return -1;
+ if (v < 0 || v > MAX_SAFE_INTEGER) {
+ JS_ThrowRangeError(ctx, "invalid array index");
+ *plen = 0;
+ return -1;
+ }
+ *plen = v;
+ return 0;
+}
+
+/* convert a value to a length between 0 and MAX_SAFE_INTEGER.
+ return -1 for exception */
+static warn_unused int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
+ JSValue val)
+{
+ int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
+ JS_FreeValue(ctx, val);
+ return res;
+}
+
+/* Note: can return an exception */
+static int JS_NumberIsInteger(JSContext *ctx, JSValueConst val)
+{
+ double d;
+ if (!JS_IsNumber(val))
+ return FALSE;
+ if (unlikely(JS_ToFloat64(ctx, &d, val)))
+ return -1;
+ return isfinite(d) && floor(d) == d;
+}
+
+static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val)
+{
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ {
+ int v;
+ v = JS_VALUE_GET_INT(val);
+ return (v < 0);
+ }
+ case JS_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ u.d = JS_VALUE_GET_FLOAT64(val);
+ return (u.u64 >> 63);
+ }
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ /* Note: integer zeros are not necessarily positive */
+ return p->num.sign && !bf_is_zero(&p->num);
+ }
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ return p->num.sign;
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ {
+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);
+ return p->num.sign;
+ }
+ break;
+#endif
+ default:
+ return FALSE;
+ }
+}
+
+#ifdef CONFIG_BIGNUM
+
+static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix)
+{
+ JSValue ret;
+ bf_t a_s, *a;
+ char *str;
+ int saved_sign;
+
+ a = JS_ToBigInt(ctx, &a_s, val);
+ if (!a)
+ return JS_EXCEPTION;
+ saved_sign = a->sign;
+ if (a->expn == BF_EXP_ZERO)
+ a->sign = 0;
+ str = bf_ftoa(NULL, a, radix, 0, BF_RNDZ | BF_FTOA_FORMAT_FRAC |
+ BF_FTOA_JS_QUIRKS);
+ a->sign = saved_sign;
+ JS_FreeBigInt(ctx, a, &a_s);
+ if (!str)
+ return JS_ThrowOutOfMemory(ctx);
+ ret = JS_NewString(ctx, str);
+ bf_free(ctx->bf_ctx, str);
+ return ret;
+}
+
+static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val)
+{
+ return js_bigint_to_string1(ctx, val, 10);
+}
+
+static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix,
+ limb_t prec, bf_flags_t flags)
+{
+ JSValue val, ret;
+ bf_t a_s, *a;
+ char *str;
+ int saved_sign;
+
+ val = JS_ToNumeric(ctx, val1);
+ if (JS_IsException(val))
+ return val;
+ a = JS_ToBigFloat(ctx, &a_s, val);
+ saved_sign = a->sign;
+ if (a->expn == BF_EXP_ZERO)
+ a->sign = 0;
+ flags |= BF_FTOA_JS_QUIRKS;
+ if ((flags & BF_FTOA_FORMAT_MASK) == BF_FTOA_FORMAT_FREE_MIN) {
+ /* Note: for floating point numbers with a radix which is not
+ a power of two, the current precision is used to compute
+ the number of digits. */
+ if ((radix & (radix - 1)) != 0) {
+ bf_t r_s, *r = &r_s;
+ int prec, flags1;
+ /* must round first */
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
+ prec = ctx->fp_env.prec;
+ flags1 = ctx->fp_env.flags &
+ (BF_FLAG_SUBNORMAL | (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT));
+ } else {
+ prec = 53;
+ flags1 = bf_set_exp_bits(11) | BF_FLAG_SUBNORMAL;
+ }
+ bf_init(ctx->bf_ctx, r);
+ bf_set(r, a);
+ bf_round(r, prec, flags1 | BF_RNDN);
+ str = bf_ftoa(NULL, r, radix, prec, flags1 | flags);
+ bf_delete(r);
+ } else {
+ str = bf_ftoa(NULL, a, radix, BF_PREC_INF, flags);
+ }
+ } else {
+ str = bf_ftoa(NULL, a, radix, prec, flags);
+ }
+ a->sign = saved_sign;
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_ThrowOutOfMemory(ctx);
+ ret = JS_NewString(ctx, str);
+ bf_free(ctx->bf_ctx, str);
+ return ret;
+}
+
+static JSValue js_bigfloat_to_string(JSContext *ctx, JSValueConst val)
+{
+ return js_ftoa(ctx, val, 10, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
+}
+
+static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val,
+ limb_t prec, int flags)
+{
+ JSValue ret;
+ bfdec_t *a;
+ char *str;
+ int saved_sign;
+
+ a = JS_ToBigDecimal(ctx, val);
+ saved_sign = a->sign;
+ if (a->expn == BF_EXP_ZERO)
+ a->sign = 0;
+ str = bfdec_ftoa(NULL, a, prec, flags | BF_FTOA_JS_QUIRKS);
+ a->sign = saved_sign;
+ if (!str)
+ return JS_ThrowOutOfMemory(ctx);
+ ret = JS_NewString(ctx, str);
+ bf_free(ctx->bf_ctx, str);
+ return ret;
+}
+
+static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val)
+{
+ return js_bigdecimal_to_string1(ctx, val, 0,
+ BF_RNDZ | BF_FTOA_FORMAT_FREE);
+}
+
+#endif /* CONFIG_BIGNUM */
+
+/* 2 <= base <= 36 */
+static char *i64toa(char *buf_end, int64_t n, unsigned int base)
+{
+ char *q = buf_end;
+ int digit, is_neg;
+
+ is_neg = 0;
+ if (n < 0) {
+ is_neg = 1;
+ n = -n;
+ }
+ *--q = '\0';
+ do {
+ digit = (uint64_t)n % base;
+ n = (uint64_t)n / base;
+ if (digit < 10)
+ digit += '0';
+ else
+ digit += 'a' - 10;
+ *--q = digit;
+ } while (n != 0);
+ if (is_neg)
+ *--q = '-';
+ return q;
+}
+
+/* buf1 contains the printf result */
+static void js_ecvt1(double d, int n_digits, int *decpt, int *sign, char *buf,
+ int rounding_mode, char *buf1, int buf1_size)
+{
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(rounding_mode);
+ snprintf(buf1, buf1_size, "%+.*e", n_digits - 1, d);
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(FE_TONEAREST);
+ *sign = (buf1[0] == '-');
+ /* mantissa */
+ buf[0] = buf1[1];
+ if (n_digits > 1)
+ memcpy(buf + 1, buf1 + 3, n_digits - 1);
+ buf[n_digits] = '\0';
+ /* exponent */
+ *decpt = atoi(buf1 + n_digits + 2 + (n_digits > 1)) + 1;
+}
+
+/* maximum buffer size for js_dtoa */
+#define JS_DTOA_BUF_SIZE 128
+
+/* needed because ecvt usually limits the number of digits to
+ 17. Return the number of digits. */
+static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
+ BOOL is_fixed)
+{
+ int rounding_mode;
+ char buf_tmp[JS_DTOA_BUF_SIZE];
+
+ if (!is_fixed) {
+ unsigned int n_digits_min, n_digits_max;
+ /* find the minimum amount of digits (XXX: inefficient but simple) */
+ n_digits_min = 1;
+ n_digits_max = 17;
+ while (n_digits_min < n_digits_max) {
+ n_digits = (n_digits_min + n_digits_max) / 2;
+ js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
+ buf_tmp, sizeof(buf_tmp));
+ if (safe_strtod(buf_tmp, NULL) == d) {
+ /* no need to keep the trailing zeros */
+ while (n_digits >= 2 && buf[n_digits - 1] == '0')
+ n_digits--;
+ n_digits_max = n_digits;
+ } else {
+ n_digits_min = n_digits + 1;
+ }
+ }
+ n_digits = n_digits_max;
+ rounding_mode = FE_TONEAREST;
+ } else {
+ rounding_mode = FE_TONEAREST;
+#ifdef CONFIG_PRINTF_RNDN
+ {
+ char buf1[JS_DTOA_BUF_SIZE], buf2[JS_DTOA_BUF_SIZE];
+ int decpt1, sign1, decpt2, sign2;
+ /* The JS rounding is specified as round to nearest ties away
+ from zero (RNDNA), but in printf the "ties" case is not
+ specified (for example it is RNDN for glibc, RNDNA for
+ Windows), so we must round manually. */
+ js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_TONEAREST,
+ buf_tmp, sizeof(buf_tmp));
+ /* XXX: could use 2 digits to reduce the average running time */
+ if (buf1[n_digits] == '5') {
+ js_ecvt1(d, n_digits + 1, &decpt1, &sign1, buf1, FE_DOWNWARD,
+ buf_tmp, sizeof(buf_tmp));
+ js_ecvt1(d, n_digits + 1, &decpt2, &sign2, buf2, FE_UPWARD,
+ buf_tmp, sizeof(buf_tmp));
+ if (memcmp(buf1, buf2, n_digits + 1) == 0 && decpt1 == decpt2) {
+ /* exact result: round away from zero */
+ if (sign1)
+ rounding_mode = FE_DOWNWARD;
+ else
+ rounding_mode = FE_UPWARD;
+ }
+ }
+ }
+#endif /* CONFIG_PRINTF_RNDN */
+ }
+ js_ecvt1(d, n_digits, decpt, sign, buf, rounding_mode,
+ buf_tmp, sizeof(buf_tmp));
+ return n_digits;
+}
+
+static int js_fcvt1(char *buf, int buf_size, double d, int n_digits,
+ int rounding_mode)
+{
+ int n;
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(rounding_mode);
+ n = snprintf(buf, buf_size, "%.*f", n_digits, d);
+ if (rounding_mode != FE_TONEAREST)
+ fesetround(FE_TONEAREST);
+ assert(n < buf_size);
+ return n;
+}
+
+static void js_fcvt(char *buf, int buf_size, double d, int n_digits)
+{
+ int rounding_mode;
+ rounding_mode = FE_TONEAREST;
+#ifdef CONFIG_PRINTF_RNDN
+ {
+ int n1, n2;
+ char buf1[JS_DTOA_BUF_SIZE];
+ char buf2[JS_DTOA_BUF_SIZE];
+
+ /* The JS rounding is specified as round to nearest ties away from
+ zero (RNDNA), but in printf the "ties" case is not specified
+ (for example it is RNDN for glibc, RNDNA for Windows), so we
+ must round manually. */
+ n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_TONEAREST);
+ rounding_mode = FE_TONEAREST;
+ /* XXX: could use 2 digits to reduce the average running time */
+ if (buf1[n1 - 1] == '5') {
+ n1 = js_fcvt1(buf1, sizeof(buf1), d, n_digits + 1, FE_DOWNWARD);
+ n2 = js_fcvt1(buf2, sizeof(buf2), d, n_digits + 1, FE_UPWARD);
+ if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) {
+ /* exact result: round away from zero */
+ if (buf1[0] == '-')
+ rounding_mode = FE_DOWNWARD;
+ else
+ rounding_mode = FE_UPWARD;
+ }
+ }
+ }
+#endif /* CONFIG_PRINTF_RNDN */
+ js_fcvt1(buf, buf_size, d, n_digits, rounding_mode);
+}
+
+/* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */
+/* use as many digits as necessary */
+#define JS_DTOA_VAR_FORMAT (0 << 0)
+/* use n_digits significant digits (1 <= n_digits <= 101) */
+#define JS_DTOA_FIXED_FORMAT (1 << 0)
+/* force fractional format: [-]dd.dd with n_digits fractional digits */
+#define JS_DTOA_FRAC_FORMAT (2 << 0)
+/* force exponential notation either in fixed or variable format */
+#define JS_DTOA_FORCE_EXP (1 << 2)
+
+/* XXX: slow and maybe not fully correct. Use libbf when it is fast enough.
+ XXX: radix != 10 is only supported for small integers
+*/
+static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags)
+{
+ char *q;
+
+ if (!isfinite(d)) {
+ if (isnan(d)) {
+ strcpy(buf, "NaN");
+ } else {
+ q = buf;
+ if (d < 0)
+ *q++ = '-';
+ strcpy(q, "Infinity");
+ }
+ } else if (flags == JS_DTOA_VAR_FORMAT) {
+ int64_t i64;
+ char buf1[70], *ptr;
+ i64 = (int64_t)d;
+ if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER)
+ goto generic_conv;
+ /* fast path for integers */
+ ptr = i64toa(buf1 + sizeof(buf1), i64, radix);
+ strcpy(buf, ptr);
+ } else {
+ if (d == 0.0)
+ d = 0.0; /* convert -0 to 0 */
+ if (flags == JS_DTOA_FRAC_FORMAT) {
+ js_fcvt(buf, JS_DTOA_BUF_SIZE, d, n_digits);
+ } else {
+ char buf1[JS_DTOA_BUF_SIZE];
+ int sign, decpt, k, n, i, p, n_max;
+ BOOL is_fixed;
+ generic_conv:
+ is_fixed = ((flags & 3) == JS_DTOA_FIXED_FORMAT);
+ if (is_fixed) {
+ n_max = n_digits;
+ } else {
+ n_max = 21;
+ }
+ /* the number has k digits (k >= 1) */
+ k = js_ecvt(d, n_digits, &decpt, &sign, buf1, is_fixed);
+ n = decpt; /* d=10^(n-k)*(buf1) i.e. d= < x.yyyy 10^(n-1) */
+ q = buf;
+ if (sign)
+ *q++ = '-';
+ if (flags & JS_DTOA_FORCE_EXP)
+ goto force_exp;
+ if (n >= 1 && n <= n_max) {
+ if (k <= n) {
+ memcpy(q, buf1, k);
+ q += k;
+ for(i = 0; i < (n - k); i++)
+ *q++ = '0';
+ *q = '\0';
+ } else {
+ /* k > n */
+ memcpy(q, buf1, n);
+ q += n;
+ *q++ = '.';
+ for(i = 0; i < (k - n); i++)
+ *q++ = buf1[n + i];
+ *q = '\0';
+ }
+ } else if (n >= -5 && n <= 0) {
+ *q++ = '0';
+ *q++ = '.';
+ for(i = 0; i < -n; i++)
+ *q++ = '0';
+ memcpy(q, buf1, k);
+ q += k;
+ *q = '\0';
+ } else {
+ force_exp:
+ /* exponential notation */
+ *q++ = buf1[0];
+ if (k > 1) {
+ *q++ = '.';
+ for(i = 1; i < k; i++)
+ *q++ = buf1[i];
+ }
+ *q++ = 'e';
+ p = n - 1;
+ if (p >= 0)
+ *q++ = '+';
+ sprintf(q, "%d", p);
+ }
+ }
+ }
+}
+
+static JSValue js_dtoa(JSContext *ctx,
+ double d, int radix, int n_digits, int flags)
+{
+ char buf[JS_DTOA_BUF_SIZE];
+ js_dtoa1(buf, d, radix, n_digits, flags);
+ return JS_NewString(ctx, buf);
+}
+
+JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey)
+{
+ uint32_t tag;
+ const char *str;
+ char buf[32];
+
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_STRING:
+ return JS_DupValue(ctx, val);
+ case JS_TAG_INT:
+ snprintf(buf, sizeof(buf), "%d", JS_VALUE_GET_INT(val));
+ str = buf;
+ goto new_string;
+ case JS_TAG_BOOL:
+ return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
+ JS_ATOM_true : JS_ATOM_false);
+ case JS_TAG_NULL:
+ return JS_AtomToString(ctx, JS_ATOM_null);
+ case JS_TAG_UNDEFINED:
+ return JS_AtomToString(ctx, JS_ATOM_undefined);
+ case JS_TAG_EXCEPTION:
+ return JS_EXCEPTION;
+ case JS_TAG_OBJECT:
+ {
+ JSValue val1, ret;
+ val1 = JS_ToPrimitive(ctx, val, HINT_STRING);
+ if (JS_IsException(val1))
+ return val1;
+ ret = JS_ToStringInternal(ctx, val1, is_ToPropertyKey);
+ JS_FreeValue(ctx, val1);
+ return ret;
+ }
+ break;
+ case JS_TAG_FUNCTION_BYTECODE:
+ str = "[function bytecode]";
+ goto new_string;
+ case JS_TAG_SYMBOL:
+ if (is_ToPropertyKey) {
+ return JS_DupValue(ctx, val);
+ } else {
+ return JS_ThrowTypeError(ctx, "cannot convert symbol to string");
+ }
+ case JS_TAG_FLOAT64:
+ return js_dtoa(ctx, JS_VALUE_GET_FLOAT64(val), 10, 0,
+ JS_DTOA_VAR_FORMAT);
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ return ctx->rt->bigint_ops.to_string(ctx, val);
+ case JS_TAG_BIG_FLOAT:
+ return ctx->rt->bigfloat_ops.to_string(ctx, val);
+ case JS_TAG_BIG_DECIMAL:
+ return ctx->rt->bigdecimal_ops.to_string(ctx, val);
+#endif
+ default:
+ str = "[unsupported type]";
+ new_string:
+ return JS_NewString(ctx, str);
+ }
+}
+
+JSValue JS_ToString(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToStringInternal(ctx, val, FALSE);
+}
+
+static JSValue JS_ToStringFree(JSContext *ctx, JSValue val)
+{
+ JSValue ret;
+ ret = JS_ToString(ctx, val);
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue JS_ToLocaleStringFree(JSContext *ctx, JSValue val)
+{
+ if (JS_IsUndefined(val) || JS_IsNull(val))
+ return JS_ToStringFree(ctx, val);
+ return JS_InvokeFree(ctx, val, JS_ATOM_toLocaleString, 0, NULL);
+}
+
+JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val)
+{
+ return JS_ToStringInternal(ctx, val, TRUE);
+}
+
+static JSValue JS_ToStringCheckObject(JSContext *ctx, JSValueConst val)
+{
+ uint32_t tag = JS_VALUE_GET_TAG(val);
+ if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
+ return JS_ThrowTypeError(ctx, "null or undefined are forbidden");
+ return JS_ToString(ctx, val);
+}
+
+static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
+{
+ JSValue val;
+ JSString *p;
+ int i;
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+ char buf[16];
+
+ val = JS_ToStringCheckObject(ctx, val1);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
+
+ if (string_buffer_init(ctx, b, p->len + 2))
+ goto fail;
+
+ if (string_buffer_putc8(b, '\"'))
+ goto fail;
+ for(i = 0; i < p->len; ) {
+ c = string_getc(p, &i);
+ switch(c) {
+ case '\t':
+ c = 't';
+ goto quote;
+ case '\r':
+ c = 'r';
+ goto quote;
+ case '\n':
+ c = 'n';
+ goto quote;
+ case '\b':
+ c = 'b';
+ goto quote;
+ case '\f':
+ c = 'f';
+ goto quote;
+ case '\"':
+ case '\\':
+ quote:
+ if (string_buffer_putc8(b, '\\'))
+ goto fail;
+ if (string_buffer_putc8(b, c))
+ goto fail;
+ break;
+ default:
+ if (c < 32 || (c >= 0xd800 && c < 0xe000)) {
+ snprintf(buf, sizeof(buf), "\\u%04x", c);
+ if (string_buffer_puts8(b, buf))
+ goto fail;
+ } else {
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+ break;
+ }
+ }
+ if (string_buffer_putc8(b, '\"'))
+ goto fail;
+ JS_FreeValue(ctx, val);
+ return string_buffer_end(b);
+ fail:
+ JS_FreeValue(ctx, val);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
+{
+ printf("%14s %4s %4s %14s %10s %s\n",
+ "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
+}
+
+/* for debug only: dump an object without side effect */
+static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
+{
+ uint32_t i;
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ JSShape *sh;
+ JSShapeProperty *prs;
+ JSProperty *pr;
+ BOOL is_first = TRUE;
+
+ /* XXX: should encode atoms with special characters */
+ sh = p->shape; /* the shape can be NULL while freeing an object */
+ printf("%14p %4d ",
+ (void *)p,
+ p->header.ref_count);
+ if (sh) {
+ printf("%3d%c %14p ",
+ sh->header.ref_count,
+ " *"[sh->is_hashed],
+ (void *)sh->proto);
+ } else {
+ printf("%3s %14s ", "-", "-");
+ }
+ printf("%10s ",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), rt->class_array[p->class_id].class_name));
+ if (p->is_exotic && p->fast_array) {
+ printf("[ ");
+ for(i = 0; i < p->u.array.count; i++) {
+ if (i != 0)
+ printf(", ");
+ switch (p->class_id) {
+ case JS_CLASS_ARRAY:
+ case JS_CLASS_ARGUMENTS:
+ JS_DumpValueShort(rt, p->u.array.u.values[i]);
+ break;
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ case JS_CLASS_FLOAT64_ARRAY:
+ {
+ int size = 1 << typed_array_size_log2(p->class_id);
+ const uint8_t *b = p->u.array.u.uint8_ptr + i * size;
+ while (size-- > 0)
+ printf("%02X", *b++);
+ }
+ break;
+ }
+ }
+ printf(" ] ");
+ }
+
+ if (sh) {
+ printf("{ ");
+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+ if (prs->atom != JS_ATOM_NULL) {
+ pr = &p->prop[i];
+ if (!is_first)
+ printf(", ");
+ printf("%s: ",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), prs->atom));
+ if ((prs->flags & JS_PROP_TMASK) == JS_PROP_GETSET) {
+ printf("[getset %p %p]", (void *)pr->u.getset.getter,
+ (void *)pr->u.getset.setter);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_VARREF) {
+ printf("[varref %p]", (void *)pr->u.var_ref);
+ } else if ((prs->flags & JS_PROP_TMASK) == JS_PROP_AUTOINIT) {
+ printf("[autoinit %p %d %p]",
+ (void *)js_autoinit_get_realm(pr),
+ js_autoinit_get_id(pr),
+ pr->u.init.opaque);
+ } else {
+ JS_DumpValueShort(rt, pr->u.value);
+ }
+ is_first = FALSE;
+ }
+ }
+ printf(" }");
+ }
+
+ if (js_class_has_bytecode(p->class_id)) {
+ JSFunctionBytecode *b = p->u.func.function_bytecode;
+ JSVarRef **var_refs;
+ if (b->closure_var_count) {
+ var_refs = p->u.func.var_refs;
+ printf(" Closure:");
+ for(i = 0; i < b->closure_var_count; i++) {
+ printf(" ");
+ JS_DumpValueShort(rt, var_refs[i]->value);
+ }
+ if (p->u.func.home_object) {
+ printf(" HomeObject: ");
+ JS_DumpValueShort(rt, JS_MKPTR(JS_TAG_OBJECT, p->u.func.home_object));
+ }
+ }
+ }
+ printf("\n");
+}
+
+static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
+{
+ if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+ JS_DumpObject(rt, (JSObject *)p);
+ } else {
+ printf("%14p %4d ",
+ (void *)p,
+ p->ref_count);
+ switch(p->gc_obj_type) {
+ case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE:
+ printf("[function bytecode]");
+ break;
+ case JS_GC_OBJ_TYPE_SHAPE:
+ printf("[shape]");
+ break;
+ case JS_GC_OBJ_TYPE_VAR_REF:
+ printf("[var_ref]");
+ break;
+ case JS_GC_OBJ_TYPE_ASYNC_FUNCTION:
+ printf("[async_function]");
+ break;
+ case JS_GC_OBJ_TYPE_JS_CONTEXT:
+ printf("[js_context]");
+ break;
+ default:
+ printf("[unknown %d]", p->gc_obj_type);
+ break;
+ }
+ printf("\n");
+ }
+}
+
+static maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ JSValueConst val)
+{
+ uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
+ const char *str;
+
+ switch(tag) {
+ case JS_TAG_INT:
+ printf("%d", JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_BOOL:
+ if (JS_VALUE_GET_BOOL(val))
+ str = "true";
+ else
+ str = "false";
+ goto print_str;
+ case JS_TAG_NULL:
+ str = "null";
+ goto print_str;
+ case JS_TAG_EXCEPTION:
+ str = "exception";
+ goto print_str;
+ case JS_TAG_UNINITIALIZED:
+ str = "uninitialized";
+ goto print_str;
+ case JS_TAG_UNDEFINED:
+ str = "undefined";
+ print_str:
+ printf("%s", str);
+ break;
+ case JS_TAG_FLOAT64:
+ printf("%.14g", JS_VALUE_GET_FLOAT64(val));
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ char *str;
+ str = bf_ftoa(NULL, &p->num, 10, 0,
+ BF_RNDZ | BF_FTOA_FORMAT_FRAC);
+ printf("%sn", str);
+ bf_realloc(&rt->bf_ctx, str, 0);
+ }
+ break;
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ char *str;
+ str = bf_ftoa(NULL, &p->num, 16, BF_PREC_INF,
+ BF_RNDZ | BF_FTOA_FORMAT_FREE | BF_FTOA_ADD_PREFIX);
+ printf("%sl", str);
+ bf_free(&rt->bf_ctx, str);
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ {
+ JSBigDecimal *p = JS_VALUE_GET_PTR(val);
+ char *str;
+ str = bfdec_ftoa(NULL, &p->num, BF_PREC_INF,
+ BF_RNDZ | BF_FTOA_FORMAT_FREE);
+ printf("%sm", str);
+ bf_free(&rt->bf_ctx, str);
+ }
+ break;
+#endif
+ case JS_TAG_STRING:
+ {
+ JSString *p;
+ p = JS_VALUE_GET_STRING(val);
+ JS_DumpString(rt, p);
+ }
+ break;
+ case JS_TAG_FUNCTION_BYTECODE:
+ {
+ JSFunctionBytecode *b = JS_VALUE_GET_PTR(val);
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ printf("[bytecode %s]", JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
+ }
+ break;
+ case JS_TAG_OBJECT:
+ {
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSAtom atom = rt->class_array[p->class_id].class_name;
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ printf("[%s %p]",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), atom), (void *)p);
+ }
+ break;
+ case JS_TAG_SYMBOL:
+ {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ printf("Symbol(%s)",
+ JS_AtomGetStrRT(rt, atom_buf, sizeof(atom_buf), js_get_atom_index(rt, p)));
+ }
+ break;
+ case JS_TAG_MODULE:
+ printf("[module]");
+ break;
+ default:
+ printf("[unknown tag %d]", tag);
+ break;
+ }
+}
+
+static maybe_unused void JS_DumpValue(JSContext *ctx,
+ JSValueConst val)
+{
+ JS_DumpValueShort(ctx->rt, val);
+}
+
+static maybe_unused void JS_PrintValue(JSContext *ctx,
+ const char *str,
+ JSValueConst val)
+{
+ printf("%s=", str);
+ JS_DumpValueShort(ctx->rt, val);
+ printf("\n");
+}
+
+/* return -1 if exception (proxy case) or TRUE/FALSE */
+int JS_IsArray(JSContext *ctx, JSValueConst val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(val);
+ if (unlikely(p->class_id == JS_CLASS_PROXY))
+ return js_proxy_isArray(ctx, val);
+ else
+ return p->class_id == JS_CLASS_ARRAY;
+ } else {
+ return FALSE;
+ }
+}
+
+static double js_pow(double a, double b)
+{
+ if (unlikely(!isfinite(b)) && fabs(a) == 1) {
+ /* not compatible with IEEE 754 */
+ return JS_FLOAT64_NAN;
+ } else {
+ return pow(a, b);
+ }
+}
+
+#ifdef CONFIG_BIGNUM
+
+JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v)
+{
+ JSValue val;
+ bf_t *a;
+ val = JS_NewBigInt(ctx);
+ if (JS_IsException(val))
+ return val;
+ a = JS_GetBigInt(val);
+ if (bf_set_si(a, v)) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ return val;
+}
+
+JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
+{
+ if (is_math_mode(ctx) &&
+ v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
+ return JS_NewInt64(ctx, v);
+ } else {
+ return JS_NewBigInt64_1(ctx, v);
+ }
+}
+
+JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
+{
+ JSValue val;
+ if (is_math_mode(ctx) && v <= MAX_SAFE_INTEGER) {
+ val = JS_NewInt64(ctx, v);
+ } else {
+ bf_t *a;
+ val = JS_NewBigInt(ctx);
+ if (JS_IsException(val))
+ return val;
+ a = JS_GetBigInt(val);
+ if (bf_set_ui(a, v)) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ }
+ return val;
+}
+
+/* if the returned bigfloat is allocated it is equal to
+ 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. Return
+ NULL in case of error. */
+static bf_t *JS_ToBigFloat(JSContext *ctx, bf_t *buf, JSValueConst val)
+{
+ uint32_t tag;
+ bf_t *r;
+ JSBigFloat *p;
+
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ if (bf_set_si(r, JS_VALUE_GET_INT(val)))
+ goto fail;
+ break;
+ case JS_TAG_FLOAT64:
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ if (bf_set_float64(r, JS_VALUE_GET_FLOAT64(val))) {
+ fail:
+ bf_delete(r);
+ return NULL;
+ }
+ break;
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ p = JS_VALUE_GET_PTR(val);
+ r = &p->num;
+ break;
+ case JS_TAG_UNDEFINED:
+ default:
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ bf_set_nan(r);
+ break;
+ }
+ return r;
+}
+
+/* return NULL if invalid type */
+static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val)
+{
+ uint32_t tag;
+ JSBigDecimal *p;
+ bfdec_t *r;
+
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_BIG_DECIMAL:
+ p = JS_VALUE_GET_PTR(val);
+ r = &p->num;
+ break;
+ default:
+ JS_ThrowTypeError(ctx, "bigdecimal expected");
+ r = NULL;
+ break;
+ }
+ return r;
+}
+
+/* return NaN if bad bigint literal */
+static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val)
+{
+ const char *str, *p;
+ size_t len;
+ int flags;
+
+ str = JS_ToCStringLen(ctx, &len, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ if ((p - str) == len) {
+ val = JS_NewBigInt64(ctx, 0);
+ } else {
+ flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT;
+ if (is_math_mode(ctx))
+ flags |= ATOD_MODE_BIGINT;
+ val = js_atof(ctx, p, &p, 0, flags);
+ p += skip_spaces(p);
+ if (!JS_IsException(val)) {
+ if ((p - str) != len) {
+ JS_FreeValue(ctx, val);
+ val = JS_NAN;
+ }
+ }
+ }
+ JS_FreeCString(ctx, str);
+ return val;
+}
+
+static JSValue JS_StringToBigIntErr(JSContext *ctx, JSValue val)
+{
+ val = JS_StringToBigInt(ctx, val);
+ if (JS_VALUE_IS_NAN(val))
+ return JS_ThrowSyntaxError(ctx, "invalid bigint literal");
+ return val;
+}
+
+/* if the returned bigfloat is allocated it is equal to
+ 'buf'. Otherwise it is a pointer to the bigfloat in 'val'. */
+static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val)
+{
+ uint32_t tag;
+ bf_t *r;
+ JSBigFloat *p;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ if (!is_math_mode(ctx))
+ goto fail;
+ /* fall tru */
+ case JS_TAG_BOOL:
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ bf_set_si(r, JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ double d = JS_VALUE_GET_FLOAT64(val);
+ if (!is_math_mode(ctx))
+ goto fail;
+ if (!isfinite(d))
+ goto fail;
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ d = trunc(d);
+ bf_set_float64(r, d);
+ }
+ break;
+ case JS_TAG_BIG_INT:
+ p = JS_VALUE_GET_PTR(val);
+ r = &p->num;
+ break;
+ case JS_TAG_BIG_FLOAT:
+ if (!is_math_mode(ctx))
+ goto fail;
+ p = JS_VALUE_GET_PTR(val);
+ if (!bf_is_finite(&p->num))
+ goto fail;
+ r = buf;
+ bf_init(ctx->bf_ctx, r);
+ bf_set(r, &p->num);
+ bf_rint(r, BF_RNDZ);
+ JS_FreeValue(ctx, val);
+ break;
+ case JS_TAG_STRING:
+ val = JS_StringToBigIntErr(ctx, val);
+ if (JS_IsException(val))
+ return NULL;
+ goto redo;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ return NULL;
+ goto redo;
+ default:
+ fail:
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeError(ctx, "cannot convert to bigint");
+ return NULL;
+ }
+ return r;
+}
+
+static bf_t *JS_ToBigInt(JSContext *ctx, bf_t *buf, JSValueConst val)
+{
+ return JS_ToBigIntFree(ctx, buf, JS_DupValue(ctx, val));
+}
+
+static __maybe_unused JSValue JS_ToBigIntValueFree(JSContext *ctx, JSValue val)
+{
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_INT) {
+ return val;
+ } else {
+ bf_t a_s, *a, *r;
+ int ret;
+ JSValue res;
+
+ res = JS_NewBigInt(ctx);
+ if (JS_IsException(res))
+ return JS_EXCEPTION;
+ a = JS_ToBigIntFree(ctx, &a_s, val);
+ if (!a) {
+ JS_FreeValue(ctx, res);
+ return JS_EXCEPTION;
+ }
+ r = JS_GetBigInt(res);
+ ret = bf_set(r, a);
+ JS_FreeBigInt(ctx, a, &a_s);
+ if (ret) {
+ JS_FreeValue(ctx, res);
+ return JS_ThrowOutOfMemory(ctx);
+ }
+ return JS_CompactBigInt(ctx, res);
+ }
+}
+
+/* free the bf_t allocated by JS_ToBigInt */
+static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf)
+{
+ if (a == buf) {
+ bf_delete(a);
+ } else {
+ JSBigFloat *p = (JSBigFloat *)((uint8_t *)a -
+ offsetof(JSBigFloat, num));
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p));
+ }
+}
+
+/* XXX: merge with JS_ToInt64Free with a specific flag */
+static int JS_ToBigInt64Free(JSContext *ctx, int64_t *pres, JSValue val)
+{
+ bf_t a_s, *a;
+
+ a = JS_ToBigIntFree(ctx, &a_s, val);
+ if (!a) {
+ *pres = 0;
+ return -1;
+ }
+ bf_get_int64(pres, a, BF_GET_INT_MOD);
+ JS_FreeBigInt(ctx, a, &a_s);
+ return 0;
+}
+
+int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+{
+ return JS_ToBigInt64Free(ctx, pres, JS_DupValue(ctx, val));
+}
+
+static JSBigFloat *js_new_bf(JSContext *ctx)
+{
+ JSBigFloat *p;
+ p = js_malloc(ctx, sizeof(*p));
+ if (!p)
+ return NULL;
+ p->header.ref_count = 1;
+ bf_init(ctx->bf_ctx, &p->num);
+ return p;
+}
+
+static JSValue JS_NewBigFloat(JSContext *ctx)
+{
+ JSBigFloat *p;
+ p = js_malloc(ctx, sizeof(*p));
+ if (!p)
+ return JS_EXCEPTION;
+ p->header.ref_count = 1;
+ bf_init(ctx->bf_ctx, &p->num);
+ return JS_MKPTR(JS_TAG_BIG_FLOAT, p);
+}
+
+static JSValue JS_NewBigDecimal(JSContext *ctx)
+{
+ JSBigDecimal *p;
+ p = js_malloc(ctx, sizeof(*p));
+ if (!p)
+ return JS_EXCEPTION;
+ p->header.ref_count = 1;
+ bfdec_init(ctx->bf_ctx, &p->num);
+ return JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
+}
+
+static JSValue JS_NewBigInt(JSContext *ctx)
+{
+ JSBigFloat *p;
+ p = js_malloc(ctx, sizeof(*p));
+ if (!p)
+ return JS_EXCEPTION;
+ p->header.ref_count = 1;
+ bf_init(ctx->bf_ctx, &p->num);
+ return JS_MKPTR(JS_TAG_BIG_INT, p);
+}
+
+static JSValue JS_CompactBigInt1(JSContext *ctx, JSValue val,
+ BOOL convert_to_safe_integer)
+{
+ int64_t v;
+ bf_t *a;
+
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_BIG_INT)
+ return val; /* fail safe */
+ a = JS_GetBigInt(val);
+ if (convert_to_safe_integer && bf_get_int64(&v, a, 0) == 0 &&
+ v >= -MAX_SAFE_INTEGER && v <= MAX_SAFE_INTEGER) {
+ JS_FreeValue(ctx, val);
+ return JS_NewInt64(ctx, v);
+ } else if (a->expn == BF_EXP_ZERO && a->sign) {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ assert(p->header.ref_count == 1);
+ a->sign = 0;
+ }
+ return val;
+}
+
+/* Convert the big int to a safe integer if in math mode. normalize
+ the zero representation. Could also be used to convert the bigint
+ to a short bigint value. The reference count of the value must be
+ 1. Cannot fail */
+static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val)
+{
+ return JS_CompactBigInt1(ctx, val, is_math_mode(ctx));
+}
+
+/* must be kept in sync with JSOverloadableOperatorEnum */
+/* XXX: use atoms ? */
+static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = {
+ "+",
+ "-",
+ "*",
+ "/",
+ "%",
+ "**",
+ "|",
+ "&",
+ "^",
+ "<<",
+ ">>",
+ ">>>",
+ "==",
+ "<",
+ "pos",
+ "neg",
+ "++",
+ "--",
+ "~",
+};
+
+static int get_ovop_from_opcode(OPCodeEnum op)
+{
+ switch(op) {
+ case OP_add:
+ return JS_OVOP_ADD;
+ case OP_sub:
+ return JS_OVOP_SUB;
+ case OP_mul:
+ return JS_OVOP_MUL;
+ case OP_div:
+ return JS_OVOP_DIV;
+ case OP_mod:
+ case OP_math_mod:
+ return JS_OVOP_MOD;
+ case OP_pow:
+ return JS_OVOP_POW;
+ case OP_or:
+ return JS_OVOP_OR;
+ case OP_and:
+ return JS_OVOP_AND;
+ case OP_xor:
+ return JS_OVOP_XOR;
+ case OP_shl:
+ return JS_OVOP_SHL;
+ case OP_sar:
+ return JS_OVOP_SAR;
+ case OP_shr:
+ return JS_OVOP_SHR;
+ case OP_eq:
+ case OP_neq:
+ return JS_OVOP_EQ;
+ case OP_lt:
+ case OP_lte:
+ case OP_gt:
+ case OP_gte:
+ return JS_OVOP_LESS;
+ case OP_plus:
+ return JS_OVOP_POS;
+ case OP_neg:
+ return JS_OVOP_NEG;
+ case OP_inc:
+ return JS_OVOP_INC;
+ case OP_dec:
+ return JS_OVOP_DEC;
+ default:
+ abort();
+ }
+}
+
+/* return NULL if not present */
+static JSObject *find_binary_op(JSBinaryOperatorDef *def,
+ uint32_t operator_index,
+ JSOverloadableOperatorEnum op)
+{
+ JSBinaryOperatorDefEntry *ent;
+ int i;
+ for(i = 0; i < def->count; i++) {
+ ent = &def->tab[i];
+ if (ent->operator_index == operator_index)
+ return ent->ops[op];
+ }
+ return NULL;
+}
+
+/* return -1 if exception, 0 if no operator overloading, 1 if
+ overloaded operator called */
+static WARN_UNUSED int js_call_binary_op_fallback(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst op1,
+ JSValueConst op2,
+ OPCodeEnum op,
+ BOOL is_numeric,
+ int hint)
+{
+ JSValue opset1_obj, opset2_obj, method, ret, new_op1, new_op2;
+ JSOperatorSetData *opset1, *opset2;
+ JSOverloadableOperatorEnum ovop;
+ JSObject *p;
+ JSValueConst args[2];
+
+ if (!ctx->allow_operator_overloading)
+ return 0;
+
+ opset2_obj = JS_UNDEFINED;
+ opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
+ if (JS_IsException(opset1_obj))
+ goto exception;
+ if (JS_IsUndefined(opset1_obj))
+ return 0;
+ opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
+ if (!opset1)
+ goto exception;
+
+ opset2_obj = JS_GetProperty(ctx, op2, JS_ATOM_Symbol_operatorSet);
+ if (JS_IsException(opset2_obj))
+ goto exception;
+ if (JS_IsUndefined(opset2_obj)) {
+ JS_FreeValue(ctx, opset1_obj);
+ return 0;
+ }
+ opset2 = JS_GetOpaque2(ctx, opset2_obj, JS_CLASS_OPERATOR_SET);
+ if (!opset2)
+ goto exception;
+
+ if (opset1->is_primitive && opset2->is_primitive) {
+ JS_FreeValue(ctx, opset1_obj);
+ JS_FreeValue(ctx, opset2_obj);
+ return 0;
+ }
+
+ ovop = get_ovop_from_opcode(op);
+
+ if (opset1->operator_counter == opset2->operator_counter) {
+ p = opset1->self_ops[ovop];
+ } else if (opset1->operator_counter > opset2->operator_counter) {
+ p = find_binary_op(&opset1->left, opset2->operator_counter, ovop);
+ } else {
+ p = find_binary_op(&opset2->right, opset1->operator_counter, ovop);
+ }
+ if (!p) {
+ JS_ThrowTypeError(ctx, "operator %s: no function defined",
+ js_overloadable_operator_names[ovop]);
+ goto exception;
+ }
+
+ if (opset1->is_primitive) {
+ if (is_numeric) {
+ new_op1 = JS_ToNumeric(ctx, op1);
+ } else {
+ new_op1 = JS_ToPrimitive(ctx, op1, hint);
+ }
+ if (JS_IsException(new_op1))
+ goto exception;
+ } else {
+ new_op1 = JS_DupValue(ctx, op1);
+ }
+
+ if (opset2->is_primitive) {
+ if (is_numeric) {
+ new_op2 = JS_ToNumeric(ctx, op2);
+ } else {
+ new_op2 = JS_ToPrimitive(ctx, op2, hint);
+ }
+ if (JS_IsException(new_op2)) {
+ JS_FreeValue(ctx, new_op1);
+ goto exception;
+ }
+ } else {
+ new_op2 = JS_DupValue(ctx, op2);
+ }
+
+ /* XXX: could apply JS_ToPrimitive() if primitive type so that the
+ operator function does not get a value object */
+
+ method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ if (ovop == JS_OVOP_LESS && (op == OP_lte || op == OP_gt)) {
+ args[0] = new_op2;
+ args[1] = new_op1;
+ } else {
+ args[0] = new_op1;
+ args[1] = new_op2;
+ }
+ ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
+ JS_FreeValue(ctx, new_op1);
+ JS_FreeValue(ctx, new_op2);
+ if (JS_IsException(ret))
+ goto exception;
+ if (ovop == JS_OVOP_EQ) {
+ BOOL res = JS_ToBoolFree(ctx, ret);
+ if (op == OP_neq)
+ res ^= 1;
+ ret = JS_NewBool(ctx, res);
+ } else if (ovop == JS_OVOP_LESS) {
+ if (JS_IsUndefined(ret)) {
+ ret = JS_FALSE;
+ } else {
+ BOOL res = JS_ToBoolFree(ctx, ret);
+ if (op == OP_lte || op == OP_gte)
+ res ^= 1;
+ ret = JS_NewBool(ctx, res);
+ }
+ }
+ JS_FreeValue(ctx, opset1_obj);
+ JS_FreeValue(ctx, opset2_obj);
+ *pret = ret;
+ return 1;
+ exception:
+ JS_FreeValue(ctx, opset1_obj);
+ JS_FreeValue(ctx, opset2_obj);
+ *pret = JS_UNDEFINED;
+ return -1;
+}
+
+/* try to call the operation on the operatorSet field of 'obj'. Only
+ used for "/" and "**" on the BigInt prototype in math mode */
+static WARN_UNUSED int js_call_binary_op_simple(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst obj,
+ JSValueConst op1,
+ JSValueConst op2,
+ OPCodeEnum op)
+{
+ JSValue opset1_obj, method, ret, new_op1, new_op2;
+ JSOperatorSetData *opset1;
+ JSOverloadableOperatorEnum ovop;
+ JSObject *p;
+ JSValueConst args[2];
+
+ opset1_obj = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
+ if (JS_IsException(opset1_obj))
+ goto exception;
+ if (JS_IsUndefined(opset1_obj))
+ return 0;
+ opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
+ if (!opset1)
+ goto exception;
+ ovop = get_ovop_from_opcode(op);
+
+ p = opset1->self_ops[ovop];
+ if (!p) {
+ JS_FreeValue(ctx, opset1_obj);
+ return 0;
+ }
+
+ new_op1 = JS_ToNumeric(ctx, op1);
+ if (JS_IsException(new_op1))
+ goto exception;
+ new_op2 = JS_ToNumeric(ctx, op2);
+ if (JS_IsException(new_op2)) {
+ JS_FreeValue(ctx, new_op1);
+ goto exception;
+ }
+
+ method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ args[0] = new_op1;
+ args[1] = new_op2;
+ ret = JS_CallFree(ctx, method, JS_UNDEFINED, 2, args);
+ JS_FreeValue(ctx, new_op1);
+ JS_FreeValue(ctx, new_op2);
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, opset1_obj);
+ *pret = ret;
+ return 1;
+ exception:
+ JS_FreeValue(ctx, opset1_obj);
+ *pret = JS_UNDEFINED;
+ return -1;
+}
+
+/* return -1 if exception, 0 if no operator overloading, 1 if
+ overloaded operator called */
+static WARN_UNUSED int js_call_unary_op_fallback(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst op1,
+ OPCodeEnum op)
+{
+ JSValue opset1_obj, method, ret;
+ JSOperatorSetData *opset1;
+ JSOverloadableOperatorEnum ovop;
+ JSObject *p;
+
+ if (!ctx->allow_operator_overloading)
+ return 0;
+
+ opset1_obj = JS_GetProperty(ctx, op1, JS_ATOM_Symbol_operatorSet);
+ if (JS_IsException(opset1_obj))
+ goto exception;
+ if (JS_IsUndefined(opset1_obj))
+ return 0;
+ opset1 = JS_GetOpaque2(ctx, opset1_obj, JS_CLASS_OPERATOR_SET);
+ if (!opset1)
+ goto exception;
+ if (opset1->is_primitive) {
+ JS_FreeValue(ctx, opset1_obj);
+ return 0;
+ }
+
+ ovop = get_ovop_from_opcode(op);
+
+ p = opset1->self_ops[ovop];
+ if (!p) {
+ JS_ThrowTypeError(ctx, "no overloaded operator %s",
+ js_overloadable_operator_names[ovop]);
+ goto exception;
+ }
+ method = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ ret = JS_CallFree(ctx, method, JS_UNDEFINED, 1, &op1);
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, opset1_obj);
+ *pret = ret;
+ return 1;
+ exception:
+ JS_FreeValue(ctx, opset1_obj);
+ *pret = JS_UNDEFINED;
+ return -1;
+}
+
+static JSValue throw_bf_exception(JSContext *ctx, int status)
+{
+ const char *str;
+ if (status & BF_ST_MEM_ERROR)
+ return JS_ThrowOutOfMemory(ctx);
+ if (status & BF_ST_DIVIDE_ZERO) {
+ str = "division by zero";
+ } else if (status & BF_ST_INVALID_OP) {
+ str = "invalid operation";
+ } else {
+ str = "integer overflow";
+ }
+ return JS_ThrowRangeError(ctx, "%s", str);
+}
+
+static int js_unary_arith_bigint(JSContext *ctx,
+ JSValue *pres, OPCodeEnum op, JSValue op1)
+{
+ bf_t a_s, *r, *a;
+ int ret, v;
+ JSValue res;
+
+ if (op == OP_plus && !is_math_mode(ctx)) {
+ JS_ThrowTypeError(ctx, "bigint argument with unary +");
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ res = JS_NewBigInt(ctx);
+ if (JS_IsException(res)) {
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ r = JS_GetBigInt(res);
+ a = JS_ToBigInt(ctx, &a_s, op1);
+ ret = 0;
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_plus:
+ ret = bf_set(r, a);
+ break;
+ case OP_neg:
+ ret = bf_set(r, a);
+ bf_neg(r);
+ break;
+ case OP_not:
+ ret = bf_add_si(r, a, 1, BF_PREC_INF, BF_RNDZ);
+ bf_neg(r);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeValue(ctx, op1);
+ if (unlikely(ret)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ res = JS_CompactBigInt(ctx, res);
+ *pres = res;
+ return 0;
+}
+
+static int js_unary_arith_bigfloat(JSContext *ctx,
+ JSValue *pres, OPCodeEnum op, JSValue op1)
+{
+ bf_t a_s, *r, *a;
+ int ret, v;
+ JSValue res;
+
+ if (op == OP_plus && !is_math_mode(ctx)) {
+ JS_ThrowTypeError(ctx, "bigfloat argument with unary +");
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res)) {
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ r = JS_GetBigFloat(res);
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ ret = 0;
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case OP_plus:
+ ret = bf_set(r, a);
+ break;
+ case OP_neg:
+ ret = bf_set(r, a);
+ bf_neg(r);
+ break;
+ default:
+ abort();
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, op1);
+ if (unlikely(ret & BF_ST_MEM_ERROR)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = res;
+ return 0;
+}
+
+static int js_unary_arith_bigdecimal(JSContext *ctx,
+ JSValue *pres, OPCodeEnum op, JSValue op1)
+{
+ bfdec_t *r, *a;
+ int ret, v;
+ JSValue res;
+
+ if (op == OP_plus && !is_math_mode(ctx)) {
+ JS_ThrowTypeError(ctx, "bigdecimal argument with unary +");
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+
+ res = JS_NewBigDecimal(ctx);
+ if (JS_IsException(res)) {
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ r = JS_GetBigDecimal(res);
+ a = JS_ToBigDecimal(ctx, op1);
+ ret = 0;
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_plus:
+ ret = bfdec_set(r, a);
+ break;
+ case OP_neg:
+ ret = bfdec_set(r, a);
+ bfdec_neg(r);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeValue(ctx, op1);
+ if (unlikely(ret)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = res;
+ return 0;
+}
+
+static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, val;
+ int v, ret;
+ uint32_t tag;
+
+ op1 = sp[-1];
+ /* fast path for float64 */
+ if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1)))
+ goto handle_float64;
+ if (JS_IsObject(op1)) {
+ ret = js_call_unary_op_fallback(ctx, &val, op1, op);
+ if (ret < 0)
+ return -1;
+ if (ret) {
+ JS_FreeValue(ctx, op1);
+ sp[-1] = val;
+ return 0;
+ }
+ }
+
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1))
+ goto exception;
+ tag = JS_VALUE_GET_TAG(op1);
+ switch(tag) {
+ case JS_TAG_INT:
+ {
+ int64_t v64;
+ v64 = JS_VALUE_GET_INT(op1);
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ v64 += v;
+ break;
+ case OP_plus:
+ break;
+ case OP_neg:
+ if (v64 == 0) {
+ sp[-1] = __JS_NewFloat64(ctx, -0.0);
+ return 0;
+ } else {
+ v64 = -v64;
+ }
+ break;
+ default:
+ abort();
+ }
+ sp[-1] = JS_NewInt64(ctx, v64);
+ }
+ break;
+ case JS_TAG_BIG_INT:
+ handle_bigint:
+ if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1))
+ goto exception;
+ break;
+ case JS_TAG_BIG_FLOAT:
+ if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1))
+ goto exception;
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1))
+ goto exception;
+ break;
+ default:
+ handle_float64:
+ {
+ double d;
+ if (is_math_mode(ctx))
+ goto handle_bigint;
+ d = JS_VALUE_GET_FLOAT64(op1);
+ switch(op) {
+ case OP_inc:
+ case OP_dec:
+ v = 2 * (op - OP_dec) - 1;
+ d += v;
+ break;
+ case OP_plus:
+ break;
+ case OP_neg:
+ d = -d;
+ break;
+ default:
+ abort();
+ }
+ sp[-1] = __JS_NewFloat64(ctx, d);
+ }
+ break;
+ }
+ return 0;
+ exception:
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static WARN_UNUSED int js_post_inc_slow(JSContext *ctx,
+ JSValue *sp, OPCodeEnum op)
+{
+ JSValue op1;
+
+ /* XXX: allow custom operators */
+ op1 = sp[-1];
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+ }
+ sp[-1] = op1;
+ sp[0] = JS_DupValue(ctx, op1);
+ return js_unary_arith_slow(ctx, sp + 1, op - OP_post_dec + OP_dec);
+}
+
+static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, val;
+ int ret;
+
+ op1 = sp[-1];
+ if (JS_IsObject(op1)) {
+ ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not);
+ if (ret < 0)
+ return -1;
+ if (ret) {
+ JS_FreeValue(ctx, op1);
+ sp[-1] = val;
+ return 0;
+ }
+ }
+
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1))
+ goto exception;
+ if (is_math_mode(ctx) || JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT) {
+ if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, OP_not, op1))
+ goto exception;
+ } else {
+ int32_t v1;
+ if (unlikely(JS_ToInt32Free(ctx, &v1, op1)))
+ goto exception;
+ sp[-1] = JS_NewInt32(ctx, ~v1);
+ }
+ return 0;
+ exception:
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static int js_binary_arith_bigfloat(JSContext *ctx, OPCodeEnum op,
+ JSValue *pres, JSValue op1, JSValue op2)
+{
+ bf_t a_s, b_s, *r, *a, *b;
+ int ret;
+ JSValue res;
+
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res)) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return -1;
+ }
+ r = JS_GetBigFloat(res);
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ b = JS_ToBigFloat(ctx, &b_s, op2);
+ bf_init(ctx->bf_ctx, r);
+ switch(op) {
+ case OP_add:
+ ret = bf_add(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case OP_sub:
+ ret = bf_sub(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case OP_mul:
+ ret = bf_mul(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case OP_div:
+ ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case OP_math_mod:
+ /* Euclidian remainder */
+ ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
+ BF_DIVREM_EUCLIDIAN);
+ break;
+ case OP_mod:
+ ret = bf_rem(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags,
+ BF_RNDZ);
+ break;
+ case OP_pow:
+ ret = bf_pow(r, a, b, ctx->fp_env.prec,
+ ctx->fp_env.flags | BF_POW_JS_QUIRKS);
+ break;
+ default:
+ abort();
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ if (b == &b_s)
+ bf_delete(b);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (unlikely(ret & BF_ST_MEM_ERROR)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = res;
+ return 0;
+}
+
+static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op,
+ JSValue *pres, JSValue op1, JSValue op2)
+{
+ bf_t a_s, b_s, *r, *a, *b;
+ int ret;
+ JSValue res;
+
+ res = JS_NewBigInt(ctx);
+ if (JS_IsException(res))
+ goto fail;
+ a = JS_ToBigInt(ctx, &a_s, op1);
+ if (!a)
+ goto fail;
+ b = JS_ToBigInt(ctx, &b_s, op2);
+ if (!b) {
+ JS_FreeBigInt(ctx, a, &a_s);
+ goto fail;
+ }
+ r = JS_GetBigInt(res);
+ ret = 0;
+ switch(op) {
+ case OP_add:
+ ret = bf_add(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_sub:
+ ret = bf_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_mul:
+ ret = bf_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_div:
+ if (!is_math_mode(ctx)) {
+ bf_t rem_s, *rem = &rem_s;
+ bf_init(ctx->bf_ctx, rem);
+ ret = bf_divrem(r, rem, a, b, BF_PREC_INF, BF_RNDZ,
+ BF_RNDZ);
+ bf_delete(rem);
+ } else {
+ goto math_mode_div_pow;
+ }
+ break;
+ case OP_math_mod:
+ /* Euclidian remainder */
+ ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
+ BF_DIVREM_EUCLIDIAN) & BF_ST_INVALID_OP;
+ break;
+ case OP_mod:
+ ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ,
+ BF_RNDZ) & BF_ST_INVALID_OP;
+ break;
+ case OP_pow:
+ if (b->sign) {
+ if (!is_math_mode(ctx)) {
+ ret = BF_ST_INVALID_OP;
+ } else {
+ math_mode_div_pow:
+ JS_FreeValue(ctx, res);
+ ret = js_call_binary_op_simple(ctx, &res, ctx->class_proto[JS_CLASS_BIG_INT], op1, op2, op);
+ if (ret != 0) {
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeBigInt(ctx, b, &b_s);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (ret < 0) {
+ return -1;
+ } else {
+ *pres = res;
+ return 0;
+ }
+ }
+ /* if no BigInt power operator defined, return a
+ bigfloat */
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res)) {
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeBigInt(ctx, b, &b_s);
+ goto fail;
+ }
+ r = JS_GetBigFloat(res);
+ if (op == OP_div) {
+ ret = bf_div(r, a, b, ctx->fp_env.prec, ctx->fp_env.flags) & BF_ST_MEM_ERROR;
+ } else {
+ ret = bf_pow(r, a, b, ctx->fp_env.prec,
+ ctx->fp_env.flags | BF_POW_JS_QUIRKS) & BF_ST_MEM_ERROR;
+ }
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeBigInt(ctx, b, &b_s);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (unlikely(ret)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = res;
+ return 0;
+ }
+ } else {
+ ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS);
+ }
+ break;
+
+ /* logical operations */
+ case OP_shl:
+ case OP_sar:
+ {
+ slimb_t v2;
+#if LIMB_BITS == 32
+ bf_get_int32(&v2, b, 0);
+ if (v2 == INT32_MIN)
+ v2 = INT32_MIN + 1;
+#else
+ bf_get_int64(&v2, b, 0);
+ if (v2 == INT64_MIN)
+ v2 = INT64_MIN + 1;
+#endif
+ if (op == OP_sar)
+ v2 = -v2;
+ ret = bf_set(r, a);
+ ret |= bf_mul_2exp(r, v2, BF_PREC_INF, BF_RNDZ);
+ if (v2 < 0) {
+ ret |= bf_rint(r, BF_RNDD) & (BF_ST_OVERFLOW | BF_ST_MEM_ERROR);
+ }
+ }
+ break;
+ case OP_and:
+ ret = bf_logic_and(r, a, b);
+ break;
+ case OP_or:
+ ret = bf_logic_or(r, a, b);
+ break;
+ case OP_xor:
+ ret = bf_logic_xor(r, a, b);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeBigInt(ctx, b, &b_s);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (unlikely(ret)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = JS_CompactBigInt(ctx, res);
+ return 0;
+ fail:
+ JS_FreeValue(ctx, res);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return -1;
+}
+
+/* b must be a positive integer */
+static int js_bfdec_pow(bfdec_t *r, const bfdec_t *a, const bfdec_t *b)
+{
+ bfdec_t b1;
+ int32_t b2;
+ int ret;
+
+ bfdec_init(b->ctx, &b1);
+ ret = bfdec_set(&b1, b);
+ if (ret) {
+ bfdec_delete(&b1);
+ return ret;
+ }
+ ret = bfdec_rint(&b1, BF_RNDZ);
+ if (ret) {
+ bfdec_delete(&b1);
+ return BF_ST_INVALID_OP; /* must be an integer */
+ }
+ ret = bfdec_get_int32(&b2, &b1);
+ bfdec_delete(&b1);
+ if (ret)
+ return ret; /* overflow */
+ if (b2 < 0)
+ return BF_ST_INVALID_OP; /* must be positive */
+ return bfdec_pow_ui(r, a, b2);
+}
+
+static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
+ JSValue *pres, JSValue op1, JSValue op2)
+{
+ bfdec_t *r, *a, *b;
+ int ret;
+ JSValue res;
+
+ res = JS_NewBigDecimal(ctx);
+ if (JS_IsException(res))
+ goto fail;
+ r = JS_GetBigDecimal(res);
+
+ a = JS_ToBigDecimal(ctx, op1);
+ if (!a)
+ goto fail;
+ b = JS_ToBigDecimal(ctx, op2);
+ if (!b)
+ goto fail;
+ switch(op) {
+ case OP_add:
+ ret = bfdec_add(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_sub:
+ ret = bfdec_sub(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_mul:
+ ret = bfdec_mul(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_div:
+ ret = bfdec_div(r, a, b, BF_PREC_INF, BF_RNDZ);
+ break;
+ case OP_math_mod:
+ /* Euclidian remainder */
+ ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_DIVREM_EUCLIDIAN);
+ break;
+ case OP_mod:
+ ret = bfdec_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ);
+ break;
+ case OP_pow:
+ ret = js_bfdec_pow(r, a, b);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (unlikely(ret)) {
+ JS_FreeValue(ctx, res);
+ throw_bf_exception(ctx, ret);
+ return -1;
+ }
+ *pres = res;
+ return 0;
+ fail:
+ JS_FreeValue(ctx, res);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return -1;
+}
+
+static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2, res;
+ uint32_t tag1, tag2;
+ int ret;
+ double d1, d2;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ /* fast path for float operations */
+ if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ goto handle_float64;
+ }
+
+ /* try to call an overloaded operator */
+ if ((tag1 == JS_TAG_OBJECT &&
+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
+ (tag2 == JS_TAG_OBJECT &&
+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
+ ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
+ if (ret != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (ret < 0) {
+ goto exception;
+ } else {
+ sp[-2] = res;
+ return 0;
+ }
+ }
+ }
+
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
+ int32_t v1, v2;
+ int64_t v;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+ switch(op) {
+ case OP_sub:
+ v = (int64_t)v1 - (int64_t)v2;
+ break;
+ case OP_mul:
+ v = (int64_t)v1 * (int64_t)v2;
+ if (is_math_mode(ctx) &&
+ (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER))
+ goto handle_bigint;
+ if (v == 0 && (v1 | v2) < 0) {
+ sp[-2] = __JS_NewFloat64(ctx, -0.0);
+ return 0;
+ }
+ break;
+ case OP_div:
+ if (is_math_mode(ctx))
+ goto handle_bigint;
+ sp[-2] = __JS_NewFloat64(ctx, (double)v1 / (double)v2);
+ return 0;
+ case OP_math_mod:
+ if (unlikely(v2 == 0)) {
+ throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO);
+ goto exception;
+ }
+ v = (int64_t)v1 % (int64_t)v2;
+ if (v < 0) {
+ if (v2 < 0)
+ v -= v2;
+ else
+ v += v2;
+ }
+ break;
+ case OP_mod:
+ if (v1 < 0 || v2 <= 0) {
+ sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2));
+ return 0;
+ } else {
+ v = (int64_t)v1 % (int64_t)v2;
+ }
+ break;
+ case OP_pow:
+ if (!is_math_mode(ctx)) {
+ sp[-2] = JS_NewFloat64(ctx, js_pow(v1, v2));
+ return 0;
+ } else {
+ goto handle_bigint;
+ }
+ break;
+ default:
+ abort();
+ }
+ sp[-2] = JS_NewInt64(ctx, v);
+ } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
+ if (ctx->rt->bigdecimal_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
+ if (ctx->rt->bigfloat_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
+ handle_bigint:
+ if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+ goto exception;
+ } else {
+ double dr;
+ /* float64 result */
+ if (JS_ToFloat64Free(ctx, &d1, op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (JS_ToFloat64Free(ctx, &d2, op2))
+ goto exception;
+ handle_float64:
+ if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
+ goto handle_bigint;
+ switch(op) {
+ case OP_sub:
+ dr = d1 - d2;
+ break;
+ case OP_mul:
+ dr = d1 * d2;
+ break;
+ case OP_div:
+ dr = d1 / d2;
+ break;
+ case OP_mod:
+ dr = fmod(d1, d2);
+ break;
+ case OP_math_mod:
+ d2 = fabs(d2);
+ dr = fmod(d1, d2);
+ /* XXX: loss of accuracy if dr < 0 */
+ if (dr < 0)
+ dr += d2;
+ break;
+ case OP_pow:
+ dr = js_pow(d1, d2);
+ break;
+ default:
+ abort();
+ }
+ sp[-2] = __JS_NewFloat64(ctx, dr);
+ }
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2, res;
+ uint32_t tag1, tag2;
+ int ret;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ /* fast path for float64 */
+ if (tag1 == JS_TAG_FLOAT64 && tag2 == JS_TAG_FLOAT64) {
+ double d1, d2;
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
+ return 0;
+ }
+
+ if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) {
+ /* try to call an overloaded operator */
+ if ((tag1 == JS_TAG_OBJECT &&
+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED &&
+ tag2 != JS_TAG_STRING)) ||
+ (tag2 == JS_TAG_OBJECT &&
+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED &&
+ tag1 != JS_TAG_STRING))) {
+ ret = js_call_binary_op_fallback(ctx, &res, op1, op2, OP_add,
+ FALSE, HINT_NONE);
+ if (ret != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (ret < 0) {
+ goto exception;
+ } else {
+ sp[-2] = res;
+ return 0;
+ }
+ }
+ }
+
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ }
+
+ if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {
+ sp[-2] = JS_ConcatString(ctx, op1, op2);
+ if (JS_IsException(sp[-2]))
+ goto exception;
+ return 0;
+ }
+
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
+ int32_t v1, v2;
+ int64_t v;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+ v = (int64_t)v1 + (int64_t)v2;
+ sp[-2] = JS_NewInt64(ctx, v);
+ } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
+ if (ctx->rt->bigdecimal_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
+ if (ctx->rt->bigfloat_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
+ handle_bigint:
+ if (ctx->rt->bigint_ops.binary_arith(ctx, OP_add, sp - 2, op1, op2))
+ goto exception;
+ } else {
+ double d1, d2;
+ /* float64 result */
+ if (JS_ToFloat64Free(ctx, &d1, op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (JS_ToFloat64Free(ctx, &d2, op2))
+ goto exception;
+ if (is_math_mode(ctx) && is_safe_integer(d1) && is_safe_integer(d2))
+ goto handle_bigint;
+ sp[-2] = __JS_NewFloat64(ctx, d1 + d2);
+ }
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline WARN_UNUSED int js_binary_logic_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2, res;
+ int ret;
+ uint32_t tag1, tag2;
+ uint32_t v1, v2, r;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ /* try to call an overloaded operator */
+ if ((tag1 == JS_TAG_OBJECT &&
+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
+ (tag2 == JS_TAG_OBJECT &&
+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
+ ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0);
+ if (ret != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (ret < 0) {
+ goto exception;
+ } else {
+ sp[-2] = res;
+ return 0;
+ }
+ }
+ }
+
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+
+ if (is_math_mode(ctx))
+ goto bigint_op;
+
+ tag1 = JS_VALUE_GET_TAG(op1);
+ tag2 = JS_VALUE_GET_TAG(op2);
+ if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
+ if (tag1 != tag2) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ JS_ThrowTypeError(ctx, "both operands must be bigint");
+ goto exception;
+ } else {
+ bigint_op:
+ if (ctx->rt->bigint_ops.binary_arith(ctx, op, sp - 2, op1, op2))
+ goto exception;
+ }
+ } else {
+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
+ goto exception;
+ switch(op) {
+ case OP_shl:
+ r = v1 << (v2 & 0x1f);
+ break;
+ case OP_sar:
+ r = (int)v1 >> (v2 & 0x1f);
+ break;
+ case OP_and:
+ r = v1 & v2;
+ break;
+ case OP_or:
+ r = v1 | v2;
+ break;
+ case OP_xor:
+ r = v1 ^ v2;
+ break;
+ default:
+ abort();
+ }
+ sp[-2] = JS_NewInt32(ctx, r);
+ }
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+/* Note: also used for bigint */
+static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op,
+ JSValue op1, JSValue op2)
+{
+ bf_t a_s, b_s, *a, *b;
+ int res;
+
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ if (!a) {
+ JS_FreeValue(ctx, op2);
+ return -1;
+ }
+ b = JS_ToBigFloat(ctx, &b_s, op2);
+ if (!b) {
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ switch(op) {
+ case OP_lt:
+ res = bf_cmp_lt(a, b); /* if NaN return false */
+ break;
+ case OP_lte:
+ res = bf_cmp_le(a, b); /* if NaN return false */
+ break;
+ case OP_gt:
+ res = bf_cmp_lt(b, a); /* if NaN return false */
+ break;
+ case OP_gte:
+ res = bf_cmp_le(b, a); /* if NaN return false */
+ break;
+ case OP_eq:
+ res = bf_cmp_eq(a, b); /* if NaN return false */
+ break;
+ default:
+ abort();
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ if (b == &b_s)
+ bf_delete(b);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return res;
+}
+
+static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op,
+ JSValue op1, JSValue op2)
+{
+ bfdec_t *a, *b;
+ int res;
+
+ /* Note: binary floats are converted to bigdecimal with
+ toString(). It is not mathematically correct but is consistent
+ with the BigDecimal() constructor behavior */
+ op1 = JS_ToBigDecimalFree(ctx, op1, TRUE);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ return -1;
+ }
+ op2 = JS_ToBigDecimalFree(ctx, op2, TRUE);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ return -1;
+ }
+ a = JS_ToBigDecimal(ctx, op1);
+ b = JS_ToBigDecimal(ctx, op2);
+
+ switch(op) {
+ case OP_lt:
+ res = bfdec_cmp_lt(a, b); /* if NaN return false */
+ break;
+ case OP_lte:
+ res = bfdec_cmp_le(a, b); /* if NaN return false */
+ break;
+ case OP_gt:
+ res = bfdec_cmp_lt(b, a); /* if NaN return false */
+ break;
+ case OP_gte:
+ res = bfdec_cmp_le(b, a); /* if NaN return false */
+ break;
+ case OP_eq:
+ res = bfdec_cmp_eq(a, b); /* if NaN return false */
+ break;
+ default:
+ abort();
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return res;
+}
+
+static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2, ret;
+ int res;
+ uint32_t tag1, tag2;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ /* try to call an overloaded operator */
+ if ((tag1 == JS_TAG_OBJECT &&
+ (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED)) ||
+ (tag2 == JS_TAG_OBJECT &&
+ (tag1 != JS_TAG_NULL && tag1 != JS_TAG_UNDEFINED))) {
+ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op,
+ FALSE, HINT_NUMBER);
+ if (res != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (res < 0) {
+ goto exception;
+ } else {
+ sp[-2] = ret;
+ return 0;
+ }
+ }
+ }
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ if (tag1 == JS_TAG_STRING && tag2 == JS_TAG_STRING) {
+ JSString *p1, *p2;
+ p1 = JS_VALUE_GET_STRING(op1);
+ p2 = JS_VALUE_GET_STRING(op2);
+ res = js_string_compare(ctx, p1, p2);
+ switch(op) {
+ case OP_lt:
+ res = (res < 0);
+ break;
+ case OP_lte:
+ res = (res <= 0);
+ break;
+ case OP_gt:
+ res = (res > 0);
+ break;
+ default:
+ case OP_gte:
+ res = (res >= 0);
+ break;
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ } else if ((tag1 <= JS_TAG_NULL || tag1 == JS_TAG_FLOAT64) &&
+ (tag2 <= JS_TAG_NULL || tag2 == JS_TAG_FLOAT64)) {
+ /* fast path for float64/int */
+ goto float64_compare;
+ } else {
+ if (((tag1 == JS_TAG_BIG_INT && tag2 == JS_TAG_STRING) ||
+ (tag2 == JS_TAG_BIG_INT && tag1 == JS_TAG_STRING)) &&
+ !is_math_mode(ctx)) {
+ if (tag1 == JS_TAG_STRING) {
+ op1 = JS_StringToBigInt(ctx, op1);
+ if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
+ goto invalid_bigint_string;
+ }
+ if (tag2 == JS_TAG_STRING) {
+ op2 = JS_StringToBigInt(ctx, op2);
+ if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
+ invalid_bigint_string:
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ res = FALSE;
+ goto done;
+ }
+ }
+ } else {
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ }
+
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+
+ if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
+ res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2);
+ if (res < 0)
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
+ res = ctx->rt->bigfloat_ops.compare(ctx, op, op1, op2);
+ if (res < 0)
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) {
+ res = ctx->rt->bigint_ops.compare(ctx, op, op1, op2);
+ if (res < 0)
+ goto exception;
+ } else {
+ double d1, d2;
+
+ float64_compare:
+ /* can use floating point comparison */
+ if (tag1 == JS_TAG_FLOAT64) {
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ } else {
+ d1 = JS_VALUE_GET_INT(op1);
+ }
+ if (tag2 == JS_TAG_FLOAT64) {
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ } else {
+ d2 = JS_VALUE_GET_INT(op2);
+ }
+ switch(op) {
+ case OP_lt:
+ res = (d1 < d2); /* if NaN return false */
+ break;
+ case OP_lte:
+ res = (d1 <= d2); /* if NaN return false */
+ break;
+ case OP_gt:
+ res = (d1 > d2); /* if NaN return false */
+ break;
+ default:
+ case OP_gte:
+ res = (d1 >= d2); /* if NaN return false */
+ break;
+ }
+ }
+ }
+ done:
+ sp[-2] = JS_NewBool(ctx, res);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static BOOL tag_is_number(uint32_t tag)
+{
+ return (tag == JS_TAG_INT || tag == JS_TAG_BIG_INT ||
+ tag == JS_TAG_FLOAT64 || tag == JS_TAG_BIG_FLOAT ||
+ tag == JS_TAG_BIG_DECIMAL);
+}
+
+static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp,
+ BOOL is_neq)
+{
+ JSValue op1, op2, ret;
+ int res;
+ uint32_t tag1, tag2;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ redo:
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ if (tag_is_number(tag1) && tag_is_number(tag2)) {
+ if (tag1 == JS_TAG_INT && tag2 == JS_TAG_INT) {
+ res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
+ } else if ((tag1 == JS_TAG_FLOAT64 &&
+ (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64)) ||
+ (tag2 == JS_TAG_FLOAT64 &&
+ (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64))) {
+ double d1, d2;
+ if (tag1 == JS_TAG_FLOAT64) {
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ } else {
+ d1 = JS_VALUE_GET_INT(op1);
+ }
+ if (tag2 == JS_TAG_FLOAT64) {
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ } else {
+ d2 = JS_VALUE_GET_INT(op2);
+ }
+ res = (d1 == d2);
+ } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) {
+ res = ctx->rt->bigdecimal_ops.compare(ctx, OP_eq, op1, op2);
+ if (res < 0)
+ goto exception;
+ } else if (tag1 == JS_TAG_BIG_FLOAT || tag2 == JS_TAG_BIG_FLOAT) {
+ res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2);
+ if (res < 0)
+ goto exception;
+ } else {
+ res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2);
+ if (res < 0)
+ goto exception;
+ }
+ } else if (tag1 == tag2) {
+ if (tag1 == JS_TAG_OBJECT) {
+ /* try the fallback operator */
+ res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
+ is_neq ? OP_neq : OP_eq,
+ FALSE, HINT_NONE);
+ if (res != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (res < 0) {
+ goto exception;
+ } else {
+ sp[-2] = ret;
+ return 0;
+ }
+ }
+ }
+ res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
+ } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
+ (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
+ res = TRUE;
+ } else if ((tag1 == JS_TAG_STRING && tag_is_number(tag2)) ||
+ (tag2 == JS_TAG_STRING && tag_is_number(tag1))) {
+
+ if ((tag1 == JS_TAG_BIG_INT || tag2 == JS_TAG_BIG_INT) &&
+ !is_math_mode(ctx)) {
+ if (tag1 == JS_TAG_STRING) {
+ op1 = JS_StringToBigInt(ctx, op1);
+ if (JS_VALUE_GET_TAG(op1) != JS_TAG_BIG_INT)
+ goto invalid_bigint_string;
+ }
+ if (tag2 == JS_TAG_STRING) {
+ op2 = JS_StringToBigInt(ctx, op2);
+ if (JS_VALUE_GET_TAG(op2) != JS_TAG_BIG_INT) {
+ invalid_bigint_string:
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ res = FALSE;
+ goto done;
+ }
+ }
+ } else {
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ }
+ res = js_strict_eq(ctx, op1, op2);
+ } else if (tag1 == JS_TAG_BOOL) {
+ op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
+ goto redo;
+ } else if (tag2 == JS_TAG_BOOL) {
+ op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
+ goto redo;
+ } else if ((tag1 == JS_TAG_OBJECT &&
+ (tag_is_number(tag2) || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) ||
+ (tag2 == JS_TAG_OBJECT &&
+ (tag_is_number(tag1) || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL))) {
+
+ /* try the fallback operator */
+ res = js_call_binary_op_fallback(ctx, &ret, op1, op2,
+ is_neq ? OP_neq : OP_eq,
+ FALSE, HINT_NONE);
+ if (res != 0) {
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ if (res < 0) {
+ goto exception;
+ } else {
+ sp[-2] = ret;
+ return 0;
+ }
+ }
+
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ goto redo;
+ } else {
+ /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
+ if ((JS_IsHTMLDDA(ctx, op1) &&
+ (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
+ (JS_IsHTMLDDA(ctx, op2) &&
+ (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
+ res = TRUE;
+ } else {
+ res = FALSE;
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ }
+ done:
+ sp[-2] = JS_NewBool(ctx, res ^ is_neq);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ uint32_t v1, v2, r;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ op1 = JS_ToNumericFree(ctx, op1);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToNumericFree(ctx, op2);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ /* XXX: could forbid >>> in bignum mode */
+ if (!is_math_mode(ctx) &&
+ (JS_VALUE_GET_TAG(op1) == JS_TAG_BIG_INT ||
+ JS_VALUE_GET_TAG(op2) == JS_TAG_BIG_INT)) {
+ JS_ThrowTypeError(ctx, "bigint operands are forbidden for >>>");
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ /* cannot give an exception */
+ JS_ToUint32Free(ctx, &v1, op1);
+ JS_ToUint32Free(ctx, &v2, op2);
+ r = v1 >> (v2 & 0x1f);
+ sp[-2] = JS_NewUint32(ctx, r);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a,
+ int64_t exponent)
+{
+ bf_t r_s, *r = &r_s;
+ double d;
+ int ret;
+
+ /* always convert to Float64 */
+ bf_init(ctx->bf_ctx, r);
+ ret = bf_mul_pow_radix(r, a, 10, exponent,
+ 53, bf_set_exp_bits(11) | BF_RNDN |
+ BF_FLAG_SUBNORMAL);
+ bf_get_float64(r, &d, BF_RNDN);
+ bf_delete(r);
+ if (ret & BF_ST_MEM_ERROR)
+ return JS_ThrowOutOfMemory(ctx);
+ else
+ return __JS_NewFloat64(ctx, d);
+}
+
+static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp)
+{
+ bf_t a_s, *a, *r;
+ JSValue op1, op2, res;
+ int64_t e;
+ int ret;
+
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res))
+ return -1;
+ r = JS_GetBigFloat(res);
+ op1 = sp[-2];
+ op2 = sp[-1];
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ if (!a)
+ return -1;
+ if (JS_IsBigInt(ctx, op2)) {
+ ret = JS_ToBigInt64(ctx, &e, op2);
+ } else {
+ ret = JS_ToInt64(ctx, &e, op2);
+ }
+ if (ret) {
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, res);
+ return -1;
+ }
+
+ bf_mul_pow_radix(r, a, 10, e, ctx->fp_env.prec, ctx->fp_env.flags);
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ sp[-2] = res;
+ return 0;
+}
+
+#else /* !CONFIG_BIGNUM */
+
+static JSValue JS_ThrowUnsupportedBigint(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "bigint is not supported");
+}
+
+JSValue JS_NewBigInt64(JSContext *ctx, int64_t v)
+{
+ return JS_ThrowUnsupportedBigint(ctx);
+}
+
+JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v)
+{
+ return JS_ThrowUnsupportedBigint(ctx);
+}
+
+int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+{
+ JS_ThrowUnsupportedBigint(ctx);
+ *pres = 0;
+ return -1;
+}
+
+static no_inline warn_unused int js_unary_arith_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1;
+ double d;
+
+ op1 = sp[-1];
+ if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+ }
+ switch(op) {
+ case OP_inc:
+ d++;
+ break;
+ case OP_dec:
+ d--;
+ break;
+ case OP_plus:
+ break;
+ case OP_neg:
+ d = -d;
+ break;
+ default:
+ abort();
+ }
+ sp[-1] = JS_NewFloat64(ctx, d);
+ return 0;
+}
+
+/* specific case necessary for correct return value semantics */
+static warn_unused int js_post_inc_slow(JSContext *ctx,
+ JSValue *sp, OPCodeEnum op)
+{
+ JSValue op1;
+ double d, r;
+
+ op1 = sp[-1];
+ if (unlikely(JS_ToFloat64Free(ctx, &d, op1))) {
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+ }
+ r = d + 2 * (op - OP_post_dec) - 1;
+ sp[0] = JS_NewFloat64(ctx, r);
+ sp[-1] = JS_NewFloat64(ctx, d);
+ return 0;
+}
+
+static no_inline warn_unused int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2;
+ double d1, d2, r;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (unlikely(JS_ToFloat64Free(ctx, &d1, op1))) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (unlikely(JS_ToFloat64Free(ctx, &d2, op2))) {
+ goto exception;
+ }
+ switch(op) {
+ case OP_sub:
+ r = d1 - d2;
+ break;
+ case OP_mul:
+ r = d1 * d2;
+ break;
+ case OP_div:
+ r = d1 / d2;
+ break;
+ case OP_mod:
+ r = fmod(d1, d2);
+ break;
+ case OP_pow:
+ r = js_pow(d1, d2);
+ break;
+ default:
+ abort();
+ }
+ sp[-2] = JS_NewFloat64(ctx, r);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline warn_unused int js_add_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ uint32_t tag1, tag2;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ tag1 = JS_VALUE_GET_TAG(op1);
+ tag2 = JS_VALUE_GET_TAG(op2);
+ if ((tag1 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag1)) &&
+ (tag2 == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag2))) {
+ goto add_numbers;
+ } else {
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ tag1 = JS_VALUE_GET_TAG(op1);
+ tag2 = JS_VALUE_GET_TAG(op2);
+ if (tag1 == JS_TAG_STRING || tag2 == JS_TAG_STRING) {
+ sp[-2] = JS_ConcatString(ctx, op1, op2);
+ if (JS_IsException(sp[-2]))
+ goto exception;
+ } else {
+ double d1, d2;
+ add_numbers:
+ if (JS_ToFloat64Free(ctx, &d1, op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (JS_ToFloat64Free(ctx, &d2, op2))
+ goto exception;
+ sp[-2] = JS_NewFloat64(ctx, d1 + d2);
+ }
+ }
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline warn_unused int js_binary_logic_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2;
+ uint32_t v1, v2, r;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v1, op1))) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (unlikely(JS_ToInt32Free(ctx, (int32_t *)&v2, op2)))
+ goto exception;
+ switch(op) {
+ case OP_shl:
+ r = v1 << (v2 & 0x1f);
+ break;
+ case OP_sar:
+ r = (int)v1 >> (v2 & 0x1f);
+ break;
+ case OP_and:
+ r = v1 & v2;
+ break;
+ case OP_or:
+ r = v1 | v2;
+ break;
+ case OP_xor:
+ r = v1 ^ v2;
+ break;
+ default:
+ abort();
+ }
+ sp[-2] = JS_NewInt32(ctx, r);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline int js_not_slow(JSContext *ctx, JSValue *sp)
+{
+ int32_t v1;
+
+ if (unlikely(JS_ToInt32Free(ctx, &v1, sp[-1]))) {
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+ }
+ sp[-1] = JS_NewInt32(ctx, ~v1);
+ return 0;
+}
+
+static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+{
+ JSValue op1, op2;
+ int res;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NUMBER);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_STRING &&
+ JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) {
+ JSString *p1, *p2;
+ p1 = JS_VALUE_GET_STRING(op1);
+ p2 = JS_VALUE_GET_STRING(op2);
+ res = js_string_compare(ctx, p1, p2);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ switch(op) {
+ case OP_lt:
+ res = (res < 0);
+ break;
+ case OP_lte:
+ res = (res <= 0);
+ break;
+ case OP_gt:
+ res = (res > 0);
+ break;
+ default:
+ case OP_gte:
+ res = (res >= 0);
+ break;
+ }
+ } else {
+ double d1, d2;
+ if (JS_ToFloat64Free(ctx, &d1, op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (JS_ToFloat64Free(ctx, &d2, op2))
+ goto exception;
+ switch(op) {
+ case OP_lt:
+ res = (d1 < d2); /* if NaN return false */
+ break;
+ case OP_lte:
+ res = (d1 <= d2); /* if NaN return false */
+ break;
+ case OP_gt:
+ res = (d1 > d2); /* if NaN return false */
+ break;
+ default:
+ case OP_gte:
+ res = (d1 >= d2); /* if NaN return false */
+ break;
+ }
+ }
+ sp[-2] = JS_NewBool(ctx, res);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline warn_unused int js_eq_slow(JSContext *ctx, JSValue *sp,
+ BOOL is_neq)
+{
+ JSValue op1, op2;
+ int tag1, tag2;
+ BOOL res;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ redo:
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ if (tag1 == tag2 ||
+ (tag1 == JS_TAG_INT && tag2 == JS_TAG_FLOAT64) ||
+ (tag2 == JS_TAG_INT && tag1 == JS_TAG_FLOAT64)) {
+ res = js_strict_eq(ctx, op1, op2);
+ } else if ((tag1 == JS_TAG_NULL && tag2 == JS_TAG_UNDEFINED) ||
+ (tag2 == JS_TAG_NULL && tag1 == JS_TAG_UNDEFINED)) {
+ res = TRUE;
+ } else if ((tag1 == JS_TAG_STRING && (tag2 == JS_TAG_INT ||
+ tag2 == JS_TAG_FLOAT64)) ||
+ (tag2 == JS_TAG_STRING && (tag1 == JS_TAG_INT ||
+ tag1 == JS_TAG_FLOAT64))) {
+ double d1;
+ double d2;
+ if (JS_ToFloat64Free(ctx, &d1, op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (JS_ToFloat64Free(ctx, &d2, op2))
+ goto exception;
+ res = (d1 == d2);
+ } else if (tag1 == JS_TAG_BOOL) {
+ op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1));
+ goto redo;
+ } else if (tag2 == JS_TAG_BOOL) {
+ op2 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op2));
+ goto redo;
+ } else if (tag1 == JS_TAG_OBJECT &&
+ (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64 || tag2 == JS_TAG_STRING || tag2 == JS_TAG_SYMBOL)) {
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1)) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ goto redo;
+ } else if (tag2 == JS_TAG_OBJECT &&
+ (tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64 || tag1 == JS_TAG_STRING || tag1 == JS_TAG_SYMBOL)) {
+ op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ goto exception;
+ }
+ goto redo;
+ } else {
+ /* IsHTMLDDA object is equivalent to undefined for '==' and '!=' */
+ if ((JS_IsHTMLDDA(ctx, op1) &&
+ (tag2 == JS_TAG_NULL || tag2 == JS_TAG_UNDEFINED)) ||
+ (JS_IsHTMLDDA(ctx, op2) &&
+ (tag1 == JS_TAG_NULL || tag1 == JS_TAG_UNDEFINED))) {
+ res = TRUE;
+ } else {
+ res = FALSE;
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ }
+ sp[-2] = JS_NewBool(ctx, res ^ is_neq);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ uint32_t v1, v2, r;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (unlikely(JS_ToUint32Free(ctx, &v1, op1))) {
+ JS_FreeValue(ctx, op2);
+ goto exception;
+ }
+ if (unlikely(JS_ToUint32Free(ctx, &v2, op2)))
+ goto exception;
+ r = v1 >> (v2 & 0x1f);
+ sp[-2] = JS_NewUint32(ctx, r);
+ return 0;
+ exception:
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+#endif /* !CONFIG_BIGNUM */
+
+/* XXX: Should take JSValueConst arguments */
+static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
+ JSStrictEqModeEnum eq_mode)
+{
+ BOOL res;
+ int tag1, tag2;
+ double d1, d2;
+
+ tag1 = JS_VALUE_GET_NORM_TAG(op1);
+ tag2 = JS_VALUE_GET_NORM_TAG(op2);
+ switch(tag1) {
+ case JS_TAG_BOOL:
+ if (tag1 != tag2) {
+ res = FALSE;
+ } else {
+ res = JS_VALUE_GET_INT(op1) == JS_VALUE_GET_INT(op2);
+ goto done_no_free;
+ }
+ break;
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ res = (tag1 == tag2);
+ break;
+ case JS_TAG_STRING:
+ {
+ JSString *p1, *p2;
+ if (tag1 != tag2) {
+ res = FALSE;
+ } else {
+ p1 = JS_VALUE_GET_STRING(op1);
+ p2 = JS_VALUE_GET_STRING(op2);
+ res = (js_string_compare(ctx, p1, p2) == 0);
+ }
+ }
+ break;
+ case JS_TAG_SYMBOL:
+ {
+ JSAtomStruct *p1, *p2;
+ if (tag1 != tag2) {
+ res = FALSE;
+ } else {
+ p1 = JS_VALUE_GET_PTR(op1);
+ p2 = JS_VALUE_GET_PTR(op2);
+ res = (p1 == p2);
+ }
+ }
+ break;
+ case JS_TAG_OBJECT:
+ if (tag1 != tag2)
+ res = FALSE;
+ else
+ res = JS_VALUE_GET_OBJ(op1) == JS_VALUE_GET_OBJ(op2);
+ break;
+ case JS_TAG_INT:
+ d1 = JS_VALUE_GET_INT(op1);
+ if (tag2 == JS_TAG_INT) {
+ d2 = JS_VALUE_GET_INT(op2);
+ goto number_test;
+ } else if (tag2 == JS_TAG_FLOAT64) {
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ goto number_test;
+ } else {
+ res = FALSE;
+ }
+ break;
+ case JS_TAG_FLOAT64:
+ d1 = JS_VALUE_GET_FLOAT64(op1);
+ if (tag2 == JS_TAG_FLOAT64) {
+ d2 = JS_VALUE_GET_FLOAT64(op2);
+ } else if (tag2 == JS_TAG_INT) {
+ d2 = JS_VALUE_GET_INT(op2);
+ } else {
+ res = FALSE;
+ break;
+ }
+ number_test:
+ if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
+ JSFloat64Union u1, u2;
+ /* NaN is not always normalized, so this test is necessary */
+ if (isnan(d1) || isnan(d2)) {
+ res = isnan(d1) == isnan(d2);
+ } else if (eq_mode == JS_EQ_SAME_VALUE_ZERO) {
+ res = (d1 == d2); /* +0 == -0 */
+ } else {
+ u1.d = d1;
+ u2.d = d2;
+ res = (u1.u64 == u2.u64); /* +0 != -0 */
+ }
+ } else {
+ res = (d1 == d2); /* if NaN return false and +0 == -0 */
+ }
+ goto done_no_free;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ {
+ bf_t a_s, *a, b_s, *b;
+ if (tag1 != tag2) {
+ res = FALSE;
+ break;
+ }
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ b = JS_ToBigFloat(ctx, &b_s, op2);
+ res = bf_cmp_eq(a, b);
+ if (a == &a_s)
+ bf_delete(a);
+ if (b == &b_s)
+ bf_delete(b);
+ }
+ break;
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p1, *p2;
+ const bf_t *a, *b;
+ if (tag1 != tag2) {
+ res = FALSE;
+ break;
+ }
+ p1 = JS_VALUE_GET_PTR(op1);
+ p2 = JS_VALUE_GET_PTR(op2);
+ a = &p1->num;
+ b = &p2->num;
+ if (unlikely(eq_mode >= JS_EQ_SAME_VALUE)) {
+ if (eq_mode == JS_EQ_SAME_VALUE_ZERO &&
+ a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) {
+ res = TRUE;
+ } else {
+ res = (bf_cmp_full(a, b) == 0);
+ }
+ } else {
+ res = bf_cmp_eq(a, b);
+ }
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ {
+ JSBigDecimal *p1, *p2;
+ const bfdec_t *a, *b;
+ if (tag1 != tag2) {
+ res = FALSE;
+ break;
+ }
+ p1 = JS_VALUE_GET_PTR(op1);
+ p2 = JS_VALUE_GET_PTR(op2);
+ a = &p1->num;
+ b = &p2->num;
+ res = bfdec_cmp_eq(a, b);
+ }
+ break;
+#endif
+ default:
+ res = FALSE;
+ break;
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ done_no_free:
+ return res;
+}
+
+static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2)
+{
+ return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT);
+}
+
+static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2)
+{
+ return js_strict_eq2(ctx,
+ JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
+ JS_EQ_SAME_VALUE);
+}
+
+static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2)
+{
+ return js_strict_eq2(ctx,
+ JS_DupValue(ctx, op1), JS_DupValue(ctx, op2),
+ JS_EQ_SAME_VALUE_ZERO);
+}
+
+static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,
+ BOOL is_neq)
+{
+ BOOL res;
+ res = js_strict_eq(ctx, sp[-2], sp[-1]);
+ sp[-2] = JS_NewBool(ctx, res ^ is_neq);
+ return 0;
+}
+
+static warn_unused int js_operator_in(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ JSAtom atom;
+ int ret;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+
+ if (JS_VALUE_GET_TAG(op2) != JS_TAG_OBJECT) {
+ JS_ThrowTypeError(ctx, "invalid 'in' operand");
+ return -1;
+ }
+ atom = JS_ValueToAtom(ctx, op1);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return -1;
+ ret = JS_HasProperty(ctx, op2, atom);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ return -1;
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ sp[-2] = JS_NewBool(ctx, ret);
+ return 0;
+}
+
+static warn_unused int js_has_unscopable(JSContext *ctx, JSValueConst obj,
+ JSAtom atom)
+{
+ JSValue arr, val;
+ int ret;
+
+ arr = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_unscopables);
+ if (JS_IsException(arr))
+ return -1;
+ ret = 0;
+ if (JS_IsObject(arr)) {
+ val = JS_GetProperty(ctx, arr, atom);
+ ret = JS_ToBoolFree(ctx, val);
+ }
+ JS_FreeValue(ctx, arr);
+ return ret;
+}
+
+static warn_unused int js_operator_instanceof(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ BOOL ret;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ ret = JS_IsInstanceOf(ctx, op1, op2);
+ if (ret < 0)
+ return ret;
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ sp[-2] = JS_NewBool(ctx, ret);
+ return 0;
+}
+
+static warn_unused int js_operator_typeof(JSContext *ctx, JSValueConst op1)
+{
+ JSAtom atom;
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_NORM_TAG(op1);
+ switch(tag) {
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ atom = JS_ATOM_bigint;
+ break;
+ case JS_TAG_BIG_FLOAT:
+ atom = JS_ATOM_bigfloat;
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ atom = JS_ATOM_bigdecimal;
+ break;
+#endif
+ case JS_TAG_INT:
+ case JS_TAG_FLOAT64:
+ atom = JS_ATOM_number;
+ break;
+ case JS_TAG_UNDEFINED:
+ atom = JS_ATOM_undefined;
+ break;
+ case JS_TAG_BOOL:
+ atom = JS_ATOM_boolean;
+ break;
+ case JS_TAG_STRING:
+ atom = JS_ATOM_string;
+ break;
+ case JS_TAG_OBJECT:
+ {
+ JSObject *p;
+ p = JS_VALUE_GET_OBJ(op1);
+ if (unlikely(p->is_HTMLDDA))
+ atom = JS_ATOM_undefined;
+ else if (JS_IsFunction(ctx, op1))
+ atom = JS_ATOM_function;
+ else
+ goto obj_type;
+ }
+ break;
+ case JS_TAG_NULL:
+ obj_type:
+ atom = JS_ATOM_object;
+ break;
+ case JS_TAG_SYMBOL:
+ atom = JS_ATOM_symbol;
+ break;
+ default:
+ atom = JS_ATOM_unknown;
+ break;
+ }
+ return atom;
+}
+
+static warn_unused int js_operator_delete(JSContext *ctx, JSValue *sp)
+{
+ JSValue op1, op2;
+ JSAtom atom;
+ int ret;
+
+ op1 = sp[-2];
+ op2 = sp[-1];
+ atom = JS_ValueToAtom(ctx, op2);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return -1;
+ ret = JS_DeleteProperty(ctx, op1, atom, JS_PROP_THROW_STRICT);
+ JS_FreeAtom(ctx, atom);
+ if (unlikely(ret < 0))
+ return -1;
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ sp[-2] = JS_NewBool(ctx, ret);
+ return 0;
+}
+
+static JSValue js_throw_type_error(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ThrowTypeError(ctx, "invalid property access");
+}
+
+/* XXX: not 100% compatible, but mozilla seems to use a similar
+ implementation to ensure that caller in non strict mode does not
+ throw (ES5 compatibility) */
+static JSValue js_function_proto_caller(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
+ if (!b || (b->js_mode & JS_MODE_STRICT) || !b->has_prototype) {
+ return js_throw_type_error(ctx, this_val, 0, NULL);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_function_proto_fileName(JSContext *ctx,
+ JSValueConst this_val)
+{
+ JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
+ if (b && b->has_debug) {
+ return JS_AtomToString(ctx, b->debug.filename);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_function_proto_lineNumber(JSContext *ctx,
+ JSValueConst this_val)
+{
+ JSFunctionBytecode *b = JS_GetFunctionBytecode(this_val);
+ if (b && b->has_debug) {
+ return JS_NewInt32(ctx, b->debug.line_num);
+ }
+ return JS_UNDEFINED;
+}
+
+static int js_arguments_define_own_property(JSContext *ctx,
+ JSValueConst this_obj,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter, int flags)
+{
+ JSObject *p;
+ uint32_t idx;
+ p = JS_VALUE_GET_OBJ(this_obj);
+ /* convert to normal array when redefining an existing numeric field */
+ if (p->fast_array && JS_AtomIsArrayIndex(ctx, &idx, prop) &&
+ idx < p->u.array.count) {
+ if (convert_fast_array_to_array(ctx, p))
+ return -1;
+ }
+ /* run the default define own property */
+ return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
+ flags | JS_PROP_NO_EXOTIC);
+}
+
+static const JSClassExoticMethods js_arguments_exotic_methods = {
+ .define_own_property = js_arguments_define_own_property,
+};
+
+static JSValue js_build_arguments(JSContext *ctx, int argc, JSValueConst *argv)
+{
+ JSValue val, *tab;
+ JSProperty *pr;
+ JSObject *p;
+ int i;
+
+ val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_ARGUMENTS);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_OBJ(val);
+
+ /* add the length field (cannot fail) */
+ pr = add_property(ctx, p, JS_ATOM_length,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ pr->u.value = JS_NewInt32(ctx, argc);
+
+ /* initialize the fast array part */
+ tab = NULL;
+ if (argc > 0) {
+ tab = js_malloc(ctx, sizeof(tab[0]) * argc);
+ if (!tab) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ for(i = 0; i < argc; i++) {
+ tab[i] = JS_DupValue(ctx, argv[i]);
+ }
+ }
+ p->u.array.u.values = tab;
+ p->u.array.count = argc;
+
+ JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
+ JS_DupValue(ctx, ctx->array_proto_values),
+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
+ /* add callee property to throw a TypeError in strict mode */
+ JS_DefineProperty(ctx, val, JS_ATOM_callee, JS_UNDEFINED,
+ ctx->throw_type_error, ctx->throw_type_error,
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET);
+ return val;
+}
+
+#define GLOBAL_VAR_OFFSET 0x40000000
+#define ARGUMENT_VAR_OFFSET 0x20000000
+
+/* legacy arguments object: add references to the function arguments */
+static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,
+ JSValueConst *argv,
+ JSStackFrame *sf, int arg_count)
+{
+ JSValue val;
+ JSProperty *pr;
+ JSObject *p;
+ int i;
+
+ val = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_MAPPED_ARGUMENTS);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_OBJ(val);
+
+ /* add the length field (cannot fail) */
+ pr = add_property(ctx, p, JS_ATOM_length,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ pr->u.value = JS_NewInt32(ctx, argc);
+
+ for(i = 0; i < arg_count; i++) {
+ JSVarRef *var_ref;
+ var_ref = get_var_ref(ctx, sf, i, TRUE);
+ if (!var_ref)
+ goto fail;
+ pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
+ if (!pr) {
+ free_var_ref(ctx->rt, var_ref);
+ goto fail;
+ }
+ pr->u.var_ref = var_ref;
+ }
+
+ /* the arguments not mapped to the arguments of the function can
+ be normal properties */
+ for(i = arg_count; i < argc; i++) {
+ if (JS_DefinePropertyValueUint32(ctx, val, i,
+ JS_DupValue(ctx, argv[i]),
+ JS_PROP_C_W_E) < 0)
+ goto fail;
+ }
+
+ JS_DefinePropertyValue(ctx, val, JS_ATOM_Symbol_iterator,
+ JS_DupValue(ctx, ctx->array_proto_values),
+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
+ /* callee returns this function in non strict mode */
+ JS_DefinePropertyValue(ctx, val, JS_ATOM_callee,
+ JS_DupValue(ctx, ctx->rt->current_stack_frame->cur_func),
+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
+ return val;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int i, ret;
+
+ val = JS_NewArray(ctx);
+ if (JS_IsException(val))
+ return val;
+ for (i = first; i < argc; i++) {
+ ret = JS_DefinePropertyValueUint32(ctx, val, i - first,
+ JS_DupValue(ctx, argv[i]),
+ JS_PROP_C_W_E);
+ if (ret < 0) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ }
+ return val;
+}
+
+static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
+{
+ JSObject *p;
+ JSPropertyEnum *tab_atom;
+ int i;
+ JSValue enum_obj, obj1;
+ JSForInIterator *it;
+ uint32_t tag, tab_atom_count;
+
+ tag = JS_VALUE_GET_TAG(obj);
+ if (tag != JS_TAG_OBJECT && tag != JS_TAG_NULL && tag != JS_TAG_UNDEFINED) {
+ obj = JS_ToObjectFree(ctx, obj);
+ }
+
+ it = js_malloc(ctx, sizeof(*it));
+ if (!it) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ enum_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_FOR_IN_ITERATOR);
+ if (JS_IsException(enum_obj)) {
+ js_free(ctx, it);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ it->is_array = FALSE;
+ it->obj = obj;
+ it->idx = 0;
+ p = JS_VALUE_GET_OBJ(enum_obj);
+ p->u.for_in_iterator = it;
+
+ if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED)
+ return enum_obj;
+
+ /* fast path: assume no enumerable properties in the prototype chain */
+ obj1 = JS_DupValue(ctx, obj);
+ for(;;) {
+ obj1 = JS_GetPrototypeFree(ctx, obj1);
+ if (JS_IsNull(obj1))
+ break;
+ if (JS_IsException(obj1))
+ goto fail;
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
+ JS_VALUE_GET_OBJ(obj1),
+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) {
+ JS_FreeValue(ctx, obj1);
+ goto fail;
+ }
+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);
+ if (tab_atom_count != 0) {
+ JS_FreeValue(ctx, obj1);
+ goto slow_path;
+ }
+ /* must check for timeout to avoid infinite loop */
+ if (js_poll_interrupts(ctx)) {
+ JS_FreeValue(ctx, obj1);
+ goto fail;
+ }
+ }
+
+ p = JS_VALUE_GET_OBJ(obj);
+
+ if (p->fast_array) {
+ JSShape *sh;
+ JSShapeProperty *prs;
+ /* check that there are no enumerable normal fields */
+ sh = p->shape;
+ for(i = 0, prs = get_shape_prop(sh); i < sh->prop_count; i++, prs++) {
+ if (prs->flags & JS_PROP_ENUMERABLE)
+ goto normal_case;
+ }
+ /* for fast arrays, we only store the number of elements */
+ it->is_array = TRUE;
+ it->array_length = p->u.array.count;
+ } else {
+ normal_case:
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
+ JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY))
+ goto fail;
+ for(i = 0; i < tab_atom_count; i++) {
+ JS_SetPropertyInternal(ctx, enum_obj, tab_atom[i].atom, JS_NULL, 0);
+ }
+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);
+ }
+ return enum_obj;
+
+ slow_path:
+ /* non enumerable properties hide the enumerables ones in the
+ prototype chain */
+ obj1 = JS_DupValue(ctx, obj);
+ for(;;) {
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count,
+ JS_VALUE_GET_OBJ(obj1),
+ JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) {
+ JS_FreeValue(ctx, obj1);
+ goto fail;
+ }
+ for(i = 0; i < tab_atom_count; i++) {
+ JS_DefinePropertyValue(ctx, enum_obj, tab_atom[i].atom, JS_NULL,
+ (tab_atom[i].is_enumerable ?
+ JS_PROP_ENUMERABLE : 0));
+ }
+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);
+ obj1 = JS_GetPrototypeFree(ctx, obj1);
+ if (JS_IsNull(obj1))
+ break;
+ if (JS_IsException(obj1))
+ goto fail;
+ /* must check for timeout to avoid infinite loop */
+ if (js_poll_interrupts(ctx)) {
+ JS_FreeValue(ctx, obj1);
+ goto fail;
+ }
+ }
+ return enum_obj;
+
+ fail:
+ JS_FreeValue(ctx, enum_obj);
+ return JS_EXCEPTION;
+}
+
+/* obj -> enum_obj */
+static warn_unused int js_for_in_start(JSContext *ctx, JSValue *sp)
+{
+ sp[-1] = build_for_in_iterator(ctx, sp[-1]);
+ if (JS_IsException(sp[-1]))
+ return -1;
+ return 0;
+}
+
+/* enum_obj -> enum_obj value done */
+static warn_unused int js_for_in_next(JSContext *ctx, JSValue *sp)
+{
+ JSValueConst enum_obj;
+ JSObject *p;
+ JSAtom prop;
+ JSForInIterator *it;
+ int ret;
+
+ enum_obj = sp[-1];
+ /* fail safe */
+ if (JS_VALUE_GET_TAG(enum_obj) != JS_TAG_OBJECT)
+ goto done;
+ p = JS_VALUE_GET_OBJ(enum_obj);
+ if (p->class_id != JS_CLASS_FOR_IN_ITERATOR)
+ goto done;
+ it = p->u.for_in_iterator;
+
+ for(;;) {
+ if (it->is_array) {
+ if (it->idx >= it->array_length)
+ goto done;
+ prop = JS_AtomFromUInt32(it->idx);
+ it->idx++;
+ } else {
+ JSShape *sh = p->shape;
+ JSShapeProperty *prs;
+ if (it->idx >= sh->prop_count)
+ goto done;
+ prs = get_shape_prop(sh) + it->idx;
+ prop = prs->atom;
+ it->idx++;
+ if (prop == JS_ATOM_NULL || !(prs->flags & JS_PROP_ENUMERABLE))
+ continue;
+ }
+ /* check if the property was deleted */
+ ret = JS_HasProperty(ctx, it->obj, prop);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ break;
+ }
+ /* return the property */
+ sp[0] = JS_AtomToValue(ctx, prop);
+ sp[1] = JS_FALSE;
+ return 0;
+ done:
+ /* return the end */
+ sp[0] = JS_UNDEFINED;
+ sp[1] = JS_TRUE;
+ return 0;
+}
+
+static JSValue JS_GetIterator2(JSContext *ctx, JSValueConst obj,
+ JSValueConst method)
+{
+ JSValue enum_obj;
+
+ enum_obj = JS_Call(ctx, method, obj, 0, NULL);
+ if (JS_IsException(enum_obj))
+ return enum_obj;
+ if (!JS_IsObject(enum_obj)) {
+ JS_FreeValue(ctx, enum_obj);
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ }
+ return enum_obj;
+}
+
+static JSValue JS_GetIterator(JSContext *ctx, JSValueConst obj, BOOL is_async)
+{
+ JSValue method, ret, sync_iter;
+
+ if (is_async) {
+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_asyncIterator);
+ if (JS_IsException(method))
+ return method;
+ if (JS_IsUndefined(method) || JS_IsNull(method)) {
+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
+ if (JS_IsException(method))
+ return method;
+ sync_iter = JS_GetIterator2(ctx, obj, method);
+ JS_FreeValue(ctx, method);
+ if (JS_IsException(sync_iter))
+ return sync_iter;
+ ret = JS_CreateAsyncFromSyncIterator(ctx, sync_iter);
+ JS_FreeValue(ctx, sync_iter);
+ return ret;
+ }
+ } else {
+ method = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
+ if (JS_IsException(method))
+ return method;
+ }
+ if (!JS_IsFunction(ctx, method)) {
+ JS_FreeValue(ctx, method);
+ return JS_ThrowTypeError(ctx, "value is not iterable");
+ }
+ ret = JS_GetIterator2(ctx, obj, method);
+ JS_FreeValue(ctx, method);
+ return ret;
+}
+
+/* return *pdone = 2 if the iterator object is not parsed */
+static JSValue JS_IteratorNext2(JSContext *ctx, JSValueConst enum_obj,
+ JSValueConst method,
+ int argc, JSValueConst *argv, int *pdone)
+{
+ JSValue obj;
+
+ /* fast path for the built-in iterators (avoid creating the
+ intermediate result object) */
+ if (JS_IsObject(method)) {
+ JSObject *p = JS_VALUE_GET_OBJ(method);
+ if (p->class_id == JS_CLASS_C_FUNCTION &&
+ p->u.cfunc.cproto == JS_CFUNC_iterator_next) {
+ JSCFunctionType func;
+ JSValueConst args[1];
+
+ /* in case the function expects one argument */
+ if (argc == 0) {
+ args[0] = JS_UNDEFINED;
+ argv = args;
+ }
+ func = p->u.cfunc.c_function;
+ return func.iterator_next(ctx, enum_obj, argc, argv,
+ pdone, p->u.cfunc.magic);
+ }
+ }
+ obj = JS_Call(ctx, method, enum_obj, argc, argv);
+ if (JS_IsException(obj))
+ goto fail;
+ if (!JS_IsObject(obj)) {
+ JS_FreeValue(ctx, obj);
+ JS_ThrowTypeError(ctx, "iterator must return an object");
+ goto fail;
+ }
+ *pdone = 2;
+ return obj;
+ fail:
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_IteratorNext(JSContext *ctx, JSValueConst enum_obj,
+ JSValueConst method,
+ int argc, JSValueConst *argv, BOOL *pdone)
+{
+ JSValue obj, value, done_val;
+ int done;
+
+ obj = JS_IteratorNext2(ctx, enum_obj, method, argc, argv, &done);
+ if (JS_IsException(obj))
+ goto fail;
+ if (done != 2) {
+ *pdone = done;
+ return obj;
+ } else {
+ done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
+ if (JS_IsException(done_val))
+ goto fail;
+ *pdone = JS_ToBoolFree(ctx, done_val);
+ value = JS_UNDEFINED;
+ if (!*pdone) {
+ value = JS_GetProperty(ctx, obj, JS_ATOM_value);
+ }
+ JS_FreeValue(ctx, obj);
+ return value;
+ }
+ fail:
+ JS_FreeValue(ctx, obj);
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+}
+
+/* return < 0 in case of exception */
+static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,
+ BOOL is_exception_pending)
+{
+ JSValue method, ret, ex_obj;
+ int res;
+
+ if (is_exception_pending) {
+ ex_obj = ctx->rt->current_exception;
+ ctx->rt->current_exception = JS_NULL;
+ res = -1;
+ } else {
+ ex_obj = JS_UNDEFINED;
+ res = 0;
+ }
+ method = JS_GetProperty(ctx, enum_obj, JS_ATOM_return);
+ if (JS_IsException(method)) {
+ res = -1;
+ goto done;
+ }
+ if (JS_IsUndefined(method) || JS_IsNull(method)) {
+ goto done;
+ }
+ ret = JS_CallFree(ctx, method, enum_obj, 0, NULL);
+ if (!is_exception_pending) {
+ if (JS_IsException(ret)) {
+ res = -1;
+ } else if (!JS_IsObject(ret)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ res = -1;
+ }
+ }
+ JS_FreeValue(ctx, ret);
+ done:
+ if (is_exception_pending) {
+ JS_Throw(ctx, ex_obj);
+ }
+ return res;
+}
+
+/* obj -> enum_rec (3 slots) */
+static warn_unused int js_for_of_start(JSContext *ctx, JSValue *sp,
+ BOOL is_async)
+{
+ JSValue op1, obj, method;
+ op1 = sp[-1];
+ obj = JS_GetIterator(ctx, op1, is_async);
+ if (JS_IsException(obj))
+ return -1;
+ JS_FreeValue(ctx, op1);
+ sp[-1] = obj;
+ method = JS_GetProperty(ctx, obj, JS_ATOM_next);
+ if (JS_IsException(method))
+ return -1;
+ sp[0] = method;
+ return 0;
+}
+
+/* enum_rec [objs] -> enum_rec [objs] value done. There are 'offset'
+ objs. If 'done' is true or in case of exception, 'enum_rec' is set
+ to undefined. If 'done' is true, 'value' is always set to
+ undefined. */
+static warn_unused int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
+{
+ JSValue value = JS_UNDEFINED;
+ int done = 1;
+
+ if (likely(!JS_IsUndefined(sp[offset]))) {
+ value = JS_IteratorNext(ctx, sp[offset], sp[offset + 1], 0, NULL, &done);
+ if (JS_IsException(value))
+ done = -1;
+ if (done) {
+ /* value is JS_UNDEFINED or JS_EXCEPTION */
+ /* replace the iteration object with undefined */
+ JS_FreeValue(ctx, sp[offset]);
+ sp[offset] = JS_UNDEFINED;
+ if (done < 0) {
+ return -1;
+ } else {
+ JS_FreeValue(ctx, value);
+ value = JS_UNDEFINED;
+ }
+ }
+ }
+ sp[0] = value;
+ sp[1] = JS_NewBool(ctx, done);
+ return 0;
+}
+
+static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,
+ BOOL *pdone)
+{
+ JSValue done_val, value;
+ BOOL done;
+ done_val = JS_GetProperty(ctx, obj, JS_ATOM_done);
+ if (JS_IsException(done_val))
+ goto fail;
+ done = JS_ToBoolFree(ctx, done_val);
+ value = JS_GetProperty(ctx, obj, JS_ATOM_value);
+ if (JS_IsException(value))
+ goto fail;
+ *pdone = done;
+ return value;
+ fail:
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+}
+
+static warn_unused int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
+{
+ JSValue obj, value;
+ BOOL done;
+ obj = sp[-1];
+ if (!JS_IsObject(obj)) {
+ JS_ThrowTypeError(ctx, "iterator must return an object");
+ return -1;
+ }
+ value = JS_IteratorGetCompleteValue(ctx, obj, &done);
+ if (JS_IsException(value))
+ return -1;
+ JS_FreeValue(ctx, obj);
+ sp[-1] = value;
+ sp[0] = JS_NewBool(ctx, done);
+ return 0;
+}
+
+static JSValue js_create_iterator_result(JSContext *ctx,
+ JSValue val,
+ BOOL done)
+{
+ JSValue obj;
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj)) {
+ JS_FreeValue(ctx, val);
+ return obj;
+ }
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_value,
+ val, JS_PROP_C_W_E) < 0) {
+ goto fail;
+ }
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_done,
+ JS_NewBool(ctx, done), JS_PROP_C_W_E) < 0) {
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ return obj;
+}
+
+static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic);
+
+static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic);
+
+static BOOL js_is_fast_array(JSContext *ctx, JSValueConst obj)
+{
+ /* Try and handle fast arrays explicitly */
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* Access an Array's internal JSValue array if available */
+static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
+ JSValue **arrpp, uint32_t *countp)
+{
+ /* Try and handle fast arrays explicitly */
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array) {
+ *countp = p->u.array.count;
+ *arrpp = p->u.array.u.values;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static warn_unused int js_append_enumerate(JSContext *ctx, JSValue *sp)
+{
+ JSValue iterator, enumobj, method, value;
+ int is_array_iterator;
+ JSValue *arrp;
+ uint32_t i, count32, pos;
+
+ if (JS_VALUE_GET_TAG(sp[-2]) != JS_TAG_INT) {
+ JS_ThrowInternalError(ctx, "invalid index for append");
+ return -1;
+ }
+
+ pos = JS_VALUE_GET_INT(sp[-2]);
+
+ /* XXX: further optimisations:
+ - use ctx->array_proto_values?
+ - check if array_iterator_prototype next method is built-in and
+ avoid constructing actual iterator object?
+ - build this into js_for_of_start and use in all `for (x of o)` loops
+ */
+ iterator = JS_GetProperty(ctx, sp[-1], JS_ATOM_Symbol_iterator);
+ if (JS_IsException(iterator))
+ return -1;
+ is_array_iterator = JS_IsCFunction(ctx, iterator,
+ (JSCFunction *)js_create_array_iterator,
+ JS_ITERATOR_KIND_VALUE);
+ JS_FreeValue(ctx, iterator);
+
+ enumobj = JS_GetIterator(ctx, sp[-1], FALSE);
+ if (JS_IsException(enumobj))
+ return -1;
+ method = JS_GetProperty(ctx, enumobj, JS_ATOM_next);
+ if (JS_IsException(method)) {
+ JS_FreeValue(ctx, enumobj);
+ return -1;
+ }
+ if (is_array_iterator
+ && JS_IsCFunction(ctx, method, (JSCFunction *)js_array_iterator_next, 0)
+ && js_get_fast_array(ctx, sp[-1], &arrp, &count32)) {
+ uint32_t len;
+ if (js_get_length32(ctx, &len, sp[-1]))
+ goto exception;
+ /* if len > count32, the elements >= count32 might be read in
+ the prototypes and might have side effects */
+ if (len != count32)
+ goto general_case;
+ /* Handle fast arrays explicitly */
+ for (i = 0; i < count32; i++) {
+ if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++,
+ JS_DupValue(ctx, arrp[i]), JS_PROP_C_W_E) < 0)
+ goto exception;
+ }
+ } else {
+ general_case:
+ for (;;) {
+ BOOL done;
+ value = JS_IteratorNext(ctx, enumobj, method, 0, NULL, &done);
+ if (JS_IsException(value))
+ goto exception;
+ if (done) {
+ /* value is JS_UNDEFINED */
+ break;
+ }
+ if (JS_DefinePropertyValueUint32(ctx, sp[-3], pos++, value, JS_PROP_C_W_E) < 0)
+ goto exception;
+ }
+ }
+ /* Note: could raise an error if too many elements */
+ sp[-2] = JS_NewInt32(ctx, pos);
+ JS_FreeValue(ctx, enumobj);
+ JS_FreeValue(ctx, method);
+ return 0;
+
+exception:
+ JS_IteratorClose(ctx, enumobj, TRUE);
+ JS_FreeValue(ctx, enumobj);
+ JS_FreeValue(ctx, method);
+ return -1;
+}
+
+static warn_unused int JS_CopyDataProperties(JSContext *ctx,
+ JSValueConst target,
+ JSValueConst source,
+ JSValueConst excluded,
+ BOOL setprop)
+{
+ JSPropertyEnum *tab_atom;
+ JSValue val;
+ uint32_t i, tab_atom_count;
+ JSObject *p;
+ JSObject *pexcl = NULL;
+ int ret, gpn_flags;
+ JSPropertyDescriptor desc;
+ BOOL is_enumerable;
+
+ if (JS_VALUE_GET_TAG(source) != JS_TAG_OBJECT)
+ return 0;
+
+ if (JS_VALUE_GET_TAG(excluded) == JS_TAG_OBJECT)
+ pexcl = JS_VALUE_GET_OBJ(excluded);
+
+ p = JS_VALUE_GET_OBJ(source);
+
+ gpn_flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK | JS_GPN_ENUM_ONLY;
+ if (p->is_exotic) {
+ const JSClassExoticMethods *em = ctx->rt->class_array[p->class_id].exotic;
+ /* cannot use JS_GPN_ENUM_ONLY with e.g. proxies because it
+ introduces a visible change */
+ if (em && em->get_own_property_names) {
+ gpn_flags &= ~JS_GPN_ENUM_ONLY;
+ }
+ }
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, p,
+ gpn_flags))
+ return -1;
+
+ for (i = 0; i < tab_atom_count; i++) {
+ if (pexcl) {
+ ret = JS_GetOwnPropertyInternal(ctx, NULL, pexcl, tab_atom[i].atom);
+ if (ret) {
+ if (ret < 0)
+ goto exception;
+ continue;
+ }
+ }
+ if (!(gpn_flags & JS_GPN_ENUM_ONLY)) {
+ /* test if the property is enumerable */
+ ret = JS_GetOwnPropertyInternal(ctx, &desc, p, tab_atom[i].atom);
+ if (ret < 0)
+ goto exception;
+ if (!ret)
+ continue;
+ is_enumerable = (desc.flags & JS_PROP_ENUMERABLE) != 0;
+ js_free_desc(ctx, &desc);
+ if (!is_enumerable)
+ continue;
+ }
+ val = JS_GetProperty(ctx, source, tab_atom[i].atom);
+ if (JS_IsException(val))
+ goto exception;
+ if (setprop)
+ ret = JS_SetProperty(ctx, target, tab_atom[i].atom, val);
+ else
+ ret = JS_DefinePropertyValue(ctx, target, tab_atom[i].atom, val,
+ JS_PROP_C_W_E);
+ if (ret < 0)
+ goto exception;
+ }
+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);
+ return 0;
+ exception:
+ js_free_prop_enum(ctx, tab_atom, tab_atom_count);
+ return -1;
+}
+
+/* only valid inside C functions */
+static JSValueConst JS_GetActiveFunction(JSContext *ctx)
+{
+ return ctx->rt->current_stack_frame->cur_func;
+}
+
+static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
+ int var_idx, BOOL is_arg)
+{
+ JSVarRef *var_ref;
+ struct list_head *el;
+
+ list_for_each(el, &sf->var_ref_list) {
+ var_ref = list_entry(el, JSVarRef, header.link);
+ if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) {
+ var_ref->header.ref_count++;
+ return var_ref;
+ }
+ }
+ /* create a new one */
+ var_ref = js_malloc(ctx, sizeof(JSVarRef));
+ if (!var_ref)
+ return NULL;
+ var_ref->header.ref_count = 1;
+ var_ref->is_detached = FALSE;
+ var_ref->is_arg = is_arg;
+ var_ref->var_idx = var_idx;
+ list_add_tail(&var_ref->header.link, &sf->var_ref_list);
+ if (is_arg)
+ var_ref->pvalue = &sf->arg_buf[var_idx];
+ else
+ var_ref->pvalue = &sf->var_buf[var_idx];
+ var_ref->value = JS_UNDEFINED;
+ return var_ref;
+}
+
+static JSValue js_closure2(JSContext *ctx, JSValue func_obj,
+ JSFunctionBytecode *b,
+ JSVarRef **cur_var_refs,
+ JSStackFrame *sf)
+{
+ JSObject *p;
+ JSVarRef **var_refs;
+ int i;
+
+ p = JS_VALUE_GET_OBJ(func_obj);
+ p->u.func.function_bytecode = b;
+ p->u.func.home_object = NULL;
+ p->u.func.var_refs = NULL;
+ if (b->closure_var_count) {
+ var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
+ if (!var_refs)
+ goto fail;
+ p->u.func.var_refs = var_refs;
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ JSVarRef *var_ref;
+ if (cv->is_local) {
+ /* reuse the existing variable reference if it already exists */
+ var_ref = get_var_ref(ctx, sf, cv->var_idx, cv->is_arg);
+ if (!var_ref)
+ goto fail;
+ } else {
+ var_ref = cur_var_refs[cv->var_idx];
+ var_ref->header.ref_count++;
+ }
+ var_refs[i] = var_ref;
+ }
+ }
+ return func_obj;
+ fail:
+ /* bfunc is freed when func_obj is freed */
+ JS_FreeValue(ctx, func_obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_instantiate_prototype(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque)
+{
+ JSValue obj, this_val;
+ int ret;
+
+ this_val = JS_MKPTR(JS_TAG_OBJECT, p);
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ set_cycle_flag(ctx, obj);
+ set_cycle_flag(ctx, this_val);
+ ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_constructor,
+ JS_DupValue(ctx, this_val),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ if (ret < 0) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ return obj;
+}
+
+static const uint16_t func_kind_to_class_id[] = {
+ [JS_FUNC_NORMAL] = JS_CLASS_BYTECODE_FUNCTION,
+ [JS_FUNC_GENERATOR] = JS_CLASS_GENERATOR_FUNCTION,
+ [JS_FUNC_ASYNC] = JS_CLASS_ASYNC_FUNCTION,
+ [JS_FUNC_ASYNC_GENERATOR] = JS_CLASS_ASYNC_GENERATOR_FUNCTION,
+};
+
+static JSValue js_closure(JSContext *ctx, JSValue bfunc,
+ JSVarRef **cur_var_refs,
+ JSStackFrame *sf)
+{
+ JSFunctionBytecode *b;
+ JSValue func_obj;
+ JSAtom name_atom;
+
+ b = JS_VALUE_GET_PTR(bfunc);
+ func_obj = JS_NewObjectClass(ctx, func_kind_to_class_id[b->func_kind]);
+ if (JS_IsException(func_obj)) {
+ JS_FreeValue(ctx, bfunc);
+ return JS_EXCEPTION;
+ }
+ func_obj = js_closure2(ctx, func_obj, b, cur_var_refs, sf);
+ if (JS_IsException(func_obj)) {
+ /* bfunc has been freed */
+ goto fail;
+ }
+ name_atom = b->func_name;
+ if (name_atom == JS_ATOM_NULL)
+ name_atom = JS_ATOM_empty_string;
+ js_function_set_properties(ctx, func_obj, name_atom,
+ b->defined_arg_count);
+
+ if (b->func_kind & JS_FUNC_GENERATOR) {
+ JSValue proto;
+ int proto_class_id;
+ /* generators have a prototype field which is used as
+ prototype for the generator object */
+ if (b->func_kind == JS_FUNC_ASYNC_GENERATOR)
+ proto_class_id = JS_CLASS_ASYNC_GENERATOR;
+ else
+ proto_class_id = JS_CLASS_GENERATOR;
+ proto = JS_NewObjectProto(ctx, ctx->class_proto[proto_class_id]);
+ if (JS_IsException(proto))
+ goto fail;
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype, proto,
+ JS_PROP_WRITABLE);
+ } else if (b->has_prototype) {
+ /* add the 'prototype' property: delay instantiation to avoid
+ creating cycles for every javascript function. The prototype
+ object is created on the fly when first accessed */
+ JS_SetConstructorBit(ctx, func_obj, TRUE);
+ JS_DefineAutoInitProperty(ctx, func_obj, JS_ATOM_prototype,
+ JS_AUTOINIT_ID_PROTOTYPE, NULL,
+ JS_PROP_WRITABLE);
+ }
+ return func_obj;
+ fail:
+ /* bfunc is freed when func_obj is freed */
+ JS_FreeValue(ctx, func_obj);
+ return JS_EXCEPTION;
+}
+
+#define JS_DEFINE_CLASS_HAS_HERITAGE (1 << 0)
+
+static int js_op_define_class(JSContext *ctx, JSValue *sp,
+ JSAtom class_name, int class_flags,
+ JSVarRef **cur_var_refs,
+ JSStackFrame *sf, BOOL is_computed_name)
+{
+ JSValue bfunc, parent_class, proto = JS_UNDEFINED;
+ JSValue ctor = JS_UNDEFINED, parent_proto = JS_UNDEFINED;
+ JSFunctionBytecode *b;
+
+ parent_class = sp[-2];
+ bfunc = sp[-1];
+
+ if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE) {
+ if (JS_IsNull(parent_class)) {
+ parent_proto = JS_NULL;
+ parent_class = JS_DupValue(ctx, ctx->function_proto);
+ } else {
+ if (!JS_IsConstructor(ctx, parent_class)) {
+ JS_ThrowTypeError(ctx, "parent class must be constructor");
+ goto fail;
+ }
+ parent_proto = JS_GetProperty(ctx, parent_class, JS_ATOM_prototype);
+ if (JS_IsException(parent_proto))
+ goto fail;
+ if (!JS_IsNull(parent_proto) && !JS_IsObject(parent_proto)) {
+ JS_ThrowTypeError(ctx, "parent prototype must be an object or null");
+ goto fail;
+ }
+ }
+ } else {
+ /* parent_class is JS_UNDEFINED in this case */
+ parent_proto = JS_DupValue(ctx, ctx->class_proto[JS_CLASS_OBJECT]);
+ parent_class = JS_DupValue(ctx, ctx->function_proto);
+ }
+ proto = JS_NewObjectProto(ctx, parent_proto);
+ if (JS_IsException(proto))
+ goto fail;
+
+ b = JS_VALUE_GET_PTR(bfunc);
+ assert(b->func_kind == JS_FUNC_NORMAL);
+ ctor = JS_NewObjectProtoClass(ctx, parent_class,
+ JS_CLASS_BYTECODE_FUNCTION);
+ if (JS_IsException(ctor))
+ goto fail;
+ ctor = js_closure2(ctx, ctor, b, cur_var_refs, sf);
+ bfunc = JS_UNDEFINED;
+ if (JS_IsException(ctor))
+ goto fail;
+ js_method_set_home_object(ctx, ctor, proto);
+ JS_SetConstructorBit(ctx, ctor, TRUE);
+
+ JS_DefinePropertyValue(ctx, ctor, JS_ATOM_length,
+ JS_NewInt32(ctx, b->defined_arg_count),
+ JS_PROP_CONFIGURABLE);
+
+ if (is_computed_name) {
+ if (JS_DefineObjectNameComputed(ctx, ctor, sp[-3],
+ JS_PROP_CONFIGURABLE) < 0)
+ goto fail;
+ } else {
+ if (JS_DefineObjectName(ctx, ctor, class_name, JS_PROP_CONFIGURABLE) < 0)
+ goto fail;
+ }
+
+ /* the constructor property must be first. It can be overriden by
+ computed property names */
+ if (JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
+ JS_DupValue(ctx, ctor),
+ JS_PROP_CONFIGURABLE |
+ JS_PROP_WRITABLE | JS_PROP_THROW) < 0)
+ goto fail;
+ /* set the prototype property */
+ if (JS_DefinePropertyValue(ctx, ctor, JS_ATOM_prototype,
+ JS_DupValue(ctx, proto), JS_PROP_THROW) < 0)
+ goto fail;
+ set_cycle_flag(ctx, ctor);
+ set_cycle_flag(ctx, proto);
+
+ JS_FreeValue(ctx, parent_proto);
+ JS_FreeValue(ctx, parent_class);
+
+ sp[-2] = ctor;
+ sp[-1] = proto;
+ return 0;
+ fail:
+ JS_FreeValue(ctx, parent_class);
+ JS_FreeValue(ctx, parent_proto);
+ JS_FreeValue(ctx, bfunc);
+ JS_FreeValue(ctx, proto);
+ JS_FreeValue(ctx, ctor);
+ sp[-2] = JS_UNDEFINED;
+ sp[-1] = JS_UNDEFINED;
+ return -1;
+}
+
+static void close_var_refs(JSRuntime *rt, JSStackFrame *sf)
+{
+ struct list_head *el, *el1;
+ JSVarRef *var_ref;
+ int var_idx;
+
+ list_for_each_safe(el, el1, &sf->var_ref_list) {
+ var_ref = list_entry(el, JSVarRef, header.link);
+ var_idx = var_ref->var_idx;
+ if (var_ref->is_arg)
+ var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]);
+ else
+ var_ref->value = JS_DupValueRT(rt, sf->var_buf[var_idx]);
+ var_ref->pvalue = &var_ref->value;
+ /* the reference is no longer to a local variable */
+ var_ref->is_detached = TRUE;
+ add_gc_object(rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
+ }
+}
+
+static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_arg)
+{
+ struct list_head *el, *el1;
+ JSVarRef *var_ref;
+ int var_idx = idx;
+
+ list_for_each_safe(el, el1, &sf->var_ref_list) {
+ var_ref = list_entry(el, JSVarRef, header.link);
+ if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) {
+ var_ref->value = JS_DupValue(ctx, sf->var_buf[var_idx]);
+ var_ref->pvalue = &var_ref->value;
+ list_del(&var_ref->header.link);
+ /* the reference is no longer to a local variable */
+ var_ref->is_detached = TRUE;
+ add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
+ }
+ }
+}
+
+#define JS_CALL_FLAG_COPY_ARGV (1 << 1)
+#define JS_CALL_FLAG_GENERATOR (1 << 2)
+
+static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags)
+{
+ JSRuntime *rt = ctx->rt;
+ JSCFunctionType func;
+ JSObject *p;
+ JSStackFrame sf_s, *sf = &sf_s, *prev_sf;
+ JSValue ret_val;
+ JSValueConst *arg_buf;
+ int arg_count, i;
+ JSCFunctionEnum cproto;
+
+ p = JS_VALUE_GET_OBJ(func_obj);
+ cproto = p->u.cfunc.cproto;
+ arg_count = p->u.cfunc.length;
+
+ /* better to always check stack overflow */
+ if (js_check_stack_overflow(rt, sizeof(arg_buf[0]) * arg_count))
+ return JS_ThrowStackOverflow(ctx);
+
+ prev_sf = rt->current_stack_frame;
+ sf->prev_frame = prev_sf;
+ rt->current_stack_frame = sf;
+ ctx = p->u.cfunc.realm; /* change the current realm */
+
+#ifdef CONFIG_BIGNUM
+ /* we only propagate the bignum mode as some runtime functions
+ test it */
+ if (prev_sf)
+ sf->js_mode = prev_sf->js_mode & JS_MODE_MATH;
+ else
+ sf->js_mode = 0;
+#else
+ sf->js_mode = 0;
+#endif
+ sf->cur_func = func_obj;
+ sf->arg_count = argc;
+ arg_buf = argv;
+
+ if (unlikely(argc < arg_count)) {
+ /* ensure that at least argc_count arguments are readable */
+ arg_buf = alloca(sizeof(arg_buf[0]) * arg_count);
+ for(i = 0; i < argc; i++)
+ arg_buf[i] = argv[i];
+ for(i = argc; i < arg_count; i++)
+ arg_buf[i] = JS_UNDEFINED;
+ sf->arg_count = arg_count;
+ }
+ sf->arg_buf = arg_buf;
+
+ func = p->u.cfunc.c_function;
+ switch(cproto) {
+ case JS_CFUNC_constructor:
+ case JS_CFUNC_constructor_or_func:
+ if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
+ if (cproto == JS_CFUNC_constructor) {
+ not_a_constructor:
+ ret_val = JS_ThrowTypeError(ctx, "must be called with new");
+ break;
+ } else {
+ this_obj = JS_UNDEFINED;
+ }
+ }
+ /* here this_obj is new_target */
+ /* fall thru */
+ case JS_CFUNC_generic:
+ ret_val = func.generic(ctx, this_obj, argc, arg_buf);
+ break;
+ case JS_CFUNC_constructor_magic:
+ case JS_CFUNC_constructor_or_func_magic:
+ if (!(flags & JS_CALL_FLAG_CONSTRUCTOR)) {
+ if (cproto == JS_CFUNC_constructor_magic) {
+ goto not_a_constructor;
+ } else {
+ this_obj = JS_UNDEFINED;
+ }
+ }
+ /* fall thru */
+ case JS_CFUNC_generic_magic:
+ ret_val = func.generic_magic(ctx, this_obj, argc, arg_buf,
+ p->u.cfunc.magic);
+ break;
+ case JS_CFUNC_getter:
+ ret_val = func.getter(ctx, this_obj);
+ break;
+ case JS_CFUNC_setter:
+ ret_val = func.setter(ctx, this_obj, arg_buf[0]);
+ break;
+ case JS_CFUNC_getter_magic:
+ ret_val = func.getter_magic(ctx, this_obj, p->u.cfunc.magic);
+ break;
+ case JS_CFUNC_setter_magic:
+ ret_val = func.setter_magic(ctx, this_obj, arg_buf[0], p->u.cfunc.magic);
+ break;
+ case JS_CFUNC_f_f:
+ {
+ double d1;
+
+ if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
+ ret_val = JS_EXCEPTION;
+ break;
+ }
+ ret_val = JS_NewFloat64(ctx, func.f_f(d1));
+ }
+ break;
+ case JS_CFUNC_f_f_f:
+ {
+ double d1, d2;
+
+ if (unlikely(JS_ToFloat64(ctx, &d1, arg_buf[0]))) {
+ ret_val = JS_EXCEPTION;
+ break;
+ }
+ if (unlikely(JS_ToFloat64(ctx, &d2, arg_buf[1]))) {
+ ret_val = JS_EXCEPTION;
+ break;
+ }
+ ret_val = JS_NewFloat64(ctx, func.f_f_f(d1, d2));
+ }
+ break;
+ case JS_CFUNC_iterator_next:
+ {
+ int done;
+ ret_val = func.iterator_next(ctx, this_obj, argc, arg_buf,
+ &done, p->u.cfunc.magic);
+ if (!JS_IsException(ret_val) && done != 2) {
+ ret_val = js_create_iterator_result(ctx, ret_val, done);
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+
+ rt->current_stack_frame = sf->prev_frame;
+ return ret_val;
+}
+
+static JSValue js_call_bound_function(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags)
+{
+ JSObject *p;
+ JSBoundFunction *bf;
+ JSValueConst *arg_buf, new_target;
+ int arg_count, i;
+
+ p = JS_VALUE_GET_OBJ(func_obj);
+ bf = p->u.bound_function;
+ arg_count = bf->argc + argc;
+ if (js_check_stack_overflow(ctx->rt, sizeof(JSValue) * arg_count))
+ return JS_ThrowStackOverflow(ctx);
+ arg_buf = alloca(sizeof(JSValue) * arg_count);
+ for(i = 0; i < bf->argc; i++) {
+ arg_buf[i] = bf->argv[i];
+ }
+ for(i = 0; i < argc; i++) {
+ arg_buf[bf->argc + i] = argv[i];
+ }
+ if (flags & JS_CALL_FLAG_CONSTRUCTOR) {
+ new_target = this_obj;
+ if (js_same_value(ctx, func_obj, new_target))
+ new_target = bf->func_obj;
+ return JS_CallConstructor2(ctx, bf->func_obj, new_target,
+ arg_count, arg_buf);
+ } else {
+ return JS_Call(ctx, bf->func_obj, bf->this_val,
+ arg_count, arg_buf);
+ }
+}
+
+/* argument of OP_special_object */
+typedef enum {
+ OP_SPECIAL_OBJECT_ARGUMENTS,
+ OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS,
+ OP_SPECIAL_OBJECT_THIS_FUNC,
+ OP_SPECIAL_OBJECT_NEW_TARGET,
+ OP_SPECIAL_OBJECT_HOME_OBJECT,
+ OP_SPECIAL_OBJECT_VAR_OBJECT,
+ OP_SPECIAL_OBJECT_IMPORT_META,
+} OPSpecialObjectEnum;
+
+#define FUNC_RET_AWAIT 0
+#define FUNC_RET_YIELD 1
+#define FUNC_RET_YIELD_STAR 2
+
+/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
+static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ JSValueConst this_obj, JSValueConst new_target,
+ int argc, JSValue *argv, int flags)
+{
+ JSRuntime *rt = caller_ctx->rt;
+ JSContext *ctx;
+ JSObject *p;
+ JSFunctionBytecode *b;
+ JSStackFrame sf_s, *sf = &sf_s;
+ const uint8_t *pc;
+ int opcode, arg_allocated_size, i;
+ JSValue *local_buf, *stack_buf, *var_buf, *arg_buf, *sp, ret_val, *pval;
+ JSVarRef **var_refs;
+ size_t alloca_size;
+
+#if !DIRECT_DISPATCH
+#define SWITCH(pc) switch (opcode = *pc++)
+#define CASE(op) case op
+#define DEFAULT default
+#define BREAK break
+#else
+ static const void * const dispatch_table[256] = {
+#define DEF(id, size, n_pop, n_push, f) && case_OP_ ## id,
+#if SHORT_OPCODES
+#define def(id, size, n_pop, n_push, f)
+#else
+#define def(id, size, n_pop, n_push, f) && case_default,
+#endif
+#include "quickjs-opcode.h"
+ [ OP_COUNT ... 255 ] = &&case_default
+ };
+#define SWITCH(pc) goto *dispatch_table[opcode = *(pc)++];
+#define CASE(op) case_ ## op
+#define DEFAULT case_default
+#define BREAK SWITCH(pc)
+#endif
+
+ if (js_poll_interrupts(caller_ctx))
+ return JS_EXCEPTION;
+ if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)) {
+ if (flags & JS_CALL_FLAG_GENERATOR) {
+ JSAsyncFunctionState *s = JS_VALUE_GET_PTR(func_obj);
+ /* func_obj get contains a pointer to JSFuncAsyncState */
+ /* the stack frame is already allocated */
+ sf = &s->frame;
+ p = JS_VALUE_GET_OBJ(sf->cur_func);
+ b = p->u.func.function_bytecode;
+ ctx = b->realm;
+ var_refs = p->u.func.var_refs;
+ local_buf = arg_buf = sf->arg_buf;
+ var_buf = sf->var_buf;
+ stack_buf = sf->var_buf + b->var_count;
+ sp = sf->cur_sp;
+ sf->cur_sp = NULL; /* cur_sp is NULL if the function is running */
+ pc = sf->cur_pc;
+ sf->prev_frame = rt->current_stack_frame;
+ rt->current_stack_frame = sf;
+ if (s->throw_flag)
+ goto exception;
+ else
+ goto restart;
+ } else {
+ goto not_a_function;
+ }
+ }
+ p = JS_VALUE_GET_OBJ(func_obj);
+ if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
+ JSClassCall *call_func;
+ call_func = rt->class_array[p->class_id].call;
+ if (!call_func) {
+ not_a_function:
+ return JS_ThrowTypeError(caller_ctx, "not a function");
+ }
+ return call_func(caller_ctx, func_obj, this_obj, argc,
+ argv, flags);
+ }
+ b = p->u.func.function_bytecode;
+
+ if (unlikely(argc < b->arg_count || (flags & JS_CALL_FLAG_COPY_ARGV))) {
+ arg_allocated_size = b->arg_count;
+ } else {
+ arg_allocated_size = 0;
+ }
+
+ alloca_size = sizeof(JSValue) * (arg_allocated_size + b->var_count +
+ b->stack_size);
+ if (js_check_stack_overflow(rt, alloca_size))
+ return JS_ThrowStackOverflow(caller_ctx);
+
+ sf->js_mode = b->js_mode;
+ arg_buf = argv;
+ sf->arg_count = argc;
+ sf->cur_func = func_obj;
+ init_list_head(&sf->var_ref_list);
+ var_refs = p->u.func.var_refs;
+
+ local_buf = alloca(alloca_size);
+ if (unlikely(arg_allocated_size)) {
+ int n = min_int(argc, b->arg_count);
+ arg_buf = local_buf;
+ for(i = 0; i < n; i++)
+ arg_buf[i] = JS_DupValue(caller_ctx, argv[i]);
+ for(; i < b->arg_count; i++)
+ arg_buf[i] = JS_UNDEFINED;
+ sf->arg_count = b->arg_count;
+ }
+ var_buf = local_buf + arg_allocated_size;
+ sf->var_buf = var_buf;
+ sf->arg_buf = arg_buf;
+
+ for(i = 0; i < b->var_count; i++)
+ var_buf[i] = JS_UNDEFINED;
+
+ stack_buf = var_buf + b->var_count;
+ sp = stack_buf;
+ pc = b->byte_code_buf;
+ sf->prev_frame = rt->current_stack_frame;
+ rt->current_stack_frame = sf;
+ ctx = b->realm; /* set the current realm */
+
+ if (ctx->handleFunctionEntered)
+ ctx->handleFunctionEntered(ctx, this_obj);
+
+ restart:
+ for(;;) {
+ int call_argc;
+ JSValue *call_argv;
+
+ SWITCH(pc) {
+ CASE(OP_push_i32):
+ *sp++ = JS_NewInt32(ctx, get_u32(pc));
+ pc += 4;
+ BREAK;
+ CASE(OP_push_const):
+ *sp++ = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
+ pc += 4;
+ BREAK;
+#if SHORT_OPCODES
+ CASE(OP_push_minus1):
+ CASE(OP_push_0):
+ CASE(OP_push_1):
+ CASE(OP_push_2):
+ CASE(OP_push_3):
+ CASE(OP_push_4):
+ CASE(OP_push_5):
+ CASE(OP_push_6):
+ CASE(OP_push_7):
+ *sp++ = JS_NewInt32(ctx, opcode - OP_push_0);
+ BREAK;
+ CASE(OP_push_i8):
+ *sp++ = JS_NewInt32(ctx, get_i8(pc));
+ pc += 1;
+ BREAK;
+ CASE(OP_push_i16):
+ *sp++ = JS_NewInt32(ctx, get_i16(pc));
+ pc += 2;
+ BREAK;
+ CASE(OP_push_const8):
+ *sp++ = JS_DupValue(ctx, b->cpool[*pc++]);
+ BREAK;
+ CASE(OP_fclosure8):
+ *sp++ = js_closure(ctx, JS_DupValue(ctx, b->cpool[*pc++]), var_refs, sf);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ BREAK;
+ CASE(OP_push_empty_string):
+ *sp++ = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ BREAK;
+ CASE(OP_get_length):
+ {
+ JSValue val;
+
+ val = JS_GetProperty(ctx, sp[-1], JS_ATOM_length);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = val;
+ }
+ BREAK;
+#endif
+ CASE(OP_push_atom_value):
+ *sp++ = JS_AtomToValue(ctx, get_u32(pc));
+ pc += 4;
+ BREAK;
+ CASE(OP_undefined):
+ *sp++ = JS_UNDEFINED;
+ BREAK;
+ CASE(OP_null):
+ *sp++ = JS_NULL;
+ BREAK;
+ CASE(OP_push_this):
+ /* OP_push_this is only called at the start of a function */
+ {
+ JSValue val;
+ if (!(b->js_mode & JS_MODE_STRICT)) {
+ uint32_t tag = JS_VALUE_GET_TAG(this_obj);
+ if (likely(tag == JS_TAG_OBJECT))
+ goto normal_this;
+ if (tag == JS_TAG_NULL || tag == JS_TAG_UNDEFINED) {
+ val = JS_DupValue(ctx, ctx->global_obj);
+ } else {
+ val = JS_ToObject(ctx, this_obj);
+ if (JS_IsException(val))
+ goto exception;
+ }
+ } else {
+ normal_this:
+ val = JS_DupValue(ctx, this_obj);
+ }
+ *sp++ = val;
+ }
+ BREAK;
+ CASE(OP_push_false):
+ *sp++ = JS_FALSE;
+ BREAK;
+ CASE(OP_push_true):
+ *sp++ = JS_TRUE;
+ BREAK;
+ CASE(OP_object):
+ *sp++ = JS_NewObject(ctx);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ BREAK;
+ CASE(OP_special_object):
+ {
+ int arg = *pc++;
+ switch(arg) {
+ case OP_SPECIAL_OBJECT_ARGUMENTS:
+ *sp++ = js_build_arguments(ctx, argc, argv);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ break;
+ case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:
+ *sp++ = js_build_mapped_arguments(ctx, argc, argv,
+ sf, min_int(argc, b->arg_count));
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ break;
+ case OP_SPECIAL_OBJECT_THIS_FUNC:
+ *sp++ = JS_DupValue(ctx, sf->cur_func);
+ break;
+ case OP_SPECIAL_OBJECT_NEW_TARGET:
+ *sp++ = JS_DupValue(ctx, new_target);
+ break;
+ case OP_SPECIAL_OBJECT_HOME_OBJECT:
+ {
+ JSObject *p1;
+ p1 = p->u.func.home_object;
+ if (unlikely(!p1))
+ *sp++ = JS_UNDEFINED;
+ else
+ *sp++ = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, p1));
+ }
+ break;
+ case OP_SPECIAL_OBJECT_VAR_OBJECT:
+ *sp++ = JS_NewObjectProto(ctx, JS_NULL);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ break;
+ case OP_SPECIAL_OBJECT_IMPORT_META:
+ *sp++ = js_import_meta(ctx);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ break;
+ default:
+ abort();
+ }
+ }
+ BREAK;
+ CASE(OP_rest):
+ {
+ int first = get_u16(pc);
+ pc += 2;
+ *sp++ = js_build_rest(ctx, first, argc, argv);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_drop):
+ JS_FreeValue(ctx, sp[-1]);
+ sp--;
+ BREAK;
+ CASE(OP_nip):
+ JS_FreeValue(ctx, sp[-2]);
+ sp[-2] = sp[-1];
+ sp--;
+ BREAK;
+ CASE(OP_nip1): /* a b c -> b c */
+ JS_FreeValue(ctx, sp[-3]);
+ sp[-3] = sp[-2];
+ sp[-2] = sp[-1];
+ sp--;
+ BREAK;
+ CASE(OP_dup):
+ sp[0] = JS_DupValue(ctx, sp[-1]);
+ sp++;
+ BREAK;
+ CASE(OP_dup2): /* a b -> a b a b */
+ sp[0] = JS_DupValue(ctx, sp[-2]);
+ sp[1] = JS_DupValue(ctx, sp[-1]);
+ sp += 2;
+ BREAK;
+ CASE(OP_dup3): /* a b c -> a b c a b c */
+ sp[0] = JS_DupValue(ctx, sp[-3]);
+ sp[1] = JS_DupValue(ctx, sp[-2]);
+ sp[2] = JS_DupValue(ctx, sp[-1]);
+ sp += 3;
+ BREAK;
+ CASE(OP_dup1): /* a b -> a a b */
+ sp[0] = sp[-1];
+ sp[-1] = JS_DupValue(ctx, sp[-2]);
+ sp++;
+ BREAK;
+ CASE(OP_insert2): /* obj a -> a obj a (dup_x1) */
+ sp[0] = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = JS_DupValue(ctx, sp[0]);
+ sp++;
+ BREAK;
+ CASE(OP_insert3): /* obj prop a -> a obj prop a (dup_x2) */
+ sp[0] = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = JS_DupValue(ctx, sp[0]);
+ sp++;
+ BREAK;
+ CASE(OP_insert4): /* this obj prop a -> a this obj prop a */
+ sp[0] = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = sp[-4];
+ sp[-4] = JS_DupValue(ctx, sp[0]);
+ sp++;
+ BREAK;
+ CASE(OP_perm3): /* obj a b -> a obj b (213) */
+ {
+ JSValue tmp;
+ tmp = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = tmp;
+ }
+ BREAK;
+ CASE(OP_rot3l): /* x a b -> a b x (231) */
+ {
+ JSValue tmp;
+ tmp = sp[-3];
+ sp[-3] = sp[-2];
+ sp[-2] = sp[-1];
+ sp[-1] = tmp;
+ }
+ BREAK;
+ CASE(OP_rot4l): /* x a b c -> a b c x */
+ {
+ JSValue tmp;
+ tmp = sp[-4];
+ sp[-4] = sp[-3];
+ sp[-3] = sp[-2];
+ sp[-2] = sp[-1];
+ sp[-1] = tmp;
+ }
+ BREAK;
+ CASE(OP_rot5l): /* x a b c d -> a b c d x */
+ {
+ JSValue tmp;
+ tmp = sp[-5];
+ sp[-5] = sp[-4];
+ sp[-4] = sp[-3];
+ sp[-3] = sp[-2];
+ sp[-2] = sp[-1];
+ sp[-1] = tmp;
+ }
+ BREAK;
+ CASE(OP_rot3r): /* a b x -> x a b (312) */
+ {
+ JSValue tmp;
+ tmp = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = tmp;
+ }
+ BREAK;
+ CASE(OP_perm4): /* obj prop a b -> a obj prop b */
+ {
+ JSValue tmp;
+ tmp = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = sp[-4];
+ sp[-4] = tmp;
+ }
+ BREAK;
+ CASE(OP_perm5): /* this obj prop a b -> a this obj prop b */
+ {
+ JSValue tmp;
+ tmp = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = sp[-4];
+ sp[-4] = sp[-5];
+ sp[-5] = tmp;
+ }
+ BREAK;
+ CASE(OP_swap): /* a b -> b a */
+ {
+ JSValue tmp;
+ tmp = sp[-2];
+ sp[-2] = sp[-1];
+ sp[-1] = tmp;
+ }
+ BREAK;
+ CASE(OP_swap2): /* a b c d -> c d a b */
+ {
+ JSValue tmp1, tmp2;
+ tmp1 = sp[-4];
+ tmp2 = sp[-3];
+ sp[-4] = sp[-2];
+ sp[-3] = sp[-1];
+ sp[-2] = tmp1;
+ sp[-1] = tmp2;
+ }
+ BREAK;
+
+ CASE(OP_fclosure):
+ {
+ JSValue bfunc = JS_DupValue(ctx, b->cpool[get_u32(pc)]);
+ pc += 4;
+ *sp++ = js_closure(ctx, bfunc, var_refs, sf);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ }
+ BREAK;
+#if SHORT_OPCODES
+ CASE(OP_call0):
+ CASE(OP_call1):
+ CASE(OP_call2):
+ CASE(OP_call3):
+ call_argc = opcode - OP_call0;
+ goto has_call_argc;
+#endif
+ CASE(OP_call):
+ CASE(OP_tail_call):
+ {
+ call_argc = get_u16(pc);
+ pc += 2;
+ goto has_call_argc;
+ has_call_argc:
+ call_argv = sp - call_argc;
+ sf->cur_pc = pc;
+ ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
+ JS_UNDEFINED, call_argc, call_argv, 0);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ if (opcode == OP_tail_call)
+ goto done;
+ for(i = -1; i < call_argc; i++)
+ JS_FreeValue(ctx, call_argv[i]);
+ sp -= call_argc + 1;
+ *sp++ = ret_val;
+ }
+ BREAK;
+ CASE(OP_call_constructor):
+ {
+ call_argc = get_u16(pc);
+ pc += 2;
+ call_argv = sp - call_argc;
+ sf->cur_pc = pc;
+ ret_val = JS_CallConstructorInternal(ctx, call_argv[-2],
+ call_argv[-1],
+ call_argc, call_argv, 0);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ for(i = -2; i < call_argc; i++)
+ JS_FreeValue(ctx, call_argv[i]);
+ sp -= call_argc + 2;
+ *sp++ = ret_val;
+ }
+ BREAK;
+ CASE(OP_call_method):
+ CASE(OP_tail_call_method):
+ {
+ call_argc = get_u16(pc);
+ pc += 2;
+ call_argv = sp - call_argc;
+ sf->cur_pc = pc;
+ ret_val = JS_CallInternal(ctx, call_argv[-1], call_argv[-2],
+ JS_UNDEFINED, call_argc, call_argv, 0);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ if (opcode == OP_tail_call_method)
+ goto done;
+ for(i = -2; i < call_argc; i++)
+ JS_FreeValue(ctx, call_argv[i]);
+ sp -= call_argc + 2;
+ *sp++ = ret_val;
+ }
+ BREAK;
+ CASE(OP_array_from):
+ {
+ int i, ret;
+
+ call_argc = get_u16(pc);
+ pc += 2;
+ ret_val = JS_NewArray(ctx);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ call_argv = sp - call_argc;
+ for(i = 0; i < call_argc; i++) {
+ ret = JS_DefinePropertyValue(ctx, ret_val, JS_AtomFromUInt32(i), call_argv[i],
+ JS_PROP_C_W_E | JS_PROP_THROW);
+ call_argv[i] = JS_UNDEFINED;
+ if (ret < 0) {
+ JS_FreeValue(ctx, ret_val);
+ goto exception;
+ }
+ }
+ sp -= call_argc;
+ *sp++ = ret_val;
+ }
+ BREAK;
+
+ CASE(OP_apply):
+ {
+ int magic;
+ magic = get_u16(pc);
+ pc += 2;
+
+ ret_val = js_function_apply(ctx, sp[-3], 2, &sp[-2], magic);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-3]);
+ JS_FreeValue(ctx, sp[-2]);
+ JS_FreeValue(ctx, sp[-1]);
+ sp -= 3;
+ *sp++ = ret_val;
+ }
+ BREAK;
+ CASE(OP_return):
+ ret_val = *--sp;
+ goto done;
+ CASE(OP_return_undef):
+ ret_val = JS_UNDEFINED;
+ goto done;
+
+ CASE(OP_check_ctor_return):
+ /* return TRUE if 'this' should be returned */
+ if (!JS_IsObject(sp[-1])) {
+ if (!JS_IsUndefined(sp[-1])) {
+ JS_ThrowTypeError(caller_ctx, "derived class constructor must return an object or undefined");
+ goto exception;
+ }
+ sp[0] = JS_TRUE;
+ } else {
+ sp[0] = JS_FALSE;
+ }
+ sp++;
+ BREAK;
+ CASE(OP_check_ctor):
+ if (JS_IsUndefined(new_target)) {
+ JS_ThrowTypeError(ctx, "class constructors must be invoked with 'new'");
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_check_brand):
+ if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0)
+ goto exception;
+ BREAK;
+ CASE(OP_add_brand):
+ if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0)
+ goto exception;
+ JS_FreeValue(ctx, sp[-2]);
+ JS_FreeValue(ctx, sp[-1]);
+ sp -= 2;
+ BREAK;
+
+ CASE(OP_throw):
+ JS_Throw(ctx, *--sp);
+ goto exception;
+
+ CASE(OP_throw_error):
+#define JS_THROW_VAR_RO 0
+#define JS_THROW_VAR_REDECL 1
+#define JS_THROW_VAR_UNINITIALIZED 2
+#define JS_THROW_ERROR_DELETE_SUPER 3
+#define JS_THROW_ERROR_ITERATOR_THROW 4
+ {
+ JSAtom atom;
+ int type;
+ atom = get_u32(pc);
+ type = pc[4];
+ pc += 5;
+ if (type == JS_THROW_VAR_RO)
+ JS_ThrowTypeErrorReadOnly(ctx, JS_PROP_THROW, atom);
+ else
+ if (type == JS_THROW_VAR_REDECL)
+ JS_ThrowSyntaxErrorVarRedeclaration(ctx, atom);
+ else
+ if (type == JS_THROW_VAR_UNINITIALIZED)
+ JS_ThrowReferenceErrorUninitialized(ctx, atom);
+ else
+ if (type == JS_THROW_ERROR_DELETE_SUPER)
+ JS_ThrowReferenceError(ctx, "unsupported reference to 'super'");
+ else
+ if (type == JS_THROW_ERROR_ITERATOR_THROW)
+ JS_ThrowTypeError(ctx, "iterator does not have a throw method");
+ else
+ JS_ThrowInternalError(ctx, "invalid throw var type %d", type);
+ }
+ goto exception;
+
+ CASE(OP_eval):
+ {
+ JSValueConst obj;
+ int scope_idx;
+ call_argc = get_u16(pc);
+ scope_idx = get_u16(pc + 2) - 1;
+ pc += 4;
+ call_argv = sp - call_argc;
+ sf->cur_pc = pc;
+ if (js_same_value(ctx, call_argv[-1], ctx->eval_obj)) {
+ if (call_argc >= 1)
+ obj = call_argv[0];
+ else
+ obj = JS_UNDEFINED;
+ ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
+ JS_EVAL_TYPE_DIRECT, scope_idx);
+ } else {
+ ret_val = JS_CallInternal(ctx, call_argv[-1], JS_UNDEFINED,
+ JS_UNDEFINED, call_argc, call_argv, 0);
+ }
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ for(i = -1; i < call_argc; i++)
+ JS_FreeValue(ctx, call_argv[i]);
+ sp -= call_argc + 1;
+ *sp++ = ret_val;
+ }
+ BREAK;
+ /* could merge with OP_apply */
+ CASE(OP_apply_eval):
+ {
+ int scope_idx;
+ uint32_t len;
+ JSValue *tab;
+ JSValueConst obj;
+
+ scope_idx = get_u16(pc) - 1;
+ pc += 2;
+ tab = build_arg_list(ctx, &len, sp[-1]);
+ if (!tab)
+ goto exception;
+ if (js_same_value(ctx, sp[-2], ctx->eval_obj)) {
+ if (len >= 1)
+ obj = tab[0];
+ else
+ obj = JS_UNDEFINED;
+ ret_val = JS_EvalObject(ctx, JS_UNDEFINED, obj,
+ JS_EVAL_TYPE_DIRECT, scope_idx);
+ } else {
+ ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,
+ tab);
+ }
+ free_arg_list(ctx, tab, len);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-2]);
+ JS_FreeValue(ctx, sp[-1]);
+ sp -= 2;
+ *sp++ = ret_val;
+ }
+ BREAK;
+
+ CASE(OP_regexp):
+ {
+ sp[-2] = js_regexp_constructor_internal(ctx, JS_UNDEFINED,
+ sp[-2], sp[-1]);
+ sp--;
+ }
+ BREAK;
+
+ CASE(OP_get_super):
+ {
+ JSValue proto;
+ proto = JS_GetPrototype(ctx, sp[-1]);
+ if (JS_IsException(proto))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = proto;
+ }
+ BREAK;
+
+ CASE(OP_import):
+ {
+ JSValue val;
+ val = js_dynamic_import(ctx, sp[-1]);
+ if (JS_IsException(val))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = val;
+ }
+ BREAK;
+
+ CASE(OP_check_var):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_CheckGlobalVar(ctx, atom);
+ if (ret < 0)
+ goto exception;
+ *sp++ = JS_NewBool(ctx, ret);
+ }
+ BREAK;
+
+ CASE(OP_get_var_undef):
+ CASE(OP_get_var):
+ {
+ JSValue val;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ val = JS_GetGlobalVar(ctx, atom, opcode - OP_get_var_undef);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ *sp++ = val;
+ }
+ BREAK;
+
+ CASE(OP_put_var):
+ CASE(OP_put_var_init):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_SetGlobalVar(ctx, atom, sp[-1], opcode - OP_put_var);
+ sp--;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_put_var_strict):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ /* sp[-2] is JS_TRUE or JS_FALSE */
+ if (unlikely(!JS_VALUE_GET_INT(sp[-2]))) {
+ JS_ThrowReferenceErrorNotDefined(ctx, atom);
+ goto exception;
+ }
+ ret = JS_SetGlobalVar(ctx, atom, sp[-1], 2);
+ sp -= 2;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_check_define_var):
+ {
+ JSAtom atom;
+ int flags;
+ atom = get_u32(pc);
+ flags = pc[4];
+ pc += 5;
+ if (JS_CheckDefineGlobalVar(ctx, atom, flags))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_define_var):
+ {
+ JSAtom atom;
+ int flags;
+ atom = get_u32(pc);
+ flags = pc[4];
+ pc += 5;
+ if (JS_DefineGlobalVar(ctx, atom, flags))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_define_func):
+ {
+ JSAtom atom;
+ int flags;
+ atom = get_u32(pc);
+ flags = pc[4];
+ pc += 5;
+ if (JS_DefineGlobalFunction(ctx, atom, sp[-1], flags))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp--;
+ }
+ BREAK;
+
+ CASE(OP_get_loc):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ sp[0] = JS_DupValue(ctx, var_buf[idx]);
+ sp++;
+ }
+ BREAK;
+ CASE(OP_put_loc):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, &var_buf[idx], sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_set_loc):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, &var_buf[idx], JS_DupValue(ctx, sp[-1]));
+ }
+ BREAK;
+ CASE(OP_get_arg):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ sp[0] = JS_DupValue(ctx, arg_buf[idx]);
+ sp++;
+ }
+ BREAK;
+ CASE(OP_put_arg):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, &arg_buf[idx], sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_set_arg):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, &arg_buf[idx], JS_DupValue(ctx, sp[-1]));
+ }
+ BREAK;
+
+#if SHORT_OPCODES
+ CASE(OP_get_loc8): *sp++ = JS_DupValue(ctx, var_buf[*pc++]); BREAK;
+ CASE(OP_put_loc8): set_value(ctx, &var_buf[*pc++], *--sp); BREAK;
+ CASE(OP_set_loc8): set_value(ctx, &var_buf[*pc++], JS_DupValue(ctx, sp[-1])); BREAK;
+
+ CASE(OP_get_loc0): *sp++ = JS_DupValue(ctx, var_buf[0]); BREAK;
+ CASE(OP_get_loc1): *sp++ = JS_DupValue(ctx, var_buf[1]); BREAK;
+ CASE(OP_get_loc2): *sp++ = JS_DupValue(ctx, var_buf[2]); BREAK;
+ CASE(OP_get_loc3): *sp++ = JS_DupValue(ctx, var_buf[3]); BREAK;
+ CASE(OP_put_loc0): set_value(ctx, &var_buf[0], *--sp); BREAK;
+ CASE(OP_put_loc1): set_value(ctx, &var_buf[1], *--sp); BREAK;
+ CASE(OP_put_loc2): set_value(ctx, &var_buf[2], *--sp); BREAK;
+ CASE(OP_put_loc3): set_value(ctx, &var_buf[3], *--sp); BREAK;
+ CASE(OP_set_loc0): set_value(ctx, &var_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_loc1): set_value(ctx, &var_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_loc2): set_value(ctx, &var_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_loc3): set_value(ctx, &var_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_get_arg0): *sp++ = JS_DupValue(ctx, arg_buf[0]); BREAK;
+ CASE(OP_get_arg1): *sp++ = JS_DupValue(ctx, arg_buf[1]); BREAK;
+ CASE(OP_get_arg2): *sp++ = JS_DupValue(ctx, arg_buf[2]); BREAK;
+ CASE(OP_get_arg3): *sp++ = JS_DupValue(ctx, arg_buf[3]); BREAK;
+ CASE(OP_put_arg0): set_value(ctx, &arg_buf[0], *--sp); BREAK;
+ CASE(OP_put_arg1): set_value(ctx, &arg_buf[1], *--sp); BREAK;
+ CASE(OP_put_arg2): set_value(ctx, &arg_buf[2], *--sp); BREAK;
+ CASE(OP_put_arg3): set_value(ctx, &arg_buf[3], *--sp); BREAK;
+ CASE(OP_set_arg0): set_value(ctx, &arg_buf[0], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_arg1): set_value(ctx, &arg_buf[1], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_arg2): set_value(ctx, &arg_buf[2], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_arg3): set_value(ctx, &arg_buf[3], JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_get_var_ref0): *sp++ = JS_DupValue(ctx, *var_refs[0]->pvalue); BREAK;
+ CASE(OP_get_var_ref1): *sp++ = JS_DupValue(ctx, *var_refs[1]->pvalue); BREAK;
+ CASE(OP_get_var_ref2): *sp++ = JS_DupValue(ctx, *var_refs[2]->pvalue); BREAK;
+ CASE(OP_get_var_ref3): *sp++ = JS_DupValue(ctx, *var_refs[3]->pvalue); BREAK;
+ CASE(OP_put_var_ref0): set_value(ctx, var_refs[0]->pvalue, *--sp); BREAK;
+ CASE(OP_put_var_ref1): set_value(ctx, var_refs[1]->pvalue, *--sp); BREAK;
+ CASE(OP_put_var_ref2): set_value(ctx, var_refs[2]->pvalue, *--sp); BREAK;
+ CASE(OP_put_var_ref3): set_value(ctx, var_refs[3]->pvalue, *--sp); BREAK;
+ CASE(OP_set_var_ref0): set_value(ctx, var_refs[0]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_var_ref1): set_value(ctx, var_refs[1]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_var_ref2): set_value(ctx, var_refs[2]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
+ CASE(OP_set_var_ref3): set_value(ctx, var_refs[3]->pvalue, JS_DupValue(ctx, sp[-1])); BREAK;
+#endif
+
+ CASE(OP_get_var_ref):
+ {
+ int idx;
+ JSValue val;
+ idx = get_u16(pc);
+ pc += 2;
+ val = *var_refs[idx]->pvalue;
+ sp[0] = JS_DupValue(ctx, val);
+ sp++;
+ }
+ BREAK;
+ CASE(OP_put_var_ref):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_set_var_ref):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, var_refs[idx]->pvalue, JS_DupValue(ctx, sp[-1]));
+ }
+ BREAK;
+ CASE(OP_get_var_ref_check):
+ {
+ int idx;
+ JSValue val;
+ idx = get_u16(pc);
+ pc += 2;
+ val = *var_refs[idx]->pvalue;
+ if (unlikely(JS_IsUninitialized(val))) {
+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
+ goto exception;
+ }
+ sp[0] = JS_DupValue(ctx, val);
+ sp++;
+ }
+ BREAK;
+ CASE(OP_put_var_ref_check):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(JS_IsUninitialized(*var_refs[idx]->pvalue))) {
+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
+ goto exception;
+ }
+ set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_put_var_ref_check_init):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(!JS_IsUninitialized(*var_refs[idx]->pvalue))) {
+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, TRUE);
+ goto exception;
+ }
+ set_value(ctx, var_refs[idx]->pvalue, sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_set_loc_uninitialized):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ set_value(ctx, &var_buf[idx], JS_UNINITIALIZED);
+ }
+ BREAK;
+ CASE(OP_get_loc_check):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
+ goto exception;
+ }
+ sp[0] = JS_DupValue(ctx, var_buf[idx]);
+ sp++;
+ }
+ BREAK;
+ CASE(OP_put_loc_check):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(JS_IsUninitialized(var_buf[idx]))) {
+ JS_ThrowReferenceErrorUninitialized2(ctx, b, idx, FALSE);
+ goto exception;
+ }
+ set_value(ctx, &var_buf[idx], sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_put_loc_check_init):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ if (unlikely(!JS_IsUninitialized(var_buf[idx]))) {
+ JS_ThrowReferenceError(ctx, "'this' can be initialized only once");
+ goto exception;
+ }
+ set_value(ctx, &var_buf[idx], sp[-1]);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_close_loc):
+ {
+ int idx;
+ idx = get_u16(pc);
+ pc += 2;
+ close_lexical_var(ctx, sf, idx, FALSE);
+ }
+ BREAK;
+
+ CASE(OP_make_loc_ref):
+ CASE(OP_make_arg_ref):
+ CASE(OP_make_var_ref_ref):
+ {
+ JSVarRef *var_ref;
+ JSProperty *pr;
+ JSAtom atom;
+ int idx;
+ atom = get_u32(pc);
+ idx = get_u16(pc + 4);
+ pc += 6;
+ *sp++ = JS_NewObjectProto(ctx, JS_NULL);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ if (opcode == OP_make_var_ref_ref) {
+ var_ref = var_refs[idx];
+ var_ref->header.ref_count++;
+ } else {
+ var_ref = get_var_ref(ctx, sf, idx, opcode == OP_make_arg_ref);
+ if (!var_ref)
+ goto exception;
+ }
+ pr = add_property(ctx, JS_VALUE_GET_OBJ(sp[-1]), atom,
+ JS_PROP_WRITABLE | JS_PROP_VARREF);
+ if (!pr) {
+ free_var_ref(rt, var_ref);
+ goto exception;
+ }
+ pr->u.var_ref = var_ref;
+ *sp++ = JS_AtomToValue(ctx, atom);
+ }
+ BREAK;
+ CASE(OP_make_var_ref):
+ {
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ if (JS_GetGlobalVarRef(ctx, atom, sp))
+ goto exception;
+ sp += 2;
+ }
+ BREAK;
+
+ CASE(OP_goto):
+ pc += (int32_t)get_u32(pc);
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ BREAK;
+#if SHORT_OPCODES
+ CASE(OP_goto16):
+ pc += (int16_t)get_u16(pc);
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ BREAK;
+ CASE(OP_goto8):
+ pc += (int8_t)pc[0];
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ BREAK;
+#endif
+ CASE(OP_if_true):
+ {
+ int res;
+ JSValue op1;
+
+ op1 = sp[-1];
+ pc += 4;
+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
+ res = JS_VALUE_GET_INT(op1);
+ } else {
+ res = JS_ToBoolFree(ctx, op1);
+ }
+ sp--;
+ if (res) {
+ pc += (int32_t)get_u32(pc - 4) - 4;
+ }
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_if_false):
+ {
+ int res;
+ JSValue op1;
+
+ op1 = sp[-1];
+ pc += 4;
+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
+ res = JS_VALUE_GET_INT(op1);
+ } else {
+ res = JS_ToBoolFree(ctx, op1);
+ }
+ sp--;
+ if (!res) {
+ pc += (int32_t)get_u32(pc - 4) - 4;
+ }
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ }
+ BREAK;
+#if SHORT_OPCODES
+ CASE(OP_if_true8):
+ {
+ int res;
+ JSValue op1;
+
+ op1 = sp[-1];
+ pc += 1;
+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
+ res = JS_VALUE_GET_INT(op1);
+ } else {
+ res = JS_ToBoolFree(ctx, op1);
+ }
+ sp--;
+ if (res) {
+ pc += (int8_t)pc[-1] - 1;
+ }
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_if_false8):
+ {
+ int res;
+ JSValue op1;
+
+ op1 = sp[-1];
+ pc += 1;
+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
+ res = JS_VALUE_GET_INT(op1);
+ } else {
+ res = JS_ToBoolFree(ctx, op1);
+ }
+ sp--;
+ if (!res) {
+ pc += (int8_t)pc[-1] - 1;
+ }
+ if (unlikely(js_poll_interrupts(ctx)))
+ goto exception;
+ }
+ BREAK;
+#endif
+ CASE(OP_catch):
+ {
+ int32_t diff;
+ diff = get_u32(pc);
+ sp[0] = JS_NewCatchOffset(ctx, pc + diff - b->byte_code_buf);
+ sp++;
+ pc += 4;
+ }
+ BREAK;
+ CASE(OP_gosub):
+ {
+ int32_t diff;
+ diff = get_u32(pc);
+ /* XXX: should have a different tag to avoid security flaw */
+ sp[0] = JS_NewInt32(ctx, pc + 4 - b->byte_code_buf);
+ sp++;
+ pc += diff;
+ }
+ BREAK;
+ CASE(OP_ret):
+ {
+ JSValue op1;
+ uint32_t pos;
+ op1 = sp[-1];
+ if (unlikely(JS_VALUE_GET_TAG(op1) != JS_TAG_INT))
+ goto ret_fail;
+ pos = JS_VALUE_GET_INT(op1);
+ if (unlikely(pos >= b->byte_code_len)) {
+ ret_fail:
+ JS_ThrowInternalError(ctx, "invalid ret value");
+ goto exception;
+ }
+ sp--;
+ pc = b->byte_code_buf + pos;
+ }
+ BREAK;
+
+ CASE(OP_for_in_start):
+ if (js_for_in_start(ctx, sp))
+ goto exception;
+ BREAK;
+ CASE(OP_for_in_next):
+ if (js_for_in_next(ctx, sp))
+ goto exception;
+ sp += 2;
+ BREAK;
+ CASE(OP_for_of_start):
+ if (js_for_of_start(ctx, sp, FALSE))
+ goto exception;
+ sp += 1;
+ *sp++ = JS_NewCatchOffset(ctx, 0);
+ BREAK;
+ CASE(OP_for_of_next):
+ {
+ int offset = -3 - pc[0];
+ pc += 1;
+ if (js_for_of_next(ctx, sp, offset))
+ goto exception;
+ sp += 2;
+ }
+ BREAK;
+ CASE(OP_for_await_of_start):
+ if (js_for_of_start(ctx, sp, TRUE))
+ goto exception;
+ sp += 1;
+ *sp++ = JS_NewCatchOffset(ctx, 0);
+ BREAK;
+ CASE(OP_iterator_get_value_done):
+ if (js_iterator_get_value_done(ctx, sp))
+ goto exception;
+ sp += 1;
+ BREAK;
+ CASE(OP_iterator_check_object):
+ if (unlikely(!JS_IsObject(sp[-1]))) {
+ JS_ThrowTypeError(ctx, "iterator must return an object");
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_iterator_close):
+ /* iter_obj next catch_offset -> */
+ sp--; /* drop the catch offset to avoid getting caught by exception */
+ JS_FreeValue(ctx, sp[-1]); /* drop the next method */
+ sp--;
+ if (!JS_IsUndefined(sp[-1])) {
+ if (JS_IteratorClose(ctx, sp[-1], FALSE))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ }
+ sp--;
+ BREAK;
+ CASE(OP_iterator_close_return):
+ {
+ JSValue ret_val;
+ /* iter_obj next catch_offset ... ret_val ->
+ ret_eval iter_obj next catch_offset */
+ ret_val = *--sp;
+ while (sp > stack_buf &&
+ JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_CATCH_OFFSET) {
+ JS_FreeValue(ctx, *--sp);
+ }
+ if (unlikely(sp < stack_buf + 3)) {
+ JS_ThrowInternalError(ctx, "iterator_close_return");
+ JS_FreeValue(ctx, ret_val);
+ goto exception;
+ }
+ sp[0] = sp[-1];
+ sp[-1] = sp[-2];
+ sp[-2] = sp[-3];
+ sp[-3] = ret_val;
+ sp++;
+ }
+ BREAK;
+
+ CASE(OP_iterator_next):
+ /* stack: iter_obj next catch_offset val */
+ {
+ JSValue ret;
+ ret = JS_Call(ctx, sp[-3], sp[-4],
+ 1, (sp - 1));
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret;
+ }
+ BREAK;
+
+ CASE(OP_iterator_call):
+ /* stack: iter_obj next catch_offset val */
+ {
+ JSValue method, ret;
+ BOOL ret_flag;
+ int flags;
+ flags = *pc++;
+ method = JS_GetProperty(ctx, sp[-4], (flags & 1) ?
+ JS_ATOM_throw : JS_ATOM_return);
+ if (JS_IsException(method))
+ goto exception;
+ if (JS_IsUndefined(method) || JS_IsNull(method)) {
+ ret_flag = TRUE;
+ } else {
+ if (flags & 2) {
+ /* no argument */
+ ret = JS_CallFree(ctx, method, sp[-4],
+ 0, NULL);
+ } else {
+ ret = JS_CallFree(ctx, method, sp[-4],
+ 1, (sp - 1));
+ }
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret;
+ ret_flag = FALSE;
+ }
+ sp[0] = JS_NewBool(ctx, ret_flag);
+ sp += 1;
+ }
+ BREAK;
+
+ CASE(OP_lnot):
+ {
+ int res;
+ JSValue op1;
+
+ op1 = sp[-1];
+ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) {
+ res = JS_VALUE_GET_INT(op1) != 0;
+ } else {
+ res = JS_ToBoolFree(ctx, op1);
+ }
+ sp[-1] = JS_NewBool(ctx, !res);
+ }
+ BREAK;
+
+ CASE(OP_get_field):
+ {
+ JSValue val;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ val = JS_GetProperty(ctx, sp[-1], atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = val;
+ }
+ BREAK;
+
+ CASE(OP_get_field2):
+ {
+ JSValue val;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ val = JS_GetProperty(ctx, sp[-1], atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ *sp++ = val;
+ }
+ BREAK;
+
+ CASE(OP_put_field):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1],
+ JS_PROP_THROW_STRICT);
+ JS_FreeValue(ctx, sp[-2]);
+ sp -= 2;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_private_symbol):
+ {
+ JSAtom atom;
+ JSValue val;
+
+ atom = get_u32(pc);
+ pc += 4;
+ val = JS_NewSymbolFromAtom(ctx, atom, JS_ATOM_TYPE_PRIVATE);
+ if (JS_IsException(val))
+ goto exception;
+ *sp++ = val;
+ }
+ BREAK;
+
+ CASE(OP_get_private_field):
+ {
+ JSValue val;
+
+ val = JS_GetPrivateField(ctx, sp[-2], sp[-1]);
+ JS_FreeValue(ctx, sp[-1]);
+ JS_FreeValue(ctx, sp[-2]);
+ sp[-2] = val;
+ sp--;
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_put_private_field):
+ {
+ int ret;
+ ret = JS_SetPrivateField(ctx, sp[-3], sp[-1], sp[-2]);
+ JS_FreeValue(ctx, sp[-3]);
+ JS_FreeValue(ctx, sp[-1]);
+ sp -= 3;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_define_private_field):
+ {
+ int ret;
+ ret = JS_DefinePrivateField(ctx, sp[-3], sp[-2], sp[-1]);
+ JS_FreeValue(ctx, sp[-2]);
+ sp -= 2;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_define_field):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_DefinePropertyValue(ctx, sp[-2], atom, sp[-1],
+ JS_PROP_C_W_E | JS_PROP_THROW);
+ sp--;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_set_name):
+ {
+ int ret;
+ JSAtom atom;
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_DefineObjectName(ctx, sp[-1], atom, JS_PROP_CONFIGURABLE);
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_set_name_computed):
+ {
+ int ret;
+ ret = JS_DefineObjectNameComputed(ctx, sp[-1], sp[-2], JS_PROP_CONFIGURABLE);
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+ CASE(OP_set_proto):
+ {
+ JSValue proto;
+ proto = sp[-1];
+ if (JS_IsObject(proto) || JS_IsNull(proto)) {
+ if (JS_SetPrototypeInternal(ctx, sp[-2], proto, TRUE) < 0)
+ goto exception;
+ }
+ JS_FreeValue(ctx, proto);
+ sp--;
+ }
+ BREAK;
+ CASE(OP_set_home_object):
+ js_method_set_home_object(ctx, sp[-1], sp[-2]);
+ BREAK;
+ CASE(OP_define_method):
+ CASE(OP_define_method_computed):
+ {
+ JSValue getter, setter, value;
+ JSValueConst obj;
+ JSAtom atom;
+ int flags, ret, op_flags;
+ BOOL is_computed;
+#define OP_DEFINE_METHOD_METHOD 0
+#define OP_DEFINE_METHOD_GETTER 1
+#define OP_DEFINE_METHOD_SETTER 2
+#define OP_DEFINE_METHOD_ENUMERABLE 4
+
+ is_computed = (opcode == OP_define_method_computed);
+ if (is_computed) {
+ atom = JS_ValueToAtom(ctx, sp[-2]);
+ if (unlikely(atom == JS_ATOM_NULL))
+ goto exception;
+ opcode += OP_define_method - OP_define_method_computed;
+ } else {
+ atom = get_u32(pc);
+ pc += 4;
+ }
+ op_flags = *pc++;
+
+ obj = sp[-2 - is_computed];
+ flags = JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE |
+ JS_PROP_HAS_ENUMERABLE | JS_PROP_THROW;
+ if (op_flags & OP_DEFINE_METHOD_ENUMERABLE)
+ flags |= JS_PROP_ENUMERABLE;
+ op_flags &= 3;
+ value = JS_UNDEFINED;
+ getter = JS_UNDEFINED;
+ setter = JS_UNDEFINED;
+ if (op_flags == OP_DEFINE_METHOD_METHOD) {
+ value = sp[-1];
+ flags |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE | JS_PROP_WRITABLE;
+ } else if (op_flags == OP_DEFINE_METHOD_GETTER) {
+ getter = sp[-1];
+ flags |= JS_PROP_HAS_GET;
+ } else {
+ setter = sp[-1];
+ flags |= JS_PROP_HAS_SET;
+ }
+ ret = js_method_set_properties(ctx, sp[-1], atom, flags, obj);
+ if (ret >= 0) {
+ ret = JS_DefineProperty(ctx, obj, atom, value,
+ getter, setter, flags);
+ }
+ JS_FreeValue(ctx, sp[-1]);
+ if (is_computed) {
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, sp[-2]);
+ }
+ sp -= 1 + is_computed;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_define_class):
+ CASE(OP_define_class_computed):
+ {
+ int class_flags;
+ JSAtom atom;
+
+ atom = get_u32(pc);
+ class_flags = pc[4];
+ pc += 5;
+ if (js_op_define_class(ctx, sp, atom, class_flags,
+ var_refs, sf,
+ (opcode == OP_define_class_computed)) < 0)
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_get_array_el):
+ {
+ JSValue val;
+
+ val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
+ JS_FreeValue(ctx, sp[-2]);
+ sp[-2] = val;
+ sp--;
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_get_array_el2):
+ {
+ JSValue val;
+
+ val = JS_GetPropertyValue(ctx, sp[-2], sp[-1]);
+ sp[-1] = val;
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_get_ref_value):
+ {
+ JSValue val;
+ if (unlikely(JS_IsUndefined(sp[-2]))) {
+ JSAtom atom = JS_ValueToAtom(ctx, sp[-1]);
+ if (atom != JS_ATOM_NULL) {
+ JS_ThrowReferenceErrorNotDefined(ctx, atom);
+ JS_FreeAtom(ctx, atom);
+ }
+ goto exception;
+ }
+ val = JS_GetPropertyValue(ctx, sp[-2],
+ JS_DupValue(ctx, sp[-1]));
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ sp[0] = val;
+ sp++;
+ }
+ BREAK;
+
+ CASE(OP_get_super_value):
+ {
+ JSValue val;
+ JSAtom atom;
+ atom = JS_ValueToAtom(ctx, sp[-1]);
+ if (unlikely(atom == JS_ATOM_NULL))
+ goto exception;
+ val = JS_GetPropertyInternal(ctx, sp[-2], atom, sp[-3], FALSE);
+ JS_FreeAtom(ctx, atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ JS_FreeValue(ctx, sp[-2]);
+ JS_FreeValue(ctx, sp[-3]);
+ sp[-3] = val;
+ sp -= 2;
+ }
+ BREAK;
+
+ CASE(OP_put_array_el):
+ {
+ int ret;
+
+ ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], JS_PROP_THROW_STRICT);
+ JS_FreeValue(ctx, sp[-3]);
+ sp -= 3;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_put_ref_value):
+ {
+ int ret, flags;
+ flags = JS_PROP_THROW_STRICT;
+ if (unlikely(JS_IsUndefined(sp[-3]))) {
+ if (is_strict_mode(ctx)) {
+ JSAtom atom = JS_ValueToAtom(ctx, sp[-2]);
+ if (atom != JS_ATOM_NULL) {
+ JS_ThrowReferenceErrorNotDefined(ctx, atom);
+ JS_FreeAtom(ctx, atom);
+ }
+ goto exception;
+ } else {
+ sp[-3] = JS_DupValue(ctx, ctx->global_obj);
+ }
+ } else {
+ if (is_strict_mode(ctx))
+ flags |= JS_PROP_NO_ADD;
+ }
+ ret = JS_SetPropertyValue(ctx, sp[-3], sp[-2], sp[-1], flags);
+ JS_FreeValue(ctx, sp[-3]);
+ sp -= 3;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_put_super_value):
+ {
+ int ret;
+ JSAtom atom;
+ if (JS_VALUE_GET_TAG(sp[-3]) != JS_TAG_OBJECT) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ goto exception;
+ }
+ atom = JS_ValueToAtom(ctx, sp[-2]);
+ if (unlikely(atom == JS_ATOM_NULL))
+ goto exception;
+ ret = JS_SetPropertyGeneric(ctx, sp[-3], atom, sp[-1], sp[-4],
+ JS_PROP_THROW_STRICT);
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, sp[-4]);
+ JS_FreeValue(ctx, sp[-3]);
+ JS_FreeValue(ctx, sp[-2]);
+ sp -= 4;
+ if (ret < 0)
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_define_array_el):
+ {
+ int ret;
+ ret = JS_DefinePropertyValueValue(ctx, sp[-3], JS_DupValue(ctx, sp[-2]), sp[-1],
+ JS_PROP_C_W_E | JS_PROP_THROW);
+ sp -= 1;
+ if (unlikely(ret < 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_append): /* array pos enumobj -- array pos */
+ {
+ if (js_append_enumerate(ctx, sp))
+ goto exception;
+ JS_FreeValue(ctx, *--sp);
+ }
+ BREAK;
+
+ CASE(OP_copy_data_properties): /* target source excludeList */
+ {
+ /* stack offsets (-1 based):
+ 2 bits for target,
+ 3 bits for source,
+ 2 bits for exclusionList */
+ int mask;
+
+ mask = *pc++;
+ if (JS_CopyDataProperties(ctx, sp[-1 - (mask & 3)],
+ sp[-1 - ((mask >> 2) & 7)],
+ sp[-1 - ((mask >> 5) & 7)], 0))
+ goto exception;
+ }
+ BREAK;
+
+ CASE(OP_add):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ int64_t r;
+ r = (int64_t)JS_VALUE_GET_INT(op1) + JS_VALUE_GET_INT(op2);
+ if (unlikely((int)r != r))
+ goto add_slow;
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+ sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) +
+ JS_VALUE_GET_FLOAT64(op2));
+ sp--;
+ } else {
+ add_slow:
+ if (js_add_slow(ctx, sp))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_add_loc):
+ {
+ JSValue *pv;
+ int idx;
+ idx = *pc;
+ pc += 1;
+
+ pv = &var_buf[idx];
+ if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) {
+ int64_t r;
+ r = (int64_t)JS_VALUE_GET_INT(*pv) +
+ JS_VALUE_GET_INT(sp[-1]);
+ if (unlikely((int)r != r))
+ goto add_loc_slow;
+ *pv = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) {
+ JSValue op1;
+ op1 = sp[-1];
+ sp--;
+ op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE);
+ if (JS_IsException(op1))
+ goto exception;
+ op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1);
+ if (JS_IsException(op1))
+ goto exception;
+ set_value(ctx, pv, op1);
+ } else {
+ JSValue ops[2];
+ add_loc_slow:
+ /* In case of exception, js_add_slow frees ops[0]
+ and ops[1], so we must duplicate *pv */
+ ops[0] = JS_DupValue(ctx, *pv);
+ ops[1] = sp[-1];
+ sp--;
+ if (js_add_slow(ctx, ops + 2))
+ goto exception;
+ set_value(ctx, pv, ops[0]);
+ }
+ }
+ BREAK;
+ CASE(OP_sub):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ int64_t r;
+ r = (int64_t)JS_VALUE_GET_INT(op1) - JS_VALUE_GET_INT(op2);
+ if (unlikely((int)r != r))
+ goto binary_arith_slow;
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+ sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) -
+ JS_VALUE_GET_FLOAT64(op2));
+ sp--;
+ } else {
+ goto binary_arith_slow;
+ }
+ }
+ BREAK;
+ CASE(OP_mul):
+ {
+ JSValue op1, op2;
+ double d;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ int32_t v1, v2;
+ int64_t r;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+ r = (int64_t)v1 * v2;
+ if (unlikely((int)r != r)) {
+#ifdef CONFIG_BIGNUM
+ if (unlikely(sf->js_mode & JS_MODE_MATH) &&
+ (r < -MAX_SAFE_INTEGER || r > MAX_SAFE_INTEGER))
+ goto binary_arith_slow;
+#endif
+ d = (double)r;
+ goto mul_fp_res;
+ }
+ /* need to test zero case for -0 result */
+ if (unlikely(r == 0 && (v1 | v2) < 0)) {
+ d = -0.0;
+ goto mul_fp_res;
+ }
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+#ifdef CONFIG_BIGNUM
+ if (unlikely(sf->js_mode & JS_MODE_MATH))
+ goto binary_arith_slow;
+#endif
+ d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
+ mul_fp_res:
+ sp[-2] = JS_NewFloat64Impl(ctx, d);
+ sp--;
+ } else {
+ goto binary_arith_slow;
+ }
+ }
+ BREAK;
+ CASE(OP_div):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ int v1, v2;
+ if (unlikely(sf->js_mode & JS_MODE_MATH))
+ goto binary_arith_slow;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+ sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2);
+ sp--;
+ } else {
+ goto binary_arith_slow;
+ }
+ }
+ BREAK;
+ CASE(OP_mod):
+#ifdef CONFIG_BIGNUM
+ CASE(OP_math_mod):
+#endif
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ int v1, v2, r;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+ /* We must avoid v2 = 0, v1 = INT32_MIN and v2 =
+ -1 and the cases where the result is -0. */
+ if (unlikely(v1 < 0 || v2 <= 0))
+ goto binary_arith_slow;
+ r = v1 % v2;
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else {
+ goto binary_arith_slow;
+ }
+ }
+ BREAK;
+ CASE(OP_pow):
+ binary_arith_slow:
+ if (js_binary_arith_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ BREAK;
+
+ CASE(OP_plus):
+ {
+ JSValue op1;
+ uint32_t tag;
+ op1 = sp[-1];
+ tag = JS_VALUE_GET_TAG(op1);
+ if (tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag)) {
+ } else {
+ if (js_unary_arith_slow(ctx, sp, opcode))
+ goto exception;
+ }
+ }
+ BREAK;
+ CASE(OP_neg):
+ {
+ JSValue op1;
+ uint32_t tag;
+ int val;
+ double d;
+ op1 = sp[-1];
+ tag = JS_VALUE_GET_TAG(op1);
+ if (tag == JS_TAG_INT) {
+ val = JS_VALUE_GET_INT(op1);
+ /* Note: -0 cannot be expressed as integer */
+ if (unlikely(val == 0)) {
+ d = -0.0;
+ goto neg_fp_res;
+ }
+ if (unlikely(val == INT32_MIN)) {
+ d = -(double)val;
+ goto neg_fp_res;
+ }
+ sp[-1] = JS_NewInt32(ctx, -val);
+ } else if (JS_TAG_IS_FLOAT64(tag)) {
+ d = -JS_VALUE_GET_FLOAT64(op1);
+ neg_fp_res:
+ sp[-1] = JS_NewFloat64Impl(ctx, d);
+ } else {
+ if (js_unary_arith_slow(ctx, sp, opcode))
+ goto exception;
+ }
+ }
+ BREAK;
+ CASE(OP_inc):
+ {
+ JSValue op1;
+ int val;
+ op1 = sp[-1];
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
+ val = JS_VALUE_GET_INT(op1);
+ if (unlikely(val == INT32_MAX))
+ goto inc_slow;
+ sp[-1] = JS_NewInt32(ctx, val + 1);
+ } else {
+ inc_slow:
+ if (js_unary_arith_slow(ctx, sp, opcode))
+ goto exception;
+ }
+ }
+ BREAK;
+ CASE(OP_dec):
+ {
+ JSValue op1;
+ int val;
+ op1 = sp[-1];
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
+ val = JS_VALUE_GET_INT(op1);
+ if (unlikely(val == INT32_MIN))
+ goto dec_slow;
+ sp[-1] = JS_NewInt32(ctx, val - 1);
+ } else {
+ dec_slow:
+ if (js_unary_arith_slow(ctx, sp, opcode))
+ goto exception;
+ }
+ }
+ BREAK;
+ CASE(OP_post_inc):
+ CASE(OP_post_dec):
+ if (js_post_inc_slow(ctx, sp, opcode))
+ goto exception;
+ sp++;
+ BREAK;
+ CASE(OP_inc_loc):
+ {
+ JSValue op1;
+ int val;
+ int idx;
+ idx = *pc;
+ pc += 1;
+
+ op1 = var_buf[idx];
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
+ val = JS_VALUE_GET_INT(op1);
+ if (unlikely(val == INT32_MAX))
+ goto inc_loc_slow;
+ var_buf[idx] = JS_NewInt32(ctx, val + 1);
+ } else {
+ inc_loc_slow:
+ /* must duplicate otherwise the variable value may
+ be destroyed before JS code accesses it */
+ op1 = JS_DupValue(ctx, op1);
+ if (js_unary_arith_slow(ctx, &op1 + 1, OP_inc))
+ goto exception;
+ set_value(ctx, &var_buf[idx], op1);
+ }
+ }
+ BREAK;
+ CASE(OP_dec_loc):
+ {
+ JSValue op1;
+ int val;
+ int idx;
+ idx = *pc;
+ pc += 1;
+
+ op1 = var_buf[idx];
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
+ val = JS_VALUE_GET_INT(op1);
+ if (unlikely(val == INT32_MIN))
+ goto dec_loc_slow;
+ var_buf[idx] = JS_NewInt32(ctx, val - 1);
+ } else {
+ dec_loc_slow:
+ /* must duplicate otherwise the variable value may
+ be destroyed before JS code accesses it */
+ op1 = JS_DupValue(ctx, op1);
+ if (js_unary_arith_slow(ctx, &op1 + 1, OP_dec))
+ goto exception;
+ set_value(ctx, &var_buf[idx], op1);
+ }
+ }
+ BREAK;
+ CASE(OP_not):
+ {
+ JSValue op1;
+ op1 = sp[-1];
+ if (JS_VALUE_GET_TAG(op1) == JS_TAG_INT) {
+ sp[-1] = JS_NewInt32(ctx, ~JS_VALUE_GET_INT(op1));
+ } else {
+ if (js_not_slow(ctx, sp))
+ goto exception;
+ }
+ }
+ BREAK;
+
+ CASE(OP_shl):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ uint32_t v1, v2;
+ v1 = JS_VALUE_GET_INT(op1);
+ v2 = JS_VALUE_GET_INT(op2);
+#ifdef CONFIG_BIGNUM
+ {
+ int64_t r;
+ if (unlikely(sf->js_mode & JS_MODE_MATH)) {
+ if (v2 > 0x1f)
+ goto shl_slow;
+ r = (int64_t)v1 << v2;
+ if ((int)r != r)
+ goto shl_slow;
+ } else {
+ v2 &= 0x1f;
+ }
+ }
+#else
+ v2 &= 0x1f;
+#endif
+ sp[-2] = JS_NewInt32(ctx, v1 << v2);
+ sp--;
+ } else {
+#ifdef CONFIG_BIGNUM
+ shl_slow:
+#endif
+ if (js_binary_logic_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_shr):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ uint32_t v2;
+ v2 = JS_VALUE_GET_INT(op2);
+ /* v1 >>> v2 retains its JS semantics if CONFIG_BIGNUM */
+ v2 &= 0x1f;
+ sp[-2] = JS_NewUint32(ctx,
+ (uint32_t)JS_VALUE_GET_INT(op1) >>
+ v2);
+ sp--;
+ } else {
+ if (js_shr_slow(ctx, sp))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_sar):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ uint32_t v2;
+ v2 = JS_VALUE_GET_INT(op2);
+#ifdef CONFIG_BIGNUM
+ if (unlikely(v2 > 0x1f)) {
+ if (unlikely(sf->js_mode & JS_MODE_MATH))
+ goto sar_slow;
+ else
+ v2 &= 0x1f;
+ }
+#else
+ v2 &= 0x1f;
+#endif
+ sp[-2] = JS_NewInt32(ctx,
+ (int)JS_VALUE_GET_INT(op1) >> v2);
+ sp--;
+ } else {
+#ifdef CONFIG_BIGNUM
+ sar_slow:
+#endif
+ if (js_binary_logic_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_and):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ sp[-2] = JS_NewInt32(ctx,
+ JS_VALUE_GET_INT(op1) &
+ JS_VALUE_GET_INT(op2));
+ sp--;
+ } else {
+ if (js_binary_logic_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_or):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ sp[-2] = JS_NewInt32(ctx,
+ JS_VALUE_GET_INT(op1) |
+ JS_VALUE_GET_INT(op2));
+ sp--;
+ } else {
+ if (js_binary_logic_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+ CASE(OP_xor):
+ {
+ JSValue op1, op2;
+ op1 = sp[-2];
+ op2 = sp[-1];
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) {
+ sp[-2] = JS_NewInt32(ctx,
+ JS_VALUE_GET_INT(op1) ^
+ JS_VALUE_GET_INT(op2));
+ sp--;
+ } else {
+ if (js_binary_logic_slow(ctx, sp, opcode))
+ goto exception;
+ sp--;
+ }
+ }
+ BREAK;
+
+
+#define OP_CMP(opcode, binary_op, slow_call) \
+ CASE(opcode): \
+ { \
+ JSValue op1, op2; \
+ op1 = sp[-2]; \
+ op2 = sp[-1]; \
+ if (likely(JS_VALUE_IS_BOTH_INT(op1, op2))) { \
+ sp[-2] = JS_NewBool(ctx, JS_VALUE_GET_INT(op1) binary_op JS_VALUE_GET_INT(op2)); \
+ sp--; \
+ } else { \
+ if (slow_call) \
+ goto exception; \
+ sp--; \
+ } \
+ } \
+ BREAK
+
+ OP_CMP(OP_lt, <, js_relational_slow(ctx, sp, opcode));
+ OP_CMP(OP_lte, <=, js_relational_slow(ctx, sp, opcode));
+ OP_CMP(OP_gt, >, js_relational_slow(ctx, sp, opcode));
+ OP_CMP(OP_gte, >=, js_relational_slow(ctx, sp, opcode));
+ OP_CMP(OP_eq, ==, js_eq_slow(ctx, sp, 0));
+ OP_CMP(OP_neq, !=, js_eq_slow(ctx, sp, 1));
+ OP_CMP(OP_strict_eq, ==, js_strict_eq_slow(ctx, sp, 0));
+ OP_CMP(OP_strict_neq, !=, js_strict_eq_slow(ctx, sp, 1));
+
+#ifdef CONFIG_BIGNUM
+ CASE(OP_mul_pow10):
+ if (rt->bigfloat_ops.mul_pow10(ctx, sp))
+ goto exception;
+ sp--;
+ BREAK;
+#endif
+ CASE(OP_in):
+ if (js_operator_in(ctx, sp))
+ goto exception;
+ sp--;
+ BREAK;
+ CASE(OP_instanceof):
+ if (js_operator_instanceof(ctx, sp))
+ goto exception;
+ sp--;
+ BREAK;
+ CASE(OP_typeof):
+ {
+ JSValue op1;
+ JSAtom atom;
+
+ op1 = sp[-1];
+ atom = js_operator_typeof(ctx, op1);
+ JS_FreeValue(ctx, op1);
+ sp[-1] = JS_AtomToString(ctx, atom);
+ }
+ BREAK;
+ CASE(OP_delete):
+ if (js_operator_delete(ctx, sp))
+ goto exception;
+ sp--;
+ BREAK;
+ CASE(OP_delete_var):
+ {
+ JSAtom atom;
+ int ret;
+
+ atom = get_u32(pc);
+ pc += 4;
+
+ ret = JS_DeleteProperty(ctx, ctx->global_obj, atom, 0);
+ if (unlikely(ret < 0))
+ goto exception;
+ *sp++ = JS_NewBool(ctx, ret);
+ }
+ BREAK;
+
+ CASE(OP_to_object):
+ if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_OBJECT) {
+ ret_val = JS_ToObject(ctx, sp[-1]);
+ if (JS_IsException(ret_val))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret_val;
+ }
+ BREAK;
+
+ CASE(OP_to_propkey):
+ switch (JS_VALUE_GET_TAG(sp[-1])) {
+ case JS_TAG_INT:
+ case JS_TAG_STRING:
+ case JS_TAG_SYMBOL:
+ break;
+ default:
+ ret_val = JS_ToPropertyKey(ctx, sp[-1]);
+ if (JS_IsException(ret_val))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret_val;
+ break;
+ }
+ BREAK;
+
+ CASE(OP_to_propkey2):
+ /* must be tested first */
+ if (unlikely(JS_IsUndefined(sp[-2]) || JS_IsNull(sp[-2]))) {
+ JS_ThrowTypeError(ctx, "value has no property");
+ goto exception;
+ }
+ switch (JS_VALUE_GET_TAG(sp[-1])) {
+ case JS_TAG_INT:
+ case JS_TAG_STRING:
+ case JS_TAG_SYMBOL:
+ break;
+ default:
+ ret_val = JS_ToPropertyKey(ctx, sp[-1]);
+ if (JS_IsException(ret_val))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret_val;
+ break;
+ }
+ BREAK;
+#if 0
+ CASE(OP_to_string):
+ if (JS_VALUE_GET_TAG(sp[-1]) != JS_TAG_STRING) {
+ ret_val = JS_ToString(ctx, sp[-1]);
+ if (JS_IsException(ret_val))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = ret_val;
+ }
+ BREAK;
+#endif
+ CASE(OP_with_get_var):
+ CASE(OP_with_put_var):
+ CASE(OP_with_delete_var):
+ CASE(OP_with_make_ref):
+ CASE(OP_with_get_ref):
+ CASE(OP_with_get_ref_undef):
+ {
+ JSAtom atom;
+ int32_t diff;
+ JSValue obj, val;
+ int ret, is_with;
+ atom = get_u32(pc);
+ diff = get_u32(pc + 4);
+ is_with = pc[8];
+ pc += 9;
+
+ obj = sp[-1];
+ ret = JS_HasProperty(ctx, obj, atom);
+ if (unlikely(ret < 0))
+ goto exception;
+ if (ret) {
+ if (is_with) {
+ ret = js_has_unscopable(ctx, obj, atom);
+ if (unlikely(ret < 0))
+ goto exception;
+ if (ret)
+ goto no_with;
+ }
+ switch (opcode) {
+ case OP_with_get_var:
+ val = JS_GetProperty(ctx, obj, atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ set_value(ctx, &sp[-1], val);
+ break;
+ case OP_with_put_var:
+ /* XXX: check if strict mode */
+ ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2],
+ JS_PROP_THROW_STRICT);
+ JS_FreeValue(ctx, sp[-1]);
+ sp -= 2;
+ if (unlikely(ret < 0))
+ goto exception;
+ break;
+ case OP_with_delete_var:
+ ret = JS_DeleteProperty(ctx, obj, atom, 0);
+ if (unlikely(ret < 0))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = JS_NewBool(ctx, ret);
+ break;
+ case OP_with_make_ref:
+ /* produce a pair object/propname on the stack */
+ *sp++ = JS_AtomToValue(ctx, atom);
+ break;
+ case OP_with_get_ref:
+ /* produce a pair object/method on the stack */
+ val = JS_GetProperty(ctx, obj, atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ *sp++ = val;
+ break;
+ case OP_with_get_ref_undef:
+ /* produce a pair undefined/function on the stack */
+ val = JS_GetProperty(ctx, obj, atom);
+ if (unlikely(JS_IsException(val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = JS_UNDEFINED;
+ *sp++ = val;
+ break;
+ }
+ pc += diff - 5;
+ } else {
+ no_with:
+ /* if not jumping, drop the object argument */
+ JS_FreeValue(ctx, sp[-1]);
+ sp--;
+ }
+ }
+ BREAK;
+
+ CASE(OP_await):
+ ret_val = JS_NewInt32(ctx, FUNC_RET_AWAIT);
+ goto done_generator;
+ CASE(OP_yield):
+ ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD);
+ goto done_generator;
+ CASE(OP_yield_star):
+ CASE(OP_async_yield_star):
+ ret_val = JS_NewInt32(ctx, FUNC_RET_YIELD_STAR);
+ goto done_generator;
+ CASE(OP_return_async):
+ CASE(OP_initial_yield):
+ ret_val = JS_UNDEFINED;
+ goto done_generator;
+
+ CASE(OP_nop):
+ BREAK;
+ CASE(OP_is_undefined_or_null):
+ if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED ||
+ JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
+ goto set_true;
+ } else {
+ goto free_and_set_false;
+ }
+#if SHORT_OPCODES
+ CASE(OP_is_undefined):
+ if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_UNDEFINED) {
+ goto set_true;
+ } else {
+ goto free_and_set_false;
+ }
+ CASE(OP_is_null):
+ if (JS_VALUE_GET_TAG(sp[-1]) == JS_TAG_NULL) {
+ goto set_true;
+ } else {
+ goto free_and_set_false;
+ }
+ /* XXX: could merge to a single opcode */
+ CASE(OP_typeof_is_undefined):
+ /* different from OP_is_undefined because of isHTMLDDA */
+ if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_undefined) {
+ goto free_and_set_true;
+ } else {
+ goto free_and_set_false;
+ }
+ CASE(OP_typeof_is_function):
+ if (js_operator_typeof(ctx, sp[-1]) == JS_ATOM_function) {
+ goto free_and_set_true;
+ } else {
+ goto free_and_set_false;
+ }
+ free_and_set_true:
+ JS_FreeValue(ctx, sp[-1]);
+#endif
+ set_true:
+ sp[-1] = JS_TRUE;
+ BREAK;
+ free_and_set_false:
+ JS_FreeValue(ctx, sp[-1]);
+ sp[-1] = JS_FALSE;
+ BREAK;
+ CASE(OP_invalid):
+ DEFAULT:
+ JS_ThrowInternalError(ctx, "invalid opcode: pc=%u opcode=0x%02x",
+ (int)(pc - b->byte_code_buf - 1), opcode);
+ goto exception;
+ }
+ }
+ exception:
+ if (JS_IsString(rt->current_exception)) {
+ JSValue error_obj = JS_NewError(ctx);
+ JSAtom msgProp = JS_NewAtom(ctx, "message");
+ JS_DefinePropertyValue(ctx, error_obj, msgProp, rt->current_exception, 0);
+ rt->current_exception = error_obj;
+ JS_FreeAtom(ctx, msgProp);
+ }
+ if (is_backtrace_needed(ctx, rt->current_exception)) {
+ /* add the backtrace information now (it is not done
+ before if the exception happens in a bytecode
+ operation */
+ sf->cur_pc = pc;
+ build_backtrace(ctx, rt->current_exception, NULL, 0, 0);
+ }
+ if (!JS_IsUncatchableError(ctx, rt->current_exception)) {
+ while (sp > stack_buf) {
+ JSValue val = *--sp;
+ JS_FreeValue(ctx, val);
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_CATCH_OFFSET) {
+ int pos = JS_VALUE_GET_INT(val);
+ if (pos == 0) {
+ /* enumerator: close it with a throw */
+ JS_FreeValue(ctx, sp[-1]); /* drop the next method */
+ sp--;
+ JS_IteratorClose(ctx, sp[-1], TRUE);
+ } else {
+ *sp++ = rt->current_exception;
+ rt->current_exception = JS_NULL;
+ pc = b->byte_code_buf + pos;
+ goto restart;
+ }
+ }
+ }
+ }
+ ret_val = JS_EXCEPTION;
+ /* the local variables are freed by the caller in the generator
+ case. Hence the label 'done' should never be reached in a
+ generator function. */
+ if (b->func_kind != JS_FUNC_NORMAL) {
+ done_generator:
+ sf->cur_pc = pc;
+ sf->cur_sp = sp;
+ } else {
+ done:
+ if (unlikely(!list_empty(&sf->var_ref_list))) {
+ /* variable references reference the stack: must close them */
+ close_var_refs(rt, sf);
+ }
+ /* free the local variables and stack */
+ for(pval = local_buf; pval < sp; pval++) {
+ JS_FreeValue(ctx, *pval);
+ }
+ }
+ rt->current_stack_frame = sf->prev_frame;
+ if (ctx->handleFunctionExited)
+ ctx->handleFunctionExited(ctx);
+ return ret_val;
+}
+
+JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
+ argc, argv, JS_CALL_FLAG_COPY_ARGV);
+}
+
+static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
+ argc, argv, JS_CALL_FLAG_COPY_ARGV);
+ JS_FreeValue(ctx, func_obj);
+ return res;
+}
+
+/* warning: the refcount of the context is not incremented. Return
+ NULL in case of exception (case of revoked proxy only) */
+static JSContext *JS_GetFunctionRealm(JSContext *ctx, JSValueConst func_obj)
+{
+ JSObject *p;
+ JSContext *realm;
+
+ if (JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT)
+ return ctx;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ switch(p->class_id) {
+ case JS_CLASS_C_FUNCTION:
+ realm = p->u.cfunc.realm;
+ break;
+ case JS_CLASS_BYTECODE_FUNCTION:
+ case JS_CLASS_GENERATOR_FUNCTION:
+ case JS_CLASS_ASYNC_FUNCTION:
+ case JS_CLASS_ASYNC_GENERATOR_FUNCTION:
+ {
+ JSFunctionBytecode *b;
+ b = p->u.func.function_bytecode;
+ realm = b->realm;
+ }
+ break;
+ case JS_CLASS_PROXY:
+ {
+ JSProxyData *s = p->u.opaque;
+ if (!s)
+ return ctx;
+ if (s->is_revoked) {
+ JS_ThrowTypeErrorRevokedProxy(ctx);
+ return NULL;
+ } else {
+ realm = JS_GetFunctionRealm(ctx, s->target);
+ }
+ }
+ break;
+ case JS_CLASS_BOUND_FUNCTION:
+ {
+ JSBoundFunction *bf = p->u.bound_function;
+ realm = JS_GetFunctionRealm(ctx, bf->func_obj);
+ }
+ break;
+ default:
+ realm = ctx;
+ break;
+ }
+ return realm;
+}
+
+static JSValue js_create_from_ctor(JSContext *ctx, JSValueConst ctor,
+ int class_id)
+{
+ JSValue proto, obj;
+ JSContext *realm;
+
+ if (JS_IsUndefined(ctor)) {
+ proto = JS_DupValue(ctx, ctx->class_proto[class_id]);
+ } else {
+ proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
+ if (JS_IsException(proto))
+ return proto;
+ if (!JS_IsObject(proto)) {
+ JS_FreeValue(ctx, proto);
+ realm = JS_GetFunctionRealm(ctx, ctor);
+ if (!realm)
+ return JS_EXCEPTION;
+ proto = JS_DupValue(ctx, realm->class_proto[class_id]);
+ }
+ }
+ obj = JS_NewObjectProtoClass(ctx, proto, class_id);
+ JS_FreeValue(ctx, proto);
+ return obj;
+}
+
+/* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */
+static JSValue JS_CallConstructorInternal(JSContext *ctx,
+ JSValueConst func_obj,
+ JSValueConst new_target,
+ int argc, JSValue *argv, int flags)
+{
+ JSObject *p;
+ JSFunctionBytecode *b;
+
+ if (js_poll_interrupts(ctx))
+ return JS_EXCEPTION;
+ flags |= JS_CALL_FLAG_CONSTRUCTOR;
+ if (unlikely(JS_VALUE_GET_TAG(func_obj) != JS_TAG_OBJECT))
+ goto not_a_function;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ if (unlikely(!p->is_constructor))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ if (unlikely(p->class_id != JS_CLASS_BYTECODE_FUNCTION)) {
+ JSClassCall *call_func;
+ call_func = ctx->rt->class_array[p->class_id].call;
+ if (!call_func) {
+ not_a_function:
+ return JS_ThrowTypeError(ctx, "not a function");
+ }
+ return call_func(ctx, func_obj, new_target, argc,
+ argv, flags);
+ }
+
+ b = p->u.func.function_bytecode;
+ if (b->is_derived_class_constructor) {
+ return JS_CallInternal(ctx, func_obj, JS_UNDEFINED, new_target, argc, argv, flags);
+ } else {
+ JSValue obj, ret;
+ /* legacy constructor behavior */
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ ret = JS_CallInternal(ctx, func_obj, obj, new_target, argc, argv, flags);
+ if (JS_VALUE_GET_TAG(ret) == JS_TAG_OBJECT ||
+ JS_IsException(ret)) {
+ JS_FreeValue(ctx, obj);
+ return ret;
+ } else {
+ JS_FreeValue(ctx, ret);
+ return obj;
+ }
+ }
+}
+
+JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ return JS_CallConstructorInternal(ctx, func_obj, new_target,
+ argc, argv,
+ JS_CALL_FLAG_COPY_ARGV);
+}
+
+JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
+ int argc, JSValueConst *argv)
+{
+ return JS_CallConstructorInternal(ctx, func_obj, func_obj,
+ argc, argv,
+ JS_CALL_FLAG_COPY_ARGV);
+}
+
+JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
+ int argc, JSValueConst *argv)
+{
+ JSValue func_obj;
+ func_obj = JS_GetProperty(ctx, this_val, atom);
+ if (JS_IsException(func_obj))
+ return func_obj;
+ return JS_CallFree(ctx, func_obj, this_val, argc, argv);
+}
+
+static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
+ int argc, JSValueConst *argv)
+{
+ JSValue res = JS_Invoke(ctx, this_val, atom, argc, argv);
+ JS_FreeValue(ctx, this_val);
+ return res;
+}
+
+/* JSAsyncFunctionState (used by generator and async functions) */
+static warn_unused int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
+ JSValueConst func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ JSFunctionBytecode *b;
+ JSStackFrame *sf;
+ int local_count, i, arg_buf_len, n;
+
+ sf = &s->frame;
+ init_list_head(&sf->var_ref_list);
+ p = JS_VALUE_GET_OBJ(func_obj);
+ b = p->u.func.function_bytecode;
+ sf->js_mode = b->js_mode;
+ sf->cur_pc = b->byte_code_buf;
+ arg_buf_len = max_int(b->arg_count, argc);
+ local_count = arg_buf_len + b->var_count + b->stack_size;
+ sf->arg_buf = js_malloc(ctx, sizeof(JSValue) * max_int(local_count, 1));
+ if (!sf->arg_buf)
+ return -1;
+ sf->cur_func = JS_DupValue(ctx, func_obj);
+ s->this_val = JS_DupValue(ctx, this_obj);
+ s->argc = argc;
+ sf->arg_count = arg_buf_len;
+ sf->var_buf = sf->arg_buf + arg_buf_len;
+ sf->cur_sp = sf->var_buf + b->var_count;
+ for(i = 0; i < argc; i++)
+ sf->arg_buf[i] = JS_DupValue(ctx, argv[i]);
+ n = arg_buf_len + b->var_count;
+ for(i = argc; i < n; i++)
+ sf->arg_buf[i] = JS_UNDEFINED;
+ return 0;
+}
+
+static void async_func_mark(JSRuntime *rt, JSAsyncFunctionState *s,
+ JS_MarkFunc *mark_func)
+{
+ JSStackFrame *sf;
+ JSValue *sp;
+
+ sf = &s->frame;
+ JS_MarkValue(rt, sf->cur_func, mark_func);
+ JS_MarkValue(rt, s->this_val, mark_func);
+ if (sf->cur_sp) {
+ /* if the function is running, cur_sp is not known so we
+ cannot mark the stack. Marking the variables is not needed
+ because a running function cannot be part of a removable
+ cycle */
+ for(sp = sf->arg_buf; sp < sf->cur_sp; sp++)
+ JS_MarkValue(rt, *sp, mark_func);
+ }
+}
+
+static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s)
+{
+ JSStackFrame *sf;
+ JSValue *sp;
+
+ sf = &s->frame;
+
+ /* close the closure variables. */
+ close_var_refs(rt, sf);
+
+ if (sf->arg_buf) {
+ /* cannot free the function if it is running */
+ assert(sf->cur_sp != NULL);
+ for(sp = sf->arg_buf; sp < sf->cur_sp; sp++) {
+ JS_FreeValueRT(rt, *sp);
+ }
+ js_free_rt(rt, sf->arg_buf);
+ }
+ JS_FreeValueRT(rt, sf->cur_func);
+ JS_FreeValueRT(rt, s->this_val);
+}
+
+static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s)
+{
+ JSValue func_obj;
+
+ if (js_check_stack_overflow(ctx->rt, 0))
+ return JS_ThrowStackOverflow(ctx);
+
+ /* the tag does not matter provided it is not an object */
+ func_obj = JS_MKPTR(JS_TAG_INT, s);
+ return JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED,
+ s->argc, s->frame.arg_buf, JS_CALL_FLAG_GENERATOR);
+}
+
+
+/* Generators */
+
+typedef enum JSGeneratorStateEnum {
+ JS_GENERATOR_STATE_SUSPENDED_START,
+ JS_GENERATOR_STATE_SUSPENDED_YIELD,
+ JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
+ JS_GENERATOR_STATE_EXECUTING,
+ JS_GENERATOR_STATE_COMPLETED,
+} JSGeneratorStateEnum;
+
+typedef struct JSGeneratorData {
+ JSGeneratorStateEnum state;
+ JSAsyncFunctionState func_state;
+} JSGeneratorData;
+
+static void free_generator_stack_rt(JSRuntime *rt, JSGeneratorData *s)
+{
+ if (s->state == JS_GENERATOR_STATE_COMPLETED)
+ return;
+ async_func_free(rt, &s->func_state);
+ s->state = JS_GENERATOR_STATE_COMPLETED;
+}
+
+static void js_generator_finalizer(JSRuntime *rt, JSValue obj)
+{
+ JSGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_GENERATOR);
+
+ if (s) {
+ free_generator_stack_rt(rt, s);
+ js_free_rt(rt, s);
+ }
+}
+
+static void free_generator_stack(JSContext *ctx, JSGeneratorData *s)
+{
+ free_generator_stack_rt(ctx->rt, s);
+}
+
+static void js_generator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSGeneratorData *s = p->u.generator_data;
+
+ if (!s || s->state == JS_GENERATOR_STATE_COMPLETED)
+ return;
+ async_func_mark(rt, &s->func_state, mark_func);
+}
+
+/* XXX: use enum */
+#define GEN_MAGIC_NEXT 0
+#define GEN_MAGIC_RETURN 1
+#define GEN_MAGIC_THROW 2
+
+static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic)
+{
+ JSGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_GENERATOR);
+ JSStackFrame *sf;
+ JSValue ret, func_ret;
+
+ *pdone = TRUE;
+ if (!s)
+ return JS_ThrowTypeError(ctx, "not a generator");
+ sf = &s->func_state.frame;
+ switch(s->state) {
+ default:
+ case JS_GENERATOR_STATE_SUSPENDED_START:
+ if (magic == GEN_MAGIC_NEXT) {
+ goto exec_no_arg;
+ } else {
+ free_generator_stack(ctx, s);
+ goto done;
+ }
+ break;
+ case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
+ case JS_GENERATOR_STATE_SUSPENDED_YIELD:
+ /* cur_sp[-1] was set to JS_UNDEFINED in the previous call */
+ ret = JS_DupValue(ctx, argv[0]);
+ if (magic == GEN_MAGIC_THROW &&
+ s->state == JS_GENERATOR_STATE_SUSPENDED_YIELD) {
+ JS_Throw(ctx, ret);
+ s->func_state.throw_flag = TRUE;
+ } else {
+ sf->cur_sp[-1] = ret;
+ sf->cur_sp[0] = JS_NewInt32(ctx, magic);
+ sf->cur_sp++;
+ exec_no_arg:
+ s->func_state.throw_flag = FALSE;
+ }
+ s->state = JS_GENERATOR_STATE_EXECUTING;
+ func_ret = async_func_resume(ctx, &s->func_state);
+ s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD;
+ if (JS_IsException(func_ret)) {
+ /* finalize the execution in case of exception */
+ free_generator_stack(ctx, s);
+ return func_ret;
+ }
+ if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
+ /* get the returned yield value at the top of the stack */
+ ret = sf->cur_sp[-1];
+ sf->cur_sp[-1] = JS_UNDEFINED;
+ if (JS_VALUE_GET_INT(func_ret) == FUNC_RET_YIELD_STAR) {
+ s->state = JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
+ /* return (value, done) object */
+ *pdone = 2;
+ } else {
+ *pdone = FALSE;
+ }
+ } else {
+ /* end of iterator */
+ ret = sf->cur_sp[-1];
+ sf->cur_sp[-1] = JS_UNDEFINED;
+ JS_FreeValue(ctx, func_ret);
+ free_generator_stack(ctx, s);
+ }
+ break;
+ case JS_GENERATOR_STATE_COMPLETED:
+ done:
+ /* execution is finished */
+ switch(magic) {
+ default:
+ case GEN_MAGIC_NEXT:
+ ret = JS_UNDEFINED;
+ break;
+ case GEN_MAGIC_RETURN:
+ ret = JS_DupValue(ctx, argv[0]);
+ break;
+ case GEN_MAGIC_THROW:
+ ret = JS_Throw(ctx, JS_DupValue(ctx, argv[0]));
+ break;
+ }
+ break;
+ case JS_GENERATOR_STATE_EXECUTING:
+ ret = JS_ThrowTypeError(ctx, "cannot invoke a running generator");
+ break;
+ }
+ return ret;
+}
+
+static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int flags)
+{
+ JSValue obj, func_ret;
+ JSGeneratorData *s;
+
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ return JS_EXCEPTION;
+ s->state = JS_GENERATOR_STATE_SUSPENDED_START;
+ if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
+ s->state = JS_GENERATOR_STATE_COMPLETED;
+ goto fail;
+ }
+
+ /* execute the function up to 'OP_initial_yield' */
+ func_ret = async_func_resume(ctx, &s->func_state);
+ if (JS_IsException(func_ret))
+ goto fail;
+ JS_FreeValue(ctx, func_ret);
+
+ obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_GENERATOR);
+ if (JS_IsException(obj))
+ goto fail;
+ JS_SetOpaque(obj, s);
+ return obj;
+ fail:
+ free_generator_stack_rt(ctx->rt, s);
+ js_free(ctx, s);
+ return JS_EXCEPTION;
+}
+
+/* AsyncFunction */
+
+static void js_async_function_terminate(JSRuntime *rt, JSAsyncFunctionData *s)
+{
+ if (s->is_active) {
+ async_func_free(rt, &s->func_state);
+ s->is_active = FALSE;
+ }
+}
+
+static void js_async_function_free0(JSRuntime *rt, JSAsyncFunctionData *s)
+{
+ js_async_function_terminate(rt, s);
+ JS_FreeValueRT(rt, s->resolving_funcs[0]);
+ JS_FreeValueRT(rt, s->resolving_funcs[1]);
+ remove_gc_object(&s->header);
+ js_free_rt(rt, s);
+}
+
+static void js_async_function_free(JSRuntime *rt, JSAsyncFunctionData *s)
+{
+ if (--s->header.ref_count == 0) {
+ js_async_function_free0(rt, s);
+ }
+}
+
+static void js_async_function_resolve_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSAsyncFunctionData *s = p->u.async_function_data;
+ if (s) {
+ js_async_function_free(rt, s);
+ }
+}
+
+static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSAsyncFunctionData *s = p->u.async_function_data;
+ if (s) {
+ mark_func(rt, &s->header);
+ }
+}
+
+static int js_async_function_resolve_create(JSContext *ctx,
+ JSAsyncFunctionData *s,
+ JSValue *resolving_funcs)
+{
+ int i;
+ JSObject *p;
+
+ for(i = 0; i < 2; i++) {
+ resolving_funcs[i] =
+ JS_NewObjectProtoClass(ctx, ctx->function_proto,
+ JS_CLASS_ASYNC_FUNCTION_RESOLVE + i);
+ if (JS_IsException(resolving_funcs[i])) {
+ if (i == 1)
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ return -1;
+ }
+ p = JS_VALUE_GET_OBJ(resolving_funcs[i]);
+ s->header.ref_count++;
+ p->u.async_function_data = s;
+ }
+ return 0;
+}
+
+static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
+{
+ JSValue func_ret, ret2;
+
+ func_ret = async_func_resume(ctx, &s->func_state);
+ if (JS_IsException(func_ret)) {
+ JSValue error;
+ fail:
+ error = JS_GetException(ctx);
+ ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
+ 1, &error);
+ JS_FreeValue(ctx, error);
+ js_async_function_terminate(ctx->rt, s);
+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
+ } else {
+ JSValue value;
+ value = s->func_state.frame.cur_sp[-1];
+ s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
+ if (JS_IsUndefined(func_ret)) {
+ /* function returned */
+ ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
+ 1, &value);
+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, value);
+ js_async_function_terminate(ctx->rt, s);
+ } else {
+ JSValue promise, resolving_funcs[2], resolving_funcs1[2];
+ int i, res;
+
+ /* await */
+ JS_FreeValue(ctx, func_ret); /* not used */
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+ 1, &value, 0);
+ JS_FreeValue(ctx, value);
+ if (JS_IsException(promise))
+ goto fail;
+ if (js_async_function_resolve_create(ctx, s, resolving_funcs)) {
+ JS_FreeValue(ctx, promise);
+ goto fail;
+ }
+
+ /* Note: no need to create 'thrownawayCapability' as in
+ the spec */
+ for(i = 0; i < 2; i++)
+ resolving_funcs1[i] = JS_UNDEFINED;
+ res = perform_promise_then(ctx, promise,
+ (JSValueConst *)resolving_funcs,
+ (JSValueConst *)resolving_funcs1);
+ JS_FreeValue(ctx, promise);
+ for(i = 0; i < 2; i++)
+ JS_FreeValue(ctx, resolving_funcs[i]);
+ if (res)
+ goto fail;
+ }
+ }
+}
+
+static JSValue js_async_function_resolve_call(JSContext *ctx,
+ JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int flags)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(func_obj);
+ JSAsyncFunctionData *s = p->u.async_function_data;
+ BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE;
+ JSValueConst arg;
+
+ if (argc > 0)
+ arg = argv[0];
+ else
+ arg = JS_UNDEFINED;
+ s->func_state.throw_flag = is_reject;
+ if (is_reject) {
+ JS_Throw(ctx, JS_DupValue(ctx, arg));
+ } else {
+ /* return value of await */
+ s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
+ }
+ js_async_function_resume(ctx, s);
+ return JS_UNDEFINED;
+}
+
+static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags)
+{
+ JSValue promise;
+ JSAsyncFunctionData *s;
+
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ return JS_EXCEPTION;
+ s->header.ref_count = 1;
+ add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
+ s->is_active = FALSE;
+ s->resolving_funcs[0] = JS_UNDEFINED;
+ s->resolving_funcs[1] = JS_UNDEFINED;
+
+ promise = JS_NewPromiseCapability(ctx, s->resolving_funcs);
+ if (JS_IsException(promise))
+ goto fail;
+
+ if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
+ fail:
+ JS_FreeValue(ctx, promise);
+ js_async_function_free(ctx->rt, s);
+ return JS_EXCEPTION;
+ }
+ s->is_active = TRUE;
+
+ js_async_function_resume(ctx, s);
+
+ js_async_function_free(ctx->rt, s);
+
+ return promise;
+}
+
+/* AsyncGenerator */
+
+typedef enum JSAsyncGeneratorStateEnum {
+ JS_ASYNC_GENERATOR_STATE_SUSPENDED_START,
+ JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD,
+ JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR,
+ JS_ASYNC_GENERATOR_STATE_EXECUTING,
+ JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN,
+ JS_ASYNC_GENERATOR_STATE_COMPLETED,
+} JSAsyncGeneratorStateEnum;
+
+typedef struct JSAsyncGeneratorRequest {
+ struct list_head link;
+ /* completion */
+ int completion_type; /* GEN_MAGIC_x */
+ JSValue result;
+ /* promise capability */
+ JSValue promise;
+ JSValue resolving_funcs[2];
+} JSAsyncGeneratorRequest;
+
+typedef struct JSAsyncGeneratorData {
+ JSObject *generator; /* back pointer to the object (const) */
+ JSAsyncGeneratorStateEnum state;
+ JSAsyncFunctionState func_state;
+ struct list_head queue; /* list of JSAsyncGeneratorRequest.link */
+} JSAsyncGeneratorData;
+
+static void js_async_generator_free(JSRuntime *rt,
+ JSAsyncGeneratorData *s)
+{
+ struct list_head *el, *el1;
+ JSAsyncGeneratorRequest *req;
+
+ list_for_each_safe(el, el1, &s->queue) {
+ req = list_entry(el, JSAsyncGeneratorRequest, link);
+ JS_FreeValueRT(rt, req->result);
+ JS_FreeValueRT(rt, req->promise);
+ JS_FreeValueRT(rt, req->resolving_funcs[0]);
+ JS_FreeValueRT(rt, req->resolving_funcs[1]);
+ js_free_rt(rt, req);
+ }
+ if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
+ s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
+ async_func_free(rt, &s->func_state);
+ }
+ js_free_rt(rt, s);
+}
+
+static void js_async_generator_finalizer(JSRuntime *rt, JSValue obj)
+{
+ JSAsyncGeneratorData *s = JS_GetOpaque(obj, JS_CLASS_ASYNC_GENERATOR);
+
+ if (s) {
+ js_async_generator_free(rt, s);
+ }
+}
+
+static void js_async_generator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSAsyncGeneratorData *s = JS_GetOpaque(val, JS_CLASS_ASYNC_GENERATOR);
+ struct list_head *el;
+ JSAsyncGeneratorRequest *req;
+ if (s) {
+ list_for_each(el, &s->queue) {
+ req = list_entry(el, JSAsyncGeneratorRequest, link);
+ JS_MarkValue(rt, req->result, mark_func);
+ JS_MarkValue(rt, req->promise, mark_func);
+ JS_MarkValue(rt, req->resolving_funcs[0], mark_func);
+ JS_MarkValue(rt, req->resolving_funcs[1], mark_func);
+ }
+ if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED &&
+ s->state != JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN) {
+ async_func_mark(rt, &s->func_state, mark_func);
+ }
+ }
+}
+
+static JSValue js_async_generator_resolve_function(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data);
+
+static int js_async_generator_resolve_function_create(JSContext *ctx,
+ JSValueConst generator,
+ JSValue *resolving_funcs,
+ BOOL is_resume_next)
+{
+ int i;
+ JSValue func;
+
+ for(i = 0; i < 2; i++) {
+ func = JS_NewCFunctionData(ctx, js_async_generator_resolve_function, 1,
+ i + is_resume_next * 2, 1, &generator);
+ if (JS_IsException(func)) {
+ if (i == 1)
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ return -1;
+ }
+ resolving_funcs[i] = func;
+ }
+ return 0;
+}
+
+static int js_async_generator_await(JSContext *ctx,
+ JSAsyncGeneratorData *s,
+ JSValueConst value)
+{
+ JSValue promise, resolving_funcs[2], resolving_funcs1[2];
+ int i, res;
+
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+ 1, &value, 0);
+ if (JS_IsException(promise))
+ goto fail;
+
+ if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator),
+ resolving_funcs, FALSE)) {
+ JS_FreeValue(ctx, promise);
+ goto fail;
+ }
+
+ /* Note: no need to create 'thrownawayCapability' as in
+ the spec */
+ for(i = 0; i < 2; i++)
+ resolving_funcs1[i] = JS_UNDEFINED;
+ res = perform_promise_then(ctx, promise,
+ (JSValueConst *)resolving_funcs,
+ (JSValueConst *)resolving_funcs1);
+ JS_FreeValue(ctx, promise);
+ for(i = 0; i < 2; i++)
+ JS_FreeValue(ctx, resolving_funcs[i]);
+ if (res)
+ goto fail;
+ return 0;
+ fail:
+ return -1;
+}
+
+static void js_async_generator_resolve_or_reject(JSContext *ctx,
+ JSAsyncGeneratorData *s,
+ JSValueConst result,
+ int is_reject)
+{
+ JSAsyncGeneratorRequest *next;
+ JSValue ret;
+
+ next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
+ list_del(&next->link);
+ ret = JS_Call(ctx, next->resolving_funcs[is_reject], JS_UNDEFINED, 1,
+ &result);
+ JS_FreeValue(ctx, ret);
+ JS_FreeValue(ctx, next->result);
+ JS_FreeValue(ctx, next->promise);
+ JS_FreeValue(ctx, next->resolving_funcs[0]);
+ JS_FreeValue(ctx, next->resolving_funcs[1]);
+ js_free(ctx, next);
+}
+
+static void js_async_generator_resolve(JSContext *ctx,
+ JSAsyncGeneratorData *s,
+ JSValueConst value,
+ BOOL done)
+{
+ JSValue result;
+ result = js_create_iterator_result(ctx, JS_DupValue(ctx, value), done);
+ /* XXX: better exception handling ? */
+ js_async_generator_resolve_or_reject(ctx, s, result, 0);
+ JS_FreeValue(ctx, result);
+ }
+
+static void js_async_generator_reject(JSContext *ctx,
+ JSAsyncGeneratorData *s,
+ JSValueConst exception)
+{
+ js_async_generator_resolve_or_reject(ctx, s, exception, 1);
+}
+
+static void js_async_generator_complete(JSContext *ctx,
+ JSAsyncGeneratorData *s)
+{
+ if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) {
+ s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
+ async_func_free(ctx->rt, &s->func_state);
+ }
+}
+
+static int js_async_generator_completed_return(JSContext *ctx,
+ JSAsyncGeneratorData *s,
+ JSValueConst value)
+{
+ JSValue promise, resolving_funcs[2], resolving_funcs1[2];
+ int res;
+
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+ 1, &value, 0);
+ if (JS_IsException(promise))
+ return -1;
+ if (js_async_generator_resolve_function_create(ctx,
+ JS_MKPTR(JS_TAG_OBJECT, s->generator),
+ resolving_funcs1,
+ TRUE)) {
+ JS_FreeValue(ctx, promise);
+ return -1;
+ }
+ resolving_funcs[0] = JS_UNDEFINED;
+ resolving_funcs[1] = JS_UNDEFINED;
+ res = perform_promise_then(ctx, promise,
+ (JSValueConst *)resolving_funcs1,
+ (JSValueConst *)resolving_funcs);
+ JS_FreeValue(ctx, resolving_funcs1[0]);
+ JS_FreeValue(ctx, resolving_funcs1[1]);
+ JS_FreeValue(ctx, promise);
+ return res;
+}
+
+static void js_async_generator_resume_next(JSContext *ctx,
+ JSAsyncGeneratorData *s)
+{
+ JSAsyncGeneratorRequest *next;
+ JSValue func_ret, value;
+
+ for(;;) {
+ if (list_empty(&s->queue))
+ break;
+ next = list_entry(s->queue.next, JSAsyncGeneratorRequest, link);
+ switch(s->state) {
+ case JS_ASYNC_GENERATOR_STATE_EXECUTING:
+ /* only happens when restarting execution after await() */
+ goto resume_exec;
+ case JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN:
+ goto done;
+ case JS_ASYNC_GENERATOR_STATE_SUSPENDED_START:
+ if (next->completion_type == GEN_MAGIC_NEXT) {
+ goto exec_no_arg;
+ } else {
+ js_async_generator_complete(ctx, s);
+ }
+ break;
+ case JS_ASYNC_GENERATOR_STATE_COMPLETED:
+ if (next->completion_type == GEN_MAGIC_NEXT) {
+ js_async_generator_resolve(ctx, s, JS_UNDEFINED, TRUE);
+ } else if (next->completion_type == GEN_MAGIC_RETURN) {
+ s->state = JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN;
+ js_async_generator_completed_return(ctx, s, next->result);
+ goto done;
+ } else {
+ js_async_generator_reject(ctx, s, next->result);
+ }
+ goto done;
+ case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD:
+ case JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR:
+ value = JS_DupValue(ctx, next->result);
+ if (next->completion_type == GEN_MAGIC_THROW &&
+ s->state == JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD) {
+ JS_Throw(ctx, value);
+ s->func_state.throw_flag = TRUE;
+ } else {
+ /* 'yield' returns a value. 'yield *' also returns a value
+ in case the 'throw' method is called */
+ s->func_state.frame.cur_sp[-1] = value;
+ s->func_state.frame.cur_sp[0] =
+ JS_NewInt32(ctx, next->completion_type);
+ s->func_state.frame.cur_sp++;
+ exec_no_arg:
+ s->func_state.throw_flag = FALSE;
+ }
+ s->state = JS_ASYNC_GENERATOR_STATE_EXECUTING;
+ resume_exec:
+ func_ret = async_func_resume(ctx, &s->func_state);
+ if (JS_IsException(func_ret)) {
+ value = JS_GetException(ctx);
+ js_async_generator_complete(ctx, s);
+ js_async_generator_reject(ctx, s, value);
+ JS_FreeValue(ctx, value);
+ } else if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) {
+ int func_ret_code;
+ value = s->func_state.frame.cur_sp[-1];
+ s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
+ func_ret_code = JS_VALUE_GET_INT(func_ret);
+ switch(func_ret_code) {
+ case FUNC_RET_YIELD:
+ case FUNC_RET_YIELD_STAR:
+ if (func_ret_code == FUNC_RET_YIELD_STAR)
+ s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD_STAR;
+ else
+ s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_YIELD;
+ js_async_generator_resolve(ctx, s, value, FALSE);
+ JS_FreeValue(ctx, value);
+ break;
+ case FUNC_RET_AWAIT:
+ js_async_generator_await(ctx, s, value);
+ JS_FreeValue(ctx, value);
+ goto done;
+ default:
+ abort();
+ }
+ } else {
+ assert(JS_IsUndefined(func_ret));
+ /* end of function */
+ value = s->func_state.frame.cur_sp[-1];
+ s->func_state.frame.cur_sp[-1] = JS_UNDEFINED;
+ js_async_generator_complete(ctx, s);
+ js_async_generator_resolve(ctx, s, value, TRUE);
+ JS_FreeValue(ctx, value);
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+ done: ;
+}
+
+static JSValue js_async_generator_resolve_function(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ BOOL is_reject = magic & 1;
+ JSAsyncGeneratorData *s = JS_GetOpaque(func_data[0], JS_CLASS_ASYNC_GENERATOR);
+ JSValueConst arg = argv[0];
+
+ /* XXX: what if s == NULL */
+
+ if (magic >= 2) {
+ /* resume next case in AWAITING_RETURN state */
+ assert(s->state == JS_ASYNC_GENERATOR_STATE_AWAITING_RETURN ||
+ s->state == JS_ASYNC_GENERATOR_STATE_COMPLETED);
+ s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
+ if (is_reject) {
+ js_async_generator_reject(ctx, s, arg);
+ } else {
+ js_async_generator_resolve(ctx, s, arg, TRUE);
+ }
+ } else {
+ /* restart function execution after await() */
+ assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING);
+ s->func_state.throw_flag = is_reject;
+ if (is_reject) {
+ JS_Throw(ctx, JS_DupValue(ctx, arg));
+ } else {
+ /* return value of await */
+ s->func_state.frame.cur_sp[-1] = JS_DupValue(ctx, arg);
+ }
+ js_async_generator_resume_next(ctx, s);
+ }
+ return JS_UNDEFINED;
+}
+
+/* magic = GEN_MAGIC_x */
+static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic)
+{
+ JSAsyncGeneratorData *s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_GENERATOR);
+ JSValue promise, resolving_funcs[2];
+ JSAsyncGeneratorRequest *req;
+
+ promise = JS_NewPromiseCapability(ctx, resolving_funcs);
+ if (JS_IsException(promise))
+ return JS_EXCEPTION;
+ if (!s) {
+ JSValue err, res2;
+ JS_ThrowTypeError(ctx, "not an AsyncGenerator object");
+ err = JS_GetException(ctx);
+ res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
+ 1, &err);
+ JS_FreeValue(ctx, err);
+ JS_FreeValue(ctx, res2);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return promise;
+ }
+ req = js_mallocz(ctx, sizeof(*req));
+ if (!req)
+ goto fail;
+ req->completion_type = magic;
+ req->result = JS_DupValue(ctx, argv[0]);
+ req->promise = JS_DupValue(ctx, promise);
+ req->resolving_funcs[0] = resolving_funcs[0];
+ req->resolving_funcs[1] = resolving_funcs[1];
+ list_add_tail(&req->link, &s->queue);
+ if (s->state != JS_ASYNC_GENERATOR_STATE_EXECUTING) {
+ js_async_generator_resume_next(ctx, s);
+ }
+ return promise;
+ fail:
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ JS_FreeValue(ctx, promise);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv,
+ int flags)
+{
+ JSValue obj, func_ret;
+ JSAsyncGeneratorData *s;
+
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ return JS_EXCEPTION;
+ s->state = JS_ASYNC_GENERATOR_STATE_SUSPENDED_START;
+ init_list_head(&s->queue);
+ if (async_func_init(ctx, &s->func_state, func_obj, this_obj, argc, argv)) {
+ s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED;
+ goto fail;
+ }
+
+ /* execute the function up to 'OP_initial_yield' (no yield nor
+ await are possible) */
+ func_ret = async_func_resume(ctx, &s->func_state);
+ if (JS_IsException(func_ret))
+ goto fail;
+ JS_FreeValue(ctx, func_ret);
+
+ obj = js_create_from_ctor(ctx, func_obj, JS_CLASS_ASYNC_GENERATOR);
+ if (JS_IsException(obj))
+ goto fail;
+ s->generator = JS_VALUE_GET_OBJ(obj);
+ JS_SetOpaque(obj, s);
+ return obj;
+ fail:
+ js_async_generator_free(ctx->rt, s);
+ return JS_EXCEPTION;
+}
+
+/* JS parser */
+
+enum {
+ TOK_NUMBER = -128,
+ TOK_STRING,
+ TOK_TEMPLATE,
+ TOK_IDENT,
+ TOK_REGEXP,
+ /* warning: order matters (see js_parse_assign_expr) */
+ TOK_MUL_ASSIGN,
+ TOK_DIV_ASSIGN,
+ TOK_MOD_ASSIGN,
+ TOK_PLUS_ASSIGN,
+ TOK_MINUS_ASSIGN,
+ TOK_SHL_ASSIGN,
+ TOK_SAR_ASSIGN,
+ TOK_SHR_ASSIGN,
+ TOK_AND_ASSIGN,
+ TOK_XOR_ASSIGN,
+ TOK_OR_ASSIGN,
+#ifdef CONFIG_BIGNUM
+ TOK_MATH_POW_ASSIGN,
+#endif
+ TOK_POW_ASSIGN,
+ TOK_LAND_ASSIGN,
+ TOK_LOR_ASSIGN,
+ TOK_DOUBLE_QUESTION_MARK_ASSIGN,
+ TOK_DEC,
+ TOK_INC,
+ TOK_SHL,
+ TOK_SAR,
+ TOK_SHR,
+ TOK_LT,
+ TOK_LTE,
+ TOK_GT,
+ TOK_GTE,
+ TOK_EQ,
+ TOK_STRICT_EQ,
+ TOK_NEQ,
+ TOK_STRICT_NEQ,
+ TOK_LAND,
+ TOK_LOR,
+#ifdef CONFIG_BIGNUM
+ TOK_MATH_POW,
+#endif
+ TOK_POW,
+ TOK_ARROW,
+ TOK_ELLIPSIS,
+ TOK_DOUBLE_QUESTION_MARK,
+ TOK_QUESTION_MARK_DOT,
+ TOK_ERROR,
+ TOK_PRIVATE_NAME,
+ TOK_EOF,
+ /* keywords: WARNING: same order as atoms */
+ TOK_NULL, /* must be first */
+ TOK_FALSE,
+ TOK_TRUE,
+ TOK_IF,
+ TOK_ELSE,
+ TOK_RETURN,
+ TOK_VAR,
+ TOK_THIS,
+ TOK_DELETE,
+ TOK_VOID,
+ TOK_TYPEOF,
+ TOK_NEW,
+ TOK_IN,
+ TOK_INSTANCEOF,
+ TOK_DO,
+ TOK_WHILE,
+ TOK_FOR,
+ TOK_BREAK,
+ TOK_CONTINUE,
+ TOK_SWITCH,
+ TOK_CASE,
+ TOK_DEFAULT,
+ TOK_THROW,
+ TOK_TRY,
+ TOK_CATCH,
+ TOK_FINALLY,
+ TOK_FUNCTION,
+ TOK_DEBUGGER,
+ TOK_WITH,
+ /* FutureReservedWord */
+ TOK_CLASS,
+ TOK_CONST,
+ TOK_ENUM,
+ TOK_EXPORT,
+ TOK_EXTENDS,
+ TOK_IMPORT,
+ TOK_SUPER,
+ /* FutureReservedWords when parsing strict mode code */
+ TOK_IMPLEMENTS,
+ TOK_INTERFACE,
+ TOK_LET,
+ TOK_PACKAGE,
+ TOK_PRIVATE,
+ TOK_PROTECTED,
+ TOK_PUBLIC,
+ TOK_STATIC,
+ TOK_YIELD,
+ TOK_AWAIT, /* must be last */
+ TOK_OF, /* only used for js_parse_skip_parens_token() */
+};
+
+#define TOK_FIRST_KEYWORD TOK_NULL
+#define TOK_LAST_KEYWORD TOK_AWAIT
+
+/* unicode code points */
+#define CP_NBSP 0x00a0
+#define CP_BOM 0xfeff
+
+#define CP_LS 0x2028
+#define CP_PS 0x2029
+
+typedef struct BlockEnv {
+ struct BlockEnv *prev;
+ JSAtom label_name; /* JS_ATOM_NULL if none */
+ int label_break; /* -1 if none */
+ int label_cont; /* -1 if none */
+ int drop_count; /* number of stack elements to drop */
+ int label_finally; /* -1 if none */
+ int scope_level;
+ int has_iterator;
+} BlockEnv;
+
+typedef struct JSGlobalVar {
+ int cpool_idx; /* if >= 0, index in the constant pool for hoisted
+ function defintion*/
+ uint8_t force_init : 1; /* force initialization to undefined */
+ uint8_t is_lexical : 1; /* global let/const definition */
+ uint8_t is_const : 1; /* const definition */
+ int scope_level; /* scope of definition */
+ JSAtom var_name; /* variable name */
+} JSGlobalVar;
+
+typedef struct RelocEntry {
+ struct RelocEntry *next;
+ uint32_t addr; /* address to patch */
+ int size; /* address size: 1, 2 or 4 bytes */
+} RelocEntry;
+
+typedef struct JumpSlot {
+ int op;
+ int size;
+ int pos;
+ int label;
+} JumpSlot;
+
+typedef struct LabelSlot {
+ int ref_count;
+ int pos; /* phase 1 address, -1 means not resolved yet */
+ int pos2; /* phase 2 address, -1 means not resolved yet */
+ int addr; /* phase 3 address, -1 means not resolved yet */
+ RelocEntry *first_reloc;
+} LabelSlot;
+
+typedef struct LineNumberSlot {
+ uint32_t pc;
+ int line_num;
+} LineNumberSlot;
+
+typedef enum JSParseFunctionEnum {
+ JS_PARSE_FUNC_STATEMENT,
+ JS_PARSE_FUNC_VAR,
+ JS_PARSE_FUNC_EXPR,
+ JS_PARSE_FUNC_ARROW,
+ JS_PARSE_FUNC_GETTER,
+ JS_PARSE_FUNC_SETTER,
+ JS_PARSE_FUNC_METHOD,
+ JS_PARSE_FUNC_CLASS_CONSTRUCTOR,
+ JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR,
+} JSParseFunctionEnum;
+
+typedef enum JSParseExportEnum {
+ JS_PARSE_EXPORT_NONE,
+ JS_PARSE_EXPORT_NAMED,
+ JS_PARSE_EXPORT_DEFAULT,
+} JSParseExportEnum;
+
+typedef struct JSFunctionDef {
+ JSContext *ctx;
+ struct JSFunctionDef *parent;
+ int parent_cpool_idx; /* index in the constant pool of the parent
+ or -1 if none */
+ int parent_scope_level; /* scope level in parent at point of definition */
+ struct list_head child_list; /* list of JSFunctionDef.link */
+ struct list_head link;
+
+ BOOL is_eval; /* TRUE if eval code */
+ int eval_type; /* only valid if is_eval = TRUE */
+ BOOL is_global_var; /* TRUE if variables are not defined locally:
+ eval global, eval module or non strict eval */
+ BOOL is_func_expr; /* TRUE if function expression */
+ BOOL has_home_object; /* TRUE if the home object is available */
+ BOOL has_prototype; /* true if a prototype field is necessary */
+ BOOL has_simple_parameter_list;
+ BOOL has_parameter_expressions; /* if true, an argument scope is created */
+ BOOL has_use_strict; /* to reject directive in special cases */
+ BOOL has_eval_call; /* true if the function contains a call to eval() */
+ BOOL has_arguments_binding; /* true if the 'arguments' binding is
+ available in the function */
+ BOOL has_this_binding; /* true if the 'this' and new.target binding are
+ available in the function */
+ BOOL new_target_allowed; /* true if the 'new.target' does not
+ throw a syntax error */
+ BOOL super_call_allowed; /* true if super() is allowed */
+ BOOL super_allowed; /* true if super. or super[] is allowed */
+ BOOL arguments_allowed; /* true if the 'arguments' identifier is allowed */
+ BOOL is_derived_class_constructor;
+ BOOL in_function_body;
+ BOOL backtrace_barrier;
+ JSFunctionKindEnum func_kind : 8;
+ JSParseFunctionEnum func_type : 8;
+ uint8_t js_mode; /* bitmap of JS_MODE_x */
+ JSAtom func_name; /* JS_ATOM_NULL if no name */
+
+ JSVarDef *vars;
+ int var_size; /* allocated size for vars[] */
+ int var_count;
+ JSVarDef *args;
+ int arg_size; /* allocated size for args[] */
+ int arg_count; /* number of arguments */
+ int defined_arg_count;
+ int var_object_idx; /* -1 if none */
+ int arg_var_object_idx; /* -1 if none (var object for the argument scope) */
+ int arguments_var_idx; /* -1 if none */
+ int arguments_arg_idx; /* argument variable definition in argument scope,
+ -1 if none */
+ int func_var_idx; /* variable containing the current function (-1
+ if none, only used if is_func_expr is true) */
+ int eval_ret_idx; /* variable containing the return value of the eval, -1 if none */
+ int this_var_idx; /* variable containg the 'this' value, -1 if none */
+ int new_target_var_idx; /* variable containg the 'new.target' value, -1 if none */
+ int this_active_func_var_idx; /* variable containg the 'this.active_func' value, -1 if none */
+ int home_object_var_idx;
+ BOOL need_home_object;
+
+ int scope_level; /* index into fd->scopes if the current lexical scope */
+ int scope_first; /* index into vd->vars of first lexically scoped variable */
+ int scope_size; /* allocated size of fd->scopes array */
+ int scope_count; /* number of entries used in the fd->scopes array */
+ JSVarScope *scopes;
+ JSVarScope def_scope_array[4];
+ int body_scope; /* scope of the body of the function or eval */
+
+ int global_var_count;
+ int global_var_size;
+ JSGlobalVar *global_vars;
+
+ DynBuf byte_code;
+ int last_opcode_pos; /* -1 if no last opcode */
+ int last_opcode_line_num;
+ BOOL use_short_opcodes; /* true if short opcodes are used in byte_code */
+
+ LabelSlot *label_slots;
+ int label_size; /* allocated size for label_slots[] */
+ int label_count;
+ BlockEnv *top_break; /* break/continue label stack */
+
+ /* constant pool (strings, functions, numbers) */
+ JSValue *cpool;
+ int cpool_count;
+ int cpool_size;
+
+ /* list of variables in the closure */
+ int closure_var_count;
+ int closure_var_size;
+ JSClosureVar *closure_var;
+
+ JumpSlot *jump_slots;
+ int jump_size;
+ int jump_count;
+
+ LineNumberSlot *line_number_slots;
+ int line_number_size;
+ int line_number_count;
+ int line_number_last;
+ int line_number_last_pc;
+
+ /* pc2line table */
+ JSAtom filename;
+ int line_num;
+ DynBuf pc2line;
+
+ char *source; /* raw source, utf-8 encoded */
+ int source_len;
+
+ JSModuleDef *module; /* != NULL when parsing a module */
+} JSFunctionDef;
+
+typedef struct JSToken {
+ int val;
+ int line_num; /* line number of token start */
+ const uint8_t *ptr;
+ union {
+ struct {
+ JSValue str;
+ int sep;
+ } str;
+ struct {
+ JSValue val;
+#ifdef CONFIG_BIGNUM
+ slimb_t exponent; /* may be != 0 only if val is a float */
+#endif
+ } num;
+ struct {
+ JSAtom atom;
+ BOOL has_escape;
+ BOOL is_reserved;
+ } ident;
+ struct {
+ JSValue body;
+ JSValue flags;
+ } regexp;
+ } u;
+} JSToken;
+
+typedef struct JSParseState {
+ JSContext *ctx;
+ int last_line_num; /* line number of last token */
+ int line_num; /* line number of current offset */
+ const char *filename;
+ JSToken token;
+ BOOL got_lf; /* true if got line feed before the current token */
+ const uint8_t *last_ptr;
+ const uint8_t *buf_ptr;
+ const uint8_t *buf_end;
+
+ /* current function code */
+ JSFunctionDef *cur_func;
+ BOOL is_module; /* parsing a module */
+ BOOL allow_html_comments;
+ BOOL ext_json; /* true if accepting JSON superset */
+} JSParseState;
+
+typedef struct JSOpCode {
+#ifdef DUMP_BYTECODE
+ const char *name;
+#endif
+ uint8_t size; /* in bytes */
+ /* the opcodes remove n_pop items from the top of the stack, then
+ pushes n_push items */
+ uint8_t n_pop;
+ uint8_t n_push;
+ uint8_t fmt;
+} JSOpCode;
+
+static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
+#define FMT(f)
+#ifdef DUMP_BYTECODE
+#define DEF(id, size, n_pop, n_push, f) { #id, size, n_pop, n_push, OP_FMT_ ## f },
+#else
+#define DEF(id, size, n_pop, n_push, f) { size, n_pop, n_push, OP_FMT_ ## f },
+#endif
+#include "quickjs-opcode.h"
+#undef DEF
+#undef FMT
+};
+
+#if SHORT_OPCODES
+/* After the final compilation pass, short opcodes are used. Their
+ opcodes overlap with the temporary opcodes which cannot appear in
+ the final bytecode. Their description is after the temporary
+ opcodes in opcode_info[]. */
+#define short_opcode_info(op) \
+ opcode_info[(op) >= OP_TEMP_START ? \
+ (op) + (OP_TEMP_END - OP_TEMP_START) : (op)]
+#else
+#define short_opcode_info(op) opcode_info[op]
+#endif
+
+static warn_unused int next_token(JSParseState *s);
+
+static void free_token(JSParseState *s, JSToken *token)
+{
+ switch(token->val) {
+#ifdef CONFIG_BIGNUM
+ case TOK_NUMBER:
+ JS_FreeValue(s->ctx, token->u.num.val);
+ break;
+#endif
+ case TOK_STRING:
+ case TOK_TEMPLATE:
+ JS_FreeValue(s->ctx, token->u.str.str);
+ break;
+ case TOK_REGEXP:
+ JS_FreeValue(s->ctx, token->u.regexp.body);
+ JS_FreeValue(s->ctx, token->u.regexp.flags);
+ break;
+ case TOK_IDENT:
+ case TOK_PRIVATE_NAME:
+ JS_FreeAtom(s->ctx, token->u.ident.atom);
+ break;
+ default:
+ if (token->val >= TOK_FIRST_KEYWORD &&
+ token->val <= TOK_LAST_KEYWORD) {
+ JS_FreeAtom(s->ctx, token->u.ident.atom);
+ }
+ break;
+ }
+}
+
+static void maybe_unused dump_token(JSParseState *s,
+ const JSToken *token)
+{
+ switch(token->val) {
+ case TOK_NUMBER:
+ {
+ double d;
+ JS_ToFloat64(s->ctx, &d, token->u.num.val); /* no exception possible */
+ printf("number: %.14g\n", d);
+ }
+ break;
+ case TOK_IDENT:
+ dump_atom:
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ printf("ident: '%s'\n",
+ JS_AtomGetStr(s->ctx, buf, sizeof(buf), token->u.ident.atom));
+ }
+ break;
+ case TOK_STRING:
+ {
+ const char *str;
+ /* XXX: quote the string */
+ str = JS_ToCString(s->ctx, token->u.str.str);
+ printf("string: '%s'\n", str);
+ JS_FreeCString(s->ctx, str);
+ }
+ break;
+ case TOK_TEMPLATE:
+ {
+ const char *str;
+ str = JS_ToCString(s->ctx, token->u.str.str);
+ printf("template: `%s`\n", str);
+ JS_FreeCString(s->ctx, str);
+ }
+ break;
+ case TOK_REGEXP:
+ {
+ const char *str, *str2;
+ str = JS_ToCString(s->ctx, token->u.regexp.body);
+ str2 = JS_ToCString(s->ctx, token->u.regexp.flags);
+ printf("regexp: '%s' '%s'\n", str, str2);
+ JS_FreeCString(s->ctx, str);
+ JS_FreeCString(s->ctx, str2);
+ }
+ break;
+ case TOK_EOF:
+ printf("eof\n");
+ break;
+ default:
+ if (s->token.val >= TOK_NULL && s->token.val <= TOK_LAST_KEYWORD) {
+ goto dump_atom;
+ } else if (s->token.val >= 256) {
+ printf("token: %d\n", token->val);
+ } else {
+ printf("token: '%c'\n", token->val);
+ }
+ break;
+ }
+}
+
+int FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, const char *fmt, ...)
+{
+ JSContext *ctx = s->ctx;
+ va_list ap;
+ int backtrace_flags;
+
+ va_start(ap, fmt);
+ JS_ThrowError2(ctx, JS_SYNTAX_ERROR, fmt, ap, FALSE);
+ va_end(ap);
+ backtrace_flags = 0;
+ if (s->cur_func && s->cur_func->backtrace_barrier)
+ backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
+ build_backtrace(ctx, ctx->rt->current_exception, s->filename, s->line_num,
+ backtrace_flags);
+ return -1;
+}
+
+static int js_parse_expect(JSParseState *s, int tok)
+{
+ if (s->token.val != tok) {
+ /* XXX: dump token correctly in all cases */
+ return js_parse_error(s, "expecting '%c'", tok);
+ }
+ return next_token(s);
+}
+
+static int js_parse_expect_semi(JSParseState *s)
+{
+ if (s->token.val != ';') {
+ /* automatic insertion of ';' */
+ if (s->token.val == TOK_EOF || s->token.val == '}' || s->got_lf) {
+ return 0;
+ }
+ return js_parse_error(s, "expecting '%c'", ';');
+ }
+ return next_token(s);
+}
+
+static int js_parse_error_reserved_identifier(JSParseState *s)
+{
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ return js_parse_error(s, "'%s' is a reserved identifier",
+ JS_AtomGetStr(s->ctx, buf1, sizeof(buf1),
+ s->token.u.ident.atom));
+}
+
+static warn_unused int js_parse_template_part(JSParseState *s, const uint8_t *p)
+{
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+
+ /* p points to the first byte of the template part */
+ if (string_buffer_init(s->ctx, b, 32))
+ goto fail;
+ for(;;) {
+ if (p >= s->buf_end)
+ goto unexpected_eof;
+ c = *p++;
+ if (c == '`') {
+ /* template end part */
+ break;
+ }
+ if (c == '$' && *p == '{') {
+ /* template start or middle part */
+ p++;
+ break;
+ }
+ if (c == '\\') {
+ if (string_buffer_putc8(b, c))
+ goto fail;
+ if (p >= s->buf_end)
+ goto unexpected_eof;
+ c = *p++;
+ }
+ /* newline sequences are normalized as single '\n' bytes */
+ if (c == '\r') {
+ if (*p == '\n')
+ p++;
+ c = '\n';
+ }
+ if (c == '\n') {
+ s->line_num++;
+ } else if (c >= 0x80) {
+ const uint8_t *p_next;
+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF) {
+ js_parse_error(s, "invalid UTF-8 sequence");
+ goto fail;
+ }
+ p = p_next;
+ }
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+ s->token.val = TOK_TEMPLATE;
+ s->token.u.str.sep = c;
+ s->token.u.str.str = string_buffer_end(b);
+ s->buf_ptr = p;
+ return 0;
+
+ unexpected_eof:
+ js_parse_error(s, "unexpected end of string");
+ fail:
+ string_buffer_free(b);
+ return -1;
+}
+
+static warn_unused int js_parse_string(JSParseState *s, int sep,
+ BOOL do_throw, const uint8_t *p,
+ JSToken *token, const uint8_t **pp)
+{
+ int ret;
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+
+ /* string */
+ if (string_buffer_init(s->ctx, b, 32))
+ goto fail;
+ for(;;) {
+ if (p >= s->buf_end)
+ goto invalid_char;
+ c = *p;
+ if (c < 0x20) {
+ if (!s->cur_func) {
+ if (do_throw)
+ js_parse_error(s, "invalid character in a JSON string");
+ goto fail;
+ }
+ if (sep == '`') {
+ if (c == '\r') {
+ if (p[1] == '\n')
+ p++;
+ c = '\n';
+ }
+ /* do not update s->line_num */
+ } else if (c == '\n' || c == '\r')
+ goto invalid_char;
+ }
+ p++;
+ if (c == sep)
+ break;
+ if (c == '$' && *p == '{' && sep == '`') {
+ /* template start or middle part */
+ p++;
+ break;
+ }
+ if (c == '\\') {
+ c = *p;
+ /* XXX: need a specific JSON case to avoid
+ accepting invalid escapes */
+ switch(c) {
+ case '\0':
+ if (p >= s->buf_end)
+ goto invalid_char;
+ p++;
+ break;
+ case '\'':
+ case '\"':
+ case '\\':
+ p++;
+ break;
+ case '\r': /* accept DOS and MAC newline sequences */
+ if (p[1] == '\n') {
+ p++;
+ }
+ /* fall thru */
+ case '\n':
+ /* ignore escaped newline sequence */
+ p++;
+ if (sep != '`')
+ s->line_num++;
+ continue;
+ default:
+ if (c >= '0' && c <= '9') {
+ if (!s->cur_func)
+ goto invalid_escape; /* JSON case */
+ if (!(s->cur_func->js_mode & JS_MODE_STRICT) && sep != '`')
+ goto parse_escape;
+ if (c == '0' && !(p[1] >= '0' && p[1] <= '9')) {
+ p++;
+ c = '\0';
+ } else {
+ if (c >= '8' || sep == '`') {
+ /* Note: according to ES2021, \8 and \9 are not
+ accepted in strict mode or in templates. */
+ goto invalid_escape;
+ } else {
+ if (do_throw)
+ js_parse_error(s, "octal escape sequences are not allowed in strict mode");
+ }
+ goto fail;
+ }
+ } else if (c >= 0x80) {
+ const uint8_t *p_next;
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF) {
+ goto invalid_utf8;
+ }
+ p = p_next;
+ /* LS or PS are skipped */
+ if (c == CP_LS || c == CP_PS)
+ continue;
+ } else {
+ parse_escape:
+ ret = lre_parse_escape(&p, TRUE);
+ if (ret == -1) {
+ invalid_escape:
+ if (do_throw)
+ js_parse_error(s, "malformed escape sequence in string literal");
+ goto fail;
+ } else if (ret < 0) {
+ /* ignore the '\' (could output a warning) */
+ p++;
+ } else {
+ c = ret;
+ }
+ }
+ break;
+ }
+ } else if (c >= 0x80) {
+ const uint8_t *p_next;
+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF)
+ goto invalid_utf8;
+ p = p_next;
+ }
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+ token->val = TOK_STRING;
+ token->u.str.sep = c;
+ token->u.str.str = string_buffer_end(b);
+ *pp = p;
+ return 0;
+
+ invalid_utf8:
+ if (do_throw)
+ js_parse_error(s, "invalid UTF-8 sequence");
+ goto fail;
+ invalid_char:
+ if (do_throw)
+ js_parse_error(s, "unexpected end of string");
+ fail:
+ string_buffer_free(b);
+ return -1;
+}
+
+static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {
+ return s->token.val == TOK_IDENT && s->token.u.ident.atom == atom &&
+ !s->token.u.ident.has_escape;
+}
+
+static warn_unused int js_parse_regexp(JSParseState *s)
+{
+ const uint8_t *p;
+ BOOL in_class;
+ StringBuffer b_s, *b = &b_s;
+ StringBuffer b2_s, *b2 = &b2_s;
+ uint32_t c;
+
+ p = s->buf_ptr;
+ p++;
+ in_class = FALSE;
+ if (string_buffer_init(s->ctx, b, 32))
+ return -1;
+ if (string_buffer_init(s->ctx, b2, 1))
+ goto fail;
+ for(;;) {
+ if (p >= s->buf_end) {
+ eof_error:
+ js_parse_error(s, "unexpected end of regexp");
+ goto fail;
+ }
+ c = *p++;
+ if (c == '\n' || c == '\r') {
+ goto eol_error;
+ } else if (c == '/') {
+ if (!in_class)
+ break;
+ } else if (c == '[') {
+ in_class = TRUE;
+ } else if (c == ']') {
+ /* XXX: incorrect as the first character in a class */
+ in_class = FALSE;
+ } else if (c == '\\') {
+ if (string_buffer_putc8(b, c))
+ goto fail;
+ c = *p++;
+ if (c == '\n' || c == '\r')
+ goto eol_error;
+ else if (c == '\0' && p >= s->buf_end)
+ goto eof_error;
+ else if (c >= 0x80) {
+ const uint8_t *p_next;
+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF) {
+ goto invalid_utf8;
+ }
+ p = p_next;
+ if (c == CP_LS || c == CP_PS)
+ goto eol_error;
+ }
+ } else if (c >= 0x80) {
+ const uint8_t *p_next;
+ c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF) {
+ invalid_utf8:
+ js_parse_error(s, "invalid UTF-8 sequence");
+ goto fail;
+ }
+ p = p_next;
+ /* LS or PS are considered as line terminator */
+ if (c == CP_LS || c == CP_PS) {
+ eol_error:
+ js_parse_error(s, "unexpected line terminator in regexp");
+ goto fail;
+ }
+ }
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+
+ /* flags */
+ for(;;) {
+ const uint8_t *p_next = p;
+ c = *p_next++;
+ if (c >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p_next);
+ if (c > 0x10FFFF) {
+ goto invalid_utf8;
+ }
+ }
+ if (!lre_js_is_ident_next(c))
+ break;
+ if (string_buffer_putc(b2, c))
+ goto fail;
+ p = p_next;
+ }
+
+ s->token.val = TOK_REGEXP;
+ s->token.u.regexp.body = string_buffer_end(b);
+ s->token.u.regexp.flags = string_buffer_end(b2);
+ s->buf_ptr = p;
+ return 0;
+ fail:
+ string_buffer_free(b);
+ string_buffer_free(b2);
+ return -1;
+}
+
+static warn_unused int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
+ const char *static_buf)
+{
+ char *buf, *new_buf;
+ size_t size, new_size;
+
+ buf = *pbuf;
+ size = *psize;
+ if (size >= (SIZE_MAX / 3) * 2)
+ new_size = SIZE_MAX;
+ else
+ new_size = size + (size >> 1);
+ if (buf == static_buf) {
+ new_buf = js_malloc(ctx, new_size);
+ if (!new_buf)
+ return -1;
+ memcpy(new_buf, buf, size);
+ } else {
+ new_buf = js_realloc(ctx, buf, new_size);
+ if (!new_buf)
+ return -1;
+ }
+ *pbuf = new_buf;
+ *psize = new_size;
+ return 0;
+}
+
+/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
+static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
+ BOOL *pident_has_escape, int c, BOOL is_private)
+{
+ const uint8_t *p, *p1;
+ char ident_buf[128], *buf;
+ size_t ident_size, ident_pos;
+ JSAtom atom;
+
+ p = *pp;
+ buf = ident_buf;
+ ident_size = sizeof(ident_buf);
+ ident_pos = 0;
+ if (is_private)
+ buf[ident_pos++] = '#';
+ for(;;) {
+ p1 = p;
+
+ if (c < 128) {
+ buf[ident_pos++] = c;
+ } else {
+ ident_pos += unicode_to_utf8((uint8_t*)buf + ident_pos, c);
+ }
+ c = *p1++;
+ if (c == '\\' && *p1 == 'u') {
+ c = lre_parse_escape(&p1, TRUE);
+ *pident_has_escape = TRUE;
+ } else if (c >= 128) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
+ }
+ if (!lre_js_is_ident_next(c))
+ break;
+ p = p1;
+ if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
+ if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
+ atom = JS_ATOM_NULL;
+ goto done;
+ }
+ }
+ }
+ atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
+ done:
+ if (unlikely(buf != ident_buf))
+ js_free(s->ctx, buf);
+ *pp = p;
+ return atom;
+}
+
+
+static warn_unused int next_token(JSParseState *s)
+{
+ const uint8_t *p;
+ int c;
+ BOOL ident_has_escape;
+ JSAtom atom;
+
+ if (js_check_stack_overflow(s->ctx->rt, 0)) {
+ return js_parse_error(s, "stack overflow");
+ }
+
+ free_token(s, &s->token);
+
+ p = s->last_ptr = s->buf_ptr;
+ s->got_lf = FALSE;
+ s->last_line_num = s->token.line_num;
+ redo:
+ s->token.line_num = s->line_num;
+ s->token.ptr = p;
+ c = *p;
+ switch(c) {
+ case 0:
+ if (p >= s->buf_end) {
+ s->token.val = TOK_EOF;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '`':
+ if (js_parse_template_part(s, p + 1))
+ goto fail;
+ p = s->buf_ptr;
+ break;
+ case '\'':
+ case '\"':
+ if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
+ goto fail;
+ break;
+ case '\r': /* accept DOS and MAC newline sequences */
+ if (p[1] == '\n') {
+ p++;
+ }
+ /* fall thru */
+ case '\n':
+ p++;
+ line_terminator:
+ s->got_lf = TRUE;
+ s->line_num++;
+ goto redo;
+ case '\f':
+ case '\v':
+ case ' ':
+ case '\t':
+ p++;
+ goto redo;
+ case '/':
+ if (p[1] == '*') {
+ /* comment */
+ p += 2;
+ for(;;) {
+ if (*p == '\0' && p >= s->buf_end) {
+ js_parse_error(s, "unexpected end of comment");
+ goto fail;
+ }
+ if (p[0] == '*' && p[1] == '/') {
+ p += 2;
+ break;
+ }
+ if (*p == '\n') {
+ s->line_num++;
+ s->got_lf = TRUE; /* considered as LF for ASI */
+ p++;
+ } else if (*p == '\r') {
+ s->got_lf = TRUE; /* considered as LF for ASI */
+ p++;
+ } else if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ if (c == CP_LS || c == CP_PS) {
+ s->got_lf = TRUE; /* considered as LF for ASI */
+ } else if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ goto redo;
+ } else if (p[1] == '/') {
+ /* line comment */
+ p += 2;
+ skip_line_comment:
+ for(;;) {
+ if (*p == '\0' && p >= s->buf_end)
+ break;
+ if (*p == '\r' || *p == '\n')
+ break;
+ if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ /* LS or PS are considered as line terminator */
+ if (c == CP_LS || c == CP_PS) {
+ break;
+ } else if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ goto redo;
+ } else if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_DIV_ASSIGN;
+ } else {
+ p++;
+ s->token.val = c;
+ }
+ break;
+ case '\\':
+ if (p[1] == 'u') {
+ const uint8_t *p1 = p + 1;
+ int c1 = lre_parse_escape(&p1, TRUE);
+ if (c1 >= 0 && lre_js_is_ident_first(c1)) {
+ c = c1;
+ p = p1;
+ ident_has_escape = TRUE;
+ goto has_ident;
+ } else {
+ /* XXX: syntax error? */
+ }
+ }
+ goto def_token;
+ case 'a': case 'b': case 'c': case 'd':
+ case 'e': case 'f': case 'g': case 'h':
+ case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p':
+ case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D':
+ case 'E': case 'F': case 'G': case 'H':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+ case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case '$':
+ /* identifier */
+ p++;
+ ident_has_escape = FALSE;
+ has_ident:
+ atom = parse_ident(s, &p, &ident_has_escape, c, FALSE);
+ if (atom == JS_ATOM_NULL)
+ goto fail;
+ s->token.u.ident.atom = atom;
+ s->token.u.ident.has_escape = ident_has_escape;
+ s->token.u.ident.is_reserved = FALSE;
+ if (s->token.u.ident.atom <= JS_ATOM_LAST_KEYWORD ||
+ (s->token.u.ident.atom <= JS_ATOM_LAST_STRICT_KEYWORD &&
+ (s->cur_func->js_mode & JS_MODE_STRICT)) ||
+ (s->token.u.ident.atom == JS_ATOM_yield &&
+ ((s->cur_func->func_kind & JS_FUNC_GENERATOR) ||
+ (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
+ !s->cur_func->in_function_body && s->cur_func->parent &&
+ (s->cur_func->parent->func_kind & JS_FUNC_GENERATOR)))) ||
+ (s->token.u.ident.atom == JS_ATOM_await &&
+ (s->is_module ||
+ (((s->cur_func->func_kind & JS_FUNC_ASYNC) ||
+ (s->cur_func->func_type == JS_PARSE_FUNC_ARROW &&
+ !s->cur_func->in_function_body && s->cur_func->parent &&
+ (s->cur_func->parent->func_kind & JS_FUNC_ASYNC))))))) {
+ if (ident_has_escape) {
+ s->token.u.ident.is_reserved = TRUE;
+ s->token.val = TOK_IDENT;
+ } else {
+ /* The keywords atoms are pre allocated */
+ s->token.val = s->token.u.ident.atom - 1 + TOK_FIRST_KEYWORD;
+ }
+ } else {
+ s->token.val = TOK_IDENT;
+ }
+ break;
+ case '#':
+ /* private name */
+ {
+ const uint8_t *p1;
+ p++;
+ p1 = p;
+ c = *p1++;
+ if (c == '\\' && *p1 == 'u') {
+ c = lre_parse_escape(&p1, TRUE);
+ } else if (c >= 128) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1);
+ }
+ if (!lre_js_is_ident_first(c)) {
+ js_parse_error(s, "invalid first character of private name");
+ goto fail;
+ }
+ p = p1;
+ ident_has_escape = FALSE; /* not used */
+ atom = parse_ident(s, &p, &ident_has_escape, c, TRUE);
+ if (atom == JS_ATOM_NULL)
+ goto fail;
+ s->token.u.ident.atom = atom;
+ s->token.val = TOK_PRIVATE_NAME;
+ }
+ break;
+ case '.':
+ if (p[1] == '.' && p[2] == '.') {
+ p += 3;
+ s->token.val = TOK_ELLIPSIS;
+ break;
+ }
+ if (p[1] >= '0' && p[1] <= '9') {
+ goto parse_number;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '0':
+ /* in strict mode, octal literals are not accepted */
+ if (is_digit(p[1]) && (s->cur_func->js_mode & JS_MODE_STRICT)) {
+ js_parse_error(s, "octal literals are deprecated in strict mode");
+ goto fail;
+ }
+ goto parse_number;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ case '9':
+ /* number */
+ parse_number:
+ {
+ JSValue ret;
+ const uint8_t *p1;
+ int flags, radix;
+ flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL |
+ ATOD_ACCEPT_UNDERSCORES;
+#ifdef CONFIG_BIGNUM
+ flags |= ATOD_ACCEPT_SUFFIX;
+ if (s->cur_func->js_mode & JS_MODE_MATH) {
+ flags |= ATOD_MODE_BIGINT;
+ if (s->cur_func->js_mode & JS_MODE_MATH)
+ flags |= ATOD_TYPE_BIG_FLOAT;
+ }
+#endif
+ radix = 0;
+#ifdef CONFIG_BIGNUM
+ s->token.u.num.exponent = 0;
+ ret = js_atof2(s->ctx, (const char *)p, (const char **)&p, radix,
+ flags, &s->token.u.num.exponent);
+#else
+ ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
+ flags);
+#endif
+ if (JS_IsException(ret))
+ goto fail;
+ /* reject `10instanceof Number` */
+ if (JS_VALUE_IS_NAN(ret) ||
+ lre_js_is_ident_next(unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1))) {
+ JS_FreeValue(s->ctx, ret);
+ js_parse_error(s, "invalid number literal");
+ goto fail;
+ }
+ s->token.val = TOK_NUMBER;
+ s->token.u.num.val = ret;
+ }
+ break;
+ case '*':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_MUL_ASSIGN;
+ } else if (p[1] == '*') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_POW_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_POW;
+ }
+ } else {
+ goto def_token;
+ }
+ break;
+ case '%':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_MOD_ASSIGN;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '+':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_PLUS_ASSIGN;
+ } else if (p[1] == '+') {
+ p += 2;
+ s->token.val = TOK_INC;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '-':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_MINUS_ASSIGN;
+ } else if (p[1] == '-') {
+ if (s->allow_html_comments &&
+ p[2] == '>' && s->last_line_num != s->line_num) {
+ /* Annex B: `-->` at beginning of line is an html comment end.
+ It extends to the end of the line.
+ */
+ goto skip_line_comment;
+ }
+ p += 2;
+ s->token.val = TOK_DEC;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '<':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_LTE;
+ } else if (p[1] == '<') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_SHL_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_SHL;
+ }
+ } else if (s->allow_html_comments &&
+ p[1] == '!' && p[2] == '-' && p[3] == '-') {
+ /* Annex B: handle `<!--` single line html comments */
+ goto skip_line_comment;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '>':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_GTE;
+ } else if (p[1] == '>') {
+ if (p[2] == '>') {
+ if (p[3] == '=') {
+ p += 4;
+ s->token.val = TOK_SHR_ASSIGN;
+ } else {
+ p += 3;
+ s->token.val = TOK_SHR;
+ }
+ } else if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_SAR_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_SAR;
+ }
+ } else {
+ goto def_token;
+ }
+ break;
+ case '=':
+ if (p[1] == '=') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_STRICT_EQ;
+ } else {
+ p += 2;
+ s->token.val = TOK_EQ;
+ }
+ } else if (p[1] == '>') {
+ p += 2;
+ s->token.val = TOK_ARROW;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '!':
+ if (p[1] == '=') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_STRICT_NEQ;
+ } else {
+ p += 2;
+ s->token.val = TOK_NEQ;
+ }
+ } else {
+ goto def_token;
+ }
+ break;
+ case '&':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_AND_ASSIGN;
+ } else if (p[1] == '&') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_LAND_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_LAND;
+ }
+ } else {
+ goto def_token;
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ /* in math mode, '^' is the power operator. '^^' is always the
+ xor operator and '**' is always the power operator */
+ case '^':
+ if (p[1] == '=') {
+ p += 2;
+ if (s->cur_func->js_mode & JS_MODE_MATH)
+ s->token.val = TOK_MATH_POW_ASSIGN;
+ else
+ s->token.val = TOK_XOR_ASSIGN;
+ } else if (p[1] == '^') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_XOR_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = '^';
+ }
+ } else {
+ p++;
+ if (s->cur_func->js_mode & JS_MODE_MATH)
+ s->token.val = TOK_MATH_POW;
+ else
+ s->token.val = '^';
+ }
+ break;
+#else
+ case '^':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_XOR_ASSIGN;
+ } else {
+ goto def_token;
+ }
+ break;
+#endif
+ case '|':
+ if (p[1] == '=') {
+ p += 2;
+ s->token.val = TOK_OR_ASSIGN;
+ } else if (p[1] == '|') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_LOR_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_LOR;
+ }
+ } else {
+ goto def_token;
+ }
+ break;
+ case '?':
+ if (p[1] == '?') {
+ if (p[2] == '=') {
+ p += 3;
+ s->token.val = TOK_DOUBLE_QUESTION_MARK_ASSIGN;
+ } else {
+ p += 2;
+ s->token.val = TOK_DOUBLE_QUESTION_MARK;
+ }
+ } else if (p[1] == '.' && !(p[2] >= '0' && p[2] <= '9')) {
+ p += 2;
+ s->token.val = TOK_QUESTION_MARK_DOT;
+ } else {
+ goto def_token;
+ }
+ break;
+ default:
+ if (c >= 128) {
+ /* unicode value */
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ switch(c) {
+ case CP_PS:
+ case CP_LS:
+ /* XXX: should avoid incrementing line_number, but
+ needed to handle HTML comments */
+ goto line_terminator;
+ default:
+ if (lre_is_space(c)) {
+ goto redo;
+ } else if (lre_js_is_ident_first(c)) {
+ ident_has_escape = FALSE;
+ goto has_ident;
+ } else {
+ js_parse_error(s, "unexpected character");
+ goto fail;
+ }
+ }
+ }
+ def_token:
+ s->token.val = c;
+ p++;
+ break;
+ }
+ s->buf_ptr = p;
+
+ // dump_token(s, &s->token);
+ return 0;
+
+ fail:
+ s->token.val = TOK_ERROR;
+ return -1;
+}
+
+/* 'c' is the first character. Return JS_ATOM_NULL in case of error */
+static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
+{
+ const uint8_t *p;
+ char ident_buf[128], *buf;
+ size_t ident_size, ident_pos;
+ JSAtom atom;
+
+ p = *pp;
+ buf = ident_buf;
+ ident_size = sizeof(ident_buf);
+ ident_pos = 0;
+ for(;;) {
+ buf[ident_pos++] = c;
+ c = *p;
+ if (c >= 128 ||
+ !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1))
+ break;
+ p++;
+ if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) {
+ if (ident_realloc(s->ctx, &buf, &ident_size, ident_buf)) {
+ atom = JS_ATOM_NULL;
+ goto done;
+ }
+ }
+ }
+ atom = JS_NewAtomLen(s->ctx, buf, ident_pos);
+ done:
+ if (unlikely(buf != ident_buf))
+ js_free(s->ctx, buf);
+ *pp = p;
+ return atom;
+}
+
+static warn_unused int json_next_token(JSParseState *s)
+{
+ const uint8_t *p;
+ int c;
+ JSAtom atom;
+
+ if (js_check_stack_overflow(s->ctx->rt, 0)) {
+ return js_parse_error(s, "stack overflow");
+ }
+
+ free_token(s, &s->token);
+
+ p = s->last_ptr = s->buf_ptr;
+ s->last_line_num = s->token.line_num;
+ redo:
+ s->token.line_num = s->line_num;
+ s->token.ptr = p;
+ c = *p;
+ switch(c) {
+ case 0:
+ if (p >= s->buf_end) {
+ s->token.val = TOK_EOF;
+ } else {
+ goto def_token;
+ }
+ break;
+ case '\'':
+ if (!s->ext_json) {
+ /* JSON does not accept single quoted strings */
+ goto def_token;
+ }
+ /* fall through */
+ case '\"':
+ if (js_parse_string(s, c, TRUE, p + 1, &s->token, &p))
+ goto fail;
+ break;
+ case '\r': /* accept DOS and MAC newline sequences */
+ if (p[1] == '\n') {
+ p++;
+ }
+ /* fall thru */
+ case '\n':
+ p++;
+ s->line_num++;
+ goto redo;
+ case '\f':
+ case '\v':
+ if (!s->ext_json) {
+ /* JSONWhitespace does not match <VT>, nor <FF> */
+ goto def_token;
+ }
+ /* fall through */
+ case ' ':
+ case '\t':
+ p++;
+ goto redo;
+ case '/':
+ if (!s->ext_json) {
+ /* JSON does not accept comments */
+ goto def_token;
+ }
+ if (p[1] == '*') {
+ /* comment */
+ p += 2;
+ for(;;) {
+ if (*p == '\0' && p >= s->buf_end) {
+ js_parse_error(s, "unexpected end of comment");
+ goto fail;
+ }
+ if (p[0] == '*' && p[1] == '/') {
+ p += 2;
+ break;
+ }
+ if (*p == '\n') {
+ s->line_num++;
+ p++;
+ } else if (*p == '\r') {
+ p++;
+ } else if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ goto redo;
+ } else if (p[1] == '/') {
+ /* line comment */
+ p += 2;
+ for(;;) {
+ if (*p == '\0' && p >= s->buf_end)
+ break;
+ if (*p == '\r' || *p == '\n')
+ break;
+ if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ /* LS or PS are considered as line terminator */
+ if (c == CP_LS || c == CP_PS) {
+ break;
+ } else if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ goto redo;
+ } else {
+ goto def_token;
+ }
+ break;
+ case 'a': case 'b': case 'c': case 'd':
+ case 'e': case 'f': case 'g': case 'h':
+ case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p':
+ case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D':
+ case 'E': case 'F': case 'G': case 'H':
+ case 'I': case 'J': case 'K': case 'L':
+ case 'M': case 'N': case 'O': case 'P':
+ case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ case '$':
+ /* identifier : only pure ascii characters are accepted */
+ p++;
+ atom = json_parse_ident(s, &p, c);
+ if (atom == JS_ATOM_NULL)
+ goto fail;
+ s->token.u.ident.atom = atom;
+ s->token.u.ident.has_escape = FALSE;
+ s->token.u.ident.is_reserved = FALSE;
+ s->token.val = TOK_IDENT;
+ break;
+ case '+':
+ if (!s->ext_json || !is_digit(p[1]))
+ goto def_token;
+ goto parse_number;
+ case '0':
+ if (is_digit(p[1]))
+ goto def_token;
+ goto parse_number;
+ case '-':
+ if (!is_digit(p[1]))
+ goto def_token;
+ goto parse_number;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8':
+ case '9':
+ /* number */
+ parse_number:
+ {
+ JSValue ret;
+ int flags, radix;
+ if (!s->ext_json) {
+ flags = 0;
+ radix = 10;
+ } else {
+ flags = ATOD_ACCEPT_BIN_OCT;
+ radix = 0;
+ }
+ ret = js_atof(s->ctx, (const char *)p, (const char **)&p, radix,
+ flags);
+ if (JS_IsException(ret))
+ goto fail;
+ s->token.val = TOK_NUMBER;
+ s->token.u.num.val = ret;
+ }
+ break;
+ default:
+ if (c >= 128) {
+ js_parse_error(s, "unexpected character");
+ goto fail;
+ }
+ def_token:
+ s->token.val = c;
+ p++;
+ break;
+ }
+ s->buf_ptr = p;
+
+ // dump_token(s, &s->token);
+ return 0;
+
+ fail:
+ s->token.val = TOK_ERROR;
+ return -1;
+}
+
+/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is
+ only set if TOK_IMPORT is returned */
+/* XXX: handle all unicode cases */
+static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator)
+{
+ const uint8_t *p;
+ uint32_t c;
+
+ /* skip spaces and comments */
+ p = *pp;
+ for (;;) {
+ switch(c = *p++) {
+ case '\r':
+ case '\n':
+ if (no_line_terminator)
+ return '\n';
+ continue;
+ case ' ':
+ case '\t':
+ case '\v':
+ case '\f':
+ continue;
+ case '/':
+ if (*p == '/') {
+ if (no_line_terminator)
+ return '\n';
+ while (*p && *p != '\r' && *p != '\n')
+ p++;
+ continue;
+ }
+ if (*p == '*') {
+ while (*++p) {
+ if ((*p == '\r' || *p == '\n') && no_line_terminator)
+ return '\n';
+ if (*p == '*' && p[1] == '/') {
+ p += 2;
+ break;
+ }
+ }
+ continue;
+ }
+ break;
+ case '=':
+ if (*p == '>')
+ return TOK_ARROW;
+ break;
+ default:
+ if (lre_js_is_ident_first(c)) {
+ if (c == 'i') {
+ if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) {
+ return TOK_IN;
+ }
+ if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' &&
+ p[3] == 'r' && p[4] == 't' &&
+ !lre_js_is_ident_next(p[5])) {
+ *pp = p + 5;
+ return TOK_IMPORT;
+ }
+ } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) {
+ return TOK_OF;
+ } else if (c == 'e' &&
+ p[0] == 'x' && p[1] == 'p' && p[2] == 'o' &&
+ p[3] == 'r' && p[4] == 't' &&
+ !lre_js_is_ident_next(p[5])) {
+ *pp = p + 5;
+ return TOK_EXPORT;
+ } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' &&
+ p[2] == 'c' && p[3] == 't' && p[4] == 'i' &&
+ p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) {
+ return TOK_FUNCTION;
+ }
+ return TOK_IDENT;
+ }
+ break;
+ }
+ return c;
+ }
+}
+
+static int peek_token(JSParseState *s, BOOL no_line_terminator)
+{
+ const uint8_t *p = s->buf_ptr;
+ return simple_next_token(&p, no_line_terminator);
+}
+
+/* return true if 'input' contains the source of a module
+ (heuristic). 'input' must be a zero terminated.
+
+ Heuristic: skip comments and expect 'import' keyword not followed
+ by '(' or '.' or export keyword.
+*/
+BOOL JS_DetectModule(const char *input, size_t input_len)
+{
+ const uint8_t *p = (const uint8_t *)input;
+ int tok;
+ switch(simple_next_token(&p, FALSE)) {
+ case TOK_IMPORT:
+ tok = simple_next_token(&p, FALSE);
+ return (tok != '.' && tok != '(');
+ case TOK_EXPORT:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static inline int get_prev_opcode(JSFunctionDef *fd) {
+ if (fd->last_opcode_pos < 0)
+ return OP_invalid;
+ else
+ return fd->byte_code.buf[fd->last_opcode_pos];
+}
+
+static BOOL js_is_live_code(JSParseState *s) {
+ switch (get_prev_opcode(s->cur_func)) {
+ case OP_tail_call:
+ case OP_tail_call_method:
+ case OP_return:
+ case OP_return_undef:
+ case OP_return_async:
+ case OP_throw:
+ case OP_throw_error:
+ case OP_goto:
+#if SHORT_OPCODES
+ case OP_goto8:
+ case OP_goto16:
+#endif
+ case OP_ret:
+ return FALSE;
+ default:
+ return TRUE;
+ }
+}
+
+static void emit_u8(JSParseState *s, uint8_t val)
+{
+ dbuf_putc(&s->cur_func->byte_code, val);
+}
+
+static void emit_u16(JSParseState *s, uint16_t val)
+{
+ dbuf_put_u16(&s->cur_func->byte_code, val);
+}
+
+static void emit_u32(JSParseState *s, uint32_t val)
+{
+ dbuf_put_u32(&s->cur_func->byte_code, val);
+}
+
+static void emit_op(JSParseState *s, uint8_t val)
+{
+ JSFunctionDef *fd = s->cur_func;
+ DynBuf *bc = &fd->byte_code;
+
+ /* Use the line number of the last token used, not the next token,
+ nor the current offset in the source file.
+ */
+ if (unlikely(fd->last_opcode_line_num != s->last_line_num)) {
+ dbuf_putc(bc, OP_line_num);
+ dbuf_put_u32(bc, s->last_line_num);
+ fd->last_opcode_line_num = s->last_line_num;
+ }
+ fd->last_opcode_pos = bc->size;
+ dbuf_putc(bc, val);
+}
+
+static void emit_atom(JSParseState *s, JSAtom name)
+{
+ emit_u32(s, JS_DupAtom(s->ctx, name));
+}
+
+static int update_label(JSFunctionDef *s, int label, int delta)
+{
+ LabelSlot *ls;
+
+ assert(label >= 0 && label < s->label_count);
+ ls = &s->label_slots[label];
+ ls->ref_count += delta;
+ assert(ls->ref_count >= 0);
+ return ls->ref_count;
+}
+
+static int new_label_fd(JSFunctionDef *fd, int label)
+{
+ LabelSlot *ls;
+
+ if (label < 0) {
+ if (js_resize_array(fd->ctx, (void *)&fd->label_slots,
+ sizeof(fd->label_slots[0]),
+ &fd->label_size, fd->label_count + 1))
+ return -1;
+ label = fd->label_count++;
+ ls = &fd->label_slots[label];
+ ls->ref_count = 0;
+ ls->pos = -1;
+ ls->pos2 = -1;
+ ls->addr = -1;
+ ls->first_reloc = NULL;
+ }
+ return label;
+}
+
+static int new_label(JSParseState *s)
+{
+ return new_label_fd(s->cur_func, -1);
+}
+
+/* return the label ID offset */
+static int emit_label(JSParseState *s, int label)
+{
+ if (label >= 0) {
+ emit_op(s, OP_label);
+ emit_u32(s, label);
+ s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size;
+ return s->cur_func->byte_code.size - 4;
+ } else {
+ return -1;
+ }
+}
+
+/* return label or -1 if dead code */
+static int emit_goto(JSParseState *s, int opcode, int label)
+{
+ if (js_is_live_code(s)) {
+ if (label < 0)
+ label = new_label(s);
+ emit_op(s, opcode);
+ emit_u32(s, label);
+ s->cur_func->label_slots[label].ref_count++;
+ return label;
+ }
+ return -1;
+}
+
+/* return the constant pool index. 'val' is not duplicated. */
+static int cpool_add(JSParseState *s, JSValue val)
+{
+ JSFunctionDef *fd = s->cur_func;
+
+ if (js_resize_array(s->ctx, (void *)&fd->cpool, sizeof(fd->cpool[0]),
+ &fd->cpool_size, fd->cpool_count + 1))
+ return -1;
+ fd->cpool[fd->cpool_count++] = val;
+ return fd->cpool_count - 1;
+}
+
+static warn_unused int emit_push_const(JSParseState *s, JSValueConst val,
+ BOOL as_atom)
+{
+ int idx;
+
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_STRING && as_atom) {
+ JSAtom atom;
+ /* warning: JS_NewAtomStr frees the string value */
+ JS_DupValue(s->ctx, val);
+ atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));
+ if (atom != JS_ATOM_NULL && !JS_AtomIsTaggedInt(atom)) {
+ emit_op(s, OP_push_atom_value);
+ emit_u32(s, atom);
+ return 0;
+ }
+ }
+
+ idx = cpool_add(s, JS_DupValue(s->ctx, val));
+ if (idx < 0)
+ return -1;
+ emit_op(s, OP_push_const);
+ emit_u32(s, idx);
+ return 0;
+}
+
+/* return the variable index or -1 if not found,
+ add ARGUMENT_VAR_OFFSET for argument variables */
+static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
+{
+ int i;
+ for(i = fd->arg_count; i-- > 0;) {
+ if (fd->args[i].var_name == name)
+ return i | ARGUMENT_VAR_OFFSET;
+ }
+ return -1;
+}
+
+static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
+{
+ int i;
+ for(i = fd->var_count; i-- > 0;) {
+ if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)
+ return i;
+ }
+ return find_arg(ctx, fd, name);
+}
+
+/* find a variable declaration in a given scope */
+static int find_var_in_scope(JSContext *ctx, JSFunctionDef *fd,
+ JSAtom name, int scope_level)
+{
+ int scope_idx;
+ for(scope_idx = fd->scopes[scope_level].first; scope_idx >= 0;
+ scope_idx = fd->vars[scope_idx].scope_next) {
+ if (fd->vars[scope_idx].scope_level != scope_level)
+ break;
+ if (fd->vars[scope_idx].var_name == name)
+ return scope_idx;
+ }
+ return -1;
+}
+
+/* return true if scope == parent_scope or if scope is a child of
+ parent_scope */
+static BOOL is_child_scope(JSContext *ctx, JSFunctionDef *fd,
+ int scope, int parent_scope)
+{
+ while (scope >= 0) {
+ if (scope == parent_scope)
+ return TRUE;
+ scope = fd->scopes[scope].parent;
+ }
+ return FALSE;
+}
+
+/* find a 'var' declaration in the same scope or a child scope */
+static int find_var_in_child_scope(JSContext *ctx, JSFunctionDef *fd,
+ JSAtom name, int scope_level)
+{
+ int i;
+ for(i = 0; i < fd->var_count; i++) {
+ JSVarDef *vd = &fd->vars[i];
+ if (vd->var_name == name && vd->scope_level == 0) {
+ if (is_child_scope(ctx, fd, vd->scope_next,
+ scope_level))
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+static JSGlobalVar *find_global_var(JSFunctionDef *fd, JSAtom name)
+{
+ int i;
+ for(i = 0; i < fd->global_var_count; i++) {
+ JSGlobalVar *hf = &fd->global_vars[i];
+ if (hf->var_name == name)
+ return hf;
+ }
+ return NULL;
+
+}
+
+static JSGlobalVar *find_lexical_global_var(JSFunctionDef *fd, JSAtom name)
+{
+ JSGlobalVar *hf = find_global_var(fd, name);
+ if (hf && hf->is_lexical)
+ return hf;
+ else
+ return NULL;
+}
+
+static int find_lexical_decl(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
+ int scope_idx, BOOL check_catch_var)
+{
+ while (scope_idx >= 0) {
+ JSVarDef *vd = &fd->vars[scope_idx];
+ if (vd->var_name == name &&
+ (vd->is_lexical || (vd->var_kind == JS_VAR_CATCH &&
+ check_catch_var)))
+ return scope_idx;
+ scope_idx = vd->scope_next;
+ }
+
+ if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_GLOBAL) {
+ if (find_lexical_global_var(fd, name))
+ return GLOBAL_VAR_OFFSET;
+ }
+ return -1;
+}
+
+static int push_scope(JSParseState *s) {
+ if (s->cur_func) {
+ JSFunctionDef *fd = s->cur_func;
+ int scope = fd->scope_count;
+ /* XXX: should check for scope overflow */
+ if ((fd->scope_count + 1) > fd->scope_size) {
+ int new_size;
+ size_t slack;
+ JSVarScope *new_buf;
+ /* XXX: potential arithmetic overflow */
+ new_size = max_int(fd->scope_count + 1, fd->scope_size * 3 / 2);
+ if (fd->scopes == fd->def_scope_array) {
+ new_buf = js_realloc2(s->ctx, NULL, new_size * sizeof(*fd->scopes), &slack);
+ if (!new_buf)
+ return -1;
+ memcpy(new_buf, fd->scopes, fd->scope_count * sizeof(*fd->scopes));
+ } else {
+ new_buf = js_realloc2(s->ctx, fd->scopes, new_size * sizeof(*fd->scopes), &slack);
+ if (!new_buf)
+ return -1;
+ }
+ new_size += slack / sizeof(*new_buf);
+ fd->scopes = new_buf;
+ fd->scope_size = new_size;
+ }
+ fd->scope_count++;
+ fd->scopes[scope].parent = fd->scope_level;
+ fd->scopes[scope].first = fd->scope_first;
+ emit_op(s, OP_enter_scope);
+ emit_u16(s, scope);
+ return fd->scope_level = scope;
+ }
+ return 0;
+}
+
+static int get_first_lexical_var(JSFunctionDef *fd, int scope)
+{
+ while (scope >= 0) {
+ int scope_idx = fd->scopes[scope].first;
+ if (scope_idx >= 0)
+ return scope_idx;
+ scope = fd->scopes[scope].parent;
+ }
+ return -1;
+}
+
+static void pop_scope(JSParseState *s) {
+ if (s->cur_func) {
+ /* disable scoped variables */
+ JSFunctionDef *fd = s->cur_func;
+ int scope = fd->scope_level;
+ emit_op(s, OP_leave_scope);
+ emit_u16(s, scope);
+ fd->scope_level = fd->scopes[scope].parent;
+ fd->scope_first = get_first_lexical_var(fd, fd->scope_level);
+ }
+}
+
+static void close_scopes(JSParseState *s, int scope, int scope_stop)
+{
+ while (scope > scope_stop) {
+ emit_op(s, OP_leave_scope);
+ emit_u16(s, scope);
+ scope = s->cur_func->scopes[scope].parent;
+ }
+}
+
+/* return the variable index or -1 if error */
+static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
+{
+ JSVarDef *vd;
+
+ /* the local variable indexes are currently stored on 16 bits */
+ if (fd->var_count >= JS_MAX_LOCAL_VARS) {
+ JS_ThrowInternalError(ctx, "too many local variables");
+ return -1;
+ }
+ if (js_resize_array(ctx, (void **)&fd->vars, sizeof(fd->vars[0]),
+ &fd->var_size, fd->var_count + 1))
+ return -1;
+ vd = &fd->vars[fd->var_count++];
+ memset(vd, 0, sizeof(*vd));
+ vd->var_name = JS_DupAtom(ctx, name);
+ vd->func_pool_idx = -1;
+ return fd->var_count - 1;
+}
+
+static int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name,
+ JSVarKindEnum var_kind)
+{
+ int idx = add_var(ctx, fd, name);
+ if (idx >= 0) {
+ JSVarDef *vd = &fd->vars[idx];
+ vd->var_kind = var_kind;
+ vd->scope_level = fd->scope_level;
+ vd->scope_next = fd->scope_first;
+ fd->scopes[fd->scope_level].first = idx;
+ fd->scope_first = idx;
+ }
+ return idx;
+}
+
+static int add_func_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
+{
+ int idx = fd->func_var_idx;
+ if (idx < 0 && (idx = add_var(ctx, fd, name)) >= 0) {
+ fd->func_var_idx = idx;
+ fd->vars[idx].var_kind = JS_VAR_FUNCTION_NAME;
+ if (fd->js_mode & JS_MODE_STRICT)
+ fd->vars[idx].is_const = TRUE;
+ }
+ return idx;
+}
+
+static int add_arguments_var(JSContext *ctx, JSFunctionDef *fd)
+{
+ int idx = fd->arguments_var_idx;
+ if (idx < 0 && (idx = add_var(ctx, fd, JS_ATOM_arguments)) >= 0) {
+ fd->arguments_var_idx = idx;
+ }
+ return idx;
+}
+
+/* add an argument definition in the argument scope. Only needed when
+ "eval()" may be called in the argument scope. Return 0 if OK. */
+static int add_arguments_arg(JSContext *ctx, JSFunctionDef *fd)
+{
+ int idx;
+ if (fd->arguments_arg_idx < 0) {
+ idx = find_var_in_scope(ctx, fd, JS_ATOM_arguments, ARG_SCOPE_INDEX);
+ if (idx < 0) {
+ /* XXX: the scope links are not fully updated. May be an
+ issue if there are child scopes of the argument
+ scope */
+ idx = add_var(ctx, fd, JS_ATOM_arguments);
+ if (idx < 0)
+ return -1;
+ fd->vars[idx].scope_next = fd->scopes[ARG_SCOPE_INDEX].first;
+ fd->scopes[ARG_SCOPE_INDEX].first = idx;
+ fd->vars[idx].scope_level = ARG_SCOPE_INDEX;
+ fd->vars[idx].is_lexical = TRUE;
+
+ fd->arguments_arg_idx = idx;
+ }
+ }
+ return 0;
+}
+
+static int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
+{
+ JSVarDef *vd;
+
+ /* the local variable indexes are currently stored on 16 bits */
+ if (fd->arg_count >= JS_MAX_LOCAL_VARS) {
+ JS_ThrowInternalError(ctx, "too many arguments");
+ return -1;
+ }
+ if (js_resize_array(ctx, (void **)&fd->args, sizeof(fd->args[0]),
+ &fd->arg_size, fd->arg_count + 1))
+ return -1;
+ vd = &fd->args[fd->arg_count++];
+ memset(vd, 0, sizeof(*vd));
+ vd->var_name = JS_DupAtom(ctx, name);
+ vd->func_pool_idx = -1;
+ return fd->arg_count - 1;
+}
+
+/* add a global variable definition */
+static JSGlobalVar *add_global_var(JSContext *ctx, JSFunctionDef *s,
+ JSAtom name)
+{
+ JSGlobalVar *hf;
+
+ if (js_resize_array(ctx, (void **)&s->global_vars,
+ sizeof(s->global_vars[0]),
+ &s->global_var_size, s->global_var_count + 1))
+ return NULL;
+ hf = &s->global_vars[s->global_var_count++];
+ hf->cpool_idx = -1;
+ hf->force_init = FALSE;
+ hf->is_lexical = FALSE;
+ hf->is_const = FALSE;
+ hf->scope_level = s->scope_level;
+ hf->var_name = JS_DupAtom(ctx, name);
+ return hf;
+}
+
+typedef enum {
+ JS_VAR_DEF_WITH,
+ JS_VAR_DEF_LET,
+ JS_VAR_DEF_CONST,
+ JS_VAR_DEF_FUNCTION_DECL, /* function declaration */
+ JS_VAR_DEF_NEW_FUNCTION_DECL, /* async/generator function declaration */
+ JS_VAR_DEF_CATCH,
+ JS_VAR_DEF_VAR,
+} JSVarDefEnum;
+
+static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name,
+ JSVarDefEnum var_def_type)
+{
+ JSContext *ctx = s->ctx;
+ JSVarDef *vd;
+ int idx;
+
+ switch (var_def_type) {
+ case JS_VAR_DEF_WITH:
+ idx = add_scope_var(ctx, fd, name, JS_VAR_NORMAL);
+ break;
+
+ case JS_VAR_DEF_LET:
+ case JS_VAR_DEF_CONST:
+ case JS_VAR_DEF_FUNCTION_DECL:
+ case JS_VAR_DEF_NEW_FUNCTION_DECL:
+ idx = find_lexical_decl(ctx, fd, name, fd->scope_first, TRUE);
+ if (idx >= 0) {
+ if (idx < GLOBAL_VAR_OFFSET) {
+ if (fd->vars[idx].scope_level == fd->scope_level) {
+ /* same scope: in non strict mode, functions
+ can be redefined (annex B.3.3.4). */
+ if (!(!(fd->js_mode & JS_MODE_STRICT) &&
+ var_def_type == JS_VAR_DEF_FUNCTION_DECL &&
+ fd->vars[idx].var_kind == JS_VAR_FUNCTION_DECL)) {
+ goto redef_lex_error;
+ }
+ } else if (fd->vars[idx].var_kind == JS_VAR_CATCH && (fd->vars[idx].scope_level + 2) == fd->scope_level) {
+ goto redef_lex_error;
+ }
+ } else {
+ if (fd->scope_level == fd->body_scope) {
+ redef_lex_error:
+ /* redefining a scoped var in the same scope: error */
+ return js_parse_error(s, "invalid redefinition of lexical identifier");
+ }
+ }
+ }
+ if (var_def_type != JS_VAR_DEF_FUNCTION_DECL &&
+ var_def_type != JS_VAR_DEF_NEW_FUNCTION_DECL &&
+ fd->scope_level == fd->body_scope &&
+ find_arg(ctx, fd, name) >= 0) {
+ /* lexical variable redefines a parameter name */
+ return js_parse_error(s, "invalid redefinition of parameter name");
+ }
+
+ if (find_var_in_child_scope(ctx, fd, name, fd->scope_level) >= 0) {
+ return js_parse_error(s, "invalid redefinition of a variable");
+ }
+
+ if (fd->is_global_var) {
+ JSGlobalVar *hf;
+ hf = find_global_var(fd, name);
+ if (hf && is_child_scope(ctx, fd, hf->scope_level,
+ fd->scope_level)) {
+ return js_parse_error(s, "invalid redefinition of global identifier");
+ }
+ }
+
+ if (fd->is_eval &&
+ (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
+ fd->eval_type == JS_EVAL_TYPE_MODULE) &&
+ fd->scope_level == fd->body_scope) {
+ JSGlobalVar *hf;
+ hf = add_global_var(s->ctx, fd, name);
+ if (!hf)
+ return -1;
+ hf->is_lexical = TRUE;
+ hf->is_const = (var_def_type == JS_VAR_DEF_CONST);
+ idx = GLOBAL_VAR_OFFSET;
+ } else {
+ JSVarKindEnum var_kind;
+ if (var_def_type == JS_VAR_DEF_FUNCTION_DECL)
+ var_kind = JS_VAR_FUNCTION_DECL;
+ else if (var_def_type == JS_VAR_DEF_NEW_FUNCTION_DECL)
+ var_kind = JS_VAR_NEW_FUNCTION_DECL;
+ else
+ var_kind = JS_VAR_NORMAL;
+ idx = add_scope_var(ctx, fd, name, var_kind);
+ if (idx >= 0) {
+ vd = &fd->vars[idx];
+ vd->is_lexical = 1;
+ vd->is_const = (var_def_type == JS_VAR_DEF_CONST);
+ }
+ }
+ break;
+
+ case JS_VAR_DEF_CATCH:
+ idx = add_scope_var(ctx, fd, name, JS_VAR_CATCH);
+ break;
+
+ case JS_VAR_DEF_VAR:
+ if (find_lexical_decl(ctx, fd, name, fd->scope_first,
+ FALSE) >= 0) {
+ invalid_lexical_redefinition:
+ /* error to redefine a var that inside a lexical scope */
+ return js_parse_error(s, "invalid redefinition of lexical identifier");
+ }
+ if (fd->is_global_var) {
+ JSGlobalVar *hf;
+ hf = find_global_var(fd, name);
+ if (hf && hf->is_lexical && hf->scope_level == fd->scope_level &&
+ fd->eval_type == JS_EVAL_TYPE_MODULE) {
+ goto invalid_lexical_redefinition;
+ }
+ hf = add_global_var(s->ctx, fd, name);
+ if (!hf)
+ return -1;
+ idx = GLOBAL_VAR_OFFSET;
+ } else {
+ /* if the variable already exists, don't add it again */
+ idx = find_var(ctx, fd, name);
+ if (idx >= 0)
+ break;
+ idx = add_var(ctx, fd, name);
+ if (idx >= 0) {
+ if (name == JS_ATOM_arguments && fd->has_arguments_binding)
+ fd->arguments_var_idx = idx;
+ fd->vars[idx].scope_next = fd->scope_level;
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ return idx;
+}
+
+/* add a private field variable in the current scope */
+static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,
+ JSAtom name, JSVarKindEnum var_kind)
+{
+ JSContext *ctx = s->ctx;
+ JSVarDef *vd;
+ int idx;
+
+ idx = add_scope_var(ctx, fd, name, var_kind);
+ if (idx < 0)
+ return idx;
+ vd = &fd->vars[idx];
+ vd->is_lexical = 1;
+ vd->is_const = 1;
+ return idx;
+}
+
+static warn_unused int js_parse_expr(JSParseState *s);
+static warn_unused int js_parse_function_decl(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name, const uint8_t *ptr,
+ int start_line);
+static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);
+static warn_unused int js_parse_function_decl2(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+ const uint8_t *ptr,
+ int function_line_num,
+ JSParseExportEnum export_flag,
+ JSFunctionDef **pfd);
+static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags);
+static warn_unused int js_parse_assign_expr(JSParseState *s);
+static warn_unused int js_parse_unary(JSParseState *s, int parse_flags);
+static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
+ JSAtom label_name,
+ int label_break, int label_cont,
+ int drop_count);
+static void pop_break_entry(JSFunctionDef *fd);
+static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
+ JSAtom local_name, JSAtom export_name,
+ JSExportTypeEnum export_type);
+
+/* Note: all the fields are already sealed except length */
+static int seal_template_obj(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ JSShapeProperty *prs;
+
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property1(p, JS_ATOM_length);
+ if (prs) {
+ if (js_update_property_flags(ctx, p, &prs,
+ prs->flags & ~(JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)))
+ return -1;
+ }
+ p->extensible = FALSE;
+ return 0;
+}
+
+static warn_unused int js_parse_template(JSParseState *s, int call, int *argc)
+{
+ JSContext *ctx = s->ctx;
+ JSValue raw_array, template_object;
+ JSToken cooked;
+ int depth, ret;
+
+ raw_array = JS_UNDEFINED; /* avoid warning */
+ template_object = JS_UNDEFINED; /* avoid warning */
+ if (call) {
+ /* Create a template object: an array of cooked strings */
+ /* Create an array of raw strings and store it to the raw property */
+ template_object = JS_NewArray(ctx);
+ if (JS_IsException(template_object))
+ return -1;
+ // pool_idx = s->cur_func->cpool_count;
+ ret = emit_push_const(s, template_object, 0);
+ JS_FreeValue(ctx, template_object);
+ if (ret)
+ return -1;
+ raw_array = JS_NewArray(ctx);
+ if (JS_IsException(raw_array))
+ return -1;
+ if (JS_DefinePropertyValue(ctx, template_object, JS_ATOM_raw,
+ raw_array, JS_PROP_THROW) < 0) {
+ return -1;
+ }
+ }
+
+ depth = 0;
+ while (s->token.val == TOK_TEMPLATE) {
+ const uint8_t *p = s->token.ptr + 1;
+ cooked = s->token;
+ if (call) {
+ if (JS_DefinePropertyValueUint32(ctx, raw_array, depth,
+ JS_DupValue(ctx, s->token.u.str.str),
+ JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
+ return -1;
+ }
+ /* re-parse the string with escape sequences but do not throw a
+ syntax error if it contains invalid sequences
+ */
+ if (js_parse_string(s, '`', FALSE, p, &cooked, &p)) {
+ cooked.u.str.str = JS_UNDEFINED;
+ }
+ if (JS_DefinePropertyValueUint32(ctx, template_object, depth,
+ cooked.u.str.str,
+ JS_PROP_ENUMERABLE | JS_PROP_THROW) < 0) {
+ return -1;
+ }
+ } else {
+ JSString *str;
+ /* re-parse the string with escape sequences and throw a
+ syntax error if it contains invalid sequences
+ */
+ JS_FreeValue(ctx, s->token.u.str.str);
+ s->token.u.str.str = JS_UNDEFINED;
+ if (js_parse_string(s, '`', TRUE, p, &cooked, &p))
+ return -1;
+ str = JS_VALUE_GET_STRING(cooked.u.str.str);
+ if (str->len != 0 || depth == 0) {
+ ret = emit_push_const(s, cooked.u.str.str, 1);
+ JS_FreeValue(s->ctx, cooked.u.str.str);
+ if (ret)
+ return -1;
+ if (depth == 0) {
+ if (s->token.u.str.sep == '`')
+ goto done1;
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_concat);
+ }
+ depth++;
+ } else {
+ JS_FreeValue(s->ctx, cooked.u.str.str);
+ }
+ }
+ if (s->token.u.str.sep == '`')
+ goto done;
+ if (next_token(s))
+ return -1;
+ if (js_parse_expr(s))
+ return -1;
+ depth++;
+ if (s->token.val != '}') {
+ return js_parse_error(s, "expected '}' after template expression");
+ }
+ /* XXX: should convert to string at this stage? */
+ free_token(s, &s->token);
+ /* Resume TOK_TEMPLATE parsing (s->token.line_num and
+ * s->token.ptr are OK) */
+ s->got_lf = FALSE;
+ s->last_line_num = s->token.line_num;
+ if (js_parse_template_part(s, s->buf_ptr))
+ return -1;
+ }
+ return js_parse_expect(s, TOK_TEMPLATE);
+
+ done:
+ if (call) {
+ /* Seal the objects */
+ seal_template_obj(ctx, raw_array);
+ seal_template_obj(ctx, template_object);
+ *argc = depth + 1;
+ } else {
+ emit_op(s, OP_call_method);
+ emit_u16(s, depth - 1);
+ }
+ done1:
+ return next_token(s);
+}
+
+
+#define PROP_TYPE_IDENT 0
+#define PROP_TYPE_VAR 1
+#define PROP_TYPE_GET 2
+#define PROP_TYPE_SET 3
+#define PROP_TYPE_STAR 4
+#define PROP_TYPE_ASYNC 5
+#define PROP_TYPE_ASYNC_STAR 6
+
+#define PROP_TYPE_PRIVATE (1 << 4)
+
+static BOOL token_is_ident(int tok)
+{
+ /* Accept keywords and reserved words as property names */
+ return (tok == TOK_IDENT ||
+ (tok >= TOK_FIRST_KEYWORD &&
+ tok <= TOK_LAST_KEYWORD));
+}
+
+/* if the property is an expression, name = JS_ATOM_NULL */
+static int warn_unused js_parse_property_name(JSParseState *s,
+ JSAtom *pname,
+ BOOL allow_method, BOOL allow_var,
+ BOOL allow_private)
+{
+ int is_private = 0;
+ BOOL is_non_reserved_ident;
+ JSAtom name;
+ int prop_type;
+
+ prop_type = PROP_TYPE_IDENT;
+ if (allow_method) {
+ if (token_is_pseudo_keyword(s, JS_ATOM_get)
+ || token_is_pseudo_keyword(s, JS_ATOM_set)) {
+ /* get x(), set x() */
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail1;
+ if (s->token.val == ':' || s->token.val == ',' ||
+ s->token.val == '}' || s->token.val == '(') {
+ is_non_reserved_ident = TRUE;
+ goto ident_found;
+ }
+ prop_type = PROP_TYPE_GET + (name == JS_ATOM_set);
+ JS_FreeAtom(s->ctx, name);
+ } else if (s->token.val == '*') {
+ if (next_token(s))
+ goto fail;
+ prop_type = PROP_TYPE_STAR;
+ } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) != '\n') {
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail1;
+ if (s->token.val == ':' || s->token.val == ',' ||
+ s->token.val == '}' || s->token.val == '(') {
+ is_non_reserved_ident = TRUE;
+ goto ident_found;
+ }
+ JS_FreeAtom(s->ctx, name);
+ if (s->token.val == '*') {
+ if (next_token(s))
+ goto fail;
+ prop_type = PROP_TYPE_ASYNC_STAR;
+ } else {
+ prop_type = PROP_TYPE_ASYNC;
+ }
+ }
+ }
+
+ if (token_is_ident(s->token.val)) {
+ /* variable can only be a non-reserved identifier */
+ is_non_reserved_ident =
+ (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved);
+ /* keywords and reserved words have a valid atom */
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail1;
+ ident_found:
+ if (is_non_reserved_ident &&
+ prop_type == PROP_TYPE_IDENT && allow_var) {
+ if (!(s->token.val == ':' ||
+ (s->token.val == '(' && allow_method))) {
+ prop_type = PROP_TYPE_VAR;
+ }
+ }
+ } else if (s->token.val == TOK_STRING) {
+ name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
+ if (name == JS_ATOM_NULL)
+ goto fail;
+ if (next_token(s))
+ goto fail1;
+ } else if (s->token.val == TOK_NUMBER) {
+ JSValue val;
+ val = s->token.u.num.val;
+#ifdef CONFIG_BIGNUM
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ val = s->ctx->rt->bigfloat_ops.
+ mul_pow10_to_float64(s->ctx, &p->num,
+ s->token.u.num.exponent);
+ if (JS_IsException(val))
+ goto fail;
+ name = JS_ValueToAtom(s->ctx, val);
+ JS_FreeValue(s->ctx, val);
+ } else
+#endif
+ {
+ name = JS_ValueToAtom(s->ctx, val);
+ }
+ if (name == JS_ATOM_NULL)
+ goto fail;
+ if (next_token(s))
+ goto fail1;
+ } else if (s->token.val == '[') {
+ if (next_token(s))
+ goto fail;
+ if (js_parse_expr(s))
+ goto fail;
+ if (js_parse_expect(s, ']'))
+ goto fail;
+ name = JS_ATOM_NULL;
+ } else if (s->token.val == TOK_PRIVATE_NAME && allow_private) {
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail1;
+ is_private = PROP_TYPE_PRIVATE;
+ } else {
+ goto invalid_prop;
+ }
+ if (prop_type != PROP_TYPE_IDENT && prop_type != PROP_TYPE_VAR &&
+ s->token.val != '(') {
+ JS_FreeAtom(s->ctx, name);
+ invalid_prop:
+ js_parse_error(s, "invalid property name");
+ goto fail;
+ }
+ *pname = name;
+ return prop_type | is_private;
+ fail1:
+ JS_FreeAtom(s->ctx, name);
+ fail:
+ *pname = JS_ATOM_NULL;
+ return -1;
+}
+
+typedef struct JSParsePos {
+ int last_line_num;
+ int line_num;
+ BOOL got_lf;
+ const uint8_t *ptr;
+} JSParsePos;
+
+static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)
+{
+ sp->last_line_num = s->last_line_num;
+ sp->line_num = s->token.line_num;
+ sp->ptr = s->token.ptr;
+ sp->got_lf = s->got_lf;
+ return 0;
+}
+
+static warn_unused int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
+{
+ s->token.line_num = sp->last_line_num;
+ s->line_num = sp->line_num;
+ s->buf_ptr = sp->ptr;
+ s->got_lf = sp->got_lf;
+ return next_token(s);
+}
+
+/* return TRUE if a regexp literal is allowed after this token */
+static BOOL is_regexp_allowed(int tok)
+{
+ switch (tok) {
+ case TOK_NUMBER:
+ case TOK_STRING:
+ case TOK_REGEXP:
+ case TOK_DEC:
+ case TOK_INC:
+ case TOK_NULL:
+ case TOK_FALSE:
+ case TOK_TRUE:
+ case TOK_THIS:
+ case ')':
+ case ']':
+ case '}': /* XXX: regexp may occur after */
+ case TOK_IDENT:
+ return FALSE;
+ default:
+ return TRUE;
+ }
+}
+
+#define SKIP_HAS_SEMI (1 << 0)
+#define SKIP_HAS_ELLIPSIS (1 << 1)
+#define SKIP_HAS_ASSIGNMENT (1 << 2)
+
+/* XXX: improve speed with early bailout */
+/* XXX: no longer works if regexps are present. Could use previous
+ regexp parsing heuristics to handle most cases */
+static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_terminator)
+{
+ char state[256];
+ size_t level = 0;
+ JSParsePos pos;
+ int last_tok, tok = TOK_EOF;
+ int tok_len, bits = 0;
+ char c;
+
+ /* protect from underflow */
+ state[level++] = 0;
+
+ js_parse_get_pos(s, &pos);
+ last_tok = 0;
+ for (;;) {
+ switch(s->token.val) {
+ case '(':
+ case '[':
+ case '{':
+ if (level >= sizeof(state))
+ goto done;
+ state[level++] = s->token.val;
+ break;
+ case ')':
+ if (state[--level] != '(')
+ goto done;
+ break;
+ case ']':
+ if (state[--level] != '[')
+ goto done;
+ break;
+ case '}':
+ c = state[--level];
+ if (c == '`') {
+ /* continue the parsing of the template */
+ free_token(s, &s->token);
+ /* Resume TOK_TEMPLATE parsing (s->token.line_num and
+ * s->token.ptr are OK) */
+ s->got_lf = FALSE;
+ s->last_line_num = s->token.line_num;
+ if (js_parse_template_part(s, s->buf_ptr))
+ goto done;
+ goto handle_template;
+ } else if (c != '{') {
+ goto done;
+ }
+ break;
+ case TOK_TEMPLATE:
+ handle_template:
+ if (s->token.u.str.sep != '`') {
+ /* '${' inside the template : closing '}' and continue
+ parsing the template */
+ if (level >= sizeof(state))
+ goto done;
+ state[level++] = '`';
+ }
+ break;
+ case TOK_EOF:
+ goto done;
+ case ';':
+ if (level == 2) {
+ bits |= SKIP_HAS_SEMI;
+ }
+ break;
+ case TOK_ELLIPSIS:
+ if (level == 2) {
+ bits |= SKIP_HAS_ELLIPSIS;
+ }
+ break;
+ case '=':
+ bits |= SKIP_HAS_ASSIGNMENT;
+ break;
+
+ case TOK_DIV_ASSIGN:
+ tok_len = 2;
+ goto parse_regexp;
+ case '/':
+ tok_len = 1;
+ parse_regexp:
+ if (is_regexp_allowed(last_tok)) {
+ s->buf_ptr -= tok_len;
+ if (js_parse_regexp(s)) {
+ /* XXX: should clear the exception */
+ goto done;
+ }
+ }
+ break;
+ }
+ /* last_tok is only used to recognize regexps */
+ if (s->token.val == TOK_IDENT &&
+ (token_is_pseudo_keyword(s, JS_ATOM_of) ||
+ token_is_pseudo_keyword(s, JS_ATOM_yield))) {
+ last_tok = TOK_OF;
+ } else {
+ last_tok = s->token.val;
+ }
+ if (next_token(s)) {
+ /* XXX: should clear the exception generated by next_token() */
+ break;
+ }
+ if (level <= 1) {
+ tok = s->token.val;
+ if (token_is_pseudo_keyword(s, JS_ATOM_of))
+ tok = TOK_OF;
+ if (no_line_terminator && s->last_line_num != s->token.line_num)
+ tok = '\n';
+ break;
+ }
+ }
+ done:
+ if (pbits) {
+ *pbits = bits;
+ }
+ if (js_parse_seek_token(s, &pos))
+ return -1;
+ return tok;
+}
+
+static void set_object_name(JSParseState *s, JSAtom name)
+{
+ JSFunctionDef *fd = s->cur_func;
+ int opcode;
+
+ opcode = get_prev_opcode(fd);
+ if (opcode == OP_set_name) {
+ /* XXX: should free atom after OP_set_name? */
+ fd->byte_code.size = fd->last_opcode_pos;
+ fd->last_opcode_pos = -1;
+ emit_op(s, OP_set_name);
+ emit_atom(s, name);
+ } else if (opcode == OP_set_class_name) {
+ int define_class_pos;
+ JSAtom atom;
+ define_class_pos = fd->last_opcode_pos + 1 -
+ get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
+ /* for consistency we free the previous atom which is
+ JS_ATOM_empty_string */
+ atom = get_u32(fd->byte_code.buf + define_class_pos + 1);
+ JS_FreeAtom(s->ctx, atom);
+ put_u32(fd->byte_code.buf + define_class_pos + 1,
+ JS_DupAtom(s->ctx, name));
+ fd->last_opcode_pos = -1;
+ }
+}
+
+static void set_object_name_computed(JSParseState *s)
+{
+ JSFunctionDef *fd = s->cur_func;
+ int opcode;
+
+ opcode = get_prev_opcode(fd);
+ if (opcode == OP_set_name) {
+ /* XXX: should free atom after OP_set_name? */
+ fd->byte_code.size = fd->last_opcode_pos;
+ fd->last_opcode_pos = -1;
+ emit_op(s, OP_set_name_computed);
+ } else if (opcode == OP_set_class_name) {
+ int define_class_pos;
+ define_class_pos = fd->last_opcode_pos + 1 -
+ get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ assert(fd->byte_code.buf[define_class_pos] == OP_define_class);
+ fd->byte_code.buf[define_class_pos] = OP_define_class_computed;
+ fd->last_opcode_pos = -1;
+ }
+}
+
+static warn_unused int js_parse_object_literal(JSParseState *s)
+{
+ JSAtom name = JS_ATOM_NULL;
+ const uint8_t *start_ptr;
+ int start_line, prop_type;
+ BOOL has_proto;
+
+ if (next_token(s))
+ goto fail;
+ /* XXX: add an initial length that will be patched back */
+ emit_op(s, OP_object);
+ has_proto = FALSE;
+ while (s->token.val != '}') {
+ /* specific case for getter/setter */
+ start_ptr = s->token.ptr;
+ start_line = s->token.line_num;
+
+ if (s->token.val == TOK_ELLIPSIS) {
+ if (next_token(s))
+ return -1;
+ if (js_parse_assign_expr(s))
+ return -1;
+ emit_op(s, OP_null); /* dummy excludeList */
+ emit_op(s, OP_copy_data_properties);
+ emit_u8(s, 2 | (1 << 2) | (0 << 5));
+ emit_op(s, OP_drop); /* pop excludeList */
+ emit_op(s, OP_drop); /* pop src object */
+ goto next;
+ }
+
+ prop_type = js_parse_property_name(s, &name, TRUE, TRUE, FALSE);
+ if (prop_type < 0)
+ goto fail;
+
+ if (prop_type == PROP_TYPE_VAR) {
+ /* shortcut for x: x */
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ emit_op(s, OP_define_field);
+ emit_atom(s, name);
+ } else if (s->token.val == '(') {
+ BOOL is_getset = (prop_type == PROP_TYPE_GET ||
+ prop_type == PROP_TYPE_SET);
+ JSParseFunctionEnum func_type;
+ JSFunctionKindEnum func_kind;
+ int op_flags;
+
+ func_kind = JS_FUNC_NORMAL;
+ if (is_getset) {
+ func_type = JS_PARSE_FUNC_GETTER + prop_type - PROP_TYPE_GET;
+ } else {
+ func_type = JS_PARSE_FUNC_METHOD;
+ if (prop_type == PROP_TYPE_STAR)
+ func_kind = JS_FUNC_GENERATOR;
+ else if (prop_type == PROP_TYPE_ASYNC)
+ func_kind = JS_FUNC_ASYNC;
+ else if (prop_type == PROP_TYPE_ASYNC_STAR)
+ func_kind = JS_FUNC_ASYNC_GENERATOR;
+ }
+ if (js_parse_function_decl(s, func_type, func_kind, JS_ATOM_NULL,
+ start_ptr, start_line))
+ goto fail;
+ if (name == JS_ATOM_NULL) {
+ emit_op(s, OP_define_method_computed);
+ } else {
+ emit_op(s, OP_define_method);
+ emit_atom(s, name);
+ }
+ if (is_getset) {
+ op_flags = OP_DEFINE_METHOD_GETTER +
+ prop_type - PROP_TYPE_GET;
+ } else {
+ op_flags = OP_DEFINE_METHOD_METHOD;
+ }
+ emit_u8(s, op_flags | OP_DEFINE_METHOD_ENUMERABLE);
+ } else {
+ if (js_parse_expect(s, ':'))
+ goto fail;
+ if (js_parse_assign_expr(s))
+ goto fail;
+ if (name == JS_ATOM_NULL) {
+ set_object_name_computed(s);
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_drop);
+ } else if (name == JS_ATOM___proto__) {
+ if (has_proto) {
+ js_parse_error(s, "duplicate __proto__ property name");
+ goto fail;
+ }
+ emit_op(s, OP_set_proto);
+ has_proto = TRUE;
+ } else {
+ set_object_name(s, name);
+ emit_op(s, OP_define_field);
+ emit_atom(s, name);
+ }
+ }
+ JS_FreeAtom(s->ctx, name);
+ next:
+ name = JS_ATOM_NULL;
+ if (s->token.val != ',')
+ break;
+ if (next_token(s))
+ goto fail;
+ }
+ if (js_parse_expect(s, '}'))
+ goto fail;
+ return 0;
+ fail:
+ JS_FreeAtom(s->ctx, name);
+ return -1;
+}
+
+/* allow the 'in' binary operator */
+#define PF_IN_ACCEPTED (1 << 0)
+/* allow function calls parsing in js_parse_postfix_expr() */
+#define PF_POSTFIX_CALL (1 << 1)
+/* allow arrow functions parsing in js_parse_postfix_expr() */
+#define PF_ARROW_FUNC (1 << 2)
+/* allow the exponentiation operator in js_parse_unary() */
+#define PF_POW_ALLOWED (1 << 3)
+/* forbid the exponentiation operator in js_parse_unary() */
+#define PF_POW_FORBIDDEN (1 << 4)
+
+static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags);
+
+static warn_unused int js_parse_left_hand_side_expr(JSParseState *s)
+{
+ return js_parse_postfix_expr(s, PF_POSTFIX_CALL);
+}
+
+/* XXX: could generate specific bytecode */
+static warn_unused int js_parse_class_default_ctor(JSParseState *s,
+ BOOL has_super,
+ JSFunctionDef **pfd)
+{
+ JSParsePos pos;
+ const char *str;
+ int ret, line_num;
+ JSParseFunctionEnum func_type;
+ const uint8_t *saved_buf_end;
+
+ js_parse_get_pos(s, &pos);
+ if (has_super) {
+ /* spec change: no argument evaluation */
+ str = "(){super(...arguments);}";
+ func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
+ } else {
+ str = "(){}";
+ func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
+ }
+ line_num = s->token.line_num;
+ saved_buf_end = s->buf_end;
+ s->buf_ptr = (uint8_t *)str;
+ s->buf_end = (uint8_t *)(str + strlen(str));
+ ret = next_token(s);
+ if (!ret) {
+ ret = js_parse_function_decl2(s, func_type, JS_FUNC_NORMAL,
+ JS_ATOM_NULL, (uint8_t *)str,
+ line_num, JS_PARSE_EXPORT_NONE, pfd);
+ }
+ s->buf_end = saved_buf_end;
+ ret |= js_parse_seek_token(s, &pos);
+ return ret;
+}
+
+/* find field in the current scope */
+static int find_private_class_field(JSContext *ctx, JSFunctionDef *fd,
+ JSAtom name, int scope_level)
+{
+ int idx;
+ idx = fd->scopes[scope_level].first;
+ while (idx != -1) {
+ if (fd->vars[idx].scope_level != scope_level)
+ break;
+ if (fd->vars[idx].var_name == name)
+ return idx;
+ idx = fd->vars[idx].scope_next;
+ }
+ return -1;
+}
+
+/* initialize the class fields, called by the constructor. Note:
+ super() can be called in an arrow function, so <this> and
+ <class_fields_init> can be variable references */
+static void emit_class_field_init(JSParseState *s)
+{
+ int label_next;
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_class_fields_init);
+ emit_u16(s, s->cur_func->scope_level);
+
+ /* no need to call the class field initializer if not defined */
+ emit_op(s, OP_dup);
+ label_next = emit_goto(s, OP_if_false, -1);
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_op(s, OP_swap);
+
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+
+ emit_label(s, label_next);
+ emit_op(s, OP_drop);
+}
+
+/* build a private setter function name from the private getter name */
+static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name)
+{
+ return js_atom_concat_str(ctx, name, "<set>");
+}
+
+typedef struct {
+ JSFunctionDef *fields_init_fd;
+ int computed_fields_count;
+ BOOL has_brand;
+ int brand_push_pos;
+} ClassFieldsDef;
+
+static warn_unused int emit_class_init_start(JSParseState *s,
+ ClassFieldsDef *cf)
+{
+ int label_add_brand;
+
+ cf->fields_init_fd = js_parse_function_class_fields_init(s);
+ if (!cf->fields_init_fd)
+ return -1;
+
+ s->cur_func = cf->fields_init_fd;
+
+ /* XXX: would be better to add the code only if needed, maybe in a
+ later pass */
+ emit_op(s, OP_push_false); /* will be patched later */
+ cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos;
+ label_add_brand = emit_goto(s, OP_if_false, -1);
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_home_object);
+ emit_u16(s, 0);
+
+ emit_op(s, OP_add_brand);
+
+ emit_label(s, label_add_brand);
+
+ s->cur_func = s->cur_func->parent;
+ return 0;
+}
+
+static warn_unused int add_brand(JSParseState *s, ClassFieldsDef *cf)
+{
+ if (!cf->has_brand) {
+ /* define the brand field in 'this' of the initializer */
+ if (!cf->fields_init_fd) {
+ if (emit_class_init_start(s, cf))
+ return -1;
+ }
+ /* patch the start of the function to enable the OP_add_brand code */
+ cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true;
+
+ cf->has_brand = TRUE;
+ }
+ return 0;
+}
+
+static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)
+{
+ int cpool_idx;
+
+ s->cur_func = cf->fields_init_fd;
+ emit_op(s, OP_return_undef);
+ s->cur_func = s->cur_func->parent;
+
+ cpool_idx = cpool_add(s, JS_NULL);
+ cf->fields_init_fd->parent_cpool_idx = cpool_idx;
+ emit_op(s, OP_fclosure);
+ emit_u32(s, cpool_idx);
+ emit_op(s, OP_set_home_object);
+}
+
+
+static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr,
+ JSParseExportEnum export_flag)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionDef *fd = s->cur_func;
+ JSAtom name = JS_ATOM_NULL, class_name = JS_ATOM_NULL, class_name1;
+ JSAtom class_var_name = JS_ATOM_NULL;
+ JSFunctionDef *method_fd, *ctor_fd;
+ int saved_js_mode, class_name_var_idx, prop_type, ctor_cpool_offset;
+ int class_flags = 0, i, define_class_offset;
+ BOOL is_static, is_private;
+ const uint8_t *class_start_ptr = s->token.ptr;
+ const uint8_t *start_ptr;
+ ClassFieldsDef class_fields[2];
+
+ /* classes are parsed and executed in strict mode */
+ saved_js_mode = fd->js_mode;
+ fd->js_mode |= JS_MODE_STRICT;
+ if (next_token(s))
+ goto fail;
+ if (s->token.val == TOK_IDENT) {
+ if (s->token.u.ident.is_reserved) {
+ js_parse_error_reserved_identifier(s);
+ goto fail;
+ }
+ class_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail;
+ } else if (!is_class_expr && export_flag != JS_PARSE_EXPORT_DEFAULT) {
+ js_parse_error(s, "class statement requires a name");
+ goto fail;
+ }
+ if (!is_class_expr) {
+ if (class_name == JS_ATOM_NULL)
+ class_var_name = JS_ATOM__default_; /* export default */
+ else
+ class_var_name = class_name;
+ class_var_name = JS_DupAtom(ctx, class_var_name);
+ }
+
+ push_scope(s);
+
+ if (s->token.val == TOK_EXTENDS) {
+ class_flags = JS_DEFINE_CLASS_HAS_HERITAGE;
+ if (next_token(s))
+ goto fail;
+ if (js_parse_left_hand_side_expr(s))
+ goto fail;
+ } else {
+ emit_op(s, OP_undefined);
+ }
+
+ /* add a 'const' definition for the class name */
+ if (class_name != JS_ATOM_NULL) {
+ class_name_var_idx = define_var(s, fd, class_name, JS_VAR_DEF_CONST);
+ if (class_name_var_idx < 0)
+ goto fail;
+ }
+
+ if (js_parse_expect(s, '{'))
+ goto fail;
+
+ /* this scope contains the private fields */
+ push_scope(s);
+
+ emit_op(s, OP_push_const);
+ ctor_cpool_offset = fd->byte_code.size;
+ emit_u32(s, 0); /* will be patched at the end of the class parsing */
+
+ if (class_name == JS_ATOM_NULL) {
+ if (class_var_name != JS_ATOM_NULL)
+ class_name1 = JS_ATOM_default;
+ else
+ class_name1 = JS_ATOM_empty_string;
+ } else {
+ class_name1 = class_name;
+ }
+
+ emit_op(s, OP_define_class);
+ emit_atom(s, class_name1);
+ emit_u8(s, class_flags);
+ define_class_offset = fd->last_opcode_pos;
+
+ for(i = 0; i < 2; i++) {
+ ClassFieldsDef *cf = &class_fields[i];
+ cf->fields_init_fd = NULL;
+ cf->computed_fields_count = 0;
+ cf->has_brand = FALSE;
+ }
+
+ ctor_fd = NULL;
+ while (s->token.val != '}') {
+ if (s->token.val == ';') {
+ if (next_token(s))
+ goto fail;
+ continue;
+ }
+ is_static = (s->token.val == TOK_STATIC);
+ prop_type = -1;
+ if (is_static) {
+ if (next_token(s))
+ goto fail;
+ /* allow "static" field name */
+ if (s->token.val == ';' || s->token.val == '=') {
+ is_static = FALSE;
+ name = JS_DupAtom(ctx, JS_ATOM_static);
+ prop_type = PROP_TYPE_IDENT;
+ }
+ }
+ if (is_static)
+ emit_op(s, OP_swap);
+ start_ptr = s->token.ptr;
+ if (prop_type < 0) {
+ prop_type = js_parse_property_name(s, &name, TRUE, FALSE, TRUE);
+ if (prop_type < 0)
+ goto fail;
+ }
+ is_private = prop_type & PROP_TYPE_PRIVATE;
+ prop_type &= ~PROP_TYPE_PRIVATE;
+
+ if ((name == JS_ATOM_constructor && !is_static &&
+ prop_type != PROP_TYPE_IDENT) ||
+ (name == JS_ATOM_prototype && is_static) ||
+ name == JS_ATOM_hash_constructor) {
+ js_parse_error(s, "invalid method name");
+ goto fail;
+ }
+ if (prop_type == PROP_TYPE_GET || prop_type == PROP_TYPE_SET) {
+ BOOL is_set = prop_type - PROP_TYPE_GET;
+ JSFunctionDef *method_fd;
+
+ if (is_private) {
+ int idx, var_kind;
+ idx = find_private_class_field(ctx, fd, name, fd->scope_level);
+ if (idx >= 0) {
+ var_kind = fd->vars[idx].var_kind;
+ if (var_kind == JS_VAR_PRIVATE_FIELD ||
+ var_kind == JS_VAR_PRIVATE_METHOD ||
+ var_kind == JS_VAR_PRIVATE_GETTER_SETTER ||
+ var_kind == (JS_VAR_PRIVATE_GETTER + is_set)) {
+ goto private_field_already_defined;
+ }
+ fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER;
+ } else {
+ if (add_private_class_field(s, fd, name,
+ JS_VAR_PRIVATE_GETTER + is_set) < 0)
+ goto fail;
+ }
+ if (add_brand(s, &class_fields[is_static]) < 0)
+ goto fail;
+ }
+
+ if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ start_ptr, s->token.line_num,
+ JS_PARSE_EXPORT_NONE, &method_fd))
+ goto fail;
+ if (is_private) {
+ method_fd->need_home_object = TRUE; /* needed for brand check */
+ emit_op(s, OP_set_home_object);
+ /* XXX: missing function name */
+ emit_op(s, OP_scope_put_var_init);
+ if (is_set) {
+ JSAtom setter_name;
+ int ret;
+
+ setter_name = get_private_setter_name(ctx, name);
+ if (setter_name == JS_ATOM_NULL)
+ goto fail;
+ emit_atom(s, setter_name);
+ ret = add_private_class_field(s, fd, setter_name,
+ JS_VAR_PRIVATE_SETTER);
+ JS_FreeAtom(ctx, setter_name);
+ if (ret < 0)
+ goto fail;
+ } else {
+ emit_atom(s, name);
+ }
+ emit_u16(s, s->cur_func->scope_level);
+ } else {
+ if (name == JS_ATOM_NULL) {
+ emit_op(s, OP_define_method_computed);
+ } else {
+ emit_op(s, OP_define_method);
+ emit_atom(s, name);
+ }
+ emit_u8(s, OP_DEFINE_METHOD_GETTER + is_set);
+ }
+ } else if (prop_type == PROP_TYPE_IDENT && s->token.val != '(') {
+ ClassFieldsDef *cf = &class_fields[is_static];
+ JSAtom field_var_name = JS_ATOM_NULL;
+
+ /* class field */
+
+ /* XXX: spec: not consistent with method name checks */
+ if (name == JS_ATOM_constructor || name == JS_ATOM_prototype) {
+ js_parse_error(s, "invalid field name");
+ goto fail;
+ }
+
+ if (is_private) {
+ if (find_private_class_field(ctx, fd, name,
+ fd->scope_level) >= 0) {
+ goto private_field_already_defined;
+ }
+ if (add_private_class_field(s, fd, name,
+ JS_VAR_PRIVATE_FIELD) < 0)
+ goto fail;
+ emit_op(s, OP_private_symbol);
+ emit_atom(s, name);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+
+ if (!cf->fields_init_fd) {
+ if (emit_class_init_start(s, cf))
+ goto fail;
+ }
+ if (name == JS_ATOM_NULL ) {
+ /* save the computed field name into a variable */
+ field_var_name = js_atom_concat_num(ctx, JS_ATOM_computed_field + is_static, cf->computed_fields_count);
+ if (field_var_name == JS_ATOM_NULL)
+ goto fail;
+ if (define_var(s, fd, field_var_name, JS_VAR_DEF_CONST) < 0) {
+ JS_FreeAtom(ctx, field_var_name);
+ goto fail;
+ }
+ emit_op(s, OP_to_propkey);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, field_var_name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+ s->cur_func = cf->fields_init_fd;
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ if (name == JS_ATOM_NULL) {
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, field_var_name);
+ emit_u16(s, s->cur_func->scope_level);
+ cf->computed_fields_count++;
+ JS_FreeAtom(ctx, field_var_name);
+ } else if (is_private) {
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+
+ if (s->token.val == '=') {
+ if (next_token(s))
+ goto fail;
+ if (js_parse_assign_expr(s))
+ goto fail;
+ } else {
+ emit_op(s, OP_undefined);
+ }
+ if (is_private) {
+ set_object_name_computed(s);
+ emit_op(s, OP_define_private_field);
+ } else if (name == JS_ATOM_NULL) {
+ set_object_name_computed(s);
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_drop);
+ } else {
+ set_object_name(s, name);
+ emit_op(s, OP_define_field);
+ emit_atom(s, name);
+ }
+ s->cur_func = s->cur_func->parent;
+ if (js_parse_expect_semi(s))
+ goto fail;
+ } else {
+ JSParseFunctionEnum func_type;
+ JSFunctionKindEnum func_kind;
+
+ func_type = JS_PARSE_FUNC_METHOD;
+ func_kind = JS_FUNC_NORMAL;
+ if (prop_type == PROP_TYPE_STAR) {
+ func_kind = JS_FUNC_GENERATOR;
+ } else if (prop_type == PROP_TYPE_ASYNC) {
+ func_kind = JS_FUNC_ASYNC;
+ } else if (prop_type == PROP_TYPE_ASYNC_STAR) {
+ func_kind = JS_FUNC_ASYNC_GENERATOR;
+ } else if (name == JS_ATOM_constructor && !is_static) {
+ if (ctor_fd) {
+ js_parse_error(s, "property constructor appears more than once");
+ goto fail;
+ }
+ if (class_flags & JS_DEFINE_CLASS_HAS_HERITAGE)
+ func_type = JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR;
+ else
+ func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR;
+ }
+ if (is_private) {
+ if (add_brand(s, &class_fields[is_static]) < 0)
+ goto fail;
+ }
+ if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd))
+ goto fail;
+ if (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR ||
+ func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
+ ctor_fd = method_fd;
+ } else if (is_private) {
+ method_fd->need_home_object = TRUE; /* needed for brand check */
+ if (find_private_class_field(ctx, fd, name,
+ fd->scope_level) >= 0) {
+ private_field_already_defined:
+ js_parse_error(s, "private class field is already defined");
+ goto fail;
+ }
+ if (add_private_class_field(s, fd, name,
+ JS_VAR_PRIVATE_METHOD) < 0)
+ goto fail;
+ emit_op(s, OP_set_home_object);
+ emit_op(s, OP_set_name);
+ emit_atom(s, name);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ } else {
+ if (name == JS_ATOM_NULL) {
+ emit_op(s, OP_define_method_computed);
+ } else {
+ emit_op(s, OP_define_method);
+ emit_atom(s, name);
+ }
+ emit_u8(s, OP_DEFINE_METHOD_METHOD);
+ }
+ }
+ if (is_static)
+ emit_op(s, OP_swap);
+ JS_FreeAtom(ctx, name);
+ name = JS_ATOM_NULL;
+ }
+
+ if (s->token.val != '}') {
+ js_parse_error(s, "expecting '%c'", '}');
+ goto fail;
+ }
+
+ if (!ctor_fd) {
+ if (js_parse_class_default_ctor(s, class_flags & JS_DEFINE_CLASS_HAS_HERITAGE, &ctor_fd))
+ goto fail;
+ }
+ /* patch the constant pool index for the constructor */
+ put_u32(fd->byte_code.buf + ctor_cpool_offset, ctor_fd->parent_cpool_idx);
+
+ /* store the class source code in the constructor. */
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ js_free(ctx, ctor_fd->source);
+ ctor_fd->source_len = s->buf_ptr - class_start_ptr;
+ ctor_fd->source = js_strndup(ctx, (const char *)class_start_ptr,
+ ctor_fd->source_len);
+ if (!ctor_fd->source)
+ goto fail;
+ }
+
+ /* consume the '}' */
+ if (next_token(s))
+ goto fail;
+
+ /* store the function to initialize the fields to that it can be
+ referenced by the constructor */
+ {
+ ClassFieldsDef *cf = &class_fields[0];
+ int var_idx;
+
+ var_idx = define_var(s, fd, JS_ATOM_class_fields_init,
+ JS_VAR_DEF_CONST);
+ if (var_idx < 0)
+ goto fail;
+ if (cf->fields_init_fd) {
+ emit_class_init_end(s, cf);
+ } else {
+ emit_op(s, OP_undefined);
+ }
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, JS_ATOM_class_fields_init);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+
+ /* drop the prototype */
+ emit_op(s, OP_drop);
+
+ /* initialize the static fields */
+ if (class_fields[1].fields_init_fd != NULL) {
+ ClassFieldsDef *cf = &class_fields[1];
+ emit_op(s, OP_dup);
+ emit_class_init_end(s, cf);
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+ emit_op(s, OP_drop);
+ }
+
+ if (class_name != JS_ATOM_NULL) {
+ /* store the class name in the scoped class name variable (it
+ is independent from the class statement variable
+ definition) */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, class_name);
+ emit_u16(s, fd->scope_level);
+ }
+ pop_scope(s);
+ pop_scope(s);
+
+ /* the class statements have a block level scope */
+ if (class_var_name != JS_ATOM_NULL) {
+ if (define_var(s, fd, class_var_name, JS_VAR_DEF_LET) < 0)
+ goto fail;
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, class_var_name);
+ emit_u16(s, fd->scope_level);
+ } else {
+ if (class_name == JS_ATOM_NULL) {
+ /* cannot use OP_set_name because the name of the class
+ must be defined before the static initializers are
+ executed */
+ emit_op(s, OP_set_class_name);
+ emit_u32(s, fd->last_opcode_pos + 1 - define_class_offset);
+ }
+ }
+
+ if (export_flag != JS_PARSE_EXPORT_NONE) {
+ if (!add_export_entry(s, fd->module,
+ class_var_name,
+ export_flag == JS_PARSE_EXPORT_NAMED ? class_var_name : JS_ATOM_default,
+ JS_EXPORT_TYPE_LOCAL))
+ goto fail;
+ }
+
+ JS_FreeAtom(ctx, class_name);
+ JS_FreeAtom(ctx, class_var_name);
+ fd->js_mode = saved_js_mode;
+ return 0;
+ fail:
+ JS_FreeAtom(ctx, name);
+ JS_FreeAtom(ctx, class_name);
+ JS_FreeAtom(ctx, class_var_name);
+ fd->js_mode = saved_js_mode;
+ return -1;
+}
+
+static warn_unused int js_parse_array_literal(JSParseState *s)
+{
+ uint32_t idx;
+ BOOL need_length;
+
+ if (next_token(s))
+ return -1;
+ /* small regular arrays are created on the stack */
+ idx = 0;
+ while (s->token.val != ']' && idx < 32) {
+ if (s->token.val == ',' || s->token.val == TOK_ELLIPSIS)
+ break;
+ if (js_parse_assign_expr(s))
+ return -1;
+ idx++;
+ /* accept trailing comma */
+ if (s->token.val == ',') {
+ if (next_token(s))
+ return -1;
+ } else
+ if (s->token.val != ']')
+ goto done;
+ }
+ emit_op(s, OP_array_from);
+ emit_u16(s, idx);
+
+ /* larger arrays and holes are handled with explicit indices */
+ need_length = FALSE;
+ while (s->token.val != ']' && idx < 0x7fffffff) {
+ if (s->token.val == TOK_ELLIPSIS)
+ break;
+ need_length = TRUE;
+ if (s->token.val != ',') {
+ if (js_parse_assign_expr(s))
+ return -1;
+ emit_op(s, OP_define_field);
+ emit_u32(s, JS_AtomFromUInt32(idx));
+ need_length = FALSE;
+ }
+ idx++;
+ /* accept trailing comma */
+ if (s->token.val == ',') {
+ if (next_token(s))
+ return -1;
+ }
+ }
+ if (s->token.val == ']') {
+ if (need_length) {
+ /* Set the length: Cannot use OP_define_field because
+ length is not configurable */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_push_i32);
+ emit_u32(s, idx);
+ emit_op(s, OP_put_field);
+ emit_atom(s, JS_ATOM_length);
+ }
+ goto done;
+ }
+
+ /* huge arrays and spread elements require a dynamic index on the stack */
+ emit_op(s, OP_push_i32);
+ emit_u32(s, idx);
+
+ /* stack has array, index */
+ while (s->token.val != ']') {
+ if (s->token.val == TOK_ELLIPSIS) {
+ if (next_token(s))
+ return -1;
+ if (js_parse_assign_expr(s))
+ return -1;
+#if 1
+ emit_op(s, OP_append);
+#else
+ int label_next, label_done;
+ label_next = new_label(s);
+ label_done = new_label(s);
+ /* enumerate object */
+ emit_op(s, OP_for_of_start);
+ emit_op(s, OP_rot5l);
+ emit_op(s, OP_rot5l);
+ emit_label(s, label_next);
+ /* on stack: enum_rec array idx */
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 2);
+ emit_goto(s, OP_if_true, label_done);
+ /* append element */
+ /* enum_rec array idx val -> enum_rec array new_idx */
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_inc);
+ emit_goto(s, OP_goto, label_next);
+ emit_label(s, label_done);
+ /* close enumeration */
+ emit_op(s, OP_drop); /* drop undef val */
+ emit_op(s, OP_nip1); /* drop enum_rec */
+ emit_op(s, OP_nip1);
+ emit_op(s, OP_nip1);
+#endif
+ } else {
+ need_length = TRUE;
+ if (s->token.val != ',') {
+ if (js_parse_assign_expr(s))
+ return -1;
+ /* a idx val */
+ emit_op(s, OP_define_array_el);
+ need_length = FALSE;
+ }
+ emit_op(s, OP_inc);
+ }
+ if (s->token.val != ',')
+ break;
+ if (next_token(s))
+ return -1;
+ }
+ if (need_length) {
+ /* Set the length: cannot use OP_define_field because
+ length is not configurable */
+ emit_op(s, OP_dup1); /* array length - array array length */
+ emit_op(s, OP_put_field);
+ emit_atom(s, JS_ATOM_length);
+ } else {
+ emit_op(s, OP_drop); /* array length - array */
+ }
+done:
+ return js_parse_expect(s, ']');
+}
+
+/* XXX: remove */
+static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
+{
+ /* check if scope chain contains a with statement */
+ while (s) {
+ int scope_idx = s->scopes[scope_level].first;
+ while (scope_idx >= 0) {
+ JSVarDef *vd = &s->vars[scope_idx];
+
+ if (vd->var_name == JS_ATOM__with_)
+ return TRUE;
+ scope_idx = vd->scope_next;
+ }
+ /* check parent scopes */
+ scope_level = s->parent_scope_level;
+ s = s->parent;
+ }
+ return FALSE;
+}
+
+static warn_unused int get_lvalue(JSParseState *s, int *popcode, int *pscope,
+ JSAtom *pname, int *plabel, int *pdepth, BOOL keep,
+ int tok)
+{
+ JSFunctionDef *fd;
+ int opcode, scope, label, depth;
+ JSAtom name;
+
+ /* we check the last opcode to get the lvalue type */
+ fd = s->cur_func;
+ scope = 0;
+ name = JS_ATOM_NULL;
+ label = -1;
+ depth = 0;
+ switch(opcode = get_prev_opcode(fd)) {
+ case OP_scope_get_var:
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
+ if ((name == JS_ATOM_arguments || name == JS_ATOM_eval) &&
+ (fd->js_mode & JS_MODE_STRICT)) {
+ return js_parse_error(s, "invalid lvalue in strict mode");
+ }
+ if (name == JS_ATOM_this || name == JS_ATOM_new_target)
+ goto invalid_lvalue;
+ depth = 2; /* will generate OP_get_ref_value */
+ break;
+ case OP_get_field:
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ depth = 1;
+ break;
+ case OP_scope_get_private_field:
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
+ depth = 1;
+ break;
+ case OP_get_array_el:
+ depth = 2;
+ break;
+ case OP_get_super_value:
+ depth = 3;
+ break;
+ default:
+ invalid_lvalue:
+ if (tok == TOK_FOR) {
+ return js_parse_error(s, "invalid for in/of left hand-side");
+ } else if (tok == TOK_INC || tok == TOK_DEC) {
+ return js_parse_error(s, "invalid increment/decrement operand");
+ } else if (tok == '[' || tok == '{') {
+ return js_parse_error(s, "invalid destructuring target");
+ } else {
+ return js_parse_error(s, "invalid assignment left-hand side");
+ }
+ }
+ /* remove the last opcode */
+ fd->byte_code.size = fd->last_opcode_pos;
+ fd->last_opcode_pos = -1;
+
+ if (keep) {
+ /* get the value but keep the object/fields on the stack */
+ switch(opcode) {
+ case OP_scope_get_var:
+ label = new_label(s);
+ emit_op(s, OP_scope_make_ref);
+ emit_atom(s, name);
+ emit_u32(s, label);
+ emit_u16(s, scope);
+ update_label(fd, label, 1);
+ emit_op(s, OP_get_ref_value);
+ opcode = OP_get_ref_value;
+ break;
+ case OP_get_field:
+ emit_op(s, OP_get_field2);
+ emit_atom(s, name);
+ break;
+ case OP_scope_get_private_field:
+ emit_op(s, OP_scope_get_private_field2);
+ emit_atom(s, name);
+ emit_u16(s, scope);
+ break;
+ case OP_get_array_el:
+ /* XXX: replace by a single opcode ? */
+ emit_op(s, OP_to_propkey2);
+ emit_op(s, OP_dup2);
+ emit_op(s, OP_get_array_el);
+ break;
+ case OP_get_super_value:
+ emit_op(s, OP_to_propkey);
+ emit_op(s, OP_dup3);
+ emit_op(s, OP_get_super_value);
+ break;
+ default:
+ abort();
+ }
+ } else {
+ switch(opcode) {
+ case OP_scope_get_var:
+ label = new_label(s);
+ emit_op(s, OP_scope_make_ref);
+ emit_atom(s, name);
+ emit_u32(s, label);
+ emit_u16(s, scope);
+ update_label(fd, label, 1);
+ opcode = OP_get_ref_value;
+ break;
+ case OP_get_array_el:
+ emit_op(s, OP_to_propkey2);
+ break;
+ case OP_get_super_value:
+ emit_op(s, OP_to_propkey);
+ break;
+ }
+ }
+
+ *popcode = opcode;
+ *pscope = scope;
+ /* name has refcount for OP_get_field and OP_get_ref_value,
+ and JS_ATOM_NULL for other opcodes */
+ *pname = name;
+ *plabel = label;
+ if (pdepth)
+ *pdepth = depth;
+ return 0;
+}
+
+typedef enum {
+ PUT_LVALUE_NOKEEP, /* [depth] v -> */
+ PUT_LVALUE_NOKEEP_DEPTH, /* [depth] v -> , keep depth (currently
+ just disable optimizations) */
+ PUT_LVALUE_KEEP_TOP, /* [depth] v -> v */
+ PUT_LVALUE_KEEP_SECOND, /* [depth] v0 v -> v0 */
+ PUT_LVALUE_NOKEEP_BOTTOM, /* v [depth] -> */
+} PutLValueEnum;
+
+/* name has a live reference. 'is_let' is only used with opcode =
+ OP_scope_get_var which is never generated by get_lvalue(). */
+static void put_lvalue(JSParseState *s, int opcode, int scope,
+ JSAtom name, int label, PutLValueEnum special,
+ BOOL is_let)
+{
+ switch(opcode) {
+ case OP_get_field:
+ case OP_scope_get_private_field:
+ /* depth = 1 */
+ switch(special) {
+ case PUT_LVALUE_NOKEEP:
+ case PUT_LVALUE_NOKEEP_DEPTH:
+ break;
+ case PUT_LVALUE_KEEP_TOP:
+ emit_op(s, OP_insert2); /* obj v -> v obj v */
+ break;
+ case PUT_LVALUE_KEEP_SECOND:
+ emit_op(s, OP_perm3); /* obj v0 v -> v0 obj v */
+ break;
+ case PUT_LVALUE_NOKEEP_BOTTOM:
+ emit_op(s, OP_swap);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case OP_get_array_el:
+ case OP_get_ref_value:
+ /* depth = 2 */
+ if (opcode == OP_get_ref_value) {
+ JS_FreeAtom(s->ctx, name);
+ emit_label(s, label);
+ }
+ switch(special) {
+ case PUT_LVALUE_NOKEEP:
+ emit_op(s, OP_nop); /* will trigger optimization */
+ break;
+ case PUT_LVALUE_NOKEEP_DEPTH:
+ break;
+ case PUT_LVALUE_KEEP_TOP:
+ emit_op(s, OP_insert3); /* obj prop v -> v obj prop v */
+ break;
+ case PUT_LVALUE_KEEP_SECOND:
+ emit_op(s, OP_perm4); /* obj prop v0 v -> v0 obj prop v */
+ break;
+ case PUT_LVALUE_NOKEEP_BOTTOM:
+ emit_op(s, OP_rot3l);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case OP_get_super_value:
+ /* depth = 3 */
+ switch(special) {
+ case PUT_LVALUE_NOKEEP:
+ case PUT_LVALUE_NOKEEP_DEPTH:
+ break;
+ case PUT_LVALUE_KEEP_TOP:
+ emit_op(s, OP_insert4); /* this obj prop v -> v this obj prop v */
+ break;
+ case PUT_LVALUE_KEEP_SECOND:
+ emit_op(s, OP_perm5); /* this obj prop v0 v -> v0 this obj prop v */
+ break;
+ case PUT_LVALUE_NOKEEP_BOTTOM:
+ emit_op(s, OP_rot4l);
+ break;
+ default:
+ abort();
+ }
+ break;
+ default:
+ break;
+ }
+
+ switch(opcode) {
+ case OP_scope_get_var: /* val -- */
+ assert(special == PUT_LVALUE_NOKEEP ||
+ special == PUT_LVALUE_NOKEEP_DEPTH);
+ emit_op(s, is_let ? OP_scope_put_var_init : OP_scope_put_var);
+ emit_u32(s, name); /* has refcount */
+ emit_u16(s, scope);
+ break;
+ case OP_get_field:
+ emit_op(s, OP_put_field);
+ emit_u32(s, name); /* name has refcount */
+ break;
+ case OP_scope_get_private_field:
+ emit_op(s, OP_scope_put_private_field);
+ emit_u32(s, name); /* name has refcount */
+ emit_u16(s, scope);
+ break;
+ case OP_get_array_el:
+ emit_op(s, OP_put_array_el);
+ break;
+ case OP_get_ref_value:
+ emit_op(s, OP_put_ref_value);
+ break;
+ case OP_get_super_value:
+ emit_op(s, OP_put_super_value);
+ break;
+ default:
+ abort();
+ }
+}
+
+static warn_unused int js_parse_expr_paren(JSParseState *s)
+{
+ if (js_parse_expect(s, '('))
+ return -1;
+ if (js_parse_expr(s))
+ return -1;
+ if (js_parse_expect(s, ')'))
+ return -1;
+ return 0;
+}
+
+static int js_unsupported_keyword(JSParseState *s, JSAtom atom)
+{
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return js_parse_error(s, "unsupported keyword: %s",
+ JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));
+}
+
+static warn_unused int js_define_var(JSParseState *s, JSAtom name, int tok)
+{
+ JSFunctionDef *fd = s->cur_func;
+ JSVarDefEnum var_def_type;
+
+ if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
+ return js_parse_error(s, "yield is a reserved identifier");
+ }
+ if ((name == JS_ATOM_arguments || name == JS_ATOM_eval)
+ && (fd->js_mode & JS_MODE_STRICT)) {
+ return js_parse_error(s, "invalid variable name in strict mode");
+ }
+ if ((name == JS_ATOM_let || name == JS_ATOM_undefined)
+ && (tok == TOK_LET || tok == TOK_CONST)) {
+ return js_parse_error(s, "invalid lexical variable name");
+ }
+ switch(tok) {
+ case TOK_LET:
+ var_def_type = JS_VAR_DEF_LET;
+ break;
+ case TOK_CONST:
+ var_def_type = JS_VAR_DEF_CONST;
+ break;
+ case TOK_VAR:
+ var_def_type = JS_VAR_DEF_VAR;
+ break;
+ case TOK_CATCH:
+ var_def_type = JS_VAR_DEF_CATCH;
+ break;
+ default:
+ abort();
+ }
+ if (define_var(s, fd, name, var_def_type) < 0)
+ return -1;
+ return 0;
+}
+
+static void js_emit_spread_code(JSParseState *s, int depth)
+{
+ int label_rest_next, label_rest_done;
+
+ /* XXX: could check if enum object is an actual array and optimize
+ slice extraction. enumeration record and target array are in a
+ different order from OP_append case. */
+ /* enum_rec xxx -- enum_rec xxx array 0 */
+ emit_op(s, OP_array_from);
+ emit_u16(s, 0);
+ emit_op(s, OP_push_i32);
+ emit_u32(s, 0);
+ emit_label(s, label_rest_next = new_label(s));
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 2 + depth);
+ label_rest_done = emit_goto(s, OP_if_true, -1);
+ /* array idx val -- array idx */
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_inc);
+ emit_goto(s, OP_goto, label_rest_next);
+ emit_label(s, label_rest_done);
+ /* enum_rec xxx array idx undef -- enum_rec xxx array */
+ emit_op(s, OP_drop);
+ emit_op(s, OP_drop);
+}
+
+static int js_parse_check_duplicate_parameter(JSParseState *s, JSAtom name)
+{
+ /* Check for duplicate parameter names */
+ JSFunctionDef *fd = s->cur_func;
+ int i;
+ for (i = 0; i < fd->arg_count; i++) {
+ if (fd->args[i].var_name == name)
+ goto duplicate;
+ }
+ for (i = 0; i < fd->var_count; i++) {
+ if (fd->vars[i].var_name == name)
+ goto duplicate;
+ }
+ return 0;
+
+duplicate:
+ return js_parse_error(s, "duplicate parameter names not allowed in this context");
+}
+
+static JSAtom js_parse_destructuring_var(JSParseState *s, int tok, int is_arg)
+{
+ JSAtom name;
+
+ if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
+ || ((s->cur_func->js_mode & JS_MODE_STRICT) &&
+ (s->token.u.ident.atom == JS_ATOM_eval || s->token.u.ident.atom == JS_ATOM_arguments))) {
+ js_parse_error(s, "invalid destructuring target");
+ return JS_ATOM_NULL;
+ }
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (is_arg && js_parse_check_duplicate_parameter(s, name))
+ goto fail;
+ if (next_token(s))
+ goto fail;
+
+ return name;
+fail:
+ JS_FreeAtom(s->ctx, name);
+ return JS_ATOM_NULL;
+}
+
+/* Return -1 if error, 0 if no initializer, 1 if an initializer is
+ present at the top level. */
+static int js_parse_destructuring_element(JSParseState *s, int tok, int is_arg,
+ int hasval, int has_ellipsis,
+ BOOL allow_initializer)
+{
+ int label_parse, label_assign, label_done, label_lvalue, depth_lvalue;
+ int start_addr, assign_addr;
+ JSAtom prop_name, var_name;
+ int opcode, scope, tok1, skip_bits;
+ BOOL has_initializer;
+
+ if (has_ellipsis < 0) {
+ /* pre-parse destructuration target for spread detection */
+ js_parse_skip_parens_token(s, &skip_bits, FALSE);
+ has_ellipsis = skip_bits & SKIP_HAS_ELLIPSIS;
+ }
+
+ label_parse = new_label(s);
+ label_assign = new_label(s);
+
+ start_addr = s->cur_func->byte_code.size;
+ if (hasval) {
+ /* consume value from the stack */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_strict_eq);
+ emit_goto(s, OP_if_true, label_parse);
+ emit_label(s, label_assign);
+ } else {
+ emit_goto(s, OP_goto, label_parse);
+ emit_label(s, label_assign);
+ /* leave value on the stack */
+ emit_op(s, OP_dup);
+ }
+ assign_addr = s->cur_func->byte_code.size;
+ if (s->token.val == '{') {
+ if (next_token(s))
+ return -1;
+ /* throw an exception if the value cannot be converted to an object */
+ emit_op(s, OP_to_object);
+ if (has_ellipsis) {
+ /* add excludeList on stack just below src object */
+ emit_op(s, OP_object);
+ emit_op(s, OP_swap);
+ }
+ while (s->token.val != '}') {
+ int prop_type;
+ if (s->token.val == TOK_ELLIPSIS) {
+ if (!has_ellipsis) {
+ JS_ThrowInternalError(s->ctx, "unexpected ellipsis token");
+ return -1;
+ }
+ if (next_token(s))
+ return -1;
+ if (tok) {
+ var_name = js_parse_destructuring_var(s, tok, is_arg);
+ if (var_name == JS_ATOM_NULL)
+ return -1;
+ opcode = OP_scope_get_var;
+ scope = s->cur_func->scope_level;
+ label_lvalue = -1;
+ depth_lvalue = 0;
+ } else {
+ if (js_parse_left_hand_side_expr(s))
+ return -1;
+
+ if (get_lvalue(s, &opcode, &scope, &var_name,
+ &label_lvalue, &depth_lvalue, FALSE, '{'))
+ return -1;
+ }
+ if (s->token.val != '}') {
+ js_parse_error(s, "assignment rest property must be last");
+ goto var_error;
+ }
+ emit_op(s, OP_object); /* target */
+ emit_op(s, OP_copy_data_properties);
+ emit_u8(s, 0 | ((depth_lvalue + 1) << 2) | ((depth_lvalue + 2) << 5));
+ goto set_val;
+ }
+ prop_type = js_parse_property_name(s, &prop_name, FALSE, TRUE, FALSE);
+ if (prop_type < 0)
+ return -1;
+ var_name = JS_ATOM_NULL;
+ opcode = OP_scope_get_var;
+ scope = s->cur_func->scope_level;
+ label_lvalue = -1;
+ depth_lvalue = 0;
+ if (prop_type == PROP_TYPE_IDENT) {
+ if (next_token(s))
+ goto prop_error;
+ if ((s->token.val == '[' || s->token.val == '{')
+ && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
+ tok1 == '=' || tok1 == '}')) {
+ if (prop_name == JS_ATOM_NULL) {
+ /* computed property name on stack */
+ if (has_ellipsis) {
+ /* define the property in excludeList */
+ emit_op(s, OP_to_propkey); /* avoid calling ToString twice */
+ emit_op(s, OP_perm3); /* TOS: src excludeList prop */
+ emit_op(s, OP_null); /* TOS: src excludeList prop null */
+ emit_op(s, OP_define_array_el); /* TOS: src excludeList prop */
+ emit_op(s, OP_perm3); /* TOS: excludeList src prop */
+ }
+ /* get the computed property from the source object */
+ emit_op(s, OP_get_array_el2);
+ } else {
+ /* named property */
+ if (has_ellipsis) {
+ /* define the property in excludeList */
+ emit_op(s, OP_swap); /* TOS: src excludeList */
+ emit_op(s, OP_null); /* TOS: src excludeList null */
+ emit_op(s, OP_define_field); /* TOS: src excludeList */
+ emit_atom(s, prop_name);
+ emit_op(s, OP_swap); /* TOS: excludeList src */
+ }
+ /* get the named property from the source object */
+ emit_op(s, OP_get_field2);
+ emit_u32(s, prop_name);
+ }
+ if (js_parse_destructuring_element(s, tok, is_arg, TRUE, -1, TRUE) < 0)
+ return -1;
+ if (s->token.val == '}')
+ break;
+ /* accept a trailing comma before the '}' */
+ if (js_parse_expect(s, ','))
+ return -1;
+ continue;
+ }
+ if (prop_name == JS_ATOM_NULL) {
+ emit_op(s, OP_to_propkey2);
+ if (has_ellipsis) {
+ /* define the property in excludeList */
+ emit_op(s, OP_perm3);
+ emit_op(s, OP_null);
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_perm3);
+ }
+ /* source prop -- source source prop */
+ emit_op(s, OP_dup1);
+ } else {
+ if (has_ellipsis) {
+ /* define the property in excludeList */
+ emit_op(s, OP_swap);
+ emit_op(s, OP_null);
+ emit_op(s, OP_define_field);
+ emit_atom(s, prop_name);
+ emit_op(s, OP_swap);
+ }
+ /* source -- source source */
+ emit_op(s, OP_dup);
+ }
+ if (tok) {
+ var_name = js_parse_destructuring_var(s, tok, is_arg);
+ if (var_name == JS_ATOM_NULL)
+ goto prop_error;
+ } else {
+ if (js_parse_left_hand_side_expr(s))
+ goto prop_error;
+ lvalue:
+ if (get_lvalue(s, &opcode, &scope, &var_name,
+ &label_lvalue, &depth_lvalue, FALSE, '{'))
+ goto prop_error;
+ /* swap ref and lvalue object if any */
+ if (prop_name == JS_ATOM_NULL) {
+ switch(depth_lvalue) {
+ case 1:
+ /* source prop x -> x source prop */
+ emit_op(s, OP_rot3r);
+ break;
+ case 2:
+ /* source prop x y -> x y source prop */
+ emit_op(s, OP_swap2); /* t p2 s p1 */
+ break;
+ case 3:
+ /* source prop x y z -> x y z source prop */
+ emit_op(s, OP_rot5l);
+ emit_op(s, OP_rot5l);
+ break;
+ }
+ } else {
+ switch(depth_lvalue) {
+ case 1:
+ /* source x -> x source */
+ emit_op(s, OP_swap);
+ break;
+ case 2:
+ /* source x y -> x y source */
+ emit_op(s, OP_rot3l);
+ break;
+ case 3:
+ /* source x y z -> x y z source */
+ emit_op(s, OP_rot4l);
+ break;
+ }
+ }
+ }
+ if (prop_name == JS_ATOM_NULL) {
+ /* computed property name on stack */
+ /* XXX: should have OP_get_array_el2x with depth */
+ /* source prop -- val */
+ emit_op(s, OP_get_array_el);
+ } else {
+ /* named property */
+ /* XXX: should have OP_get_field2x with depth */
+ /* source -- val */
+ emit_op(s, OP_get_field);
+ emit_u32(s, prop_name);
+ }
+ } else {
+ /* prop_type = PROP_TYPE_VAR, cannot be a computed property */
+ if (is_arg && js_parse_check_duplicate_parameter(s, prop_name))
+ goto prop_error;
+ if ((s->cur_func->js_mode & JS_MODE_STRICT) &&
+ (prop_name == JS_ATOM_eval || prop_name == JS_ATOM_arguments)) {
+ js_parse_error(s, "invalid destructuring target");
+ goto prop_error;
+ }
+ if (has_ellipsis) {
+ /* define the property in excludeList */
+ emit_op(s, OP_swap);
+ emit_op(s, OP_null);
+ emit_op(s, OP_define_field);
+ emit_atom(s, prop_name);
+ emit_op(s, OP_swap);
+ }
+ if (!tok || tok == TOK_VAR) {
+ /* generate reference */
+ /* source -- source source */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, prop_name);
+ emit_u16(s, s->cur_func->scope_level);
+ goto lvalue;
+ }
+ var_name = JS_DupAtom(s->ctx, prop_name);
+ /* source -- source val */
+ emit_op(s, OP_get_field2);
+ emit_u32(s, prop_name);
+ }
+ set_val:
+ if (tok) {
+ if (js_define_var(s, var_name, tok))
+ goto var_error;
+ scope = s->cur_func->scope_level;
+ }
+ if (s->token.val == '=') { /* handle optional default value */
+ int label_hasval;
+ emit_op(s, OP_dup);
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_strict_eq);
+ label_hasval = emit_goto(s, OP_if_false, -1);
+ if (next_token(s))
+ goto var_error;
+ emit_op(s, OP_drop);
+ if (js_parse_assign_expr(s))
+ goto var_error;
+ if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
+ set_object_name(s, var_name);
+ emit_label(s, label_hasval);
+ }
+ /* store value into lvalue object */
+ put_lvalue(s, opcode, scope, var_name, label_lvalue,
+ PUT_LVALUE_NOKEEP_DEPTH,
+ (tok == TOK_CONST || tok == TOK_LET));
+ if (s->token.val == '}')
+ break;
+ /* accept a trailing comma before the '}' */
+ if (js_parse_expect(s, ','))
+ return -1;
+ }
+ /* drop the source object */
+ emit_op(s, OP_drop);
+ if (has_ellipsis) {
+ emit_op(s, OP_drop); /* pop excludeList */
+ }
+ if (next_token(s))
+ return -1;
+ } else if (s->token.val == '[') {
+ BOOL has_spread;
+ int enum_depth;
+ BlockEnv block_env;
+
+ if (next_token(s))
+ return -1;
+ /* the block environment is only needed in generators in case
+ 'yield' triggers a 'return' */
+ push_break_entry(s->cur_func, &block_env,
+ JS_ATOM_NULL, -1, -1, 2);
+ block_env.has_iterator = TRUE;
+ emit_op(s, OP_for_of_start);
+ has_spread = FALSE;
+ while (s->token.val != ']') {
+ /* get the next value */
+ if (s->token.val == TOK_ELLIPSIS) {
+ if (next_token(s))
+ return -1;
+ if (s->token.val == ',' || s->token.val == ']')
+ return js_parse_error(s, "missing binding pattern...");
+ has_spread = TRUE;
+ }
+ if (s->token.val == ',') {
+ /* do nothing, skip the value, has_spread is false */
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 0);
+ emit_op(s, OP_drop);
+ emit_op(s, OP_drop);
+ } else if ((s->token.val == '[' || s->token.val == '{')
+ && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == ',' ||
+ tok1 == '=' || tok1 == ']')) {
+ if (has_spread) {
+ if (tok1 == '=')
+ return js_parse_error(s, "rest element cannot have a default value");
+ js_emit_spread_code(s, 0);
+ } else {
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 0);
+ emit_op(s, OP_drop);
+ }
+ if (js_parse_destructuring_element(s, tok, is_arg, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
+ return -1;
+ } else {
+ var_name = JS_ATOM_NULL;
+ enum_depth = 0;
+ if (tok) {
+ var_name = js_parse_destructuring_var(s, tok, is_arg);
+ if (var_name == JS_ATOM_NULL)
+ goto var_error;
+ if (js_define_var(s, var_name, tok))
+ goto var_error;
+ opcode = OP_scope_get_var;
+ scope = s->cur_func->scope_level;
+ } else {
+ if (js_parse_left_hand_side_expr(s))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &var_name,
+ &label_lvalue, &enum_depth, FALSE, '[')) {
+ return -1;
+ }
+ }
+ if (has_spread) {
+ js_emit_spread_code(s, enum_depth);
+ } else {
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, enum_depth);
+ emit_op(s, OP_drop);
+ }
+ if (s->token.val == '=' && !has_spread) {
+ /* handle optional default value */
+ int label_hasval;
+ emit_op(s, OP_dup);
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_strict_eq);
+ label_hasval = emit_goto(s, OP_if_false, -1);
+ if (next_token(s))
+ goto var_error;
+ emit_op(s, OP_drop);
+ if (js_parse_assign_expr(s))
+ goto var_error;
+ if (opcode == OP_scope_get_var || opcode == OP_get_ref_value)
+ set_object_name(s, var_name);
+ emit_label(s, label_hasval);
+ }
+ /* store value into lvalue object */
+ put_lvalue(s, opcode, scope, var_name,
+ label_lvalue, PUT_LVALUE_NOKEEP_DEPTH,
+ (tok == TOK_CONST || tok == TOK_LET));
+ }
+ if (s->token.val == ']')
+ break;
+ if (has_spread)
+ return js_parse_error(s, "rest element must be the last one");
+ /* accept a trailing comma before the ']' */
+ if (js_parse_expect(s, ','))
+ return -1;
+ }
+ /* close iterator object:
+ if completed, enum_obj has been replaced by undefined */
+ emit_op(s, OP_iterator_close);
+ pop_break_entry(s->cur_func);
+ if (next_token(s))
+ return -1;
+ } else {
+ return js_parse_error(s, "invalid assignment syntax");
+ }
+ if (s->token.val == '=' && allow_initializer) {
+ label_done = emit_goto(s, OP_goto, -1);
+ if (next_token(s))
+ return -1;
+ emit_label(s, label_parse);
+ if (hasval)
+ emit_op(s, OP_drop);
+ if (js_parse_assign_expr(s))
+ return -1;
+ emit_goto(s, OP_goto, label_assign);
+ emit_label(s, label_done);
+ has_initializer = TRUE;
+ } else {
+ /* normally hasval is true except if
+ js_parse_skip_parens_token() was wrong in the parsing */
+ // assert(hasval);
+ if (!hasval) {
+ js_parse_error(s, "too complicated destructuring expression");
+ return -1;
+ }
+ /* remove test and decrement label ref count */
+ memset(s->cur_func->byte_code.buf + start_addr, OP_nop,
+ assign_addr - start_addr);
+ s->cur_func->label_slots[label_parse].ref_count--;
+ has_initializer = FALSE;
+ }
+ return has_initializer;
+
+ prop_error:
+ JS_FreeAtom(s->ctx, prop_name);
+ var_error:
+ JS_FreeAtom(s->ctx, var_name);
+ return -1;
+}
+
+typedef enum FuncCallType {
+ FUNC_CALL_NORMAL,
+ FUNC_CALL_NEW,
+ FUNC_CALL_SUPER_CTOR,
+ FUNC_CALL_TEMPLATE,
+} FuncCallType;
+
+static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,
+ int drop_count)
+{
+ int label_next, i;
+ if (*poptional_chaining_label < 0)
+ *poptional_chaining_label = new_label(s);
+ /* XXX: could be more efficient with a specific opcode */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_is_undefined_or_null);
+ label_next = emit_goto(s, OP_if_false, -1);
+ for(i = 0; i < drop_count; i++)
+ emit_op(s, OP_drop);
+ emit_op(s, OP_undefined);
+ emit_goto(s, OP_goto, *poptional_chaining_label);
+ emit_label(s, label_next);
+}
+
+/* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */
+static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags)
+{
+ FuncCallType call_type;
+ int optional_chaining_label;
+ BOOL accept_lparen = (parse_flags & PF_POSTFIX_CALL) != 0;
+
+ call_type = FUNC_CALL_NORMAL;
+ switch(s->token.val) {
+ case TOK_NUMBER:
+ {
+ JSValue val;
+ val = s->token.u.num.val;
+
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) {
+ emit_op(s, OP_push_i32);
+ emit_u32(s, JS_VALUE_GET_INT(val));
+ } else
+#ifdef CONFIG_BIGNUM
+ if (JS_VALUE_GET_TAG(val) == JS_TAG_BIG_FLOAT) {
+ slimb_t e;
+ int ret;
+
+ /* need a runtime conversion */
+ /* XXX: could add a cache and/or do it once at
+ the start of the function */
+ if (emit_push_const(s, val, 0) < 0)
+ return -1;
+ e = s->token.u.num.exponent;
+ if (e == (int32_t)e) {
+ emit_op(s, OP_push_i32);
+ emit_u32(s, e);
+ } else {
+ val = JS_NewBigInt64_1(s->ctx, e);
+ if (JS_IsException(val))
+ return -1;
+ ret = emit_push_const(s, val, 0);
+ JS_FreeValue(s->ctx, val);
+ if (ret < 0)
+ return -1;
+ }
+ emit_op(s, OP_mul_pow10);
+ } else
+#endif
+ {
+ if (emit_push_const(s, val, 0) < 0)
+ return -1;
+ }
+ }
+ if (next_token(s))
+ return -1;
+ break;
+ case TOK_TEMPLATE:
+ if (js_parse_template(s, 0, NULL))
+ return -1;
+ break;
+ case TOK_STRING:
+ if (emit_push_const(s, s->token.u.str.str, 1))
+ return -1;
+ if (next_token(s))
+ return -1;
+ break;
+
+ case TOK_DIV_ASSIGN:
+ s->buf_ptr -= 2;
+ goto parse_regexp;
+ case '/':
+ s->buf_ptr--;
+ parse_regexp:
+ {
+ JSValue str;
+ int ret, backtrace_flags;
+ if (!s->ctx->compile_regexp)
+ return js_parse_error(s, "RegExp are not supported");
+ /* the previous token is '/' or '/=', so no need to free */
+ if (js_parse_regexp(s))
+ return -1;
+ ret = emit_push_const(s, s->token.u.regexp.body, 0);
+ str = s->ctx->compile_regexp(s->ctx, s->token.u.regexp.body,
+ s->token.u.regexp.flags);
+ if (JS_IsException(str)) {
+ /* add the line number info */
+ backtrace_flags = 0;
+ if (s->cur_func && s->cur_func->backtrace_barrier)
+ backtrace_flags = JS_BACKTRACE_FLAG_SINGLE_LEVEL;
+ build_backtrace(s->ctx, s->ctx->rt->current_exception,
+ s->filename, s->token.line_num,
+ backtrace_flags);
+ return -1;
+ }
+ ret = emit_push_const(s, str, 0);
+ JS_FreeValue(s->ctx, str);
+ if (ret)
+ return -1;
+ /* we use a specific opcode to be sure the correct
+ function is called (otherwise the bytecode would have
+ to be verified by the RegExp constructor) */
+ emit_op(s, OP_regexp);
+ if (next_token(s))
+ return -1;
+ }
+ break;
+ case '(':
+ if ((parse_flags & PF_ARROW_FUNC) &&
+ js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) {
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num))
+ return -1;
+ } else {
+ if (js_parse_expr_paren(s))
+ return -1;
+ }
+ break;
+ case TOK_FUNCTION:
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num))
+ return -1;
+ break;
+ case TOK_CLASS:
+ if (js_parse_class(s, TRUE, JS_PARSE_EXPORT_NONE))
+ return -1;
+ break;
+ case TOK_NULL:
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_null);
+ break;
+ case TOK_THIS:
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+ break;
+ case TOK_FALSE:
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_push_false);
+ break;
+ case TOK_TRUE:
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_push_true);
+ break;
+ case TOK_IDENT:
+ {
+ JSAtom name;
+ if (s->token.u.ident.is_reserved) {
+ return js_parse_error_reserved_identifier(s);
+ }
+ if ((parse_flags & PF_ARROW_FUNC) &&
+ peek_token(s, TRUE) == TOK_ARROW) {
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num))
+ return -1;
+ } else if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) != '\n') {
+ const uint8_t *source_ptr;
+ int source_line_num;
+
+ source_ptr = s->token.ptr;
+ source_line_num = s->token.line_num;
+ if (next_token(s))
+ return -1;
+ if (s->token.val == TOK_FUNCTION) {
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR,
+ JS_FUNC_ASYNC, JS_ATOM_NULL,
+ source_ptr, source_line_num))
+ return -1;
+ } else if ((parse_flags & PF_ARROW_FUNC) &&
+ ((s->token.val == '(' &&
+ js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) ||
+ (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
+ peek_token(s, TRUE) == TOK_ARROW))) {
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW,
+ JS_FUNC_ASYNC, JS_ATOM_NULL,
+ source_ptr, source_line_num))
+ return -1;
+ } else {
+ name = JS_DupAtom(s->ctx, JS_ATOM_async);
+ goto do_get_var;
+ }
+ } else {
+ if (s->token.u.ident.atom == JS_ATOM_arguments &&
+ !s->cur_func->arguments_allowed) {
+ js_parse_error(s, "'arguments' identifier is not allowed in class field initializer");
+ return -1;
+ }
+ name = JS_DupAtom(s->ctx, s->token.u.ident.atom);
+ if (next_token(s)) /* update line number before emitting code */
+ return -1;
+ do_get_var:
+ emit_op(s, OP_scope_get_var);
+ emit_u32(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+ }
+ break;
+ case '{':
+ case '[':
+ {
+ int skip_bits;
+ if (js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
+ if (js_parse_destructuring_element(s, 0, 0, FALSE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
+ return -1;
+ } else {
+ if (s->token.val == '{') {
+ if (js_parse_object_literal(s))
+ return -1;
+ } else {
+ if (js_parse_array_literal(s))
+ return -1;
+ }
+ }
+ }
+ break;
+ case TOK_NEW:
+ if (next_token(s))
+ return -1;
+ if (s->token.val == '.') {
+ if (next_token(s))
+ return -1;
+ if (!token_is_pseudo_keyword(s, JS_ATOM_target))
+ return js_parse_error(s, "expecting target");
+ if (!s->cur_func->new_target_allowed)
+ return js_parse_error(s, "new.target only allowed within functions");
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_new_target);
+ emit_u16(s, 0);
+ } else {
+ if (js_parse_postfix_expr(s, 0))
+ return -1;
+ accept_lparen = TRUE;
+ if (s->token.val != '(') {
+ /* new operator on an object */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_call_constructor);
+ emit_u16(s, 0);
+ } else {
+ call_type = FUNC_CALL_NEW;
+ }
+ }
+ break;
+ case TOK_SUPER:
+ if (next_token(s))
+ return -1;
+ if (s->token.val == '(') {
+ if (!s->cur_func->super_call_allowed)
+ return js_parse_error(s, "super() is only valid in a derived class constructor");
+ call_type = FUNC_CALL_SUPER_CTOR;
+ } else if (s->token.val == '.' || s->token.val == '[') {
+ if (!s->cur_func->super_allowed)
+ return js_parse_error(s, "'super' is only valid in a method");
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_home_object);
+ emit_u16(s, 0);
+ emit_op(s, OP_get_super);
+ } else {
+ return js_parse_error(s, "invalid use of 'super'");
+ }
+ break;
+ case TOK_IMPORT:
+ if (next_token(s))
+ return -1;
+ if (s->token.val == '.') {
+ if (next_token(s))
+ return -1;
+ if (!token_is_pseudo_keyword(s, JS_ATOM_meta))
+ return js_parse_error(s, "meta expected");
+ if (!s->is_module)
+ return js_parse_error(s, "import.meta only valid in module code");
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_special_object);
+ emit_u8(s, OP_SPECIAL_OBJECT_IMPORT_META);
+ } else {
+ if (js_parse_expect(s, '('))
+ return -1;
+ if (!accept_lparen)
+ return js_parse_error(s, "invalid use of 'import()'");
+ if (js_parse_assign_expr(s))
+ return -1;
+ if (js_parse_expect(s, ')'))
+ return -1;
+ emit_op(s, OP_import);
+ }
+ break;
+ default:
+ return js_parse_error(s, "unexpected token in expression: '%.*s'",
+ (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
+ }
+
+ optional_chaining_label = -1;
+ for(;;) {
+ JSFunctionDef *fd = s->cur_func;
+ BOOL has_optional_chain = FALSE;
+
+ if (s->token.val == TOK_QUESTION_MARK_DOT) {
+ /* optional chaining */
+ if (next_token(s))
+ return -1;
+ has_optional_chain = TRUE;
+ if (s->token.val == '(' && accept_lparen) {
+ goto parse_func_call;
+ } else if (s->token.val == '[') {
+ goto parse_array_access;
+ } else {
+ goto parse_property;
+ }
+ } else if (s->token.val == TOK_TEMPLATE &&
+ call_type == FUNC_CALL_NORMAL) {
+ if (optional_chaining_label >= 0) {
+ return js_parse_error(s, "template literal cannot appear in an optional chain");
+ }
+ call_type = FUNC_CALL_TEMPLATE;
+ goto parse_func_call2;
+ } else if (s->token.val == '(' && accept_lparen) {
+ int opcode, arg_count, drop_count;
+
+ /* function call */
+ parse_func_call:
+ if (next_token(s))
+ return -1;
+
+ if (call_type == FUNC_CALL_NORMAL) {
+ parse_func_call2:
+ switch(opcode = get_prev_opcode(fd)) {
+ case OP_get_field:
+ /* keep the object on the stack */
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2;
+ drop_count = 2;
+ break;
+ case OP_scope_get_private_field:
+ /* keep the object on the stack */
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2;
+ drop_count = 2;
+ break;
+ case OP_get_array_el:
+ /* keep the object on the stack */
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2;
+ drop_count = 2;
+ break;
+ case OP_scope_get_var:
+ {
+ JSAtom name;
+ int scope;
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ scope = get_u16(fd->byte_code.buf + fd->last_opcode_pos + 5);
+ if (name == JS_ATOM_eval && call_type == FUNC_CALL_NORMAL && !has_optional_chain) {
+ /* direct 'eval' */
+ opcode = OP_eval;
+ } else {
+ /* verify if function name resolves to a simple
+ get_loc/get_arg: a function call inside a `with`
+ statement can resolve to a method call of the
+ `with` context object
+ */
+ /* XXX: always generate the OP_scope_get_ref
+ and remove it in variable resolution
+ pass ? */
+ if (has_with_scope(fd, scope)) {
+ opcode = OP_scope_get_ref;
+ fd->byte_code.buf[fd->last_opcode_pos] = opcode;
+ }
+ }
+ drop_count = 1;
+ }
+ break;
+ case OP_get_super_value:
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el;
+ /* on stack: this func_obj */
+ opcode = OP_get_array_el;
+ drop_count = 2;
+ break;
+ default:
+ opcode = OP_invalid;
+ drop_count = 1;
+ break;
+ }
+ if (has_optional_chain) {
+ optional_chain_test(s, &optional_chaining_label,
+ drop_count);
+ }
+ } else {
+ opcode = OP_invalid;
+ }
+
+ if (call_type == FUNC_CALL_TEMPLATE) {
+ if (js_parse_template(s, 1, &arg_count))
+ return -1;
+ goto emit_func_call;
+ } else if (call_type == FUNC_CALL_SUPER_CTOR) {
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this_active_func);
+ emit_u16(s, 0);
+
+ emit_op(s, OP_get_super);
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_new_target);
+ emit_u16(s, 0);
+ } else if (call_type == FUNC_CALL_NEW) {
+ emit_op(s, OP_dup); /* new.target = function */
+ }
+
+ /* parse arguments */
+ arg_count = 0;
+ while (s->token.val != ')') {
+ if (arg_count >= 65535) {
+ return js_parse_error(s, "Too many call arguments");
+ }
+ if (s->token.val == TOK_ELLIPSIS)
+ break;
+ if (js_parse_assign_expr(s))
+ return -1;
+ arg_count++;
+ if (s->token.val == ')')
+ break;
+ /* accept a trailing comma before the ')' */
+ if (js_parse_expect(s, ','))
+ return -1;
+ }
+ if (s->token.val == TOK_ELLIPSIS) {
+ emit_op(s, OP_array_from);
+ emit_u16(s, arg_count);
+ emit_op(s, OP_push_i32);
+ emit_u32(s, arg_count);
+
+ /* on stack: array idx */
+ while (s->token.val != ')') {
+ if (s->token.val == TOK_ELLIPSIS) {
+ if (next_token(s))
+ return -1;
+ if (js_parse_assign_expr(s))
+ return -1;
+#if 1
+ /* XXX: could pass is_last indicator? */
+ emit_op(s, OP_append);
+#else
+ int label_next, label_done;
+ label_next = new_label(s);
+ label_done = new_label(s);
+ /* push enumerate object below array/idx pair */
+ emit_op(s, OP_for_of_start);
+ emit_op(s, OP_rot5l);
+ emit_op(s, OP_rot5l);
+ emit_label(s, label_next);
+ /* on stack: enum_rec array idx */
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 2);
+ emit_goto(s, OP_if_true, label_done);
+ /* append element */
+ /* enum_rec array idx val -> enum_rec array new_idx */
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_inc);
+ emit_goto(s, OP_goto, label_next);
+ emit_label(s, label_done);
+ /* close enumeration, drop enum_rec and idx */
+ emit_op(s, OP_drop); /* drop undef */
+ emit_op(s, OP_nip1); /* drop enum_rec */
+ emit_op(s, OP_nip1);
+ emit_op(s, OP_nip1);
+#endif
+ } else {
+ if (js_parse_assign_expr(s))
+ return -1;
+ /* array idx val */
+ emit_op(s, OP_define_array_el);
+ emit_op(s, OP_inc);
+ }
+ if (s->token.val == ')')
+ break;
+ /* accept a trailing comma before the ')' */
+ if (js_parse_expect(s, ','))
+ return -1;
+ }
+ if (next_token(s))
+ return -1;
+ /* drop the index */
+ emit_op(s, OP_drop);
+
+ /* apply function call */
+ switch(opcode) {
+ case OP_get_field:
+ case OP_scope_get_private_field:
+ case OP_get_array_el:
+ case OP_scope_get_ref:
+ /* obj func array -> func obj array */
+ emit_op(s, OP_perm3);
+ emit_op(s, OP_apply);
+ emit_u16(s, call_type == FUNC_CALL_NEW);
+ break;
+ case OP_eval:
+ emit_op(s, OP_apply_eval);
+ emit_u16(s, fd->scope_level);
+ fd->has_eval_call = TRUE;
+ break;
+ default:
+ if (call_type == FUNC_CALL_SUPER_CTOR) {
+ emit_op(s, OP_apply);
+ emit_u16(s, 1);
+ /* set the 'this' value */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_class_field_init(s);
+ } else if (call_type == FUNC_CALL_NEW) {
+ /* obj func array -> func obj array */
+ emit_op(s, OP_perm3);
+ emit_op(s, OP_apply);
+ emit_u16(s, 1);
+ } else {
+ /* func array -> func undef array */
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_swap);
+ emit_op(s, OP_apply);
+ emit_u16(s, 0);
+ }
+ break;
+ }
+ } else {
+ if (next_token(s))
+ return -1;
+ emit_func_call:
+ switch(opcode) {
+ case OP_get_field:
+ case OP_scope_get_private_field:
+ case OP_get_array_el:
+ case OP_scope_get_ref:
+ emit_op(s, OP_call_method);
+ emit_u16(s, arg_count);
+ break;
+ case OP_eval:
+ emit_op(s, OP_eval);
+ emit_u16(s, arg_count);
+ emit_u16(s, fd->scope_level);
+ fd->has_eval_call = TRUE;
+ break;
+ default:
+ if (call_type == FUNC_CALL_SUPER_CTOR) {
+ emit_op(s, OP_call_constructor);
+ emit_u16(s, arg_count);
+
+ /* set the 'this' value */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_class_field_init(s);
+ } else if (call_type == FUNC_CALL_NEW) {
+ emit_op(s, OP_call_constructor);
+ emit_u16(s, arg_count);
+ } else {
+ emit_op(s, OP_call);
+ emit_u16(s, arg_count);
+ }
+ break;
+ }
+ }
+ call_type = FUNC_CALL_NORMAL;
+ } else if (s->token.val == '.') {
+ if (next_token(s))
+ return -1;
+ parse_property:
+ if (s->token.val == TOK_PRIVATE_NAME) {
+ /* private class field */
+ if (get_prev_opcode(fd) == OP_get_super) {
+ return js_parse_error(s, "private class field forbidden after super");
+ }
+ if (has_optional_chain) {
+ optional_chain_test(s, &optional_chaining_label, 1);
+ }
+ emit_op(s, OP_scope_get_private_field);
+ emit_atom(s, s->token.u.ident.atom);
+ emit_u16(s, s->cur_func->scope_level);
+ } else {
+ if (!token_is_ident(s->token.val)) {
+ return js_parse_error(s, "expecting field name");
+ }
+ if (get_prev_opcode(fd) == OP_get_super) {
+ JSValue val;
+ int ret;
+ val = JS_AtomToValue(s->ctx, s->token.u.ident.atom);
+ ret = emit_push_const(s, val, 1);
+ JS_FreeValue(s->ctx, val);
+ if (ret)
+ return -1;
+ emit_op(s, OP_get_super_value);
+ } else {
+ if (has_optional_chain) {
+ optional_chain_test(s, &optional_chaining_label, 1);
+ }
+ emit_op(s, OP_get_field);
+ emit_atom(s, s->token.u.ident.atom);
+ }
+ }
+ if (next_token(s))
+ return -1;
+ } else if (s->token.val == '[') {
+ int prev_op;
+
+ parse_array_access:
+ prev_op = get_prev_opcode(fd);
+ if (has_optional_chain) {
+ optional_chain_test(s, &optional_chaining_label, 1);
+ }
+ if (next_token(s))
+ return -1;
+ if (js_parse_expr(s))
+ return -1;
+ if (js_parse_expect(s, ']'))
+ return -1;
+ if (prev_op == OP_get_super) {
+ emit_op(s, OP_get_super_value);
+ } else {
+ emit_op(s, OP_get_array_el);
+ }
+ } else {
+ break;
+ }
+ }
+ if (optional_chaining_label >= 0)
+ emit_label(s, optional_chaining_label);
+ return 0;
+}
+
+static warn_unused int js_parse_delete(JSParseState *s)
+{
+ JSFunctionDef *fd = s->cur_func;
+ JSAtom name;
+
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_FORBIDDEN))
+ return -1;
+ switch (get_prev_opcode(fd)) {
+ case OP_get_field:
+ {
+ JSValue val;
+ int ret;
+
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ fd->byte_code.size = fd->last_opcode_pos;
+ fd->last_opcode_pos = -1;
+ val = JS_AtomToValue(s->ctx, name);
+ ret = emit_push_const(s, val, 1);
+ JS_FreeValue(s->ctx, val);
+ JS_FreeAtom(s->ctx, name);
+ if (ret)
+ return ret;
+ }
+ goto do_delete;
+ case OP_get_array_el:
+ fd->byte_code.size = fd->last_opcode_pos;
+ fd->last_opcode_pos = -1;
+ do_delete:
+ emit_op(s, OP_delete);
+ break;
+ case OP_scope_get_var:
+ /* 'delete this': this is not a reference */
+ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1);
+ if (name == JS_ATOM_this || name == JS_ATOM_new_target)
+ goto ret_true;
+ if (fd->js_mode & JS_MODE_STRICT) {
+ return js_parse_error(s, "cannot delete a direct reference in strict mode");
+ } else {
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_delete_var;
+ }
+ break;
+ case OP_scope_get_private_field:
+ return js_parse_error(s, "cannot delete a private class field");
+ case OP_get_super_value:
+ emit_op(s, OP_throw_error);
+ emit_atom(s, JS_ATOM_NULL);
+ emit_u8(s, JS_THROW_ERROR_DELETE_SUPER);
+ break;
+ default:
+ ret_true:
+ emit_op(s, OP_drop);
+ emit_op(s, OP_push_true);
+ break;
+ }
+ return 0;
+}
+
+/* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */
+static warn_unused int js_parse_unary(JSParseState *s, int parse_flags)
+{
+ int op;
+
+ switch(s->token.val) {
+ case '+':
+ case '-':
+ case '!':
+ case '~':
+ case TOK_VOID:
+ op = s->token.val;
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_FORBIDDEN))
+ return -1;
+ switch(op) {
+ case '-':
+ emit_op(s, OP_neg);
+ break;
+ case '+':
+ emit_op(s, OP_plus);
+ break;
+ case '!':
+ emit_op(s, OP_lnot);
+ break;
+ case '~':
+ emit_op(s, OP_not);
+ break;
+ case TOK_VOID:
+ emit_op(s, OP_drop);
+ emit_op(s, OP_undefined);
+ break;
+ default:
+ abort();
+ }
+ parse_flags = 0;
+ break;
+ case TOK_DEC:
+ case TOK_INC:
+ {
+ int opcode, op, scope, label;
+ JSAtom name;
+ op = s->token.val;
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, 0))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
+ return -1;
+ emit_op(s, OP_dec + op - TOK_DEC);
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP,
+ FALSE);
+ }
+ break;
+ case TOK_TYPEOF:
+ {
+ JSFunctionDef *fd;
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_FORBIDDEN))
+ return -1;
+ /* reference access should not return an exception, so we
+ patch the get_var */
+ fd = s->cur_func;
+ if (get_prev_opcode(fd) == OP_scope_get_var) {
+ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_var_undef;
+ }
+ emit_op(s, OP_typeof);
+ parse_flags = 0;
+ }
+ break;
+ case TOK_DELETE:
+ if (js_parse_delete(s))
+ return -1;
+ parse_flags = 0;
+ break;
+ case TOK_AWAIT:
+ if (!(s->cur_func->func_kind & JS_FUNC_ASYNC))
+ return js_parse_error(s, "unexpected 'await' keyword");
+ if (!s->cur_func->in_function_body)
+ return js_parse_error(s, "await in default expression");
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_FORBIDDEN))
+ return -1;
+ emit_op(s, OP_await);
+ parse_flags = 0;
+ break;
+ default:
+ if (js_parse_postfix_expr(s, (parse_flags & PF_ARROW_FUNC) |
+ PF_POSTFIX_CALL))
+ return -1;
+ if (!s->got_lf &&
+ (s->token.val == TOK_DEC || s->token.val == TOK_INC)) {
+ int opcode, op, scope, label;
+ JSAtom name;
+ op = s->token.val;
+ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, TRUE, op))
+ return -1;
+ emit_op(s, OP_post_dec + op - TOK_DEC);
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_SECOND,
+ FALSE);
+ if (next_token(s))
+ return -1;
+ }
+ break;
+ }
+ if (parse_flags & (PF_POW_ALLOWED | PF_POW_FORBIDDEN)) {
+#ifdef CONFIG_BIGNUM
+ if (s->token.val == TOK_POW || s->token.val == TOK_MATH_POW) {
+ /* Extended exponentiation syntax rules: we extend the ES7
+ grammar in order to have more intuitive semantics:
+ -2**2 evaluates to -4. */
+ if (!(s->cur_func->js_mode & JS_MODE_MATH)) {
+ if (parse_flags & PF_POW_FORBIDDEN) {
+ JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
+ return -1;
+ }
+ }
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_ALLOWED))
+ return -1;
+ emit_op(s, OP_pow);
+ }
+#else
+ if (s->token.val == TOK_POW) {
+ /* Strict ES7 exponentiation syntax rules: To solve
+ conficting semantics between different implementations
+ regarding the precedence of prefix operators and the
+ postifx exponential, ES7 specifies that -2**2 is a
+ syntax error. */
+ if (parse_flags & PF_POW_FORBIDDEN) {
+ JS_ThrowSyntaxError(s->ctx, "unparenthesized unary expression can't appear on the left-hand side of '**'");
+ return -1;
+ }
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_ALLOWED))
+ return -1;
+ emit_op(s, OP_pow);
+ }
+#endif
+ }
+ return 0;
+}
+
+/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+static warn_unused int js_parse_expr_binary(JSParseState *s, int level,
+ int parse_flags)
+{
+ int op, opcode;
+
+ if (level == 0) {
+ return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) |
+ PF_POW_ALLOWED);
+ }
+ if (js_parse_expr_binary(s, level - 1, parse_flags))
+ return -1;
+ for(;;) {
+ op = s->token.val;
+ switch(level) {
+ case 1:
+ switch(op) {
+ case '*':
+ opcode = OP_mul;
+ break;
+ case '/':
+ opcode = OP_div;
+ break;
+ case '%':
+#ifdef CONFIG_BIGNUM
+ if (s->cur_func->js_mode & JS_MODE_MATH)
+ opcode = OP_math_mod;
+ else
+#endif
+ opcode = OP_mod;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 2:
+ switch(op) {
+ case '+':
+ opcode = OP_add;
+ break;
+ case '-':
+ opcode = OP_sub;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 3:
+ switch(op) {
+ case TOK_SHL:
+ opcode = OP_shl;
+ break;
+ case TOK_SAR:
+ opcode = OP_sar;
+ break;
+ case TOK_SHR:
+ opcode = OP_shr;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 4:
+ switch(op) {
+ case '<':
+ opcode = OP_lt;
+ break;
+ case '>':
+ opcode = OP_gt;
+ break;
+ case TOK_LTE:
+ opcode = OP_lte;
+ break;
+ case TOK_GTE:
+ opcode = OP_gte;
+ break;
+ case TOK_INSTANCEOF:
+ opcode = OP_instanceof;
+ break;
+ case TOK_IN:
+ if (parse_flags & PF_IN_ACCEPTED) {
+ opcode = OP_in;
+ } else {
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 5:
+ switch(op) {
+ case TOK_EQ:
+ opcode = OP_eq;
+ break;
+ case TOK_NEQ:
+ opcode = OP_neq;
+ break;
+ case TOK_STRICT_EQ:
+ opcode = OP_strict_eq;
+ break;
+ case TOK_STRICT_NEQ:
+ opcode = OP_strict_neq;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 6:
+ switch(op) {
+ case '&':
+ opcode = OP_and;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 7:
+ switch(op) {
+ case '^':
+ opcode = OP_xor;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ case 8:
+ switch(op) {
+ case '|':
+ opcode = OP_or;
+ break;
+ default:
+ return 0;
+ }
+ break;
+ default:
+ abort();
+ }
+ if (next_token(s))
+ return -1;
+ if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC))
+ return -1;
+ emit_op(s, opcode);
+ }
+ return 0;
+}
+
+/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+static warn_unused int js_parse_logical_and_or(JSParseState *s, int op,
+ int parse_flags)
+{
+ int label1;
+
+ if (op == TOK_LAND) {
+ if (js_parse_expr_binary(s, 8, parse_flags))
+ return -1;
+ } else {
+ if (js_parse_logical_and_or(s, TOK_LAND, parse_flags))
+ return -1;
+ }
+ if (s->token.val == op) {
+ label1 = new_label(s);
+
+ for(;;) {
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_dup);
+ emit_goto(s, op == TOK_LAND ? OP_if_false : OP_if_true, label1);
+ emit_op(s, OP_drop);
+
+ if (op == TOK_LAND) {
+ if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))
+ return -1;
+ } else {
+ if (js_parse_logical_and_or(s, TOK_LAND,
+ parse_flags & ~PF_ARROW_FUNC))
+ return -1;
+ }
+ if (s->token.val != op) {
+ if (s->token.val == TOK_DOUBLE_QUESTION_MARK)
+ return js_parse_error(s, "cannot mix ?? with && or ||");
+ break;
+ }
+ }
+
+ emit_label(s, label1);
+ }
+ return 0;
+}
+
+static warn_unused int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
+{
+ int label1;
+
+ if (js_parse_logical_and_or(s, TOK_LOR, parse_flags))
+ return -1;
+ if (s->token.val == TOK_DOUBLE_QUESTION_MARK) {
+ label1 = new_label(s);
+ for(;;) {
+ if (next_token(s))
+ return -1;
+
+ emit_op(s, OP_dup);
+ emit_op(s, OP_is_undefined_or_null);
+ emit_goto(s, OP_if_false, label1);
+ emit_op(s, OP_drop);
+
+ if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC))
+ return -1;
+ if (s->token.val != TOK_DOUBLE_QUESTION_MARK)
+ break;
+ }
+ emit_label(s, label1);
+ }
+ return 0;
+}
+
+/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+static warn_unused int js_parse_cond_expr(JSParseState *s, int parse_flags)
+{
+ int label1, label2;
+
+ if (js_parse_coalesce_expr(s, parse_flags))
+ return -1;
+ if (s->token.val == '?') {
+ if (next_token(s))
+ return -1;
+ label1 = emit_goto(s, OP_if_false, -1);
+
+ if (js_parse_assign_expr(s))
+ return -1;
+ if (js_parse_expect(s, ':'))
+ return -1;
+
+ label2 = emit_goto(s, OP_goto, -1);
+
+ emit_label(s, label1);
+
+ if (js_parse_assign_expr2(s, parse_flags & PF_IN_ACCEPTED))
+ return -1;
+
+ emit_label(s, label2);
+ }
+ return 0;
+}
+
+static void emit_return(JSParseState *s, BOOL hasval);
+
+/* allowed parse_flags: PF_IN_ACCEPTED */
+static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags)
+{
+ int opcode, op, scope;
+ JSAtom name0 = JS_ATOM_NULL;
+ JSAtom name;
+
+ if (s->token.val == TOK_YIELD) {
+ BOOL is_star = FALSE, is_async;
+
+ if (!(s->cur_func->func_kind & JS_FUNC_GENERATOR))
+ return js_parse_error(s, "unexpected 'yield' keyword");
+ if (!s->cur_func->in_function_body)
+ return js_parse_error(s, "yield in default expression");
+ if (next_token(s))
+ return -1;
+ /* XXX: is there a better method to detect 'yield' without
+ parameters ? */
+ if (s->token.val != ';' && s->token.val != ')' &&
+ s->token.val != ']' && s->token.val != '}' &&
+ s->token.val != ',' && s->token.val != ':' && !s->got_lf) {
+ if (s->token.val == '*') {
+ is_star = TRUE;
+ if (next_token(s))
+ return -1;
+ }
+ if (js_parse_assign_expr2(s, parse_flags))
+ return -1;
+ } else {
+ emit_op(s, OP_undefined);
+ }
+ is_async = (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR);
+
+ if (is_star) {
+ int label_loop, label_return, label_next;
+ int label_return1, label_yield, label_throw, label_throw1;
+ int label_throw2;
+
+ label_loop = new_label(s);
+ label_yield = new_label(s);
+
+ emit_op(s, is_async ? OP_for_await_of_start : OP_for_of_start);
+
+ /* remove the catch offset (XXX: could avoid pushing back
+ undefined) */
+ emit_op(s, OP_drop);
+ emit_op(s, OP_undefined);
+
+ emit_op(s, OP_undefined); /* initial value */
+
+ emit_label(s, label_loop);
+ emit_op(s, OP_iterator_next);
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_op(s, OP_iterator_check_object);
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_done);
+ label_next = emit_goto(s, OP_if_true, -1); /* end of loop */
+ emit_label(s, label_yield);
+ if (is_async) {
+ /* OP_async_yield_star takes the value as parameter */
+ emit_op(s, OP_get_field);
+ emit_atom(s, JS_ATOM_value);
+ emit_op(s, OP_await);
+ emit_op(s, OP_async_yield_star);
+ } else {
+ /* OP_yield_star takes (value, done) as parameter */
+ emit_op(s, OP_yield_star);
+ }
+ emit_op(s, OP_dup);
+ label_return = emit_goto(s, OP_if_true, -1);
+ emit_op(s, OP_drop);
+ emit_goto(s, OP_goto, label_loop);
+
+ emit_label(s, label_return);
+ emit_op(s, OP_push_i32);
+ emit_u32(s, 2);
+ emit_op(s, OP_strict_eq);
+ label_throw = emit_goto(s, OP_if_true, -1);
+
+ /* return handling */
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_op(s, OP_iterator_call);
+ emit_u8(s, 0);
+ label_return1 = emit_goto(s, OP_if_true, -1);
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_op(s, OP_iterator_check_object);
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_done);
+ emit_goto(s, OP_if_false, label_yield);
+
+ emit_op(s, OP_get_field);
+ emit_atom(s, JS_ATOM_value);
+
+ emit_label(s, label_return1);
+ emit_op(s, OP_nip);
+ emit_op(s, OP_nip);
+ emit_op(s, OP_nip);
+ emit_return(s, TRUE);
+
+ /* throw handling */
+ emit_label(s, label_throw);
+ emit_op(s, OP_iterator_call);
+ emit_u8(s, 1);
+ label_throw1 = emit_goto(s, OP_if_true, -1);
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_op(s, OP_iterator_check_object);
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_done);
+ emit_goto(s, OP_if_false, label_yield);
+ emit_goto(s, OP_goto, label_next);
+ /* close the iterator and throw a type error exception */
+ emit_label(s, label_throw1);
+ emit_op(s, OP_iterator_call);
+ emit_u8(s, 2);
+ label_throw2 = emit_goto(s, OP_if_true, -1);
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_label(s, label_throw2);
+
+ emit_op(s, OP_throw_error);
+ emit_atom(s, JS_ATOM_NULL);
+ emit_u8(s, JS_THROW_ERROR_ITERATOR_THROW);
+
+ emit_label(s, label_next);
+ emit_op(s, OP_get_field);
+ emit_atom(s, JS_ATOM_value);
+ emit_op(s, OP_nip); /* keep the value associated with
+ done = true */
+ emit_op(s, OP_nip);
+ emit_op(s, OP_nip);
+ } else {
+ int label_next;
+
+ if (is_async)
+ emit_op(s, OP_await);
+ emit_op(s, OP_yield);
+ label_next = emit_goto(s, OP_if_false, -1);
+ emit_return(s, TRUE);
+ emit_label(s, label_next);
+ }
+ return 0;
+ }
+ if (s->token.val == TOK_IDENT) {
+ /* name0 is used to check for OP_set_name pattern, not duplicated */
+ name0 = s->token.u.ident.atom;
+ }
+ if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC))
+ return -1;
+
+ op = s->token.val;
+ if (op == '=' || (op >= TOK_MUL_ASSIGN && op <= TOK_POW_ASSIGN)) {
+ int label;
+ if (next_token(s))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &name, &label, NULL, (op != '='), op) < 0)
+ return -1;
+
+ if (js_parse_assign_expr2(s, parse_flags)) {
+ JS_FreeAtom(s->ctx, name);
+ return -1;
+ }
+
+ if (op == '=') {
+ if (opcode == OP_get_ref_value && name == name0) {
+ set_object_name(s, name);
+ }
+ } else {
+ static const uint8_t assign_opcodes[] = {
+ OP_mul, OP_div, OP_mod, OP_add, OP_sub,
+ OP_shl, OP_sar, OP_shr, OP_and, OP_xor, OP_or,
+#ifdef CONFIG_BIGNUM
+ OP_pow,
+#endif
+ OP_pow,
+ };
+ op = assign_opcodes[op - TOK_MUL_ASSIGN];
+#ifdef CONFIG_BIGNUM
+ if (s->cur_func->js_mode & JS_MODE_MATH) {
+ if (op == OP_mod)
+ op = OP_math_mod;
+ }
+#endif
+ emit_op(s, op);
+ }
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_KEEP_TOP, FALSE);
+ } else if (op >= TOK_LAND_ASSIGN && op <= TOK_DOUBLE_QUESTION_MARK_ASSIGN) {
+ int label, label1, depth_lvalue, label2;
+
+ if (next_token(s))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &name, &label,
+ &depth_lvalue, TRUE, op) < 0)
+ return -1;
+
+ emit_op(s, OP_dup);
+ if (op == TOK_DOUBLE_QUESTION_MARK_ASSIGN)
+ emit_op(s, OP_is_undefined_or_null);
+ label1 = emit_goto(s, op == TOK_LOR_ASSIGN ? OP_if_true : OP_if_false,
+ -1);
+ emit_op(s, OP_drop);
+
+ if (js_parse_assign_expr2(s, parse_flags)) {
+ JS_FreeAtom(s->ctx, name);
+ return -1;
+ }
+
+ if (opcode == OP_get_ref_value && name == name0) {
+ set_object_name(s, name);
+ }
+
+ switch(depth_lvalue) {
+ case 1:
+ emit_op(s, OP_insert2);
+ break;
+ case 2:
+ emit_op(s, OP_insert3);
+ break;
+ case 3:
+ emit_op(s, OP_insert4);
+ break;
+ default:
+ abort();
+ }
+
+ /* XXX: we disable the OP_put_ref_value optimization by not
+ using put_lvalue() otherwise depth_lvalue is not correct */
+ put_lvalue(s, opcode, scope, name, label, PUT_LVALUE_NOKEEP_DEPTH,
+ FALSE);
+ label2 = emit_goto(s, OP_goto, -1);
+
+ emit_label(s, label1);
+
+ /* remove the lvalue stack entries */
+ while (depth_lvalue != 0) {
+ emit_op(s, OP_nip);
+ depth_lvalue--;
+ }
+
+ emit_label(s, label2);
+ }
+ return 0;
+}
+
+static warn_unused int js_parse_assign_expr(JSParseState *s)
+{
+ return js_parse_assign_expr2(s, PF_IN_ACCEPTED);
+}
+
+/* allowed parse_flags: PF_IN_ACCEPTED */
+static warn_unused int js_parse_expr2(JSParseState *s, int parse_flags)
+{
+ BOOL comma = FALSE;
+ for(;;) {
+ if (js_parse_assign_expr2(s, parse_flags))
+ return -1;
+ if (comma) {
+ /* prevent get_lvalue from using the last expression
+ as an lvalue. This also prevents the conversion of
+ of get_var to get_ref for method lookup in function
+ call inside `with` statement.
+ */
+ s->cur_func->last_opcode_pos = -1;
+ }
+ if (s->token.val != ',')
+ break;
+ comma = TRUE;
+ if (next_token(s))
+ return -1;
+ emit_op(s, OP_drop);
+ }
+ return 0;
+}
+
+static warn_unused int js_parse_expr(JSParseState *s)
+{
+ return js_parse_expr2(s, PF_IN_ACCEPTED);
+}
+
+static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
+ JSAtom label_name,
+ int label_break, int label_cont,
+ int drop_count)
+{
+ be->prev = fd->top_break;
+ fd->top_break = be;
+ be->label_name = label_name;
+ be->label_break = label_break;
+ be->label_cont = label_cont;
+ be->drop_count = drop_count;
+ be->label_finally = -1;
+ be->scope_level = fd->scope_level;
+ be->has_iterator = FALSE;
+}
+
+static void pop_break_entry(JSFunctionDef *fd)
+{
+ BlockEnv *be;
+ be = fd->top_break;
+ fd->top_break = be->prev;
+}
+
+static warn_unused int emit_break(JSParseState *s, JSAtom name, int is_cont)
+{
+ BlockEnv *top;
+ int i, scope_level;
+
+ scope_level = s->cur_func->scope_level;
+ top = s->cur_func->top_break;
+ while (top != NULL) {
+ close_scopes(s, scope_level, top->scope_level);
+ scope_level = top->scope_level;
+ if (is_cont &&
+ top->label_cont != -1 &&
+ (name == JS_ATOM_NULL || top->label_name == name)) {
+ /* continue stays inside the same block */
+ emit_goto(s, OP_goto, top->label_cont);
+ return 0;
+ }
+ if (!is_cont &&
+ top->label_break != -1 &&
+ (name == JS_ATOM_NULL || top->label_name == name)) {
+ emit_goto(s, OP_goto, top->label_break);
+ return 0;
+ }
+ i = 0;
+ if (top->has_iterator) {
+ emit_op(s, OP_iterator_close);
+ i += 3;
+ }
+ for(; i < top->drop_count; i++)
+ emit_op(s, OP_drop);
+ if (top->label_finally != -1) {
+ /* must push dummy value to keep same stack depth */
+ emit_op(s, OP_undefined);
+ emit_goto(s, OP_gosub, top->label_finally);
+ emit_op(s, OP_drop);
+ }
+ top = top->prev;
+ }
+ if (name == JS_ATOM_NULL) {
+ if (is_cont)
+ return js_parse_error(s, "continue must be inside loop");
+ else
+ return js_parse_error(s, "break must be inside loop or switch");
+ } else {
+ return js_parse_error(s, "break/continue label not found");
+ }
+}
+
+/* execute the finally blocks before return */
+static void emit_return(JSParseState *s, BOOL hasval)
+{
+ BlockEnv *top;
+ int drop_count;
+
+ drop_count = 0;
+ top = s->cur_func->top_break;
+ while (top != NULL) {
+ /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not
+ required as all local variables will be closed upon returning
+ from JS_CallInternal, but not in the same order. */
+ if (top->has_iterator) {
+ /* with 'yield', the exact number of OP_drop to emit is
+ unknown, so we use a specific operation to look for
+ the catch offset */
+ if (!hasval) {
+ emit_op(s, OP_undefined);
+ hasval = TRUE;
+ }
+ emit_op(s, OP_iterator_close_return);
+ if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
+ int label_next, label_next2;
+
+ emit_op(s, OP_drop); /* catch offset */
+ emit_op(s, OP_drop); /* next */
+ emit_op(s, OP_get_field2);
+ emit_atom(s, JS_ATOM_return);
+ /* stack: iter_obj return_func */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_is_undefined_or_null);
+ label_next = emit_goto(s, OP_if_true, -1);
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+ emit_op(s, OP_iterator_check_object);
+ emit_op(s, OP_await);
+ label_next2 = emit_goto(s, OP_goto, -1);
+ emit_label(s, label_next);
+ emit_op(s, OP_drop);
+ emit_label(s, label_next2);
+ emit_op(s, OP_drop);
+ } else {
+ emit_op(s, OP_iterator_close);
+ }
+ drop_count = -3;
+ }
+ drop_count += top->drop_count;
+ if (top->label_finally != -1) {
+ while(drop_count) {
+ /* must keep the stack top if hasval */
+ emit_op(s, hasval ? OP_nip : OP_drop);
+ drop_count--;
+ }
+ if (!hasval) {
+ /* must push return value to keep same stack size */
+ emit_op(s, OP_undefined);
+ hasval = TRUE;
+ }
+ emit_goto(s, OP_gosub, top->label_finally);
+ }
+ top = top->prev;
+ }
+ if (s->cur_func->is_derived_class_constructor) {
+ int label_return;
+
+ /* 'this' can be uninitialized, so it may be accessed only if
+ the derived class constructor does not return an object */
+ if (hasval) {
+ emit_op(s, OP_check_ctor_return);
+ label_return = emit_goto(s, OP_if_false, -1);
+ emit_op(s, OP_drop);
+ } else {
+ label_return = -1;
+ }
+
+ /* XXX: if this is not initialized, should throw the
+ ReferenceError in the caller realm */
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, JS_ATOM_this);
+ emit_u16(s, 0);
+
+ emit_label(s, label_return);
+ emit_op(s, OP_return);
+ } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) {
+ if (!hasval) {
+ emit_op(s, OP_undefined);
+ } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) {
+ emit_op(s, OP_await);
+ }
+ emit_op(s, OP_return_async);
+ } else {
+ emit_op(s, hasval ? OP_return : OP_return_undef);
+ }
+}
+
+#define DECL_MASK_FUNC (1 << 0) /* allow normal function declaration */
+/* ored with DECL_MASK_FUNC if function declarations are allowed with a label */
+#define DECL_MASK_FUNC_WITH_LABEL (1 << 1)
+#define DECL_MASK_OTHER (1 << 2) /* all other declarations */
+#define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)
+
+static warn_unused int js_parse_statement_or_decl(JSParseState *s,
+ int decl_mask);
+
+static warn_unused int js_parse_statement(JSParseState *s)
+{
+ return js_parse_statement_or_decl(s, 0);
+}
+
+static warn_unused int js_parse_block(JSParseState *s)
+{
+ if (js_parse_expect(s, '{'))
+ return -1;
+ if (s->token.val != '}') {
+ push_scope(s);
+ for(;;) {
+ if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
+ return -1;
+ if (s->token.val == '}')
+ break;
+ }
+ pop_scope(s);
+ }
+ if (next_token(s))
+ return -1;
+ return 0;
+}
+
+/* allowed parse_flags: PF_IN_ACCEPTED */
+static warn_unused int js_parse_var(JSParseState *s, int parse_flags, int tok,
+ BOOL export_flag)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionDef *fd = s->cur_func;
+ JSAtom name = JS_ATOM_NULL;
+
+ for (;;) {
+ if (s->token.val == TOK_IDENT) {
+ if (s->token.u.ident.is_reserved) {
+ return js_parse_error_reserved_identifier(s);
+ }
+ name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (name == JS_ATOM_let && (tok == TOK_LET || tok == TOK_CONST)) {
+ js_parse_error(s, "'let' is not a valid lexical identifier");
+ goto var_error;
+ }
+ if (next_token(s))
+ goto var_error;
+ if (js_define_var(s, name, tok))
+ goto var_error;
+ if (export_flag) {
+ if (!add_export_entry(s, s->cur_func->module, name, name,
+ JS_EXPORT_TYPE_LOCAL))
+ goto var_error;
+ }
+
+ if (s->token.val == '=') {
+ if (next_token(s))
+ goto var_error;
+ if (tok == TOK_VAR) {
+ /* Must make a reference for proper `with` semantics */
+ int opcode, scope, label;
+ JSAtom name1;
+
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ if (get_lvalue(s, &opcode, &scope, &name1, &label, NULL, FALSE, '=') < 0)
+ goto var_error;
+ if (js_parse_assign_expr2(s, parse_flags)) {
+ JS_FreeAtom(ctx, name1);
+ goto var_error;
+ }
+ set_object_name(s, name);
+ put_lvalue(s, opcode, scope, name1, label,
+ PUT_LVALUE_NOKEEP, FALSE);
+ } else {
+ if (js_parse_assign_expr2(s, parse_flags))
+ goto var_error;
+ set_object_name(s, name);
+ emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
+ OP_scope_put_var_init : OP_scope_put_var);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ }
+ } else {
+ if (tok == TOK_CONST) {
+ js_parse_error(s, "missing initializer for const variable");
+ goto var_error;
+ }
+ if (tok == TOK_LET) {
+ /* initialize lexical variable upon entering its scope */
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ }
+ }
+ JS_FreeAtom(ctx, name);
+ } else {
+ int skip_bits;
+ if ((s->token.val == '[' || s->token.val == '{')
+ && js_parse_skip_parens_token(s, &skip_bits, FALSE) == '=') {
+ emit_op(s, OP_undefined);
+ if (js_parse_destructuring_element(s, tok, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
+ return -1;
+ } else {
+ return js_parse_error(s, "variable name expected");
+ }
+ }
+ if (s->token.val != ',')
+ break;
+ if (next_token(s))
+ return -1;
+ }
+ return 0;
+
+ var_error:
+ JS_FreeAtom(ctx, name);
+ return -1;
+}
+
+/* test if the current token is a label. Use simplistic look-ahead scanner */
+static BOOL is_label(JSParseState *s)
+{
+ return (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved &&
+ peek_token(s, FALSE) == ':');
+}
+
+/* test if the current token is a let keyword. Use simplistic look-ahead scanner */
+static int is_let(JSParseState *s, int decl_mask)
+{
+ int res = FALSE;
+
+ if (token_is_pseudo_keyword(s, JS_ATOM_let)) {
+#if 1
+ JSParsePos pos;
+ js_parse_get_pos(s, &pos);
+ for (;;) {
+ if (next_token(s)) {
+ res = -1;
+ break;
+ }
+ if (s->token.val == '[') {
+ /* let [ is a syntax restriction:
+ it never introduces an ExpressionStatement */
+ res = TRUE;
+ break;
+ }
+ if (s->token.val == '{' ||
+ (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved) ||
+ s->token.val == TOK_LET ||
+ s->token.val == TOK_YIELD ||
+ s->token.val == TOK_AWAIT) {
+ /* Check for possible ASI if not scanning for Declaration */
+ /* XXX: should also check that `{` introduces a BindingPattern,
+ but Firefox does not and rejects eval("let=1;let\n{if(1)2;}") */
+ if (s->last_line_num == s->token.line_num || (decl_mask & DECL_MASK_OTHER)) {
+ res = TRUE;
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ if (js_parse_seek_token(s, &pos)) {
+ res = -1;
+ }
+#else
+ int tok = peek_token(s, TRUE);
+ if (tok == '{' || tok == TOK_IDENT || peek_token(s, FALSE) == '[') {
+ res = TRUE;
+ }
+#endif
+ }
+ return res;
+}
+
+/* XXX: handle IteratorClose when exiting the loop before the
+ enumeration is done */
+static warn_unused int js_parse_for_in_of(JSParseState *s, int label_name,
+ BOOL is_async)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionDef *fd = s->cur_func;
+ JSAtom var_name;
+ BOOL has_initializer, is_for_of, has_destructuring;
+ int tok, tok1, opcode, scope, block_scope_level;
+ int label_next, label_expr, label_cont, label_body, label_break;
+ int pos_next, pos_expr;
+ BlockEnv break_entry;
+
+ has_initializer = FALSE;
+ has_destructuring = FALSE;
+ is_for_of = FALSE;
+ block_scope_level = fd->scope_level;
+ label_cont = new_label(s);
+ label_body = new_label(s);
+ label_break = new_label(s);
+ label_next = new_label(s);
+
+ /* create scope for the lexical variables declared in the enumeration
+ expressions. XXX: Not completely correct because of weird capturing
+ semantics in `for (i of o) a.push(function(){return i})` */
+ push_scope(s);
+
+ /* local for_in scope starts here so individual elements
+ can be closed in statement. */
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, label_cont, 1);
+ break_entry.scope_level = block_scope_level;
+
+ label_expr = emit_goto(s, OP_goto, -1);
+
+ pos_next = s->cur_func->byte_code.size;
+ emit_label(s, label_next);
+
+ tok = s->token.val;
+ switch (is_let(s, DECL_MASK_OTHER)) {
+ case TRUE:
+ tok = TOK_LET;
+ break;
+ case FALSE:
+ break;
+ default:
+ return -1;
+ }
+ if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
+ if (next_token(s))
+ return -1;
+
+ if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
+ if (s->token.val == '[' || s->token.val == '{') {
+ if (js_parse_destructuring_element(s, tok, 0, TRUE, -1, FALSE) < 0)
+ return -1;
+ has_destructuring = TRUE;
+ } else {
+ return js_parse_error(s, "variable name expected");
+ }
+ var_name = JS_ATOM_NULL;
+ } else {
+ var_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s)) {
+ JS_FreeAtom(s->ctx, var_name);
+ return -1;
+ }
+ if (js_define_var(s, var_name, tok)) {
+ JS_FreeAtom(s->ctx, var_name);
+ return -1;
+ }
+ emit_op(s, (tok == TOK_CONST || tok == TOK_LET) ?
+ OP_scope_put_var_init : OP_scope_put_var);
+ emit_atom(s, var_name);
+ emit_u16(s, fd->scope_level);
+ }
+ } else {
+ int skip_bits;
+ if ((s->token.val == '[' || s->token.val == '{')
+ && ((tok1 = js_parse_skip_parens_token(s, &skip_bits, FALSE)) == TOK_IN || tok1 == TOK_OF)) {
+ if (js_parse_destructuring_element(s, 0, 0, TRUE, skip_bits & SKIP_HAS_ELLIPSIS, TRUE) < 0)
+ return -1;
+ } else {
+ int lvalue_label;
+ if (js_parse_left_hand_side_expr(s))
+ return -1;
+ if (get_lvalue(s, &opcode, &scope, &var_name, &lvalue_label,
+ NULL, FALSE, TOK_FOR))
+ return -1;
+ put_lvalue(s, opcode, scope, var_name, lvalue_label,
+ PUT_LVALUE_NOKEEP_BOTTOM, FALSE);
+ }
+ var_name = JS_ATOM_NULL;
+ }
+ emit_goto(s, OP_goto, label_body);
+
+ pos_expr = s->cur_func->byte_code.size;
+ emit_label(s, label_expr);
+ if (s->token.val == '=') {
+ /* XXX: potential scoping issue if inside `with` statement */
+ has_initializer = TRUE;
+ /* parse and evaluate initializer prior to evaluating the
+ object (only used with "for in" with a non lexical variable
+ in non strict mode */
+ if (next_token(s) || js_parse_assign_expr2(s, 0)) {
+ JS_FreeAtom(ctx, var_name);
+ return -1;
+ }
+ if (var_name != JS_ATOM_NULL) {
+ emit_op(s, OP_scope_put_var);
+ emit_atom(s, var_name);
+ emit_u16(s, fd->scope_level);
+ }
+ }
+ JS_FreeAtom(ctx, var_name);
+
+ if (token_is_pseudo_keyword(s, JS_ATOM_of)) {
+ break_entry.has_iterator = is_for_of = TRUE;
+ break_entry.drop_count += 2;
+ if (has_initializer)
+ goto initializer_error;
+ } else if (s->token.val == TOK_IN) {
+ if (is_async)
+ return js_parse_error(s, "'for await' loop should be used with 'of'");
+ if (has_initializer &&
+ (tok != TOK_VAR || (fd->js_mode & JS_MODE_STRICT) ||
+ has_destructuring)) {
+ initializer_error:
+ return js_parse_error(s, "a declaration in the head of a for-%s loop can't have an initializer",
+ is_for_of ? "of" : "in");
+ }
+ } else {
+ return js_parse_error(s, "expected 'of' or 'in' in for control expression");
+ }
+ if (next_token(s))
+ return -1;
+ if (is_for_of) {
+ if (js_parse_assign_expr(s))
+ return -1;
+ } else {
+ if (js_parse_expr(s))
+ return -1;
+ }
+ /* close the scope after having evaluated the expression so that
+ the TDZ values are in the closures */
+ close_scopes(s, s->cur_func->scope_level, block_scope_level);
+ if (is_for_of) {
+ if (is_async)
+ emit_op(s, OP_for_await_of_start);
+ else
+ emit_op(s, OP_for_of_start);
+ /* on stack: enum_rec */
+ } else {
+ emit_op(s, OP_for_in_start);
+ /* on stack: enum_obj */
+ }
+ emit_goto(s, OP_goto, label_cont);
+
+ if (js_parse_expect(s, ')'))
+ return -1;
+
+ if (OPTIMIZE) {
+ /* move the `next` code here */
+ DynBuf *bc = &s->cur_func->byte_code;
+ int chunk_size = pos_expr - pos_next;
+ int offset = bc->size - pos_next;
+ int i;
+ dbuf_realloc(bc, bc->size + chunk_size);
+ dbuf_put(bc, bc->buf + pos_next, chunk_size);
+ memset(bc->buf + pos_next, OP_nop, chunk_size);
+ /* `next` part ends with a goto */
+ s->cur_func->last_opcode_pos = bc->size - 5;
+ /* relocate labels */
+ for (i = label_cont; i < s->cur_func->label_count; i++) {
+ LabelSlot *ls = &s->cur_func->label_slots[i];
+ if (ls->pos >= pos_next && ls->pos < pos_expr)
+ ls->pos += offset;
+ }
+ }
+
+ emit_label(s, label_body);
+ if (js_parse_statement(s))
+ return -1;
+
+ close_scopes(s, s->cur_func->scope_level, block_scope_level);
+
+ emit_label(s, label_cont);
+ if (is_for_of) {
+ if (is_async) {
+ /* call the next method */
+ /* stack: iter_obj next catch_offset */
+ emit_op(s, OP_dup3);
+ emit_op(s, OP_drop);
+ emit_op(s, OP_call_method);
+ emit_u16(s, 0);
+ /* get the result of the promise */
+ emit_op(s, OP_await);
+ /* unwrap the value and done values */
+ emit_op(s, OP_iterator_get_value_done);
+ } else {
+ emit_op(s, OP_for_of_next);
+ emit_u8(s, 0);
+ }
+ } else {
+ emit_op(s, OP_for_in_next);
+ }
+ /* on stack: enum_rec / enum_obj value bool */
+ emit_goto(s, OP_if_false, label_next);
+ /* drop the undefined value from for_xx_next */
+ emit_op(s, OP_drop);
+
+ emit_label(s, label_break);
+ if (is_for_of) {
+ /* close and drop enum_rec */
+ emit_op(s, OP_iterator_close);
+ } else {
+ emit_op(s, OP_drop);
+ }
+ pop_break_entry(s->cur_func);
+ pop_scope(s);
+ return 0;
+}
+
+static void set_eval_ret_undefined(JSParseState *s)
+{
+ if (s->cur_func->eval_ret_idx >= 0) {
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_put_loc);
+ emit_u16(s, s->cur_func->eval_ret_idx);
+ }
+}
+
+static warn_unused int js_parse_statement_or_decl(JSParseState *s,
+ int decl_mask)
+{
+ JSContext *ctx = s->ctx;
+ JSAtom label_name;
+ int tok;
+
+ /* specific label handling */
+ /* XXX: support multiple labels on loop statements */
+ label_name = JS_ATOM_NULL;
+ if (is_label(s)) {
+ BlockEnv *be;
+
+ label_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+
+ for (be = s->cur_func->top_break; be; be = be->prev) {
+ if (be->label_name == label_name) {
+ js_parse_error(s, "duplicate label name");
+ goto fail;
+ }
+ }
+
+ if (next_token(s))
+ goto fail;
+ if (js_parse_expect(s, ':'))
+ goto fail;
+ if (s->token.val != TOK_FOR
+ && s->token.val != TOK_DO
+ && s->token.val != TOK_WHILE) {
+ /* labelled regular statement */
+ int label_break, mask;
+ BlockEnv break_entry;
+
+ label_break = new_label(s);
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, -1, 0);
+ if (!(s->cur_func->js_mode & JS_MODE_STRICT) &&
+ (decl_mask & DECL_MASK_FUNC_WITH_LABEL)) {
+ mask = DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL;
+ } else {
+ mask = 0;
+ }
+ if (js_parse_statement_or_decl(s, mask))
+ goto fail;
+ emit_label(s, label_break);
+ pop_break_entry(s->cur_func);
+ goto done;
+ }
+ }
+
+ switch(tok = s->token.val) {
+ case '{':
+ if (js_parse_block(s))
+ goto fail;
+ break;
+ case TOK_RETURN:
+ if (s->cur_func->is_eval) {
+ js_parse_error(s, "return not in a function");
+ goto fail;
+ }
+ if (next_token(s))
+ goto fail;
+ if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) {
+ if (js_parse_expr(s))
+ goto fail;
+ emit_return(s, TRUE);
+ } else {
+ emit_return(s, FALSE);
+ }
+ if (js_parse_expect_semi(s))
+ goto fail;
+ break;
+ case TOK_THROW:
+ if (next_token(s))
+ goto fail;
+ if (s->got_lf) {
+ js_parse_error(s, "line terminator not allowed after throw");
+ goto fail;
+ }
+ if (js_parse_expr(s))
+ goto fail;
+ emit_op(s, OP_throw);
+ if (js_parse_expect_semi(s))
+ goto fail;
+ break;
+ case TOK_LET:
+ case TOK_CONST:
+ haslet:
+ if (!(decl_mask & DECL_MASK_OTHER)) {
+ js_parse_error(s, "lexical declarations can't appear in single-statement context");
+ goto fail;
+ }
+ /* fall thru */
+ case TOK_VAR:
+ if (next_token(s))
+ goto fail;
+ if (js_parse_var(s, TRUE, tok, FALSE))
+ goto fail;
+ if (js_parse_expect_semi(s))
+ goto fail;
+ break;
+ case TOK_IF:
+ {
+ int label1, label2, mask;
+ if (next_token(s))
+ goto fail;
+ /* create a new scope for `let f;if(1) function f(){}` */
+ push_scope(s);
+ set_eval_ret_undefined(s);
+ if (js_parse_expr_paren(s))
+ goto fail;
+ label1 = emit_goto(s, OP_if_false, -1);
+ if (s->cur_func->js_mode & JS_MODE_STRICT)
+ mask = 0;
+ else
+ mask = DECL_MASK_FUNC; /* Annex B.3.4 */
+
+ if (js_parse_statement_or_decl(s, mask))
+ goto fail;
+
+ if (s->token.val == TOK_ELSE) {
+ label2 = emit_goto(s, OP_goto, -1);
+ if (next_token(s))
+ goto fail;
+
+ emit_label(s, label1);
+ if (js_parse_statement_or_decl(s, mask))
+ goto fail;
+
+ label1 = label2;
+ }
+ emit_label(s, label1);
+ pop_scope(s);
+ }
+ break;
+ case TOK_WHILE:
+ {
+ int label_cont, label_break;
+ BlockEnv break_entry;
+
+ label_cont = new_label(s);
+ label_break = new_label(s);
+
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, label_cont, 0);
+
+ if (next_token(s))
+ goto fail;
+
+ set_eval_ret_undefined(s);
+
+ emit_label(s, label_cont);
+ if (js_parse_expr_paren(s))
+ goto fail;
+ emit_goto(s, OP_if_false, label_break);
+
+ if (js_parse_statement(s))
+ goto fail;
+ emit_goto(s, OP_goto, label_cont);
+
+ emit_label(s, label_break);
+
+ pop_break_entry(s->cur_func);
+ }
+ break;
+ case TOK_DO:
+ {
+ int label_cont, label_break, label1;
+ BlockEnv break_entry;
+
+ label_cont = new_label(s);
+ label_break = new_label(s);
+ label1 = new_label(s);
+
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, label_cont, 0);
+
+ if (next_token(s))
+ goto fail;
+
+ emit_label(s, label1);
+
+ set_eval_ret_undefined(s);
+
+ if (js_parse_statement(s))
+ goto fail;
+
+ emit_label(s, label_cont);
+ if (js_parse_expect(s, TOK_WHILE))
+ goto fail;
+ if (js_parse_expr_paren(s))
+ goto fail;
+ /* Insert semicolon if missing */
+ if (s->token.val == ';') {
+ if (next_token(s))
+ goto fail;
+ }
+ emit_goto(s, OP_if_true, label1);
+
+ emit_label(s, label_break);
+
+ pop_break_entry(s->cur_func);
+ }
+ break;
+ case TOK_FOR:
+ {
+ int label_cont, label_break, label_body, label_test;
+ int pos_cont, pos_body, block_scope_level;
+ BlockEnv break_entry;
+ int tok, bits;
+ BOOL is_async;
+
+ if (next_token(s))
+ goto fail;
+
+ set_eval_ret_undefined(s);
+ bits = 0;
+ is_async = FALSE;
+ if (s->token.val == '(') {
+ js_parse_skip_parens_token(s, &bits, FALSE);
+ } else if (s->token.val == TOK_AWAIT) {
+ if (!(s->cur_func->func_kind & JS_FUNC_ASYNC)) {
+ js_parse_error(s, "for await is only valid in asynchronous functions");
+ goto fail;
+ }
+ is_async = TRUE;
+ if (next_token(s))
+ goto fail;
+ }
+ if (js_parse_expect(s, '('))
+ goto fail;
+
+ if (!(bits & SKIP_HAS_SEMI)) {
+ /* parse for/in or for/of */
+ if (js_parse_for_in_of(s, label_name, is_async))
+ goto fail;
+ break;
+ }
+ block_scope_level = s->cur_func->scope_level;
+
+ /* create scope for the lexical variables declared in the initial,
+ test and increment expressions */
+ push_scope(s);
+ /* initial expression */
+ tok = s->token.val;
+ if (tok != ';') {
+ switch (is_let(s, DECL_MASK_OTHER)) {
+ case TRUE:
+ tok = TOK_LET;
+ break;
+ case FALSE:
+ break;
+ default:
+ goto fail;
+ }
+ if (tok == TOK_VAR || tok == TOK_LET || tok == TOK_CONST) {
+ if (next_token(s))
+ goto fail;
+ if (js_parse_var(s, FALSE, tok, FALSE))
+ goto fail;
+ } else {
+ if (js_parse_expr2(s, FALSE))
+ goto fail;
+ emit_op(s, OP_drop);
+ }
+
+ /* close the closures before the first iteration */
+ close_scopes(s, s->cur_func->scope_level, block_scope_level);
+ }
+ if (js_parse_expect(s, ';'))
+ goto fail;
+
+ label_test = new_label(s);
+ label_cont = new_label(s);
+ label_body = new_label(s);
+ label_break = new_label(s);
+
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, label_cont, 0);
+
+ /* test expression */
+ if (s->token.val == ';') {
+ /* no test expression */
+ label_test = label_body;
+ } else {
+ emit_label(s, label_test);
+ if (js_parse_expr(s))
+ goto fail;
+ emit_goto(s, OP_if_false, label_break);
+ }
+ if (js_parse_expect(s, ';'))
+ goto fail;
+
+ if (s->token.val == ')') {
+ /* no end expression */
+ break_entry.label_cont = label_cont = label_test;
+ pos_cont = 0; /* avoid warning */
+ } else {
+ /* skip the end expression */
+ emit_goto(s, OP_goto, label_body);
+
+ pos_cont = s->cur_func->byte_code.size;
+ emit_label(s, label_cont);
+ if (js_parse_expr(s))
+ goto fail;
+ emit_op(s, OP_drop);
+ if (label_test != label_body)
+ emit_goto(s, OP_goto, label_test);
+ }
+ if (js_parse_expect(s, ')'))
+ goto fail;
+
+ pos_body = s->cur_func->byte_code.size;
+ emit_label(s, label_body);
+ if (js_parse_statement(s))
+ goto fail;
+
+ /* close the closures before the next iteration */
+ /* XXX: check continue case */
+ close_scopes(s, s->cur_func->scope_level, block_scope_level);
+
+ if (OPTIMIZE && label_test != label_body && label_cont != label_test) {
+ /* move the increment code here */
+ DynBuf *bc = &s->cur_func->byte_code;
+ int chunk_size = pos_body - pos_cont;
+ int offset = bc->size - pos_cont;
+ int i;
+ dbuf_realloc(bc, bc->size + chunk_size);
+ dbuf_put(bc, bc->buf + pos_cont, chunk_size);
+ memset(bc->buf + pos_cont, OP_nop, chunk_size);
+ /* increment part ends with a goto */
+ s->cur_func->last_opcode_pos = bc->size - 5;
+ /* relocate labels */
+ for (i = label_cont; i < s->cur_func->label_count; i++) {
+ LabelSlot *ls = &s->cur_func->label_slots[i];
+ if (ls->pos >= pos_cont && ls->pos < pos_body)
+ ls->pos += offset;
+ }
+ } else {
+ emit_goto(s, OP_goto, label_cont);
+ }
+
+ emit_label(s, label_break);
+
+ pop_break_entry(s->cur_func);
+ pop_scope(s);
+ }
+ break;
+ case TOK_BREAK:
+ case TOK_CONTINUE:
+ {
+ int is_cont = s->token.val - TOK_BREAK;
+ int label;
+
+ if (next_token(s))
+ goto fail;
+ if (!s->got_lf && s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)
+ label = s->token.u.ident.atom;
+ else
+ label = JS_ATOM_NULL;
+ if (emit_break(s, label, is_cont))
+ goto fail;
+ if (label != JS_ATOM_NULL) {
+ if (next_token(s))
+ goto fail;
+ }
+ if (js_parse_expect_semi(s))
+ goto fail;
+ }
+ break;
+ case TOK_SWITCH:
+ {
+ int label_case, label_break, label1;
+ int default_label_pos;
+ BlockEnv break_entry;
+
+ if (next_token(s))
+ goto fail;
+
+ set_eval_ret_undefined(s);
+ if (js_parse_expr_paren(s))
+ goto fail;
+
+ push_scope(s);
+ label_break = new_label(s);
+ push_break_entry(s->cur_func, &break_entry,
+ label_name, label_break, -1, 1);
+
+ if (js_parse_expect(s, '{'))
+ goto fail;
+
+ default_label_pos = -1;
+ label_case = -1;
+ while (s->token.val != '}') {
+ if (s->token.val == TOK_CASE) {
+ label1 = -1;
+ if (label_case >= 0) {
+ /* skip the case if needed */
+ label1 = emit_goto(s, OP_goto, -1);
+ }
+ emit_label(s, label_case);
+ label_case = -1;
+ for (;;) {
+ /* parse a sequence of case clauses */
+ if (next_token(s))
+ goto fail;
+ emit_op(s, OP_dup);
+ if (js_parse_expr(s))
+ goto fail;
+ if (js_parse_expect(s, ':'))
+ goto fail;
+ emit_op(s, OP_strict_eq);
+ if (s->token.val == TOK_CASE) {
+ label1 = emit_goto(s, OP_if_true, label1);
+ } else {
+ label_case = emit_goto(s, OP_if_false, -1);
+ emit_label(s, label1);
+ break;
+ }
+ }
+ } else if (s->token.val == TOK_DEFAULT) {
+ if (next_token(s))
+ goto fail;
+ if (js_parse_expect(s, ':'))
+ goto fail;
+ if (default_label_pos >= 0) {
+ js_parse_error(s, "duplicate default");
+ goto fail;
+ }
+ if (label_case < 0) {
+ /* falling thru direct from switch expression */
+ label_case = emit_goto(s, OP_goto, -1);
+ }
+ /* Emit a dummy label opcode. Label will be patched after
+ the end of the switch body. Do not use emit_label(s, 0)
+ because it would clobber label 0 address, preventing
+ proper optimizer operation.
+ */
+ emit_op(s, OP_label);
+ emit_u32(s, 0);
+ default_label_pos = s->cur_func->byte_code.size - 4;
+ } else {
+ if (label_case < 0) {
+ /* falling thru direct from switch expression */
+ js_parse_error(s, "invalid switch statement");
+ goto fail;
+ }
+ if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
+ goto fail;
+ }
+ }
+ if (js_parse_expect(s, '}'))
+ goto fail;
+ if (default_label_pos >= 0) {
+ /* Ugly patch for the the `default` label, shameful and risky */
+ put_u32(s->cur_func->byte_code.buf + default_label_pos,
+ label_case);
+ s->cur_func->label_slots[label_case].pos = default_label_pos + 4;
+ } else {
+ emit_label(s, label_case);
+ }
+ emit_label(s, label_break);
+ emit_op(s, OP_drop); /* drop the switch expression */
+
+ pop_break_entry(s->cur_func);
+ pop_scope(s);
+ }
+ break;
+ case TOK_TRY:
+ {
+ int label_catch, label_catch2, label_finally, label_end;
+ JSAtom name;
+ BlockEnv block_env;
+
+ set_eval_ret_undefined(s);
+ if (next_token(s))
+ goto fail;
+ label_catch = new_label(s);
+ label_catch2 = new_label(s);
+ label_finally = new_label(s);
+ label_end = new_label(s);
+
+ emit_goto(s, OP_catch, label_catch);
+
+ push_break_entry(s->cur_func, &block_env,
+ JS_ATOM_NULL, -1, -1, 1);
+ block_env.label_finally = label_finally;
+
+ if (js_parse_block(s))
+ goto fail;
+
+ pop_break_entry(s->cur_func);
+
+ if (js_is_live_code(s)) {
+ /* drop the catch offset */
+ emit_op(s, OP_drop);
+ /* must push dummy value to keep same stack size */
+ emit_op(s, OP_undefined);
+ emit_goto(s, OP_gosub, label_finally);
+ emit_op(s, OP_drop);
+
+ emit_goto(s, OP_goto, label_end);
+ }
+
+ if (s->token.val == TOK_CATCH) {
+ if (next_token(s))
+ goto fail;
+
+ push_scope(s); /* catch variable */
+ emit_label(s, label_catch);
+
+ if (s->token.val == '{') {
+ /* support optional-catch-binding feature */
+ emit_op(s, OP_drop); /* pop the exception object */
+ } else {
+ if (js_parse_expect(s, '('))
+ goto fail;
+ if (!(s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved)) {
+ if (s->token.val == '[' || s->token.val == '{') {
+ /* XXX: TOK_LET is not completely correct */
+ if (js_parse_destructuring_element(s, TOK_LET, 0, TRUE, -1, TRUE) < 0)
+ goto fail;
+ } else {
+ js_parse_error(s, "identifier expected");
+ goto fail;
+ }
+ } else {
+ name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s)
+ || js_define_var(s, name, TOK_CATCH) < 0) {
+ JS_FreeAtom(ctx, name);
+ goto fail;
+ }
+ /* store the exception value in the catch variable */
+ emit_op(s, OP_scope_put_var);
+ emit_u32(s, name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+ if (js_parse_expect(s, ')'))
+ goto fail;
+ }
+ /* XXX: should keep the address to nop it out if there is no finally block */
+ emit_goto(s, OP_catch, label_catch2);
+
+ push_scope(s); /* catch block */
+ push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
+ -1, -1, 1);
+ block_env.label_finally = label_finally;
+
+ if (js_parse_block(s))
+ goto fail;
+
+ pop_break_entry(s->cur_func);
+ pop_scope(s); /* catch block */
+ pop_scope(s); /* catch variable */
+
+ if (js_is_live_code(s)) {
+ /* drop the catch2 offset */
+ emit_op(s, OP_drop);
+ /* XXX: should keep the address to nop it out if there is no finally block */
+ /* must push dummy value to keep same stack size */
+ emit_op(s, OP_undefined);
+ emit_goto(s, OP_gosub, label_finally);
+ emit_op(s, OP_drop);
+ emit_goto(s, OP_goto, label_end);
+ }
+ /* catch exceptions thrown in the catch block to execute the
+ * finally clause and rethrow the exception */
+ emit_label(s, label_catch2);
+ /* catch value is at TOS, no need to push undefined */
+ emit_goto(s, OP_gosub, label_finally);
+ emit_op(s, OP_throw);
+
+ } else if (s->token.val == TOK_FINALLY) {
+ /* finally without catch : execute the finally clause
+ * and rethrow the exception */
+ emit_label(s, label_catch);
+ /* catch value is at TOS, no need to push undefined */
+ emit_goto(s, OP_gosub, label_finally);
+ emit_op(s, OP_throw);
+ } else {
+ js_parse_error(s, "expecting catch or finally");
+ goto fail;
+ }
+ emit_label(s, label_finally);
+ if (s->token.val == TOK_FINALLY) {
+ int saved_eval_ret_idx = 0; /* avoid warning */
+
+ if (next_token(s))
+ goto fail;
+ /* on the stack: ret_value gosub_ret_value */
+ push_break_entry(s->cur_func, &block_env, JS_ATOM_NULL,
+ -1, -1, 2);
+
+ if (s->cur_func->eval_ret_idx >= 0) {
+ /* 'finally' updates eval_ret only if not a normal
+ termination */
+ saved_eval_ret_idx =
+ add_var(s->ctx, s->cur_func, JS_ATOM__ret_);
+ if (saved_eval_ret_idx < 0)
+ goto fail;
+ emit_op(s, OP_get_loc);
+ emit_u16(s, s->cur_func->eval_ret_idx);
+ emit_op(s, OP_put_loc);
+ emit_u16(s, saved_eval_ret_idx);
+ set_eval_ret_undefined(s);
+ }
+
+ if (js_parse_block(s))
+ goto fail;
+
+ if (s->cur_func->eval_ret_idx >= 0) {
+ emit_op(s, OP_get_loc);
+ emit_u16(s, saved_eval_ret_idx);
+ emit_op(s, OP_put_loc);
+ emit_u16(s, s->cur_func->eval_ret_idx);
+ }
+ pop_break_entry(s->cur_func);
+ }
+ emit_op(s, OP_ret);
+ emit_label(s, label_end);
+ }
+ break;
+ case ';':
+ /* empty statement */
+ if (next_token(s))
+ goto fail;
+ break;
+ case TOK_WITH:
+ if (s->cur_func->js_mode & JS_MODE_STRICT) {
+ js_parse_error(s, "invalid keyword: with");
+ goto fail;
+ } else {
+ int with_idx;
+
+ if (next_token(s))
+ goto fail;
+
+ if (js_parse_expr_paren(s))
+ goto fail;
+
+ push_scope(s);
+ with_idx = define_var(s, s->cur_func, JS_ATOM__with_,
+ JS_VAR_DEF_WITH);
+ if (with_idx < 0)
+ goto fail;
+ emit_op(s, OP_to_object);
+ emit_op(s, OP_put_loc);
+ emit_u16(s, with_idx);
+
+ set_eval_ret_undefined(s);
+ if (js_parse_statement(s))
+ goto fail;
+
+ /* Popping scope drops lexical context for the with object variable */
+ pop_scope(s);
+ }
+ break;
+ case TOK_FUNCTION:
+ /* ES6 Annex B.3.2 and B.3.3 semantics */
+ if (!(decl_mask & DECL_MASK_FUNC))
+ goto func_decl_error;
+ if (!(decl_mask & DECL_MASK_OTHER) && peek_token(s, FALSE) == '*')
+ goto func_decl_error;
+ goto parse_func_var;
+ case TOK_IDENT:
+ if (s->token.u.ident.is_reserved) {
+ js_parse_error_reserved_identifier(s);
+ goto fail;
+ }
+ /* Determine if `let` introduces a Declaration or an ExpressionStatement */
+ switch (is_let(s, decl_mask)) {
+ case TRUE:
+ tok = TOK_LET;
+ goto haslet;
+ case FALSE:
+ break;
+ default:
+ goto fail;
+ }
+ if (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) == TOK_FUNCTION) {
+ if (!(decl_mask & DECL_MASK_OTHER)) {
+ func_decl_error:
+ js_parse_error(s, "function declarations can't appear in single-statement context");
+ goto fail;
+ }
+ parse_func_var:
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_VAR,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num))
+ goto fail;
+ break;
+ }
+ goto hasexpr;
+
+ case TOK_CLASS:
+ if (!(decl_mask & DECL_MASK_OTHER)) {
+ js_parse_error(s, "class declarations can't appear in single-statement context");
+ goto fail;
+ }
+ if (js_parse_class(s, FALSE, JS_PARSE_EXPORT_NONE))
+ return -1;
+ break;
+
+ case TOK_DEBUGGER:
+ /* currently no debugger, so just skip the keyword */
+ if (next_token(s))
+ goto fail;
+ if (js_parse_expect_semi(s))
+ goto fail;
+ break;
+
+ case TOK_ENUM:
+ case TOK_EXPORT:
+ case TOK_EXTENDS:
+ js_unsupported_keyword(s, s->token.u.ident.atom);
+ goto fail;
+
+ default:
+ hasexpr:
+ if (js_parse_expr(s))
+ goto fail;
+ if (s->cur_func->eval_ret_idx >= 0) {
+ /* store the expression value so that it can be returned
+ by eval() */
+ emit_op(s, OP_put_loc);
+ emit_u16(s, s->cur_func->eval_ret_idx);
+ } else {
+ emit_op(s, OP_drop); /* drop the result */
+ }
+ if (js_parse_expect_semi(s))
+ goto fail;
+ break;
+ }
+done:
+ JS_FreeAtom(ctx, label_name);
+ return 0;
+fail:
+ JS_FreeAtom(ctx, label_name);
+ return -1;
+}
+
+/* 'name' is freed */
+static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name)
+{
+ JSModuleDef *m;
+ m = js_mallocz(ctx, sizeof(*m));
+ if (!m) {
+ JS_FreeAtom(ctx, name);
+ return NULL;
+ }
+ m->header.ref_count = 1;
+ m->module_name = name;
+ m->module_ns = JS_UNDEFINED;
+ m->func_obj = JS_UNDEFINED;
+ m->eval_exception = JS_UNDEFINED;
+ m->meta_obj = JS_UNDEFINED;
+ list_add_tail(&m->link, &ctx->loaded_modules);
+ return m;
+}
+
+static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
+ JS_MarkFunc *mark_func)
+{
+ int i;
+
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL &&
+ me->u.local.var_ref) {
+ mark_func(rt, &me->u.local.var_ref->header);
+ }
+ }
+
+ JS_MarkValue(rt, m->module_ns, mark_func);
+ JS_MarkValue(rt, m->func_obj, mark_func);
+ JS_MarkValue(rt, m->eval_exception, mark_func);
+ JS_MarkValue(rt, m->meta_obj, mark_func);
+}
+
+static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
+{
+ int i;
+
+ JS_FreeAtom(ctx, m->module_name);
+
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ JS_FreeAtom(ctx, rme->module_name);
+ }
+ js_free(ctx, m->req_module_entries);
+
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL)
+ free_var_ref(ctx->rt, me->u.local.var_ref);
+ JS_FreeAtom(ctx, me->export_name);
+ JS_FreeAtom(ctx, me->local_name);
+ }
+ js_free(ctx, m->export_entries);
+
+ js_free(ctx, m->star_export_entries);
+
+ for(i = 0; i < m->import_entries_count; i++) {
+ JSImportEntry *mi = &m->import_entries[i];
+ JS_FreeAtom(ctx, mi->import_name);
+ }
+ js_free(ctx, m->import_entries);
+
+ JS_FreeValue(ctx, m->module_ns);
+ JS_FreeValue(ctx, m->func_obj);
+ JS_FreeValue(ctx, m->eval_exception);
+ JS_FreeValue(ctx, m->meta_obj);
+ list_del(&m->link);
+ js_free(ctx, m);
+}
+
+static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
+ JSAtom module_name)
+{
+ JSReqModuleEntry *rme;
+ int i;
+
+ /* no need to add the module request if it is already present */
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ rme = &m->req_module_entries[i];
+ if (rme->module_name == module_name)
+ return i;
+ }
+
+ if (js_resize_array(ctx, (void **)&m->req_module_entries,
+ sizeof(JSReqModuleEntry),
+ &m->req_module_entries_size,
+ m->req_module_entries_count + 1))
+ return -1;
+ rme = &m->req_module_entries[m->req_module_entries_count++];
+ rme->module_name = JS_DupAtom(ctx, module_name);
+ rme->module = NULL;
+ return i;
+}
+
+static JSExportEntry *find_export_entry(JSContext *ctx, JSModuleDef *m,
+ JSAtom export_name)
+{
+ JSExportEntry *me;
+ int i;
+ for(i = 0; i < m->export_entries_count; i++) {
+ me = &m->export_entries[i];
+ if (me->export_name == export_name)
+ return me;
+ }
+ return NULL;
+}
+
+static JSExportEntry *add_export_entry2(JSContext *ctx,
+ JSParseState *s, JSModuleDef *m,
+ JSAtom local_name, JSAtom export_name,
+ JSExportTypeEnum export_type)
+{
+ JSExportEntry *me;
+
+ if (find_export_entry(ctx, m, export_name)) {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ if (s) {
+ js_parse_error(s, "duplicate exported name '%s'",
+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name));
+ } else {
+ JS_ThrowSyntaxErrorAtom(ctx, "duplicate exported name '%s'", export_name);
+ }
+ return NULL;
+ }
+
+ if (js_resize_array(ctx, (void **)&m->export_entries,
+ sizeof(JSExportEntry),
+ &m->export_entries_size,
+ m->export_entries_count + 1))
+ return NULL;
+ me = &m->export_entries[m->export_entries_count++];
+ memset(me, 0, sizeof(*me));
+ me->local_name = JS_DupAtom(ctx, local_name);
+ me->export_name = JS_DupAtom(ctx, export_name);
+ me->export_type = export_type;
+ return me;
+}
+
+static JSExportEntry *add_export_entry(JSParseState *s, JSModuleDef *m,
+ JSAtom local_name, JSAtom export_name,
+ JSExportTypeEnum export_type)
+{
+ return add_export_entry2(s->ctx, s, m, local_name, export_name,
+ export_type);
+}
+
+static int add_star_export_entry(JSContext *ctx, JSModuleDef *m,
+ int req_module_idx)
+{
+ JSStarExportEntry *se;
+
+ if (js_resize_array(ctx, (void **)&m->star_export_entries,
+ sizeof(JSStarExportEntry),
+ &m->star_export_entries_size,
+ m->star_export_entries_count + 1))
+ return -1;
+ se = &m->star_export_entries[m->star_export_entries_count++];
+ se->req_module_idx = req_module_idx;
+ return 0;
+}
+
+/* create a C module */
+JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
+ JSModuleInitFunc *func)
+{
+ JSModuleDef *m;
+ JSAtom name;
+ name = JS_NewAtom(ctx, name_str);
+ if (name == JS_ATOM_NULL)
+ return NULL;
+ m = js_new_module_def(ctx, name);
+ m->init_func = func;
+ return m;
+}
+
+int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name)
+{
+ JSExportEntry *me;
+ JSAtom name;
+ name = JS_NewAtom(ctx, export_name);
+ if (name == JS_ATOM_NULL)
+ return -1;
+ me = add_export_entry2(ctx, NULL, m, JS_ATOM_NULL, name,
+ JS_EXPORT_TYPE_LOCAL);
+ JS_FreeAtom(ctx, name);
+ if (!me)
+ return -1;
+ else
+ return 0;
+}
+
+int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
+ JSValue val)
+{
+ JSExportEntry *me;
+ JSAtom name;
+ name = JS_NewAtom(ctx, export_name);
+ if (name == JS_ATOM_NULL)
+ goto fail;
+ me = find_export_entry(ctx, m, name);
+ JS_FreeAtom(ctx, name);
+ if (!me)
+ goto fail;
+ set_value(ctx, me->u.local.var_ref->pvalue, val);
+ return 0;
+ fail:
+ JS_FreeValue(ctx, val);
+ return -1;
+}
+
+void JS_SetModuleLoaderFunc(JSRuntime *rt,
+ JSModuleNormalizeFunc *module_normalize,
+ JSModuleLoaderFunc *module_loader, void *opaque)
+{
+ rt->module_normalize_func = module_normalize;
+ rt->module_loader_func = module_loader;
+ rt->module_loader_opaque = opaque;
+}
+
+/* default module filename normalizer */
+static char *js_default_module_normalize_name(JSContext *ctx,
+ const char *base_name,
+ const char *name)
+{
+ char *filename, *p;
+ const char *r;
+ int len;
+
+ if (name[0] != '.') {
+ /* if no initial dot, the module name is not modified */
+ return js_strdup(ctx, name);
+ }
+
+ p = strrchr(base_name, '/');
+ if (p)
+ len = p - base_name;
+ else
+ len = 0;
+
+ filename = js_malloc(ctx, len + strlen(name) + 1 + 1);
+ if (!filename)
+ return NULL;
+ memcpy(filename, base_name, len);
+ filename[len] = '\0';
+
+ /* we only normalize the leading '..' or '.' */
+ r = name;
+ for(;;) {
+ if (r[0] == '.' && r[1] == '/') {
+ r += 2;
+ } else if (r[0] == '.' && r[1] == '.' && r[2] == '/') {
+ /* remove the last path element of filename, except if "."
+ or ".." */
+ if (filename[0] == '\0')
+ break;
+ p = strrchr(filename, '/');
+ if (!p)
+ p = filename;
+ else
+ p++;
+ if (!strcmp(p, ".") || !strcmp(p, ".."))
+ break;
+ if (p > filename)
+ p--;
+ *p = '\0';
+ r += 3;
+ } else {
+ break;
+ }
+ }
+ if (filename[0] != '\0')
+ strcat(filename, "/");
+ strcat(filename, r);
+ // printf("normalize: %s %s -> %s\n", base_name, name, filename);
+ return filename;
+}
+
+static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name)
+{
+ struct list_head *el;
+ JSModuleDef *m;
+
+ /* first look at the loaded modules */
+ list_for_each(el, &ctx->loaded_modules) {
+ m = list_entry(el, JSModuleDef, link);
+ if (m->module_name == name)
+ return m;
+ }
+ return NULL;
+}
+
+/* return NULL in case of exception (e.g. module could not be loaded) */
+static JSModuleDef *js_host_resolve_imported_module(JSContext *ctx,
+ const char *base_cname,
+ const char *cname1)
+{
+ JSRuntime *rt = ctx->rt;
+ JSModuleDef *m;
+ char *cname;
+ JSAtom module_name;
+
+ if (!rt->module_normalize_func) {
+ cname = js_default_module_normalize_name(ctx, base_cname, cname1);
+ } else {
+ cname = rt->module_normalize_func(ctx, base_cname, cname1,
+ rt->module_loader_opaque);
+ }
+ if (!cname)
+ return NULL;
+
+ module_name = JS_NewAtom(ctx, cname);
+ if (module_name == JS_ATOM_NULL) {
+ js_free(ctx, cname);
+ return NULL;
+ }
+
+ /* first look at the loaded modules */
+ m = js_find_loaded_module(ctx, module_name);
+ if (m) {
+ js_free(ctx, cname);
+ JS_FreeAtom(ctx, module_name);
+ return m;
+ }
+
+ JS_FreeAtom(ctx, module_name);
+
+ /* load the module */
+ if (!rt->module_loader_func) {
+ /* XXX: use a syntax error ? */
+ JS_ThrowReferenceError(ctx, "could not load module '%s'",
+ cname);
+ js_free(ctx, cname);
+ return NULL;
+ }
+
+ m = rt->module_loader_func(ctx, cname, rt->module_loader_opaque);
+ js_free(ctx, cname);
+ return m;
+}
+
+static JSModuleDef *js_host_resolve_imported_module_atom(JSContext *ctx,
+ JSAtom base_module_name,
+ JSAtom module_name1)
+{
+ const char *base_cname, *cname;
+ JSModuleDef *m;
+
+ base_cname = JS_AtomToCString(ctx, base_module_name);
+ if (!base_cname)
+ return NULL;
+ cname = JS_AtomToCString(ctx, module_name1);
+ if (!cname) {
+ JS_FreeCString(ctx, base_cname);
+ return NULL;
+ }
+ m = js_host_resolve_imported_module(ctx, base_cname, cname);
+ JS_FreeCString(ctx, base_cname);
+ JS_FreeCString(ctx, cname);
+ return m;
+}
+
+typedef struct JSResolveEntry {
+ JSModuleDef *module;
+ JSAtom name;
+} JSResolveEntry;
+
+typedef struct JSResolveState {
+ JSResolveEntry *array;
+ int size;
+ int count;
+} JSResolveState;
+
+static int find_resolve_entry(JSResolveState *s,
+ JSModuleDef *m, JSAtom name)
+{
+ int i;
+ for(i = 0; i < s->count; i++) {
+ JSResolveEntry *re = &s->array[i];
+ if (re->module == m && re->name == name)
+ return i;
+ }
+ return -1;
+}
+
+static int add_resolve_entry(JSContext *ctx, JSResolveState *s,
+ JSModuleDef *m, JSAtom name)
+{
+ JSResolveEntry *re;
+
+ if (js_resize_array(ctx, (void **)&s->array,
+ sizeof(JSResolveEntry),
+ &s->size, s->count + 1))
+ return -1;
+ re = &s->array[s->count++];
+ re->module = m;
+ re->name = JS_DupAtom(ctx, name);
+ return 0;
+}
+
+typedef enum JSResolveResultEnum {
+ JS_RESOLVE_RES_EXCEPTION = -1, /* memory alloc error */
+ JS_RESOLVE_RES_FOUND = 0,
+ JS_RESOLVE_RES_NOT_FOUND,
+ JS_RESOLVE_RES_CIRCULAR,
+ JS_RESOLVE_RES_AMBIGUOUS,
+} JSResolveResultEnum;
+
+static JSResolveResultEnum js_resolve_export1(JSContext *ctx,
+ JSModuleDef **pmodule,
+ JSExportEntry **pme,
+ JSModuleDef *m,
+ JSAtom export_name,
+ JSResolveState *s)
+{
+ JSExportEntry *me;
+
+ *pmodule = NULL;
+ *pme = NULL;
+ if (find_resolve_entry(s, m, export_name) >= 0)
+ return JS_RESOLVE_RES_CIRCULAR;
+ if (add_resolve_entry(ctx, s, m, export_name) < 0)
+ return JS_RESOLVE_RES_EXCEPTION;
+ me = find_export_entry(ctx, m, export_name);
+ if (me) {
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ /* local export */
+ *pmodule = m;
+ *pme = me;
+ return JS_RESOLVE_RES_FOUND;
+ } else {
+ /* indirect export */
+ JSModuleDef *m1;
+ m1 = m->req_module_entries[me->u.req_module_idx].module;
+ if (me->local_name == JS_ATOM__star_) {
+ /* export ns from */
+ *pmodule = m;
+ *pme = me;
+ return JS_RESOLVE_RES_FOUND;
+ } else {
+ return js_resolve_export1(ctx, pmodule, pme, m1,
+ me->local_name, s);
+ }
+ }
+ } else {
+ if (export_name != JS_ATOM_default) {
+ /* not found in direct or indirect exports: try star exports */
+ int i;
+
+ for(i = 0; i < m->star_export_entries_count; i++) {
+ JSStarExportEntry *se = &m->star_export_entries[i];
+ JSModuleDef *m1, *res_m;
+ JSExportEntry *res_me;
+ JSResolveResultEnum ret;
+
+ m1 = m->req_module_entries[se->req_module_idx].module;
+ ret = js_resolve_export1(ctx, &res_m, &res_me, m1,
+ export_name, s);
+ if (ret == JS_RESOLVE_RES_AMBIGUOUS ||
+ ret == JS_RESOLVE_RES_EXCEPTION) {
+ return ret;
+ } else if (ret == JS_RESOLVE_RES_FOUND) {
+ if (*pme != NULL) {
+ if (*pmodule != res_m ||
+ res_me->local_name != (*pme)->local_name) {
+ *pmodule = NULL;
+ *pme = NULL;
+ return JS_RESOLVE_RES_AMBIGUOUS;
+ }
+ } else {
+ *pmodule = res_m;
+ *pme = res_me;
+ }
+ }
+ }
+ if (*pme != NULL)
+ return JS_RESOLVE_RES_FOUND;
+ }
+ return JS_RESOLVE_RES_NOT_FOUND;
+ }
+}
+
+/* If the return value is JS_RESOLVE_RES_FOUND, return the module
+ (*pmodule) and the corresponding local export entry
+ (*pme). Otherwise return (NULL, NULL) */
+static JSResolveResultEnum js_resolve_export(JSContext *ctx,
+ JSModuleDef **pmodule,
+ JSExportEntry **pme,
+ JSModuleDef *m,
+ JSAtom export_name)
+{
+ JSResolveState ss, *s = &ss;
+ int i;
+ JSResolveResultEnum ret;
+
+ s->array = NULL;
+ s->size = 0;
+ s->count = 0;
+
+ ret = js_resolve_export1(ctx, pmodule, pme, m, export_name, s);
+
+ for(i = 0; i < s->count; i++)
+ JS_FreeAtom(ctx, s->array[i].name);
+ js_free(ctx, s->array);
+
+ return ret;
+}
+
+static void js_resolve_export_throw_error(JSContext *ctx,
+ JSResolveResultEnum res,
+ JSModuleDef *m, JSAtom export_name)
+{
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ char buf2[ATOM_GET_STR_BUF_SIZE];
+ switch(res) {
+ case JS_RESOLVE_RES_EXCEPTION:
+ break;
+ default:
+ case JS_RESOLVE_RES_NOT_FOUND:
+ JS_ThrowSyntaxError(ctx, "Could not find export '%s' in module '%s'",
+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
+ JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
+ break;
+ case JS_RESOLVE_RES_CIRCULAR:
+ JS_ThrowSyntaxError(ctx, "circular reference when looking for export '%s' in module '%s'",
+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
+ JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
+ break;
+ case JS_RESOLVE_RES_AMBIGUOUS:
+ JS_ThrowSyntaxError(ctx, "export '%s' in module '%s' is ambiguous",
+ JS_AtomGetStr(ctx, buf1, sizeof(buf1), export_name),
+ JS_AtomGetStr(ctx, buf2, sizeof(buf2), m->module_name));
+ break;
+ }
+}
+
+
+typedef enum {
+ EXPORTED_NAME_AMBIGUOUS,
+ EXPORTED_NAME_NORMAL,
+ EXPORTED_NAME_NS,
+} ExportedNameEntryEnum;
+
+typedef struct ExportedNameEntry {
+ JSAtom export_name;
+ ExportedNameEntryEnum export_type;
+ union {
+ JSExportEntry *me; /* using when the list is built */
+ JSVarRef *var_ref; /* EXPORTED_NAME_NORMAL */
+ JSModuleDef *module; /* for EXPORTED_NAME_NS */
+ } u;
+} ExportedNameEntry;
+
+typedef struct GetExportNamesState {
+ JSModuleDef **modules;
+ int modules_size;
+ int modules_count;
+
+ ExportedNameEntry *exported_names;
+ int exported_names_size;
+ int exported_names_count;
+} GetExportNamesState;
+
+static int find_exported_name(GetExportNamesState *s, JSAtom name)
+{
+ int i;
+ for(i = 0; i < s->exported_names_count; i++) {
+ if (s->exported_names[i].export_name == name)
+ return i;
+ }
+ return -1;
+}
+
+static warn_unused int get_exported_names(JSContext *ctx,
+ GetExportNamesState *s,
+ JSModuleDef *m, BOOL from_star)
+{
+ ExportedNameEntry *en;
+ int i, j;
+
+ /* check circular reference */
+ for(i = 0; i < s->modules_count; i++) {
+ if (s->modules[i] == m)
+ return 0;
+ }
+ if (js_resize_array(ctx, (void **)&s->modules, sizeof(s->modules[0]),
+ &s->modules_size, s->modules_count + 1))
+ return -1;
+ s->modules[s->modules_count++] = m;
+
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (from_star && me->export_name == JS_ATOM_default)
+ continue;
+ j = find_exported_name(s, me->export_name);
+ if (j < 0) {
+ if (js_resize_array(ctx, (void **)&s->exported_names, sizeof(s->exported_names[0]),
+ &s->exported_names_size,
+ s->exported_names_count + 1))
+ return -1;
+ en = &s->exported_names[s->exported_names_count++];
+ en->export_name = me->export_name;
+ /* avoid a second lookup for simple module exports */
+ if (from_star || me->export_type != JS_EXPORT_TYPE_LOCAL)
+ en->u.me = NULL;
+ else
+ en->u.me = me;
+ } else {
+ en = &s->exported_names[j];
+ en->u.me = NULL;
+ }
+ }
+ for(i = 0; i < m->star_export_entries_count; i++) {
+ JSStarExportEntry *se = &m->star_export_entries[i];
+ JSModuleDef *m1;
+ m1 = m->req_module_entries[se->req_module_idx].module;
+ if (get_exported_names(ctx, s, m1, TRUE))
+ return -1;
+ }
+ return 0;
+}
+
+/* Unfortunately, the spec gives a different behavior from GetOwnProperty ! */
+static int js_module_ns_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
+{
+ return (find_own_property1(JS_VALUE_GET_OBJ(obj), atom) != NULL);
+}
+
+static const JSClassExoticMethods js_module_ns_exotic_methods = {
+ .has_property = js_module_ns_has,
+};
+
+static int exported_names_cmp(const void *p1, const void *p2, void *opaque)
+{
+ JSContext *ctx = opaque;
+ const ExportedNameEntry *me1 = p1;
+ const ExportedNameEntry *me2 = p2;
+ JSValue str1, str2;
+ int ret;
+
+ /* XXX: should avoid allocation memory in atom comparison */
+ str1 = JS_AtomToString(ctx, me1->export_name);
+ str2 = JS_AtomToString(ctx, me2->export_name);
+ if (JS_IsException(str1) || JS_IsException(str2)) {
+ /* XXX: raise an error ? */
+ ret = 0;
+ } else {
+ ret = js_string_compare(ctx, JS_VALUE_GET_STRING(str1),
+ JS_VALUE_GET_STRING(str2));
+ }
+ JS_FreeValue(ctx, str1);
+ JS_FreeValue(ctx, str2);
+ return ret;
+}
+
+static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m);
+
+static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom,
+ void *opaque)
+{
+ JSModuleDef *m = opaque;
+ return js_get_module_ns(ctx, m);
+}
+
+static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
+{
+ JSValue obj;
+ JSObject *p;
+ GetExportNamesState s_s, *s = &s_s;
+ int i, ret;
+ JSProperty *pr;
+
+ obj = JS_NewObjectClass(ctx, JS_CLASS_MODULE_NS);
+ if (JS_IsException(obj))
+ return obj;
+ p = JS_VALUE_GET_OBJ(obj);
+
+ memset(s, 0, sizeof(*s));
+ ret = get_exported_names(ctx, s, m, FALSE);
+ js_free(ctx, s->modules);
+ if (ret)
+ goto fail;
+
+ /* Resolve the exported names. The ambiguous exports are removed */
+ for(i = 0; i < s->exported_names_count; i++) {
+ ExportedNameEntry *en = &s->exported_names[i];
+ JSResolveResultEnum res;
+ JSExportEntry *res_me;
+ JSModuleDef *res_m;
+
+ if (en->u.me) {
+ res_me = en->u.me; /* fast case: no resolution needed */
+ res_m = m;
+ res = JS_RESOLVE_RES_FOUND;
+ } else {
+ res = js_resolve_export(ctx, &res_m, &res_me, m,
+ en->export_name);
+ }
+ if (res != JS_RESOLVE_RES_FOUND) {
+ if (res != JS_RESOLVE_RES_AMBIGUOUS) {
+ js_resolve_export_throw_error(ctx, res, m, en->export_name);
+ goto fail;
+ }
+ en->export_type = EXPORTED_NAME_AMBIGUOUS;
+ } else {
+ if (res_me->local_name == JS_ATOM__star_) {
+ en->export_type = EXPORTED_NAME_NS;
+ en->u.module = res_m->req_module_entries[res_me->u.req_module_idx].module;
+ } else {
+ en->export_type = EXPORTED_NAME_NORMAL;
+ if (res_me->u.local.var_ref) {
+ en->u.var_ref = res_me->u.local.var_ref;
+ } else {
+ JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
+ en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
+ }
+ }
+ }
+ }
+
+ /* sort the exported names */
+ rqsort(s->exported_names, s->exported_names_count,
+ sizeof(s->exported_names[0]), exported_names_cmp, ctx);
+
+ for(i = 0; i < s->exported_names_count; i++) {
+ ExportedNameEntry *en = &s->exported_names[i];
+ switch(en->export_type) {
+ case EXPORTED_NAME_NORMAL:
+ {
+ JSVarRef *var_ref = en->u.var_ref;
+ pr = add_property(ctx, p, en->export_name,
+ JS_PROP_ENUMERABLE | JS_PROP_WRITABLE |
+ JS_PROP_VARREF);
+ if (!pr)
+ goto fail;
+ var_ref->header.ref_count++;
+ pr->u.var_ref = var_ref;
+ }
+ break;
+ case EXPORTED_NAME_NS:
+ /* the exported namespace must be created on demand */
+ if (JS_DefineAutoInitProperty(ctx, obj,
+ en->export_name,
+ JS_AUTOINIT_ID_MODULE_NS,
+ en->u.module, JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
+ goto fail;
+ break;
+ default:
+ break;
+ }
+ }
+
+ js_free(ctx, s->exported_names);
+
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_toStringTag,
+ JS_AtomToString(ctx, JS_ATOM_Module),
+ 0);
+
+ p->extensible = FALSE;
+ return obj;
+ fail:
+ js_free(ctx, s->exported_names);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m)
+{
+ if (JS_IsUndefined(m->module_ns)) {
+ JSValue val;
+ val = js_build_module_ns(ctx, m);
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+ m->module_ns = val;
+ }
+ return JS_DupValue(ctx, m->module_ns);
+}
+
+/* Load all the required modules for module 'm' */
+static int js_resolve_module(JSContext *ctx, JSModuleDef *m)
+{
+ int i;
+ JSModuleDef *m1;
+
+ if (m->resolved)
+ return 0;
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ printf("resolving module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
+ }
+#endif
+ m->resolved = TRUE;
+ /* resolve each requested module */
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ m1 = js_host_resolve_imported_module_atom(ctx, m->module_name,
+ rme->module_name);
+ if (!m1)
+ return -1;
+ rme->module = m1;
+ /* already done in js_host_resolve_imported_module() except if
+ the module was loaded with JS_EvalBinary() */
+ if (js_resolve_module(ctx, m1) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+static JSVarRef *js_create_module_var(JSContext *ctx, BOOL is_lexical)
+{
+ JSVarRef *var_ref;
+ var_ref = js_malloc(ctx, sizeof(JSVarRef));
+ if (!var_ref)
+ return NULL;
+ var_ref->header.ref_count = 1;
+ if (is_lexical)
+ var_ref->value = JS_UNINITIALIZED;
+ else
+ var_ref->value = JS_UNDEFINED;
+ var_ref->pvalue = &var_ref->value;
+ var_ref->is_detached = TRUE;
+ add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF);
+ return var_ref;
+}
+
+/* Create the <eval> function associated with the module */
+static int js_create_module_bytecode_function(JSContext *ctx, JSModuleDef *m)
+{
+ JSFunctionBytecode *b;
+ int i;
+ JSVarRef **var_refs;
+ JSValue func_obj, bfunc;
+ JSObject *p;
+
+ bfunc = m->func_obj;
+ func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
+ JS_CLASS_BYTECODE_FUNCTION);
+
+ if (JS_IsException(func_obj))
+ return -1;
+ b = JS_VALUE_GET_PTR(bfunc);
+
+ p = JS_VALUE_GET_OBJ(func_obj);
+ p->u.func.function_bytecode = b;
+ b->header.ref_count++;
+ p->u.func.home_object = NULL;
+ p->u.func.var_refs = NULL;
+ if (b->closure_var_count) {
+ var_refs = js_mallocz(ctx, sizeof(var_refs[0]) * b->closure_var_count);
+ if (!var_refs)
+ goto fail;
+ p->u.func.var_refs = var_refs;
+
+ /* create the global variables. The other variables are
+ imported from other modules */
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ JSVarRef *var_ref;
+ if (cv->is_local) {
+ var_ref = js_create_module_var(ctx, cv->is_lexical);
+ if (!var_ref)
+ goto fail;
+#ifdef DUMP_MODULE_RESOLVE
+ printf("local %d: %p\n", i, var_ref);
+#endif
+ var_refs[i] = var_ref;
+ }
+ }
+ }
+ m->func_obj = func_obj;
+ JS_FreeValue(ctx, bfunc);
+ return 0;
+ fail:
+ JS_FreeValue(ctx, func_obj);
+ return -1;
+}
+
+/* must be done before js_link_module() because of cyclic references */
+static int js_create_module_function(JSContext *ctx, JSModuleDef *m)
+{
+ BOOL is_c_module;
+ int i;
+ JSVarRef *var_ref;
+
+ if (m->func_created)
+ return 0;
+
+ is_c_module = (m->init_func != NULL);
+
+ if (is_c_module) {
+ /* initialize the exported variables */
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ var_ref = js_create_module_var(ctx, FALSE);
+ if (!var_ref)
+ return -1;
+ me->u.local.var_ref = var_ref;
+ }
+ }
+ } else {
+ if (js_create_module_bytecode_function(ctx, m))
+ return -1;
+ }
+ m->func_created = TRUE;
+
+ /* do it on the dependencies */
+
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ if (js_create_module_function(ctx, rme->module) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* Prepare a module to be executed by resolving all the imported
+ variables. */
+static int js_link_module(JSContext *ctx, JSModuleDef *m)
+{
+ int i;
+ JSImportEntry *mi;
+ JSModuleDef *m1;
+ JSVarRef **var_refs, *var_ref;
+ JSObject *p;
+ BOOL is_c_module;
+ JSValue ret_val;
+
+ if (m->instantiated)
+ return 0;
+ m->instantiated = TRUE;
+
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
+ }
+#endif
+
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ if (js_link_module(ctx, rme->module) < 0)
+ goto fail;
+ }
+
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ char buf1[ATOM_GET_STR_BUF_SIZE];
+ printf("instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name));
+ }
+#endif
+ /* check the indirect exports */
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_INDIRECT &&
+ me->local_name != JS_ATOM__star_) {
+ JSResolveResultEnum ret;
+ JSExportEntry *res_me;
+ JSModuleDef *res_m, *m1;
+ m1 = m->req_module_entries[me->u.req_module_idx].module;
+ ret = js_resolve_export(ctx, &res_m, &res_me, m1, me->local_name);
+ if (ret != JS_RESOLVE_RES_FOUND) {
+ js_resolve_export_throw_error(ctx, ret, m, me->export_name);
+ goto fail;
+ }
+ }
+ }
+
+#ifdef DUMP_MODULE_RESOLVE
+ {
+ printf("exported bindings:\n");
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ printf(" name="); print_atom(ctx, me->export_name);
+ printf(" local="); print_atom(ctx, me->local_name);
+ printf(" type=%d idx=%d\n", me->export_type, me->u.local.var_idx);
+ }
+ }
+#endif
+
+ is_c_module = (m->init_func != NULL);
+
+ if (!is_c_module) {
+ p = JS_VALUE_GET_OBJ(m->func_obj);
+ var_refs = p->u.func.var_refs;
+
+ for(i = 0; i < m->import_entries_count; i++) {
+ mi = &m->import_entries[i];
+#ifdef DUMP_MODULE_RESOLVE
+ printf("import var_idx=%d name=", mi->var_idx);
+ print_atom(ctx, mi->import_name);
+ printf(": ");
+#endif
+ m1 = m->req_module_entries[mi->req_module_idx].module;
+ if (mi->import_name == JS_ATOM__star_) {
+ JSValue val;
+ /* name space import */
+ val = js_get_module_ns(ctx, m1);
+ if (JS_IsException(val))
+ goto fail;
+ set_value(ctx, &var_refs[mi->var_idx]->value, val);
+#ifdef DUMP_MODULE_RESOLVE
+ printf("namespace\n");
+#endif
+ } else {
+ JSResolveResultEnum ret;
+ JSExportEntry *res_me;
+ JSModuleDef *res_m;
+ JSObject *p1;
+
+ ret = js_resolve_export(ctx, &res_m,
+ &res_me, m1, mi->import_name);
+ if (ret != JS_RESOLVE_RES_FOUND) {
+ js_resolve_export_throw_error(ctx, ret, m1, mi->import_name);
+ goto fail;
+ }
+ if (res_me->local_name == JS_ATOM__star_) {
+ JSValue val;
+ JSModuleDef *m2;
+ /* name space import from */
+ m2 = res_m->req_module_entries[res_me->u.req_module_idx].module;
+ val = js_get_module_ns(ctx, m2);
+ if (JS_IsException(val))
+ goto fail;
+ var_ref = js_create_module_var(ctx, TRUE);
+ if (!var_ref) {
+ JS_FreeValue(ctx, val);
+ goto fail;
+ }
+ set_value(ctx, &var_ref->value, val);
+ var_refs[mi->var_idx] = var_ref;
+#ifdef DUMP_MODULE_RESOLVE
+ printf("namespace from\n");
+#endif
+ } else {
+ var_ref = res_me->u.local.var_ref;
+ if (!var_ref) {
+ p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
+ var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
+ }
+ var_ref->header.ref_count++;
+ var_refs[mi->var_idx] = var_ref;
+#ifdef DUMP_MODULE_RESOLVE
+ printf("local export (var_ref=%p)\n", var_ref);
+#endif
+ }
+ }
+ }
+
+ /* keep the exported variables in the module export entries (they
+ are used when the eval function is deleted and cannot be
+ initialized before in case imports are exported) */
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ var_ref = var_refs[me->u.local.var_idx];
+ var_ref->header.ref_count++;
+ me->u.local.var_ref = var_ref;
+ }
+ }
+
+ /* initialize the global variables */
+ ret_val = JS_Call(ctx, m->func_obj, JS_TRUE, 0, NULL);
+ if (JS_IsException(ret_val))
+ goto fail;
+ JS_FreeValue(ctx, ret_val);
+ }
+
+#ifdef DUMP_MODULE_RESOLVE
+ printf("done instantiate\n");
+#endif
+ return 0;
+ fail:
+ return -1;
+}
+
+/* return JS_ATOM_NULL if the name cannot be found. Only works with
+ not striped bytecode functions. */
+JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels)
+{
+ JSStackFrame *sf;
+ JSFunctionBytecode *b;
+ JSObject *p;
+ /* XXX: currently we just use the filename of the englobing
+ function. It does not work for eval(). Need to add a
+ ScriptOrModule info in JSFunctionBytecode */
+ sf = ctx->rt->current_stack_frame;
+ if (!sf)
+ return JS_ATOM_NULL;
+ while (n_stack_levels-- > 0) {
+ sf = sf->prev_frame;
+ if (!sf)
+ return JS_ATOM_NULL;
+ }
+ if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT)
+ return JS_ATOM_NULL;
+ p = JS_VALUE_GET_OBJ(sf->cur_func);
+ if (!js_class_has_bytecode(p->class_id))
+ return JS_ATOM_NULL;
+ b = p->u.func.function_bytecode;
+ if (!b->has_debug)
+ return JS_ATOM_NULL;
+ return JS_DupAtom(ctx, b->debug.filename);
+}
+
+JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m)
+{
+ return JS_DupAtom(ctx, m->module_name);
+}
+
+JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m)
+{
+ JSValue obj;
+ /* allocate meta_obj only if requested to save memory */
+ obj = m->meta_obj;
+ if (JS_IsUndefined(obj)) {
+ obj = JS_NewObjectProto(ctx, JS_NULL);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ m->meta_obj = obj;
+ }
+ return JS_DupValue(ctx, obj);
+}
+
+static JSValue js_import_meta(JSContext *ctx)
+{
+ JSAtom filename;
+ JSModuleDef *m;
+
+ filename = JS_GetScriptOrModuleName(ctx, 0);
+ if (filename == JS_ATOM_NULL)
+ goto fail;
+
+ /* XXX: inefficient, need to add a module or script pointer in
+ JSFunctionBytecode */
+ m = js_find_loaded_module(ctx, filename);
+ JS_FreeAtom(ctx, filename);
+ if (!m) {
+ fail:
+ JS_ThrowTypeError(ctx, "import.meta not supported in this context");
+ return JS_EXCEPTION;
+ }
+ return JS_GetImportMeta(ctx, m);
+}
+
+/* used by os.Worker() and import() */
+JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
+ const char *filename)
+{
+ JSModuleDef *m;
+ JSValue ret, func_obj;
+
+ m = js_host_resolve_imported_module(ctx, basename, filename);
+ if (!m)
+ return NULL;
+
+ if (js_resolve_module(ctx, m) < 0) {
+ js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
+ return NULL;
+ }
+
+ /* Evaluate the module code */
+ func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+ ret = JS_EvalFunction(ctx, func_obj);
+ if (JS_IsException(ret))
+ return NULL;
+ JS_FreeValue(ctx, ret);
+ return m;
+}
+
+static JSValue js_dynamic_import_job(JSContext *ctx,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst *resolving_funcs = argv;
+ JSValueConst basename_val = argv[2];
+ JSValueConst specifier = argv[3];
+ JSModuleDef *m;
+ const char *basename = NULL, *filename;
+ JSValue ret, err, ns;
+
+ if (!JS_IsString(basename_val)) {
+ JS_ThrowTypeError(ctx, "no function filename for import()");
+ goto exception;
+ }
+ basename = JS_ToCString(ctx, basename_val);
+ if (!basename)
+ goto exception;
+
+ filename = JS_ToCString(ctx, specifier);
+ if (!filename)
+ goto exception;
+
+ m = JS_RunModule(ctx, basename, filename);
+ JS_FreeCString(ctx, filename);
+ if (!m)
+ goto exception;
+
+ /* return the module namespace */
+ ns = js_get_module_ns(ctx, m);
+ if (JS_IsException(ns))
+ goto exception;
+
+ ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
+ 1, &ns);
+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, ns);
+ JS_FreeCString(ctx, basename);
+ return JS_UNDEFINED;
+ exception:
+
+ err = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
+ 1, &err);
+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, err);
+ JS_FreeCString(ctx, basename);
+ return JS_UNDEFINED;
+}
+
+static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier)
+{
+ JSAtom basename;
+ JSValue promise, resolving_funcs[2], basename_val;
+ JSValueConst args[4];
+
+ basename = JS_GetScriptOrModuleName(ctx, 0);
+ if (basename == JS_ATOM_NULL)
+ basename_val = JS_NULL;
+ else
+ basename_val = JS_AtomToValue(ctx, basename);
+ JS_FreeAtom(ctx, basename);
+ if (JS_IsException(basename_val))
+ return basename_val;
+
+ promise = JS_NewPromiseCapability(ctx, resolving_funcs);
+ if (JS_IsException(promise)) {
+ JS_FreeValue(ctx, basename_val);
+ return promise;
+ }
+
+ args[0] = resolving_funcs[0];
+ args[1] = resolving_funcs[1];
+ args[2] = basename_val;
+ args[3] = specifier;
+
+ JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args);
+
+ JS_FreeValue(ctx, basename_val);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return promise;
+}
+
+/* Run the <eval> function of the module and of all its requested
+ modules. */
+static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
+{
+ JSModuleDef *m1;
+ int i;
+ JSValue ret_val;
+
+ if (m->eval_mark)
+ return JS_UNDEFINED; /* avoid cycles */
+
+ if (m->evaluated) {
+ /* if the module was already evaluated, rethrow the exception
+ it raised */
+ if (m->eval_has_exception) {
+ return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception));
+ } else {
+ return JS_UNDEFINED;
+ }
+ }
+
+ m->eval_mark = TRUE;
+
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ m1 = rme->module;
+ if (!m1->eval_mark) {
+ ret_val = js_evaluate_module(ctx, m1);
+ if (JS_IsException(ret_val)) {
+ m->eval_mark = FALSE;
+ return ret_val;
+ }
+ JS_FreeValue(ctx, ret_val);
+ }
+ }
+
+ if (m->init_func) {
+ /* C module init */
+ if (m->init_func(ctx, m) < 0)
+ ret_val = JS_EXCEPTION;
+ else
+ ret_val = JS_UNDEFINED;
+ } else {
+ ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL);
+ m->func_obj = JS_UNDEFINED;
+ }
+ if (JS_IsException(ret_val)) {
+ /* save the thrown exception value */
+ m->eval_has_exception = TRUE;
+ m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception);
+ }
+ m->eval_mark = FALSE;
+ m->evaluated = TRUE;
+ return ret_val;
+}
+
+static warn_unused JSAtom js_parse_from_clause(JSParseState *s)
+{
+ JSAtom module_name;
+ if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {
+ js_parse_error(s, "from clause expected");
+ return JS_ATOM_NULL;
+ }
+ if (next_token(s))
+ return JS_ATOM_NULL;
+ if (s->token.val != TOK_STRING) {
+ js_parse_error(s, "string expected");
+ return JS_ATOM_NULL;
+ }
+ module_name = JS_ValueToAtom(s->ctx, s->token.u.str.str);
+ if (module_name == JS_ATOM_NULL)
+ return JS_ATOM_NULL;
+ if (next_token(s)) {
+ JS_FreeAtom(s->ctx, module_name);
+ return JS_ATOM_NULL;
+ }
+ return module_name;
+}
+
+static warn_unused int js_parse_export(JSParseState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSModuleDef *m = s->cur_func->module;
+ JSAtom local_name, export_name;
+ int first_export, idx, i, tok;
+ JSAtom module_name;
+ JSExportEntry *me;
+
+ if (next_token(s))
+ return -1;
+
+ tok = s->token.val;
+ if (tok == TOK_CLASS) {
+ return js_parse_class(s, FALSE, JS_PARSE_EXPORT_NAMED);
+ } else if (tok == TOK_FUNCTION ||
+ (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) == TOK_FUNCTION)) {
+ return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num,
+ JS_PARSE_EXPORT_NAMED, NULL);
+ }
+
+ if (next_token(s))
+ return -1;
+
+ switch(tok) {
+ case '{':
+ first_export = m->export_entries_count;
+ while (s->token.val != '}') {
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ return -1;
+ }
+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ export_name = JS_ATOM_NULL;
+ if (next_token(s))
+ goto fail;
+ if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
+ if (next_token(s))
+ goto fail;
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ goto fail;
+ }
+ export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s)) {
+ fail:
+ JS_FreeAtom(ctx, local_name);
+ fail1:
+ JS_FreeAtom(ctx, export_name);
+ return -1;
+ }
+ } else {
+ export_name = JS_DupAtom(ctx, local_name);
+ }
+ me = add_export_entry(s, m, local_name, export_name,
+ JS_EXPORT_TYPE_LOCAL);
+ JS_FreeAtom(ctx, local_name);
+ JS_FreeAtom(ctx, export_name);
+ if (!me)
+ return -1;
+ if (s->token.val != ',')
+ break;
+ if (next_token(s))
+ return -1;
+ }
+ if (js_parse_expect(s, '}'))
+ return -1;
+ if (token_is_pseudo_keyword(s, JS_ATOM_from)) {
+ module_name = js_parse_from_clause(s);
+ if (module_name == JS_ATOM_NULL)
+ return -1;
+ idx = add_req_module_entry(ctx, m, module_name);
+ JS_FreeAtom(ctx, module_name);
+ if (idx < 0)
+ return -1;
+ for(i = first_export; i < m->export_entries_count; i++) {
+ me = &m->export_entries[i];
+ me->export_type = JS_EXPORT_TYPE_INDIRECT;
+ me->u.req_module_idx = idx;
+ }
+ }
+ break;
+ case '*':
+ if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
+ /* export ns from */
+ if (next_token(s))
+ return -1;
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ return -1;
+ }
+ export_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s))
+ goto fail1;
+ module_name = js_parse_from_clause(s);
+ if (module_name == JS_ATOM_NULL)
+ goto fail1;
+ idx = add_req_module_entry(ctx, m, module_name);
+ JS_FreeAtom(ctx, module_name);
+ if (idx < 0)
+ goto fail1;
+ me = add_export_entry(s, m, JS_ATOM__star_, export_name,
+ JS_EXPORT_TYPE_INDIRECT);
+ JS_FreeAtom(ctx, export_name);
+ if (!me)
+ return -1;
+ me->u.req_module_idx = idx;
+ } else {
+ module_name = js_parse_from_clause(s);
+ if (module_name == JS_ATOM_NULL)
+ return -1;
+ idx = add_req_module_entry(ctx, m, module_name);
+ JS_FreeAtom(ctx, module_name);
+ if (idx < 0)
+ return -1;
+ if (add_star_export_entry(ctx, m, idx) < 0)
+ return -1;
+ }
+ break;
+ case TOK_DEFAULT:
+ if (s->token.val == TOK_CLASS) {
+ return js_parse_class(s, FALSE, JS_PARSE_EXPORT_DEFAULT);
+ } else if (s->token.val == TOK_FUNCTION ||
+ (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) == TOK_FUNCTION)) {
+ return js_parse_function_decl2(s, JS_PARSE_FUNC_STATEMENT,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num,
+ JS_PARSE_EXPORT_DEFAULT, NULL);
+ } else {
+ if (js_parse_assign_expr(s))
+ return -1;
+ }
+ /* set the name of anonymous functions */
+ set_object_name(s, JS_ATOM_default);
+
+ /* store the value in the _default_ global variable and export
+ it */
+ local_name = JS_ATOM__default_;
+ if (define_var(s, s->cur_func, local_name, JS_VAR_DEF_LET) < 0)
+ return -1;
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, local_name);
+ emit_u16(s, 0);
+
+ if (!add_export_entry(s, m, local_name, JS_ATOM_default,
+ JS_EXPORT_TYPE_LOCAL))
+ return -1;
+ break;
+ case TOK_VAR:
+ case TOK_LET:
+ case TOK_CONST:
+ return js_parse_var(s, TRUE, tok, TRUE);
+ default:
+ return js_parse_error(s, "invalid export syntax");
+ }
+ return js_parse_expect_semi(s);
+}
+
+static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
+ BOOL is_local, BOOL is_arg,
+ int var_idx, JSAtom var_name,
+ BOOL is_const, BOOL is_lexical,
+ JSVarKindEnum var_kind);
+
+static int add_import(JSParseState *s, JSModuleDef *m,
+ JSAtom local_name, JSAtom import_name)
+{
+ JSContext *ctx = s->ctx;
+ int i, var_idx;
+ JSImportEntry *mi;
+ BOOL is_local;
+
+ if (local_name == JS_ATOM_arguments || local_name == JS_ATOM_eval)
+ return js_parse_error(s, "invalid import binding");
+
+ if (local_name != JS_ATOM_default) {
+ for (i = 0; i < s->cur_func->closure_var_count; i++) {
+ if (s->cur_func->closure_var[i].var_name == local_name)
+ return js_parse_error(s, "duplicate import binding");
+ }
+ }
+
+ is_local = (import_name == JS_ATOM__star_);
+ var_idx = add_closure_var(ctx, s->cur_func, is_local, FALSE,
+ m->import_entries_count,
+ local_name, TRUE, TRUE, FALSE);
+ if (var_idx < 0)
+ return -1;
+ if (js_resize_array(ctx, (void **)&m->import_entries,
+ sizeof(JSImportEntry),
+ &m->import_entries_size,
+ m->import_entries_count + 1))
+ return -1;
+ mi = &m->import_entries[m->import_entries_count++];
+ mi->import_name = JS_DupAtom(ctx, import_name);
+ mi->var_idx = var_idx;
+ return 0;
+}
+
+static warn_unused int js_parse_import(JSParseState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSModuleDef *m = s->cur_func->module;
+ JSAtom local_name, import_name, module_name;
+ int first_import, i, idx;
+
+ if (next_token(s))
+ return -1;
+
+ first_import = m->import_entries_count;
+ if (s->token.val == TOK_STRING) {
+ module_name = JS_ValueToAtom(ctx, s->token.u.str.str);
+ if (module_name == JS_ATOM_NULL)
+ return -1;
+ if (next_token(s)) {
+ JS_FreeAtom(ctx, module_name);
+ return -1;
+ }
+ } else {
+ if (s->token.val == TOK_IDENT) {
+ if (s->token.u.ident.is_reserved) {
+ return js_parse_error_reserved_identifier(s);
+ }
+ /* "default" import */
+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ import_name = JS_ATOM_default;
+ if (next_token(s))
+ goto fail;
+ if (add_import(s, m, local_name, import_name))
+ goto fail;
+ JS_FreeAtom(ctx, local_name);
+
+ if (s->token.val != ',')
+ goto end_import_clause;
+ if (next_token(s))
+ return -1;
+ }
+
+ if (s->token.val == '*') {
+ /* name space import */
+ if (next_token(s))
+ return -1;
+ if (!token_is_pseudo_keyword(s, JS_ATOM_as))
+ return js_parse_error(s, "expecting 'as'");
+ if (next_token(s))
+ return -1;
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ return -1;
+ }
+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ import_name = JS_ATOM__star_;
+ if (next_token(s))
+ goto fail;
+ if (add_import(s, m, local_name, import_name))
+ goto fail;
+ JS_FreeAtom(ctx, local_name);
+ } else if (s->token.val == '{') {
+ if (next_token(s))
+ return -1;
+
+ while (s->token.val != '}') {
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ return -1;
+ }
+ import_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ local_name = JS_ATOM_NULL;
+ if (next_token(s))
+ goto fail;
+ if (token_is_pseudo_keyword(s, JS_ATOM_as)) {
+ if (next_token(s))
+ goto fail;
+ if (!token_is_ident(s->token.val)) {
+ js_parse_error(s, "identifier expected");
+ goto fail;
+ }
+ local_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s)) {
+ fail:
+ JS_FreeAtom(ctx, local_name);
+ JS_FreeAtom(ctx, import_name);
+ return -1;
+ }
+ } else {
+ local_name = JS_DupAtom(ctx, import_name);
+ }
+ if (add_import(s, m, local_name, import_name))
+ goto fail;
+ JS_FreeAtom(ctx, local_name);
+ JS_FreeAtom(ctx, import_name);
+ if (s->token.val != ',')
+ break;
+ if (next_token(s))
+ return -1;
+ }
+ if (js_parse_expect(s, '}'))
+ return -1;
+ }
+ end_import_clause:
+ module_name = js_parse_from_clause(s);
+ if (module_name == JS_ATOM_NULL)
+ return -1;
+ }
+ idx = add_req_module_entry(ctx, m, module_name);
+ JS_FreeAtom(ctx, module_name);
+ if (idx < 0)
+ return -1;
+ for(i = first_import; i < m->import_entries_count; i++)
+ m->import_entries[i].req_module_idx = idx;
+
+ return js_parse_expect_semi(s);
+}
+
+static warn_unused int js_parse_source_element(JSParseState *s)
+{
+ JSFunctionDef *fd = s->cur_func;
+ int tok;
+
+ if (s->token.val == TOK_FUNCTION ||
+ (token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) == TOK_FUNCTION)) {
+ if (js_parse_function_decl(s, JS_PARSE_FUNC_STATEMENT,
+ JS_FUNC_NORMAL, JS_ATOM_NULL,
+ s->token.ptr, s->token.line_num))
+ return -1;
+ } else if (s->token.val == TOK_EXPORT && fd->module) {
+ if (js_parse_export(s))
+ return -1;
+ } else if (s->token.val == TOK_IMPORT && fd->module &&
+ ((tok = peek_token(s, FALSE)) != '(' && tok != '.')) {
+ /* the peek_token is needed to avoid confusion with ImportCall
+ (dynamic import) or import.meta */
+ if (js_parse_import(s))
+ return -1;
+ } else {
+ if (js_parse_statement_or_decl(s, DECL_MASK_ALL))
+ return -1;
+ }
+ return 0;
+}
+
+static JSFunctionDef *js_new_function_def(JSContext *ctx,
+ JSFunctionDef *parent,
+ BOOL is_eval,
+ BOOL is_func_expr,
+ const char *filename, int line_num)
+{
+ JSFunctionDef *fd;
+
+ fd = js_mallocz(ctx, sizeof(*fd));
+ if (!fd)
+ return NULL;
+
+ fd->ctx = ctx;
+ init_list_head(&fd->child_list);
+
+ /* insert in parent list */
+ fd->parent = parent;
+ fd->parent_cpool_idx = -1;
+ if (parent) {
+ list_add_tail(&fd->link, &parent->child_list);
+ fd->js_mode = parent->js_mode;
+ fd->parent_scope_level = parent->scope_level;
+ }
+
+ fd->is_eval = is_eval;
+ fd->is_func_expr = is_func_expr;
+ js_dbuf_init(ctx, &fd->byte_code);
+ fd->last_opcode_pos = -1;
+ fd->func_name = JS_ATOM_NULL;
+ fd->var_object_idx = -1;
+ fd->arg_var_object_idx = -1;
+ fd->arguments_var_idx = -1;
+ fd->arguments_arg_idx = -1;
+ fd->func_var_idx = -1;
+ fd->eval_ret_idx = -1;
+ fd->this_var_idx = -1;
+ fd->new_target_var_idx = -1;
+ fd->this_active_func_var_idx = -1;
+ fd->home_object_var_idx = -1;
+
+ /* XXX: should distinguish arg, var and var object and body scopes */
+ fd->scopes = fd->def_scope_array;
+ fd->scope_size = countof(fd->def_scope_array);
+ fd->scope_count = 1;
+ fd->scopes[0].first = -1;
+ fd->scopes[0].parent = -1;
+ fd->scope_level = 0; /* 0: var/arg scope */
+ fd->scope_first = -1;
+ fd->body_scope = -1;
+
+ fd->filename = JS_NewAtom(ctx, filename);
+ fd->line_num = line_num;
+
+ js_dbuf_init(ctx, &fd->pc2line);
+ //fd->pc2line_last_line_num = line_num;
+ //fd->pc2line_last_pc = 0;
+ fd->last_opcode_line_num = line_num;
+
+ return fd;
+}
+
+static void free_bytecode_atoms(JSRuntime *rt,
+ const uint8_t *bc_buf, int bc_len,
+ BOOL use_short_opcodes)
+{
+ int pos, len, op;
+ JSAtom atom;
+ const JSOpCode *oi;
+
+ pos = 0;
+ while (pos < bc_len) {
+ op = bc_buf[pos];
+ if (use_short_opcodes)
+ oi = &short_opcode_info(op);
+ else
+ oi = &opcode_info[op];
+
+ len = oi->size;
+ switch(oi->fmt) {
+ case OP_FMT_atom:
+ case OP_FMT_atom_u8:
+ case OP_FMT_atom_u16:
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ atom = get_u32(bc_buf + pos + 1);
+ JS_FreeAtomRT(rt, atom);
+ break;
+ default:
+ break;
+ }
+ pos += len;
+ }
+}
+
+static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
+{
+ int i;
+ struct list_head *el, *el1;
+
+ /* free the child functions */
+ list_for_each_safe(el, el1, &fd->child_list) {
+ JSFunctionDef *fd1;
+ fd1 = list_entry(el, JSFunctionDef, link);
+ js_free_function_def(ctx, fd1);
+ }
+
+ free_bytecode_atoms(ctx->rt, fd->byte_code.buf, fd->byte_code.size,
+ fd->use_short_opcodes);
+ dbuf_free(&fd->byte_code);
+ js_free(ctx, fd->jump_slots);
+ js_free(ctx, fd->label_slots);
+ js_free(ctx, fd->line_number_slots);
+
+ for(i = 0; i < fd->cpool_count; i++) {
+ JS_FreeValue(ctx, fd->cpool[i]);
+ }
+ js_free(ctx, fd->cpool);
+
+ JS_FreeAtom(ctx, fd->func_name);
+
+ for(i = 0; i < fd->var_count; i++) {
+ JS_FreeAtom(ctx, fd->vars[i].var_name);
+ }
+ js_free(ctx, fd->vars);
+ for(i = 0; i < fd->arg_count; i++) {
+ JS_FreeAtom(ctx, fd->args[i].var_name);
+ }
+ js_free(ctx, fd->args);
+
+ for(i = 0; i < fd->global_var_count; i++) {
+ JS_FreeAtom(ctx, fd->global_vars[i].var_name);
+ }
+ js_free(ctx, fd->global_vars);
+
+ for(i = 0; i < fd->closure_var_count; i++) {
+ JSClosureVar *cv = &fd->closure_var[i];
+ JS_FreeAtom(ctx, cv->var_name);
+ }
+ js_free(ctx, fd->closure_var);
+
+ if (fd->scopes != fd->def_scope_array)
+ js_free(ctx, fd->scopes);
+
+ JS_FreeAtom(ctx, fd->filename);
+ dbuf_free(&fd->pc2line);
+
+ js_free(ctx, fd->source);
+
+ if (fd->parent) {
+ /* remove in parent list */
+ list_del(&fd->link);
+ }
+ js_free(ctx, fd);
+}
+
+#ifdef DUMP_BYTECODE
+static const char *skip_lines(const char *p, int n) {
+ while (n-- > 0 && *p) {
+ while (*p && *p++ != '\n')
+ continue;
+ }
+ return p;
+}
+
+static void print_lines(const char *source, int line, int line1) {
+ const char *s = source;
+ const char *p = skip_lines(s, line);
+ if (*p) {
+ while (line++ < line1) {
+ p = skip_lines(s = p, 1);
+ printf(";; %.*s", (int)(p - s), s);
+ if (!*p) {
+ if (p[-1] != '\n')
+ printf("\n");
+ break;
+ }
+ }
+ }
+}
+
+static void dump_byte_code(JSContext *ctx, int pass,
+ const uint8_t *tab, int len,
+ const JSVarDef *args, int arg_count,
+ const JSVarDef *vars, int var_count,
+ const JSClosureVar *closure_var, int closure_var_count,
+ const JSValue *cpool, uint32_t cpool_count,
+ const char *source, int line_num,
+ const LabelSlot *label_slots, JSFunctionBytecode *b)
+{
+ const JSOpCode *oi;
+ int pos, pos_next, op, size, idx, addr, line, line1, in_source;
+ uint8_t *bits = js_mallocz(ctx, len * sizeof(*bits));
+ BOOL use_short_opcodes = (b != NULL);
+
+ /* scan for jump targets */
+ for (pos = 0; pos < len; pos = pos_next) {
+ op = tab[pos];
+ if (use_short_opcodes)
+ oi = &short_opcode_info(op);
+ else
+ oi = &opcode_info[op];
+ pos_next = pos + oi->size;
+ if (op < OP_COUNT) {
+ switch (oi->fmt) {
+#if SHORT_OPCODES
+ case OP_FMT_label8:
+ pos++;
+ addr = (int8_t)tab[pos];
+ goto has_addr;
+ case OP_FMT_label16:
+ pos++;
+ addr = (int16_t)get_u16(tab + pos);
+ goto has_addr;
+#endif
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ pos += 4;
+ /* fall thru */
+ case OP_FMT_label:
+ case OP_FMT_label_u16:
+ pos++;
+ addr = get_u32(tab + pos);
+ goto has_addr;
+ has_addr:
+ if (pass == 1)
+ addr = label_slots[addr].pos;
+ if (pass == 2)
+ addr = label_slots[addr].pos2;
+ if (pass == 3)
+ addr += pos;
+ if (addr >= 0 && addr < len)
+ bits[addr] |= 1;
+ break;
+ }
+ }
+ }
+ in_source = 0;
+ if (source) {
+ /* Always print first line: needed if single line */
+ print_lines(source, 0, 1);
+ in_source = 1;
+ }
+ line1 = line = 1;
+ pos = 0;
+ while (pos < len) {
+ op = tab[pos];
+ if (source) {
+ if (b) {
+ line1 = find_line_num(ctx, b, pos) - line_num + 1;
+ } else if (op == OP_line_num) {
+ line1 = get_u32(tab + pos + 1) - line_num + 1;
+ }
+ if (line1 > line) {
+ if (!in_source)
+ printf("\n");
+ in_source = 1;
+ print_lines(source, line, line1);
+ line = line1;
+ //bits[pos] |= 2;
+ }
+ }
+ if (in_source)
+ printf("\n");
+ in_source = 0;
+ if (op >= OP_COUNT) {
+ printf("invalid opcode (0x%02x)\n", op);
+ pos++;
+ continue;
+ }
+ if (use_short_opcodes)
+ oi = &short_opcode_info(op);
+ else
+ oi = &opcode_info[op];
+ size = oi->size;
+ if (pos + size > len) {
+ printf("truncated opcode (0x%02x)\n", op);
+ break;
+ }
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 16)
+ {
+ int i, x, x0;
+ x = x0 = printf("%5d ", pos);
+ for (i = 0; i < size; i++) {
+ if (i == 6) {
+ printf("\n%*s", x = x0, "");
+ }
+ x += printf(" %02X", tab[pos + i]);
+ }
+ printf("%*s", x0 + 20 - x, "");
+ }
+#endif
+ if (bits[pos]) {
+ printf("%5d: ", pos);
+ } else {
+ printf(" ");
+ }
+ printf("%s", oi->name);
+ pos++;
+ switch(oi->fmt) {
+ case OP_FMT_none_int:
+ printf(" %d", op - OP_push_0);
+ break;
+ case OP_FMT_npopx:
+ printf(" %d", op - OP_call0);
+ break;
+ case OP_FMT_u8:
+ printf(" %u", get_u8(tab + pos));
+ break;
+ case OP_FMT_i8:
+ printf(" %d", get_i8(tab + pos));
+ break;
+ case OP_FMT_u16:
+ case OP_FMT_npop:
+ printf(" %u", get_u16(tab + pos));
+ break;
+ case OP_FMT_npop_u16:
+ printf(" %u,%u", get_u16(tab + pos), get_u16(tab + pos + 2));
+ break;
+ case OP_FMT_i16:
+ printf(" %d", get_i16(tab + pos));
+ break;
+ case OP_FMT_i32:
+ printf(" %d", get_i32(tab + pos));
+ break;
+ case OP_FMT_u32:
+ printf(" %u", get_u32(tab + pos));
+ break;
+#if SHORT_OPCODES
+ case OP_FMT_label8:
+ addr = get_i8(tab + pos);
+ goto has_addr1;
+ case OP_FMT_label16:
+ addr = get_i16(tab + pos);
+ goto has_addr1;
+#endif
+ case OP_FMT_label:
+ addr = get_u32(tab + pos);
+ goto has_addr1;
+ has_addr1:
+ if (pass == 1)
+ printf(" %u:%u", addr, label_slots[addr].pos);
+ if (pass == 2)
+ printf(" %u:%u", addr, label_slots[addr].pos2);
+ if (pass == 3)
+ printf(" %u", addr + pos);
+ break;
+ case OP_FMT_label_u16:
+ addr = get_u32(tab + pos);
+ if (pass == 1)
+ printf(" %u:%u", addr, label_slots[addr].pos);
+ if (pass == 2)
+ printf(" %u:%u", addr, label_slots[addr].pos2);
+ if (pass == 3)
+ printf(" %u", addr + pos);
+ printf(",%u", get_u16(tab + pos + 4));
+ break;
+#if SHORT_OPCODES
+ case OP_FMT_const8:
+ idx = get_u8(tab + pos);
+ goto has_pool_idx;
+#endif
+ case OP_FMT_const:
+ idx = get_u32(tab + pos);
+ goto has_pool_idx;
+ has_pool_idx:
+ printf(" %u: ", idx);
+ if (idx < cpool_count) {
+ JS_DumpValue(ctx, cpool[idx]);
+ }
+ break;
+ case OP_FMT_atom:
+ printf(" ");
+ print_atom(ctx, get_u32(tab + pos));
+ break;
+ case OP_FMT_atom_u8:
+ printf(" ");
+ print_atom(ctx, get_u32(tab + pos));
+ printf(",%d", get_u8(tab + pos + 4));
+ break;
+ case OP_FMT_atom_u16:
+ printf(" ");
+ print_atom(ctx, get_u32(tab + pos));
+ printf(",%d", get_u16(tab + pos + 4));
+ break;
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ printf(" ");
+ print_atom(ctx, get_u32(tab + pos));
+ addr = get_u32(tab + pos + 4);
+ if (pass == 1)
+ printf(",%u:%u", addr, label_slots[addr].pos);
+ if (pass == 2)
+ printf(",%u:%u", addr, label_slots[addr].pos2);
+ if (pass == 3)
+ printf(",%u", addr + pos + 4);
+ if (oi->fmt == OP_FMT_atom_label_u8)
+ printf(",%u", get_u8(tab + pos + 8));
+ else
+ printf(",%u", get_u16(tab + pos + 8));
+ break;
+ case OP_FMT_none_loc:
+ idx = (op - OP_get_loc0) % 4;
+ goto has_loc;
+ case OP_FMT_loc8:
+ idx = get_u8(tab + pos);
+ goto has_loc;
+ case OP_FMT_loc:
+ idx = get_u16(tab + pos);
+ has_loc:
+ printf(" %d: ", idx);
+ if (idx < var_count) {
+ print_atom(ctx, vars[idx].var_name);
+ }
+ break;
+ case OP_FMT_none_arg:
+ idx = (op - OP_get_arg0) % 4;
+ goto has_arg;
+ case OP_FMT_arg:
+ idx = get_u16(tab + pos);
+ has_arg:
+ printf(" %d: ", idx);
+ if (idx < arg_count) {
+ print_atom(ctx, args[idx].var_name);
+ }
+ break;
+ case OP_FMT_none_var_ref:
+ idx = (op - OP_get_var_ref0) % 4;
+ goto has_var_ref;
+ case OP_FMT_var_ref:
+ idx = get_u16(tab + pos);
+ has_var_ref:
+ printf(" %d: ", idx);
+ if (idx < closure_var_count) {
+ print_atom(ctx, closure_var[idx].var_name);
+ }
+ break;
+ default:
+ break;
+ }
+ printf("\n");
+ pos += oi->size - 1;
+ }
+ if (source) {
+ if (!in_source)
+ printf("\n");
+ print_lines(source, line, INT32_MAX);
+ }
+ js_free(ctx, bits);
+}
+
+static __maybe_unused void dump_pc2line(JSContext *ctx, const uint8_t *buf, int len,
+ int line_num)
+{
+ const uint8_t *p_end, *p_next, *p;
+ int pc, v;
+ unsigned int op;
+
+ if (len <= 0)
+ return;
+
+ printf("%5s %5s\n", "PC", "LINE");
+
+ p = buf;
+ p_end = buf + len;
+ pc = 0;
+ while (p < p_end) {
+ op = *p++;
+ if (op == 0) {
+ v = unicode_from_utf8(p, p_end - p, &p_next);
+ if (v < 0)
+ goto fail;
+ pc += v;
+ p = p_next;
+ v = unicode_from_utf8(p, p_end - p, &p_next);
+ if (v < 0) {
+ fail:
+ printf("invalid pc2line encode pos=%d\n", (int)(p - buf));
+ return;
+ }
+ if (!(v & 1)) {
+ v = v >> 1;
+ } else {
+ v = -(v >> 1) - 1;
+ }
+ line_num += v;
+ p = p_next;
+ } else {
+ op -= PC2LINE_OP_FIRST;
+ pc += (op / PC2LINE_RANGE);
+ line_num += (op % PC2LINE_RANGE) + PC2LINE_BASE;
+ }
+ printf("%5d %5d\n", pc, line_num);
+ }
+}
+
+static __maybe_unused void js_dump_function_bytecode(JSContext *ctx, JSFunctionBytecode *b)
+{
+ int i;
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ const char *str;
+
+ if (b->has_debug && b->debug.filename != JS_ATOM_NULL) {
+ str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->debug.filename);
+ printf("%s:%d: ", str, b->debug.line_num);
+ }
+
+ str = JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), b->func_name);
+ printf("function: %s%s\n", &"*"[b->func_kind != JS_FUNC_GENERATOR], str);
+ if (b->js_mode) {
+ printf(" mode:");
+ if (b->js_mode & JS_MODE_STRICT)
+ printf(" strict");
+#ifdef CONFIG_BIGNUM
+ if (b->js_mode & JS_MODE_MATH)
+ printf(" math");
+#endif
+ printf("\n");
+ }
+ if (b->arg_count && b->vardefs) {
+ printf(" args:");
+ for(i = 0; i < b->arg_count; i++) {
+ printf(" %s", JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf),
+ b->vardefs[i].var_name));
+ }
+ printf("\n");
+ }
+ if (b->var_count && b->vardefs) {
+ printf(" locals:\n");
+ for(i = 0; i < b->var_count; i++) {
+ JSVarDef *vd = &b->vardefs[b->arg_count + i];
+ printf("%5d: %s %s", i,
+ vd->var_kind == JS_VAR_CATCH ? "catch" :
+ (vd->var_kind == JS_VAR_FUNCTION_DECL ||
+ vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) ? "function" :
+ vd->is_const ? "const" :
+ vd->is_lexical ? "let" : "var",
+ JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), vd->var_name));
+ if (vd->scope_level)
+ printf(" [level:%d next:%d]", vd->scope_level, vd->scope_next);
+ printf("\n");
+ }
+ }
+ if (b->closure_var_count) {
+ printf(" closure vars:\n");
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ printf("%5d: %s %s:%s%d %s\n", i,
+ JS_AtomGetStr(ctx, atom_buf, sizeof(atom_buf), cv->var_name),
+ cv->is_local ? "local" : "parent",
+ cv->is_arg ? "arg" : "loc", cv->var_idx,
+ cv->is_const ? "const" :
+ cv->is_lexical ? "let" : "var");
+ }
+ }
+ printf(" stack_size: %d\n", b->stack_size);
+ printf(" opcodes:\n");
+ dump_byte_code(ctx, 3, b->byte_code_buf, b->byte_code_len,
+ b->vardefs, b->arg_count,
+ b->vardefs ? b->vardefs + b->arg_count : NULL, b->var_count,
+ b->closure_var, b->closure_var_count,
+ b->cpool, b->cpool_count,
+ b->has_debug ? b->debug.source : NULL,
+ b->has_debug ? b->debug.line_num : -1, NULL, b);
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 32)
+ if (b->has_debug)
+ dump_pc2line(ctx, b->debug.pc2line_buf, b->debug.pc2line_len, b->debug.line_num);
+#endif
+ printf("\n");
+}
+#endif
+
+static int add_closure_var(JSContext *ctx, JSFunctionDef *s,
+ BOOL is_local, BOOL is_arg,
+ int var_idx, JSAtom var_name,
+ BOOL is_const, BOOL is_lexical,
+ JSVarKindEnum var_kind)
+{
+ JSClosureVar *cv;
+
+ /* the closure variable indexes are currently stored on 16 bits */
+ if (s->closure_var_count >= JS_MAX_LOCAL_VARS) {
+ JS_ThrowInternalError(ctx, "too many closure variables");
+ return -1;
+ }
+
+ if (js_resize_array(ctx, (void **)&s->closure_var,
+ sizeof(s->closure_var[0]),
+ &s->closure_var_size, s->closure_var_count + 1))
+ return -1;
+ cv = &s->closure_var[s->closure_var_count++];
+ cv->is_local = is_local;
+ cv->is_arg = is_arg;
+ cv->is_const = is_const;
+ cv->is_lexical = is_lexical;
+ cv->var_kind = var_kind;
+ cv->var_idx = var_idx;
+ cv->var_name = JS_DupAtom(ctx, var_name);
+ return s->closure_var_count - 1;
+}
+
+static int find_closure_var(JSContext *ctx, JSFunctionDef *s,
+ JSAtom var_name)
+{
+ int i;
+ for(i = 0; i < s->closure_var_count; i++) {
+ JSClosureVar *cv = &s->closure_var[i];
+ if (cv->var_name == var_name)
+ return i;
+ }
+ return -1;
+}
+
+/* 'fd' must be a parent of 's'. Create in 's' a closure referencing a
+ local variable (is_local = TRUE) or a closure (is_local = FALSE) in
+ 'fd' */
+static int get_closure_var2(JSContext *ctx, JSFunctionDef *s,
+ JSFunctionDef *fd, BOOL is_local,
+ BOOL is_arg, int var_idx, JSAtom var_name,
+ BOOL is_const, BOOL is_lexical,
+ JSVarKindEnum var_kind)
+{
+ int i;
+
+ if (fd != s->parent) {
+ var_idx = get_closure_var2(ctx, s->parent, fd, is_local,
+ is_arg, var_idx, var_name,
+ is_const, is_lexical, var_kind);
+ if (var_idx < 0)
+ return -1;
+ is_local = FALSE;
+ }
+ for(i = 0; i < s->closure_var_count; i++) {
+ JSClosureVar *cv = &s->closure_var[i];
+ if (cv->var_idx == var_idx && cv->is_arg == is_arg &&
+ cv->is_local == is_local)
+ return i;
+ }
+ return add_closure_var(ctx, s, is_local, is_arg, var_idx, var_name,
+ is_const, is_lexical, var_kind);
+}
+
+static int get_closure_var(JSContext *ctx, JSFunctionDef *s,
+ JSFunctionDef *fd, BOOL is_arg,
+ int var_idx, JSAtom var_name,
+ BOOL is_const, BOOL is_lexical,
+ JSVarKindEnum var_kind)
+{
+ return get_closure_var2(ctx, s, fd, TRUE, is_arg,
+ var_idx, var_name, is_const, is_lexical,
+ var_kind);
+}
+
+static int get_with_scope_opcode(int op)
+{
+ if (op == OP_scope_get_var_undef)
+ return OP_with_get_var;
+ else
+ return OP_with_get_var + (op - OP_scope_get_var);
+}
+
+static BOOL can_opt_put_ref_value(const uint8_t *bc_buf, int pos)
+{
+ int opcode = bc_buf[pos];
+ return (bc_buf[pos + 1] == OP_put_ref_value &&
+ (opcode == OP_insert3 ||
+ opcode == OP_perm4 ||
+ opcode == OP_nop ||
+ opcode == OP_rot3l));
+}
+
+static BOOL can_opt_put_global_ref_value(const uint8_t *bc_buf, int pos)
+{
+ int opcode = bc_buf[pos];
+ return (bc_buf[pos + 1] == OP_put_ref_value &&
+ (opcode == OP_insert3 ||
+ opcode == OP_perm4 ||
+ opcode == OP_nop ||
+ opcode == OP_rot3l));
+}
+
+static int optimize_scope_make_ref(JSContext *ctx, JSFunctionDef *s,
+ DynBuf *bc, uint8_t *bc_buf,
+ LabelSlot *ls, int pos_next,
+ int get_op, int var_idx)
+{
+ int label_pos, end_pos, pos;
+
+ /* XXX: should optimize `loc(a) += expr` as `expr add_loc(a)`
+ but only if expr does not modify `a`.
+ should scan the code between pos_next and label_pos
+ for operations that can potentially change `a`:
+ OP_scope_make_ref(a), function calls, jumps and gosub.
+ */
+ /* replace the reference get/put with normal variable
+ accesses */
+ if (bc_buf[pos_next] == OP_get_ref_value) {
+ dbuf_putc(bc, get_op);
+ dbuf_put_u16(bc, var_idx);
+ pos_next++;
+ }
+ /* remove the OP_label to make room for replacement */
+ /* label should have a refcount of 0 anyway */
+ /* XXX: should avoid this patch by inserting nops in phase 1 */
+ label_pos = ls->pos;
+ pos = label_pos - 5;
+ assert(bc_buf[pos] == OP_label);
+ /* label points to an instruction pair:
+ - insert3 / put_ref_value
+ - perm4 / put_ref_value
+ - rot3l / put_ref_value
+ - nop / put_ref_value
+ */
+ end_pos = label_pos + 2;
+ if (bc_buf[label_pos] == OP_insert3)
+ bc_buf[pos++] = OP_dup;
+ bc_buf[pos] = get_op + 1;
+ put_u16(bc_buf + pos + 1, var_idx);
+ pos += 3;
+ /* pad with OP_nop */
+ while (pos < end_pos)
+ bc_buf[pos++] = OP_nop;
+ return pos_next;
+}
+
+static int optimize_scope_make_global_ref(JSContext *ctx, JSFunctionDef *s,
+ DynBuf *bc, uint8_t *bc_buf,
+ LabelSlot *ls, int pos_next,
+ JSAtom var_name)
+{
+ int label_pos, end_pos, pos, op;
+ BOOL is_strict;
+ is_strict = ((s->js_mode & JS_MODE_STRICT) != 0);
+
+ /* replace the reference get/put with normal variable
+ accesses */
+ if (is_strict) {
+ /* need to check if the variable exists before evaluating the right
+ expression */
+ /* XXX: need an extra OP_true if destructuring an array */
+ dbuf_putc(bc, OP_check_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ } else {
+ /* XXX: need 2 extra OP_true if destructuring an array */
+ }
+ if (bc_buf[pos_next] == OP_get_ref_value) {
+ dbuf_putc(bc, OP_get_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ pos_next++;
+ }
+ /* remove the OP_label to make room for replacement */
+ /* label should have a refcount of 0 anyway */
+ /* XXX: should have emitted several OP_nop to avoid this kludge */
+ label_pos = ls->pos;
+ pos = label_pos - 5;
+ assert(bc_buf[pos] == OP_label);
+ end_pos = label_pos + 2;
+ op = bc_buf[label_pos];
+ if (is_strict) {
+ if (op != OP_nop) {
+ switch(op) {
+ case OP_insert3:
+ op = OP_insert2;
+ break;
+ case OP_perm4:
+ op = OP_perm3;
+ break;
+ case OP_rot3l:
+ op = OP_swap;
+ break;
+ default:
+ abort();
+ }
+ bc_buf[pos++] = op;
+ }
+ } else {
+ if (op == OP_insert3)
+ bc_buf[pos++] = OP_dup;
+ }
+ if (is_strict) {
+ bc_buf[pos] = OP_put_var_strict;
+ /* XXX: need 1 extra OP_drop if destructuring an array */
+ } else {
+ bc_buf[pos] = OP_put_var;
+ /* XXX: need 2 extra OP_drop if destructuring an array */
+ }
+ put_u32(bc_buf + pos + 1, JS_DupAtom(ctx, var_name));
+ pos += 5;
+ /* pad with OP_nop */
+ while (pos < end_pos)
+ bc_buf[pos++] = OP_nop;
+ return pos_next;
+}
+
+static int add_var_this(JSContext *ctx, JSFunctionDef *fd)
+{
+ int idx;
+ idx = add_var(ctx, fd, JS_ATOM_this);
+ if (idx >= 0 && fd->is_derived_class_constructor) {
+ JSVarDef *vd = &fd->vars[idx];
+ /* XXX: should have is_this flag or var type */
+ vd->is_lexical = 1; /* used to trigger 'uninitialized' checks
+ in a derived class constructor */
+ }
+ return idx;
+}
+
+static int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,
+ JSAtom var_name)
+{
+ int var_idx;
+
+ if (!s->has_this_binding)
+ return -1;
+ switch(var_name) {
+ case JS_ATOM_home_object:
+ /* 'home_object' pseudo variable */
+ if (s->home_object_var_idx < 0)
+ s->home_object_var_idx = add_var(ctx, s, var_name);
+ var_idx = s->home_object_var_idx;
+ break;
+ case JS_ATOM_this_active_func:
+ /* 'this.active_func' pseudo variable */
+ if (s->this_active_func_var_idx < 0)
+ s->this_active_func_var_idx = add_var(ctx, s, var_name);
+ var_idx = s->this_active_func_var_idx;
+ break;
+ case JS_ATOM_new_target:
+ /* 'new.target' pseudo variable */
+ if (s->new_target_var_idx < 0)
+ s->new_target_var_idx = add_var(ctx, s, var_name);
+ var_idx = s->new_target_var_idx;
+ break;
+ case JS_ATOM_this:
+ /* 'this' pseudo variable */
+ if (s->this_var_idx < 0)
+ s->this_var_idx = add_var_this(ctx, s);
+ var_idx = s->this_var_idx;
+ break;
+ default:
+ var_idx = -1;
+ break;
+ }
+ return var_idx;
+}
+
+/* test if 'var_name' is in the variable object on the stack. If is it
+ the case, handle it and jump to 'label_done' */
+static void var_object_test(JSContext *ctx, JSFunctionDef *s,
+ JSAtom var_name, int op, DynBuf *bc,
+ int *plabel_done, BOOL is_with)
+{
+ dbuf_putc(bc, get_with_scope_opcode(op));
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ *plabel_done = new_label_fd(s, *plabel_done);
+ dbuf_put_u32(bc, *plabel_done);
+ dbuf_putc(bc, is_with);
+ update_label(s, *plabel_done, 1);
+ s->jump_size++;
+}
+
+/* return the position of the next opcode */
+static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
+ JSAtom var_name, int scope_level, int op,
+ DynBuf *bc, uint8_t *bc_buf,
+ LabelSlot *ls, int pos_next)
+{
+ int idx, var_idx, is_put;
+ int label_done;
+ JSFunctionDef *fd;
+ JSVarDef *vd;
+ BOOL is_pseudo_var, is_arg_scope;
+
+ label_done = -1;
+
+ /* XXX: could be simpler to use a specific function to
+ resolve the pseudo variables */
+ is_pseudo_var = (var_name == JS_ATOM_home_object ||
+ var_name == JS_ATOM_this_active_func ||
+ var_name == JS_ATOM_new_target ||
+ var_name == JS_ATOM_this);
+
+ /* resolve local scoped variables */
+ var_idx = -1;
+ for (idx = s->scopes[scope_level].first; idx >= 0;) {
+ vd = &s->vars[idx];
+ if (vd->var_name == var_name) {
+ if (op == OP_scope_put_var || op == OP_scope_make_ref) {
+ if (vd->is_const) {
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ goto done;
+ }
+ }
+ var_idx = idx;
+ break;
+ } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
+ }
+ idx = vd->scope_next;
+ }
+ is_arg_scope = (idx == ARG_SCOPE_END);
+ if (var_idx < 0) {
+ /* argument scope: variables are not visible but pseudo
+ variables are visible */
+ if (!is_arg_scope) {
+ var_idx = find_var(ctx, s, var_name);
+ }
+
+ if (var_idx < 0 && is_pseudo_var)
+ var_idx = resolve_pseudo_var(ctx, s, var_name);
+
+ if (var_idx < 0 && var_name == JS_ATOM_arguments &&
+ s->has_arguments_binding) {
+ /* 'arguments' pseudo variable */
+ var_idx = add_arguments_var(ctx, s);
+ }
+ if (var_idx < 0 && s->is_func_expr && var_name == s->func_name) {
+ /* add a new variable with the function name */
+ var_idx = add_func_var(ctx, s, var_name);
+ }
+ }
+ if (var_idx >= 0) {
+ if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
+ !(var_idx & ARGUMENT_VAR_OFFSET) &&
+ s->vars[var_idx].is_const) {
+ /* only happens when assigning a function expression name
+ in strict mode */
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ goto done;
+ }
+ /* OP_scope_put_var_init is only used to initialize a
+ lexical variable, so it is never used in a with or var object. It
+ can be used with a closure (module global variable case). */
+ switch (op) {
+ case OP_scope_make_ref:
+ if (!(var_idx & ARGUMENT_VAR_OFFSET) &&
+ s->vars[var_idx].var_kind == JS_VAR_FUNCTION_NAME) {
+ /* Create a dummy object reference for the func_var */
+ dbuf_putc(bc, OP_object);
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, var_idx);
+ dbuf_putc(bc, OP_define_field);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, OP_push_atom_value);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ } else
+ if (label_done == -1 && can_opt_put_ref_value(bc_buf, ls->pos)) {
+ int get_op;
+ if (var_idx & ARGUMENT_VAR_OFFSET) {
+ get_op = OP_get_arg;
+ var_idx -= ARGUMENT_VAR_OFFSET;
+ } else {
+ if (s->vars[var_idx].is_lexical)
+ get_op = OP_get_loc_check;
+ else
+ get_op = OP_get_loc;
+ }
+ pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
+ pos_next, get_op, var_idx);
+ } else {
+ /* Create a dummy object with a named slot that is
+ a reference to the local variable */
+ if (var_idx & ARGUMENT_VAR_OFFSET) {
+ dbuf_putc(bc, OP_make_arg_ref);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
+ } else {
+ dbuf_putc(bc, OP_make_loc_ref);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_put_u16(bc, var_idx);
+ }
+ }
+ break;
+ case OP_scope_get_ref:
+ dbuf_putc(bc, OP_undefined);
+ /* fall thru */
+ case OP_scope_get_var_undef:
+ case OP_scope_get_var:
+ case OP_scope_put_var:
+ case OP_scope_put_var_init:
+ is_put = (op == OP_scope_put_var || op == OP_scope_put_var_init);
+ if (var_idx & ARGUMENT_VAR_OFFSET) {
+ dbuf_putc(bc, OP_get_arg + is_put);
+ dbuf_put_u16(bc, var_idx - ARGUMENT_VAR_OFFSET);
+ } else {
+ if (is_put) {
+ if (s->vars[var_idx].is_lexical) {
+ if (op == OP_scope_put_var_init) {
+ /* 'this' can only be initialized once */
+ if (var_name == JS_ATOM_this)
+ dbuf_putc(bc, OP_put_loc_check_init);
+ else
+ dbuf_putc(bc, OP_put_loc);
+ } else {
+ dbuf_putc(bc, OP_put_loc_check);
+ }
+ } else {
+ dbuf_putc(bc, OP_put_loc);
+ }
+ } else {
+ if (s->vars[var_idx].is_lexical) {
+ dbuf_putc(bc, OP_get_loc_check);
+ } else {
+ dbuf_putc(bc, OP_get_loc);
+ }
+ }
+ dbuf_put_u16(bc, var_idx);
+ }
+ break;
+ case OP_scope_delete_var:
+ dbuf_putc(bc, OP_push_false);
+ break;
+ }
+ goto done;
+ }
+ /* check eval object */
+ if (!is_arg_scope && s->var_object_idx >= 0 && !is_pseudo_var) {
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, s->var_object_idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
+ }
+ /* check eval object in argument scope */
+ if (s->arg_var_object_idx >= 0 && !is_pseudo_var) {
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, s->arg_var_object_idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
+ }
+
+ /* check parent scopes */
+ for (fd = s; fd->parent;) {
+ scope_level = fd->parent_scope_level;
+ fd = fd->parent;
+ for (idx = fd->scopes[scope_level].first; idx >= 0;) {
+ vd = &fd->vars[idx];
+ if (vd->var_name == var_name) {
+ if (op == OP_scope_put_var || op == OP_scope_make_ref) {
+ if (vd->is_const) {
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ goto done;
+ }
+ }
+ var_idx = idx;
+ break;
+ } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
+ vd->is_captured = 1;
+ idx = get_closure_var(ctx, s, fd, FALSE, idx, vd->var_name, FALSE, FALSE, JS_VAR_NORMAL);
+ if (idx >= 0) {
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
+ }
+ }
+ idx = vd->scope_next;
+ }
+ is_arg_scope = (idx == ARG_SCOPE_END);
+ if (var_idx >= 0)
+ break;
+
+ if (!is_arg_scope) {
+ var_idx = find_var(ctx, fd, var_name);
+ if (var_idx >= 0)
+ break;
+ }
+ if (is_pseudo_var) {
+ var_idx = resolve_pseudo_var(ctx, fd, var_name);
+ if (var_idx >= 0)
+ break;
+ }
+ if (var_name == JS_ATOM_arguments && fd->has_arguments_binding) {
+ var_idx = add_arguments_var(ctx, fd);
+ break;
+ }
+ if (fd->is_func_expr && fd->func_name == var_name) {
+ /* add a new variable with the function name */
+ var_idx = add_func_var(ctx, fd, var_name);
+ break;
+ }
+
+ /* check eval object */
+ if (!is_arg_scope && fd->var_object_idx >= 0 && !is_pseudo_var) {
+ vd = &fd->vars[fd->var_object_idx];
+ vd->is_captured = 1;
+ idx = get_closure_var(ctx, s, fd, FALSE,
+ fd->var_object_idx, vd->var_name,
+ FALSE, FALSE, JS_VAR_NORMAL);
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
+ }
+
+ /* check eval object in argument scope */
+ if (fd->arg_var_object_idx >= 0 && !is_pseudo_var) {
+ vd = &fd->vars[fd->arg_var_object_idx];
+ vd->is_captured = 1;
+ idx = get_closure_var(ctx, s, fd, FALSE,
+ fd->arg_var_object_idx, vd->var_name,
+ FALSE, FALSE, JS_VAR_NORMAL);
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 0);
+ }
+
+ if (fd->is_eval)
+ break; /* it it necessarily the top level function */
+ }
+
+ /* check direct eval scope (in the closure of the eval function
+ which is necessarily at the top level) */
+ if (!fd)
+ fd = s;
+ if (var_idx < 0 && fd->is_eval) {
+ int idx1;
+ for (idx1 = 0; idx1 < fd->closure_var_count; idx1++) {
+ JSClosureVar *cv = &fd->closure_var[idx1];
+ if (var_name == cv->var_name) {
+ if (fd != s) {
+ idx = get_closure_var2(ctx, s, fd,
+ FALSE,
+ cv->is_arg, idx1,
+ cv->var_name, cv->is_const,
+ cv->is_lexical, cv->var_kind);
+ } else {
+ idx = idx1;
+ }
+ goto has_idx;
+ } else if ((cv->var_name == JS_ATOM__var_ ||
+ cv->var_name == JS_ATOM__arg_var_ ||
+ cv->var_name == JS_ATOM__with_) && !is_pseudo_var) {
+ int is_with = (cv->var_name == JS_ATOM__with_);
+ if (fd != s) {
+ idx = get_closure_var2(ctx, s, fd,
+ FALSE,
+ cv->is_arg, idx1,
+ cv->var_name, FALSE, FALSE,
+ JS_VAR_NORMAL);
+ } else {
+ idx = idx1;
+ }
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, is_with);
+ }
+ }
+ }
+
+ if (var_idx >= 0) {
+ /* find the corresponding closure variable */
+ if (var_idx & ARGUMENT_VAR_OFFSET) {
+ fd->args[var_idx - ARGUMENT_VAR_OFFSET].is_captured = 1;
+ idx = get_closure_var(ctx, s, fd,
+ TRUE, var_idx - ARGUMENT_VAR_OFFSET,
+ var_name, FALSE, FALSE, JS_VAR_NORMAL);
+ } else {
+ fd->vars[var_idx].is_captured = 1;
+ idx = get_closure_var(ctx, s, fd,
+ FALSE, var_idx,
+ var_name,
+ fd->vars[var_idx].is_const,
+ fd->vars[var_idx].is_lexical,
+ fd->vars[var_idx].var_kind);
+ }
+ if (idx >= 0) {
+ has_idx:
+ if ((op == OP_scope_put_var || op == OP_scope_make_ref) &&
+ s->closure_var[idx].is_const) {
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ goto done;
+ }
+ switch (op) {
+ case OP_scope_make_ref:
+ if (s->closure_var[idx].var_kind == JS_VAR_FUNCTION_NAME) {
+ /* Create a dummy object reference for the func_var */
+ dbuf_putc(bc, OP_object);
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ dbuf_putc(bc, OP_define_field);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, OP_push_atom_value);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ } else
+ if (label_done == -1 &&
+ can_opt_put_ref_value(bc_buf, ls->pos)) {
+ int get_op;
+ if (s->closure_var[idx].is_lexical)
+ get_op = OP_get_var_ref_check;
+ else
+ get_op = OP_get_var_ref;
+ pos_next = optimize_scope_make_ref(ctx, s, bc, bc_buf, ls,
+ pos_next,
+ get_op, idx);
+ } else {
+ /* Create a dummy object with a named slot that is
+ a reference to the closure variable */
+ dbuf_putc(bc, OP_make_var_ref_ref);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_put_u16(bc, idx);
+ }
+ break;
+ case OP_scope_get_ref:
+ /* XXX: should create a dummy object with a named slot that is
+ a reference to the closure variable */
+ dbuf_putc(bc, OP_undefined);
+ /* fall thru */
+ case OP_scope_get_var_undef:
+ case OP_scope_get_var:
+ case OP_scope_put_var:
+ case OP_scope_put_var_init:
+ is_put = (op == OP_scope_put_var ||
+ op == OP_scope_put_var_init);
+ if (is_put) {
+ if (s->closure_var[idx].is_lexical) {
+ if (op == OP_scope_put_var_init) {
+ /* 'this' can only be initialized once */
+ if (var_name == JS_ATOM_this)
+ dbuf_putc(bc, OP_put_var_ref_check_init);
+ else
+ dbuf_putc(bc, OP_put_var_ref);
+ } else {
+ dbuf_putc(bc, OP_put_var_ref_check);
+ }
+ } else {
+ dbuf_putc(bc, OP_put_var_ref);
+ }
+ } else {
+ if (s->closure_var[idx].is_lexical) {
+ dbuf_putc(bc, OP_get_var_ref_check);
+ } else {
+ dbuf_putc(bc, OP_get_var_ref);
+ }
+ }
+ dbuf_put_u16(bc, idx);
+ break;
+ case OP_scope_delete_var:
+ dbuf_putc(bc, OP_push_false);
+ break;
+ }
+ goto done;
+ }
+ }
+
+ /* global variable access */
+
+ switch (op) {
+ case OP_scope_make_ref:
+ if (label_done == -1 && can_opt_put_global_ref_value(bc_buf, ls->pos)) {
+ pos_next = optimize_scope_make_global_ref(ctx, s, bc, bc_buf, ls,
+ pos_next, var_name);
+ } else {
+ dbuf_putc(bc, OP_make_var_ref);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ }
+ break;
+ case OP_scope_get_ref:
+ /* XXX: should create a dummy object with a named slot that is
+ a reference to the global variable */
+ dbuf_putc(bc, OP_undefined);
+ dbuf_putc(bc, OP_get_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ break;
+ case OP_scope_get_var_undef:
+ case OP_scope_get_var:
+ case OP_scope_put_var:
+ dbuf_putc(bc, OP_get_var_undef + (op - OP_scope_get_var_undef));
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ break;
+ case OP_scope_put_var_init:
+ dbuf_putc(bc, OP_put_var_init);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ break;
+ case OP_scope_delete_var:
+ dbuf_putc(bc, OP_delete_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ break;
+ }
+done:
+ if (label_done >= 0) {
+ dbuf_putc(bc, OP_label);
+ dbuf_put_u32(bc, label_done);
+ s->label_slots[label_done].pos2 = bc->size;
+ }
+ return pos_next;
+}
+
+/* search in all scopes */
+static int find_private_class_field_all(JSContext *ctx, JSFunctionDef *fd,
+ JSAtom name, int scope_level)
+{
+ int idx;
+
+ idx = fd->scopes[scope_level].first;
+ while (idx >= 0) {
+ if (fd->vars[idx].var_name == name)
+ return idx;
+ idx = fd->vars[idx].scope_next;
+ }
+ return -1;
+}
+
+static void get_loc_or_ref(DynBuf *bc, BOOL is_ref, int idx)
+{
+ /* if the field is not initialized, the error is catched when
+ accessing it */
+ if (is_ref)
+ dbuf_putc(bc, OP_get_var_ref);
+ else
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, idx);
+}
+
+static int resolve_scope_private_field1(JSContext *ctx,
+ BOOL *pis_ref, int *pvar_kind,
+ JSFunctionDef *s,
+ JSAtom var_name, int scope_level)
+{
+ int idx, var_kind;
+ JSFunctionDef *fd;
+ BOOL is_ref;
+
+ fd = s;
+ is_ref = FALSE;
+ for(;;) {
+ idx = find_private_class_field_all(ctx, fd, var_name, scope_level);
+ if (idx >= 0) {
+ var_kind = fd->vars[idx].var_kind;
+ if (is_ref) {
+ idx = get_closure_var(ctx, s, fd, FALSE, idx, var_name,
+ TRUE, TRUE, JS_VAR_NORMAL);
+ if (idx < 0)
+ return -1;
+ }
+ break;
+ }
+ scope_level = fd->parent_scope_level;
+ if (!fd->parent) {
+ if (fd->is_eval) {
+ /* closure of the eval function (top level) */
+ for (idx = 0; idx < fd->closure_var_count; idx++) {
+ JSClosureVar *cv = &fd->closure_var[idx];
+ if (cv->var_name == var_name) {
+ var_kind = cv->var_kind;
+ is_ref = TRUE;
+ if (fd != s) {
+ idx = get_closure_var2(ctx, s, fd,
+ FALSE,
+ cv->is_arg, idx,
+ cv->var_name, cv->is_const,
+ cv->is_lexical,
+ cv->var_kind);
+ if (idx < 0)
+ return -1;
+ }
+ goto done;
+ }
+ }
+ }
+ /* XXX: no line number info */
+ JS_ThrowSyntaxErrorAtom(ctx, "undefined private field '%s'",
+ var_name);
+ return -1;
+ } else {
+ fd = fd->parent;
+ }
+ is_ref = TRUE;
+ }
+ done:
+ *pis_ref = is_ref;
+ *pvar_kind = var_kind;
+ return idx;
+}
+
+/* return 0 if OK or -1 if the private field could not be resolved */
+static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s,
+ JSAtom var_name, int scope_level, int op,
+ DynBuf *bc)
+{
+ int idx, var_kind;
+ BOOL is_ref;
+
+ idx = resolve_scope_private_field1(ctx, &is_ref, &var_kind, s,
+ var_name, scope_level);
+ if (idx < 0)
+ return -1;
+ assert(var_kind != JS_VAR_NORMAL);
+ switch (op) {
+ case OP_scope_get_private_field:
+ case OP_scope_get_private_field2:
+ switch(var_kind) {
+ case JS_VAR_PRIVATE_FIELD:
+ if (op == OP_scope_get_private_field2)
+ dbuf_putc(bc, OP_dup);
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_get_private_field);
+ break;
+ case JS_VAR_PRIVATE_METHOD:
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_check_brand);
+ if (op != OP_scope_get_private_field2)
+ dbuf_putc(bc, OP_nip);
+ break;
+ case JS_VAR_PRIVATE_GETTER:
+ case JS_VAR_PRIVATE_GETTER_SETTER:
+ if (op == OP_scope_get_private_field2)
+ dbuf_putc(bc, OP_dup);
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_check_brand);
+ dbuf_putc(bc, OP_call_method);
+ dbuf_put_u16(bc, 0);
+ break;
+ case JS_VAR_PRIVATE_SETTER:
+ /* XXX: add clearer error message */
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ break;
+ default:
+ abort();
+ }
+ break;
+ case OP_scope_put_private_field:
+ switch(var_kind) {
+ case JS_VAR_PRIVATE_FIELD:
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_put_private_field);
+ break;
+ case JS_VAR_PRIVATE_METHOD:
+ case JS_VAR_PRIVATE_GETTER:
+ /* XXX: add clearer error message */
+ dbuf_putc(bc, OP_throw_error);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, var_name));
+ dbuf_putc(bc, JS_THROW_VAR_RO);
+ break;
+ case JS_VAR_PRIVATE_SETTER:
+ case JS_VAR_PRIVATE_GETTER_SETTER:
+ {
+ JSAtom setter_name = get_private_setter_name(ctx, var_name);
+ if (setter_name == JS_ATOM_NULL)
+ return -1;
+ idx = resolve_scope_private_field1(ctx, &is_ref,
+ &var_kind, s,
+ setter_name, scope_level);
+ JS_FreeAtom(ctx, setter_name);
+ if (idx < 0)
+ return -1;
+ assert(var_kind == JS_VAR_PRIVATE_SETTER);
+ get_loc_or_ref(bc, is_ref, idx);
+ dbuf_putc(bc, OP_swap);
+ /* obj func value */
+ dbuf_putc(bc, OP_rot3r);
+ /* value obj func */
+ dbuf_putc(bc, OP_check_brand);
+ dbuf_putc(bc, OP_rot3l);
+ /* obj func value */
+ dbuf_putc(bc, OP_call_method);
+ dbuf_put_u16(bc, 1);
+ }
+ break;
+ default:
+ abort();
+ }
+ break;
+ default:
+ abort();
+ }
+ return 0;
+}
+
+static void mark_eval_captured_variables(JSContext *ctx, JSFunctionDef *s,
+ int scope_level)
+{
+ int idx;
+ JSVarDef *vd;
+
+ for (idx = s->scopes[scope_level].first; idx >= 0;) {
+ vd = &s->vars[idx];
+ vd->is_captured = 1;
+ idx = vd->scope_next;
+ }
+}
+
+/* XXX: should handle the argument scope generically */
+static BOOL is_var_in_arg_scope(const JSVarDef *vd)
+{
+ return (vd->var_name == JS_ATOM_home_object ||
+ vd->var_name == JS_ATOM_this_active_func ||
+ vd->var_name == JS_ATOM_new_target ||
+ vd->var_name == JS_ATOM_this ||
+ vd->var_name == JS_ATOM__arg_var_ ||
+ vd->var_kind == JS_VAR_FUNCTION_NAME);
+}
+
+static void add_eval_variables(JSContext *ctx, JSFunctionDef *s)
+{
+ JSFunctionDef *fd;
+ JSVarDef *vd;
+ int i, scope_level, scope_idx;
+ BOOL has_arguments_binding, has_this_binding, is_arg_scope;
+
+ /* in non strict mode, variables are created in the caller's
+ environment object */
+ if (!s->is_eval && !(s->js_mode & JS_MODE_STRICT)) {
+ s->var_object_idx = add_var(ctx, s, JS_ATOM__var_);
+ if (s->has_parameter_expressions) {
+ /* an additional variable object is needed for the
+ argument scope */
+ s->arg_var_object_idx = add_var(ctx, s, JS_ATOM__arg_var_);
+ }
+ }
+
+ /* eval can potentially use 'arguments' so we must define it */
+ has_this_binding = s->has_this_binding;
+ if (has_this_binding) {
+ if (s->this_var_idx < 0)
+ s->this_var_idx = add_var_this(ctx, s);
+ if (s->new_target_var_idx < 0)
+ s->new_target_var_idx = add_var(ctx, s, JS_ATOM_new_target);
+ if (s->is_derived_class_constructor && s->this_active_func_var_idx < 0)
+ s->this_active_func_var_idx = add_var(ctx, s, JS_ATOM_this_active_func);
+ if (s->has_home_object && s->home_object_var_idx < 0)
+ s->home_object_var_idx = add_var(ctx, s, JS_ATOM_home_object);
+ }
+ has_arguments_binding = s->has_arguments_binding;
+ if (has_arguments_binding) {
+ add_arguments_var(ctx, s);
+ /* also add an arguments binding in the argument scope to
+ raise an error if a direct eval in the argument scope tries
+ to redefine it */
+ if (s->has_parameter_expressions && !(s->js_mode & JS_MODE_STRICT))
+ add_arguments_arg(ctx, s);
+ }
+ if (s->is_func_expr && s->func_name != JS_ATOM_NULL)
+ add_func_var(ctx, s, s->func_name);
+
+ /* eval can use all the variables of the enclosing functions, so
+ they must be all put in the closure. The closure variables are
+ ordered by scope. It works only because no closure are created
+ before. */
+ assert(s->is_eval || s->closure_var_count == 0);
+
+ /* XXX: inefficient, but eval performance is less critical */
+ fd = s;
+ for(;;) {
+ scope_level = fd->parent_scope_level;
+ fd = fd->parent;
+ if (!fd)
+ break;
+ /* add 'this' if it was not previously added */
+ if (!has_this_binding && fd->has_this_binding) {
+ if (fd->this_var_idx < 0)
+ fd->this_var_idx = add_var_this(ctx, fd);
+ if (fd->new_target_var_idx < 0)
+ fd->new_target_var_idx = add_var(ctx, fd, JS_ATOM_new_target);
+ if (fd->is_derived_class_constructor && fd->this_active_func_var_idx < 0)
+ fd->this_active_func_var_idx = add_var(ctx, fd, JS_ATOM_this_active_func);
+ if (fd->has_home_object && fd->home_object_var_idx < 0)
+ fd->home_object_var_idx = add_var(ctx, fd, JS_ATOM_home_object);
+ has_this_binding = TRUE;
+ }
+ /* add 'arguments' if it was not previously added */
+ if (!has_arguments_binding && fd->has_arguments_binding) {
+ add_arguments_var(ctx, fd);
+ has_arguments_binding = TRUE;
+ }
+ /* add function name */
+ if (fd->is_func_expr && fd->func_name != JS_ATOM_NULL)
+ add_func_var(ctx, fd, fd->func_name);
+
+ /* add lexical variables */
+ scope_idx = fd->scopes[scope_level].first;
+ while (scope_idx >= 0) {
+ vd = &fd->vars[scope_idx];
+ vd->is_captured = 1;
+ get_closure_var(ctx, s, fd, FALSE, scope_idx,
+ vd->var_name, vd->is_const, vd->is_lexical, vd->var_kind);
+ scope_idx = vd->scope_next;
+ }
+ is_arg_scope = (scope_idx == ARG_SCOPE_END);
+ if (!is_arg_scope) {
+ /* add unscoped variables */
+ for(i = 0; i < fd->arg_count; i++) {
+ vd = &fd->args[i];
+ if (vd->var_name != JS_ATOM_NULL) {
+ get_closure_var(ctx, s, fd,
+ TRUE, i, vd->var_name, FALSE, FALSE,
+ JS_VAR_NORMAL);
+ }
+ }
+ for(i = 0; i < fd->var_count; i++) {
+ vd = &fd->vars[i];
+ /* do not close top level last result */
+ if (vd->scope_level == 0 &&
+ vd->var_name != JS_ATOM__ret_ &&
+ vd->var_name != JS_ATOM_NULL) {
+ get_closure_var(ctx, s, fd,
+ FALSE, i, vd->var_name, FALSE, FALSE,
+ JS_VAR_NORMAL);
+ }
+ }
+ } else {
+ for(i = 0; i < fd->var_count; i++) {
+ vd = &fd->vars[i];
+ /* do not close top level last result */
+ if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
+ get_closure_var(ctx, s, fd,
+ FALSE, i, vd->var_name, FALSE, FALSE,
+ JS_VAR_NORMAL);
+ }
+ }
+ }
+ if (fd->is_eval) {
+ int idx;
+ /* add direct eval variables (we are necessarily at the
+ top level) */
+ for (idx = 0; idx < fd->closure_var_count; idx++) {
+ JSClosureVar *cv = &fd->closure_var[idx];
+ get_closure_var2(ctx, s, fd,
+ FALSE, cv->is_arg,
+ idx, cv->var_name, cv->is_const,
+ cv->is_lexical, cv->var_kind);
+ }
+ }
+ }
+}
+
+static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
+ JSVarDef *vd, int var_idx)
+{
+ cv->is_local = TRUE;
+ cv->is_arg = FALSE;
+ cv->is_const = vd->is_const;
+ cv->is_lexical = vd->is_lexical;
+ cv->var_kind = vd->var_kind;
+ cv->var_idx = var_idx;
+ cv->var_name = JS_DupAtom(ctx, vd->var_name);
+}
+
+/* for direct eval compilation: add references to the variables of the
+ calling function */
+static warn_unused int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
+ JSFunctionBytecode *b, int scope_idx)
+{
+ int i, count;
+ JSVarDef *vd;
+ BOOL is_arg_scope;
+
+ count = b->arg_count + b->var_count + b->closure_var_count;
+ s->closure_var = NULL;
+ s->closure_var_count = 0;
+ s->closure_var_size = count;
+ if (count == 0)
+ return 0;
+ s->closure_var = js_malloc(ctx, sizeof(s->closure_var[0]) * count);
+ if (!s->closure_var)
+ return -1;
+ /* Add lexical variables in scope at the point of evaluation */
+ for (i = scope_idx; i >= 0;) {
+ vd = &b->vardefs[b->arg_count + i];
+ if (vd->scope_level > 0) {
+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
+ set_closure_from_var(ctx, cv, vd, i);
+ }
+ i = vd->scope_next;
+ }
+ is_arg_scope = (i == ARG_SCOPE_END);
+ if (!is_arg_scope) {
+ /* Add argument variables */
+ for(i = 0; i < b->arg_count; i++) {
+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
+ vd = &b->vardefs[i];
+ cv->is_local = TRUE;
+ cv->is_arg = TRUE;
+ cv->is_const = FALSE;
+ cv->is_lexical = FALSE;
+ cv->var_kind = JS_VAR_NORMAL;
+ cv->var_idx = i;
+ cv->var_name = JS_DupAtom(ctx, vd->var_name);
+ }
+ /* Add local non lexical variables */
+ for(i = 0; i < b->var_count; i++) {
+ vd = &b->vardefs[b->arg_count + i];
+ if (vd->scope_level == 0 && vd->var_name != JS_ATOM__ret_) {
+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
+ set_closure_from_var(ctx, cv, vd, i);
+ }
+ }
+ } else {
+ /* only add pseudo variables */
+ for(i = 0; i < b->var_count; i++) {
+ vd = &b->vardefs[b->arg_count + i];
+ if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) {
+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
+ set_closure_from_var(ctx, cv, vd, i);
+ }
+ }
+ }
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv0 = &b->closure_var[i];
+ JSClosureVar *cv = &s->closure_var[s->closure_var_count++];
+ cv->is_local = FALSE;
+ cv->is_arg = cv0->is_arg;
+ cv->is_const = cv0->is_const;
+ cv->is_lexical = cv0->is_lexical;
+ cv->var_kind = cv0->var_kind;
+ cv->var_idx = i;
+ cv->var_name = JS_DupAtom(ctx, cv0->var_name);
+ }
+ return 0;
+}
+
+typedef struct CodeContext {
+ const uint8_t *bc_buf; /* code buffer */
+ int bc_len; /* length of the code buffer */
+ int pos; /* position past the matched code pattern */
+ int line_num; /* last visited OP_line_num parameter or -1 */
+ int op;
+ int idx;
+ int label;
+ int val;
+ JSAtom atom;
+} CodeContext;
+
+#define M2(op1, op2) ((op1) | ((op2) << 8))
+#define M3(op1, op2, op3) ((op1) | ((op2) << 8) | ((op3) << 16))
+#define M4(op1, op2, op3, op4) ((op1) | ((op2) << 8) | ((op3) << 16) | ((op4) << 24))
+
+static BOOL code_match(CodeContext *s, int pos, ...)
+{
+ const uint8_t *tab = s->bc_buf;
+ int op, len, op1, line_num, pos_next;
+ va_list ap;
+ BOOL ret = FALSE;
+
+ line_num = -1;
+ va_start(ap, pos);
+
+ for(;;) {
+ op1 = va_arg(ap, int);
+ if (op1 == -1) {
+ s->pos = pos;
+ s->line_num = line_num;
+ ret = TRUE;
+ break;
+ }
+ for (;;) {
+ if (pos >= s->bc_len)
+ goto done;
+ op = tab[pos];
+ len = opcode_info[op].size;
+ pos_next = pos + len;
+ if (pos_next > s->bc_len)
+ goto done;
+ if (op == OP_line_num) {
+ line_num = get_u32(tab + pos + 1);
+ pos = pos_next;
+ } else {
+ break;
+ }
+ }
+ if (op != op1) {
+ if (op1 == (uint8_t)op1 || !op)
+ break;
+ if (op != (uint8_t)op1
+ && op != (uint8_t)(op1 >> 8)
+ && op != (uint8_t)(op1 >> 16)
+ && op != (uint8_t)(op1 >> 24)) {
+ break;
+ }
+ s->op = op;
+ }
+
+ pos++;
+ switch(opcode_info[op].fmt) {
+ case OP_FMT_loc8:
+ case OP_FMT_u8:
+ {
+ int idx = tab[pos];
+ int arg = va_arg(ap, int);
+ if (arg == -1) {
+ s->idx = idx;
+ } else {
+ if (arg != idx)
+ goto done;
+ }
+ break;
+ }
+ case OP_FMT_u16:
+ case OP_FMT_npop:
+ case OP_FMT_loc:
+ case OP_FMT_arg:
+ case OP_FMT_var_ref:
+ {
+ int idx = get_u16(tab + pos);
+ int arg = va_arg(ap, int);
+ if (arg == -1) {
+ s->idx = idx;
+ } else {
+ if (arg != idx)
+ goto done;
+ }
+ break;
+ }
+ case OP_FMT_i32:
+ case OP_FMT_u32:
+ case OP_FMT_label:
+ case OP_FMT_const:
+ {
+ s->label = get_u32(tab + pos);
+ break;
+ }
+ case OP_FMT_label_u16:
+ {
+ s->label = get_u32(tab + pos);
+ s->val = get_u16(tab + pos + 4);
+ break;
+ }
+ case OP_FMT_atom:
+ {
+ s->atom = get_u32(tab + pos);
+ break;
+ }
+ case OP_FMT_atom_u8:
+ {
+ s->atom = get_u32(tab + pos);
+ s->val = get_u8(tab + pos + 4);
+ break;
+ }
+ case OP_FMT_atom_u16:
+ {
+ s->atom = get_u32(tab + pos);
+ s->val = get_u16(tab + pos + 4);
+ break;
+ }
+ case OP_FMT_atom_label_u8:
+ {
+ s->atom = get_u32(tab + pos);
+ s->label = get_u32(tab + pos + 4);
+ s->val = get_u8(tab + pos + 8);
+ break;
+ }
+ default:
+ break;
+ }
+ pos = pos_next;
+ }
+ done:
+ va_end(ap);
+ return ret;
+}
+
+static void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc)
+{
+ int i, idx, label_next = -1;
+
+ /* add the hoisted functions in arguments and local variables */
+ for(i = 0; i < s->arg_count; i++) {
+ JSVarDef *vd = &s->args[i];
+ if (vd->func_pool_idx >= 0) {
+ dbuf_putc(bc, OP_fclosure);
+ dbuf_put_u32(bc, vd->func_pool_idx);
+ dbuf_putc(bc, OP_put_arg);
+ dbuf_put_u16(bc, i);
+ }
+ }
+ for(i = 0; i < s->var_count; i++) {
+ JSVarDef *vd = &s->vars[i];
+ if (vd->scope_level == 0 && vd->func_pool_idx >= 0) {
+ dbuf_putc(bc, OP_fclosure);
+ dbuf_put_u32(bc, vd->func_pool_idx);
+ dbuf_putc(bc, OP_put_loc);
+ dbuf_put_u16(bc, i);
+ }
+ }
+
+ /* the module global variables must be initialized before
+ evaluating the module so that the exported functions are
+ visible if there are cyclic module references */
+ if (s->module) {
+ label_next = new_label_fd(s, -1);
+
+ /* if 'this' is true, initialize the global variables and return */
+ dbuf_putc(bc, OP_push_this);
+ dbuf_putc(bc, OP_if_false);
+ dbuf_put_u32(bc, label_next);
+ update_label(s, label_next, 1);
+ s->jump_size++;
+ }
+
+ /* add the global variables (only happens if s->is_global_var is
+ true) */
+ for(i = 0; i < s->global_var_count; i++) {
+ JSGlobalVar *hf = &s->global_vars[i];
+ int has_closure = 0;
+ BOOL force_init = hf->force_init;
+ /* we are in an eval, so the closure contains all the
+ enclosing variables */
+ /* If the outer function has a variable environment,
+ create a property for the variable there */
+ for(idx = 0; idx < s->closure_var_count; idx++) {
+ JSClosureVar *cv = &s->closure_var[idx];
+ if (cv->var_name == hf->var_name) {
+ has_closure = 2;
+ force_init = FALSE;
+ break;
+ }
+ if (cv->var_name == JS_ATOM__var_ ||
+ cv->var_name == JS_ATOM__arg_var_) {
+ dbuf_putc(bc, OP_get_var_ref);
+ dbuf_put_u16(bc, idx);
+ has_closure = 1;
+ force_init = TRUE;
+ break;
+ }
+ }
+ if (!has_closure) {
+ int flags;
+
+ flags = 0;
+ if (s->eval_type != JS_EVAL_TYPE_GLOBAL)
+ flags |= JS_PROP_CONFIGURABLE;
+ if (hf->cpool_idx >= 0 && !hf->is_lexical) {
+ /* global function definitions need a specific handling */
+ dbuf_putc(bc, OP_fclosure);
+ dbuf_put_u32(bc, hf->cpool_idx);
+
+ dbuf_putc(bc, OP_define_func);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
+ dbuf_putc(bc, flags);
+
+ goto done_global_var;
+ } else {
+ if (hf->is_lexical) {
+ flags |= DEFINE_GLOBAL_LEX_VAR;
+ if (!hf->is_const)
+ flags |= JS_PROP_WRITABLE;
+ }
+ dbuf_putc(bc, OP_define_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
+ dbuf_putc(bc, flags);
+ }
+ }
+ if (hf->cpool_idx >= 0 || force_init) {
+ if (hf->cpool_idx >= 0) {
+ dbuf_putc(bc, OP_fclosure);
+ dbuf_put_u32(bc, hf->cpool_idx);
+ if (hf->var_name == JS_ATOM__default_) {
+ /* set default export function name */
+ dbuf_putc(bc, OP_set_name);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, JS_ATOM_default));
+ }
+ } else {
+ dbuf_putc(bc, OP_undefined);
+ }
+ if (has_closure == 2) {
+ dbuf_putc(bc, OP_put_var_ref);
+ dbuf_put_u16(bc, idx);
+ } else if (has_closure == 1) {
+ dbuf_putc(bc, OP_define_field);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
+ dbuf_putc(bc, OP_drop);
+ } else {
+ /* XXX: Check if variable is writable and enumerable */
+ dbuf_putc(bc, OP_put_var);
+ dbuf_put_u32(bc, JS_DupAtom(ctx, hf->var_name));
+ }
+ }
+ done_global_var:
+ JS_FreeAtom(ctx, hf->var_name);
+ }
+
+ if (s->module) {
+ dbuf_putc(bc, OP_return_undef);
+
+ dbuf_putc(bc, OP_label);
+ dbuf_put_u32(bc, label_next);
+ s->label_slots[label_next].pos2 = bc->size;
+ }
+
+ js_free(ctx, s->global_vars);
+ s->global_vars = NULL;
+ s->global_var_count = 0;
+ s->global_var_size = 0;
+}
+
+static int skip_dead_code(JSFunctionDef *s, const uint8_t *bc_buf, int bc_len,
+ int pos, int *linep)
+{
+ int op, len, label;
+
+ for (; pos < bc_len; pos += len) {
+ op = bc_buf[pos];
+ len = opcode_info[op].size;
+ if (op == OP_line_num) {
+ *linep = get_u32(bc_buf + pos + 1);
+ } else
+ if (op == OP_label) {
+ label = get_u32(bc_buf + pos + 1);
+ if (update_label(s, label, 0) > 0)
+ break;
+#if 0
+ if (s->label_slots[label].first_reloc) {
+ printf("line %d: unreferenced label %d:%d has relocations\n",
+ *linep, label, s->label_slots[label].pos2);
+ }
+#endif
+ assert(s->label_slots[label].first_reloc == NULL);
+ } else {
+ /* XXX: output a warning for unreachable code? */
+ JSAtom atom;
+ switch(opcode_info[op].fmt) {
+ case OP_FMT_label:
+ case OP_FMT_label_u16:
+ label = get_u32(bc_buf + pos + 1);
+ update_label(s, label, -1);
+ break;
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ label = get_u32(bc_buf + pos + 5);
+ update_label(s, label, -1);
+ /* fall thru */
+ case OP_FMT_atom:
+ case OP_FMT_atom_u8:
+ case OP_FMT_atom_u16:
+ atom = get_u32(bc_buf + pos + 1);
+ JS_FreeAtom(s->ctx, atom);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ return pos;
+}
+
+static int get_label_pos(JSFunctionDef *s, int label)
+{
+ int i, pos;
+ for (i = 0; i < 20; i++) {
+ pos = s->label_slots[label].pos;
+ for (;;) {
+ switch (s->byte_code.buf[pos]) {
+ case OP_line_num:
+ case OP_label:
+ pos += 5;
+ continue;
+ case OP_goto:
+ label = get_u32(s->byte_code.buf + pos + 1);
+ break;
+ default:
+ return pos;
+ }
+ break;
+ }
+ }
+ return pos;
+}
+
+/* convert global variable accesses to local variables or closure
+ variables when necessary */
+static warn_unused int resolve_variables(JSContext *ctx, JSFunctionDef *s)
+{
+ int pos, pos_next, bc_len, op, len, i, idx, line_num;
+ uint8_t *bc_buf;
+ JSAtom var_name;
+ DynBuf bc_out;
+ CodeContext cc;
+ int scope;
+
+ cc.bc_buf = bc_buf = s->byte_code.buf;
+ cc.bc_len = bc_len = s->byte_code.size;
+ js_dbuf_init(ctx, &bc_out);
+
+ /* first pass for runtime checks (must be done before the
+ variables are created) */
+ for(i = 0; i < s->global_var_count; i++) {
+ JSGlobalVar *hf = &s->global_vars[i];
+ int flags;
+
+ /* check if global variable (XXX: simplify) */
+ for(idx = 0; idx < s->closure_var_count; idx++) {
+ JSClosureVar *cv = &s->closure_var[idx];
+ if (cv->var_name == hf->var_name) {
+ if (s->eval_type == JS_EVAL_TYPE_DIRECT &&
+ cv->is_lexical) {
+ /* Check if a lexical variable is
+ redefined as 'var'. XXX: Could abort
+ compilation here, but for consistency
+ with the other checks, we delay the
+ error generation. */
+ dbuf_putc(&bc_out, OP_throw_error);
+ dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
+ dbuf_putc(&bc_out, JS_THROW_VAR_REDECL);
+ }
+ goto next;
+ }
+ if (cv->var_name == JS_ATOM__var_ ||
+ cv->var_name == JS_ATOM__arg_var_)
+ goto next;
+ }
+
+ dbuf_putc(&bc_out, OP_check_define_var);
+ dbuf_put_u32(&bc_out, JS_DupAtom(ctx, hf->var_name));
+ flags = 0;
+ if (hf->is_lexical)
+ flags |= DEFINE_GLOBAL_LEX_VAR;
+ if (hf->cpool_idx >= 0)
+ flags |= DEFINE_GLOBAL_FUNC_VAR;
+ dbuf_putc(&bc_out, flags);
+ next: ;
+ }
+
+ line_num = 0; /* avoid warning */
+ for (pos = 0; pos < bc_len; pos = pos_next) {
+ op = bc_buf[pos];
+ len = opcode_info[op].size;
+ pos_next = pos + len;
+ switch(op) {
+ case OP_line_num:
+ line_num = get_u32(bc_buf + pos + 1);
+ s->line_number_size++;
+ goto no_change;
+
+ case OP_eval: /* convert scope index to adjusted variable index */
+ {
+ int call_argc = get_u16(bc_buf + pos + 1);
+ scope = get_u16(bc_buf + pos + 1 + 2);
+ mark_eval_captured_variables(ctx, s, scope);
+ dbuf_putc(&bc_out, op);
+ dbuf_put_u16(&bc_out, call_argc);
+ dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
+ }
+ break;
+ case OP_apply_eval: /* convert scope index to adjusted variable index */
+ scope = get_u16(bc_buf + pos + 1);
+ mark_eval_captured_variables(ctx, s, scope);
+ dbuf_putc(&bc_out, op);
+ dbuf_put_u16(&bc_out, s->scopes[scope].first + 1);
+ break;
+ case OP_scope_get_var_undef:
+ case OP_scope_get_var:
+ case OP_scope_put_var:
+ case OP_scope_delete_var:
+ case OP_scope_get_ref:
+ case OP_scope_put_var_init:
+ var_name = get_u32(bc_buf + pos + 1);
+ scope = get_u16(bc_buf + pos + 5);
+ pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
+ NULL, NULL, pos_next);
+ JS_FreeAtom(ctx, var_name);
+ break;
+ case OP_scope_make_ref:
+ {
+ int label;
+ LabelSlot *ls;
+ var_name = get_u32(bc_buf + pos + 1);
+ label = get_u32(bc_buf + pos + 5);
+ scope = get_u16(bc_buf + pos + 9);
+ ls = &s->label_slots[label];
+ ls->ref_count--; /* always remove label reference */
+ pos_next = resolve_scope_var(ctx, s, var_name, scope, op, &bc_out,
+ bc_buf, ls, pos_next);
+ JS_FreeAtom(ctx, var_name);
+ }
+ break;
+ case OP_scope_get_private_field:
+ case OP_scope_get_private_field2:
+ case OP_scope_put_private_field:
+ {
+ int ret;
+ var_name = get_u32(bc_buf + pos + 1);
+ scope = get_u16(bc_buf + pos + 5);
+ ret = resolve_scope_private_field(ctx, s, var_name, scope, op, &bc_out);
+ if (ret < 0)
+ goto fail;
+ JS_FreeAtom(ctx, var_name);
+ }
+ break;
+ case OP_gosub:
+ s->jump_size++;
+ if (OPTIMIZE) {
+ /* remove calls to empty finalizers */
+ int label;
+ LabelSlot *ls;
+
+ label = get_u32(bc_buf + pos + 1);
+ assert(label >= 0 && label < s->label_count);
+ ls = &s->label_slots[label];
+ if (code_match(&cc, ls->pos, OP_ret, -1)) {
+ ls->ref_count--;
+ break;
+ }
+ }
+ goto no_change;
+ case OP_drop:
+ if (0) {
+ /* remove drops before return_undef */
+ /* do not perform this optimization in pass2 because
+ it breaks patterns recognised in resolve_labels */
+ int pos1 = pos_next;
+ int line1 = line_num;
+ while (code_match(&cc, pos1, OP_drop, -1)) {
+ if (cc.line_num >= 0) line1 = cc.line_num;
+ pos1 = cc.pos;
+ }
+ if (code_match(&cc, pos1, OP_return_undef, -1)) {
+ pos_next = pos1;
+ if (line1 != -1 && line1 != line_num) {
+ line_num = line1;
+ s->line_number_size++;
+ dbuf_putc(&bc_out, OP_line_num);
+ dbuf_put_u32(&bc_out, line_num);
+ }
+ break;
+ }
+ }
+ goto no_change;
+ case OP_insert3:
+ if (OPTIMIZE) {
+ /* Transformation: insert3 put_array_el|put_ref_value drop -> put_array_el|put_ref_value */
+ if (code_match(&cc, pos_next, M2(OP_put_array_el, OP_put_ref_value), OP_drop, -1)) {
+ dbuf_putc(&bc_out, cc.op);
+ pos_next = cc.pos;
+ if (cc.line_num != -1 && cc.line_num != line_num) {
+ line_num = cc.line_num;
+ s->line_number_size++;
+ dbuf_putc(&bc_out, OP_line_num);
+ dbuf_put_u32(&bc_out, line_num);
+ }
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_goto:
+ s->jump_size++;
+ /* fall thru */
+ case OP_tail_call:
+ case OP_tail_call_method:
+ case OP_return:
+ case OP_return_undef:
+ case OP_throw:
+ case OP_throw_error:
+ case OP_ret:
+ if (OPTIMIZE) {
+ /* remove dead code */
+ int line = -1;
+ dbuf_put(&bc_out, bc_buf + pos, len);
+ pos = skip_dead_code(s, bc_buf, bc_len, pos + len, &line);
+ pos_next = pos;
+ if (pos < bc_len && line >= 0 && line_num != line) {
+ line_num = line;
+ s->line_number_size++;
+ dbuf_putc(&bc_out, OP_line_num);
+ dbuf_put_u32(&bc_out, line_num);
+ }
+ break;
+ }
+ goto no_change;
+
+ case OP_label:
+ {
+ int label;
+ LabelSlot *ls;
+
+ label = get_u32(bc_buf + pos + 1);
+ assert(label >= 0 && label < s->label_count);
+ ls = &s->label_slots[label];
+ ls->pos2 = bc_out.size + opcode_info[op].size;
+ }
+ goto no_change;
+
+ case OP_enter_scope:
+ {
+ int scope_idx, scope = get_u16(bc_buf + pos + 1);
+
+ if (scope == s->body_scope) {
+ instantiate_hoisted_definitions(ctx, s, &bc_out);
+ }
+
+ for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
+ JSVarDef *vd = &s->vars[scope_idx];
+ if (vd->scope_level == scope) {
+ if (scope_idx != s->arguments_arg_idx) {
+ if (vd->var_kind == JS_VAR_FUNCTION_DECL ||
+ vd->var_kind == JS_VAR_NEW_FUNCTION_DECL) {
+ /* Initialize lexical variable upon entering scope */
+ dbuf_putc(&bc_out, OP_fclosure);
+ dbuf_put_u32(&bc_out, vd->func_pool_idx);
+ dbuf_putc(&bc_out, OP_put_loc);
+ dbuf_put_u16(&bc_out, scope_idx);
+ } else {
+ /* XXX: should check if variable can be used
+ before initialization */
+ dbuf_putc(&bc_out, OP_set_loc_uninitialized);
+ dbuf_put_u16(&bc_out, scope_idx);
+ }
+ }
+ scope_idx = vd->scope_next;
+ } else {
+ break;
+ }
+ }
+ }
+ break;
+
+ case OP_leave_scope:
+ {
+ int scope_idx, scope = get_u16(bc_buf + pos + 1);
+
+ for(scope_idx = s->scopes[scope].first; scope_idx >= 0;) {
+ JSVarDef *vd = &s->vars[scope_idx];
+ if (vd->scope_level == scope) {
+ if (vd->is_captured) {
+ dbuf_putc(&bc_out, OP_close_loc);
+ dbuf_put_u16(&bc_out, scope_idx);
+ }
+ scope_idx = vd->scope_next;
+ } else {
+ break;
+ }
+ }
+ }
+ break;
+
+ case OP_set_name:
+ {
+ /* remove dummy set_name opcodes */
+ JSAtom name = get_u32(bc_buf + pos + 1);
+ if (name == JS_ATOM_NULL)
+ break;
+ }
+ goto no_change;
+
+ case OP_if_false:
+ case OP_if_true:
+ case OP_catch:
+ s->jump_size++;
+ goto no_change;
+
+ case OP_dup:
+ if (OPTIMIZE) {
+ /* Transformation: dup if_false(l1) drop, l1: if_false(l2) -> if_false(l2) */
+ /* Transformation: dup if_true(l1) drop, l1: if_true(l2) -> if_true(l2) */
+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), OP_drop, -1)) {
+ int lab0, lab1, op1, pos1, line1, pos2;
+ lab0 = lab1 = cc.label;
+ assert(lab1 >= 0 && lab1 < s->label_count);
+ op1 = cc.op;
+ pos1 = cc.pos;
+ line1 = cc.line_num;
+ while (code_match(&cc, (pos2 = get_label_pos(s, lab1)), OP_dup, op1, OP_drop, -1)) {
+ lab1 = cc.label;
+ }
+ if (code_match(&cc, pos2, op1, -1)) {
+ s->jump_size++;
+ update_label(s, lab0, -1);
+ update_label(s, cc.label, +1);
+ dbuf_putc(&bc_out, op1);
+ dbuf_put_u32(&bc_out, cc.label);
+ pos_next = pos1;
+ if (line1 != -1 && line1 != line_num) {
+ line_num = line1;
+ s->line_number_size++;
+ dbuf_putc(&bc_out, OP_line_num);
+ dbuf_put_u32(&bc_out, line_num);
+ }
+ break;
+ }
+ }
+ }
+ goto no_change;
+
+ case OP_nop:
+ /* remove erased code */
+ break;
+ case OP_set_class_name:
+ /* only used during parsing */
+ break;
+
+ default:
+ no_change:
+ dbuf_put(&bc_out, bc_buf + pos, len);
+ break;
+ }
+ }
+
+ /* set the new byte code */
+ dbuf_free(&s->byte_code);
+ s->byte_code = bc_out;
+ if (dbuf_error(&s->byte_code)) {
+ JS_ThrowOutOfMemory(ctx);
+ return -1;
+ }
+ return 0;
+ fail:
+ /* continue the copy to keep the atom refcounts consistent */
+ /* XXX: find a better solution ? */
+ for (; pos < bc_len; pos = pos_next) {
+ op = bc_buf[pos];
+ len = opcode_info[op].size;
+ pos_next = pos + len;
+ dbuf_put(&bc_out, bc_buf + pos, len);
+ }
+ dbuf_free(&s->byte_code);
+ s->byte_code = bc_out;
+ return -1;
+}
+
+/* the pc2line table gives a line number for each PC value */
+static void add_pc2line_info(JSFunctionDef *s, uint32_t pc, int line_num)
+{
+ if (s->line_number_slots != NULL
+ && s->line_number_count < s->line_number_size
+ && pc >= s->line_number_last_pc
+ && line_num != s->line_number_last) {
+ s->line_number_slots[s->line_number_count].pc = pc;
+ s->line_number_slots[s->line_number_count].line_num = line_num;
+ s->line_number_count++;
+ s->line_number_last_pc = pc;
+ s->line_number_last = line_num;
+ }
+}
+
+static void compute_pc2line_info(JSFunctionDef *s)
+{
+ if (!(s->js_mode & JS_MODE_STRIP) && s->line_number_slots) {
+ int last_line_num = s->line_num;
+ uint32_t last_pc = 0;
+ int i;
+
+ js_dbuf_init(s->ctx, &s->pc2line);
+ for (i = 0; i < s->line_number_count; i++) {
+ uint32_t pc = s->line_number_slots[i].pc;
+ int line_num = s->line_number_slots[i].line_num;
+ int diff_pc, diff_line;
+
+ if (line_num < 0)
+ continue;
+
+ diff_pc = pc - last_pc;
+ diff_line = line_num - last_line_num;
+ if (diff_line == 0 || diff_pc < 0)
+ continue;
+
+ if (diff_line >= PC2LINE_BASE &&
+ diff_line < PC2LINE_BASE + PC2LINE_RANGE &&
+ diff_pc <= PC2LINE_DIFF_PC_MAX) {
+ dbuf_putc(&s->pc2line, (diff_line - PC2LINE_BASE) +
+ diff_pc * PC2LINE_RANGE + PC2LINE_OP_FIRST);
+ } else {
+ /* longer encoding */
+ dbuf_putc(&s->pc2line, 0);
+ dbuf_put_leb128(&s->pc2line, diff_pc);
+ dbuf_put_sleb128(&s->pc2line, diff_line);
+ }
+ last_pc = pc;
+ last_line_num = line_num;
+ }
+ }
+}
+
+static RelocEntry *add_reloc(JSContext *ctx, LabelSlot *ls, uint32_t addr, int size)
+{
+ RelocEntry *re;
+ re = js_malloc(ctx, sizeof(*re));
+ if (!re)
+ return NULL;
+ re->addr = addr;
+ re->size = size;
+ re->next = ls->first_reloc;
+ ls->first_reloc = re;
+ return re;
+}
+
+static BOOL code_has_label(CodeContext *s, int pos, int label)
+{
+ while (pos < s->bc_len) {
+ int op = s->bc_buf[pos];
+ if (op == OP_line_num) {
+ pos += 5;
+ continue;
+ }
+ if (op == OP_label) {
+ int lab = get_u32(s->bc_buf + pos + 1);
+ if (lab == label)
+ return TRUE;
+ pos += 5;
+ continue;
+ }
+ if (op == OP_goto) {
+ int lab = get_u32(s->bc_buf + pos + 1);
+ if (lab == label)
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+/* return the target label, following the OP_goto jumps
+ the first opcode at destination is stored in *pop
+ */
+static int find_jump_target(JSFunctionDef *s, int label, int *pop, int *pline)
+{
+ int i, pos, op;
+
+ update_label(s, label, -1);
+ for (i = 0; i < 10; i++) {
+ assert(label >= 0 && label < s->label_count);
+ pos = s->label_slots[label].pos2;
+ for (;;) {
+ switch(op = s->byte_code.buf[pos]) {
+ case OP_line_num:
+ if (pline)
+ *pline = get_u32(s->byte_code.buf + pos + 1);
+ /* fall thru */
+ case OP_label:
+ pos += opcode_info[op].size;
+ continue;
+ case OP_goto:
+ label = get_u32(s->byte_code.buf + pos + 1);
+ break;
+ case OP_drop:
+ /* ignore drop opcodes if followed by OP_return_undef */
+ while (s->byte_code.buf[++pos] == OP_drop)
+ continue;
+ if (s->byte_code.buf[pos] == OP_return_undef)
+ op = OP_return_undef;
+ /* fall thru */
+ default:
+ goto done;
+ }
+ break;
+ }
+ }
+ /* cycle detected, could issue a warning */
+ done:
+ *pop = op;
+ update_label(s, label, +1);
+ return label;
+}
+
+static void push_short_int(DynBuf *bc_out, int val)
+{
+#if SHORT_OPCODES
+ if (val >= -1 && val <= 7) {
+ dbuf_putc(bc_out, OP_push_0 + val);
+ return;
+ }
+ if (val == (int8_t)val) {
+ dbuf_putc(bc_out, OP_push_i8);
+ dbuf_putc(bc_out, val);
+ return;
+ }
+ if (val == (int16_t)val) {
+ dbuf_putc(bc_out, OP_push_i16);
+ dbuf_put_u16(bc_out, val);
+ return;
+ }
+#endif
+ dbuf_putc(bc_out, OP_push_i32);
+ dbuf_put_u32(bc_out, val);
+}
+
+static void put_short_code(DynBuf *bc_out, int op, int idx)
+{
+#if SHORT_OPCODES
+ if (idx < 4) {
+ switch (op) {
+ case OP_get_loc:
+ dbuf_putc(bc_out, OP_get_loc0 + idx);
+ return;
+ case OP_put_loc:
+ dbuf_putc(bc_out, OP_put_loc0 + idx);
+ return;
+ case OP_set_loc:
+ dbuf_putc(bc_out, OP_set_loc0 + idx);
+ return;
+ case OP_get_arg:
+ dbuf_putc(bc_out, OP_get_arg0 + idx);
+ return;
+ case OP_put_arg:
+ dbuf_putc(bc_out, OP_put_arg0 + idx);
+ return;
+ case OP_set_arg:
+ dbuf_putc(bc_out, OP_set_arg0 + idx);
+ return;
+ case OP_get_var_ref:
+ dbuf_putc(bc_out, OP_get_var_ref0 + idx);
+ return;
+ case OP_put_var_ref:
+ dbuf_putc(bc_out, OP_put_var_ref0 + idx);
+ return;
+ case OP_set_var_ref:
+ dbuf_putc(bc_out, OP_set_var_ref0 + idx);
+ return;
+ case OP_call:
+ dbuf_putc(bc_out, OP_call0 + idx);
+ return;
+ }
+ }
+ if (idx < 256) {
+ switch (op) {
+ case OP_get_loc:
+ dbuf_putc(bc_out, OP_get_loc8);
+ dbuf_putc(bc_out, idx);
+ return;
+ case OP_put_loc:
+ dbuf_putc(bc_out, OP_put_loc8);
+ dbuf_putc(bc_out, idx);
+ return;
+ case OP_set_loc:
+ dbuf_putc(bc_out, OP_set_loc8);
+ dbuf_putc(bc_out, idx);
+ return;
+ }
+ }
+#endif
+ dbuf_putc(bc_out, op);
+ dbuf_put_u16(bc_out, idx);
+}
+
+/* peephole optimizations and resolve goto/labels */
+static warn_unused int resolve_labels(JSContext *ctx, JSFunctionDef *s)
+{
+ int pos, pos_next, bc_len, op, op1, len, i, line_num;
+ const uint8_t *bc_buf;
+ DynBuf bc_out;
+ LabelSlot *label_slots, *ls;
+ RelocEntry *re, *re_next;
+ CodeContext cc;
+ int label;
+#if SHORT_OPCODES
+ JumpSlot *jp;
+#endif
+
+ label_slots = s->label_slots;
+
+ line_num = s->line_num;
+
+ cc.bc_buf = bc_buf = s->byte_code.buf;
+ cc.bc_len = bc_len = s->byte_code.size;
+ js_dbuf_init(ctx, &bc_out);
+
+#if SHORT_OPCODES
+ if (s->jump_size) {
+ s->jump_slots = js_mallocz(s->ctx, sizeof(*s->jump_slots) * s->jump_size);
+ if (s->jump_slots == NULL)
+ return -1;
+ }
+#endif
+ /* XXX: Should skip this phase if not generating SHORT_OPCODES */
+ if (s->line_number_size && !(s->js_mode & JS_MODE_STRIP)) {
+ s->line_number_slots = js_mallocz(s->ctx, sizeof(*s->line_number_slots) * s->line_number_size);
+ if (s->line_number_slots == NULL)
+ return -1;
+ s->line_number_last = s->line_num;
+ s->line_number_last_pc = 0;
+ }
+
+ /* initialize the 'home_object' variable if needed */
+ if (s->home_object_var_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_HOME_OBJECT);
+ put_short_code(&bc_out, OP_put_loc, s->home_object_var_idx);
+ }
+ /* initialize the 'this.active_func' variable if needed */
+ if (s->this_active_func_var_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
+ put_short_code(&bc_out, OP_put_loc, s->this_active_func_var_idx);
+ }
+ /* initialize the 'new.target' variable if needed */
+ if (s->new_target_var_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_NEW_TARGET);
+ put_short_code(&bc_out, OP_put_loc, s->new_target_var_idx);
+ }
+ /* initialize the 'this' variable if needed. In a derived class
+ constructor, this is initially uninitialized. */
+ if (s->this_var_idx >= 0) {
+ if (s->is_derived_class_constructor) {
+ dbuf_putc(&bc_out, OP_set_loc_uninitialized);
+ dbuf_put_u16(&bc_out, s->this_var_idx);
+ } else {
+ dbuf_putc(&bc_out, OP_push_this);
+ put_short_code(&bc_out, OP_put_loc, s->this_var_idx);
+ }
+ }
+ /* initialize the 'arguments' variable if needed */
+ if (s->arguments_var_idx >= 0) {
+ if ((s->js_mode & JS_MODE_STRICT) || !s->has_simple_parameter_list) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_ARGUMENTS);
+ } else {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS);
+ }
+ if (s->arguments_arg_idx >= 0)
+ put_short_code(&bc_out, OP_set_loc, s->arguments_arg_idx);
+ put_short_code(&bc_out, OP_put_loc, s->arguments_var_idx);
+ }
+ /* initialize a reference to the current function if needed */
+ if (s->func_var_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_THIS_FUNC);
+ put_short_code(&bc_out, OP_put_loc, s->func_var_idx);
+ }
+ /* initialize the variable environment object if needed */
+ if (s->var_object_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
+ put_short_code(&bc_out, OP_put_loc, s->var_object_idx);
+ }
+ if (s->arg_var_object_idx >= 0) {
+ dbuf_putc(&bc_out, OP_special_object);
+ dbuf_putc(&bc_out, OP_SPECIAL_OBJECT_VAR_OBJECT);
+ put_short_code(&bc_out, OP_put_loc, s->arg_var_object_idx);
+ }
+
+ for (pos = 0; pos < bc_len; pos = pos_next) {
+ int val;
+ op = bc_buf[pos];
+ len = opcode_info[op].size;
+ pos_next = pos + len;
+ switch(op) {
+ case OP_line_num:
+ /* line number info (for debug). We put it in a separate
+ compressed table to reduce memory usage and get better
+ performance */
+ line_num = get_u32(bc_buf + pos + 1);
+ break;
+
+ case OP_label:
+ {
+ label = get_u32(bc_buf + pos + 1);
+ assert(label >= 0 && label < s->label_count);
+ ls = &label_slots[label];
+ assert(ls->addr == -1);
+ ls->addr = bc_out.size;
+ /* resolve the relocation entries */
+ for(re = ls->first_reloc; re != NULL; re = re_next) {
+ int diff = ls->addr - re->addr;
+ re_next = re->next;
+ switch (re->size) {
+ case 4:
+ put_u32(bc_out.buf + re->addr, diff);
+ break;
+ case 2:
+ assert(diff == (int16_t)diff);
+ put_u16(bc_out.buf + re->addr, diff);
+ break;
+ case 1:
+ assert(diff == (int8_t)diff);
+ put_u8(bc_out.buf + re->addr, diff);
+ break;
+ }
+ js_free(ctx, re);
+ }
+ ls->first_reloc = NULL;
+ }
+ break;
+
+ case OP_call:
+ case OP_call_method:
+ {
+ /* detect and transform tail calls */
+ int argc;
+ argc = get_u16(bc_buf + pos + 1);
+ if (code_match(&cc, pos_next, OP_return, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op + 1, argc);
+ pos_next = skip_dead_code(s, bc_buf, bc_len, cc.pos, &line_num);
+ break;
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op, argc);
+ break;
+ }
+ goto no_change;
+
+ case OP_return:
+ case OP_return_undef:
+ case OP_return_async:
+ case OP_throw:
+ case OP_throw_error:
+ pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
+ goto no_change;
+
+ case OP_goto:
+ label = get_u32(bc_buf + pos + 1);
+ has_goto:
+ if (OPTIMIZE) {
+ int line1 = -1;
+ /* Use custom matcher because multiple labels can follow */
+ label = find_jump_target(s, label, &op1, &line1);
+ if (code_has_label(&cc, pos_next, label)) {
+ /* jump to next instruction: remove jump */
+ update_label(s, label, -1);
+ break;
+ }
+ if (op1 == OP_return || op1 == OP_return_undef || op1 == OP_throw) {
+ /* jump to return/throw: remove jump, append return/throw */
+ /* updating the line number obfuscates assembly listing */
+ //if (line1 >= 0) line_num = line1;
+ update_label(s, label, -1);
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, op1);
+ pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
+ break;
+ }
+ /* XXX: should duplicate single instructions followed by goto or return */
+ /* For example, can match one of these followed by return:
+ push_i32 / push_const / push_atom_value / get_var /
+ undefined / null / push_false / push_true / get_ref_value /
+ get_loc / get_arg / get_var_ref
+ */
+ }
+ goto has_label;
+
+ case OP_gosub:
+ label = get_u32(bc_buf + pos + 1);
+ if (0 && OPTIMIZE) {
+ label = find_jump_target(s, label, &op1, NULL);
+ if (op1 == OP_ret) {
+ update_label(s, label, -1);
+ /* empty finally clause: remove gosub */
+ break;
+ }
+ }
+ goto has_label;
+
+ case OP_catch:
+ label = get_u32(bc_buf + pos + 1);
+ goto has_label;
+
+ case OP_if_true:
+ case OP_if_false:
+ label = get_u32(bc_buf + pos + 1);
+ if (OPTIMIZE) {
+ label = find_jump_target(s, label, &op1, NULL);
+ /* transform if_false/if_true(l1) label(l1) -> drop label(l1) */
+ if (code_has_label(&cc, pos_next, label)) {
+ update_label(s, label, -1);
+ dbuf_putc(&bc_out, OP_drop);
+ break;
+ }
+ /* transform if_false(l1) goto(l2) label(l1) -> if_false(l2) label(l1) */
+ if (code_match(&cc, pos_next, OP_goto, -1)) {
+ int pos1 = cc.pos;
+ int line1 = cc.line_num;
+ if (code_has_label(&cc, pos1, label)) {
+ if (line1 >= 0) line_num = line1;
+ pos_next = pos1;
+ update_label(s, label, -1);
+ label = cc.label;
+ op ^= OP_if_true ^ OP_if_false;
+ }
+ }
+ }
+ has_label:
+ add_pc2line_info(s, bc_out.size, line_num);
+ if (op == OP_goto) {
+ pos_next = skip_dead_code(s, bc_buf, bc_len, pos_next, &line_num);
+ }
+ assert(label >= 0 && label < s->label_count);
+ ls = &label_slots[label];
+#if SHORT_OPCODES
+ jp = &s->jump_slots[s->jump_count++];
+ jp->op = op;
+ jp->size = 4;
+ jp->pos = bc_out.size + 1;
+ jp->label = label;
+
+ if (ls->addr == -1) {
+ int diff = ls->pos2 - pos - 1;
+ if (diff < 128 && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
+ jp->size = 1;
+ jp->op = OP_if_false8 + (op - OP_if_false);
+ dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
+ dbuf_putc(&bc_out, 0);
+ if (!add_reloc(ctx, ls, bc_out.size - 1, 1))
+ goto fail;
+ break;
+ }
+ if (diff < 32768 && op == OP_goto) {
+ jp->size = 2;
+ jp->op = OP_goto16;
+ dbuf_putc(&bc_out, OP_goto16);
+ dbuf_put_u16(&bc_out, 0);
+ if (!add_reloc(ctx, ls, bc_out.size - 2, 2))
+ goto fail;
+ break;
+ }
+ } else {
+ int diff = ls->addr - bc_out.size - 1;
+ if (diff == (int8_t)diff && (op == OP_if_false || op == OP_if_true || op == OP_goto)) {
+ jp->size = 1;
+ jp->op = OP_if_false8 + (op - OP_if_false);
+ dbuf_putc(&bc_out, OP_if_false8 + (op - OP_if_false));
+ dbuf_putc(&bc_out, diff);
+ break;
+ }
+ if (diff == (int16_t)diff && op == OP_goto) {
+ jp->size = 2;
+ jp->op = OP_goto16;
+ dbuf_putc(&bc_out, OP_goto16);
+ dbuf_put_u16(&bc_out, diff);
+ break;
+ }
+ }
+#endif
+ dbuf_putc(&bc_out, op);
+ dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
+ if (ls->addr == -1) {
+ /* unresolved yet: create a new relocation entry */
+ if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
+ goto fail;
+ }
+ break;
+ case OP_with_get_var:
+ case OP_with_put_var:
+ case OP_with_delete_var:
+ case OP_with_make_ref:
+ case OP_with_get_ref:
+ case OP_with_get_ref_undef:
+ {
+ JSAtom atom;
+ int is_with;
+
+ atom = get_u32(bc_buf + pos + 1);
+ label = get_u32(bc_buf + pos + 5);
+ is_with = bc_buf[pos + 9];
+ if (OPTIMIZE) {
+ label = find_jump_target(s, label, &op1, NULL);
+ }
+ assert(label >= 0 && label < s->label_count);
+ ls = &label_slots[label];
+ add_pc2line_info(s, bc_out.size, line_num);
+#if SHORT_OPCODES
+ jp = &s->jump_slots[s->jump_count++];
+ jp->op = op;
+ jp->size = 4;
+ jp->pos = bc_out.size + 5;
+ jp->label = label;
+#endif
+ dbuf_putc(&bc_out, op);
+ dbuf_put_u32(&bc_out, atom);
+ dbuf_put_u32(&bc_out, ls->addr - bc_out.size);
+ if (ls->addr == -1) {
+ /* unresolved yet: create a new relocation entry */
+ if (!add_reloc(ctx, ls, bc_out.size - 4, 4))
+ goto fail;
+ }
+ dbuf_putc(&bc_out, is_with);
+ }
+ break;
+
+ case OP_drop:
+ if (OPTIMIZE) {
+ /* remove useless drops before return */
+ if (code_match(&cc, pos_next, OP_return_undef, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_null:
+#if SHORT_OPCODES
+ if (OPTIMIZE) {
+ /* transform null strict_eq into is_null */
+ if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_is_null);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transform null strict_neq if_false/if_true -> is_null if_true/if_false */
+ if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_is_null);
+ pos_next = cc.pos;
+ label = cc.label;
+ op = cc.op ^ OP_if_false ^ OP_if_true;
+ goto has_label;
+ }
+ }
+#endif
+ /* fall thru */
+ case OP_push_false:
+ case OP_push_true:
+ if (OPTIMIZE) {
+ val = (op == OP_push_true);
+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
+ has_constant_test:
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ if (val == cc.op - OP_if_false) {
+ /* transform null if_false(l1) -> goto l1 */
+ /* transform false if_false(l1) -> goto l1 */
+ /* transform true if_true(l1) -> goto l1 */
+ pos_next = cc.pos;
+ op = OP_goto;
+ label = cc.label;
+ goto has_goto;
+ } else {
+ /* transform null if_true(l1) -> nop */
+ /* transform false if_true(l1) -> nop */
+ /* transform true if_false(l1) -> nop */
+ pos_next = cc.pos;
+ update_label(s, cc.label, -1);
+ break;
+ }
+ }
+ }
+ goto no_change;
+
+ case OP_push_i32:
+ if (OPTIMIZE) {
+ /* transform i32(val) neg -> i32(-val) */
+ val = get_i32(bc_buf + pos + 1);
+ if ((val != INT32_MIN && val != 0)
+ && code_match(&cc, pos_next, OP_neg, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ if (code_match(&cc, cc.pos, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ } else {
+ add_pc2line_info(s, bc_out.size, line_num);
+ push_short_int(&bc_out, -val);
+ }
+ pos_next = cc.pos;
+ break;
+ }
+ /* remove push/drop pairs generated by the parser */
+ if (code_match(&cc, pos_next, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ pos_next = cc.pos;
+ break;
+ }
+ /* Optimize constant tests: `if (0)`, `if (1)`, `if (!0)`... */
+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
+ val = (val != 0);
+ goto has_constant_test;
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ push_short_int(&bc_out, val);
+ break;
+ }
+ goto no_change;
+
+#if SHORT_OPCODES
+ case OP_push_const:
+ case OP_fclosure:
+ if (OPTIMIZE) {
+ int idx = get_u32(bc_buf + pos + 1);
+ if (idx < 256) {
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_push_const8 + op - OP_push_const);
+ dbuf_putc(&bc_out, idx);
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_get_field:
+ if (OPTIMIZE) {
+ JSAtom atom = get_u32(bc_buf + pos + 1);
+ if (atom == JS_ATOM_length) {
+ JS_FreeAtom(ctx, atom);
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_get_length);
+ break;
+ }
+ }
+ goto no_change;
+#endif
+ case OP_push_atom_value:
+ if (OPTIMIZE) {
+ JSAtom atom = get_u32(bc_buf + pos + 1);
+ /* remove push/drop pairs generated by the parser */
+ if (code_match(&cc, pos_next, OP_drop, -1)) {
+ JS_FreeAtom(ctx, atom);
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ pos_next = cc.pos;
+ break;
+ }
+#if SHORT_OPCODES
+ if (atom == JS_ATOM_empty_string) {
+ JS_FreeAtom(ctx, atom);
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_push_empty_string);
+ break;
+ }
+#endif
+ }
+ goto no_change;
+
+ case OP_to_propkey:
+ case OP_to_propkey2:
+ if (OPTIMIZE) {
+ /* remove redundant to_propkey/to_propkey2 opcodes when storing simple data */
+ if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_put_array_el, -1)
+ || code_match(&cc, pos_next, M3(OP_push_i32, OP_push_const, OP_push_atom_value), OP_put_array_el, -1)
+ || code_match(&cc, pos_next, M4(OP_undefined, OP_null, OP_push_true, OP_push_false), OP_put_array_el, -1)) {
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_undefined:
+ if (OPTIMIZE) {
+ /* remove push/drop pairs generated by the parser */
+ if (code_match(&cc, pos_next, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ pos_next = cc.pos;
+ break;
+ }
+ /* transform undefined return -> return_undefined */
+ if (code_match(&cc, pos_next, OP_return, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_return_undef);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transform undefined if_true(l1)/if_false(l1) -> nop/goto(l1) */
+ if (code_match(&cc, pos_next, M2(OP_if_false, OP_if_true), -1)) {
+ val = 0;
+ goto has_constant_test;
+ }
+#if SHORT_OPCODES
+ /* transform undefined strict_eq -> is_undefined */
+ if (code_match(&cc, pos_next, OP_strict_eq, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_is_undefined);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transform undefined strict_neq if_false/if_true -> is_undefined if_true/if_false */
+ if (code_match(&cc, pos_next, OP_strict_neq, M2(OP_if_false, OP_if_true), -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_is_undefined);
+ pos_next = cc.pos;
+ label = cc.label;
+ op = cc.op ^ OP_if_false ^ OP_if_true;
+ goto has_label;
+ }
+#endif
+ }
+ goto no_change;
+
+ case OP_insert2:
+ if (OPTIMIZE) {
+ /* Transformation:
+ insert2 put_field(a) drop -> put_field(a)
+ insert2 put_var_strict(a) drop -> put_var_strict(a)
+ */
+ if (code_match(&cc, pos_next, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, cc.op);
+ dbuf_put_u32(&bc_out, cc.atom);
+ pos_next = cc.pos;
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_dup:
+ if (OPTIMIZE) {
+ /* Transformation: dup put_x(n) drop -> put_x(n) */
+ int op1, line2 = -1;
+ /* Transformation: dup put_x(n) -> set_x(n) */
+ if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ op1 = cc.op + 1; /* put_x -> set_x */
+ pos_next = cc.pos;
+ if (code_match(&cc, cc.pos, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ op1 -= 1; /* set_x drop -> put_x */
+ pos_next = cc.pos;
+ if (code_match(&cc, cc.pos, op1 - 1, cc.idx, -1)) {
+ line2 = cc.line_num; /* delay line number update */
+ op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */
+ pos_next = cc.pos;
+ }
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op1, cc.idx);
+ if (line2 >= 0) line_num = line2;
+ break;
+ }
+ }
+ goto no_change;
+
+ case OP_get_loc:
+ if (OPTIMIZE) {
+ /* transformation:
+ get_loc(n) post_dec put_loc(n) drop -> dec_loc(n)
+ get_loc(n) post_inc put_loc(n) drop -> inc_loc(n)
+ get_loc(n) dec dup put_loc(n) drop -> dec_loc(n)
+ get_loc(n) inc dup put_loc(n) drop -> inc_loc(n)
+ */
+ int idx;
+ idx = get_u16(bc_buf + pos + 1);
+ if (idx >= 256)
+ goto no_change;
+ if (code_match(&cc, pos_next, M2(OP_post_dec, OP_post_inc), OP_put_loc, idx, OP_drop, -1) ||
+ code_match(&cc, pos_next, M2(OP_dec, OP_inc), OP_dup, OP_put_loc, idx, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, (cc.op == OP_inc || cc.op == OP_post_inc) ? OP_inc_loc : OP_dec_loc);
+ dbuf_putc(&bc_out, idx);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transformation:
+ get_loc(n) push_atom_value(x) add dup put_loc(n) drop -> push_atom_value(x) add_loc(n)
+ */
+ if (code_match(&cc, pos_next, OP_push_atom_value, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+#if SHORT_OPCODES
+ if (cc.atom == JS_ATOM_empty_string) {
+ JS_FreeAtom(ctx, cc.atom);
+ dbuf_putc(&bc_out, OP_push_empty_string);
+ } else
+#endif
+ {
+ dbuf_putc(&bc_out, OP_push_atom_value);
+ dbuf_put_u32(&bc_out, cc.atom);
+ }
+ dbuf_putc(&bc_out, OP_add_loc);
+ dbuf_putc(&bc_out, idx);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transformation:
+ get_loc(n) push_i32(x) add dup put_loc(n) drop -> push_i32(x) add_loc(n)
+ */
+ if (code_match(&cc, pos_next, OP_push_i32, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ push_short_int(&bc_out, cc.label);
+ dbuf_putc(&bc_out, OP_add_loc);
+ dbuf_putc(&bc_out, idx);
+ pos_next = cc.pos;
+ break;
+ }
+ /* transformation: XXX: also do these:
+ get_loc(n) get_loc(x) add dup put_loc(n) drop -> get_loc(x) add_loc(n)
+ get_loc(n) get_arg(x) add dup put_loc(n) drop -> get_arg(x) add_loc(n)
+ get_loc(n) get_var_ref(x) add dup put_loc(n) drop -> get_var_ref(x) add_loc(n)
+ */
+ if (code_match(&cc, pos_next, M3(OP_get_loc, OP_get_arg, OP_get_var_ref), -1, OP_add, OP_dup, OP_put_loc, idx, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, cc.op, cc.idx);
+ dbuf_putc(&bc_out, OP_add_loc);
+ dbuf_putc(&bc_out, idx);
+ pos_next = cc.pos;
+ break;
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op, idx);
+ break;
+ }
+ goto no_change;
+#if SHORT_OPCODES
+ case OP_get_arg:
+ case OP_get_var_ref:
+ if (OPTIMIZE) {
+ int idx;
+ idx = get_u16(bc_buf + pos + 1);
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op, idx);
+ break;
+ }
+ goto no_change;
+#endif
+ case OP_put_loc:
+ case OP_put_arg:
+ case OP_put_var_ref:
+ if (OPTIMIZE) {
+ /* transformation: put_x(n) get_x(n) -> set_x(n) */
+ int idx;
+ idx = get_u16(bc_buf + pos + 1);
+ if (code_match(&cc, pos_next, op - 1, idx, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op + 1, idx);
+ pos_next = cc.pos;
+ break;
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ put_short_code(&bc_out, op, idx);
+ break;
+ }
+ goto no_change;
+
+ case OP_post_inc:
+ case OP_post_dec:
+ if (OPTIMIZE) {
+ /* transformation:
+ post_inc put_x drop -> inc put_x
+ post_inc perm3 put_field drop -> inc put_field
+ post_inc perm3 put_var_strict drop -> inc put_var_strict
+ post_inc perm4 put_array_el drop -> inc put_array_el
+ */
+ int op1, idx;
+ if (code_match(&cc, pos_next, M3(OP_put_loc, OP_put_arg, OP_put_var_ref), -1, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ op1 = cc.op;
+ idx = cc.idx;
+ pos_next = cc.pos;
+ if (code_match(&cc, cc.pos, op1 - 1, idx, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ op1 += 1; /* put_x(n) get_x(n) -> set_x(n) */
+ pos_next = cc.pos;
+ }
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
+ put_short_code(&bc_out, op1, idx);
+ break;
+ }
+ if (code_match(&cc, pos_next, OP_perm3, M2(OP_put_field, OP_put_var_strict), OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
+ dbuf_putc(&bc_out, cc.op);
+ dbuf_put_u32(&bc_out, cc.atom);
+ pos_next = cc.pos;
+ break;
+ }
+ if (code_match(&cc, pos_next, OP_perm4, OP_put_array_el, OP_drop, -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, OP_dec + (op - OP_post_dec));
+ dbuf_putc(&bc_out, OP_put_array_el);
+ pos_next = cc.pos;
+ break;
+ }
+ }
+ goto no_change;
+
+#if SHORT_OPCODES
+ case OP_typeof:
+ if (OPTIMIZE) {
+ /* simplify typeof tests */
+ if (code_match(&cc, pos_next, OP_push_atom_value, M4(OP_strict_eq, OP_strict_neq, OP_eq, OP_neq), -1)) {
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ int op1 = (cc.op == OP_strict_eq || cc.op == OP_eq) ? OP_strict_eq : OP_strict_neq;
+ int op2 = -1;
+ switch (cc.atom) {
+ case JS_ATOM_undefined:
+ op2 = OP_typeof_is_undefined;
+ break;
+ case JS_ATOM_function:
+ op2 = OP_typeof_is_function;
+ break;
+ }
+ if (op2 >= 0) {
+ /* transform typeof(s) == "<type>" into is_<type> */
+ if (op1 == OP_strict_eq) {
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, op2);
+ JS_FreeAtom(ctx, cc.atom);
+ pos_next = cc.pos;
+ break;
+ }
+ if (op1 == OP_strict_neq && code_match(&cc, cc.pos, OP_if_false, -1)) {
+ /* transform typeof(s) != "<type>" if_false into is_<type> if_true */
+ if (cc.line_num >= 0) line_num = cc.line_num;
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_putc(&bc_out, op2);
+ JS_FreeAtom(ctx, cc.atom);
+ pos_next = cc.pos;
+ label = cc.label;
+ op = OP_if_true;
+ goto has_label;
+ }
+ }
+ }
+ }
+ goto no_change;
+#endif
+
+ default:
+ no_change:
+ add_pc2line_info(s, bc_out.size, line_num);
+ dbuf_put(&bc_out, bc_buf + pos, len);
+ break;
+ }
+ }
+
+ /* check that there were no missing labels */
+ for(i = 0; i < s->label_count; i++) {
+ assert(label_slots[i].first_reloc == NULL);
+ }
+#if SHORT_OPCODES
+ if (OPTIMIZE) {
+ /* more jump optimizations */
+ int patch_offsets = 0;
+ for (i = 0, jp = s->jump_slots; i < s->jump_count; i++, jp++) {
+ LabelSlot *ls;
+ JumpSlot *jp1;
+ int j, pos, diff, delta;
+
+ delta = 3;
+ switch (op = jp->op) {
+ case OP_goto16:
+ delta = 1;
+ /* fall thru */
+ case OP_if_false:
+ case OP_if_true:
+ case OP_goto:
+ pos = jp->pos;
+ diff = s->label_slots[jp->label].addr - pos;
+ if (diff >= -128 && diff <= 127 + delta) {
+ //put_u8(bc_out.buf + pos, diff);
+ jp->size = 1;
+ if (op == OP_goto16) {
+ bc_out.buf[pos - 1] = jp->op = OP_goto8;
+ } else {
+ bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);
+ }
+ goto shrink;
+ } else if (diff == (int16_t)diff && op == OP_goto) {
+ //put_u16(bc_out.buf + pos, diff);
+ jp->size = 2;
+ delta = 2;
+ bc_out.buf[pos - 1] = jp->op = OP_goto16;
+ shrink:
+ /* XXX: should reduce complexity, using 2 finger copy scheme */
+ memmove(bc_out.buf + pos + jp->size, bc_out.buf + pos + jp->size + delta,
+ bc_out.size - pos - jp->size - delta);
+ bc_out.size -= delta;
+ patch_offsets++;
+ for (j = 0, ls = s->label_slots; j < s->label_count; j++, ls++) {
+ if (ls->addr > pos)
+ ls->addr -= delta;
+ }
+ for (j = i + 1, jp1 = jp + 1; j < s->jump_count; j++, jp1++) {
+ if (jp1->pos > pos)
+ jp1->pos -= delta;
+ }
+ for (j = 0; j < s->line_number_count; j++) {
+ if (s->line_number_slots[j].pc > pos)
+ s->line_number_slots[j].pc -= delta;
+ }
+ continue;
+ }
+ break;
+ }
+ }
+ if (patch_offsets) {
+ JumpSlot *jp1;
+ int j;
+ for (j = 0, jp1 = s->jump_slots; j < s->jump_count; j++, jp1++) {
+ int diff1 = s->label_slots[jp1->label].addr - jp1->pos;
+ switch (jp1->size) {
+ case 1:
+ put_u8(bc_out.buf + jp1->pos, diff1);
+ break;
+ case 2:
+ put_u16(bc_out.buf + jp1->pos, diff1);
+ break;
+ case 4:
+ put_u32(bc_out.buf + jp1->pos, diff1);
+ break;
+ }
+ }
+ }
+ }
+ js_free(ctx, s->jump_slots);
+ s->jump_slots = NULL;
+#endif
+ js_free(ctx, s->label_slots);
+ s->label_slots = NULL;
+ /* XXX: should delay until copying to runtime bytecode function */
+ compute_pc2line_info(s);
+ js_free(ctx, s->line_number_slots);
+ s->line_number_slots = NULL;
+ /* set the new byte code */
+ dbuf_free(&s->byte_code);
+ s->byte_code = bc_out;
+ s->use_short_opcodes = TRUE;
+ if (dbuf_error(&s->byte_code)) {
+ JS_ThrowOutOfMemory(ctx);
+ return -1;
+ }
+ return 0;
+ fail:
+ /* XXX: not safe */
+ dbuf_free(&bc_out);
+ return -1;
+}
+
+/* compute the maximum stack size needed by the function */
+
+typedef struct StackSizeState {
+ int bc_len;
+ int stack_len_max;
+ uint16_t *stack_level_tab;
+ int *pc_stack;
+ int pc_stack_len;
+ int pc_stack_size;
+} StackSizeState;
+
+/* 'op' is only used for error indication */
+static warn_unused int ss_check(JSContext *ctx, StackSizeState *s,
+ int pos, int op, int stack_len)
+{
+ if ((unsigned)pos >= s->bc_len) {
+ JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
+ return -1;
+ }
+ if (stack_len > s->stack_len_max) {
+ s->stack_len_max = stack_len;
+ if (s->stack_len_max > JS_STACK_SIZE_MAX) {
+ JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
+ return -1;
+ }
+ }
+ if (s->stack_level_tab[pos] != 0xffff) {
+ /* already explored: check that the stack size is consistent */
+ if (s->stack_level_tab[pos] != stack_len) {
+ JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)",
+ s->stack_level_tab[pos], stack_len, pos);
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ /* mark as explored and store the stack size */
+ s->stack_level_tab[pos] = stack_len;
+
+ /* queue the new PC to explore */
+ if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]),
+ &s->pc_stack_size, s->pc_stack_len + 1))
+ return -1;
+ s->pc_stack[s->pc_stack_len++] = pos;
+ return 0;
+}
+
+static warn_unused int compute_stack_size(JSContext *ctx,
+ JSFunctionDef *fd,
+ int *pstack_size)
+{
+ StackSizeState s_s, *s = &s_s;
+ int i, diff, n_pop, pos_next, stack_len, pos, op;
+ const JSOpCode *oi;
+ const uint8_t *bc_buf;
+
+ bc_buf = fd->byte_code.buf;
+ s->bc_len = fd->byte_code.size;
+ /* bc_len > 0 */
+ s->stack_level_tab = js_malloc(ctx, sizeof(s->stack_level_tab[0]) *
+ s->bc_len);
+ if (!s->stack_level_tab)
+ return -1;
+ for(i = 0; i < s->bc_len; i++)
+ s->stack_level_tab[i] = 0xffff;
+ s->stack_len_max = 0;
+ s->pc_stack = NULL;
+ s->pc_stack_len = 0;
+ s->pc_stack_size = 0;
+
+ /* breadth-first graph exploration */
+ if (ss_check(ctx, s, 0, OP_invalid, 0))
+ goto fail;
+
+ while (s->pc_stack_len > 0) {
+ pos = s->pc_stack[--s->pc_stack_len];
+ stack_len = s->stack_level_tab[pos];
+ op = bc_buf[pos];
+ if (op == 0 || op >= OP_COUNT) {
+ JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos);
+ goto fail;
+ }
+ oi = &short_opcode_info(op);
+ pos_next = pos + oi->size;
+ if (pos_next > s->bc_len) {
+ JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos);
+ goto fail;
+ }
+ n_pop = oi->n_pop;
+ /* call pops a variable number of arguments */
+ if (oi->fmt == OP_FMT_npop || oi->fmt == OP_FMT_npop_u16) {
+ n_pop += get_u16(bc_buf + pos + 1);
+ } else {
+#if SHORT_OPCODES
+ if (oi->fmt == OP_FMT_npopx) {
+ n_pop += op - OP_call0;
+ }
+#endif
+ }
+
+ if (stack_len < n_pop) {
+ JS_ThrowInternalError(ctx, "stack underflow (op=%d, pc=%d)", op, pos);
+ goto fail;
+ }
+ stack_len += oi->n_push - n_pop;
+ if (stack_len > s->stack_len_max) {
+ s->stack_len_max = stack_len;
+ if (s->stack_len_max > JS_STACK_SIZE_MAX) {
+ JS_ThrowInternalError(ctx, "stack overflow (op=%d, pc=%d)", op, pos);
+ goto fail;
+ }
+ }
+ switch(op) {
+ case OP_tail_call:
+ case OP_tail_call_method:
+ case OP_return:
+ case OP_return_undef:
+ case OP_return_async:
+ case OP_throw:
+ case OP_throw_error:
+ case OP_ret:
+ goto done_insn;
+ case OP_goto:
+ diff = get_u32(bc_buf + pos + 1);
+ pos_next = pos + 1 + diff;
+ break;
+#if SHORT_OPCODES
+ case OP_goto16:
+ diff = (int16_t)get_u16(bc_buf + pos + 1);
+ pos_next = pos + 1 + diff;
+ break;
+ case OP_goto8:
+ diff = (int8_t)bc_buf[pos + 1];
+ pos_next = pos + 1 + diff;
+ break;
+ case OP_if_true8:
+ case OP_if_false8:
+ diff = (int8_t)bc_buf[pos + 1];
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
+ goto fail;
+ break;
+#endif
+ case OP_if_true:
+ case OP_if_false:
+ case OP_catch:
+ diff = get_u32(bc_buf + pos + 1);
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len))
+ goto fail;
+ break;
+ case OP_gosub:
+ diff = get_u32(bc_buf + pos + 1);
+ if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1))
+ goto fail;
+ break;
+ case OP_with_get_var:
+ case OP_with_delete_var:
+ diff = get_u32(bc_buf + pos + 5);
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1))
+ goto fail;
+ break;
+ case OP_with_make_ref:
+ case OP_with_get_ref:
+ case OP_with_get_ref_undef:
+ diff = get_u32(bc_buf + pos + 5);
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2))
+ goto fail;
+ break;
+ case OP_with_put_var:
+ diff = get_u32(bc_buf + pos + 5);
+ if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1))
+ goto fail;
+ break;
+
+ default:
+ break;
+ }
+ if (ss_check(ctx, s, pos_next, op, stack_len))
+ goto fail;
+ done_insn: ;
+ }
+ js_free(ctx, s->stack_level_tab);
+ js_free(ctx, s->pc_stack);
+ *pstack_size = s->stack_len_max;
+ return 0;
+ fail:
+ js_free(ctx, s->stack_level_tab);
+ js_free(ctx, s->pc_stack);
+ *pstack_size = 0;
+ return -1;
+}
+
+static int add_module_variables(JSContext *ctx, JSFunctionDef *fd)
+{
+ int i, idx;
+ JSModuleDef *m = fd->module;
+ JSExportEntry *me;
+ JSGlobalVar *hf;
+
+ /* The imported global variables were added as closure variables
+ in js_parse_import(). We add here the module global
+ variables. */
+
+ for(i = 0; i < fd->global_var_count; i++) {
+ hf = &fd->global_vars[i];
+ if (add_closure_var(ctx, fd, TRUE, FALSE, i, hf->var_name, hf->is_const,
+ hf->is_lexical, FALSE) < 0)
+ return -1;
+ }
+
+ /* resolve the variable names of the local exports */
+ for(i = 0; i < m->export_entries_count; i++) {
+ me = &m->export_entries[i];
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ idx = find_closure_var(ctx, fd, me->local_name);
+ if (idx < 0) {
+ JS_ThrowSyntaxErrorAtom(ctx, "exported variable '%s' does not exist",
+ me->local_name);
+ return -1;
+ }
+ me->u.local.var_idx = idx;
+ }
+ }
+ return 0;
+}
+
+/* create a function object from a function definition. The function
+ definition is freed. All the child functions are also created. It
+ must be done this way to resolve all the variables. */
+static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
+{
+ JSValue func_obj;
+ JSFunctionBytecode *b;
+ struct list_head *el, *el1;
+ int stack_size, scope, idx;
+ int function_size, byte_code_offset, cpool_offset;
+ int closure_var_offset, vardefs_offset;
+
+ /* recompute scope linkage */
+ for (scope = 0; scope < fd->scope_count; scope++) {
+ fd->scopes[scope].first = -1;
+ }
+ if (fd->has_parameter_expressions) {
+ /* special end of variable list marker for the argument scope */
+ fd->scopes[ARG_SCOPE_INDEX].first = ARG_SCOPE_END;
+ }
+ for (idx = 0; idx < fd->var_count; idx++) {
+ JSVarDef *vd = &fd->vars[idx];
+ vd->scope_next = fd->scopes[vd->scope_level].first;
+ fd->scopes[vd->scope_level].first = idx;
+ }
+ for (scope = 2; scope < fd->scope_count; scope++) {
+ JSVarScope *sd = &fd->scopes[scope];
+ if (sd->first < 0)
+ sd->first = fd->scopes[sd->parent].first;
+ }
+ for (idx = 0; idx < fd->var_count; idx++) {
+ JSVarDef *vd = &fd->vars[idx];
+ if (vd->scope_next < 0 && vd->scope_level > 1) {
+ scope = fd->scopes[vd->scope_level].parent;
+ vd->scope_next = fd->scopes[scope].first;
+ }
+ }
+
+ /* if the function contains an eval call, the closure variables
+ are used to compile the eval and they must be ordered by scope,
+ so it is necessary to create the closure variables before any
+ other variable lookup is done. */
+ if (fd->has_eval_call)
+ add_eval_variables(ctx, fd);
+
+ /* add the module global variables in the closure */
+ if (fd->module) {
+ if (add_module_variables(ctx, fd))
+ goto fail;
+ }
+
+ /* first create all the child functions */
+ list_for_each_safe(el, el1, &fd->child_list) {
+ JSFunctionDef *fd1;
+ int cpool_idx;
+
+ fd1 = list_entry(el, JSFunctionDef, link);
+ cpool_idx = fd1->parent_cpool_idx;
+ func_obj = js_create_function(ctx, fd1);
+ if (JS_IsException(func_obj))
+ goto fail;
+ /* save it in the constant pool */
+ assert(cpool_idx >= 0);
+ fd->cpool[cpool_idx] = func_obj;
+ }
+
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 4)
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ printf("pass 1\n");
+ dump_byte_code(ctx, 1, fd->byte_code.buf, fd->byte_code.size,
+ fd->args, fd->arg_count, fd->vars, fd->var_count,
+ fd->closure_var, fd->closure_var_count,
+ fd->cpool, fd->cpool_count, fd->source, fd->line_num,
+ fd->label_slots, NULL);
+ printf("\n");
+ }
+#endif
+
+ if (resolve_variables(ctx, fd))
+ goto fail;
+
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 2)
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ printf("pass 2\n");
+ dump_byte_code(ctx, 2, fd->byte_code.buf, fd->byte_code.size,
+ fd->args, fd->arg_count, fd->vars, fd->var_count,
+ fd->closure_var, fd->closure_var_count,
+ fd->cpool, fd->cpool_count, fd->source, fd->line_num,
+ fd->label_slots, NULL);
+ printf("\n");
+ }
+#endif
+
+ if (resolve_labels(ctx, fd))
+ goto fail;
+
+ if (compute_stack_size(ctx, fd, &stack_size) < 0)
+ goto fail;
+
+ if (fd->js_mode & JS_MODE_STRIP) {
+ function_size = offsetof(JSFunctionBytecode, debug);
+ } else {
+ function_size = sizeof(*b);
+ }
+ cpool_offset = function_size;
+ function_size += fd->cpool_count * sizeof(*fd->cpool);
+ vardefs_offset = function_size;
+ if (!(fd->js_mode & JS_MODE_STRIP) || fd->has_eval_call) {
+ function_size += (fd->arg_count + fd->var_count) * sizeof(*b->vardefs);
+ }
+ closure_var_offset = function_size;
+ function_size += fd->closure_var_count * sizeof(*fd->closure_var);
+ byte_code_offset = function_size;
+ function_size += fd->byte_code.size;
+
+ b = js_mallocz(ctx, function_size);
+ if (!b)
+ goto fail;
+ b->header.ref_count = 1;
+
+ b->byte_code_buf = (void *)((uint8_t*)b + byte_code_offset);
+ b->byte_code_len = fd->byte_code.size;
+ memcpy(b->byte_code_buf, fd->byte_code.buf, fd->byte_code.size);
+ js_free(ctx, fd->byte_code.buf);
+ fd->byte_code.buf = NULL;
+
+ b->func_name = fd->func_name;
+ if (fd->arg_count + fd->var_count > 0) {
+ if ((fd->js_mode & JS_MODE_STRIP) && !fd->has_eval_call) {
+ /* Strip variable definitions not needed at runtime */
+ int i;
+ for(i = 0; i < fd->var_count; i++) {
+ JS_FreeAtom(ctx, fd->vars[i].var_name);
+ }
+ for(i = 0; i < fd->arg_count; i++) {
+ JS_FreeAtom(ctx, fd->args[i].var_name);
+ }
+ for(i = 0; i < fd->closure_var_count; i++) {
+ JS_FreeAtom(ctx, fd->closure_var[i].var_name);
+ fd->closure_var[i].var_name = JS_ATOM_NULL;
+ }
+ } else {
+ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
+ if (fd->arg_count)
+ memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
+ if (fd->var_count)
+ memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
+ }
+ b->var_count = fd->var_count;
+ b->arg_count = fd->arg_count;
+ b->defined_arg_count = fd->defined_arg_count;
+ js_free(ctx, fd->args);
+ js_free(ctx, fd->vars);
+ }
+ b->cpool_count = fd->cpool_count;
+ if (b->cpool_count) {
+ b->cpool = (void *)((uint8_t*)b + cpool_offset);
+ memcpy(b->cpool, fd->cpool, b->cpool_count * sizeof(*b->cpool));
+ }
+ js_free(ctx, fd->cpool);
+ fd->cpool = NULL;
+
+ b->stack_size = stack_size;
+
+ if (fd->js_mode & JS_MODE_STRIP) {
+ JS_FreeAtom(ctx, fd->filename);
+ dbuf_free(&fd->pc2line); // probably useless
+ } else {
+ /* XXX: source and pc2line info should be packed at the end of the
+ JSFunctionBytecode structure, avoiding allocation overhead
+ */
+ b->has_debug = 1;
+ b->debug.filename = fd->filename;
+ b->debug.line_num = fd->line_num;
+
+ //DynBuf pc2line;
+ //compute_pc2line_info(fd, &pc2line);
+ //js_free(ctx, fd->line_number_slots)
+ b->debug.pc2line_buf = js_realloc(ctx, fd->pc2line.buf, fd->pc2line.size);
+ if (!b->debug.pc2line_buf)
+ b->debug.pc2line_buf = fd->pc2line.buf;
+ b->debug.pc2line_len = fd->pc2line.size;
+ b->debug.source = fd->source;
+ b->debug.source_len = fd->source_len;
+ }
+ if (fd->scopes != fd->def_scope_array)
+ js_free(ctx, fd->scopes);
+
+ b->closure_var_count = fd->closure_var_count;
+ if (b->closure_var_count) {
+ b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
+ memcpy(b->closure_var, fd->closure_var, b->closure_var_count * sizeof(*b->closure_var));
+ }
+ js_free(ctx, fd->closure_var);
+ fd->closure_var = NULL;
+
+ b->has_prototype = fd->has_prototype;
+ b->has_simple_parameter_list = fd->has_simple_parameter_list;
+ b->js_mode = fd->js_mode;
+ b->is_derived_class_constructor = fd->is_derived_class_constructor;
+ b->func_kind = fd->func_kind;
+ b->need_home_object = (fd->home_object_var_idx >= 0 ||
+ fd->need_home_object);
+ b->new_target_allowed = fd->new_target_allowed;
+ b->super_call_allowed = fd->super_call_allowed;
+ b->super_allowed = fd->super_allowed;
+ b->arguments_allowed = fd->arguments_allowed;
+ b->backtrace_barrier = fd->backtrace_barrier;
+ b->realm = JS_DupContext(ctx);
+
+ add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
+
+#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 1)
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ js_dump_function_bytecode(ctx, b);
+ }
+#endif
+
+ if (fd->parent) {
+ /* remove from parent list */
+ list_del(&fd->link);
+ }
+
+ js_free(ctx, fd);
+ return JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
+ fail:
+ js_free_function_def(ctx, fd);
+ return JS_EXCEPTION;
+}
+
+static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
+{
+ int i;
+
+#if 0
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ printf("freeing %s\n",
+ JS_AtomGetStrRT(rt, buf, sizeof(buf), b->func_name));
+ }
+#endif
+ free_bytecode_atoms(rt, b->byte_code_buf, b->byte_code_len, TRUE);
+
+ if (b->vardefs) {
+ for(i = 0; i < b->arg_count + b->var_count; i++) {
+ JS_FreeAtomRT(rt, b->vardefs[i].var_name);
+ }
+ }
+ for(i = 0; i < b->cpool_count; i++)
+ JS_FreeValueRT(rt, b->cpool[i]);
+
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ JS_FreeAtomRT(rt, cv->var_name);
+ }
+ if (b->realm)
+ JS_FreeContext(b->realm);
+
+ JS_FreeAtomRT(rt, b->func_name);
+ if (b->has_debug) {
+ JS_FreeAtomRT(rt, b->debug.filename);
+ js_free_rt(rt, b->debug.pc2line_buf);
+ js_free_rt(rt, b->debug.source);
+ }
+
+ remove_gc_object(&b->header);
+ if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && b->header.ref_count != 0) {
+ list_add_tail(&b->header.link, &rt->gc_zero_ref_count_list);
+ } else {
+ js_free_rt(rt, b);
+ }
+}
+
+static warn_unused int js_parse_directives(JSParseState *s)
+{
+ char str[20];
+ JSParsePos pos;
+ BOOL has_semi;
+
+ if (s->token.val != TOK_STRING)
+ return 0;
+
+ js_parse_get_pos(s, &pos);
+
+ while(s->token.val == TOK_STRING) {
+ /* Copy actual source string representation */
+ snprintf(str, sizeof str, "%.*s",
+ (int)(s->buf_ptr - s->token.ptr - 2), s->token.ptr + 1);
+
+ if (next_token(s))
+ return -1;
+
+ has_semi = FALSE;
+ switch (s->token.val) {
+ case ';':
+ if (next_token(s))
+ return -1;
+ has_semi = TRUE;
+ break;
+ case '}':
+ case TOK_EOF:
+ has_semi = TRUE;
+ break;
+ case TOK_NUMBER:
+ case TOK_STRING:
+ case TOK_TEMPLATE:
+ case TOK_IDENT:
+ case TOK_REGEXP:
+ case TOK_DEC:
+ case TOK_INC:
+ case TOK_NULL:
+ case TOK_FALSE:
+ case TOK_TRUE:
+ case TOK_IF:
+ case TOK_RETURN:
+ case TOK_VAR:
+ case TOK_THIS:
+ case TOK_DELETE:
+ case TOK_TYPEOF:
+ case TOK_NEW:
+ case TOK_DO:
+ case TOK_WHILE:
+ case TOK_FOR:
+ case TOK_SWITCH:
+ case TOK_THROW:
+ case TOK_TRY:
+ case TOK_FUNCTION:
+ case TOK_DEBUGGER:
+ case TOK_WITH:
+ case TOK_CLASS:
+ case TOK_CONST:
+ case TOK_ENUM:
+ case TOK_EXPORT:
+ case TOK_IMPORT:
+ case TOK_SUPER:
+ case TOK_INTERFACE:
+ case TOK_LET:
+ case TOK_PACKAGE:
+ case TOK_PRIVATE:
+ case TOK_PROTECTED:
+ case TOK_PUBLIC:
+ case TOK_STATIC:
+ /* automatic insertion of ';' */
+ if (s->got_lf)
+ has_semi = TRUE;
+ break;
+ default:
+ break;
+ }
+ if (!has_semi)
+ break;
+ if (!strcmp(str, "use strict")) {
+ s->cur_func->has_use_strict = TRUE;
+ s->cur_func->js_mode |= JS_MODE_STRICT;
+ }
+#if !defined(DUMP_BYTECODE) || !(DUMP_BYTECODE & 8)
+ else if (!strcmp(str, "use strip")) {
+ s->cur_func->js_mode |= JS_MODE_STRIP;
+ }
+#endif
+#ifdef CONFIG_BIGNUM
+ else if (s->ctx->bignum_ext && !strcmp(str, "use math")) {
+ s->cur_func->js_mode |= JS_MODE_MATH;
+ }
+#endif
+ }
+ return js_parse_seek_token(s, &pos);
+}
+
+static int js_parse_function_check_names(JSParseState *s, JSFunctionDef *fd,
+ JSAtom func_name)
+{
+ JSAtom name;
+ int i, idx;
+
+ if (fd->js_mode & JS_MODE_STRICT) {
+ if (!fd->has_simple_parameter_list && fd->has_use_strict) {
+ return js_parse_error(s, "\"use strict\" not allowed in function with default or destructuring parameter");
+ }
+ if (func_name == JS_ATOM_eval || func_name == JS_ATOM_arguments) {
+ return js_parse_error(s, "invalid function name in strict code");
+ }
+ for (idx = 0; idx < fd->arg_count; idx++) {
+ name = fd->args[idx].var_name;
+
+ if (name == JS_ATOM_eval || name == JS_ATOM_arguments) {
+ return js_parse_error(s, "invalid argument name in strict code");
+ }
+ }
+ }
+ /* check async_generator case */
+ if ((fd->js_mode & JS_MODE_STRICT)
+ || !fd->has_simple_parameter_list
+ || (fd->func_type == JS_PARSE_FUNC_METHOD && fd->func_kind == JS_FUNC_ASYNC)
+ || fd->func_type == JS_PARSE_FUNC_ARROW
+ || fd->func_type == JS_PARSE_FUNC_METHOD) {
+ for (idx = 0; idx < fd->arg_count; idx++) {
+ name = fd->args[idx].var_name;
+ if (name != JS_ATOM_NULL) {
+ for (i = 0; i < idx; i++) {
+ if (fd->args[i].var_name == name)
+ goto duplicate;
+ }
+ /* Check if argument name duplicates a destructuring parameter */
+ /* XXX: should have a flag for such variables */
+ for (i = 0; i < fd->var_count; i++) {
+ if (fd->vars[i].var_name == name &&
+ fd->vars[i].scope_level == 0)
+ goto duplicate;
+ }
+ }
+ }
+ }
+ return 0;
+
+duplicate:
+ return js_parse_error(s, "duplicate argument names not allowed in this context");
+}
+
+/* create a function to initialize class fields */
+static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)
+{
+ JSFunctionDef *fd;
+
+ fd = js_new_function_def(s->ctx, s->cur_func, FALSE, FALSE,
+ s->filename, 0);
+ if (!fd)
+ return NULL;
+ fd->func_name = JS_ATOM_NULL;
+ fd->has_prototype = FALSE;
+ fd->has_home_object = TRUE;
+
+ fd->has_arguments_binding = FALSE;
+ fd->has_this_binding = TRUE;
+ fd->is_derived_class_constructor = FALSE;
+ fd->new_target_allowed = TRUE;
+ fd->super_call_allowed = FALSE;
+ fd->super_allowed = fd->has_home_object;
+ fd->arguments_allowed = FALSE;
+
+ fd->func_kind = JS_FUNC_NORMAL;
+ fd->func_type = JS_PARSE_FUNC_METHOD;
+ return fd;
+}
+
+/* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and
+ JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */
+static warn_unused int js_parse_function_decl2(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+ const uint8_t *ptr,
+ int function_line_num,
+ JSParseExportEnum export_flag,
+ JSFunctionDef **pfd)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionDef *fd = s->cur_func;
+ BOOL is_expr;
+ int func_idx, lexical_func_idx = -1;
+ BOOL has_opt_arg;
+ BOOL create_func_var = FALSE;
+
+ is_expr = (func_type != JS_PARSE_FUNC_STATEMENT &&
+ func_type != JS_PARSE_FUNC_VAR);
+
+ if (func_type == JS_PARSE_FUNC_STATEMENT ||
+ func_type == JS_PARSE_FUNC_VAR ||
+ func_type == JS_PARSE_FUNC_EXPR) {
+ if (func_kind == JS_FUNC_NORMAL &&
+ token_is_pseudo_keyword(s, JS_ATOM_async) &&
+ peek_token(s, TRUE) != '\n') {
+ if (next_token(s))
+ return -1;
+ func_kind = JS_FUNC_ASYNC;
+ }
+ if (next_token(s))
+ return -1;
+ if (s->token.val == '*') {
+ if (next_token(s))
+ return -1;
+ func_kind |= JS_FUNC_GENERATOR;
+ }
+
+ if (s->token.val == TOK_IDENT) {
+ if (s->token.u.ident.is_reserved ||
+ (s->token.u.ident.atom == JS_ATOM_yield &&
+ func_type == JS_PARSE_FUNC_EXPR &&
+ (func_kind & JS_FUNC_GENERATOR)) ||
+ (s->token.u.ident.atom == JS_ATOM_await &&
+ func_type == JS_PARSE_FUNC_EXPR &&
+ (func_kind & JS_FUNC_ASYNC))) {
+ return js_parse_error_reserved_identifier(s);
+ }
+ }
+ if (s->token.val == TOK_IDENT ||
+ (((s->token.val == TOK_YIELD && !(fd->js_mode & JS_MODE_STRICT)) ||
+ (s->token.val == TOK_AWAIT && !s->is_module)) &&
+ func_type == JS_PARSE_FUNC_EXPR)) {
+ func_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ if (next_token(s)) {
+ JS_FreeAtom(ctx, func_name);
+ return -1;
+ }
+ } else {
+ if (func_type != JS_PARSE_FUNC_EXPR &&
+ export_flag != JS_PARSE_EXPORT_DEFAULT) {
+ return js_parse_error(s, "function name expected");
+ }
+ }
+ } else if (func_type != JS_PARSE_FUNC_ARROW) {
+ func_name = JS_DupAtom(ctx, func_name);
+ }
+
+ if (fd->is_eval && fd->eval_type == JS_EVAL_TYPE_MODULE &&
+ (func_type == JS_PARSE_FUNC_STATEMENT || func_type == JS_PARSE_FUNC_VAR)) {
+ JSGlobalVar *hf;
+ hf = find_global_var(fd, func_name);
+ /* XXX: should check scope chain */
+ if (hf && hf->scope_level == fd->scope_level) {
+ js_parse_error(s, "invalid redefinition of global identifier in module code");
+ JS_FreeAtom(ctx, func_name);
+ return -1;
+ }
+ }
+
+ if (func_type == JS_PARSE_FUNC_VAR) {
+ if (!(fd->js_mode & JS_MODE_STRICT)
+ && func_kind == JS_FUNC_NORMAL
+ && find_lexical_decl(ctx, fd, func_name, fd->scope_first, FALSE) < 0
+ && !((func_idx = find_var(ctx, fd, func_name)) >= 0 && (func_idx & ARGUMENT_VAR_OFFSET))
+ && !(func_name == JS_ATOM_arguments && fd->has_arguments_binding)) {
+ create_func_var = TRUE;
+ }
+ /* Create the lexical name here so that the function closure
+ contains it */
+ if (fd->is_eval &&
+ (fd->eval_type == JS_EVAL_TYPE_GLOBAL ||
+ fd->eval_type == JS_EVAL_TYPE_MODULE) &&
+ fd->scope_level == fd->body_scope) {
+ /* avoid creating a lexical variable in the global
+ scope. XXX: check annex B */
+ JSGlobalVar *hf;
+ hf = find_global_var(fd, func_name);
+ /* XXX: should check scope chain */
+ if (hf && hf->scope_level == fd->scope_level) {
+ js_parse_error(s, "invalid redefinition of global identifier");
+ JS_FreeAtom(ctx, func_name);
+ return -1;
+ }
+ } else {
+ /* Always create a lexical name, fail if at the same scope as
+ existing name */
+ /* Lexical variable will be initialized upon entering scope */
+ lexical_func_idx = define_var(s, fd, func_name,
+ func_kind != JS_FUNC_NORMAL ?
+ JS_VAR_DEF_NEW_FUNCTION_DECL :
+ JS_VAR_DEF_FUNCTION_DECL);
+ if (lexical_func_idx < 0) {
+ JS_FreeAtom(ctx, func_name);
+ return -1;
+ }
+ }
+ }
+
+ fd = js_new_function_def(ctx, fd, FALSE, is_expr,
+ s->filename, function_line_num);
+ if (!fd) {
+ JS_FreeAtom(ctx, func_name);
+ return -1;
+ }
+ if (pfd)
+ *pfd = fd;
+ s->cur_func = fd;
+ fd->func_name = func_name;
+ /* XXX: test !fd->is_generator is always false */
+ fd->has_prototype = (func_type == JS_PARSE_FUNC_STATEMENT ||
+ func_type == JS_PARSE_FUNC_VAR ||
+ func_type == JS_PARSE_FUNC_EXPR) &&
+ func_kind == JS_FUNC_NORMAL;
+ fd->has_home_object = (func_type == JS_PARSE_FUNC_METHOD ||
+ func_type == JS_PARSE_FUNC_GETTER ||
+ func_type == JS_PARSE_FUNC_SETTER ||
+ func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
+ func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
+ fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW);
+ fd->has_this_binding = fd->has_arguments_binding;
+ fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR);
+ if (func_type == JS_PARSE_FUNC_ARROW) {
+ fd->new_target_allowed = fd->parent->new_target_allowed;
+ fd->super_call_allowed = fd->parent->super_call_allowed;
+ fd->super_allowed = fd->parent->super_allowed;
+ fd->arguments_allowed = fd->parent->arguments_allowed;
+ } else {
+ fd->new_target_allowed = TRUE;
+ fd->super_call_allowed = fd->is_derived_class_constructor;
+ fd->super_allowed = fd->has_home_object;
+ fd->arguments_allowed = TRUE;
+ }
+
+ /* fd->in_function_body == FALSE prevents yield/await during the parsing
+ of the arguments in generator/async functions. They are parsed as
+ regular identifiers for other function kinds. */
+ fd->func_kind = func_kind;
+ fd->func_type = func_type;
+
+ if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR ||
+ func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
+ /* error if not invoked as a constructor */
+ emit_op(s, OP_check_ctor);
+ }
+
+ if (func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR) {
+ emit_class_field_init(s);
+ }
+
+ /* parse arguments */
+ fd->has_simple_parameter_list = TRUE;
+ fd->has_parameter_expressions = FALSE;
+ has_opt_arg = FALSE;
+ if (func_type == JS_PARSE_FUNC_ARROW && s->token.val == TOK_IDENT) {
+ JSAtom name;
+ if (s->token.u.ident.is_reserved) {
+ js_parse_error_reserved_identifier(s);
+ goto fail;
+ }
+ name = s->token.u.ident.atom;
+ if (add_arg(ctx, fd, name) < 0)
+ goto fail;
+ fd->defined_arg_count = 1;
+ } else {
+ if (s->token.val == '(') {
+ int skip_bits;
+ /* if there is an '=' inside the parameter list, we
+ consider there is a parameter expression inside */
+ js_parse_skip_parens_token(s, &skip_bits, FALSE);
+ if (skip_bits & SKIP_HAS_ASSIGNMENT)
+ fd->has_parameter_expressions = TRUE;
+ if (next_token(s))
+ goto fail;
+ } else {
+ if (js_parse_expect(s, '('))
+ goto fail;
+ }
+
+ if (fd->has_parameter_expressions) {
+ fd->scope_level = -1; /* force no parent scope */
+ if (push_scope(s) < 0)
+ return -1;
+ }
+
+ while (s->token.val != ')') {
+ JSAtom name;
+ BOOL rest = FALSE;
+ int idx, has_initializer;
+
+ if (s->token.val == TOK_ELLIPSIS) {
+ fd->has_simple_parameter_list = FALSE;
+ rest = TRUE;
+ if (next_token(s))
+ goto fail;
+ }
+ if (s->token.val == '[' || s->token.val == '{') {
+ fd->has_simple_parameter_list = FALSE;
+ if (rest) {
+ emit_op(s, OP_rest);
+ emit_u16(s, fd->arg_count);
+ } else {
+ /* unnamed arg for destructuring */
+ idx = add_arg(ctx, fd, JS_ATOM_NULL);
+ emit_op(s, OP_get_arg);
+ emit_u16(s, idx);
+ }
+ has_initializer = js_parse_destructuring_element(s, fd->has_parameter_expressions ? TOK_LET : TOK_VAR, 1, TRUE, -1, TRUE);
+ if (has_initializer < 0)
+ goto fail;
+ if (has_initializer)
+ has_opt_arg = TRUE;
+ if (!has_opt_arg)
+ fd->defined_arg_count++;
+ } else if (s->token.val == TOK_IDENT) {
+ if (s->token.u.ident.is_reserved) {
+ js_parse_error_reserved_identifier(s);
+ goto fail;
+ }
+ name = s->token.u.ident.atom;
+ if (name == JS_ATOM_yield && fd->func_kind == JS_FUNC_GENERATOR) {
+ js_parse_error_reserved_identifier(s);
+ goto fail;
+ }
+ if (fd->has_parameter_expressions) {
+ if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0)
+ goto fail;
+ }
+ /* XXX: could avoid allocating an argument if rest is true */
+ idx = add_arg(ctx, fd, name);
+ if (idx < 0)
+ goto fail;
+ if (next_token(s))
+ goto fail;
+ if (rest) {
+ emit_op(s, OP_rest);
+ emit_u16(s, idx);
+ if (fd->has_parameter_expressions) {
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ }
+ emit_op(s, OP_put_arg);
+ emit_u16(s, idx);
+ fd->has_simple_parameter_list = FALSE;
+ has_opt_arg = TRUE;
+ } else if (s->token.val == '=') {
+ int label;
+
+ fd->has_simple_parameter_list = FALSE;
+ has_opt_arg = TRUE;
+
+ if (next_token(s))
+ goto fail;
+
+ label = new_label(s);
+ emit_op(s, OP_get_arg);
+ emit_u16(s, idx);
+ emit_op(s, OP_dup);
+ emit_op(s, OP_undefined);
+ emit_op(s, OP_strict_eq);
+ emit_goto(s, OP_if_false, label);
+ emit_op(s, OP_drop);
+ if (js_parse_assign_expr(s))
+ goto fail;
+ set_object_name(s, name);
+ emit_op(s, OP_dup);
+ emit_op(s, OP_put_arg);
+ emit_u16(s, idx);
+ emit_label(s, label);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ } else {
+ if (!has_opt_arg) {
+ fd->defined_arg_count++;
+ }
+ if (fd->has_parameter_expressions) {
+ /* copy the argument to the argument scope */
+ emit_op(s, OP_get_arg);
+ emit_u16(s, idx);
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, name);
+ emit_u16(s, fd->scope_level);
+ }
+ }
+ } else {
+ js_parse_error(s, "missing formal parameter");
+ goto fail;
+ }
+ if (rest && s->token.val != ')') {
+ js_parse_expect(s, ')');
+ goto fail;
+ }
+ if (s->token.val == ')')
+ break;
+ if (js_parse_expect(s, ','))
+ goto fail;
+ }
+ if ((func_type == JS_PARSE_FUNC_GETTER && fd->arg_count != 0) ||
+ (func_type == JS_PARSE_FUNC_SETTER && fd->arg_count != 1)) {
+ js_parse_error(s, "invalid number of arguments for getter or setter");
+ goto fail;
+ }
+ }
+
+ if (fd->has_parameter_expressions) {
+ int idx;
+
+ /* Copy the variables in the argument scope to the variable
+ scope (see FunctionDeclarationInstantiation() in spec). The
+ normal arguments are already present, so no need to copy
+ them. */
+ idx = fd->scopes[fd->scope_level].first;
+ while (idx >= 0) {
+ JSVarDef *vd = &fd->vars[idx];
+ if (vd->scope_level != fd->scope_level)
+ break;
+ if (find_var(ctx, fd, vd->var_name) < 0) {
+ if (add_var(ctx, fd, vd->var_name) < 0)
+ goto fail;
+ vd = &fd->vars[idx]; /* fd->vars may have been reallocated */
+ emit_op(s, OP_scope_get_var);
+ emit_atom(s, vd->var_name);
+ emit_u16(s, fd->scope_level);
+ emit_op(s, OP_scope_put_var);
+ emit_atom(s, vd->var_name);
+ emit_u16(s, 0);
+ }
+ idx = vd->scope_next;
+ }
+
+ /* the argument scope has no parent, hence we don't use pop_scope(s) */
+ emit_op(s, OP_leave_scope);
+ emit_u16(s, fd->scope_level);
+
+ /* set the variable scope as the current scope */
+ fd->scope_level = 0;
+ fd->scope_first = fd->scopes[fd->scope_level].first;
+ }
+
+ if (next_token(s))
+ goto fail;
+
+ /* generator function: yield after the parameters are evaluated */
+ if (func_kind == JS_FUNC_GENERATOR ||
+ func_kind == JS_FUNC_ASYNC_GENERATOR)
+ emit_op(s, OP_initial_yield);
+
+ /* in generators, yield expression is forbidden during the parsing
+ of the arguments */
+ fd->in_function_body = TRUE;
+ push_scope(s); /* enter body scope */
+ fd->body_scope = fd->scope_level;
+
+ if (s->token.val == TOK_ARROW) {
+ if (next_token(s))
+ goto fail;
+
+ if (s->token.val != '{') {
+ if (js_parse_function_check_names(s, fd, func_name))
+ goto fail;
+
+ if (js_parse_assign_expr(s))
+ goto fail;
+
+ if (func_kind != JS_FUNC_NORMAL)
+ emit_op(s, OP_return_async);
+ else
+ emit_op(s, OP_return);
+
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ /* save the function source code */
+ /* the end of the function source code is after the last
+ token of the function source stored into s->last_ptr */
+ fd->source_len = s->last_ptr - ptr;
+ fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
+ if (!fd->source)
+ goto fail;
+ }
+ goto done;
+ }
+ }
+
+ if (js_parse_expect(s, '{'))
+ goto fail;
+
+ if (js_parse_directives(s))
+ goto fail;
+
+ /* in strict_mode, check function and argument names */
+ if (js_parse_function_check_names(s, fd, func_name))
+ goto fail;
+
+ while (s->token.val != '}') {
+ if (js_parse_source_element(s))
+ goto fail;
+ }
+ if (!(fd->js_mode & JS_MODE_STRIP)) {
+ /* save the function source code */
+ fd->source_len = s->buf_ptr - ptr;
+ fd->source = js_strndup(ctx, (const char *)ptr, fd->source_len);
+ if (!fd->source)
+ goto fail;
+ }
+
+ if (next_token(s)) {
+ /* consume the '}' */
+ goto fail;
+ }
+
+ /* in case there is no return, add one */
+ if (js_is_live_code(s)) {
+ emit_return(s, FALSE);
+ }
+done:
+ s->cur_func = fd->parent;
+
+ /* create the function object */
+ {
+ int idx;
+ JSAtom func_name = fd->func_name;
+
+ /* the real object will be set at the end of the compilation */
+ idx = cpool_add(s, JS_NULL);
+ fd->parent_cpool_idx = idx;
+
+ if (is_expr) {
+ /* for constructors, no code needs to be generated here */
+ if (func_type != JS_PARSE_FUNC_CLASS_CONSTRUCTOR &&
+ func_type != JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR) {
+ /* OP_fclosure creates the function object from the bytecode
+ and adds the scope information */
+ emit_op(s, OP_fclosure);
+ emit_u32(s, idx);
+ if (func_name == JS_ATOM_NULL) {
+ emit_op(s, OP_set_name);
+ emit_u32(s, JS_ATOM_NULL);
+ }
+ }
+ } else if (func_type == JS_PARSE_FUNC_VAR) {
+ emit_op(s, OP_fclosure);
+ emit_u32(s, idx);
+ if (create_func_var) {
+ if (s->cur_func->is_global_var) {
+ JSGlobalVar *hf;
+ /* the global variable must be defined at the start of the
+ function */
+ hf = add_global_var(ctx, s->cur_func, func_name);
+ if (!hf)
+ goto fail;
+ /* it is considered as defined at the top level
+ (needed for annex B.3.3.4 and B.3.3.5
+ checks) */
+ hf->scope_level = 0;
+ hf->force_init = ((s->cur_func->js_mode & JS_MODE_STRICT) != 0);
+ /* store directly into global var, bypass lexical scope */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var);
+ emit_atom(s, func_name);
+ emit_u16(s, 0);
+ } else {
+ /* do not call define_var to bypass lexical scope check */
+ func_idx = find_var(ctx, s->cur_func, func_name);
+ if (func_idx < 0) {
+ func_idx = add_var(ctx, s->cur_func, func_name);
+ if (func_idx < 0)
+ goto fail;
+ }
+ /* store directly into local var, bypass lexical catch scope */
+ emit_op(s, OP_dup);
+ emit_op(s, OP_scope_put_var);
+ emit_atom(s, func_name);
+ emit_u16(s, 0);
+ }
+ }
+ if (lexical_func_idx >= 0) {
+ /* lexical variable will be initialized upon entering scope */
+ s->cur_func->vars[lexical_func_idx].func_pool_idx = idx;
+ emit_op(s, OP_drop);
+ } else {
+ /* store function object into its lexical name */
+ /* XXX: could use OP_put_loc directly */
+ emit_op(s, OP_scope_put_var_init);
+ emit_atom(s, func_name);
+ emit_u16(s, s->cur_func->scope_level);
+ }
+ } else {
+ if (!s->cur_func->is_global_var) {
+ int var_idx = define_var(s, s->cur_func, func_name, JS_VAR_DEF_VAR);
+
+ if (var_idx < 0)
+ goto fail;
+ /* the variable will be assigned at the top of the function */
+ if (var_idx & ARGUMENT_VAR_OFFSET) {
+ s->cur_func->args[var_idx - ARGUMENT_VAR_OFFSET].func_pool_idx = idx;
+ } else {
+ s->cur_func->vars[var_idx].func_pool_idx = idx;
+ }
+ } else {
+ JSAtom func_var_name;
+ JSGlobalVar *hf;
+ if (func_name == JS_ATOM_NULL)
+ func_var_name = JS_ATOM__default_; /* export default */
+ else
+ func_var_name = func_name;
+ /* the variable will be assigned at the top of the function */
+ hf = add_global_var(ctx, s->cur_func, func_var_name);
+ if (!hf)
+ goto fail;
+ hf->cpool_idx = idx;
+ if (export_flag != JS_PARSE_EXPORT_NONE) {
+ if (!add_export_entry(s, s->cur_func->module, func_var_name,
+ export_flag == JS_PARSE_EXPORT_NAMED ? func_var_name : JS_ATOM_default, JS_EXPORT_TYPE_LOCAL))
+ goto fail;
+ }
+ }
+ }
+ }
+ return 0;
+ fail:
+ s->cur_func = fd->parent;
+ js_free_function_def(ctx, fd);
+ if (pfd)
+ *pfd = NULL;
+ return -1;
+}
+
+static warn_unused int js_parse_function_decl(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+ const uint8_t *ptr,
+ int function_line_num)
+{
+ return js_parse_function_decl2(s, func_type, func_kind, func_name, ptr,
+ function_line_num, JS_PARSE_EXPORT_NONE,
+ NULL);
+}
+
+static warn_unused int js_parse_program(JSParseState *s)
+{
+ JSFunctionDef *fd = s->cur_func;
+ int idx;
+
+ if (next_token(s))
+ return -1;
+
+ if (js_parse_directives(s))
+ return -1;
+
+ fd->is_global_var = (fd->eval_type == JS_EVAL_TYPE_GLOBAL) ||
+ (fd->eval_type == JS_EVAL_TYPE_MODULE) ||
+ !(fd->js_mode & JS_MODE_STRICT);
+
+ if (!s->is_module) {
+ /* hidden variable for the return value */
+ fd->eval_ret_idx = idx = add_var(s->ctx, fd, JS_ATOM__ret_);
+ if (idx < 0)
+ return -1;
+ }
+
+ while (s->token.val != TOK_EOF) {
+ if (js_parse_source_element(s))
+ return -1;
+ }
+
+ if (!s->is_module) {
+ /* return the value of the hidden variable eval_ret_idx */
+ emit_op(s, OP_get_loc);
+ emit_u16(s, fd->eval_ret_idx);
+
+ emit_op(s, OP_return);
+ } else {
+ emit_op(s, OP_return_undef);
+ }
+
+ return 0;
+}
+
+static void js_parse_init(JSContext *ctx, JSParseState *s,
+ const char *input, size_t input_len,
+ const char *filename, int line)
+{
+ memset(s, 0, sizeof(*s));
+ s->ctx = ctx;
+ s->filename = filename;
+ s->line_num = line;
+ s->buf_ptr = (const uint8_t *)input;
+ s->buf_end = s->buf_ptr + input_len;
+ s->token.val = ' ';
+ s->token.line_num = 1;
+}
+
+static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
+ JSValueConst this_obj,
+ JSVarRef **var_refs, JSStackFrame *sf)
+{
+ JSValue ret_val;
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_TAG(fun_obj);
+ if (tag == JS_TAG_FUNCTION_BYTECODE) {
+ fun_obj = js_closure(ctx, fun_obj, var_refs, sf);
+ ret_val = JS_CallFree(ctx, fun_obj, this_obj, 0, NULL);
+ } else if (tag == JS_TAG_MODULE) {
+ JSModuleDef *m;
+ m = JS_VALUE_GET_PTR(fun_obj);
+ /* the module refcount should be >= 2 */
+ JS_FreeValue(ctx, fun_obj);
+ if (js_create_module_function(ctx, m) < 0)
+ goto fail;
+ if (js_link_module(ctx, m) < 0)
+ goto fail;
+ ret_val = js_evaluate_module(ctx, m);
+ if (JS_IsException(ret_val)) {
+ fail:
+ js_free_modules(ctx, JS_FREE_MODULE_NOT_EVALUATED);
+ return JS_EXCEPTION;
+ }
+ } else {
+ JS_FreeValue(ctx, fun_obj);
+ ret_val = JS_ThrowTypeError(ctx, "bytecode function expected");
+ }
+ return ret_val;
+}
+
+JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj)
+{
+ return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL);
+}
+
+static void skip_shebang(JSParseState *s)
+{
+ const uint8_t *p = s->buf_ptr;
+ int c;
+
+ if (p[0] == '#' && p[1] == '!') {
+ p += 2;
+ while (p < s->buf_end) {
+ if (*p == '\n' || *p == '\r') {
+ break;
+ } else if (*p >= 0x80) {
+ c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p);
+ if (c == CP_LS || c == CP_PS) {
+ break;
+ } else if (c == -1) {
+ p++; /* skip invalid UTF-8 */
+ }
+ } else {
+ p++;
+ }
+ }
+ s->buf_ptr = p;
+ }
+}
+
+/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
+static JSValue JS_EvalInternalImpl(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int flags, int scope_idx)
+{
+ JSParseState s1, *s = &s1;
+ int err, js_mode, eval_type;
+ JSValue fun_obj, ret_val;
+ JSStackFrame *sf;
+ JSVarRef **var_refs;
+ JSFunctionBytecode *b;
+ JSFunctionDef *fd;
+ JSModuleDef *m;
+
+ js_parse_init(ctx, s, input, input_len, filename, line);
+ skip_shebang(s);
+
+ eval_type = flags & JS_EVAL_TYPE_MASK;
+ m = NULL;
+ if (eval_type == JS_EVAL_TYPE_DIRECT) {
+ JSObject *p;
+ sf = ctx->rt->current_stack_frame;
+ assert(sf != NULL);
+ assert(JS_VALUE_GET_TAG(sf->cur_func) == JS_TAG_OBJECT);
+ p = JS_VALUE_GET_OBJ(sf->cur_func);
+ assert(js_class_has_bytecode(p->class_id));
+ b = p->u.func.function_bytecode;
+ var_refs = p->u.func.var_refs;
+ js_mode = b->js_mode;
+ } else {
+ sf = NULL;
+ b = NULL;
+ var_refs = NULL;
+ js_mode = 0;
+ if (flags & JS_EVAL_FLAG_STRICT)
+ js_mode |= JS_MODE_STRICT;
+ if (flags & JS_EVAL_FLAG_STRIP)
+ js_mode |= JS_MODE_STRIP;
+ if (eval_type == JS_EVAL_TYPE_MODULE) {
+ JSAtom module_name = JS_NewAtom(ctx, filename);
+ if (module_name == JS_ATOM_NULL)
+ return JS_EXCEPTION;
+ m = js_new_module_def(ctx, module_name);
+ if (!m)
+ return JS_EXCEPTION;
+ js_mode |= JS_MODE_STRICT;
+ }
+ }
+ fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, line);
+ if (!fd)
+ goto fail1;
+ s->cur_func = fd;
+ fd->eval_type = eval_type;
+ fd->has_this_binding = (eval_type != JS_EVAL_TYPE_DIRECT);
+ fd->backtrace_barrier = ((flags & JS_EVAL_FLAG_BACKTRACE_BARRIER) != 0);
+ if (eval_type == JS_EVAL_TYPE_DIRECT) {
+ fd->new_target_allowed = b->new_target_allowed;
+ fd->super_call_allowed = b->super_call_allowed;
+ fd->super_allowed = b->super_allowed;
+ fd->arguments_allowed = b->arguments_allowed;
+ } else {
+ fd->new_target_allowed = FALSE;
+ fd->super_call_allowed = FALSE;
+ fd->super_allowed = FALSE;
+ fd->arguments_allowed = TRUE;
+ }
+ fd->js_mode = js_mode;
+ fd->func_name = JS_DupAtom(ctx, JS_ATOM__eval_);
+ if (b) {
+ if (add_closure_variables(ctx, fd, b, scope_idx))
+ goto fail;
+ }
+ fd->module = m;
+ s->is_module = (m != NULL);
+ s->allow_html_comments = !s->is_module;
+
+ push_scope(s); /* body scope */
+ fd->body_scope = fd->scope_level;
+
+ err = js_parse_program(s);
+ if (err) {
+ fail:
+ free_token(s, &s->token);
+ js_free_function_def(ctx, fd);
+ goto fail1;
+ }
+
+ /* create the function object and all the enclosed functions */
+ fun_obj = js_create_function(ctx, fd);
+ if (JS_IsException(fun_obj))
+ goto fail1;
+ /* Could add a flag to avoid resolution if necessary */
+ if (m) {
+ m->func_obj = fun_obj;
+ if (js_resolve_module(ctx, m) < 0)
+ goto fail1;
+ fun_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+ }
+ if (flags & JS_EVAL_FLAG_COMPILE_ONLY) {
+ ret_val = fun_obj;
+ } else {
+ ret_val = JS_EvalFunctionInternal(ctx, fun_obj, this_obj, var_refs, sf);
+ }
+ return ret_val;
+ fail1:
+ /* XXX: should free all the unresolved dependencies */
+ if (m)
+ js_free_module_def(ctx, m);
+ return JS_EXCEPTION;
+}
+
+/* the indirection is needed to make 'eval' optional */
+static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int flags, int scope_idx)
+{
+ if (unlikely(!ctx->eval_internal)) {
+ return JS_ThrowTypeError(ctx, "eval is not supported");
+ }
+ return ctx->eval_internal(ctx, this_obj, input, input_len, filename, line,
+ flags, scope_idx);
+}
+
+static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
+ JSValueConst val, int flags, int scope_idx)
+{
+ JSValue ret;
+ const char *str;
+ size_t len;
+
+ if (!JS_IsString(val))
+ return JS_DupValue(ctx, val);
+ str = JS_ToCStringLen(ctx, &len, val);
+ if (!str)
+ return JS_EXCEPTION;
+ ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", 1, flags, scope_idx);
+ JS_FreeCString(ctx, str);
+ return ret;
+
+}
+
+JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int eval_flags)
+{
+ int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
+ JSValue ret;
+
+ assert(eval_type == JS_EVAL_TYPE_GLOBAL ||
+ eval_type == JS_EVAL_TYPE_MODULE);
+ ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, line,
+ eval_flags, -1);
+ return ret;
+}
+
+JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
+ const char *filename, int eval_flags)
+{
+ return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, 1,
+ eval_flags);
+}
+
+int JS_ResolveModule(JSContext *ctx, JSValueConst obj)
+{
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_MODULE) {
+ JSModuleDef *m = JS_VALUE_GET_PTR(obj);
+ if (js_resolve_module(ctx, m) < 0) {
+ js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*******************************************************************/
+/* object list */
+
+typedef struct {
+ JSObject *obj;
+ uint32_t hash_next; /* -1 if no next entry */
+} JSObjectListEntry;
+
+/* XXX: reuse it to optimize weak references */
+typedef struct {
+ JSObjectListEntry *object_tab;
+ int object_count;
+ int object_size;
+ uint32_t *hash_table;
+ uint32_t hash_size;
+} JSObjectList;
+
+static void js_object_list_init(JSObjectList *s)
+{
+ memset(s, 0, sizeof(*s));
+}
+
+static uint32_t js_object_list_get_hash(JSObject *p, uint32_t hash_size)
+{
+ return ((uintptr_t)p * 3163) & (hash_size - 1);
+}
+
+static int js_object_list_resize_hash(JSContext *ctx, JSObjectList *s,
+ uint32_t new_hash_size)
+{
+ JSObjectListEntry *e;
+ uint32_t i, h, *new_hash_table;
+
+ new_hash_table = js_malloc(ctx, sizeof(new_hash_table[0]) * new_hash_size);
+ if (!new_hash_table)
+ return -1;
+ js_free(ctx, s->hash_table);
+ s->hash_table = new_hash_table;
+ s->hash_size = new_hash_size;
+
+ for(i = 0; i < s->hash_size; i++) {
+ s->hash_table[i] = -1;
+ }
+ for(i = 0; i < s->object_count; i++) {
+ e = &s->object_tab[i];
+ h = js_object_list_get_hash(e->obj, s->hash_size);
+ e->hash_next = s->hash_table[h];
+ s->hash_table[h] = i;
+ }
+ return 0;
+}
+
+/* the reference count of 'obj' is not modified. Return 0 if OK, -1 if
+ memory error */
+static int js_object_list_add(JSContext *ctx, JSObjectList *s, JSObject *obj)
+{
+ JSObjectListEntry *e;
+ uint32_t h, new_hash_size;
+
+ if (js_resize_array(ctx, (void *)&s->object_tab,
+ sizeof(s->object_tab[0]),
+ &s->object_size, s->object_count + 1))
+ return -1;
+ if (unlikely((s->object_count + 1) >= s->hash_size)) {
+ new_hash_size = max_uint32(s->hash_size, 4);
+ while (new_hash_size <= s->object_count)
+ new_hash_size *= 2;
+ if (js_object_list_resize_hash(ctx, s, new_hash_size))
+ return -1;
+ }
+ e = &s->object_tab[s->object_count++];
+ h = js_object_list_get_hash(obj, s->hash_size);
+ e->obj = obj;
+ e->hash_next = s->hash_table[h];
+ s->hash_table[h] = s->object_count - 1;
+ return 0;
+}
+
+/* return -1 if not present or the object index */
+static int js_object_list_find(JSContext *ctx, JSObjectList *s, JSObject *obj)
+{
+ JSObjectListEntry *e;
+ uint32_t h, p;
+
+ /* must test empty size because there is no hash table */
+ if (s->object_count == 0)
+ return -1;
+ h = js_object_list_get_hash(obj, s->hash_size);
+ p = s->hash_table[h];
+ while (p != -1) {
+ e = &s->object_tab[p];
+ if (e->obj == obj)
+ return p;
+ p = e->hash_next;
+ }
+ return -1;
+}
+
+static void js_object_list_end(JSContext *ctx, JSObjectList *s)
+{
+ js_free(ctx, s->object_tab);
+ js_free(ctx, s->hash_table);
+}
+
+/*******************************************************************/
+/* binary object writer & reader */
+
+typedef enum BCTagEnum {
+ BC_TAG_NULL = 1,
+ BC_TAG_UNDEFINED,
+ BC_TAG_BOOL_FALSE,
+ BC_TAG_BOOL_TRUE,
+ BC_TAG_INT32,
+ BC_TAG_FLOAT64,
+ BC_TAG_STRING,
+ BC_TAG_OBJECT,
+ BC_TAG_ARRAY,
+ BC_TAG_BIG_INT,
+ BC_TAG_BIG_FLOAT,
+ BC_TAG_BIG_DECIMAL,
+ BC_TAG_TEMPLATE_OBJECT,
+ BC_TAG_FUNCTION_BYTECODE,
+ BC_TAG_MODULE,
+ BC_TAG_TYPED_ARRAY,
+ BC_TAG_ARRAY_BUFFER,
+ BC_TAG_SHARED_ARRAY_BUFFER,
+ BC_TAG_DATE,
+ BC_TAG_OBJECT_VALUE,
+ BC_TAG_OBJECT_REFERENCE,
+} BCTagEnum;
+
+#ifdef CONFIG_BIGNUM
+#define BC_BASE_VERSION 2
+#else
+#define BC_BASE_VERSION 1
+#endif
+#define BC_BE_VERSION 0x40
+#ifdef WORDS_BIGENDIAN
+#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION)
+#else
+#define BC_VERSION BC_BASE_VERSION
+#endif
+
+typedef struct BCWriterState {
+ JSContext *ctx;
+ DynBuf dbuf;
+ BOOL byte_swap : 8;
+ BOOL allow_bytecode : 8;
+ BOOL allow_sab : 8;
+ BOOL allow_reference : 8;
+ uint32_t first_atom;
+ uint32_t *atom_to_idx;
+ int atom_to_idx_size;
+ JSAtom *idx_to_atom;
+ int idx_to_atom_count;
+ int idx_to_atom_size;
+ uint8_t **sab_tab;
+ int sab_tab_len;
+ int sab_tab_size;
+ /* list of referenced objects (used if allow_reference = TRUE) */
+ JSObjectList object_list;
+} BCWriterState;
+
+#ifdef DUMP_READ_OBJECT
+static const char * const bc_tag_str[] = {
+ "invalid",
+ "null",
+ "undefined",
+ "false",
+ "true",
+ "int32",
+ "float64",
+ "string",
+ "object",
+ "array",
+ "bigint",
+ "bigfloat",
+ "bigdecimal",
+ "template",
+ "function",
+ "module",
+ "TypedArray",
+ "ArrayBuffer",
+ "SharedArrayBuffer",
+ "Date",
+ "ObjectValue",
+ "ObjectReference",
+};
+#endif
+
+static void bc_put_u8(BCWriterState *s, uint8_t v)
+{
+ dbuf_putc(&s->dbuf, v);
+}
+
+static void bc_put_u16(BCWriterState *s, uint16_t v)
+{
+ if (s->byte_swap)
+ v = bswap16(v);
+ dbuf_put_u16(&s->dbuf, v);
+}
+
+static maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
+{
+ if (s->byte_swap)
+ v = bswap32(v);
+ dbuf_put_u32(&s->dbuf, v);
+}
+
+static void bc_put_u64(BCWriterState *s, uint64_t v)
+{
+ if (s->byte_swap)
+ v = bswap64(v);
+ dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v));
+}
+
+static void bc_put_leb128(BCWriterState *s, uint32_t v)
+{
+ dbuf_put_leb128(&s->dbuf, v);
+}
+
+static void bc_put_sleb128(BCWriterState *s, int32_t v)
+{
+ dbuf_put_sleb128(&s->dbuf, v);
+}
+
+static void bc_set_flags(uint32_t *pflags, int *pidx, uint32_t val, int n)
+{
+ *pflags = *pflags | (val << *pidx);
+ *pidx += n;
+}
+
+static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
+{
+ uint32_t v;
+
+ if (atom < s->first_atom || JS_AtomIsTaggedInt(atom)) {
+ *pres = atom;
+ return 0;
+ }
+ atom -= s->first_atom;
+ if (atom < s->atom_to_idx_size && s->atom_to_idx[atom] != 0) {
+ *pres = s->atom_to_idx[atom];
+ return 0;
+ }
+ if (atom >= s->atom_to_idx_size) {
+ int old_size, i;
+ old_size = s->atom_to_idx_size;
+ if (js_resize_array(s->ctx, (void **)&s->atom_to_idx,
+ sizeof(s->atom_to_idx[0]), &s->atom_to_idx_size,
+ atom + 1))
+ return -1;
+ /* XXX: could add a specific js_resize_array() function to do it */
+ for(i = old_size; i < s->atom_to_idx_size; i++)
+ s->atom_to_idx[i] = 0;
+ }
+ if (js_resize_array(s->ctx, (void **)&s->idx_to_atom,
+ sizeof(s->idx_to_atom[0]),
+ &s->idx_to_atom_size, s->idx_to_atom_count + 1))
+ goto fail;
+
+ v = s->idx_to_atom_count++;
+ s->idx_to_atom[v] = atom + s->first_atom;
+ v += s->first_atom;
+ s->atom_to_idx[atom] = v;
+ *pres = v;
+ return 0;
+ fail:
+ *pres = 0;
+ return -1;
+}
+
+static int bc_put_atom(BCWriterState *s, JSAtom atom)
+{
+ uint32_t v;
+
+ if (JS_AtomIsTaggedInt(atom)) {
+ v = (JS_AtomToUInt32(atom) << 1) | 1;
+ } else {
+ if (bc_atom_to_idx(s, &v, atom))
+ return -1;
+ v <<= 1;
+ }
+ bc_put_leb128(s, v);
+ return 0;
+}
+
+static void bc_byte_swap(uint8_t *bc_buf, int bc_len)
+{
+ int pos, len, op, fmt;
+
+ pos = 0;
+ while (pos < bc_len) {
+ op = bc_buf[pos];
+ len = short_opcode_info(op).size;
+ fmt = short_opcode_info(op).fmt;
+ switch(fmt) {
+ case OP_FMT_u16:
+ case OP_FMT_i16:
+ case OP_FMT_label16:
+ case OP_FMT_npop:
+ case OP_FMT_loc:
+ case OP_FMT_arg:
+ case OP_FMT_var_ref:
+ put_u16(bc_buf + pos + 1,
+ bswap16(get_u16(bc_buf + pos + 1)));
+ break;
+ case OP_FMT_i32:
+ case OP_FMT_u32:
+ case OP_FMT_const:
+ case OP_FMT_label:
+ case OP_FMT_atom:
+ case OP_FMT_atom_u8:
+ put_u32(bc_buf + pos + 1,
+ bswap32(get_u32(bc_buf + pos + 1)));
+ break;
+ case OP_FMT_atom_u16:
+ case OP_FMT_label_u16:
+ put_u32(bc_buf + pos + 1,
+ bswap32(get_u32(bc_buf + pos + 1)));
+ put_u16(bc_buf + pos + 1 + 4,
+ bswap16(get_u16(bc_buf + pos + 1 + 4)));
+ break;
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ put_u32(bc_buf + pos + 1,
+ bswap32(get_u32(bc_buf + pos + 1)));
+ put_u32(bc_buf + pos + 1 + 4,
+ bswap32(get_u32(bc_buf + pos + 1 + 4)));
+ if (fmt == OP_FMT_atom_label_u16) {
+ put_u16(bc_buf + pos + 1 + 4 + 4,
+ bswap16(get_u16(bc_buf + pos + 1 + 4 + 4)));
+ }
+ break;
+ case OP_FMT_npop_u16:
+ put_u16(bc_buf + pos + 1,
+ bswap16(get_u16(bc_buf + pos + 1)));
+ put_u16(bc_buf + pos + 1 + 2,
+ bswap16(get_u16(bc_buf + pos + 1 + 2)));
+ break;
+ default:
+ break;
+ }
+ pos += len;
+ }
+}
+
+static int JS_WriteFunctionBytecode(BCWriterState *s,
+ const uint8_t *bc_buf1, int bc_len)
+{
+ int pos, len, op;
+ JSAtom atom;
+ uint8_t *bc_buf;
+ uint32_t val;
+
+ bc_buf = js_malloc(s->ctx, bc_len);
+ if (!bc_buf)
+ return -1;
+ memcpy(bc_buf, bc_buf1, bc_len);
+
+ pos = 0;
+ while (pos < bc_len) {
+ op = bc_buf[pos];
+ len = short_opcode_info(op).size;
+ switch(short_opcode_info(op).fmt) {
+ case OP_FMT_atom:
+ case OP_FMT_atom_u8:
+ case OP_FMT_atom_u16:
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ atom = get_u32(bc_buf + pos + 1);
+ if (bc_atom_to_idx(s, &val, atom))
+ goto fail;
+ put_u32(bc_buf + pos + 1, val);
+ break;
+ default:
+ break;
+ }
+ pos += len;
+ }
+
+ if (s->byte_swap)
+ bc_byte_swap(bc_buf, bc_len);
+
+ dbuf_put(&s->dbuf, bc_buf, bc_len);
+
+ js_free(s->ctx, bc_buf);
+ return 0;
+ fail:
+ js_free(s->ctx, bc_buf);
+ return -1;
+}
+
+static void JS_WriteString(BCWriterState *s, JSString *p)
+{
+ int i;
+ bc_put_leb128(s, (p->len << 1) | p->is_wide_char);
+ if (p->is_wide_char) {
+ for(i = 0; i < p->len; i++)
+ bc_put_u16(s, p->u.str16[i]);
+ } else {
+ dbuf_put(&s->dbuf, p->u.str8, p->len);
+ }
+}
+
+#ifdef CONFIG_BIGNUM
+static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj)
+{
+ uint32_t tag, tag1;
+ int64_t e;
+ JSBigFloat *bf = JS_VALUE_GET_PTR(obj);
+ bf_t *a = &bf->num;
+ size_t len, i, n1, j;
+ limb_t v;
+
+ tag = JS_VALUE_GET_TAG(obj);
+ switch(tag) {
+ case JS_TAG_BIG_INT:
+ tag1 = BC_TAG_BIG_INT;
+ break;
+ case JS_TAG_BIG_FLOAT:
+ tag1 = BC_TAG_BIG_FLOAT;
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ tag1 = BC_TAG_BIG_DECIMAL;
+ break;
+ default:
+ abort();
+ }
+ bc_put_u8(s, tag1);
+
+ /* sign + exponent */
+ if (a->expn == BF_EXP_ZERO)
+ e = 0;
+ else if (a->expn == BF_EXP_INF)
+ e = 1;
+ else if (a->expn == BF_EXP_NAN)
+ e = 2;
+ else if (a->expn >= 0)
+ e = a->expn + 3;
+ else
+ e = a->expn;
+ e = (e << 1) | a->sign;
+ if (e < INT32_MIN || e > INT32_MAX) {
+ JS_ThrowInternalError(s->ctx, "bignum exponent is too large");
+ return -1;
+ }
+ bc_put_sleb128(s, e);
+
+ /* mantissa */
+ if (a->len != 0) {
+ if (tag != JS_TAG_BIG_DECIMAL) {
+ i = 0;
+ while (i < a->len && a->tab[i] == 0)
+ i++;
+ assert(i < a->len);
+ v = a->tab[i];
+ n1 = sizeof(limb_t);
+ while ((v & 0xff) == 0) {
+ n1--;
+ v >>= 8;
+ }
+ i++;
+ len = (a->len - i) * sizeof(limb_t) + n1;
+ if (len > INT32_MAX) {
+ JS_ThrowInternalError(s->ctx, "bignum is too large");
+ return -1;
+ }
+ bc_put_leb128(s, len);
+ /* always saved in byte based little endian representation */
+ for(j = 0; j < n1; j++) {
+ dbuf_putc(&s->dbuf, v >> (j * 8));
+ }
+ for(; i < a->len; i++) {
+ limb_t v = a->tab[i];
+#if LIMB_BITS == 32
+#ifdef WORDS_BIGENDIAN
+ v = bswap32(v);
+#endif
+ dbuf_put_u32(&s->dbuf, v);
+#else
+#ifdef WORDS_BIGENDIAN
+ v = bswap64(v);
+#endif
+ dbuf_put_u64(&s->dbuf, v);
+#endif
+ }
+ } else {
+ int bpos, d;
+ uint8_t v8;
+ size_t i0;
+
+ /* little endian BCD */
+ i = 0;
+ while (i < a->len && a->tab[i] == 0)
+ i++;
+ assert(i < a->len);
+ len = a->len * LIMB_DIGITS;
+ v = a->tab[i];
+ j = 0;
+ while ((v % 10) == 0) {
+ j++;
+ v /= 10;
+ }
+ len -= j;
+ assert(len > 0);
+ if (len > INT32_MAX) {
+ JS_ThrowInternalError(s->ctx, "bignum is too large");
+ return -1;
+ }
+ bc_put_leb128(s, len);
+
+ bpos = 0;
+ v8 = 0;
+ i0 = i;
+ for(; i < a->len; i++) {
+ if (i != i0) {
+ v = a->tab[i];
+ j = 0;
+ }
+ for(; j < LIMB_DIGITS; j++) {
+ d = v % 10;
+ v /= 10;
+ if (bpos == 0) {
+ v8 = d;
+ bpos = 1;
+ } else {
+ dbuf_putc(&s->dbuf, v8 | (d << 4));
+ bpos = 0;
+ }
+ }
+ }
+ /* flush the last digit */
+ if (bpos) {
+ dbuf_putc(&s->dbuf, v8);
+ }
+ }
+ }
+ return 0;
+}
+#endif /* CONFIG_BIGNUM */
+
+static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj);
+
+static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj)
+{
+ JSFunctionBytecode *b = JS_VALUE_GET_PTR(obj);
+ uint32_t flags;
+ int idx, i;
+
+ bc_put_u8(s, BC_TAG_FUNCTION_BYTECODE);
+ flags = idx = 0;
+ bc_set_flags(&flags, &idx, b->has_prototype, 1);
+ bc_set_flags(&flags, &idx, b->has_simple_parameter_list, 1);
+ bc_set_flags(&flags, &idx, b->is_derived_class_constructor, 1);
+ bc_set_flags(&flags, &idx, b->need_home_object, 1);
+ bc_set_flags(&flags, &idx, b->func_kind, 2);
+ bc_set_flags(&flags, &idx, b->new_target_allowed, 1);
+ bc_set_flags(&flags, &idx, b->super_call_allowed, 1);
+ bc_set_flags(&flags, &idx, b->super_allowed, 1);
+ bc_set_flags(&flags, &idx, b->arguments_allowed, 1);
+ bc_set_flags(&flags, &idx, b->has_debug, 1);
+ bc_set_flags(&flags, &idx, b->backtrace_barrier, 1);
+ assert(idx <= 16);
+ bc_put_u16(s, flags);
+ bc_put_u8(s, b->js_mode);
+ bc_put_atom(s, b->func_name);
+
+ bc_put_leb128(s, b->arg_count);
+ bc_put_leb128(s, b->var_count);
+ bc_put_leb128(s, b->defined_arg_count);
+ bc_put_leb128(s, b->stack_size);
+ bc_put_leb128(s, b->closure_var_count);
+ bc_put_leb128(s, b->cpool_count);
+ bc_put_leb128(s, b->byte_code_len);
+ if (b->vardefs) {
+ /* XXX: this field is redundant */
+ bc_put_leb128(s, b->arg_count + b->var_count);
+ for(i = 0; i < b->arg_count + b->var_count; i++) {
+ JSVarDef *vd = &b->vardefs[i];
+ bc_put_atom(s, vd->var_name);
+ bc_put_leb128(s, vd->scope_level);
+ bc_put_leb128(s, vd->scope_next + 1);
+ flags = idx = 0;
+ bc_set_flags(&flags, &idx, vd->var_kind, 4);
+ bc_set_flags(&flags, &idx, vd->is_const, 1);
+ bc_set_flags(&flags, &idx, vd->is_lexical, 1);
+ bc_set_flags(&flags, &idx, vd->is_captured, 1);
+ assert(idx <= 8);
+ bc_put_u8(s, flags);
+ }
+ } else {
+ bc_put_leb128(s, 0);
+ }
+
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ bc_put_atom(s, cv->var_name);
+ bc_put_leb128(s, cv->var_idx);
+ flags = idx = 0;
+ bc_set_flags(&flags, &idx, cv->is_local, 1);
+ bc_set_flags(&flags, &idx, cv->is_arg, 1);
+ bc_set_flags(&flags, &idx, cv->is_const, 1);
+ bc_set_flags(&flags, &idx, cv->is_lexical, 1);
+ bc_set_flags(&flags, &idx, cv->var_kind, 4);
+ assert(idx <= 8);
+ bc_put_u8(s, flags);
+ }
+
+ if (JS_WriteFunctionBytecode(s, b->byte_code_buf, b->byte_code_len))
+ goto fail;
+
+ if (b->has_debug) {
+ bc_put_atom(s, b->debug.filename);
+ bc_put_leb128(s, b->debug.line_num);
+ bc_put_leb128(s, b->debug.pc2line_len);
+ dbuf_put(&s->dbuf, b->debug.pc2line_buf, b->debug.pc2line_len);
+ }
+
+ for(i = 0; i < b->cpool_count; i++) {
+ if (JS_WriteObjectRec(s, b->cpool[i]))
+ goto fail;
+ }
+ return 0;
+ fail:
+ return -1;
+}
+
+static int JS_WriteModule(BCWriterState *s, JSValueConst obj)
+{
+ JSModuleDef *m = JS_VALUE_GET_PTR(obj);
+ int i;
+
+ bc_put_u8(s, BC_TAG_MODULE);
+ bc_put_atom(s, m->module_name);
+
+ bc_put_leb128(s, m->req_module_entries_count);
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ bc_put_atom(s, rme->module_name);
+ }
+
+ bc_put_leb128(s, m->export_entries_count);
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ bc_put_u8(s, me->export_type);
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ bc_put_leb128(s, me->u.local.var_idx);
+ } else {
+ bc_put_leb128(s, me->u.req_module_idx);
+ bc_put_atom(s, me->local_name);
+ }
+ bc_put_atom(s, me->export_name);
+ }
+
+ bc_put_leb128(s, m->star_export_entries_count);
+ for(i = 0; i < m->star_export_entries_count; i++) {
+ JSStarExportEntry *se = &m->star_export_entries[i];
+ bc_put_leb128(s, se->req_module_idx);
+ }
+
+ bc_put_leb128(s, m->import_entries_count);
+ for(i = 0; i < m->import_entries_count; i++) {
+ JSImportEntry *mi = &m->import_entries[i];
+ bc_put_leb128(s, mi->var_idx);
+ bc_put_atom(s, mi->import_name);
+ bc_put_leb128(s, mi->req_module_idx);
+ }
+
+ if (JS_WriteObjectRec(s, m->func_obj))
+ goto fail;
+ return 0;
+ fail:
+ return -1;
+}
+
+static int JS_WriteArray(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ uint32_t i, len;
+ JSValue val;
+ int ret;
+ BOOL is_template;
+
+ if (s->allow_bytecode && !p->extensible) {
+ /* not extensible array: we consider it is a
+ template when we are saving bytecode */
+ bc_put_u8(s, BC_TAG_TEMPLATE_OBJECT);
+ is_template = TRUE;
+ } else {
+ bc_put_u8(s, BC_TAG_ARRAY);
+ is_template = FALSE;
+ }
+ if (js_get_length32(s->ctx, &len, obj))
+ goto fail1;
+ bc_put_leb128(s, len);
+ for(i = 0; i < len; i++) {
+ val = JS_GetPropertyUint32(s->ctx, obj, i);
+ if (JS_IsException(val))
+ goto fail1;
+ ret = JS_WriteObjectRec(s, val);
+ JS_FreeValue(s->ctx, val);
+ if (ret)
+ goto fail1;
+ }
+ if (is_template) {
+ val = JS_GetProperty(s->ctx, obj, JS_ATOM_raw);
+ if (JS_IsException(val))
+ goto fail1;
+ ret = JS_WriteObjectRec(s, val);
+ JS_FreeValue(s->ctx, val);
+ if (ret)
+ goto fail1;
+ }
+ return 0;
+ fail1:
+ return -1;
+}
+
+static int JS_WriteObjectTag(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ uint32_t i, prop_count;
+ JSShape *sh;
+ JSShapeProperty *pr;
+ int pass;
+ JSAtom atom;
+
+ bc_put_u8(s, BC_TAG_OBJECT);
+ prop_count = 0;
+ sh = p->shape;
+ for(pass = 0; pass < 2; pass++) {
+ if (pass == 1)
+ bc_put_leb128(s, prop_count);
+ for(i = 0, pr = get_shape_prop(sh); i < sh->prop_count; i++, pr++) {
+ atom = pr->atom;
+ if (atom != JS_ATOM_NULL &&
+ JS_AtomIsString(s->ctx, atom) &&
+ (pr->flags & JS_PROP_ENUMERABLE)) {
+ if (pr->flags & JS_PROP_TMASK) {
+ JS_ThrowTypeError(s->ctx, "only value properties are supported");
+ goto fail;
+ }
+ if (pass == 0) {
+ prop_count++;
+ } else {
+ bc_put_atom(s, atom);
+ if (JS_WriteObjectRec(s, p->prop[i].u.value))
+ goto fail;
+ }
+ }
+ }
+ }
+ return 0;
+ fail:
+ return -1;
+}
+
+static int JS_WriteTypedArray(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ JSTypedArray *ta = p->u.typed_array;
+
+ bc_put_u8(s, BC_TAG_TYPED_ARRAY);
+ bc_put_u8(s, p->class_id - JS_CLASS_UINT8C_ARRAY);
+ bc_put_leb128(s, p->u.array.count);
+ bc_put_leb128(s, ta->offset);
+ if (JS_WriteObjectRec(s, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)))
+ return -1;
+ return 0;
+}
+
+static int JS_WriteArrayBuffer(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ JSArrayBuffer *abuf = p->u.array_buffer;
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(s->ctx);
+ return -1;
+ }
+ bc_put_u8(s, BC_TAG_ARRAY_BUFFER);
+ bc_put_leb128(s, abuf->byte_length);
+ dbuf_put(&s->dbuf, abuf->data, abuf->byte_length);
+ return 0;
+}
+
+static int JS_WriteSharedArrayBuffer(BCWriterState *s, JSValueConst obj)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ JSArrayBuffer *abuf = p->u.array_buffer;
+ assert(!abuf->detached); /* SharedArrayBuffer are never detached */
+ bc_put_u8(s, BC_TAG_SHARED_ARRAY_BUFFER);
+ bc_put_leb128(s, abuf->byte_length);
+ bc_put_u64(s, (uintptr_t)abuf->data);
+ if (js_resize_array(s->ctx, (void **)&s->sab_tab, sizeof(s->sab_tab[0]),
+ &s->sab_tab_size, s->sab_tab_len + 1))
+ return -1;
+ /* keep the SAB pointer so that the user can clone it or free it */
+ s->sab_tab[s->sab_tab_len++] = abuf->data;
+ return 0;
+}
+
+static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj)
+{
+ uint32_t tag;
+
+ if (js_check_stack_overflow(s->ctx->rt, 0)) {
+ JS_ThrowStackOverflow(s->ctx);
+ return -1;
+ }
+
+ tag = JS_VALUE_GET_NORM_TAG(obj);
+ switch(tag) {
+ case JS_TAG_NULL:
+ bc_put_u8(s, BC_TAG_NULL);
+ break;
+ case JS_TAG_UNDEFINED:
+ bc_put_u8(s, BC_TAG_UNDEFINED);
+ break;
+ case JS_TAG_BOOL:
+ bc_put_u8(s, BC_TAG_BOOL_FALSE + JS_VALUE_GET_INT(obj));
+ break;
+ case JS_TAG_INT:
+ bc_put_u8(s, BC_TAG_INT32);
+ bc_put_sleb128(s, JS_VALUE_GET_INT(obj));
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ bc_put_u8(s, BC_TAG_FLOAT64);
+ u.d = JS_VALUE_GET_FLOAT64(obj);
+ bc_put_u64(s, u.u64);
+ }
+ break;
+ case JS_TAG_STRING:
+ {
+ JSString *p = JS_VALUE_GET_STRING(obj);
+ bc_put_u8(s, BC_TAG_STRING);
+ JS_WriteString(s, p);
+ }
+ break;
+ case JS_TAG_FUNCTION_BYTECODE:
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ if (JS_WriteFunctionTag(s, obj))
+ goto fail;
+ break;
+ case JS_TAG_MODULE:
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ if (JS_WriteModule(s, obj))
+ goto fail;
+ break;
+ case JS_TAG_OBJECT:
+ {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ int ret, idx;
+
+ if (s->allow_reference) {
+ idx = js_object_list_find(s->ctx, &s->object_list, p);
+ if (idx >= 0) {
+ bc_put_u8(s, BC_TAG_OBJECT_REFERENCE);
+ bc_put_leb128(s, idx);
+ break;
+ } else {
+ if (js_object_list_add(s->ctx, &s->object_list, p))
+ goto fail;
+ }
+ } else {
+ if (p->tmp_mark) {
+ JS_ThrowTypeError(s->ctx, "circular reference");
+ goto fail;
+ }
+ p->tmp_mark = 1;
+ }
+ switch(p->class_id) {
+ case JS_CLASS_ARRAY:
+ ret = JS_WriteArray(s, obj);
+ break;
+ case JS_CLASS_OBJECT:
+ ret = JS_WriteObjectTag(s, obj);
+ break;
+ case JS_CLASS_ARRAY_BUFFER:
+ ret = JS_WriteArrayBuffer(s, obj);
+ break;
+ case JS_CLASS_SHARED_ARRAY_BUFFER:
+ if (!s->allow_sab)
+ goto invalid_tag;
+ ret = JS_WriteSharedArrayBuffer(s, obj);
+ break;
+ case JS_CLASS_DATE:
+ bc_put_u8(s, BC_TAG_DATE);
+ ret = JS_WriteObjectRec(s, p->u.object_data);
+ break;
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_STRING:
+ case JS_CLASS_BOOLEAN:
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT:
+ case JS_CLASS_BIG_FLOAT:
+ case JS_CLASS_BIG_DECIMAL:
+#endif
+ bc_put_u8(s, BC_TAG_OBJECT_VALUE);
+ ret = JS_WriteObjectRec(s, p->u.object_data);
+ break;
+ default:
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ ret = JS_WriteTypedArray(s, obj);
+ } else {
+ JS_ThrowTypeError(s->ctx, "unsupported object class");
+ ret = -1;
+ }
+ break;
+ }
+ p->tmp_mark = 0;
+ if (ret)
+ goto fail;
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ case JS_TAG_BIG_DECIMAL:
+ if (JS_WriteBigNum(s, obj))
+ goto fail;
+ break;
+#endif
+ default:
+ invalid_tag:
+ JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag);
+ goto fail;
+ }
+ return 0;
+
+ fail:
+ return -1;
+}
+
+/* create the atom table */
+static int JS_WriteObjectAtoms(BCWriterState *s)
+{
+ JSRuntime *rt = s->ctx->rt;
+ DynBuf dbuf1;
+ int i, atoms_size;
+ uint8_t version;
+
+ dbuf1 = s->dbuf;
+ js_dbuf_init(s->ctx, &s->dbuf);
+
+ version = BC_VERSION;
+ if (s->byte_swap)
+ version ^= BC_BE_VERSION;
+ bc_put_u8(s, version);
+
+ bc_put_leb128(s, s->idx_to_atom_count);
+ for(i = 0; i < s->idx_to_atom_count; i++) {
+ JSAtomStruct *p = rt->atom_array[s->idx_to_atom[i]];
+ JS_WriteString(s, p);
+ }
+ /* XXX: should check for OOM in above phase */
+
+ /* move the atoms at the start */
+ /* XXX: could just append dbuf1 data, but it uses more memory if
+ dbuf1 is larger than dbuf */
+ atoms_size = s->dbuf.size;
+ if (dbuf_realloc(&dbuf1, dbuf1.size + atoms_size))
+ goto fail;
+ memmove(dbuf1.buf + atoms_size, dbuf1.buf, dbuf1.size);
+ memcpy(dbuf1.buf, s->dbuf.buf, atoms_size);
+ dbuf1.size += atoms_size;
+ dbuf_free(&s->dbuf);
+ s->dbuf = dbuf1;
+ return 0;
+ fail:
+ dbuf_free(&dbuf1);
+ return -1;
+}
+
+uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
+ int flags, uint8_t ***psab_tab, size_t *psab_tab_len)
+{
+ BCWriterState ss, *s = &ss;
+
+ memset(s, 0, sizeof(*s));
+ s->ctx = ctx;
+ /* XXX: byte swapped output is untested */
+ s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0);
+ s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0);
+ s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0);
+ s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0);
+ /* XXX: could use a different version when bytecode is included */
+ if (s->allow_bytecode)
+ s->first_atom = JS_ATOM_END;
+ else
+ s->first_atom = 1;
+ js_dbuf_init(ctx, &s->dbuf);
+ js_object_list_init(&s->object_list);
+
+ if (JS_WriteObjectRec(s, obj))
+ goto fail;
+ if (JS_WriteObjectAtoms(s))
+ goto fail;
+ js_object_list_end(ctx, &s->object_list);
+ js_free(ctx, s->atom_to_idx);
+ js_free(ctx, s->idx_to_atom);
+ *psize = s->dbuf.size;
+ if (psab_tab)
+ *psab_tab = s->sab_tab;
+ if (psab_tab_len)
+ *psab_tab_len = s->sab_tab_len;
+ return s->dbuf.buf;
+ fail:
+ js_object_list_end(ctx, &s->object_list);
+ js_free(ctx, s->atom_to_idx);
+ js_free(ctx, s->idx_to_atom);
+ dbuf_free(&s->dbuf);
+ *psize = 0;
+ if (psab_tab)
+ *psab_tab = NULL;
+ if (psab_tab_len)
+ *psab_tab_len = 0;
+ return NULL;
+}
+
+uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
+ int flags)
+{
+ return JS_WriteObject2(ctx, psize, obj, flags, NULL, NULL);
+}
+
+typedef struct BCReaderState {
+ JSContext *ctx;
+ const uint8_t *buf_start, *ptr, *buf_end;
+ uint32_t first_atom;
+ uint32_t idx_to_atom_count;
+ JSAtom *idx_to_atom;
+ int error_state;
+ BOOL allow_sab : 8;
+ BOOL allow_bytecode : 8;
+ BOOL is_rom_data : 8;
+ BOOL allow_reference : 8;
+ /* object references */
+ JSObject **objects;
+ int objects_count;
+ int objects_size;
+
+#ifdef DUMP_READ_OBJECT
+ const uint8_t *ptr_last;
+ int level;
+#endif
+} BCReaderState;
+
+#ifdef DUMP_READ_OBJECT
+static void FORMAT_ATTR(2, 3) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
+ va_list ap;
+ int i, n, n0;
+
+ if (!s->ptr_last)
+ s->ptr_last = s->buf_start;
+
+ n = n0 = 0;
+ if (s->ptr > s->ptr_last || s->ptr == s->buf_start) {
+ n0 = printf("%04x: ", (int)(s->ptr_last - s->buf_start));
+ n += n0;
+ }
+ for (i = 0; s->ptr_last < s->ptr; i++) {
+ if ((i & 7) == 0 && i > 0) {
+ printf("\n%*s", n0, "");
+ n = n0;
+ }
+ n += printf(" %02x", *s->ptr_last++);
+ }
+ if (*fmt == '}')
+ s->level--;
+ if (n < 32 + s->level * 2) {
+ printf("%*s", 32 + s->level * 2 - n, "");
+ }
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ if (strchr(fmt, '{'))
+ s->level++;
+}
+#else
+#define bc_read_trace(...)
+#endif
+
+static int bc_read_error_end(BCReaderState *s)
+{
+ if (!s->error_state) {
+ JS_ThrowSyntaxError(s->ctx, "read after the end of the buffer");
+ }
+ return s->error_state = -1;
+}
+
+static int bc_get_u8(BCReaderState *s, uint8_t *pval)
+{
+ if (unlikely(s->buf_end - s->ptr < 1)) {
+ *pval = 0; /* avoid warning */
+ return bc_read_error_end(s);
+ }
+ *pval = *s->ptr++;
+ return 0;
+}
+
+static int bc_get_u16(BCReaderState *s, uint16_t *pval)
+{
+ if (unlikely(s->buf_end - s->ptr < 2)) {
+ *pval = 0; /* avoid warning */
+ return bc_read_error_end(s);
+ }
+ *pval = get_u16(s->ptr);
+ s->ptr += 2;
+ return 0;
+}
+
+static maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
+{
+ if (unlikely(s->buf_end - s->ptr < 4)) {
+ *pval = 0; /* avoid warning */
+ return bc_read_error_end(s);
+ }
+ *pval = get_u32(s->ptr);
+ s->ptr += 4;
+ return 0;
+}
+
+static int bc_get_u64(BCReaderState *s, uint64_t *pval)
+{
+ if (unlikely(s->buf_end - s->ptr < 8)) {
+ *pval = 0; /* avoid warning */
+ return bc_read_error_end(s);
+ }
+ *pval = get_u64(s->ptr);
+ s->ptr += 8;
+ return 0;
+}
+
+static int bc_get_leb128(BCReaderState *s, uint32_t *pval)
+{
+ int ret;
+ ret = get_leb128(pval, s->ptr, s->buf_end);
+ if (unlikely(ret < 0))
+ return bc_read_error_end(s);
+ s->ptr += ret;
+ return 0;
+}
+
+static int bc_get_sleb128(BCReaderState *s, int32_t *pval)
+{
+ int ret;
+ ret = get_sleb128(pval, s->ptr, s->buf_end);
+ if (unlikely(ret < 0))
+ return bc_read_error_end(s);
+ s->ptr += ret;
+ return 0;
+}
+
+/* XXX: used to read an `int` with a positive value */
+static int bc_get_leb128_int(BCReaderState *s, int *pval)
+{
+ return bc_get_leb128(s, (uint32_t *)pval);
+}
+
+static int bc_get_leb128_u16(BCReaderState *s, uint16_t *pval)
+{
+ uint32_t val;
+ if (bc_get_leb128(s, &val)) {
+ *pval = 0;
+ return -1;
+ }
+ *pval = val;
+ return 0;
+}
+
+static int bc_get_buf(BCReaderState *s, uint8_t *buf, uint32_t buf_len)
+{
+ if (buf_len != 0) {
+ if (unlikely(!buf || s->buf_end - s->ptr < buf_len))
+ return bc_read_error_end(s);
+ memcpy(buf, s->ptr, buf_len);
+ s->ptr += buf_len;
+ }
+ return 0;
+}
+
+static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)
+{
+ JSAtom atom;
+
+ if (JS_AtomIsTaggedInt(idx)) {
+ atom = idx;
+ } else if (idx < s->first_atom) {
+ atom = JS_DupAtom(s->ctx, idx);
+ } else {
+ idx -= s->first_atom;
+ if (idx >= s->idx_to_atom_count) {
+ JS_ThrowSyntaxError(s->ctx, "invalid atom index (pos=%u)",
+ (unsigned int)(s->ptr - s->buf_start));
+ *patom = JS_ATOM_NULL;
+ return s->error_state = -1;
+ }
+ atom = JS_DupAtom(s->ctx, s->idx_to_atom[idx]);
+ }
+ *patom = atom;
+ return 0;
+}
+
+static int bc_get_atom(BCReaderState *s, JSAtom *patom)
+{
+ uint32_t v;
+ if (bc_get_leb128(s, &v))
+ return -1;
+ if (v & 1) {
+ *patom = JS_AtomFromUInt32(v >> 1);
+ return 0;
+ } else {
+ return bc_idx_to_atom(s, patom, v >> 1);
+ }
+}
+
+static JSString *JS_ReadString(BCReaderState *s)
+{
+ uint32_t len;
+ size_t size;
+ BOOL is_wide_char;
+ JSString *p;
+
+ if (bc_get_leb128(s, &len))
+ return NULL;
+ is_wide_char = len & 1;
+ len >>= 1;
+ p = js_alloc_string(s->ctx, len, is_wide_char);
+ if (!p) {
+ s->error_state = -1;
+ return NULL;
+ }
+ size = (size_t)len << is_wide_char;
+ if ((s->buf_end - s->ptr) < size) {
+ bc_read_error_end(s);
+ js_free_string(s->ctx->rt, p);
+ return NULL;
+ }
+ memcpy(p->u.str8, s->ptr, size);
+ s->ptr += size;
+ if (!is_wide_char) {
+ p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */
+ }
+#ifdef DUMP_READ_OBJECT
+ JS_DumpString(s->ctx->rt, p); printf("\n");
+#endif
+ return p;
+}
+
+static uint32_t bc_get_flags(uint32_t flags, int *pidx, int n)
+{
+ uint32_t val;
+ /* XXX: this does not work for n == 32 */
+ val = (flags >> *pidx) & ((1U << n) - 1);
+ *pidx += n;
+ return val;
+}
+
+static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b,
+ int byte_code_offset, uint32_t bc_len)
+{
+ uint8_t *bc_buf;
+ int pos, len, op;
+ JSAtom atom;
+ uint32_t idx;
+
+ if (s->is_rom_data) {
+ /* directly use the input buffer */
+ if (unlikely(s->buf_end - s->ptr < bc_len))
+ return bc_read_error_end(s);
+ bc_buf = (uint8_t *)s->ptr;
+ s->ptr += bc_len;
+ } else {
+ bc_buf = (void *)((uint8_t*)b + byte_code_offset);
+ if (bc_get_buf(s, bc_buf, bc_len))
+ return -1;
+ }
+ b->byte_code_buf = bc_buf;
+
+ pos = 0;
+ while (pos < bc_len) {
+ op = bc_buf[pos];
+ len = short_opcode_info(op).size;
+ switch(short_opcode_info(op).fmt) {
+ case OP_FMT_atom:
+ case OP_FMT_atom_u8:
+ case OP_FMT_atom_u16:
+ case OP_FMT_atom_label_u8:
+ case OP_FMT_atom_label_u16:
+ idx = get_u32(bc_buf + pos + 1);
+ if (s->is_rom_data) {
+ /* just increment the reference count of the atom */
+ JS_DupAtom(s->ctx, (JSAtom)idx);
+ } else {
+ if (bc_idx_to_atom(s, &atom, idx)) {
+ /* Note: the atoms will be freed up to this position */
+ b->byte_code_len = pos;
+ return -1;
+ }
+ put_u32(bc_buf + pos + 1, atom);
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "at %d, fixup atom: ", pos + 1); print_atom(s->ctx, atom); printf("\n");
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+ pos += len;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_BIGNUM
+static JSValue JS_ReadBigNum(BCReaderState *s, int tag)
+{
+ JSValue obj = JS_UNDEFINED;
+ uint8_t v8;
+ int32_t e;
+ uint32_t len;
+ limb_t l, i, n, j;
+ JSBigFloat *p;
+ limb_t v;
+ bf_t *a;
+ int bpos, d;
+
+ p = js_new_bf(s->ctx);
+ if (!p)
+ goto fail;
+ switch(tag) {
+ case BC_TAG_BIG_INT:
+ obj = JS_MKPTR(JS_TAG_BIG_INT, p);
+ break;
+ case BC_TAG_BIG_FLOAT:
+ obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
+ break;
+ case BC_TAG_BIG_DECIMAL:
+ obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p);
+ break;
+ default:
+ abort();
+ }
+
+ /* sign + exponent */
+ if (bc_get_sleb128(s, &e))
+ goto fail;
+
+ a = &p->num;
+ a->sign = e & 1;
+ e >>= 1;
+ if (e == 0)
+ a->expn = BF_EXP_ZERO;
+ else if (e == 1)
+ a->expn = BF_EXP_INF;
+ else if (e == 2)
+ a->expn = BF_EXP_NAN;
+ else if (e >= 3)
+ a->expn = e - 3;
+ else
+ a->expn = e;
+
+ /* mantissa */
+ if (a->expn != BF_EXP_ZERO &&
+ a->expn != BF_EXP_INF &&
+ a->expn != BF_EXP_NAN) {
+ if (bc_get_leb128(s, &len))
+ goto fail;
+ bc_read_trace(s, "len=%" PRId64 "\n", (int64_t)len);
+ if (len == 0) {
+ JS_ThrowInternalError(s->ctx, "invalid bignum length");
+ goto fail;
+ }
+ if (tag != BC_TAG_BIG_DECIMAL)
+ l = (len + sizeof(limb_t) - 1) / sizeof(limb_t);
+ else
+ l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS;
+ if (bf_resize(a, l)) {
+ JS_ThrowOutOfMemory(s->ctx);
+ goto fail;
+ }
+ if (tag != BC_TAG_BIG_DECIMAL) {
+ n = len & (sizeof(limb_t) - 1);
+ if (n != 0) {
+ v = 0;
+ for(i = 0; i < n; i++) {
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8);
+ }
+ a->tab[0] = v;
+ i = 1;
+ } else {
+ i = 0;
+ }
+ for(; i < l; i++) {
+#if LIMB_BITS == 32
+ if (bc_get_u32(s, &v))
+ goto fail;
+#ifdef WORDS_BIGENDIAN
+ v = bswap32(v);
+#endif
+#else
+ if (bc_get_u64(s, &v))
+ goto fail;
+#ifdef WORDS_BIGENDIAN
+ v = bswap64(v);
+#endif
+#endif
+ a->tab[i] = v;
+ }
+ } else {
+ bpos = 0;
+ for(i = 0; i < l; i++) {
+ if (i == 0 && (n = len % LIMB_DIGITS) != 0) {
+ j = LIMB_DIGITS - n;
+ } else {
+ j = 0;
+ }
+ v = 0;
+ for(; j < LIMB_DIGITS; j++) {
+ if (bpos == 0) {
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ d = v8 & 0xf;
+ bpos = 1;
+ } else {
+ d = v8 >> 4;
+ bpos = 0;
+ }
+ if (d >= 10) {
+ JS_ThrowInternalError(s->ctx, "invalid digit");
+ goto fail;
+ }
+ v += mp_pow_dec[j] * d;
+ }
+ a->tab[i] = v;
+ }
+ }
+ }
+ bc_read_trace(s, "}\n");
+ return obj;
+ fail:
+ JS_FreeValue(s->ctx, obj);
+ return JS_EXCEPTION;
+}
+#endif /* CONFIG_BIGNUM */
+
+static JSValue JS_ReadObjectRec(BCReaderState *s);
+
+static int BC_add_object_ref1(BCReaderState *s, JSObject *p)
+{
+ if (s->allow_reference) {
+ if (js_resize_array(s->ctx, (void *)&s->objects,
+ sizeof(s->objects[0]),
+ &s->objects_size, s->objects_count + 1))
+ return -1;
+ s->objects[s->objects_count++] = p;
+ }
+ return 0;
+}
+
+static int BC_add_object_ref(BCReaderState *s, JSValueConst obj)
+{
+ return BC_add_object_ref1(s, JS_VALUE_GET_OBJ(obj));
+}
+
+static JSValue JS_ReadFunctionTag(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSFunctionBytecode bc, *b;
+ JSValue obj = JS_UNDEFINED;
+ uint16_t v16;
+ uint8_t v8;
+ int idx, i, local_count;
+ int function_size, cpool_offset, byte_code_offset;
+ int closure_var_offset, vardefs_offset;
+
+ memset(&bc, 0, sizeof(bc));
+ bc.header.ref_count = 1;
+ //bc.gc_header.mark = 0;
+
+ if (bc_get_u16(s, &v16))
+ goto fail;
+ idx = 0;
+ bc.has_prototype = bc_get_flags(v16, &idx, 1);
+ bc.has_simple_parameter_list = bc_get_flags(v16, &idx, 1);
+ bc.is_derived_class_constructor = bc_get_flags(v16, &idx, 1);
+ bc.need_home_object = bc_get_flags(v16, &idx, 1);
+ bc.func_kind = bc_get_flags(v16, &idx, 2);
+ bc.new_target_allowed = bc_get_flags(v16, &idx, 1);
+ bc.super_call_allowed = bc_get_flags(v16, &idx, 1);
+ bc.super_allowed = bc_get_flags(v16, &idx, 1);
+ bc.arguments_allowed = bc_get_flags(v16, &idx, 1);
+ bc.has_debug = bc_get_flags(v16, &idx, 1);
+ bc.backtrace_barrier = bc_get_flags(v16, &idx, 1);
+ bc.read_only_bytecode = s->is_rom_data;
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ bc.js_mode = v8;
+ if (bc_get_atom(s, &bc.func_name)) //@ atom leak if failure
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.arg_count))
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.var_count))
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.defined_arg_count))
+ goto fail;
+ if (bc_get_leb128_u16(s, &bc.stack_size))
+ goto fail;
+ if (bc_get_leb128_int(s, &bc.closure_var_count))
+ goto fail;
+ if (bc_get_leb128_int(s, &bc.cpool_count))
+ goto fail;
+ if (bc_get_leb128_int(s, &bc.byte_code_len))
+ goto fail;
+ if (bc_get_leb128_int(s, &local_count))
+ goto fail;
+
+ if (bc.has_debug) {
+ function_size = sizeof(*b);
+ } else {
+ function_size = offsetof(JSFunctionBytecode, debug);
+ }
+ cpool_offset = function_size;
+ function_size += bc.cpool_count * sizeof(*bc.cpool);
+ vardefs_offset = function_size;
+ function_size += local_count * sizeof(*bc.vardefs);
+ closure_var_offset = function_size;
+ function_size += bc.closure_var_count * sizeof(*bc.closure_var);
+ byte_code_offset = function_size;
+ if (!bc.read_only_bytecode) {
+ function_size += bc.byte_code_len;
+ }
+
+ b = js_mallocz(ctx, function_size);
+ if (!b)
+ return JS_EXCEPTION;
+
+ memcpy(b, &bc, offsetof(JSFunctionBytecode, debug));
+ b->header.ref_count = 1;
+ if (local_count != 0) {
+ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
+ }
+ if (b->closure_var_count != 0) {
+ b->closure_var = (void *)((uint8_t*)b + closure_var_offset);
+ }
+ if (b->cpool_count != 0) {
+ b->cpool = (void *)((uint8_t*)b + cpool_offset);
+ }
+
+ add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
+
+ obj = JS_MKPTR(JS_TAG_FUNCTION_BYTECODE, b);
+
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, b->func_name); printf("\n");
+#endif
+ bc_read_trace(s, "args=%d vars=%d defargs=%d closures=%d cpool=%d\n",
+ b->arg_count, b->var_count, b->defined_arg_count,
+ b->closure_var_count, b->cpool_count);
+ bc_read_trace(s, "stack=%d bclen=%d locals=%d\n",
+ b->stack_size, b->byte_code_len, local_count);
+
+ if (local_count != 0) {
+ bc_read_trace(s, "vars {\n");
+ for(i = 0; i < local_count; i++) {
+ JSVarDef *vd = &b->vardefs[i];
+ if (bc_get_atom(s, &vd->var_name))
+ goto fail;
+ if (bc_get_leb128_int(s, &vd->scope_level))
+ goto fail;
+ if (bc_get_leb128_int(s, &vd->scope_next))
+ goto fail;
+ vd->scope_next--;
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ idx = 0;
+ vd->var_kind = bc_get_flags(v8, &idx, 4);
+ vd->is_const = bc_get_flags(v8, &idx, 1);
+ vd->is_lexical = bc_get_flags(v8, &idx, 1);
+ vd->is_captured = bc_get_flags(v8, &idx, 1);
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, vd->var_name); printf("\n");
+#endif
+ }
+ bc_read_trace(s, "}\n");
+ }
+ if (b->closure_var_count != 0) {
+ bc_read_trace(s, "closure vars {\n");
+ for(i = 0; i < b->closure_var_count; i++) {
+ JSClosureVar *cv = &b->closure_var[i];
+ int var_idx;
+ if (bc_get_atom(s, &cv->var_name))
+ goto fail;
+ if (bc_get_leb128_int(s, &var_idx))
+ goto fail;
+ cv->var_idx = var_idx;
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ idx = 0;
+ cv->is_local = bc_get_flags(v8, &idx, 1);
+ cv->is_arg = bc_get_flags(v8, &idx, 1);
+ cv->is_const = bc_get_flags(v8, &idx, 1);
+ cv->is_lexical = bc_get_flags(v8, &idx, 1);
+ cv->var_kind = bc_get_flags(v8, &idx, 4);
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, cv->var_name); printf("\n");
+#endif
+ }
+ bc_read_trace(s, "}\n");
+ }
+ {
+ bc_read_trace(s, "bytecode {\n");
+ if (JS_ReadFunctionBytecode(s, b, byte_code_offset, b->byte_code_len))
+ goto fail;
+ bc_read_trace(s, "}\n");
+ }
+ if (b->has_debug) {
+ /* read optional debug information */
+ bc_read_trace(s, "debug {\n");
+ if (bc_get_atom(s, &b->debug.filename))
+ goto fail;
+ if (bc_get_leb128_int(s, &b->debug.line_num))
+ goto fail;
+ if (bc_get_leb128_int(s, &b->debug.pc2line_len))
+ goto fail;
+ if (b->debug.pc2line_len) {
+ b->debug.pc2line_buf = js_mallocz(ctx, b->debug.pc2line_len);
+ if (!b->debug.pc2line_buf)
+ goto fail;
+ if (bc_get_buf(s, b->debug.pc2line_buf, b->debug.pc2line_len))
+ goto fail;
+ }
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "filename: "); print_atom(s->ctx, b->debug.filename); printf("\n");
+#endif
+ bc_read_trace(s, "}\n");
+ }
+ if (b->cpool_count != 0) {
+ bc_read_trace(s, "cpool {\n");
+ for(i = 0; i < b->cpool_count; i++) {
+ JSValue val;
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ b->cpool[i] = val;
+ }
+ bc_read_trace(s, "}\n");
+ }
+ b->realm = JS_DupContext(ctx);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadModule(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj;
+ JSModuleDef *m = NULL;
+ JSAtom module_name;
+ int i;
+ uint8_t v8;
+
+ if (bc_get_atom(s, &module_name))
+ goto fail;
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "name: "); print_atom(s->ctx, module_name); printf("\n");
+#endif
+ m = js_new_module_def(ctx, module_name);
+ if (!m)
+ goto fail;
+ obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m));
+ if (bc_get_leb128_int(s, &m->req_module_entries_count))
+ goto fail;
+ if (m->req_module_entries_count != 0) {
+ m->req_module_entries_size = m->req_module_entries_count;
+ m->req_module_entries = js_mallocz(ctx, sizeof(m->req_module_entries[0]) * m->req_module_entries_size);
+ if (!m->req_module_entries)
+ goto fail;
+ for(i = 0; i < m->req_module_entries_count; i++) {
+ JSReqModuleEntry *rme = &m->req_module_entries[i];
+ if (bc_get_atom(s, &rme->module_name))
+ goto fail;
+ }
+ }
+
+ if (bc_get_leb128_int(s, &m->export_entries_count))
+ goto fail;
+ if (m->export_entries_count != 0) {
+ m->export_entries_size = m->export_entries_count;
+ m->export_entries = js_mallocz(ctx, sizeof(m->export_entries[0]) * m->export_entries_size);
+ if (!m->export_entries)
+ goto fail;
+ for(i = 0; i < m->export_entries_count; i++) {
+ JSExportEntry *me = &m->export_entries[i];
+ if (bc_get_u8(s, &v8))
+ goto fail;
+ me->export_type = v8;
+ if (me->export_type == JS_EXPORT_TYPE_LOCAL) {
+ if (bc_get_leb128_int(s, &me->u.local.var_idx))
+ goto fail;
+ } else {
+ if (bc_get_leb128_int(s, &me->u.req_module_idx))
+ goto fail;
+ if (bc_get_atom(s, &me->local_name))
+ goto fail;
+ }
+ if (bc_get_atom(s, &me->export_name))
+ goto fail;
+ }
+ }
+
+ if (bc_get_leb128_int(s, &m->star_export_entries_count))
+ goto fail;
+ if (m->star_export_entries_count != 0) {
+ m->star_export_entries_size = m->star_export_entries_count;
+ m->star_export_entries = js_mallocz(ctx, sizeof(m->star_export_entries[0]) * m->star_export_entries_size);
+ if (!m->star_export_entries)
+ goto fail;
+ for(i = 0; i < m->star_export_entries_count; i++) {
+ JSStarExportEntry *se = &m->star_export_entries[i];
+ if (bc_get_leb128_int(s, &se->req_module_idx))
+ goto fail;
+ }
+ }
+
+ if (bc_get_leb128_int(s, &m->import_entries_count))
+ goto fail;
+ if (m->import_entries_count != 0) {
+ m->import_entries_size = m->import_entries_count;
+ m->import_entries = js_mallocz(ctx, sizeof(m->import_entries[0]) * m->import_entries_size);
+ if (!m->import_entries)
+ goto fail;
+ for(i = 0; i < m->import_entries_count; i++) {
+ JSImportEntry *mi = &m->import_entries[i];
+ if (bc_get_leb128_int(s, &mi->var_idx))
+ goto fail;
+ if (bc_get_atom(s, &mi->import_name))
+ goto fail;
+ if (bc_get_leb128_int(s, &mi->req_module_idx))
+ goto fail;
+ }
+ }
+
+ m->func_obj = JS_ReadObjectRec(s);
+ if (JS_IsException(m->func_obj))
+ goto fail;
+ return obj;
+ fail:
+ if (m) {
+ js_free_module_def(ctx, m);
+ }
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadObjectTag(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj;
+ uint32_t prop_count, i;
+ JSAtom atom;
+ JSValue val;
+ int ret;
+
+ obj = JS_NewObject(ctx);
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ if (bc_get_leb128(s, &prop_count))
+ goto fail;
+ for(i = 0; i < prop_count; i++) {
+ if (bc_get_atom(s, &atom))
+ goto fail;
+#ifdef DUMP_READ_OBJECT
+ bc_read_trace(s, "propname: "); print_atom(s->ctx, atom); printf("\n");
+#endif
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val)) {
+ JS_FreeAtom(ctx, atom);
+ goto fail;
+ }
+ ret = JS_DefinePropertyValue(ctx, obj, atom, val, JS_PROP_C_W_E);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ goto fail;
+ }
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadArray(BCReaderState *s, int tag)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj;
+ uint32_t len, i;
+ JSValue val;
+ int ret, prop_flags;
+ BOOL is_template;
+
+ obj = JS_NewArray(ctx);
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ is_template = (tag == BC_TAG_TEMPLATE_OBJECT);
+ if (bc_get_leb128(s, &len))
+ goto fail;
+ for(i = 0; i < len; i++) {
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (is_template)
+ prop_flags = JS_PROP_ENUMERABLE;
+ else
+ prop_flags = JS_PROP_C_W_E;
+ ret = JS_DefinePropertyValueUint32(ctx, obj, i, val,
+ prop_flags);
+ if (ret < 0)
+ goto fail;
+ }
+ if (is_template) {
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (!JS_IsUndefined(val)) {
+ ret = JS_DefinePropertyValue(ctx, obj, JS_ATOM_raw, val, 0);
+ if (ret < 0)
+ goto fail;
+ }
+ JS_PreventExtensions(ctx, obj);
+ }
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadTypedArray(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue obj = JS_UNDEFINED, array_buffer = JS_UNDEFINED;
+ uint8_t array_tag;
+ JSValueConst args[3];
+ uint32_t offset, len, idx;
+
+ if (bc_get_u8(s, &array_tag))
+ return JS_EXCEPTION;
+ if (array_tag >= JS_TYPED_ARRAY_COUNT)
+ return JS_ThrowTypeError(ctx, "invalid typed array");
+ if (bc_get_leb128(s, &len))
+ return JS_EXCEPTION;
+ if (bc_get_leb128(s, &offset))
+ return JS_EXCEPTION;
+ /* XXX: this hack could be avoided if the typed array could be
+ created before the array buffer */
+ idx = s->objects_count;
+ if (BC_add_object_ref1(s, NULL))
+ goto fail;
+ array_buffer = JS_ReadObjectRec(s);
+ if (JS_IsException(array_buffer))
+ return JS_EXCEPTION;
+ if (!js_get_array_buffer(ctx, array_buffer)) {
+ JS_FreeValue(ctx, array_buffer);
+ return JS_EXCEPTION;
+ }
+ args[0] = array_buffer;
+ args[1] = JS_NewInt64(ctx, offset);
+ args[2] = JS_NewInt64(ctx, len);
+ obj = js_typed_array_constructor(ctx, JS_UNDEFINED,
+ 3, args,
+ JS_CLASS_UINT8C_ARRAY + array_tag);
+ if (JS_IsException(obj))
+ goto fail;
+ if (s->allow_reference) {
+ s->objects[idx] = JS_VALUE_GET_OBJ(obj);
+ }
+ JS_FreeValue(ctx, array_buffer);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, array_buffer);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadArrayBuffer(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ uint32_t byte_length;
+ JSValue obj;
+
+ if (bc_get_leb128(s, &byte_length))
+ return JS_EXCEPTION;
+ if (unlikely(s->buf_end - s->ptr < byte_length)) {
+ bc_read_error_end(s);
+ return JS_EXCEPTION;
+ }
+ obj = JS_NewArrayBufferCopy(ctx, s->ptr, byte_length);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ s->ptr += byte_length;
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadSharedArrayBuffer(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ uint32_t byte_length;
+ uint8_t *data_ptr;
+ JSValue obj;
+ uint64_t u64;
+
+ if (bc_get_leb128(s, &byte_length))
+ return JS_EXCEPTION;
+ if (bc_get_u64(s, &u64))
+ return JS_EXCEPTION;
+ data_ptr = (uint8_t *)(uintptr_t)u64;
+ /* the SharedArrayBuffer is cloned */
+ obj = js_array_buffer_constructor3(ctx, JS_UNDEFINED, byte_length,
+ JS_CLASS_SHARED_ARRAY_BUFFER,
+ data_ptr,
+ NULL, NULL, FALSE);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadDate(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue val, obj = JS_UNDEFINED;
+
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (!JS_IsNumber(val)) {
+ JS_ThrowTypeError(ctx, "Number tag expected for date");
+ goto fail;
+ }
+ obj = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_DATE],
+ JS_CLASS_DATE);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ JS_SetObjectData(ctx, obj, val);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadObjectValue(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue val, obj = JS_UNDEFINED;
+
+ val = JS_ReadObjectRec(s);
+ if (JS_IsException(val))
+ goto fail;
+ obj = JS_ToObject(ctx, val);
+ if (JS_IsException(obj))
+ goto fail;
+ if (BC_add_object_ref(s, obj))
+ goto fail;
+ JS_FreeValue(ctx, val);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_ReadObjectRec(BCReaderState *s)
+{
+ JSContext *ctx = s->ctx;
+ uint8_t tag;
+ JSValue obj = JS_UNDEFINED;
+
+ if (js_check_stack_overflow(ctx->rt, 0))
+ return JS_ThrowStackOverflow(ctx);
+
+ if (bc_get_u8(s, &tag))
+ return JS_EXCEPTION;
+
+ bc_read_trace(s, "%s {\n", bc_tag_str[tag]);
+
+ switch(tag) {
+ case BC_TAG_NULL:
+ obj = JS_NULL;
+ break;
+ case BC_TAG_UNDEFINED:
+ obj = JS_UNDEFINED;
+ break;
+ case BC_TAG_BOOL_FALSE:
+ case BC_TAG_BOOL_TRUE:
+ obj = JS_NewBool(ctx, tag - BC_TAG_BOOL_FALSE);
+ break;
+ case BC_TAG_INT32:
+ {
+ int32_t val;
+ if (bc_get_sleb128(s, &val))
+ return JS_EXCEPTION;
+ bc_read_trace(s, "%d\n", val);
+ obj = JS_NewInt32(ctx, val);
+ }
+ break;
+ case BC_TAG_FLOAT64:
+ {
+ JSFloat64Union u;
+ if (bc_get_u64(s, &u.u64))
+ return JS_EXCEPTION;
+ bc_read_trace(s, "%g\n", u.d);
+ obj = JS_NewFloat64Impl(ctx, u.d);
+ }
+ break;
+ case BC_TAG_STRING:
+ {
+ JSString *p;
+ p = JS_ReadString(s);
+ if (!p)
+ return JS_EXCEPTION;
+ obj = JS_MKPTR(JS_TAG_STRING, p);
+ }
+ break;
+ case BC_TAG_FUNCTION_BYTECODE:
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ obj = JS_ReadFunctionTag(s);
+ break;
+ case BC_TAG_MODULE:
+ if (!s->allow_bytecode)
+ goto invalid_tag;
+ obj = JS_ReadModule(s);
+ break;
+ case BC_TAG_OBJECT:
+ obj = JS_ReadObjectTag(s);
+ break;
+ case BC_TAG_ARRAY:
+ case BC_TAG_TEMPLATE_OBJECT:
+ obj = JS_ReadArray(s, tag);
+ break;
+ case BC_TAG_TYPED_ARRAY:
+ obj = JS_ReadTypedArray(s);
+ break;
+ case BC_TAG_ARRAY_BUFFER:
+ obj = JS_ReadArrayBuffer(s);
+ break;
+ case BC_TAG_SHARED_ARRAY_BUFFER:
+ if (!s->allow_sab || !ctx->rt->sab_funcs.sab_dup)
+ goto invalid_tag;
+ obj = JS_ReadSharedArrayBuffer(s);
+ break;
+ case BC_TAG_DATE:
+ obj = JS_ReadDate(s);
+ break;
+ case BC_TAG_OBJECT_VALUE:
+ obj = JS_ReadObjectValue(s);
+ break;
+#ifdef CONFIG_BIGNUM
+ case BC_TAG_BIG_INT:
+ case BC_TAG_BIG_FLOAT:
+ case BC_TAG_BIG_DECIMAL:
+ obj = JS_ReadBigNum(s, tag);
+ break;
+#endif
+ case BC_TAG_OBJECT_REFERENCE:
+ {
+ uint32_t val;
+ if (!s->allow_reference)
+ return JS_ThrowSyntaxError(ctx, "object references are not allowed");
+ if (bc_get_leb128(s, &val))
+ return JS_EXCEPTION;
+ bc_read_trace(s, "%u\n", val);
+ if (val >= s->objects_count) {
+ return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)",
+ val, s->objects_count);
+ }
+ obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, s->objects[val]));
+ }
+ break;
+ default:
+ invalid_tag:
+ return JS_ThrowSyntaxError(ctx, "invalid tag (tag=%d pos=%u)",
+ tag, (unsigned int)(s->ptr - s->buf_start));
+ }
+ bc_read_trace(s, "}\n");
+ return obj;
+}
+
+static int JS_ReadObjectAtoms(BCReaderState *s)
+{
+ uint8_t v8;
+ JSString *p;
+ int i;
+ JSAtom atom;
+
+ if (bc_get_u8(s, &v8))
+ return -1;
+ /* XXX: could support byte swapped input */
+ if (v8 != BC_VERSION) {
+ JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)",
+ v8, BC_VERSION);
+ return -1;
+ }
+ if (bc_get_leb128(s, &s->idx_to_atom_count))
+ return -1;
+
+ bc_read_trace(s, "%d atom indexes {\n", s->idx_to_atom_count);
+
+ if (s->idx_to_atom_count != 0) {
+ s->idx_to_atom = js_mallocz(s->ctx, s->idx_to_atom_count *
+ sizeof(s->idx_to_atom[0]));
+ if (!s->idx_to_atom)
+ return s->error_state = -1;
+ }
+ for(i = 0; i < s->idx_to_atom_count; i++) {
+ p = JS_ReadString(s);
+ if (!p)
+ return -1;
+ atom = JS_NewAtomStr(s->ctx, p);
+ if (atom == JS_ATOM_NULL)
+ return s->error_state = -1;
+ s->idx_to_atom[i] = atom;
+ if (s->is_rom_data && (atom != (i + s->first_atom)))
+ s->is_rom_data = FALSE; /* atoms must be relocated */
+ }
+ bc_read_trace(s, "}\n");
+ return 0;
+}
+
+static void bc_reader_free(BCReaderState *s)
+{
+ int i;
+ if (s->idx_to_atom) {
+ for(i = 0; i < s->idx_to_atom_count; i++) {
+ JS_FreeAtom(s->ctx, s->idx_to_atom[i]);
+ }
+ js_free(s->ctx, s->idx_to_atom);
+ }
+ js_free(s->ctx, s->objects);
+}
+
+JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
+ int flags)
+{
+ BCReaderState ss, *s = &ss;
+ JSValue obj;
+
+ ctx->binary_object_count += 1;
+ ctx->binary_object_size += buf_len;
+
+ memset(s, 0, sizeof(*s));
+ s->ctx = ctx;
+ s->buf_start = buf;
+ s->buf_end = buf + buf_len;
+ s->ptr = buf;
+ s->allow_bytecode = ((flags & JS_READ_OBJ_BYTECODE) != 0);
+ s->is_rom_data = ((flags & JS_READ_OBJ_ROM_DATA) != 0);
+ s->allow_sab = ((flags & JS_READ_OBJ_SAB) != 0);
+ s->allow_reference = ((flags & JS_READ_OBJ_REFERENCE) != 0);
+ if (s->allow_bytecode)
+ s->first_atom = JS_ATOM_END;
+ else
+ s->first_atom = 1;
+ if (JS_ReadObjectAtoms(s)) {
+ obj = JS_EXCEPTION;
+ } else {
+ obj = JS_ReadObjectRec(s);
+ }
+ bc_reader_free(s);
+ return obj;
+}
+
+/*******************************************************************/
+/* runtime functions & objects */
+
+static JSValue js_string_constructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+static JSValue js_number_constructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+
+static int check_function(JSContext *ctx, JSValueConst obj)
+{
+ if (likely(JS_IsFunction(ctx, obj)))
+ return 0;
+ JS_ThrowTypeError(ctx, "not a function");
+ return -1;
+}
+
+static int check_exception_free(JSContext *ctx, JSValue obj)
+{
+ JS_FreeValue(ctx, obj);
+ return JS_IsException(obj);
+}
+
+static JSAtom find_atom(JSContext *ctx, const char *name)
+{
+ JSAtom atom;
+ int len;
+
+ if (*name == '[') {
+ name++;
+ len = strlen(name) - 1;
+ /* We assume 8 bit non null strings, which is the case for these
+ symbols */
+ for(atom = JS_ATOM_Symbol_toPrimitive; atom < JS_ATOM_END; atom++) {
+ JSAtomStruct *p = ctx->rt->atom_array[atom];
+ JSString *str = p;
+ if (str->len == len && !memcmp(str->u.str8, name, len))
+ return JS_DupAtom(ctx, atom);
+ }
+ abort();
+ } else {
+ atom = JS_NewAtom(ctx, name);
+ }
+ return atom;
+}
+
+static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
+ JSAtom atom, void *opaque)
+{
+ const JSCFunctionListEntry *e = opaque;
+ JSValue val;
+
+ switch(e->def_type) {
+ case JS_DEF_CFUNC:
+ val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
+ e->name, e->u.func.length, e->u.func.cproto, e->magic);
+ break;
+ case JS_DEF_PROP_STRING:
+ val = JS_NewAtomString(ctx, e->u.str);
+ break;
+ case JS_DEF_OBJECT:
+ val = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
+ break;
+ default:
+ abort();
+ }
+ return val;
+}
+
+static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj,
+ JSAtom atom,
+ const JSCFunctionListEntry *e)
+{
+ JSValue val;
+ int prop_flags = e->prop_flags;
+
+ switch(e->def_type) {
+ case JS_DEF_ALIAS: /* using autoinit for aliases is not safe */
+ {
+ JSAtom atom1 = find_atom(ctx, e->u.alias.name);
+ switch (e->u.alias.base) {
+ case -1:
+ val = JS_GetProperty(ctx, obj, atom1);
+ break;
+ case 0:
+ val = JS_GetProperty(ctx, ctx->global_obj, atom1);
+ break;
+ case 1:
+ val = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], atom1);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeAtom(ctx, atom1);
+ if (atom == JS_ATOM_Symbol_toPrimitive) {
+ /* Symbol.toPrimitive functions are not writable */
+ prop_flags = JS_PROP_CONFIGURABLE;
+ } else if (atom == JS_ATOM_Symbol_hasInstance) {
+ /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
+ prop_flags = 0;
+ }
+ }
+ break;
+ case JS_DEF_CFUNC:
+ if (atom == JS_ATOM_Symbol_toPrimitive) {
+ /* Symbol.toPrimitive functions are not writable */
+ prop_flags = JS_PROP_CONFIGURABLE;
+ } else if (atom == JS_ATOM_Symbol_hasInstance) {
+ /* Function.prototype[Symbol.hasInstance] is not writable nor configurable */
+ prop_flags = 0;
+ }
+ JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
+ (void *)e, prop_flags);
+ return 0;
+ case JS_DEF_CGETSET: /* XXX: use autoinit again ? */
+ case JS_DEF_CGETSET_MAGIC:
+ {
+ JSValue getter, setter;
+ char buf[64];
+
+ getter = JS_UNDEFINED;
+ if (e->u.getset.get.generic) {
+ snprintf(buf, sizeof(buf), "get %s", e->name);
+ getter = JS_NewCFunction2(ctx, e->u.getset.get.generic,
+ buf, 0, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_getter_magic : JS_CFUNC_getter,
+ e->magic);
+ }
+ setter = JS_UNDEFINED;
+ if (e->u.getset.set.generic) {
+ snprintf(buf, sizeof(buf), "set %s", e->name);
+ setter = JS_NewCFunction2(ctx, e->u.getset.set.generic,
+ buf, 1, e->def_type == JS_DEF_CGETSET_MAGIC ? JS_CFUNC_setter_magic : JS_CFUNC_setter,
+ e->magic);
+ }
+ JS_DefinePropertyGetSet(ctx, obj, atom, getter, setter, prop_flags);
+ return 0;
+ }
+ break;
+ case JS_DEF_PROP_INT32:
+ val = JS_NewInt32(ctx, e->u.i32);
+ break;
+ case JS_DEF_PROP_INT64:
+ val = JS_NewInt64(ctx, e->u.i64);
+ break;
+ case JS_DEF_PROP_DOUBLE:
+ val = JS_NewFloat64Impl(ctx, e->u.f64);
+ break;
+ case JS_DEF_PROP_UNDEFINED:
+ val = JS_UNDEFINED;
+ break;
+ case JS_DEF_PROP_STRING:
+ case JS_DEF_OBJECT:
+ JS_DefineAutoInitProperty(ctx, obj, atom, JS_AUTOINIT_ID_PROP,
+ (void *)e, prop_flags);
+ return 0;
+ default:
+ abort();
+ }
+ JS_DefinePropertyValue(ctx, obj, atom, val, prop_flags);
+ return 0;
+}
+
+void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
+ const JSCFunctionListEntry *tab, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ const JSCFunctionListEntry *e = &tab[i];
+ JSAtom atom = find_atom(ctx, e->name);
+ JS_InstantiateFunctionListItem(ctx, obj, atom, e);
+ JS_FreeAtom(ctx, atom);
+ }
+}
+
+int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,
+ const JSCFunctionListEntry *tab, int len)
+{
+ int i;
+ for(i = 0; i < len; i++) {
+ if (JS_AddModuleExport(ctx, m, tab[i].name))
+ return -1;
+ }
+ return 0;
+}
+
+int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
+ const JSCFunctionListEntry *tab, int len)
+{
+ int i;
+ JSValue val;
+
+ for(i = 0; i < len; i++) {
+ const JSCFunctionListEntry *e = &tab[i];
+ switch(e->def_type) {
+ case JS_DEF_CFUNC:
+ val = JS_NewCFunction2(ctx, e->u.func.cfunc.generic,
+ e->name, e->u.func.length, e->u.func.cproto, e->magic);
+ break;
+ case JS_DEF_PROP_STRING:
+ val = JS_NewString(ctx, e->u.str);
+ break;
+ case JS_DEF_PROP_INT32:
+ val = JS_NewInt32(ctx, e->u.i32);
+ break;
+ case JS_DEF_PROP_INT64:
+ val = JS_NewInt64(ctx, e->u.i64);
+ break;
+ case JS_DEF_PROP_DOUBLE:
+ val = JS_NewFloat64Impl(ctx, e->u.f64);
+ break;
+ case JS_DEF_OBJECT:
+ val = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, val, e->u.prop_list.tab, e->u.prop_list.len);
+ break;
+ default:
+ abort();
+ }
+ if (JS_SetModuleExport(ctx, m, e->name, val))
+ return -1;
+ }
+ return 0;
+}
+
+/* Note: 'func_obj' is not necessarily a constructor */
+static void JS_SetConstructor2(JSContext *ctx,
+ JSValueConst func_obj,
+ JSValueConst proto,
+ int proto_flags, int ctor_flags)
+{
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_prototype,
+ JS_DupValue(ctx, proto), proto_flags);
+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_constructor,
+ JS_DupValue(ctx, func_obj),
+ ctor_flags);
+ set_cycle_flag(ctx, func_obj);
+ set_cycle_flag(ctx, proto);
+}
+
+void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst proto)
+{
+ JS_SetConstructor2(ctx, func_obj, proto,
+ 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+}
+
+static void JS_NewGlobalCConstructor2(JSContext *ctx,
+ JSValue func_obj,
+ const char *name,
+ JSValueConst proto)
+{
+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, name,
+ JS_DupValue(ctx, func_obj),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_SetConstructor(ctx, func_obj, proto);
+ JS_FreeValue(ctx, func_obj);
+}
+
+static JSValueConst JS_NewGlobalCConstructor(JSContext *ctx, const char *name,
+ JSCFunction *func, int length,
+ JSValueConst proto)
+{
+ JSValue func_obj;
+ func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor_or_func, 0);
+ JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
+ return func_obj;
+}
+
+static JSValueConst JS_NewGlobalCConstructorOnly(JSContext *ctx, const char *name,
+ JSCFunction *func, int length,
+ JSValueConst proto)
+{
+ JSValue func_obj;
+ func_obj = JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_constructor, 0);
+ JS_NewGlobalCConstructor2(ctx, func_obj, name, proto);
+ return func_obj;
+}
+
+static JSValue js_global_eval(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_EvalObject(ctx, ctx->global_obj, argv[0], JS_EVAL_TYPE_INDIRECT, -1);
+}
+
+static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ double d;
+
+ /* XXX: does this work for bigfloat? */
+ if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
+ return JS_EXCEPTION;
+ return JS_NewBool(ctx, isnan(d));
+}
+
+static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ BOOL res;
+ double d;
+ if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
+ return JS_EXCEPTION;
+ res = isfinite(d);
+ return JS_NewBool(ctx, res);
+}
+
+/* Object class */
+
+static JSValue JS_ToObject(JSContext *ctx, JSValueConst val)
+{
+ int tag = JS_VALUE_GET_NORM_TAG(val);
+ JSValue obj;
+
+ switch(tag) {
+ default:
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ return JS_ThrowTypeError(ctx, "cannot convert to object");
+ case JS_TAG_OBJECT:
+ case JS_TAG_EXCEPTION:
+ return JS_DupValue(ctx, val);
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT);
+ goto set_value;
+ case JS_TAG_BIG_FLOAT:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT);
+ goto set_value;
+ case JS_TAG_BIG_DECIMAL:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_DECIMAL);
+ goto set_value;
+#endif
+ case JS_TAG_INT:
+ case JS_TAG_FLOAT64:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_NUMBER);
+ goto set_value;
+ case JS_TAG_STRING:
+ /* XXX: should call the string constructor */
+ {
+ JSString *p1 = JS_VALUE_GET_STRING(val);
+ obj = JS_NewObjectClass(ctx, JS_CLASS_STRING);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
+ }
+ goto set_value;
+ case JS_TAG_BOOL:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_BOOLEAN);
+ goto set_value;
+ case JS_TAG_SYMBOL:
+ obj = JS_NewObjectClass(ctx, JS_CLASS_SYMBOL);
+ set_value:
+ if (!JS_IsException(obj))
+ JS_SetObjectData(ctx, obj, JS_DupValue(ctx, val));
+ return obj;
+ }
+}
+
+static JSValue JS_ToObjectFree(JSContext *ctx, JSValue val)
+{
+ JSValue obj = JS_ToObject(ctx, val);
+ JS_FreeValue(ctx, val);
+ return obj;
+}
+
+static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
+ JSValueConst desc)
+{
+ JSValue val, getter, setter;
+ int flags;
+
+ if (!JS_IsObject(desc)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ flags = 0;
+ val = JS_UNDEFINED;
+ getter = JS_UNDEFINED;
+ setter = JS_UNDEFINED;
+ if (JS_HasProperty(ctx, desc, JS_ATOM_configurable)) {
+ JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_configurable);
+ if (JS_IsException(prop))
+ goto fail;
+ flags |= JS_PROP_HAS_CONFIGURABLE;
+ if (JS_ToBoolFree(ctx, prop))
+ flags |= JS_PROP_CONFIGURABLE;
+ }
+ if (JS_HasProperty(ctx, desc, JS_ATOM_writable)) {
+ JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_writable);
+ if (JS_IsException(prop))
+ goto fail;
+ flags |= JS_PROP_HAS_WRITABLE;
+ if (JS_ToBoolFree(ctx, prop))
+ flags |= JS_PROP_WRITABLE;
+ }
+ if (JS_HasProperty(ctx, desc, JS_ATOM_enumerable)) {
+ JSValue prop = JS_GetProperty(ctx, desc, JS_ATOM_enumerable);
+ if (JS_IsException(prop))
+ goto fail;
+ flags |= JS_PROP_HAS_ENUMERABLE;
+ if (JS_ToBoolFree(ctx, prop))
+ flags |= JS_PROP_ENUMERABLE;
+ }
+ if (JS_HasProperty(ctx, desc, JS_ATOM_value)) {
+ flags |= JS_PROP_HAS_VALUE;
+ val = JS_GetProperty(ctx, desc, JS_ATOM_value);
+ if (JS_IsException(val))
+ goto fail;
+ }
+ if (JS_HasProperty(ctx, desc, JS_ATOM_get)) {
+ flags |= JS_PROP_HAS_GET;
+ getter = JS_GetProperty(ctx, desc, JS_ATOM_get);
+ if (JS_IsException(getter) ||
+ !(JS_IsUndefined(getter) || JS_IsFunction(ctx, getter))) {
+ JS_ThrowTypeError(ctx, "invalid getter");
+ goto fail;
+ }
+ }
+ if (JS_HasProperty(ctx, desc, JS_ATOM_set)) {
+ flags |= JS_PROP_HAS_SET;
+ setter = JS_GetProperty(ctx, desc, JS_ATOM_set);
+ if (JS_IsException(setter) ||
+ !(JS_IsUndefined(setter) || JS_IsFunction(ctx, setter))) {
+ JS_ThrowTypeError(ctx, "invalid setter");
+ goto fail;
+ }
+ }
+ if ((flags & (JS_PROP_HAS_SET | JS_PROP_HAS_GET)) &&
+ (flags & (JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE))) {
+ JS_ThrowTypeError(ctx, "cannot have setter/getter and value or writable");
+ goto fail;
+ }
+ d->flags = flags;
+ d->value = val;
+ d->getter = getter;
+ d->setter = setter;
+ return 0;
+ fail:
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, getter);
+ JS_FreeValue(ctx, setter);
+ return -1;
+}
+
+static warn_unused int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
+ JSAtom prop, JSValueConst desc,
+ int flags)
+{
+ JSPropertyDescriptor d;
+ int ret;
+
+ if (js_obj_to_desc(ctx, &d, desc) < 0)
+ return -1;
+
+ ret = JS_DefineProperty(ctx, obj, prop,
+ d.value, d.getter, d.setter, d.flags | flags);
+ js_free_desc(ctx, &d);
+ return ret;
+}
+
+static warn_unused int JS_ObjectDefineProperties(JSContext *ctx,
+ JSValueConst obj,
+ JSValueConst properties)
+{
+ JSValue props, desc;
+ JSObject *p;
+ JSPropertyEnum *atoms;
+ uint32_t len, i;
+ int ret = -1;
+
+ if (!JS_IsObject(obj)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ desc = JS_UNDEFINED;
+ props = JS_ToObject(ctx, properties);
+ if (JS_IsException(props))
+ return -1;
+ p = JS_VALUE_GET_OBJ(props);
+ if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK) < 0)
+ goto exception;
+ for(i = 0; i < len; i++) {
+ JS_FreeValue(ctx, desc);
+ desc = JS_GetProperty(ctx, props, atoms[i].atom);
+ if (JS_IsException(desc))
+ goto exception;
+ if (JS_DefinePropertyDesc(ctx, obj, atoms[i].atom, desc, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ ret = 0;
+
+exception:
+ js_free_prop_enum(ctx, atoms, len);
+ JS_FreeValue(ctx, props);
+ JS_FreeValue(ctx, desc);
+ return ret;
+}
+
+static JSValue js_object_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue ret;
+ if (!JS_IsUndefined(new_target) &&
+ JS_VALUE_GET_OBJ(new_target) !=
+ JS_VALUE_GET_OBJ(JS_GetActiveFunction(ctx))) {
+ ret = js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
+ } else {
+ int tag = JS_VALUE_GET_NORM_TAG(argv[0]);
+ switch(tag) {
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ ret = JS_NewObject(ctx);
+ break;
+ default:
+ ret = JS_ToObject(ctx, argv[0]);
+ break;
+ }
+ }
+ return ret;
+}
+
+static JSValue js_object_create(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst proto, props;
+ JSValue obj;
+
+ proto = argv[0];
+ if (!JS_IsObject(proto) && !JS_IsNull(proto))
+ return JS_ThrowTypeError(ctx, "not a prototype");
+ obj = JS_NewObjectProto(ctx, proto);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ props = argv[1];
+ if (!JS_IsUndefined(props)) {
+ if (JS_ObjectDefineProperties(ctx, obj, props)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ }
+ return obj;
+}
+
+static JSValue js_object_getPrototypeOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValueConst val;
+
+ val = argv[0];
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) {
+ /* ES6 feature non compatible with ES5.1: primitive types are
+ accepted */
+ if (magic || JS_VALUE_GET_TAG(val) == JS_TAG_NULL ||
+ JS_VALUE_GET_TAG(val) == JS_TAG_UNDEFINED)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ }
+ return JS_GetPrototype(ctx, val);
+}
+
+static JSValue js_object_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj;
+ obj = argv[0];
+ if (JS_SetPrototypeInternal(ctx, obj, argv[1], TRUE) < 0)
+ return JS_EXCEPTION;
+ return JS_DupValue(ctx, obj);
+}
+
+/* magic = 1 if called as Reflect.defineProperty */
+static JSValue js_object_defineProperty(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValueConst obj, prop, desc;
+ int ret, flags;
+ JSAtom atom;
+
+ obj = argv[0];
+ prop = argv[1];
+ desc = argv[2];
+
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ flags = 0;
+ if (!magic)
+ flags |= JS_PROP_THROW;
+ ret = JS_DefinePropertyDesc(ctx, obj, atom, desc, flags);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0) {
+ return JS_EXCEPTION;
+ } else if (magic) {
+ return JS_NewBool(ctx, ret);
+ } else {
+ return JS_DupValue(ctx, obj);
+ }
+}
+
+static JSValue js_object_defineProperties(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // defineProperties(obj, properties)
+ JSValueConst obj = argv[0];
+
+ if (JS_ObjectDefineProperties(ctx, obj, argv[1]))
+ return JS_EXCEPTION;
+ else
+ return JS_DupValue(ctx, obj);
+}
+
+/* magic = 1 if called as __defineSetter__ */
+static JSValue js_object___defineGetter__(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue obj;
+ JSValueConst prop, value, get, set;
+ int ret, flags;
+ JSAtom atom;
+
+ prop = argv[0];
+ value = argv[1];
+
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+
+ if (check_function(ctx, value)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ flags = JS_PROP_THROW |
+ JS_PROP_HAS_ENUMERABLE | JS_PROP_ENUMERABLE |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE;
+ if (magic) {
+ get = JS_UNDEFINED;
+ set = value;
+ flags |= JS_PROP_HAS_SET;
+ } else {
+ get = value;
+ set = JS_UNDEFINED;
+ flags |= JS_PROP_HAS_GET;
+ }
+ ret = JS_DefineProperty(ctx, obj, atom, JS_UNDEFINED, get, set, flags);
+ JS_FreeValue(ctx, obj);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0) {
+ return JS_EXCEPTION;
+ } else {
+ return JS_UNDEFINED;
+ }
+}
+
+static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValueConst prop;
+ JSAtom atom;
+ JSValue ret, obj;
+ JSPropertyDescriptor desc;
+ int res, flags;
+
+ if (magic) {
+ /* Reflect.getOwnPropertyDescriptor case */
+ if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ obj = JS_DupValue(ctx, argv[0]);
+ } else {
+ obj = JS_ToObject(ctx, argv[0]);
+ if (JS_IsException(obj))
+ return obj;
+ }
+ prop = argv[1];
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ goto exception;
+ ret = JS_UNDEFINED;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), atom);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ ret = JS_NewObject(ctx);
+ if (JS_IsException(ret))
+ goto exception1;
+ flags = JS_PROP_C_W_E | JS_PROP_THROW;
+ if (desc.flags & JS_PROP_GETSET) {
+ if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, desc.getter), flags) < 0
+ || JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, desc.setter), flags) < 0)
+ goto exception1;
+ } else {
+ if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0
+ || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
+ JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0)
+ goto exception1;
+ }
+ if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
+ JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0
+ || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
+ JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0)
+ goto exception1;
+ js_free_desc(ctx, &desc);
+ }
+ }
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, obj);
+ return ret;
+
+exception1:
+ js_free_desc(ctx, &desc);
+ JS_FreeValue(ctx, ret);
+exception:
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_object_getOwnPropertyDescriptors(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ //getOwnPropertyDescriptors(obj)
+ JSValue obj, r;
+ JSObject *p;
+ JSPropertyEnum *props;
+ uint32_t len, i;
+
+ r = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, argv[0]);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p,
+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
+ goto exception;
+ r = JS_NewObject(ctx);
+ if (JS_IsException(r))
+ goto exception;
+ for(i = 0; i < len; i++) {
+ JSValue atomValue, desc;
+ JSValueConst args[2];
+
+ atomValue = JS_AtomToValue(ctx, props[i].atom);
+ if (JS_IsException(atomValue))
+ goto exception;
+ args[0] = obj;
+ args[1] = atomValue;
+ desc = js_object_getOwnPropertyDescriptor(ctx, JS_UNDEFINED, 2, args, 0);
+ JS_FreeValue(ctx, atomValue);
+ if (JS_IsException(desc))
+ goto exception;
+ if (!JS_IsUndefined(desc)) {
+ if (JS_DefinePropertyValue(ctx, r, props[i].atom, desc,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ js_free_prop_enum(ctx, props, len);
+ JS_FreeValue(ctx, obj);
+ return r;
+
+exception:
+ js_free_prop_enum(ctx, props, len);
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, r);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_GetOwnPropertyNames2(JSContext *ctx, JSValueConst obj1,
+ int flags, int kind)
+{
+ JSValue obj, r, val, key, value;
+ JSObject *p;
+ JSPropertyEnum *atoms;
+ uint32_t len, i, j;
+
+ r = JS_UNDEFINED;
+ val = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, obj1);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, p, flags & ~JS_GPN_ENUM_ONLY))
+ goto exception;
+ r = JS_NewArray(ctx);
+ if (JS_IsException(r))
+ goto exception;
+ for(j = i = 0; i < len; i++) {
+ JSAtom atom = atoms[i].atom;
+ if (flags & JS_GPN_ENUM_ONLY) {
+ JSPropertyDescriptor desc;
+ int res;
+
+ /* Check if property is still enumerable */
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
+ if (res < 0)
+ goto exception;
+ if (!res)
+ continue;
+ js_free_desc(ctx, &desc);
+ if (!(desc.flags & JS_PROP_ENUMERABLE))
+ continue;
+ }
+ switch(kind) {
+ default:
+ case JS_ITERATOR_KIND_KEY:
+ val = JS_AtomToValue(ctx, atom);
+ if (JS_IsException(val))
+ goto exception;
+ break;
+ case JS_ITERATOR_KIND_VALUE:
+ val = JS_GetProperty(ctx, obj, atom);
+ if (JS_IsException(val))
+ goto exception;
+ break;
+ case JS_ITERATOR_KIND_KEY_AND_VALUE:
+ val = JS_NewArray(ctx);
+ if (JS_IsException(val))
+ goto exception;
+ key = JS_AtomToValue(ctx, atom);
+ if (JS_IsException(key))
+ goto exception1;
+ if (JS_CreateDataPropertyUint32(ctx, val, 0, key, JS_PROP_THROW) < 0)
+ goto exception1;
+ value = JS_GetProperty(ctx, obj, atom);
+ if (JS_IsException(value))
+ goto exception1;
+ if (JS_CreateDataPropertyUint32(ctx, val, 1, value, JS_PROP_THROW) < 0)
+ goto exception1;
+ break;
+ }
+ if (JS_CreateDataPropertyUint32(ctx, r, j++, val, 0) < 0)
+ goto exception;
+ }
+ goto done;
+
+exception1:
+ JS_FreeValue(ctx, val);
+exception:
+ JS_FreeValue(ctx, r);
+ r = JS_EXCEPTION;
+done:
+ js_free_prop_enum(ctx, atoms, len);
+ JS_FreeValue(ctx, obj);
+ return r;
+}
+
+static JSValue js_object_getOwnPropertyNames(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_GetOwnPropertyNames2(ctx, argv[0],
+ JS_GPN_STRING_MASK, JS_ITERATOR_KIND_KEY);
+}
+
+static JSValue js_object_getOwnPropertySymbols(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_GetOwnPropertyNames2(ctx, argv[0],
+ JS_GPN_SYMBOL_MASK, JS_ITERATOR_KIND_KEY);
+}
+
+static JSValue js_object_keys(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int kind)
+{
+ return JS_GetOwnPropertyNames2(ctx, argv[0],
+ JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK, kind);
+}
+
+static JSValue js_object_isExtensible(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int reflect)
+{
+ JSValueConst obj;
+ int ret;
+
+ obj = argv[0];
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
+ if (reflect)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ else
+ return JS_FALSE;
+ }
+ ret = JS_IsExtensible(ctx, obj);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_object_preventExtensions(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int reflect)
+{
+ JSValueConst obj;
+ int ret;
+
+ obj = argv[0];
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) {
+ if (reflect)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ else
+ return JS_DupValue(ctx, obj);
+ }
+ ret = JS_PreventExtensions(ctx, obj);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ if (reflect) {
+ return JS_NewBool(ctx, ret);
+ } else {
+ if (!ret)
+ return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
+ return JS_DupValue(ctx, obj);
+ }
+}
+
+static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ JSAtom atom;
+ JSObject *p;
+ BOOL ret;
+
+ atom = JS_ValueToAtom(ctx, argv[0]); /* must be done first */
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj)) {
+ JS_FreeAtom(ctx, atom);
+ return obj;
+ }
+ p = JS_VALUE_GET_OBJ(obj);
+ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, obj);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToObject(ctx, this_val);
+}
+
+static JSValue js_object_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, tag;
+ int is_array;
+ JSAtom atom;
+ JSObject *p;
+
+ if (JS_IsNull(this_val)) {
+ tag = JS_NewString(ctx, "Null");
+ } else if (JS_IsUndefined(this_val)) {
+ tag = JS_NewString(ctx, "Undefined");
+ } else {
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ return obj;
+ is_array = JS_IsArray(ctx, obj);
+ if (is_array < 0) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ if (is_array) {
+ atom = JS_ATOM_Array;
+ } else if (JS_IsFunction(ctx, obj)) {
+ atom = JS_ATOM_Function;
+ } else {
+ p = JS_VALUE_GET_OBJ(obj);
+ switch(p->class_id) {
+ case JS_CLASS_STRING:
+ case JS_CLASS_ARGUMENTS:
+ case JS_CLASS_MAPPED_ARGUMENTS:
+ case JS_CLASS_ERROR:
+ case JS_CLASS_BOOLEAN:
+ case JS_CLASS_NUMBER:
+ case JS_CLASS_DATE:
+ case JS_CLASS_REGEXP:
+ atom = ctx->rt->class_array[p->class_id].class_name;
+ break;
+ default:
+ atom = JS_ATOM_Object;
+ break;
+ }
+ }
+ tag = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_toStringTag);
+ JS_FreeValue(ctx, obj);
+ if (JS_IsException(tag))
+ return JS_EXCEPTION;
+ if (!JS_IsString(tag)) {
+ JS_FreeValue(ctx, tag);
+ tag = JS_AtomToString(ctx, atom);
+ }
+ }
+ return JS_ConcatString3(ctx, "[object ", tag, "]");
+}
+
+static JSValue js_object_toLocaleString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_Invoke(ctx, this_val, JS_ATOM_toString, 0, NULL);
+}
+
+static JSValue js_object_assign(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // Object.assign(obj, source1)
+ JSValue obj, s;
+ int i;
+
+ s = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, argv[0]);
+ if (JS_IsException(obj))
+ goto exception;
+ for (i = 1; i < argc; i++) {
+ if (!JS_IsNull(argv[i]) && !JS_IsUndefined(argv[i])) {
+ s = JS_ToObject(ctx, argv[i]);
+ if (JS_IsException(s))
+ goto exception;
+ if (JS_CopyDataProperties(ctx, obj, s, JS_UNDEFINED, TRUE))
+ goto exception;
+ JS_FreeValue(ctx, s);
+ }
+ }
+ return obj;
+exception:
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, s);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_object_seal(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int freeze_flag)
+{
+ JSValueConst obj = argv[0];
+ JSObject *p;
+ JSPropertyEnum *props;
+ uint32_t len, i;
+ int flags, desc_flags, res;
+
+ if (!JS_IsObject(obj))
+ return JS_DupValue(ctx, obj);
+
+ res = JS_PreventExtensions(ctx, obj);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (!res) {
+ return JS_ThrowTypeError(ctx, "proxy preventExtensions handler returned false");
+ }
+
+ p = JS_VALUE_GET_OBJ(obj);
+ flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
+ if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
+ return JS_EXCEPTION;
+
+ for(i = 0; i < len; i++) {
+ JSPropertyDescriptor desc;
+ JSAtom prop = props[i].atom;
+
+ desc_flags = JS_PROP_THROW | JS_PROP_HAS_CONFIGURABLE;
+ if (freeze_flag) {
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ if (desc.flags & JS_PROP_WRITABLE)
+ desc_flags |= JS_PROP_HAS_WRITABLE;
+ js_free_desc(ctx, &desc);
+ }
+ }
+ if (JS_DefineProperty(ctx, obj, prop, JS_UNDEFINED,
+ JS_UNDEFINED, JS_UNDEFINED, desc_flags) < 0)
+ goto exception;
+ }
+ js_free_prop_enum(ctx, props, len);
+ return JS_DupValue(ctx, obj);
+
+ exception:
+ js_free_prop_enum(ctx, props, len);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_object_isSealed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int is_frozen)
+{
+ JSValueConst obj = argv[0];
+ JSObject *p;
+ JSPropertyEnum *props;
+ uint32_t len, i;
+ int flags, res;
+
+ if (!JS_IsObject(obj))
+ return JS_TRUE;
+
+ p = JS_VALUE_GET_OBJ(obj);
+ flags = JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK;
+ if (JS_GetOwnPropertyNamesInternal(ctx, &props, &len, p, flags))
+ return JS_EXCEPTION;
+
+ for(i = 0; i < len; i++) {
+ JSPropertyDescriptor desc;
+ JSAtom prop = props[i].atom;
+
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ js_free_desc(ctx, &desc);
+ if ((desc.flags & JS_PROP_CONFIGURABLE)
+ || (is_frozen && (desc.flags & JS_PROP_WRITABLE))) {
+ res = FALSE;
+ goto done;
+ }
+ }
+ }
+ res = JS_IsExtensible(ctx, obj);
+ if (res < 0)
+ return JS_EXCEPTION;
+ res ^= 1;
+done:
+ js_free_prop_enum(ctx, props, len);
+ return JS_NewBool(ctx, res);
+
+exception:
+ js_free_prop_enum(ctx, props, len);
+ return JS_EXCEPTION;
+}
+
+JSValue JS_ObjectSeal(JSContext *ctx, JSValueConst obj, int freeze)
+{
+ JSValueConst argv[] = {obj};
+ JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, argv, freeze));
+ return obj;
+}
+
+static JSValue js_object_fromEntries(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, iter, next_method = JS_UNDEFINED;
+ JSValueConst iterable;
+ BOOL done;
+
+ /* RequireObjectCoercible() not necessary because it is tested in
+ JS_GetIterator() by JS_GetProperty() */
+ iterable = argv[0];
+
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj))
+ return obj;
+
+ iter = JS_GetIterator(ctx, iterable, FALSE);
+ if (JS_IsException(iter))
+ goto fail;
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto fail;
+
+ for(;;) {
+ JSValue key, value, item;
+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(item))
+ goto fail;
+ if (done) {
+ JS_FreeValue(ctx, item);
+ break;
+ }
+
+ key = JS_UNDEFINED;
+ value = JS_UNDEFINED;
+ if (!JS_IsObject(item)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ goto fail1;
+ }
+ key = JS_GetPropertyUint32(ctx, item, 0);
+ if (JS_IsException(key))
+ goto fail1;
+ value = JS_GetPropertyUint32(ctx, item, 1);
+ if (JS_IsException(value)) {
+ JS_FreeValue(ctx, key);
+ goto fail1;
+ }
+ if (JS_DefinePropertyValueValue(ctx, obj, key, value,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0) {
+ fail1:
+ JS_FreeValue(ctx, item);
+ goto fail;
+ }
+ JS_FreeValue(ctx, item);
+ }
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ return obj;
+ fail:
+ if (JS_IsObject(iter)) {
+ /* close the iterator object, preserving pending exception */
+ JS_IteratorClose(ctx, iter, TRUE);
+ }
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+#if 0
+/* Note: corresponds to ECMA spec: CreateDataPropertyOrThrow() */
+static JSValue js_object___setOwnProperty(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int ret;
+ ret = JS_DefinePropertyValueValue(ctx, argv[0], JS_DupValue(ctx, argv[1]),
+ JS_DupValue(ctx, argv[2]),
+ JS_PROP_C_W_E | JS_PROP_THROW);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_object___toObject(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToObject(ctx, argv[0]);
+}
+
+static JSValue js_object___toPrimitive(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int hint = HINT_NONE;
+
+ if (JS_VALUE_GET_TAG(argv[1]) == JS_TAG_INT)
+ hint = JS_VALUE_GET_INT(argv[1]);
+
+ return JS_ToPrimitive(ctx, argv[0], hint);
+}
+#endif
+
+/* return an empty string if not an object */
+static JSValue js_object___getClass(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSAtom atom;
+ JSObject *p;
+ uint32_t tag;
+ int class_id;
+
+ tag = JS_VALUE_GET_NORM_TAG(argv[0]);
+ if (tag == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(argv[0]);
+ class_id = p->class_id;
+ if (class_id == JS_CLASS_PROXY && JS_IsFunction(ctx, argv[0]))
+ class_id = JS_CLASS_BYTECODE_FUNCTION;
+ atom = ctx->rt->class_array[class_id].class_name;
+ } else {
+ atom = JS_ATOM_empty_string;
+ }
+ return JS_AtomToString(ctx, atom);
+}
+
+static JSValue js_object_is(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_NewBool(ctx, js_same_value(ctx, argv[0], argv[1]));
+}
+
+#if 0
+static JSValue js_object___getObjectData(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_GetObjectData(ctx, argv[0]);
+}
+
+static JSValue js_object___setObjectData(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (JS_SetObjectData(ctx, argv[0], JS_DupValue(ctx, argv[1])))
+ return JS_EXCEPTION;
+ return JS_DupValue(ctx, argv[1]);
+}
+
+static JSValue js_object___toPropertyKey(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToPropertyKey(ctx, argv[0]);
+}
+
+static JSValue js_object___isObject(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_NewBool(ctx, JS_IsObject(argv[0]));
+}
+
+static JSValue js_object___isSameValueZero(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_NewBool(ctx, js_same_value_zero(ctx, argv[0], argv[1]));
+}
+
+static JSValue js_object___isConstructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_NewBool(ctx, JS_IsConstructor(ctx, argv[0]));
+}
+#endif
+
+static JSValue JS_SpeciesConstructor(JSContext *ctx, JSValueConst obj,
+ JSValueConst defaultConstructor)
+{
+ JSValue ctor, species;
+
+ if (!JS_IsObject(obj))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
+ if (JS_IsException(ctor))
+ return ctor;
+ if (JS_IsUndefined(ctor))
+ return JS_DupValue(ctx, defaultConstructor);
+ if (!JS_IsObject(ctor)) {
+ JS_FreeValue(ctx, ctor);
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ }
+ species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
+ JS_FreeValue(ctx, ctor);
+ if (JS_IsException(species))
+ return species;
+ if (JS_IsUndefined(species) || JS_IsNull(species))
+ return JS_DupValue(ctx, defaultConstructor);
+ if (!JS_IsConstructor(ctx, species)) {
+ JS_FreeValue(ctx, species);
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ }
+ return species;
+}
+
+#if 0
+static JSValue js_object___speciesConstructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_SpeciesConstructor(ctx, argv[0], argv[1]);
+}
+#endif
+
+static JSValue js_object_get___proto__(JSContext *ctx, JSValueConst this_val)
+{
+ JSValue val, ret;
+
+ val = JS_ToObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ ret = JS_GetPrototype(ctx, val);
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue js_object_set___proto__(JSContext *ctx, JSValueConst this_val,
+ JSValueConst proto)
+{
+ if (JS_IsUndefined(this_val) || JS_IsNull(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ if (!JS_IsObject(proto) && !JS_IsNull(proto))
+ return JS_UNDEFINED;
+ if (JS_SetPrototypeInternal(ctx, this_val, proto, TRUE) < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_UNDEFINED;
+}
+
+static JSValue js_object_isPrototypeOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, v1;
+ JSValueConst v;
+ int res;
+
+ v = argv[0];
+ if (!JS_IsObject(v))
+ return JS_FALSE;
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ v1 = JS_DupValue(ctx, v);
+ for(;;) {
+ v1 = JS_GetPrototypeFree(ctx, v1);
+ if (JS_IsException(v1))
+ goto exception;
+ if (JS_IsNull(v1)) {
+ res = FALSE;
+ break;
+ }
+ if (JS_VALUE_GET_OBJ(obj) == JS_VALUE_GET_OBJ(v1)) {
+ res = TRUE;
+ break;
+ }
+ /* avoid infinite loop (possible with proxies) */
+ if (js_poll_interrupts(ctx))
+ goto exception;
+ }
+ JS_FreeValue(ctx, v1);
+ JS_FreeValue(ctx, obj);
+ return JS_NewBool(ctx, res);
+
+exception:
+ JS_FreeValue(ctx, v1);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, res = JS_EXCEPTION;
+ JSAtom prop = JS_ATOM_NULL;
+ JSPropertyDescriptor desc;
+ int has_prop;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ goto exception;
+ prop = JS_ValueToAtom(ctx, argv[0]);
+ if (unlikely(prop == JS_ATOM_NULL))
+ goto exception;
+
+ has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
+ if (has_prop < 0)
+ goto exception;
+ if (has_prop) {
+ res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0);
+ js_free_desc(ctx, &desc);
+ } else {
+ res = JS_FALSE;
+ }
+
+exception:
+ JS_FreeAtom(ctx, prop);
+ JS_FreeValue(ctx, obj);
+ return res;
+}
+
+static JSValue js_object___lookupGetter__(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int setter)
+{
+ JSValue obj, res = JS_EXCEPTION;
+ JSAtom prop = JS_ATOM_NULL;
+ JSPropertyDescriptor desc;
+ int has_prop;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ goto exception;
+ prop = JS_ValueToAtom(ctx, argv[0]);
+ if (unlikely(prop == JS_ATOM_NULL))
+ goto exception;
+
+ for (;;) {
+ has_prop = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(obj), prop);
+ if (has_prop < 0)
+ goto exception;
+ if (has_prop) {
+ if (desc.flags & JS_PROP_GETSET)
+ res = JS_DupValue(ctx, setter ? desc.setter : desc.getter);
+ else
+ res = JS_UNDEFINED;
+ js_free_desc(ctx, &desc);
+ break;
+ }
+ obj = JS_GetPrototypeFree(ctx, obj);
+ if (JS_IsException(obj))
+ goto exception;
+ if (JS_IsNull(obj)) {
+ res = JS_UNDEFINED;
+ break;
+ }
+ /* avoid infinite loop (possible with proxies) */
+ if (js_poll_interrupts(ctx))
+ goto exception;
+ }
+
+exception:
+ JS_FreeAtom(ctx, prop);
+ JS_FreeValue(ctx, obj);
+ return res;
+}
+
+static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ JSAtom atom;
+ JSObject *p;
+ BOOL ret;
+
+ obj = JS_ToObject(ctx, argv[0]);
+ if (JS_IsException(obj))
+ return obj;
+ atom = JS_ValueToAtom(ctx, argv[1]);
+ if (unlikely(atom == JS_ATOM_NULL)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ p = JS_VALUE_GET_OBJ(obj);
+ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
+ JS_FreeAtom(ctx, atom);
+ JS_FreeValue(ctx, obj);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static const JSCFunctionListEntry js_object_funcs[] = {
+ JS_CFUNC_DEF("create", 2, js_object_create ),
+ JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),
+ JS_CFUNC_DEF("setPrototypeOf", 2, js_object_setPrototypeOf ),
+ JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 0 ),
+ JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ),
+ JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ),
+ JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ),
+ JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ),
+ JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ),
+ JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ),
+ JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 0 ),
+ JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 0 ),
+ JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 0 ),
+ JS_CFUNC_DEF("getOwnPropertyDescriptors", 1, js_object_getOwnPropertyDescriptors ),
+ JS_CFUNC_DEF("is", 2, js_object_is ),
+ JS_CFUNC_DEF("assign", 2, js_object_assign ),
+ JS_CFUNC_MAGIC_DEF("seal", 1, js_object_seal, 0 ),
+ JS_CFUNC_MAGIC_DEF("freeze", 1, js_object_seal, 1 ),
+ JS_CFUNC_MAGIC_DEF("isSealed", 1, js_object_isSealed, 0 ),
+ JS_CFUNC_MAGIC_DEF("isFrozen", 1, js_object_isSealed, 1 ),
+ JS_CFUNC_DEF("__getClass", 1, js_object___getClass ),
+ //JS_CFUNC_DEF("__isObject", 1, js_object___isObject ),
+ //JS_CFUNC_DEF("__isConstructor", 1, js_object___isConstructor ),
+ //JS_CFUNC_DEF("__toObject", 1, js_object___toObject ),
+ //JS_CFUNC_DEF("__setOwnProperty", 3, js_object___setOwnProperty ),
+ //JS_CFUNC_DEF("__toPrimitive", 2, js_object___toPrimitive ),
+ //JS_CFUNC_DEF("__toPropertyKey", 1, js_object___toPropertyKey ),
+ //JS_CFUNC_DEF("__speciesConstructor", 2, js_object___speciesConstructor ),
+ //JS_CFUNC_DEF("__isSameValueZero", 2, js_object___isSameValueZero ),
+ //JS_CFUNC_DEF("__getObjectData", 1, js_object___getObjectData ),
+ //JS_CFUNC_DEF("__setObjectData", 2, js_object___setObjectData ),
+ JS_CFUNC_DEF("fromEntries", 1, js_object_fromEntries ),
+ JS_CFUNC_DEF("hasOwn", 2, js_object_hasOwn ),
+};
+
+static const JSCFunctionListEntry js_object_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_object_toString ),
+ JS_CFUNC_DEF("toLocaleString", 0, js_object_toLocaleString ),
+ JS_CFUNC_DEF("valueOf", 0, js_object_valueOf ),
+ JS_CFUNC_DEF("hasOwnProperty", 1, js_object_hasOwnProperty ),
+ JS_CFUNC_DEF("isPrototypeOf", 1, js_object_isPrototypeOf ),
+ JS_CFUNC_DEF("propertyIsEnumerable", 1, js_object_propertyIsEnumerable ),
+ JS_CGETSET_DEF("__proto__", js_object_get___proto__, js_object_set___proto__ ),
+ JS_CFUNC_MAGIC_DEF("__defineGetter__", 2, js_object___defineGetter__, 0 ),
+ JS_CFUNC_MAGIC_DEF("__defineSetter__", 2, js_object___defineGetter__, 1 ),
+ JS_CFUNC_MAGIC_DEF("__lookupGetter__", 1, js_object___lookupGetter__, 0 ),
+ JS_CFUNC_MAGIC_DEF("__lookupSetter__", 1, js_object___lookupGetter__, 1 ),
+};
+
+/* Function class */
+
+static JSValue js_function_proto(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_UNDEFINED;
+}
+
+/* XXX: add a specific eval mode so that Function("}), ({") is rejected */
+static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSFunctionKindEnum func_kind = magic;
+ int i, n, ret;
+ JSValue s, proto, obj = JS_UNDEFINED;
+ StringBuffer b_s, *b = &b_s;
+
+ string_buffer_init(ctx, b, 0);
+ string_buffer_putc8(b, '(');
+
+ if (func_kind == JS_FUNC_ASYNC || func_kind == JS_FUNC_ASYNC_GENERATOR) {
+ string_buffer_puts8(b, "async ");
+ }
+ string_buffer_puts8(b, "function");
+
+ if (func_kind == JS_FUNC_GENERATOR || func_kind == JS_FUNC_ASYNC_GENERATOR) {
+ string_buffer_putc8(b, '*');
+ }
+ string_buffer_puts8(b, " anonymous(");
+
+ n = argc - 1;
+ for(i = 0; i < n; i++) {
+ if (i != 0) {
+ string_buffer_putc8(b, ',');
+ }
+ if (string_buffer_concat_value(b, argv[i]))
+ goto fail;
+ }
+ string_buffer_puts8(b, "\n) {\n");
+ if (n >= 0) {
+ if (string_buffer_concat_value(b, argv[n]))
+ goto fail;
+ }
+ string_buffer_puts8(b, "\n})");
+ s = string_buffer_end(b);
+ if (JS_IsException(s))
+ goto fail1;
+
+ obj = JS_EvalObject(ctx, ctx->global_obj, s, JS_EVAL_TYPE_INDIRECT, -1);
+ JS_FreeValue(ctx, s);
+ if (JS_IsException(obj))
+ goto fail1;
+ if (!JS_IsUndefined(new_target)) {
+ /* set the prototype */
+ proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
+ if (JS_IsException(proto))
+ goto fail1;
+ if (!JS_IsObject(proto)) {
+ JSContext *realm;
+ JS_FreeValue(ctx, proto);
+ realm = JS_GetFunctionRealm(ctx, new_target);
+ if (!realm)
+ goto fail1;
+ proto = JS_DupValue(ctx, realm->class_proto[func_kind_to_class_id[func_kind]]);
+ }
+ ret = JS_SetPrototypeInternal(ctx, obj, proto, TRUE);
+ JS_FreeValue(ctx, proto);
+ if (ret < 0)
+ goto fail1;
+ }
+ return obj;
+
+ fail:
+ string_buffer_free(b);
+ fail1:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres,
+ JSValueConst obj)
+{
+ JSValue len_val;
+ len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
+ if (JS_IsException(len_val)) {
+ *pres = 0;
+ return -1;
+ }
+ return JS_ToUint32Free(ctx, pres, len_val);
+}
+
+static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres,
+ JSValueConst obj)
+{
+ JSValue len_val;
+ len_val = JS_GetProperty(ctx, obj, JS_ATOM_length);
+ if (JS_IsException(len_val)) {
+ *pres = 0;
+ return -1;
+ }
+ return JS_ToLengthFree(ctx, pres, len_val);
+}
+
+static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len)
+{
+ uint32_t i;
+ for(i = 0; i < len; i++) {
+ JS_FreeValue(ctx, tab[i]);
+ }
+ js_free(ctx, tab);
+}
+
+/* XXX: should use ValueArray */
+static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
+ JSValueConst array_arg)
+{
+ uint32_t len, i;
+ JSValue *tab, ret;
+ JSObject *p;
+
+ if (JS_VALUE_GET_TAG(array_arg) != JS_TAG_OBJECT) {
+ JS_ThrowTypeError(ctx, "not a object");
+ return NULL;
+ }
+ if (js_get_length32(ctx, &len, array_arg))
+ return NULL;
+ if (len > JS_MAX_LOCAL_VARS) {
+ JS_ThrowInternalError(ctx, "too many arguments");
+ return NULL;
+ }
+ /* avoid allocating 0 bytes */
+ tab = js_mallocz(ctx, sizeof(tab[0]) * max_uint32(1, len));
+ if (!tab)
+ return NULL;
+ p = JS_VALUE_GET_OBJ(array_arg);
+ if ((p->class_id == JS_CLASS_ARRAY || p->class_id == JS_CLASS_ARGUMENTS) &&
+ p->fast_array &&
+ len == p->u.array.count) {
+ for(i = 0; i < len; i++) {
+ tab[i] = JS_DupValue(ctx, p->u.array.u.values[i]);
+ }
+ } else {
+ for(i = 0; i < len; i++) {
+ ret = JS_GetPropertyUint32(ctx, array_arg, i);
+ if (JS_IsException(ret)) {
+ free_arg_list(ctx, tab, i);
+ return NULL;
+ }
+ tab[i] = ret;
+ }
+ }
+ *plen = len;
+ return tab;
+}
+
+/* magic value: 0 = normal apply, 1 = apply for constructor, 2 =
+ Reflect.apply */
+static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValueConst this_arg, array_arg;
+ uint32_t len;
+ JSValue *tab, ret;
+
+ if (check_function(ctx, this_val))
+ return JS_EXCEPTION;
+ this_arg = argv[0];
+ array_arg = argv[1];
+ if ((JS_VALUE_GET_TAG(array_arg) == JS_TAG_UNDEFINED ||
+ JS_VALUE_GET_TAG(array_arg) == JS_TAG_NULL) && magic != 2) {
+ return JS_Call(ctx, this_val, this_arg, 0, NULL);
+ }
+ tab = build_arg_list(ctx, &len, array_arg);
+ if (!tab)
+ return JS_EXCEPTION;
+ if (magic & 1) {
+ ret = JS_CallConstructor2(ctx, this_val, this_arg, len, tab);
+ } else {
+ ret = JS_Call(ctx, this_val, this_arg, len, tab);
+ }
+ free_arg_list(ctx, tab, len);
+ return ret;
+}
+
+static JSValue js_function_call(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (argc <= 0) {
+ return JS_Call(ctx, this_val, JS_UNDEFINED, 0, NULL);
+ } else {
+ return JS_Call(ctx, this_val, argv[0], argc - 1, argv + 1);
+ }
+}
+
+static JSValue js_function_bind(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSBoundFunction *bf;
+ JSValue func_obj, name1, len_val;
+ JSObject *p;
+ int arg_count, i, ret;
+
+ if (check_function(ctx, this_val))
+ return JS_EXCEPTION;
+
+ func_obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
+ JS_CLASS_BOUND_FUNCTION);
+ if (JS_IsException(func_obj))
+ return JS_EXCEPTION;
+ p = JS_VALUE_GET_OBJ(func_obj);
+ p->is_constructor = JS_IsConstructor(ctx, this_val);
+ arg_count = max_int(0, argc - 1);
+ bf = js_malloc(ctx, sizeof(*bf) + arg_count * sizeof(JSValue));
+ if (!bf)
+ goto exception;
+ bf->func_obj = JS_DupValue(ctx, this_val);
+ bf->this_val = JS_DupValue(ctx, argv[0]);
+ bf->argc = arg_count;
+ for(i = 0; i < arg_count; i++) {
+ bf->argv[i] = JS_DupValue(ctx, argv[i + 1]);
+ }
+ p->u.bound_function = bf;
+
+ /* XXX: the spec could be simpler by only using GetOwnProperty */
+ ret = JS_GetOwnProperty(ctx, NULL, this_val, JS_ATOM_length);
+ if (ret < 0)
+ goto exception;
+ if (!ret) {
+ len_val = JS_NewInt32(ctx, 0);
+ } else {
+ len_val = JS_GetProperty(ctx, this_val, JS_ATOM_length);
+ if (JS_IsException(len_val))
+ goto exception;
+ if (JS_VALUE_GET_TAG(len_val) == JS_TAG_INT) {
+ /* most common case */
+ int len1 = JS_VALUE_GET_INT(len_val);
+ if (len1 <= arg_count)
+ len1 = 0;
+ else
+ len1 -= arg_count;
+ len_val = JS_NewInt32(ctx, len1);
+ } else if (JS_VALUE_GET_NORM_TAG(len_val) == JS_TAG_FLOAT64) {
+ double d = JS_VALUE_GET_FLOAT64(len_val);
+ if (isnan(d)) {
+ d = 0.0;
+ } else {
+ d = trunc(d);
+ if (d <= (double)arg_count)
+ d = 0.0;
+ else
+ d -= (double)arg_count; /* also converts -0 to +0 */
+ }
+ len_val = JS_NewFloat64(ctx, d);
+ } else {
+ JS_FreeValue(ctx, len_val);
+ len_val = JS_NewInt32(ctx, 0);
+ }
+ }
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_length,
+ len_val, JS_PROP_CONFIGURABLE);
+
+ name1 = JS_GetProperty(ctx, this_val, JS_ATOM_name);
+ if (JS_IsException(name1))
+ goto exception;
+ if (!JS_IsString(name1)) {
+ JS_FreeValue(ctx, name1);
+ name1 = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ }
+ name1 = JS_ConcatString3(ctx, "bound ", name1, "");
+ if (JS_IsException(name1))
+ goto exception;
+ JS_DefinePropertyValue(ctx, func_obj, JS_ATOM_name, name1,
+ JS_PROP_CONFIGURABLE);
+ return func_obj;
+ exception:
+ JS_FreeValue(ctx, func_obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_function_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ JSFunctionKindEnum func_kind = JS_FUNC_NORMAL;
+
+ if (check_function(ctx, this_val))
+ return JS_EXCEPTION;
+
+ p = JS_VALUE_GET_OBJ(this_val);
+ if (js_class_has_bytecode(p->class_id)) {
+ JSFunctionBytecode *b = p->u.func.function_bytecode;
+ if (b->has_debug && b->debug.source) {
+ return JS_NewStringLen(ctx, b->debug.source, b->debug.source_len);
+ }
+ func_kind = b->func_kind;
+ }
+ {
+ JSValue name;
+ const char *pref, *suff;
+
+ switch(func_kind) {
+ default:
+ case JS_FUNC_NORMAL:
+ pref = "function ";
+ break;
+ case JS_FUNC_GENERATOR:
+ pref = "function *";
+ break;
+ case JS_FUNC_ASYNC:
+ pref = "async function ";
+ break;
+ case JS_FUNC_ASYNC_GENERATOR:
+ pref = "async function *";
+ break;
+ }
+ suff = "() {\n [native code]\n}";
+ name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
+ if (JS_IsUndefined(name))
+ name = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ return JS_ConcatString3(ctx, pref, name, suff);
+ }
+}
+
+static JSValue js_function_hasInstance(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int ret;
+ ret = JS_OrdinaryIsInstanceOf(ctx, argv[0], this_val);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static const JSCFunctionListEntry js_function_proto_funcs[] = {
+ JS_CFUNC_DEF("call", 1, js_function_call ),
+ JS_CFUNC_MAGIC_DEF("apply", 2, js_function_apply, 0 ),
+ JS_CFUNC_DEF("bind", 1, js_function_bind ),
+ JS_CFUNC_DEF("toString", 0, js_function_toString ),
+ JS_CFUNC_DEF("[Symbol.hasInstance]", 1, js_function_hasInstance ),
+ JS_CGETSET_DEF("fileName", js_function_proto_fileName, NULL ),
+ JS_CGETSET_DEF("lineNumber", js_function_proto_lineNumber, NULL ),
+};
+
+/* Error class */
+
+static JSValue iterator_to_array(JSContext *ctx, JSValueConst items)
+{
+ JSValue iter, next_method = JS_UNDEFINED;
+ JSValue v, r = JS_UNDEFINED;
+ int64_t k;
+ BOOL done;
+
+ iter = JS_GetIterator(ctx, items, FALSE);
+ if (JS_IsException(iter))
+ goto exception;
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto exception;
+ r = JS_NewArray(ctx);
+ if (JS_IsException(r))
+ goto exception;
+ for (k = 0;; k++) {
+ v = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(v))
+ goto exception_close;
+ if (done)
+ break;
+ if (JS_DefinePropertyValueInt64(ctx, r, k, v,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception_close;
+ }
+ done:
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ return r;
+ exception_close:
+ JS_IteratorClose(ctx, iter, TRUE);
+ exception:
+ JS_FreeValue(ctx, r);
+ r = JS_EXCEPTION;
+ goto done;
+}
+
+static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue obj, msg, proto;
+ JSValueConst message;
+
+ if (JS_IsUndefined(new_target))
+ new_target = JS_GetActiveFunction(ctx);
+ proto = JS_GetProperty(ctx, new_target, JS_ATOM_prototype);
+ if (JS_IsException(proto))
+ return proto;
+ if (!JS_IsObject(proto)) {
+ JSContext *realm;
+ JSValueConst proto1;
+
+ JS_FreeValue(ctx, proto);
+ realm = JS_GetFunctionRealm(ctx, new_target);
+ if (!realm)
+ return JS_EXCEPTION;
+ if (magic < 0) {
+ proto1 = realm->class_proto[JS_CLASS_ERROR];
+ } else {
+ proto1 = realm->native_error_proto[magic];
+ }
+ proto = JS_DupValue(ctx, proto1);
+ }
+ obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_ERROR);
+ JS_FreeValue(ctx, proto);
+ if (JS_IsException(obj))
+ return obj;
+ if (magic == JS_AGGREGATE_ERROR) {
+ message = argv[1];
+ } else {
+ message = argv[0];
+ }
+
+ if (!JS_IsUndefined(message)) {
+ msg = JS_ToString(ctx, message);
+ if (unlikely(JS_IsException(msg)))
+ goto exception;
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_message, msg,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ }
+
+ if (magic == JS_AGGREGATE_ERROR) {
+ JSValue error_list = iterator_to_array(ctx, argv[0]);
+ if (JS_IsException(error_list))
+ goto exception;
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, error_list,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ }
+
+ /* skip the Error() function in the backtrace */
+ build_backtrace(ctx, obj, NULL, 0, JS_BACKTRACE_FLAG_SKIP_FIRST_LEVEL);
+ return obj;
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_error_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue name, msg;
+
+ if (!JS_IsObject(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ name = JS_GetProperty(ctx, this_val, JS_ATOM_name);
+ if (JS_IsUndefined(name))
+ name = JS_AtomToString(ctx, JS_ATOM_Error);
+ else
+ name = JS_ToStringFree(ctx, name);
+ if (JS_IsException(name))
+ return JS_EXCEPTION;
+
+ msg = JS_GetProperty(ctx, this_val, JS_ATOM_message);
+ if (JS_IsUndefined(msg))
+ msg = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ else
+ msg = JS_ToStringFree(ctx, msg);
+ if (JS_IsException(msg)) {
+ JS_FreeValue(ctx, name);
+ return JS_EXCEPTION;
+ }
+ if (!JS_IsEmptyString(name) && !JS_IsEmptyString(msg))
+ name = JS_ConcatString3(ctx, "", name, ": ");
+ return JS_ConcatString(ctx, name, msg);
+}
+
+static const JSCFunctionListEntry js_error_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_error_toString ),
+ JS_PROP_STRING_DEF("name", "Error", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+ JS_PROP_STRING_DEF("message", "", JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+};
+
+/* AggregateError */
+
+/* used by C code. */
+static JSValue js_aggregate_error_constructor(JSContext *ctx,
+ JSValueConst errors)
+{
+ JSValue obj;
+
+ obj = JS_NewObjectProtoClass(ctx,
+ ctx->native_error_proto[JS_AGGREGATE_ERROR],
+ JS_CLASS_ERROR);
+ if (JS_IsException(obj))
+ return obj;
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_errors, JS_DupValue(ctx, errors),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ return obj;
+}
+
+/* Array */
+
+static int JS_CopySubArray(JSContext *ctx,
+ JSValueConst obj, int64_t to_pos,
+ int64_t from_pos, int64_t count, int dir)
+{
+ JSObject *p;
+ int64_t i, from, to, len;
+ JSValue val;
+ int fromPresent;
+
+ p = NULL;
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id != JS_CLASS_ARRAY || !p->fast_array) {
+ p = NULL;
+ }
+ }
+
+ for (i = 0; i < count; ) {
+ if (dir < 0) {
+ from = from_pos + count - i - 1;
+ to = to_pos + count - i - 1;
+ } else {
+ from = from_pos + i;
+ to = to_pos + i;
+ }
+ if (p && p->fast_array &&
+ from >= 0 && from < (len = p->u.array.count) &&
+ to >= 0 && to < len) {
+ int64_t l, j;
+ /* Fast path for fast arrays. Since we don't look at the
+ prototype chain, we can optimize only the cases where
+ all the elements are present in the array. */
+ l = count - i;
+ if (dir < 0) {
+ l = min_int64(l, from + 1);
+ l = min_int64(l, to + 1);
+ for(j = 0; j < l; j++) {
+ set_value(ctx, &p->u.array.u.values[to - j],
+ JS_DupValue(ctx, p->u.array.u.values[from - j]));
+ }
+ } else {
+ l = min_int64(l, len - from);
+ l = min_int64(l, len - to);
+ for(j = 0; j < l; j++) {
+ set_value(ctx, &p->u.array.u.values[to + j],
+ JS_DupValue(ctx, p->u.array.u.values[from + j]));
+ }
+ }
+ i += l;
+ } else {
+ fromPresent = JS_TryGetPropertyInt64(ctx, obj, from, &val);
+ if (fromPresent < 0)
+ goto exception;
+
+ if (fromPresent) {
+ if (JS_SetPropertyInt64(ctx, obj, to, val) < 0)
+ goto exception;
+ } else {
+ if (JS_DeletePropertyInt64(ctx, obj, to, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ i++;
+ }
+ }
+ return 0;
+
+ exception:
+ return -1;
+}
+
+static JSValue js_array_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ int i;
+
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_ARRAY);
+ if (JS_IsException(obj))
+ return obj;
+ if (argc == 1 && JS_IsNumber(argv[0])) {
+ uint32_t len;
+ if (JS_ToArrayLengthFree(ctx, &len, JS_DupValue(ctx, argv[0]), TRUE))
+ goto fail;
+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, len)) < 0)
+ goto fail;
+ } else {
+ for(i = 0; i < argc; i++) {
+ if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0)
+ goto fail;
+ }
+ }
+ return obj;
+fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_from(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // from(items, mapfn = void 0, this_arg = void 0)
+ JSValueConst items = argv[0], mapfn, this_arg;
+ JSValueConst args[2];
+ JSValue stack[2];
+ JSValue iter, r, v, v2, arrayLike;
+ int64_t k, len;
+ int done, mapping;
+
+ mapping = FALSE;
+ mapfn = JS_UNDEFINED;
+ this_arg = JS_UNDEFINED;
+ r = JS_UNDEFINED;
+ arrayLike = JS_UNDEFINED;
+ stack[0] = JS_UNDEFINED;
+ stack[1] = JS_UNDEFINED;
+
+ if (argc > 1) {
+ mapfn = argv[1];
+ if (!JS_IsUndefined(mapfn)) {
+ if (check_function(ctx, mapfn))
+ goto exception;
+ mapping = 1;
+ if (argc > 2)
+ this_arg = argv[2];
+ }
+ }
+ iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
+ if (JS_IsException(iter))
+ goto exception;
+ if (!JS_IsUndefined(iter)) {
+ JS_FreeValue(ctx, iter);
+ if (JS_IsConstructor(ctx, this_val))
+ r = JS_CallConstructor(ctx, this_val, 0, NULL);
+ else
+ r = JS_NewArray(ctx);
+ if (JS_IsException(r))
+ goto exception;
+ stack[0] = JS_DupValue(ctx, items);
+ if (js_for_of_start(ctx, &stack[1], FALSE))
+ goto exception;
+ for (k = 0;; k++) {
+ v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
+ if (JS_IsException(v))
+ goto exception_close;
+ if (done)
+ break;
+ if (mapping) {
+ args[0] = v;
+ args[1] = JS_NewInt32(ctx, k);
+ v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
+ JS_FreeValue(ctx, v);
+ v = v2;
+ if (JS_IsException(v))
+ goto exception_close;
+ }
+ if (JS_DefinePropertyValueInt64(ctx, r, k, v,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception_close;
+ }
+ } else {
+ arrayLike = JS_ToObject(ctx, items);
+ if (JS_IsException(arrayLike))
+ goto exception;
+ if (js_get_length64(ctx, &len, arrayLike) < 0)
+ goto exception;
+ v = JS_NewInt64(ctx, len);
+ args[0] = v;
+ if (JS_IsConstructor(ctx, this_val)) {
+ r = JS_CallConstructor(ctx, this_val, 1, args);
+ } else {
+ r = js_array_constructor(ctx, JS_UNDEFINED, 1, args);
+ }
+ JS_FreeValue(ctx, v);
+ if (JS_IsException(r))
+ goto exception;
+ for(k = 0; k < len; k++) {
+ v = JS_GetPropertyInt64(ctx, arrayLike, k);
+ if (JS_IsException(v))
+ goto exception;
+ if (mapping) {
+ args[0] = v;
+ args[1] = JS_NewInt32(ctx, k);
+ v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
+ JS_FreeValue(ctx, v);
+ v = v2;
+ if (JS_IsException(v))
+ goto exception;
+ }
+ if (JS_DefinePropertyValueInt64(ctx, r, k, v,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ if (JS_SetProperty(ctx, r, JS_ATOM_length, JS_NewUint32(ctx, k)) < 0)
+ goto exception;
+ goto done;
+
+ exception_close:
+ if (!JS_IsUndefined(stack[0]))
+ JS_IteratorClose(ctx, stack[0], TRUE);
+ exception:
+ JS_FreeValue(ctx, r);
+ r = JS_EXCEPTION;
+ done:
+ JS_FreeValue(ctx, arrayLike);
+ JS_FreeValue(ctx, stack[0]);
+ JS_FreeValue(ctx, stack[1]);
+ return r;
+}
+
+static JSValue js_array_of(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, args[1];
+ int i;
+
+ if (JS_IsConstructor(ctx, this_val)) {
+ args[0] = JS_NewInt32(ctx, argc);
+ obj = JS_CallConstructor(ctx, this_val, 1, (JSValueConst *)args);
+ } else {
+ obj = JS_NewArray(ctx);
+ }
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ for(i = 0; i < argc; i++) {
+ if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i]),
+ JS_PROP_THROW) < 0) {
+ goto fail;
+ }
+ }
+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewUint32(ctx, argc)) < 0) {
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ return obj;
+}
+
+static JSValue js_array_isArray(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int ret;
+ ret = JS_IsArray(ctx, argv[0]);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_get_this(JSContext *ctx,
+ JSValueConst this_val)
+{
+ return JS_DupValue(ctx, this_val);
+}
+
+static JSValue JS_ArraySpeciesCreate(JSContext *ctx, JSValueConst obj,
+ JSValueConst len_val)
+{
+ JSValue ctor, ret, species;
+ int res;
+ JSContext *realm;
+
+ res = JS_IsArray(ctx, obj);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (!res)
+ return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
+ ctor = JS_GetProperty(ctx, obj, JS_ATOM_constructor);
+ if (JS_IsException(ctor))
+ return ctor;
+ if (JS_IsConstructor(ctx, ctor)) {
+ /* legacy web compatibility */
+ realm = JS_GetFunctionRealm(ctx, ctor);
+ if (!realm) {
+ JS_FreeValue(ctx, ctor);
+ return JS_EXCEPTION;
+ }
+ if (realm != ctx &&
+ js_same_value(ctx, ctor, realm->array_ctor)) {
+ JS_FreeValue(ctx, ctor);
+ ctor = JS_UNDEFINED;
+ }
+ }
+ if (JS_IsObject(ctor)) {
+ species = JS_GetProperty(ctx, ctor, JS_ATOM_Symbol_species);
+ JS_FreeValue(ctx, ctor);
+ if (JS_IsException(species))
+ return species;
+ ctor = species;
+ if (JS_IsNull(ctor))
+ ctor = JS_UNDEFINED;
+ }
+ if (JS_IsUndefined(ctor)) {
+ return js_array_constructor(ctx, JS_UNDEFINED, 1, &len_val);
+ } else {
+ ret = JS_CallConstructor(ctx, ctor, 1, &len_val);
+ JS_FreeValue(ctx, ctor);
+ return ret;
+ }
+}
+
+static const JSCFunctionListEntry js_array_funcs[] = {
+ JS_CFUNC_DEF("isArray", 1, js_array_isArray ),
+ JS_CFUNC_DEF("from", 1, js_array_from ),
+ JS_CFUNC_DEF("of", 0, js_array_of ),
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+};
+
+static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj)
+{
+ JSValue val;
+
+ if (!JS_IsObject(obj))
+ return FALSE;
+ val = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_isConcatSpreadable);
+ if (JS_IsException(val))
+ return -1;
+ if (!JS_IsUndefined(val))
+ return JS_ToBoolFree(ctx, val);
+ return JS_IsArray(ctx, obj);
+}
+
+static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, arr, val;
+ JSValueConst e;
+ int64_t len, k, n;
+ int i, res;
+
+ arr = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ goto exception;
+
+ arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
+ if (JS_IsException(arr))
+ goto exception;
+ n = 0;
+ for (i = -1; i < argc; i++) {
+ if (i < 0)
+ e = obj;
+ else
+ e = argv[i];
+
+ res = JS_isConcatSpreadable(ctx, e);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ if (js_get_length64(ctx, &len, e))
+ goto exception;
+ if (n + len > MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "Array loo long");
+ goto exception;
+ }
+ for (k = 0; k < len; k++, n++) {
+ res = JS_TryGetPropertyInt64(ctx, e, k, &val);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ if (JS_DefinePropertyValueInt64(ctx, arr, n, val,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ } else {
+ if (n >= MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "Array loo long");
+ goto exception;
+ }
+ if (JS_DefinePropertyValueInt64(ctx, arr, n, JS_DupValue(ctx, e),
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ n++;
+ }
+ }
+ if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
+ goto exception;
+
+ JS_FreeValue(ctx, obj);
+ return arr;
+
+exception:
+ JS_FreeValue(ctx, arr);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+#define special_every 0
+#define special_some 1
+#define special_forEach 2
+#define special_map 3
+#define special_filter 4
+#define special_TA 8
+
+static int js_typed_array_get_length_internal(JSContext *ctx, JSValueConst obj);
+
+static JSValue js_typed_array___speciesCreate(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv);
+
+static JSValue js_array_every(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int special)
+{
+ JSValue obj, val, index_val, res, ret;
+ JSValueConst args[3];
+ JSValueConst func, this_arg;
+ int64_t len, k, n;
+ int present;
+
+ ret = JS_UNDEFINED;
+ val = JS_UNDEFINED;
+ if (special & special_TA) {
+ obj = JS_DupValue(ctx, this_val);
+ len = js_typed_array_get_length_internal(ctx, obj);
+ if (len < 0)
+ goto exception;
+ } else {
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+ }
+ func = argv[0];
+ this_arg = JS_UNDEFINED;
+ if (argc > 1)
+ this_arg = argv[1];
+
+ if (check_function(ctx, func))
+ goto exception;
+
+ switch (special) {
+ case special_every:
+ case special_every | special_TA:
+ ret = JS_TRUE;
+ break;
+ case special_some:
+ case special_some | special_TA:
+ ret = JS_FALSE;
+ break;
+ case special_map:
+ /* XXX: JS_ArraySpeciesCreate should take int64_t */
+ ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt64(ctx, len));
+ if (JS_IsException(ret))
+ goto exception;
+ break;
+ case special_filter:
+ ret = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
+ if (JS_IsException(ret))
+ goto exception;
+ break;
+ case special_map | special_TA:
+ args[0] = obj;
+ args[1] = JS_NewInt32(ctx, len);
+ ret = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
+ if (JS_IsException(ret))
+ goto exception;
+ break;
+ case special_filter | special_TA:
+ ret = JS_NewArray(ctx);
+ if (JS_IsException(ret))
+ goto exception;
+ break;
+ }
+ n = 0;
+
+ for(k = 0; k < len; k++) {
+ if (special & special_TA) {
+ val = JS_GetPropertyInt64(ctx, obj, k);
+ if (JS_IsException(val))
+ goto exception;
+ present = TRUE;
+ } else {
+ present = JS_TryGetPropertyInt64(ctx, obj, k, &val);
+ if (present < 0)
+ goto exception;
+ }
+ if (present) {
+ index_val = JS_NewInt64(ctx, k);
+ if (JS_IsException(index_val))
+ goto exception;
+ args[0] = val;
+ args[1] = index_val;
+ args[2] = obj;
+ res = JS_Call(ctx, func, this_arg, 3, args);
+ JS_FreeValue(ctx, index_val);
+ if (JS_IsException(res))
+ goto exception;
+ switch (special) {
+ case special_every:
+ case special_every | special_TA:
+ if (!JS_ToBoolFree(ctx, res)) {
+ ret = JS_FALSE;
+ goto done;
+ }
+ break;
+ case special_some:
+ case special_some | special_TA:
+ if (JS_ToBoolFree(ctx, res)) {
+ ret = JS_TRUE;
+ goto done;
+ }
+ break;
+ case special_map:
+ if (JS_DefinePropertyValueInt64(ctx, ret, k, res,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ break;
+ case special_map | special_TA:
+ if (JS_SetPropertyValue(ctx, ret, JS_NewInt32(ctx, k), res, JS_PROP_THROW) < 0)
+ goto exception;
+ break;
+ case special_filter:
+ case special_filter | special_TA:
+ if (JS_ToBoolFree(ctx, res)) {
+ if (JS_DefinePropertyValueInt64(ctx, ret, n++, JS_DupValue(ctx, val),
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ break;
+ default:
+ JS_FreeValue(ctx, res);
+ break;
+ }
+ JS_FreeValue(ctx, val);
+ val = JS_UNDEFINED;
+ }
+ }
+done:
+ if (special == (special_filter | special_TA)) {
+ JSValue arr;
+ args[0] = obj;
+ args[1] = JS_NewInt32(ctx, n);
+ arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
+ if (JS_IsException(arr))
+ goto exception;
+ args[0] = ret;
+ res = JS_Invoke(ctx, arr, JS_ATOM_set, 1, args);
+ if (check_exception_free(ctx, res))
+ goto exception;
+ JS_FreeValue(ctx, ret);
+ ret = arr;
+ }
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return ret;
+
+exception:
+ JS_FreeValue(ctx, ret);
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+#define special_reduce 0
+#define special_reduceRight 1
+
+static JSValue js_array_reduce(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int special)
+{
+ JSValue obj, val, index_val, acc, acc1;
+ JSValueConst args[4];
+ JSValueConst func;
+ int64_t len, k, k1;
+ int present;
+
+ acc = JS_UNDEFINED;
+ val = JS_UNDEFINED;
+ if (special & special_TA) {
+ obj = JS_DupValue(ctx, this_val);
+ len = js_typed_array_get_length_internal(ctx, obj);
+ if (len < 0)
+ goto exception;
+ } else {
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+ }
+ func = argv[0];
+
+ if (check_function(ctx, func))
+ goto exception;
+
+ k = 0;
+ if (argc > 1) {
+ acc = JS_DupValue(ctx, argv[1]);
+ } else {
+ for(;;) {
+ if (k >= len) {
+ JS_ThrowTypeError(ctx, "empty array");
+ goto exception;
+ }
+ k1 = (special & special_reduceRight) ? len - k - 1 : k;
+ k++;
+ if (special & special_TA) {
+ acc = JS_GetPropertyInt64(ctx, obj, k1);
+ if (JS_IsException(acc))
+ goto exception;
+ break;
+ } else {
+ present = JS_TryGetPropertyInt64(ctx, obj, k1, &acc);
+ if (present < 0)
+ goto exception;
+ if (present)
+ break;
+ }
+ }
+ }
+ for (; k < len; k++) {
+ k1 = (special & special_reduceRight) ? len - k - 1 : k;
+ if (special & special_TA) {
+ val = JS_GetPropertyInt64(ctx, obj, k1);
+ if (JS_IsException(val))
+ goto exception;
+ present = TRUE;
+ } else {
+ present = JS_TryGetPropertyInt64(ctx, obj, k1, &val);
+ if (present < 0)
+ goto exception;
+ }
+ if (present) {
+ index_val = JS_NewInt64(ctx, k1);
+ if (JS_IsException(index_val))
+ goto exception;
+ args[0] = acc;
+ args[1] = val;
+ args[2] = index_val;
+ args[3] = obj;
+ acc1 = JS_Call(ctx, func, JS_UNDEFINED, 4, args);
+ JS_FreeValue(ctx, index_val);
+ JS_FreeValue(ctx, val);
+ val = JS_UNDEFINED;
+ if (JS_IsException(acc1))
+ goto exception;
+ JS_FreeValue(ctx, acc);
+ acc = acc1;
+ }
+ }
+ JS_FreeValue(ctx, obj);
+ return acc;
+
+exception:
+ JS_FreeValue(ctx, acc);
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_fill(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ int64_t len, start, end;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ start = 0;
+ if (argc > 1 && !JS_IsUndefined(argv[1])) {
+ if (JS_ToInt64Clamp(ctx, &start, argv[1], 0, len, len))
+ goto exception;
+ }
+
+ end = len;
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt64Clamp(ctx, &end, argv[2], 0, len, len))
+ goto exception;
+ }
+
+ /* XXX: should special case fast arrays */
+ while (start < end) {
+ if (JS_SetPropertyInt64(ctx, obj, start,
+ JS_DupValue(ctx, argv[0])) < 0)
+ goto exception;
+ start++;
+ }
+ return obj;
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, val;
+ int64_t len, n, res;
+ JSValue *arrp;
+ uint32_t count;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ res = FALSE;
+ if (len > 0) {
+ n = 0;
+ if (argc > 1) {
+ if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
+ goto exception;
+ }
+ if (js_get_fast_array(ctx, obj, &arrp, &count)) {
+ for (; n < count; n++) {
+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
+ JS_DupValue(ctx, arrp[n]),
+ JS_EQ_SAME_VALUE_ZERO)) {
+ res = TRUE;
+ goto done;
+ }
+ }
+ }
+ for (; n < len; n++) {
+ val = JS_GetPropertyInt64(ctx, obj, n);
+ if (JS_IsException(val))
+ goto exception;
+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val,
+ JS_EQ_SAME_VALUE_ZERO)) {
+ res = TRUE;
+ break;
+ }
+ }
+ }
+ done:
+ JS_FreeValue(ctx, obj);
+ return JS_NewBool(ctx, res);
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_indexOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, val;
+ int64_t len, n, res;
+ JSValue *arrp;
+ uint32_t count;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ res = -1;
+ if (len > 0) {
+ n = 0;
+ if (argc > 1) {
+ if (JS_ToInt64Clamp(ctx, &n, argv[1], 0, len, len))
+ goto exception;
+ }
+ if (js_get_fast_array(ctx, obj, &arrp, &count)) {
+ for (; n < count; n++) {
+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]),
+ JS_DupValue(ctx, arrp[n]), JS_EQ_STRICT)) {
+ res = n;
+ goto done;
+ }
+ }
+ }
+ for (; n < len; n++) {
+ int present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
+ if (present < 0)
+ goto exception;
+ if (present) {
+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
+ res = n;
+ break;
+ }
+ }
+ }
+ }
+ done:
+ JS_FreeValue(ctx, obj);
+ return JS_NewInt64(ctx, res);
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, val;
+ int64_t len, n, res;
+ int present;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ res = -1;
+ if (len > 0) {
+ n = len - 1;
+ if (argc > 1) {
+ if (JS_ToInt64Clamp(ctx, &n, argv[1], -1, len - 1, len))
+ goto exception;
+ }
+ /* XXX: should special case fast arrays */
+ for (; n >= 0; n--) {
+ present = JS_TryGetPropertyInt64(ctx, obj, n, &val);
+ if (present < 0)
+ goto exception;
+ if (present) {
+ if (js_strict_eq2(ctx, JS_DupValue(ctx, argv[0]), val, JS_EQ_STRICT)) {
+ res = n;
+ break;
+ }
+ }
+ }
+ }
+ JS_FreeValue(ctx, obj);
+ return JS_NewInt64(ctx, res);
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_find(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int findIndex)
+{
+ JSValueConst func, this_arg;
+ JSValueConst args[3];
+ JSValue obj, val, index_val, res;
+ int64_t len, k;
+
+ index_val = JS_UNDEFINED;
+ val = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ func = argv[0];
+ if (check_function(ctx, func))
+ goto exception;
+
+ this_arg = JS_UNDEFINED;
+ if (argc > 1)
+ this_arg = argv[1];
+
+ for(k = 0; k < len; k++) {
+ index_val = JS_NewInt64(ctx, k);
+ if (JS_IsException(index_val))
+ goto exception;
+ val = JS_GetPropertyValue(ctx, obj, index_val);
+ if (JS_IsException(val))
+ goto exception;
+ args[0] = val;
+ args[1] = index_val;
+ args[2] = this_val;
+ res = JS_Call(ctx, func, this_arg, 3, args);
+ if (JS_IsException(res))
+ goto exception;
+ if (JS_ToBoolFree(ctx, res)) {
+ if (findIndex) {
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return index_val;
+ } else {
+ JS_FreeValue(ctx, index_val);
+ JS_FreeValue(ctx, obj);
+ return val;
+ }
+ }
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, index_val);
+ }
+ JS_FreeValue(ctx, obj);
+ if (findIndex)
+ return JS_NewInt32(ctx, -1);
+ else
+ return JS_UNDEFINED;
+
+exception:
+ JS_FreeValue(ctx, index_val);
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, method, ret;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ method = JS_GetProperty(ctx, obj, JS_ATOM_join);
+ if (JS_IsException(method)) {
+ ret = JS_EXCEPTION;
+ } else if (!JS_IsFunction(ctx, method)) {
+ /* Use intrinsic Object.prototype.toString */
+ JS_FreeValue(ctx, method);
+ ret = js_object_toString(ctx, obj, 0, NULL);
+ } else {
+ ret = JS_CallFree(ctx, method, obj, 0, NULL);
+ }
+ JS_FreeValue(ctx, obj);
+ return ret;
+}
+
+static JSValue js_array_join(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int toLocaleString)
+{
+ JSValue obj, sep = JS_UNDEFINED, el;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p = NULL;
+ int64_t i, n;
+ int c;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &n, obj))
+ goto exception;
+
+ c = ','; /* default separator */
+ if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
+ sep = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(sep))
+ goto exception;
+ p = JS_VALUE_GET_STRING(sep);
+ if (p->len == 1 && !p->is_wide_char)
+ c = p->u.str8[0];
+ else
+ c = -1;
+ }
+ string_buffer_init(ctx, b, 0);
+
+ for(i = 0; i < n; i++) {
+ if (i > 0) {
+ if (c >= 0) {
+ string_buffer_putc8(b, c);
+ } else {
+ string_buffer_concat(b, p, 0, p->len);
+ }
+ }
+ el = JS_GetPropertyUint32(ctx, obj, i);
+ if (JS_IsException(el))
+ goto fail;
+ if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
+ if (toLocaleString) {
+ el = JS_ToLocaleStringFree(ctx, el);
+ }
+ if (string_buffer_concat_value_free(b, el))
+ goto fail;
+ }
+ }
+ JS_FreeValue(ctx, sep);
+ JS_FreeValue(ctx, obj);
+ return string_buffer_end(b);
+
+fail:
+ string_buffer_free(b);
+ JS_FreeValue(ctx, sep);
+exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_pop(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int shift)
+{
+ JSValue obj, res = JS_UNDEFINED;
+ int64_t len, newLen;
+ JSValue *arrp;
+ uint32_t count32;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+ newLen = 0;
+ if (len > 0) {
+ newLen = len - 1;
+ /* Special case fast arrays */
+ if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ if (shift) {
+ res = arrp[0];
+ memmove(arrp, arrp + 1, (count32 - 1) * sizeof(*arrp));
+ p->u.array.count--;
+ } else {
+ res = arrp[count32 - 1];
+ p->u.array.count--;
+ }
+ } else {
+ if (shift) {
+ res = JS_GetPropertyInt64(ctx, obj, 0);
+ if (JS_IsException(res))
+ goto exception;
+ if (JS_CopySubArray(ctx, obj, 0, 1, len - 1, +1))
+ goto exception;
+ } else {
+ res = JS_GetPropertyInt64(ctx, obj, newLen);
+ if (JS_IsException(res))
+ goto exception;
+ }
+ if (JS_DeletePropertyInt64(ctx, obj, newLen, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
+ goto exception;
+
+ JS_FreeValue(ctx, obj);
+ return res;
+
+ exception:
+ JS_FreeValue(ctx, res);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_push(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int unshift)
+{
+ JSValue obj;
+ int i;
+ int64_t len, from, newLen;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+ newLen = len + argc;
+ if (newLen > MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "Array loo long");
+ goto exception;
+ }
+ from = len;
+ if (unshift && argc > 0) {
+ if (JS_CopySubArray(ctx, obj, argc, 0, len, -1))
+ goto exception;
+ from = 0;
+ }
+ for(i = 0; i < argc; i++) {
+ if (JS_SetPropertyInt64(ctx, obj, from + i,
+ JS_DupValue(ctx, argv[i])) < 0)
+ goto exception;
+ }
+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, newLen)) < 0)
+ goto exception;
+
+ JS_FreeValue(ctx, obj);
+ return JS_NewInt64(ctx, newLen);
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, lval, hval;
+ JSValue *arrp;
+ int64_t len, l, h;
+ int l_present, h_present;
+ uint32_t count32;
+
+ lval = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ /* Special case fast arrays */
+ if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) {
+ uint32_t ll, hh;
+
+ if (count32 > 1) {
+ for (ll = 0, hh = count32 - 1; ll < hh; ll++, hh--) {
+ lval = arrp[ll];
+ arrp[ll] = arrp[hh];
+ arrp[hh] = lval;
+ }
+ }
+ return obj;
+ }
+
+ for (l = 0, h = len - 1; l < h; l++, h--) {
+ l_present = JS_TryGetPropertyInt64(ctx, obj, l, &lval);
+ if (l_present < 0)
+ goto exception;
+ h_present = JS_TryGetPropertyInt64(ctx, obj, h, &hval);
+ if (h_present < 0)
+ goto exception;
+ if (h_present) {
+ if (JS_SetPropertyInt64(ctx, obj, l, hval) < 0)
+ goto exception;
+
+ if (l_present) {
+ if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
+ lval = JS_UNDEFINED;
+ goto exception;
+ }
+ lval = JS_UNDEFINED;
+ } else {
+ if (JS_DeletePropertyInt64(ctx, obj, h, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ } else {
+ if (l_present) {
+ if (JS_DeletePropertyInt64(ctx, obj, l, JS_PROP_THROW) < 0)
+ goto exception;
+ if (JS_SetPropertyInt64(ctx, obj, h, lval) < 0) {
+ lval = JS_UNDEFINED;
+ goto exception;
+ }
+ lval = JS_UNDEFINED;
+ }
+ }
+ }
+ return obj;
+
+ exception:
+ JS_FreeValue(ctx, lval);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int splice)
+{
+ JSValue obj, arr, val, len_val;
+ int64_t len, start, k, final, n, count, del_count, new_len;
+ int kPresent;
+ JSValue *arrp;
+ uint32_t count32, i, item_count;
+
+ arr = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
+ goto exception;
+
+ if (splice) {
+ if (argc == 0) {
+ item_count = 0;
+ del_count = 0;
+ } else if (argc == 1) {
+ item_count = 0;
+ del_count = len - start;
+ } else {
+ item_count = argc - 2;
+ if (JS_ToInt64Clamp(ctx, &del_count, argv[1], 0, len - start, 0))
+ goto exception;
+ }
+ if (len + item_count - del_count > MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "Array loo long");
+ goto exception;
+ }
+ count = del_count;
+ } else {
+ item_count = 0; /* avoid warning */
+ final = len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt64Clamp(ctx, &final, argv[1], 0, len, len))
+ goto exception;
+ }
+ count = max_int64(final - start, 0);
+ }
+ len_val = JS_NewInt64(ctx, count);
+ arr = JS_ArraySpeciesCreate(ctx, obj, len_val);
+ JS_FreeValue(ctx, len_val);
+ if (JS_IsException(arr))
+ goto exception;
+
+ k = start;
+ final = start + count;
+ n = 0;
+ /* The fast array test on arr ensures that
+ JS_CreateDataPropertyUint32() won't modify obj in case arr is
+ an exotic object */
+ /* Special case fast arrays */
+ if (js_get_fast_array(ctx, obj, &arrp, &count32) &&
+ js_is_fast_array(ctx, arr)) {
+ /* XXX: should share code with fast array constructor */
+ for (; k < final && k < count32; k++, n++) {
+ if (JS_CreateDataPropertyUint32(ctx, arr, n, JS_DupValue(ctx, arrp[k]), JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ /* Copy the remaining elements if any (handle case of inherited properties) */
+ for (; k < final; k++, n++) {
+ kPresent = JS_TryGetPropertyInt64(ctx, obj, k, &val);
+ if (kPresent < 0)
+ goto exception;
+ if (kPresent) {
+ if (JS_CreateDataPropertyUint32(ctx, arr, n, val, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, n)) < 0)
+ goto exception;
+
+ if (splice) {
+ new_len = len + item_count - del_count;
+ if (item_count != del_count) {
+ if (JS_CopySubArray(ctx, obj, start + item_count,
+ start + del_count, len - (start + del_count),
+ item_count <= del_count ? +1 : -1) < 0)
+ goto exception;
+
+ for (k = len; k-- > new_len; ) {
+ if (JS_DeletePropertyInt64(ctx, obj, k, JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ for (i = 0; i < item_count; i++) {
+ if (JS_SetPropertyInt64(ctx, obj, start + i, JS_DupValue(ctx, argv[i + 2])) < 0)
+ goto exception;
+ }
+ if (JS_SetProperty(ctx, obj, JS_ATOM_length, JS_NewInt64(ctx, new_len)) < 0)
+ goto exception;
+ }
+ JS_FreeValue(ctx, obj);
+ return arr;
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ int64_t len, from, to, final, count;
+
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ if (JS_ToInt64Clamp(ctx, &to, argv[0], 0, len, len))
+ goto exception;
+
+ if (JS_ToInt64Clamp(ctx, &from, argv[1], 0, len, len))
+ goto exception;
+
+ final = len;
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt64Clamp(ctx, &final, argv[2], 0, len, len))
+ goto exception;
+ }
+
+ count = min_int64(final - from, len - to);
+
+ if (JS_CopySubArray(ctx, obj, to, from, count,
+ (from < to && to < from + count) ? -1 : +1))
+ goto exception;
+
+ return obj;
+
+ exception:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,
+ JSValueConst source, int64_t sourceLen,
+ int64_t targetIndex, int depth,
+ JSValueConst mapperFunction,
+ JSValueConst thisArg)
+{
+ JSValue element;
+ int64_t sourceIndex, elementLen;
+ int present, is_array;
+
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ JS_ThrowStackOverflow(ctx);
+ return -1;
+ }
+
+ for (sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {
+ present = JS_TryGetPropertyInt64(ctx, source, sourceIndex, &element);
+ if (present < 0)
+ return -1;
+ if (!present)
+ continue;
+ if (!JS_IsUndefined(mapperFunction)) {
+ JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };
+ element = JS_Call(ctx, mapperFunction, thisArg, 3, args);
+ JS_FreeValue(ctx, args[0]);
+ JS_FreeValue(ctx, args[1]);
+ if (JS_IsException(element))
+ return -1;
+ }
+ if (depth > 0) {
+ is_array = JS_IsArray(ctx, element);
+ if (is_array < 0)
+ goto fail;
+ if (is_array) {
+ if (js_get_length64(ctx, &elementLen, element) < 0)
+ goto fail;
+ targetIndex = JS_FlattenIntoArray(ctx, target, element,
+ elementLen, targetIndex,
+ depth - 1,
+ JS_UNDEFINED, JS_UNDEFINED);
+ if (targetIndex < 0)
+ goto fail;
+ JS_FreeValue(ctx, element);
+ continue;
+ }
+ }
+ if (targetIndex >= MAX_SAFE_INTEGER) {
+ JS_ThrowTypeError(ctx, "Array too long");
+ goto fail;
+ }
+ if (JS_DefinePropertyValueInt64(ctx, target, targetIndex, element,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ return -1;
+ targetIndex++;
+ }
+ return targetIndex;
+
+fail:
+ JS_FreeValue(ctx, element);
+ return -1;
+}
+
+static JSValue js_array_flatten(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int map)
+{
+ JSValue obj, arr;
+ JSValueConst mapperFunction, thisArg;
+ int64_t sourceLen;
+ int depthNum;
+
+ arr = JS_UNDEFINED;
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &sourceLen, obj))
+ goto exception;
+
+ depthNum = 1;
+ mapperFunction = JS_UNDEFINED;
+ thisArg = JS_UNDEFINED;
+ if (map) {
+ mapperFunction = argv[0];
+ if (argc > 1) {
+ thisArg = argv[1];
+ }
+ if (check_function(ctx, mapperFunction))
+ goto exception;
+ } else {
+ if (argc > 0 && !JS_IsUndefined(argv[0])) {
+ if (JS_ToInt32Sat(ctx, &depthNum, argv[0]) < 0)
+ goto exception;
+ }
+ }
+ arr = JS_ArraySpeciesCreate(ctx, obj, JS_NewInt32(ctx, 0));
+ if (JS_IsException(arr))
+ goto exception;
+ if (JS_FlattenIntoArray(ctx, arr, obj, sourceLen, 0, depthNum,
+ mapperFunction, thisArg) < 0)
+ goto exception;
+ JS_FreeValue(ctx, obj);
+ return arr;
+
+exception:
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+}
+
+/* Array sort */
+
+typedef struct ValueSlot {
+ JSValue val;
+ JSString *str;
+ int64_t pos;
+} ValueSlot;
+
+struct array_sort_context {
+ JSContext *ctx;
+ int exception;
+ int has_method;
+ JSValueConst method;
+};
+
+static int js_array_cmp_generic(const void *a, const void *b, void *opaque) {
+ struct array_sort_context *psc = opaque;
+ JSContext *ctx = psc->ctx;
+ JSValueConst argv[2];
+ JSValue res;
+ ValueSlot *ap = (ValueSlot *)(void *)a;
+ ValueSlot *bp = (ValueSlot *)(void *)b;
+ int cmp;
+
+ if (psc->exception)
+ return 0;
+
+ if (psc->has_method) {
+ /* custom sort function is specified as returning 0 for identical
+ * objects: avoid method call overhead.
+ */
+ if (!memcmp(&ap->val, &bp->val, sizeof(ap->val)))
+ goto cmp_same;
+ argv[0] = ap->val;
+ argv[1] = bp->val;
+ res = JS_Call(ctx, psc->method, JS_UNDEFINED, 2, argv);
+ if (JS_IsException(res))
+ goto exception;
+ if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
+ int val = JS_VALUE_GET_INT(res);
+ cmp = (val > 0) - (val < 0);
+ } else {
+ double val;
+ if (JS_ToFloat64Free(ctx, &val, res) < 0)
+ goto exception;
+ cmp = (val > 0) - (val < 0);
+ }
+ } else {
+ /* Not supposed to bypass ToString even for identical objects as
+ * tested in test262/test/built-ins/Array/prototype/sort/bug_596_1.js
+ */
+ if (!ap->str) {
+ JSValue str = JS_ToString(ctx, ap->val);
+ if (JS_IsException(str))
+ goto exception;
+ ap->str = JS_VALUE_GET_STRING(str);
+ }
+ if (!bp->str) {
+ JSValue str = JS_ToString(ctx, bp->val);
+ if (JS_IsException(str))
+ goto exception;
+ bp->str = JS_VALUE_GET_STRING(str);
+ }
+ cmp = js_string_compare(ctx, ap->str, bp->str);
+ }
+ if (cmp != 0)
+ return cmp;
+cmp_same:
+ /* make sort stable: compare array offsets */
+ return (ap->pos > bp->pos) - (ap->pos < bp->pos);
+
+exception:
+ psc->exception = 1;
+ return 0;
+}
+
+static JSValue js_array_sort(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ struct array_sort_context asc = { ctx, 0, 0, argv[0] };
+ JSValue obj = JS_UNDEFINED;
+ ValueSlot *array = NULL;
+ size_t array_size = 0, pos = 0, n = 0;
+ int64_t i, len, undefined_count = 0;
+ int present;
+
+ if (!JS_IsUndefined(asc.method)) {
+ if (check_function(ctx, asc.method))
+ goto exception;
+ asc.has_method = 1;
+ }
+ obj = JS_ToObject(ctx, this_val);
+ if (js_get_length64(ctx, &len, obj))
+ goto exception;
+
+ /* XXX: should special case fast arrays */
+ for (i = 0; i < len; i++) {
+ if (pos >= array_size) {
+ size_t new_size, slack;
+ ValueSlot *new_array;
+ new_size = (array_size + (array_size >> 1) + 31) & ~15;
+ new_array = js_realloc2(ctx, array, new_size * sizeof(*array), &slack);
+ if (new_array == NULL)
+ goto exception;
+ new_size += slack / sizeof(*new_array);
+ array = new_array;
+ array_size = new_size;
+ }
+ present = JS_TryGetPropertyInt64(ctx, obj, i, &array[pos].val);
+ if (present < 0)
+ goto exception;
+ if (present == 0)
+ continue;
+ if (JS_IsUndefined(array[pos].val)) {
+ undefined_count++;
+ continue;
+ }
+ array[pos].str = NULL;
+ array[pos].pos = i;
+ pos++;
+ }
+ rqsort(array, pos, sizeof(*array), js_array_cmp_generic, &asc);
+ if (asc.exception)
+ goto exception;
+
+ /* XXX: should special case fast arrays */
+ while (n < pos) {
+ if (array[n].str)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
+ if (array[n].pos == n) {
+ JS_FreeValue(ctx, array[n].val);
+ } else {
+ if (JS_SetPropertyInt64(ctx, obj, n, array[n].val) < 0) {
+ n++;
+ goto exception;
+ }
+ }
+ n++;
+ }
+ js_free(ctx, array);
+ for (i = n; undefined_count-- > 0; i++) {
+ if (JS_SetPropertyInt64(ctx, obj, i, JS_UNDEFINED) < 0)
+ goto fail;
+ }
+ for (; i < len; i++) {
+ if (JS_DeletePropertyInt64(ctx, obj, i, JS_PROP_THROW) < 0)
+ goto fail;
+ }
+ return obj;
+
+exception:
+ for (; n < pos; n++) {
+ JS_FreeValue(ctx, array[n].val);
+ if (array[n].str)
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, array[n].str));
+ }
+ js_free(ctx, array);
+fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+typedef struct JSArrayIteratorData {
+ JSValue obj;
+ JSIteratorKindEnum kind;
+ uint32_t idx;
+} JSArrayIteratorData;
+
+static void js_array_iterator_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSArrayIteratorData *it = p->u.array_iterator_data;
+ if (it) {
+ JS_FreeValueRT(rt, it->obj);
+ js_free_rt(rt, it);
+ }
+}
+
+static void js_array_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSArrayIteratorData *it = p->u.array_iterator_data;
+ if (it) {
+ JS_MarkValue(rt, it->obj, mark_func);
+ }
+}
+
+static JSValue js_create_array(JSContext *ctx, int len, JSValueConst *tab)
+{
+ JSValue obj;
+ int i;
+
+ obj = JS_NewArray(ctx);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ for(i = 0; i < len; i++) {
+ if (JS_CreateDataPropertyUint32(ctx, obj, i, JS_DupValue(ctx, tab[i]), 0) < 0) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ }
+ return obj;
+}
+
+static JSValue js_create_array_iterator(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue enum_obj, arr;
+ JSArrayIteratorData *it;
+ JSIteratorKindEnum kind;
+ int class_id;
+
+ kind = magic & 3;
+ if (magic & 4) {
+ /* string iterator case */
+ arr = JS_ToStringCheckObject(ctx, this_val);
+ class_id = JS_CLASS_STRING_ITERATOR;
+ } else {
+ arr = JS_ToObject(ctx, this_val);
+ class_id = JS_CLASS_ARRAY_ITERATOR;
+ }
+ if (JS_IsException(arr))
+ goto fail;
+ enum_obj = JS_NewObjectClass(ctx, class_id);
+ if (JS_IsException(enum_obj))
+ goto fail;
+ it = js_malloc(ctx, sizeof(*it));
+ if (!it)
+ goto fail1;
+ it->obj = arr;
+ it->kind = kind;
+ it->idx = 0;
+ JS_SetOpaque(enum_obj, it);
+ return enum_obj;
+ fail1:
+ JS_FreeValue(ctx, enum_obj);
+ fail:
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_array_iterator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic)
+{
+ JSArrayIteratorData *it;
+ uint32_t len, idx;
+ JSValue val, obj;
+ JSObject *p;
+
+ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_ARRAY_ITERATOR);
+ if (!it)
+ goto fail1;
+ if (JS_IsUndefined(it->obj))
+ goto done;
+ p = JS_VALUE_GET_OBJ(it->obj);
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ if (typed_array_is_detached(ctx, p)) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail1;
+ }
+ len = p->u.array.count;
+ } else {
+ if (js_get_length32(ctx, &len, it->obj)) {
+ fail1:
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+ }
+ }
+ idx = it->idx;
+ if (idx >= len) {
+ JS_FreeValue(ctx, it->obj);
+ it->obj = JS_UNDEFINED;
+ done:
+ *pdone = TRUE;
+ return JS_UNDEFINED;
+ }
+ it->idx = idx + 1;
+ *pdone = FALSE;
+ if (it->kind == JS_ITERATOR_KIND_KEY) {
+ return JS_NewUint32(ctx, idx);
+ } else {
+ val = JS_GetPropertyUint32(ctx, it->obj, idx);
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+ if (it->kind == JS_ITERATOR_KIND_VALUE) {
+ return val;
+ } else {
+ JSValueConst args[2];
+ JSValue num;
+ num = JS_NewUint32(ctx, idx);
+ args[0] = num;
+ args[1] = val;
+ obj = js_create_array(ctx, 2, args);
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, num);
+ return obj;
+ }
+ }
+}
+
+static JSValue js_iterator_proto_iterator(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_DupValue(ctx, this_val);
+}
+
+static const JSCFunctionListEntry js_iterator_proto_funcs[] = {
+ JS_CFUNC_DEF("[Symbol.iterator]", 0, js_iterator_proto_iterator ),
+};
+
+static const JSCFunctionListEntry js_array_proto_funcs[] = {
+ JS_CFUNC_DEF("concat", 1, js_array_concat ),
+ JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ),
+ JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ),
+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach ),
+ JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map ),
+ JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter ),
+ JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ),
+ JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ),
+ JS_CFUNC_DEF("fill", 1, js_array_fill ),
+ JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ),
+ JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ),
+ JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ),
+ JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ),
+ JS_CFUNC_DEF("includes", 1, js_array_includes ),
+ JS_CFUNC_MAGIC_DEF("join", 1, js_array_join, 0 ),
+ JS_CFUNC_DEF("toString", 0, js_array_toString ),
+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_array_join, 1 ),
+ JS_CFUNC_MAGIC_DEF("pop", 0, js_array_pop, 0 ),
+ JS_CFUNC_MAGIC_DEF("push", 1, js_array_push, 0 ),
+ JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ),
+ JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ),
+ JS_CFUNC_DEF("reverse", 0, js_array_reverse ),
+ JS_CFUNC_DEF("sort", 1, js_array_sort ),
+ JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ),
+ JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ),
+ JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ),
+ JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ),
+ JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ),
+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE ),
+ JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
+ JS_CFUNC_MAGIC_DEF("keys", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY ),
+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
+};
+
+static const JSCFunctionListEntry js_array_iterator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 0, js_array_iterator_next, 0 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Array Iterator", JS_PROP_CONFIGURABLE ),
+};
+
+/* Number */
+
+static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, obj;
+ if (argc == 0) {
+ val = JS_NewInt32(ctx, 0);
+ } else {
+ val = JS_ToNumeric(ctx, argv[0]);
+ if (JS_IsException(val))
+ return val;
+ switch(JS_VALUE_GET_TAG(val)) {
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ double d;
+ bf_get_float64(&p->num, &d, BF_RNDN);
+ JS_FreeValue(ctx, val);
+ val = __JS_NewFloat64(ctx, d);
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ val = JS_ToStringFree(ctx, val);
+ if (JS_IsException(val))
+ return val;
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ return val;
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+ if (!JS_IsUndefined(new_target)) {
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_NUMBER);
+ if (!JS_IsException(obj))
+ JS_SetObjectData(ctx, obj, val);
+ return obj;
+ } else {
+ return val;
+ }
+}
+
+#if 0
+static JSValue js_number___toInteger(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[0]));
+}
+
+static JSValue js_number___toLength(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int64_t v;
+ if (JS_ToLengthFree(ctx, &v, JS_DupValue(ctx, argv[0])))
+ return JS_EXCEPTION;
+ return JS_NewInt64(ctx, v);
+}
+#endif
+
+static JSValue js_number_isNaN(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (!JS_IsNumber(argv[0]))
+ return JS_FALSE;
+ return js_global_isNaN(ctx, this_val, argc, argv);
+}
+
+static JSValue js_number_isFinite(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (!JS_IsNumber(argv[0]))
+ return JS_FALSE;
+ return js_global_isFinite(ctx, this_val, argc, argv);
+}
+
+static JSValue js_number_isInteger(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int ret;
+ ret = JS_NumberIsInteger(ctx, argv[0]);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_number_isSafeInteger(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ double d;
+ if (!JS_IsNumber(argv[0]))
+ return JS_FALSE;
+ if (unlikely(JS_ToFloat64(ctx, &d, argv[0])))
+ return JS_EXCEPTION;
+ return JS_NewBool(ctx, is_safe_integer(d));
+}
+
+static const JSCFunctionListEntry js_number_funcs[] = {
+ /* global ParseInt and parseFloat should be defined already or delayed */
+ JS_ALIAS_BASE_DEF("parseInt", "parseInt", 0 ),
+ JS_ALIAS_BASE_DEF("parseFloat", "parseFloat", 0 ),
+ JS_CFUNC_DEF("isNaN", 1, js_number_isNaN ),
+ JS_CFUNC_DEF("isFinite", 1, js_number_isFinite ),
+ JS_CFUNC_DEF("isInteger", 1, js_number_isInteger ),
+ JS_CFUNC_DEF("isSafeInteger", 1, js_number_isSafeInteger ),
+ JS_PROP_DOUBLE_DEF("MAX_VALUE", 1.7976931348623157e+308, 0 ),
+ JS_PROP_DOUBLE_DEF("MIN_VALUE", 5e-324, 0 ),
+ JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
+ JS_PROP_DOUBLE_DEF("NEGATIVE_INFINITY", -INFINITY, 0 ),
+ JS_PROP_DOUBLE_DEF("POSITIVE_INFINITY", INFINITY, 0 ),
+ JS_PROP_DOUBLE_DEF("EPSILON", 2.220446049250313e-16, 0 ), /* ES6 */
+ JS_PROP_DOUBLE_DEF("MAX_SAFE_INTEGER", 9007199254740991.0, 0 ), /* ES6 */
+ JS_PROP_DOUBLE_DEF("MIN_SAFE_INTEGER", -9007199254740991.0, 0 ), /* ES6 */
+ //JS_CFUNC_DEF("__toInteger", 1, js_number___toInteger ),
+ //JS_CFUNC_DEF("__toLength", 1, js_number___toLength ),
+};
+
+static JSValue js_thisNumberValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_IsNumber(this_val))
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_NUMBER) {
+ if (JS_IsNumber(p->u.object_data))
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a number");
+}
+
+static JSValue js_number_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisNumberValue(ctx, this_val);
+}
+
+static int js_get_radix(JSContext *ctx, JSValueConst val)
+{
+ int radix;
+ if (JS_ToInt32Sat(ctx, &radix, val))
+ return -1;
+ if (radix < 2 || radix > 36) {
+ JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
+ return -1;
+ }
+ return radix;
+}
+
+static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue val;
+ int base;
+ double d;
+
+ val = js_thisNumberValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (magic || JS_IsUndefined(argv[0])) {
+ base = 10;
+ } else {
+ base = js_get_radix(ctx, argv[0]);
+ if (base < 0)
+ goto fail;
+ }
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return JS_EXCEPTION;
+ return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT);
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int f;
+ double d;
+
+ val = js_thisNumberValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return JS_EXCEPTION;
+ if (JS_ToInt32Sat(ctx, &f, argv[0]))
+ return JS_EXCEPTION;
+ if (f < 0 || f > 100)
+ return JS_ThrowRangeError(ctx, "invalid number of digits");
+ if (fabs(d) >= 1e21) {
+ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ } else {
+ return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);
+ }
+}
+
+static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int f, flags;
+ double d;
+
+ val = js_thisNumberValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return JS_EXCEPTION;
+ if (JS_ToInt32Sat(ctx, &f, argv[0]))
+ return JS_EXCEPTION;
+ if (!isfinite(d)) {
+ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ }
+ if (JS_IsUndefined(argv[0])) {
+ flags = 0;
+ f = 0;
+ } else {
+ if (f < 0 || f > 100)
+ return JS_ThrowRangeError(ctx, "invalid number of digits");
+ f++;
+ flags = JS_DTOA_FIXED_FORMAT;
+ }
+ return js_dtoa(ctx, d, 10, f, flags | JS_DTOA_FORCE_EXP);
+}
+
+static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int p;
+ double d;
+
+ val = js_thisNumberValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToFloat64Free(ctx, &d, val))
+ return JS_EXCEPTION;
+ if (JS_IsUndefined(argv[0]))
+ goto to_string;
+ if (JS_ToInt32Sat(ctx, &p, argv[0]))
+ return JS_EXCEPTION;
+ if (!isfinite(d)) {
+ to_string:
+ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ }
+ if (p < 1 || p > 100)
+ return JS_ThrowRangeError(ctx, "invalid number of digits");
+ return js_dtoa(ctx, d, 10, p, JS_DTOA_FIXED_FORMAT);
+}
+
+static const JSCFunctionListEntry js_number_proto_funcs[] = {
+ JS_CFUNC_DEF("toExponential", 1, js_number_toExponential ),
+ JS_CFUNC_DEF("toFixed", 1, js_number_toFixed ),
+ JS_CFUNC_DEF("toPrecision", 1, js_number_toPrecision ),
+ JS_CFUNC_MAGIC_DEF("toString", 1, js_number_toString, 0 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_number_toString, 1 ),
+ JS_CFUNC_DEF("valueOf", 0, js_number_valueOf ),
+};
+
+static JSValue js_parseInt(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *str, *p;
+ int radix, flags;
+ JSValue ret;
+
+ str = JS_ToCString(ctx, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &radix, argv[1])) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ if (radix != 0 && (radix < 2 || radix > 36)) {
+ ret = JS_NAN;
+ } else {
+ p = str;
+ p += skip_spaces(p);
+ flags = ATOD_INT_ONLY | ATOD_ACCEPT_PREFIX_AFTER_SIGN;
+ ret = js_atof(ctx, p, NULL, radix, flags);
+ }
+ JS_FreeCString(ctx, str);
+ return ret;
+}
+
+static JSValue js_parseFloat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *str, *p;
+ JSValue ret;
+
+ str = JS_ToCString(ctx, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ ret = js_atof(ctx, p, NULL, 10, 0);
+ JS_FreeCString(ctx, str);
+ return ret;
+}
+
+/* Boolean */
+static JSValue js_boolean_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, obj;
+ val = JS_NewBool(ctx, JS_ToBool(ctx, argv[0]));
+ if (!JS_IsUndefined(new_target)) {
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_BOOLEAN);
+ if (!JS_IsException(obj))
+ JS_SetObjectData(ctx, obj, val);
+ return obj;
+ } else {
+ return val;
+ }
+}
+
+static JSValue js_thisBooleanValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_BOOL)
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_BOOLEAN) {
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_BOOL)
+ return p->u.object_data;
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a boolean");
+}
+
+static JSValue js_boolean_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val = js_thisBooleanValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ return JS_AtomToString(ctx, JS_VALUE_GET_BOOL(val) ?
+ JS_ATOM_true : JS_ATOM_false);
+}
+
+static JSValue js_boolean_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisBooleanValue(ctx, this_val);
+}
+
+static const JSCFunctionListEntry js_boolean_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_boolean_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_boolean_valueOf ),
+};
+
+/* String */
+
+static int js_string_get_own_property(JSContext *ctx,
+ JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop)
+{
+ JSObject *p;
+ JSString *p1;
+ uint32_t idx, ch;
+
+ /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
+ if (JS_AtomIsTaggedInt(prop)) {
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
+ p1 = JS_VALUE_GET_STRING(p->u.object_data);
+ idx = JS_AtomToUInt32(prop);
+ if (idx < p1->len) {
+ if (desc) {
+ if (p1->is_wide_char)
+ ch = p1->u.str16[idx];
+ else
+ ch = p1->u.str8[idx];
+ desc->flags = JS_PROP_ENUMERABLE;
+ desc->value = js_new_string_char(ctx, ch);
+ desc->getter = JS_UNDEFINED;
+ desc->setter = JS_UNDEFINED;
+ }
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+static int js_string_define_own_property(JSContext *ctx,
+ JSValueConst this_obj,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter,
+ JSValueConst setter, int flags)
+{
+ uint32_t idx;
+ JSObject *p;
+ JSString *p1, *p2;
+
+ if (JS_AtomIsTaggedInt(prop)) {
+ idx = JS_AtomToUInt32(prop);
+ p = JS_VALUE_GET_OBJ(this_obj);
+ if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)
+ goto def;
+ p1 = JS_VALUE_GET_STRING(p->u.object_data);
+ if (idx >= p1->len)
+ goto def;
+ if (!check_define_prop_flags(JS_PROP_ENUMERABLE, flags))
+ goto fail;
+ /* check that the same value is configured */
+ if (flags & JS_PROP_HAS_VALUE) {
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_STRING)
+ goto fail;
+ p2 = JS_VALUE_GET_STRING(val);
+ if (p2->len != 1)
+ goto fail;
+ if (string_get(p1, idx) != string_get(p2, 0)) {
+ fail:
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "property is not configurable");
+ }
+ }
+ return TRUE;
+ } else {
+ def:
+ return JS_DefineProperty(ctx, this_obj, prop, val, getter, setter,
+ flags | JS_PROP_NO_EXOTIC);
+ }
+}
+
+static int js_string_delete_property(JSContext *ctx,
+ JSValueConst obj, JSAtom prop)
+{
+ uint32_t idx;
+
+ if (JS_AtomIsTaggedInt(prop)) {
+ idx = JS_AtomToUInt32(prop);
+ if (idx < js_string_obj_get_length(ctx, obj)) {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static const JSClassExoticMethods js_string_exotic_methods = {
+ .get_own_property = js_string_get_own_property,
+ .define_own_property = js_string_define_own_property,
+ .delete_property = js_string_delete_property,
+};
+
+static JSValue js_string_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, obj;
+ if (argc == 0) {
+ val = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ } else {
+ if (JS_IsUndefined(new_target) && JS_IsSymbol(argv[0])) {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(argv[0]);
+ val = JS_ConcatString3(ctx, "Symbol(", JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p)), ")");
+ } else {
+ val = JS_ToString(ctx, argv[0]);
+ }
+ if (JS_IsException(val))
+ return val;
+ }
+ if (!JS_IsUndefined(new_target)) {
+ JSString *p1 = JS_VALUE_GET_STRING(val);
+
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_STRING);
+ if (!JS_IsException(obj)) {
+ JS_SetObjectData(ctx, obj, val);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_length, JS_NewInt32(ctx, p1->len), 0);
+ }
+ return obj;
+ } else {
+ return val;
+ }
+}
+
+static JSValue js_thisStringValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_STRING)
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_STRING) {
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING)
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a string");
+}
+
+static JSValue js_string_fromCharCode(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int i;
+ StringBuffer b_s, *b = &b_s;
+
+ string_buffer_init(ctx, b, argc);
+
+ for(i = 0; i < argc; i++) {
+ int32_t c;
+ if (JS_ToInt32(ctx, &c, argv[i]) || string_buffer_putc16(b, c & 0xffff)) {
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+ }
+ }
+ return string_buffer_end(b);
+}
+
+static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ double d;
+ int i, c;
+ StringBuffer b_s, *b = &b_s;
+
+ /* XXX: could pre-compute string length if all arguments are JS_TAG_INT */
+
+ if (string_buffer_init(ctx, b, argc))
+ goto fail;
+ for(i = 0; i < argc; i++) {
+ if (JS_VALUE_GET_TAG(argv[i]) == JS_TAG_INT) {
+ c = JS_VALUE_GET_INT(argv[i]);
+ if (c < 0 || c > 0x10ffff)
+ goto range_error;
+ } else {
+ if (JS_ToFloat64(ctx, &d, argv[i]))
+ goto fail;
+ if (d < 0 || d > 0x10ffff || (c = (int)d) != d)
+ goto range_error;
+ }
+ if (string_buffer_putc(b, c))
+ goto fail;
+ }
+ return string_buffer_end(b);
+
+ range_error:
+ JS_ThrowRangeError(ctx, "invalid code point");
+ fail:
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_raw(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // raw(temp,...a)
+ JSValue cooked, val, raw;
+ StringBuffer b_s, *b = &b_s;
+ int64_t i, n;
+
+ string_buffer_init(ctx, b, 0);
+ raw = JS_UNDEFINED;
+ cooked = JS_ToObject(ctx, argv[0]);
+ if (JS_IsException(cooked))
+ goto exception;
+ raw = JS_ToObjectFree(ctx, JS_GetProperty(ctx, cooked, JS_ATOM_raw));
+ if (JS_IsException(raw))
+ goto exception;
+ if (js_get_length64(ctx, &n, raw) < 0)
+ goto exception;
+
+ for (i = 0; i < n; i++) {
+ val = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, raw, i));
+ if (JS_IsException(val))
+ goto exception;
+ string_buffer_concat_value_free(b, val);
+ if (i < n - 1 && i + 1 < argc) {
+ if (string_buffer_concat_value(b, argv[i + 1]))
+ goto exception;
+ }
+ }
+ JS_FreeValue(ctx, cooked);
+ JS_FreeValue(ctx, raw);
+ return string_buffer_end(b);
+
+exception:
+ JS_FreeValue(ctx, cooked);
+ JS_FreeValue(ctx, raw);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+/* only used in test262 */
+JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ uint32_t start, end, i, n;
+ StringBuffer b_s, *b = &b_s;
+
+ if (JS_ToUint32(ctx, &start, argv[0]) ||
+ JS_ToUint32(ctx, &end, argv[1]))
+ return JS_EXCEPTION;
+ end = min_uint32(end, 0x10ffff + 1);
+
+ if (start > end) {
+ start = end;
+ }
+ n = end - start;
+ if (end > 0x10000) {
+ n += end - max_uint32(start, 0x10000);
+ }
+ if (string_buffer_init2(ctx, b, n, end >= 0x100))
+ return JS_EXCEPTION;
+ for(i = start; i < end; i++) {
+ string_buffer_putc(b, i);
+ }
+ return string_buffer_end(b);
+}
+
+#if 0
+static JSValue js_string___isSpace(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int c;
+ if (JS_ToInt32(ctx, &c, argv[0]))
+ return JS_EXCEPTION;
+ return JS_NewBool(ctx, lre_is_space(c));
+}
+#endif
+
+static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ JSString *p;
+ int idx, c;
+
+ val = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
+ if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ if (idx < 0 || idx >= p->len) {
+ ret = JS_NAN;
+ } else {
+ if (p->is_wide_char)
+ c = p->u.str16[idx];
+ else
+ c = p->u.str8[idx];
+ ret = JS_NewInt32(ctx, c);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ JSString *p;
+ int idx, c;
+
+ val = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
+ if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ if (idx < 0 || idx >= p->len) {
+ ret = js_new_string8(ctx, NULL, 0);
+ } else {
+ if (p->is_wide_char)
+ c = p->u.str16[idx];
+ else
+ c = p->u.str8[idx];
+ ret = js_new_string_char(ctx, c);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue js_string_codePointAt(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ JSString *p;
+ int idx, c;
+
+ val = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
+ if (JS_ToInt32Sat(ctx, &idx, argv[0])) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ if (idx < 0 || idx >= p->len) {
+ ret = JS_UNDEFINED;
+ } else {
+ c = string_getc(p, &idx);
+ ret = JS_NewInt32(ctx, c);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue js_string_concat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue r;
+ int i;
+
+ /* XXX: Use more efficient method */
+ /* XXX: This method is OK if r has a single refcount */
+ /* XXX: should use string_buffer? */
+ r = JS_ToStringCheckObject(ctx, this_val);
+ for (i = 0; i < argc; i++) {
+ if (JS_IsException(r))
+ break;
+ r = JS_ConcatString(ctx, r, JS_DupValue(ctx, argv[i]));
+ }
+ return r;
+}
+
+static int string_cmp(JSString *p1, JSString *p2, int x1, int x2, int len)
+{
+ int i, c1, c2;
+ for (i = 0; i < len; i++) {
+ if ((c1 = string_get(p1, x1 + i)) != (c2 = string_get(p2, x2 + i)))
+ return c1 - c2;
+ }
+ return 0;
+}
+
+static int string_indexof_char(JSString *p, int c, int from)
+{
+ /* assuming 0 <= from <= p->len */
+ int i, len = p->len;
+ if (p->is_wide_char) {
+ for (i = from; i < len; i++) {
+ if (p->u.str16[i] == c)
+ return i;
+ }
+ } else {
+ if ((c & ~0xff) == 0) {
+ for (i = from; i < len; i++) {
+ if (p->u.str8[i] == (uint8_t)c)
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+static int string_indexof(JSString *p1, JSString *p2, int from)
+{
+ /* assuming 0 <= from <= p1->len */
+ int c, i, j, len1 = p1->len, len2 = p2->len;
+ if (len2 == 0)
+ return from;
+ for (i = from, c = string_get(p2, 0); i + len2 <= len1; i = j + 1) {
+ j = string_indexof_char(p1, c, i);
+ if (j < 0 || j + len2 > len1)
+ break;
+ if (!string_cmp(p1, p2, j + 1, 1, len2 - 1))
+ return j;
+ }
+ return -1;
+}
+
+static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode)
+{
+ if (!unicode || index >= p->len || !p->is_wide_char) {
+ index++;
+ } else {
+ int index32 = (int)index;
+ string_getc(p, &index32);
+ index = index32;
+ }
+ return index;
+}
+
+static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int lastIndexOf)
+{
+ JSValue str, v;
+ int i, len, v_len, pos, start, stop, ret, inc;
+ JSString *p;
+ JSString *p1;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ v = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(v))
+ goto fail;
+ p = JS_VALUE_GET_STRING(str);
+ p1 = JS_VALUE_GET_STRING(v);
+ len = p->len;
+ v_len = p1->len;
+ if (lastIndexOf) {
+ pos = len - v_len;
+ if (argc > 1) {
+ double d;
+ if (JS_ToFloat64(ctx, &d, argv[1]))
+ goto fail;
+ if (!isnan(d)) {
+ if (d <= 0)
+ pos = 0;
+ else if (d < pos)
+ pos = d;
+ }
+ }
+ start = pos;
+ stop = 0;
+ inc = -1;
+ } else {
+ pos = 0;
+ if (argc > 1) {
+ if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
+ goto fail;
+ }
+ start = pos;
+ stop = len - v_len;
+ inc = 1;
+ }
+ ret = -1;
+ if (len >= v_len && inc * (stop - start) >= 0) {
+ for (i = start;; i += inc) {
+ if (!string_cmp(p, p1, i, 0, v_len)) {
+ ret = i;
+ break;
+ }
+ if (i == stop)
+ break;
+ }
+ }
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, v);
+ return JS_NewInt32(ctx, ret);
+
+fail:
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, v);
+ return JS_EXCEPTION;
+}
+
+/* return < 0 if exception or TRUE/FALSE */
+static int js_is_regexp(JSContext *ctx, JSValueConst obj);
+
+static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue str, v = JS_UNDEFINED;
+ int i, len, v_len, pos, start, stop, ret;
+ JSString *p;
+ JSString *p1;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ ret = js_is_regexp(ctx, argv[0]);
+ if (ret) {
+ if (ret > 0)
+ JS_ThrowTypeError(ctx, "regex not supported");
+ goto fail;
+ }
+ v = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(v))
+ goto fail;
+ p = JS_VALUE_GET_STRING(str);
+ p1 = JS_VALUE_GET_STRING(v);
+ len = p->len;
+ v_len = p1->len;
+ pos = (magic == 2) ? len : 0;
+ if (argc > 1 && !JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &pos, argv[1], 0, len, 0))
+ goto fail;
+ }
+ len -= v_len;
+ ret = 0;
+ if (magic == 0) {
+ start = pos;
+ stop = len;
+ } else {
+ if (magic == 1) {
+ if (pos > len)
+ goto done;
+ } else {
+ pos -= v_len;
+ }
+ start = stop = pos;
+ }
+ if (start >= 0 && start <= stop) {
+ for (i = start;; i++) {
+ if (!string_cmp(p, p1, i, 0, v_len)) {
+ ret = 1;
+ break;
+ }
+ if (i == stop)
+ break;
+ }
+ }
+ done:
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, v);
+ return JS_NewBool(ctx, ret);
+
+fail:
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, v);
+ return JS_EXCEPTION;
+}
+
+static int check_regexp_g_flag(JSContext *ctx, JSValueConst regexp)
+{
+ int ret;
+ JSValue flags;
+
+ ret = js_is_regexp(ctx, regexp);
+ if (ret < 0)
+ return -1;
+ if (ret) {
+ flags = JS_GetProperty(ctx, regexp, JS_ATOM_flags);
+ if (JS_IsException(flags))
+ return -1;
+ if (JS_IsUndefined(flags) || JS_IsNull(flags)) {
+ JS_ThrowTypeError(ctx, "cannot convert to object");
+ return -1;
+ }
+ flags = JS_ToStringFree(ctx, flags);
+ if (JS_IsException(flags))
+ return -1;
+ ret = string_indexof_char(JS_VALUE_GET_STRING(flags), 'g', 0);
+ JS_FreeValue(ctx, flags);
+ if (ret < 0) {
+ JS_ThrowTypeError(ctx, "regexp must have the 'g' flag");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int atom)
+{
+ // match(rx), search(rx), matchAll(rx)
+ // atom is JS_ATOM_Symbol_match, JS_ATOM_Symbol_search, or JS_ATOM_Symbol_matchAll
+ JSValueConst O = this_val, regexp = argv[0], args[2];
+ JSValue matcher, S, rx, result, str;
+ int args_len;
+
+ if (JS_IsUndefined(O) || JS_IsNull(O))
+ return JS_ThrowTypeError(ctx, "cannot convert to object");
+
+ if (!JS_IsUndefined(regexp) && !JS_IsNull(regexp)) {
+ matcher = JS_GetProperty(ctx, regexp, atom);
+ if (JS_IsException(matcher))
+ return JS_EXCEPTION;
+ if (atom == JS_ATOM_Symbol_matchAll) {
+ if (check_regexp_g_flag(ctx, regexp) < 0) {
+ JS_FreeValue(ctx, matcher);
+ return JS_EXCEPTION;
+ }
+ }
+ if (!JS_IsUndefined(matcher) && !JS_IsNull(matcher)) {
+ return JS_CallFree(ctx, matcher, regexp, 1, &O);
+ }
+ }
+ S = JS_ToString(ctx, O);
+ if (JS_IsException(S))
+ return JS_EXCEPTION;
+ args_len = 1;
+ args[0] = regexp;
+ str = JS_UNDEFINED;
+ if (atom == JS_ATOM_Symbol_matchAll) {
+ str = JS_NewString(ctx, "g");
+ if (JS_IsException(str))
+ goto fail;
+ args[args_len++] = str;
+ }
+ rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);
+ JS_FreeValue(ctx, str);
+ if (JS_IsException(rx)) {
+ fail:
+ JS_FreeValue(ctx, S);
+ return JS_EXCEPTION;
+ }
+ result = JS_InvokeFree(ctx, rx, atom, 1, &S);
+ JS_FreeValue(ctx, S);
+ return result;
+}
+
+static JSValue js_string___GetSubstitution(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // GetSubstitution(matched, str, position, captures, namedCaptures, rep)
+ JSValueConst matched, str, captures, namedCaptures, rep;
+ JSValue capture, name, s;
+ uint32_t position, len, matched_len, captures_len;
+ int i, j, j0, k, k1;
+ int c, c1;
+ StringBuffer b_s, *b = &b_s;
+ JSString *sp, *rp;
+
+ matched = argv[0];
+ str = argv[1];
+ captures = argv[3];
+ namedCaptures = argv[4];
+ rep = argv[5];
+
+ if (!JS_IsString(rep) || !JS_IsString(str))
+ return JS_ThrowTypeError(ctx, "not a string");
+
+ sp = JS_VALUE_GET_STRING(str);
+ rp = JS_VALUE_GET_STRING(rep);
+
+ string_buffer_init(ctx, b, 0);
+
+ captures_len = 0;
+ if (!JS_IsUndefined(captures)) {
+ if (js_get_length32(ctx, &captures_len, captures))
+ goto exception;
+ }
+ if (js_get_length32(ctx, &matched_len, matched))
+ goto exception;
+ if (JS_ToUint32(ctx, &position, argv[2]) < 0)
+ goto exception;
+
+ len = rp->len;
+ i = 0;
+ for(;;) {
+ j = string_indexof_char(rp, '$', i);
+ if (j < 0 || j + 1 >= len)
+ break;
+ string_buffer_concat(b, rp, i, j);
+ j0 = j++;
+ c = string_get(rp, j++);
+ if (c == '$') {
+ string_buffer_putc8(b, '$');
+ } else if (c == '&') {
+ if (string_buffer_concat_value(b, matched))
+ goto exception;
+ } else if (c == '`') {
+ string_buffer_concat(b, sp, 0, position);
+ } else if (c == '\'') {
+ string_buffer_concat(b, sp, position + matched_len, sp->len);
+ } else if (c >= '0' && c <= '9') {
+ k = c - '0';
+ if (j < len) {
+ c1 = string_get(rp, j);
+ if (c1 >= '0' && c1 <= '9') {
+ /* This behavior is specified in ES6 and refined in ECMA 2019 */
+ /* ECMA 2019 does not have the extra test, but
+ Test262 S15.5.4.11_A3_T1..3 require this behavior */
+ k1 = k * 10 + c1 - '0';
+ if (k1 >= 1 && k1 < captures_len) {
+ k = k1;
+ j++;
+ }
+ }
+ }
+ if (k >= 1 && k < captures_len) {
+ s = JS_GetPropertyInt64(ctx, captures, k);
+ if (JS_IsException(s))
+ goto exception;
+ if (!JS_IsUndefined(s)) {
+ if (string_buffer_concat_value_free(b, s))
+ goto exception;
+ }
+ } else {
+ goto norep;
+ }
+ } else if (c == '<' && !JS_IsUndefined(namedCaptures)) {
+ k = string_indexof_char(rp, '>', j);
+ if (k < 0)
+ goto norep;
+ name = js_sub_string(ctx, rp, j, k);
+ if (JS_IsException(name))
+ goto exception;
+ capture = JS_GetPropertyValue(ctx, namedCaptures, name);
+ if (JS_IsException(capture))
+ goto exception;
+ if (!JS_IsUndefined(capture)) {
+ if (string_buffer_concat_value_free(b, capture))
+ goto exception;
+ }
+ j = k + 1;
+ } else {
+ norep:
+ string_buffer_concat(b, rp, j0, j);
+ }
+ i = j;
+ }
+ string_buffer_concat(b, rp, i, rp->len);
+ return string_buffer_end(b);
+exception:
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_replace(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int is_replaceAll)
+{
+ // replace(rx, rep)
+ JSValueConst O = this_val, searchValue = argv[0], replaceValue = argv[1];
+ JSValueConst args[6];
+ JSValue str, search_str, replaceValue_str, repl_str;
+ JSString *sp, *searchp;
+ StringBuffer b_s, *b = &b_s;
+ int pos, functionalReplace, endOfLastMatch;
+ BOOL is_first;
+
+ if (JS_IsUndefined(O) || JS_IsNull(O))
+ return JS_ThrowTypeError(ctx, "cannot convert to object");
+
+ search_str = JS_UNDEFINED;
+ replaceValue_str = JS_UNDEFINED;
+ repl_str = JS_UNDEFINED;
+
+ if (!JS_IsUndefined(searchValue) && !JS_IsNull(searchValue)) {
+ JSValue replacer;
+ if (is_replaceAll) {
+ if (check_regexp_g_flag(ctx, searchValue) < 0)
+ return JS_EXCEPTION;
+ }
+ replacer = JS_GetProperty(ctx, searchValue, JS_ATOM_Symbol_replace);
+ if (JS_IsException(replacer))
+ return JS_EXCEPTION;
+ if (!JS_IsUndefined(replacer) && !JS_IsNull(replacer)) {
+ args[0] = O;
+ args[1] = replaceValue;
+ return JS_CallFree(ctx, replacer, searchValue, 2, args);
+ }
+ }
+ string_buffer_init(ctx, b, 0);
+
+ str = JS_ToString(ctx, O);
+ if (JS_IsException(str))
+ goto exception;
+ search_str = JS_ToString(ctx, searchValue);
+ if (JS_IsException(search_str))
+ goto exception;
+ functionalReplace = JS_IsFunction(ctx, replaceValue);
+ if (!functionalReplace) {
+ replaceValue_str = JS_ToString(ctx, replaceValue);
+ if (JS_IsException(replaceValue_str))
+ goto exception;
+ }
+
+ sp = JS_VALUE_GET_STRING(str);
+ searchp = JS_VALUE_GET_STRING(search_str);
+ endOfLastMatch = 0;
+ is_first = TRUE;
+ for(;;) {
+ if (unlikely(searchp->len == 0)) {
+ if (is_first)
+ pos = 0;
+ else if (endOfLastMatch >= sp->len)
+ pos = -1;
+ else
+ pos = endOfLastMatch + 1;
+ } else {
+ pos = string_indexof(sp, searchp, endOfLastMatch);
+ }
+ if (pos < 0) {
+ if (is_first) {
+ string_buffer_free(b);
+ JS_FreeValue(ctx, search_str);
+ JS_FreeValue(ctx, replaceValue_str);
+ return str;
+ } else {
+ break;
+ }
+ }
+ if (functionalReplace) {
+ args[0] = search_str;
+ args[1] = JS_NewInt32(ctx, pos);
+ args[2] = str;
+ repl_str = JS_ToStringFree(ctx, JS_Call(ctx, replaceValue, JS_UNDEFINED, 3, args));
+ } else {
+ args[0] = search_str;
+ args[1] = str;
+ args[2] = JS_NewInt32(ctx, pos);
+ args[3] = JS_UNDEFINED;
+ args[4] = JS_UNDEFINED;
+ args[5] = replaceValue_str;
+ repl_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
+ }
+ if (JS_IsException(repl_str))
+ goto exception;
+
+ string_buffer_concat(b, sp, endOfLastMatch, pos);
+ string_buffer_concat_value_free(b, repl_str);
+ endOfLastMatch = pos + searchp->len;
+ is_first = FALSE;
+ if (!is_replaceAll)
+ break;
+ }
+ string_buffer_concat(b, sp, endOfLastMatch, sp->len);
+ JS_FreeValue(ctx, search_str);
+ JS_FreeValue(ctx, replaceValue_str);
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+
+exception:
+ string_buffer_free(b);
+ JS_FreeValue(ctx, search_str);
+ JS_FreeValue(ctx, replaceValue_str);
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_split(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // split(sep, limit)
+ JSValueConst O = this_val, separator = argv[0], limit = argv[1];
+ JSValueConst args[2];
+ JSValue S, A, R, T;
+ uint32_t lim, lengthA;
+ int64_t p, q, s, r, e;
+ JSString *sp, *rp;
+
+ if (JS_IsUndefined(O) || JS_IsNull(O))
+ return JS_ThrowTypeError(ctx, "cannot convert to object");
+
+ S = JS_UNDEFINED;
+ A = JS_UNDEFINED;
+ R = JS_UNDEFINED;
+
+ if (!JS_IsUndefined(separator) && !JS_IsNull(separator)) {
+ JSValue splitter;
+ splitter = JS_GetProperty(ctx, separator, JS_ATOM_Symbol_split);
+ if (JS_IsException(splitter))
+ return JS_EXCEPTION;
+ if (!JS_IsUndefined(splitter) && !JS_IsNull(splitter)) {
+ args[0] = O;
+ args[1] = limit;
+ return JS_CallFree(ctx, splitter, separator, 2, args);
+ }
+ }
+ S = JS_ToString(ctx, O);
+ if (JS_IsException(S))
+ goto exception;
+ A = JS_NewArray(ctx);
+ if (JS_IsException(A))
+ goto exception;
+ lengthA = 0;
+ if (JS_IsUndefined(limit)) {
+ lim = 0xffffffff;
+ } else {
+ if (JS_ToUint32(ctx, &lim, limit) < 0)
+ goto exception;
+ }
+ sp = JS_VALUE_GET_STRING(S);
+ s = sp->len;
+ R = JS_ToString(ctx, separator);
+ if (JS_IsException(R))
+ goto exception;
+ rp = JS_VALUE_GET_STRING(R);
+ r = rp->len;
+ p = 0;
+ if (lim == 0)
+ goto done;
+ if (JS_IsUndefined(separator))
+ goto add_tail;
+ if (s == 0) {
+ if (r != 0)
+ goto add_tail;
+ goto done;
+ }
+ q = p;
+ for (q = p; (q += !r) <= s - r - !r; q = p = e + r) {
+ e = string_indexof(sp, rp, q);
+ if (e < 0)
+ break;
+ T = js_sub_string(ctx, sp, p, e);
+ if (JS_IsException(T))
+ goto exception;
+ if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T, 0) < 0)
+ goto exception;
+ if (lengthA == lim)
+ goto done;
+ }
+add_tail:
+ T = js_sub_string(ctx, sp, p, s);
+ if (JS_IsException(T))
+ goto exception;
+ if (JS_CreateDataPropertyUint32(ctx, A, lengthA++, T,0 ) < 0)
+ goto exception;
+done:
+ JS_FreeValue(ctx, S);
+ JS_FreeValue(ctx, R);
+ return A;
+
+exception:
+ JS_FreeValue(ctx, A);
+ JS_FreeValue(ctx, S);
+ JS_FreeValue(ctx, R);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_substring(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str, ret;
+ int a, b, start, end;
+ JSString *p;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ p = JS_VALUE_GET_STRING(str);
+ if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, p->len, 0)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ b = p->len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &b, argv[1], 0, p->len, 0)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ }
+ if (a < b) {
+ start = a;
+ end = b;
+ } else {
+ start = b;
+ end = a;
+ }
+ ret = js_sub_string(ctx, p, start, end);
+ JS_FreeValue(ctx, str);
+ return ret;
+}
+
+static JSValue js_string_substr(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str, ret;
+ int a, len, n;
+ JSString *p;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ p = JS_VALUE_GET_STRING(str);
+ len = p->len;
+ if (JS_ToInt32Clamp(ctx, &a, argv[0], 0, len, len)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ n = len - a;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &n, argv[1], 0, len - a, 0)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ }
+ ret = js_sub_string(ctx, p, a, a + n);
+ JS_FreeValue(ctx, str);
+ return ret;
+}
+
+static JSValue js_string_slice(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str, ret;
+ int len, start, end;
+ JSString *p;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ p = JS_VALUE_GET_STRING(str);
+ len = p->len;
+ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ end = len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &end, argv[1], 0, len, len)) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ }
+ ret = js_sub_string(ctx, p, start, max_int(end, start));
+ JS_FreeValue(ctx, str);
+ return ret;
+}
+
+static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int padEnd)
+{
+ JSValue str, v = JS_UNDEFINED;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p, *p1 = NULL;
+ int n, len, c = ' ';
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ goto fail1;
+ if (JS_ToInt32Sat(ctx, &n, argv[0]))
+ goto fail2;
+ p = JS_VALUE_GET_STRING(str);
+ len = p->len;
+ if (len >= n)
+ return str;
+ if (argc > 1 && !JS_IsUndefined(argv[1])) {
+ v = JS_ToString(ctx, argv[1]);
+ if (JS_IsException(v))
+ goto fail2;
+ p1 = JS_VALUE_GET_STRING(v);
+ if (p1->len == 0) {
+ JS_FreeValue(ctx, v);
+ return str;
+ }
+ if (p1->len == 1) {
+ c = string_get(p1, 0);
+ p1 = NULL;
+ }
+ }
+ if (n > JS_STRING_LEN_MAX) {
+ JS_ThrowInternalError(ctx, "string too long");
+ goto fail2;
+ }
+ if (string_buffer_init(ctx, b, n))
+ goto fail3;
+ n -= len;
+ if (padEnd) {
+ if (string_buffer_concat(b, p, 0, len))
+ goto fail;
+ }
+ if (p1) {
+ while (n > 0) {
+ int chunk = min_int(n, p1->len);
+ if (string_buffer_concat(b, p1, 0, chunk))
+ goto fail;
+ n -= chunk;
+ }
+ } else {
+ if (string_buffer_fill(b, c, n))
+ goto fail;
+ }
+ if (!padEnd) {
+ if (string_buffer_concat(b, p, 0, len))
+ goto fail;
+ }
+ JS_FreeValue(ctx, v);
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+
+fail:
+ string_buffer_free(b);
+fail3:
+ JS_FreeValue(ctx, v);
+fail2:
+ JS_FreeValue(ctx, str);
+fail1:
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int64_t val;
+ int n, len;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ goto fail;
+ if (JS_ToInt64Sat(ctx, &val, argv[0]))
+ goto fail;
+ if (val < 0 || val > 2147483647) {
+ JS_ThrowRangeError(ctx, "invalid repeat count");
+ goto fail;
+ }
+ n = val;
+ p = JS_VALUE_GET_STRING(str);
+ len = p->len;
+ if (len == 0 || n == 1)
+ return str;
+ if (val * len > JS_STRING_LEN_MAX) {
+ JS_ThrowInternalError(ctx, "string too long");
+ goto fail;
+ }
+ if (string_buffer_init2(ctx, b, n * len, p->is_wide_char))
+ goto fail;
+ if (len == 1) {
+ string_buffer_fill(b, string_get(p, 0), n);
+ } else {
+ while (n-- > 0) {
+ string_buffer_concat(b, p, 0, len);
+ }
+ }
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+
+fail:
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_trim(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue str, ret;
+ int a, b, len;
+ JSString *p;
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return str;
+ p = JS_VALUE_GET_STRING(str);
+ a = 0;
+ b = len = p->len;
+ if (magic & 1) {
+ while (a < len && lre_is_space(string_get(p, a)))
+ a++;
+ }
+ if (magic & 2) {
+ while (b > a && lre_is_space(string_get(p, b - 1)))
+ b--;
+ }
+ ret = js_sub_string(ctx, p, a, b);
+ JS_FreeValue(ctx, str);
+ return ret;
+}
+
+static JSValue js_string___quote(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToQuotedString(ctx, this_val);
+}
+
+/* return 0 if before the first char */
+static int string_prevc(JSString *p, int *pidx)
+{
+ int idx, c, c1;
+
+ idx = *pidx;
+ if (idx <= 0)
+ return 0;
+ idx--;
+ if (p->is_wide_char) {
+ c = p->u.str16[idx];
+ if (c >= 0xdc00 && c < 0xe000 && idx > 0) {
+ c1 = p->u.str16[idx - 1];
+ if (c1 >= 0xd800 && c1 <= 0xdc00) {
+ c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000;
+ idx--;
+ }
+ }
+ } else {
+ c = p->u.str8[idx];
+ }
+ *pidx = idx;
+ return c;
+}
+
+static BOOL test_final_sigma(JSString *p, int sigma_pos)
+{
+ int k, c1;
+
+ /* before C: skip case ignorable chars and check there is
+ a cased letter */
+ k = sigma_pos;
+ for(;;) {
+ c1 = string_prevc(p, &k);
+ if (!lre_is_case_ignorable(c1))
+ break;
+ }
+ if (!lre_is_cased(c1))
+ return FALSE;
+
+ /* after C: skip case ignorable chars and check there is
+ no cased letter */
+ k = sigma_pos + 1;
+ for(;;) {
+ if (k >= p->len)
+ return TRUE;
+ c1 = string_getc(p, &k);
+ if (!lre_is_case_ignorable(c1))
+ break;
+ }
+ return !lre_is_cased(c1);
+}
+
+static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue a, b;
+ int cmp;
+
+ a = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(a))
+ return JS_EXCEPTION;
+ b = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(b)) {
+ JS_FreeValue(ctx, a);
+ return JS_EXCEPTION;
+ }
+ cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b));
+ JS_FreeValue(ctx, a);
+ JS_FreeValue(ctx, b);
+ return JS_NewInt32(ctx, cmp);
+}
+
+static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int to_lower)
+{
+ JSValue val;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int i, c, j, l;
+ uint32_t res[LRE_CC_RES_LEN_MAX];
+
+ val = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_STRING(val);
+ if (p->len == 0)
+ return val;
+ if (string_buffer_init(ctx, b, p->len))
+ goto fail;
+ for(i = 0; i < p->len;) {
+ c = string_getc(p, &i);
+ if (c == 0x3a3 && to_lower && test_final_sigma(p, i - 1)) {
+ res[0] = 0x3c2; /* final sigma */
+ l = 1;
+ } else {
+ l = lre_case_conv(res, c, to_lower);
+ }
+ for(j = 0; j < l; j++) {
+ if (string_buffer_putc(b, res[j]))
+ goto fail;
+ }
+ }
+ JS_FreeValue(ctx, val);
+ return string_buffer_end(b);
+ fail:
+ JS_FreeValue(ctx, val);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+#ifdef CONFIG_ALL_UNICODE
+
+/* return (-1, NULL) if exception, otherwise (len, buf) */
+static int JS_ToUTF32String(JSContext *ctx, uint32_t **pbuf, JSValueConst val1)
+{
+ JSValue val;
+ JSString *p;
+ uint32_t *buf;
+ int i, j, len;
+
+ val = JS_ToString(ctx, val1);
+ if (JS_IsException(val))
+ return -1;
+ p = JS_VALUE_GET_STRING(val);
+ len = p->len;
+ /* UTF32 buffer length is len minus the number of correct surrogates pairs */
+ buf = js_malloc(ctx, sizeof(buf[0]) * max_int(len, 1));
+ if (!buf) {
+ JS_FreeValue(ctx, val);
+ goto fail;
+ }
+ for(i = j = 0; i < len;)
+ buf[j++] = string_getc(p, &i);
+ JS_FreeValue(ctx, val);
+ *pbuf = buf;
+ return j;
+ fail:
+ *pbuf = NULL;
+ return -1;
+}
+
+static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len)
+{
+ int i;
+ StringBuffer b_s, *b = &b_s;
+ if (string_buffer_init(ctx, b, len))
+ return JS_EXCEPTION;
+ for(i = 0; i < len; i++) {
+ if (string_buffer_putc(b, buf[i]))
+ goto fail;
+ }
+ return string_buffer_end(b);
+ fail:
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ const char *form, *p;
+ size_t form_len;
+ int is_compat, buf_len, out_len;
+ UnicodeNormalizationEnum n_type;
+ JSValue val;
+ uint32_t *buf, *out_buf;
+
+ val = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ buf_len = JS_ToUTF32String(ctx, &buf, val);
+ JS_FreeValue(ctx, val);
+ if (buf_len < 0)
+ return JS_EXCEPTION;
+
+ if (argc == 0 || JS_IsUndefined(argv[0])) {
+ n_type = UNICODE_NFC;
+ } else {
+ form = JS_ToCStringLen(ctx, &form_len, argv[0]);
+ if (!form)
+ goto fail1;
+ p = form;
+ if (p[0] != 'N' || p[1] != 'F')
+ goto bad_form;
+ p += 2;
+ is_compat = FALSE;
+ if (*p == 'K') {
+ is_compat = TRUE;
+ p++;
+ }
+ if (*p == 'C' || *p == 'D') {
+ n_type = UNICODE_NFC + is_compat * 2 + (*p - 'C');
+ if ((p + 1 - form) != form_len)
+ goto bad_form;
+ } else {
+ bad_form:
+ JS_FreeCString(ctx, form);
+ JS_ThrowRangeError(ctx, "bad normalization form");
+ fail1:
+ js_free(ctx, buf);
+ return JS_EXCEPTION;
+ }
+ JS_FreeCString(ctx, form);
+ }
+
+ out_len = unicode_normalize(&out_buf, buf, buf_len, n_type,
+ ctx->rt, (DynBufReallocFunc *)js_realloc_rt);
+ js_free(ctx, buf);
+ if (out_len < 0)
+ return JS_EXCEPTION;
+ val = JS_NewUTF32String(ctx, out_buf, out_len);
+ js_free(ctx, out_buf);
+ return val;
+}
+#endif /* CONFIG_ALL_UNICODE */
+
+/* also used for String.prototype.valueOf */
+static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisStringValue(ctx, this_val);
+}
+
+#if 0
+static JSValue js_string___toStringCheckObject(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToStringCheckObject(ctx, argv[0]);
+}
+
+static JSValue js_string___toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ToString(ctx, argv[0]);
+}
+
+static JSValue js_string___advanceStringIndex(JSContext *ctx, JSValueConst
+ this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ int idx;
+ BOOL is_unicode;
+ JSString *p;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+ if (JS_ToInt32Sat(ctx, &idx, argv[1])) {
+ JS_FreeValue(ctx, str);
+ return JS_EXCEPTION;
+ }
+ is_unicode = JS_ToBool(ctx, argv[2]);
+ p = JS_VALUE_GET_STRING(str);
+ if (!is_unicode || (unsigned)idx >= p->len || !p->is_wide_char) {
+ idx++;
+ } else {
+ string_getc(p, &idx);
+ }
+ JS_FreeValue(ctx, str);
+ return JS_NewInt32(ctx, idx);
+}
+#endif
+
+/* String Iterator */
+
+static JSValue js_string_iterator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic)
+{
+ JSArrayIteratorData *it;
+ uint32_t idx, c, start;
+ JSString *p;
+
+ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_STRING_ITERATOR);
+ if (!it) {
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+ }
+ if (JS_IsUndefined(it->obj))
+ goto done;
+ p = JS_VALUE_GET_STRING(it->obj);
+ idx = it->idx;
+ if (idx >= p->len) {
+ JS_FreeValue(ctx, it->obj);
+ it->obj = JS_UNDEFINED;
+ done:
+ *pdone = TRUE;
+ return JS_UNDEFINED;
+ }
+
+ start = idx;
+ c = string_getc(p, (int *)&idx);
+ it->idx = idx;
+ *pdone = FALSE;
+ if (c <= 0xffff) {
+ return js_new_string_char(ctx, c);
+ } else {
+ return js_new_string16(ctx, p->u.str16 + start, 2);
+ }
+}
+
+/* ES6 Annex B 2.3.2 etc. */
+enum {
+ magic_string_anchor,
+ magic_string_big,
+ magic_string_blink,
+ magic_string_bold,
+ magic_string_fixed,
+ magic_string_fontcolor,
+ magic_string_fontsize,
+ magic_string_italics,
+ magic_string_link,
+ magic_string_small,
+ magic_string_strike,
+ magic_string_sub,
+ magic_string_sup,
+};
+
+static JSValue js_string_CreateHTML(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue str;
+ const JSString *p;
+ StringBuffer b_s, *b = &b_s;
+ static struct { const char *tag, *attr; } const defs[] = {
+ { "a", "name" }, { "big", NULL }, { "blink", NULL }, { "b", NULL },
+ { "tt", NULL }, { "font", "color" }, { "font", "size" }, { "i", NULL },
+ { "a", "href" }, { "small", NULL }, { "strike", NULL },
+ { "sub", NULL }, { "sup", NULL },
+ };
+
+ str = JS_ToStringCheckObject(ctx, this_val);
+ if (JS_IsException(str))
+ return JS_EXCEPTION;
+ string_buffer_init(ctx, b, 7);
+ string_buffer_putc8(b, '<');
+ string_buffer_puts8(b, defs[magic].tag);
+ if (defs[magic].attr) {
+ // r += " " + attr + "=\"" + value + "\"";
+ JSValue value;
+ int i;
+
+ string_buffer_putc8(b, ' ');
+ string_buffer_puts8(b, defs[magic].attr);
+ string_buffer_puts8(b, "=\"");
+ value = JS_ToStringCheckObject(ctx, argv[0]);
+ if (JS_IsException(value)) {
+ JS_FreeValue(ctx, str);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+ }
+ p = JS_VALUE_GET_STRING(value);
+ for (i = 0; i < p->len; i++) {
+ int c = string_get(p, i);
+ if (c == '"') {
+ string_buffer_puts8(b, "&quot;");
+ } else {
+ string_buffer_putc16(b, c);
+ }
+ }
+ JS_FreeValue(ctx, value);
+ string_buffer_putc8(b, '\"');
+ }
+ // return r + ">" + str + "</" + tag + ">";
+ string_buffer_putc8(b, '>');
+ string_buffer_concat_value_free(b, str);
+ string_buffer_puts8(b, "</");
+ string_buffer_puts8(b, defs[magic].tag);
+ string_buffer_putc8(b, '>');
+ return string_buffer_end(b);
+}
+
+static const JSCFunctionListEntry js_string_funcs[] = {
+ JS_CFUNC_DEF("fromCharCode", 1, js_string_fromCharCode ),
+ JS_CFUNC_DEF("fromCodePoint", 1, js_string_fromCodePoint ),
+ JS_CFUNC_DEF("raw", 1, js_string_raw ),
+ //JS_CFUNC_DEF("__toString", 1, js_string___toString ),
+ //JS_CFUNC_DEF("__isSpace", 1, js_string___isSpace ),
+ //JS_CFUNC_DEF("__toStringCheckObject", 1, js_string___toStringCheckObject ),
+ //JS_CFUNC_DEF("__advanceStringIndex", 3, js_string___advanceStringIndex ),
+ //JS_CFUNC_DEF("__GetSubstitution", 6, js_string___GetSubstitution ),
+};
+
+static const JSCFunctionListEntry js_string_proto_funcs[] = {
+ JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ),
+ JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ),
+ JS_CFUNC_DEF("charAt", 1, js_string_charAt ),
+ JS_CFUNC_DEF("concat", 1, js_string_concat ),
+ JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ),
+ JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ),
+ JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ),
+ JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ),
+ JS_CFUNC_MAGIC_DEF("endsWith", 1, js_string_includes, 2 ),
+ JS_CFUNC_MAGIC_DEF("startsWith", 1, js_string_includes, 1 ),
+ JS_CFUNC_MAGIC_DEF("match", 1, js_string_match, JS_ATOM_Symbol_match ),
+ JS_CFUNC_MAGIC_DEF("matchAll", 1, js_string_match, JS_ATOM_Symbol_matchAll ),
+ JS_CFUNC_MAGIC_DEF("search", 1, js_string_match, JS_ATOM_Symbol_search ),
+ JS_CFUNC_DEF("split", 2, js_string_split ),
+ JS_CFUNC_DEF("substring", 2, js_string_substring ),
+ JS_CFUNC_DEF("substr", 2, js_string_substr ),
+ JS_CFUNC_DEF("slice", 2, js_string_slice ),
+ JS_CFUNC_DEF("repeat", 1, js_string_repeat ),
+ JS_CFUNC_MAGIC_DEF("replace", 2, js_string_replace, 0 ),
+ JS_CFUNC_MAGIC_DEF("replaceAll", 2, js_string_replace, 1 ),
+ JS_CFUNC_MAGIC_DEF("padEnd", 1, js_string_pad, 1 ),
+ JS_CFUNC_MAGIC_DEF("padStart", 1, js_string_pad, 0 ),
+ JS_CFUNC_MAGIC_DEF("trim", 0, js_string_trim, 3 ),
+ JS_CFUNC_MAGIC_DEF("trimEnd", 0, js_string_trim, 2 ),
+ JS_ALIAS_DEF("trimRight", "trimEnd" ),
+ JS_CFUNC_MAGIC_DEF("trimStart", 0, js_string_trim, 1 ),
+ JS_ALIAS_DEF("trimLeft", "trimStart" ),
+ JS_CFUNC_DEF("toString", 0, js_string_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_string_toString ),
+ JS_CFUNC_DEF("__quote", 1, js_string___quote ),
+ JS_CFUNC_DEF("localeCompare", 1, js_string_localeCompare ),
+ JS_CFUNC_MAGIC_DEF("toLowerCase", 0, js_string_toLowerCase, 1 ),
+ JS_CFUNC_MAGIC_DEF("toUpperCase", 0, js_string_toLowerCase, 0 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleLowerCase", 0, js_string_toLowerCase, 1 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleUpperCase", 0, js_string_toLowerCase, 0 ),
+ JS_CFUNC_MAGIC_DEF("[Symbol.iterator]", 0, js_create_array_iterator, JS_ITERATOR_KIND_VALUE | 4 ),
+ /* ES6 Annex B 2.3.2 etc. */
+ JS_CFUNC_MAGIC_DEF("anchor", 1, js_string_CreateHTML, magic_string_anchor ),
+ JS_CFUNC_MAGIC_DEF("big", 0, js_string_CreateHTML, magic_string_big ),
+ JS_CFUNC_MAGIC_DEF("blink", 0, js_string_CreateHTML, magic_string_blink ),
+ JS_CFUNC_MAGIC_DEF("bold", 0, js_string_CreateHTML, magic_string_bold ),
+ JS_CFUNC_MAGIC_DEF("fixed", 0, js_string_CreateHTML, magic_string_fixed ),
+ JS_CFUNC_MAGIC_DEF("fontcolor", 1, js_string_CreateHTML, magic_string_fontcolor ),
+ JS_CFUNC_MAGIC_DEF("fontsize", 1, js_string_CreateHTML, magic_string_fontsize ),
+ JS_CFUNC_MAGIC_DEF("italics", 0, js_string_CreateHTML, magic_string_italics ),
+ JS_CFUNC_MAGIC_DEF("link", 1, js_string_CreateHTML, magic_string_link ),
+ JS_CFUNC_MAGIC_DEF("small", 0, js_string_CreateHTML, magic_string_small ),
+ JS_CFUNC_MAGIC_DEF("strike", 0, js_string_CreateHTML, magic_string_strike ),
+ JS_CFUNC_MAGIC_DEF("sub", 0, js_string_CreateHTML, magic_string_sub ),
+ JS_CFUNC_MAGIC_DEF("sup", 0, js_string_CreateHTML, magic_string_sup ),
+};
+
+static const JSCFunctionListEntry js_string_iterator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 0, js_string_iterator_next, 0 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "String Iterator", JS_PROP_CONFIGURABLE ),
+};
+
+#ifdef CONFIG_ALL_UNICODE
+static const JSCFunctionListEntry js_string_proto_normalize[] = {
+ JS_CFUNC_DEF("normalize", 0, js_string_normalize ),
+};
+#endif
+
+void JS_AddIntrinsicStringNormalize(JSContext *ctx)
+{
+#ifdef CONFIG_ALL_UNICODE
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_normalize,
+ countof(js_string_proto_normalize));
+#endif
+}
+
+/* Math */
+
+/* precondition: a and b are not NaN */
+static double js_fmin(double a, double b)
+{
+ if (a == 0 && b == 0) {
+ JSFloat64Union a1, b1;
+ a1.d = a;
+ b1.d = b;
+ a1.u64 |= b1.u64;
+ return a1.d;
+ } else {
+ return fmin(a, b);
+ }
+}
+
+/* precondition: a and b are not NaN */
+static double js_fmax(double a, double b)
+{
+ if (a == 0 && b == 0) {
+ JSFloat64Union a1, b1;
+ a1.d = a;
+ b1.d = b;
+ a1.u64 &= b1.u64;
+ return a1.d;
+ } else {
+ return fmax(a, b);
+ }
+}
+
+static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ BOOL is_max = magic;
+ double r, a;
+ int i;
+ uint32_t tag;
+
+ if (unlikely(argc == 0)) {
+ return JS_NewFloat64Impl(ctx, is_max ? INFINITY : -INFINITY);
+ }
+
+ tag = JS_VALUE_GET_TAG(argv[0]);
+ if (tag == JS_TAG_INT) {
+ int a1, r1 = JS_VALUE_GET_INT(argv[0]);
+ for(i = 1; i < argc; i++) {
+ tag = JS_VALUE_GET_TAG(argv[i]);
+ if (tag != JS_TAG_INT) {
+ r = r1;
+ goto generic_case;
+ }
+ a1 = JS_VALUE_GET_INT(argv[i]);
+ if (is_max)
+ r1 = max_int(r1, a1);
+ else
+ r1 = min_int(r1, a1);
+
+ }
+ return JS_NewInt32(ctx, r1);
+ } else {
+ if (JS_ToFloat64(ctx, &r, argv[0]))
+ return JS_EXCEPTION;
+ i = 1;
+ generic_case:
+ while (i < argc) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isnan(r)) {
+ if (isnan(a)) {
+ r = a;
+ } else {
+ if (is_max)
+ r = js_fmax(r, a);
+ else
+ r = js_fmin(r, a);
+ }
+ }
+ i++;
+ }
+ return JS_NewFloat64(ctx, r);
+ }
+}
+
+static double js_math_sign(double a)
+{
+ if (isnan(a) || a == 0.0)
+ return a;
+ if (a < 0)
+ return -1;
+ else
+ return 1;
+}
+
+static double js_math_round(double a)
+{
+ JSFloat64Union u;
+ uint64_t frac_mask, one;
+ unsigned int e, s;
+
+ u.d = a;
+ e = (u.u64 >> 52) & 0x7ff;
+ if (e < 1023) {
+ /* abs(a) < 1 */
+ if (e == (1023 - 1) && u.u64 != 0xbfe0000000000000) {
+ /* abs(a) > 0.5 or a = 0.5: return +/-1.0 */
+ u.u64 = (u.u64 & ((uint64_t)1 << 63)) | ((uint64_t)1023 << 52);
+ } else {
+ /* return +/-0.0 */
+ u.u64 &= (uint64_t)1 << 63;
+ }
+ } else if (e < (1023 + 52)) {
+ s = u.u64 >> 63;
+ one = (uint64_t)1 << (52 - (e - 1023));
+ frac_mask = one - 1;
+ u.u64 += (one >> 1) - s;
+ u.u64 &= ~frac_mask; /* truncate to an integer */
+ }
+ /* otherwise: abs(a) >= 2^52, or NaN, +/-Infinity: no change */
+ return u.d;
+}
+
+static JSValue js_math_hypot(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ double r, a;
+ int i;
+
+ r = 0;
+ if (argc > 0) {
+ if (JS_ToFloat64(ctx, &r, argv[0]))
+ return JS_EXCEPTION;
+ if (argc == 1) {
+ r = fabs(r);
+ } else {
+ /* use the built-in function to minimize precision loss */
+ for (i = 1; i < argc; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ r = hypot(r, a);
+ }
+ }
+ }
+ return JS_NewFloat64(ctx, r);
+}
+
+static double js_math_fround(double a)
+{
+ return (float)a;
+}
+
+static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int a, b;
+
+ if (JS_ToInt32(ctx, &a, argv[0]))
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &b, argv[1]))
+ return JS_EXCEPTION;
+ /* purposely ignoring overflow */
+ return JS_NewInt32(ctx, a * b);
+}
+
+static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ uint32_t a, r;
+
+ if (JS_ToUint32(ctx, &a, argv[0]))
+ return JS_EXCEPTION;
+ if (a == 0)
+ r = 32;
+ else
+ r = clz32(a);
+ return JS_NewInt32(ctx, r);
+}
+
+/* xorshift* random number generator by Marsaglia */
+static uint64_t xorshift64star(uint64_t *pstate)
+{
+ uint64_t x;
+ x = *pstate;
+ x ^= x >> 12;
+ x ^= x << 25;
+ x ^= x >> 27;
+ *pstate = x;
+ return x * 0x2545F4914F6CDD1D;
+}
+
+static void js_random_init(JSContext *ctx)
+{
+#ifdef _MSC_VER
+ ctx->random_state = time(NULL);
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
+#endif
+ /* the state must be non zero */
+ if (ctx->random_state == 0)
+ ctx->random_state = 1;
+}
+
+static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSFloat64Union u;
+ uint64_t v;
+
+ v = xorshift64star(&ctx->random_state);
+ /* 1.0 <= u.d < 2 */
+ u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);
+ return JS_NewFloat64Impl(ctx, u.d - 1.0);
+}
+
+// MSVC inexplicably refuses to initialize the array below with
+// these functions, so use wrappers.
+static double floorWrapper(double x) { return floor(x); }
+static double ceilWrapper(double x) { return ceil(x); }
+static double log2Wrapper(double x) { return log2(x); }
+
+static const JSCFunctionListEntry js_math_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
+ JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
+ JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
+ JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floorWrapper ),
+ JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceilWrapper ),
+ JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
+ JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),
+
+ JS_CFUNC_SPECIAL_DEF("acos", 1, f_f, acos ),
+ JS_CFUNC_SPECIAL_DEF("asin", 1, f_f, asin ),
+ JS_CFUNC_SPECIAL_DEF("atan", 1, f_f, atan ),
+ JS_CFUNC_SPECIAL_DEF("atan2", 2, f_f_f, atan2 ),
+ JS_CFUNC_SPECIAL_DEF("cos", 1, f_f, cos ),
+ JS_CFUNC_SPECIAL_DEF("exp", 1, f_f, exp ),
+ JS_CFUNC_SPECIAL_DEF("log", 1, f_f, log ),
+ JS_CFUNC_SPECIAL_DEF("pow", 2, f_f_f, js_pow ),
+ JS_CFUNC_SPECIAL_DEF("sin", 1, f_f, sin ),
+ JS_CFUNC_SPECIAL_DEF("tan", 1, f_f, tan ),
+ /* ES6 */
+ JS_CFUNC_SPECIAL_DEF("trunc", 1, f_f, trunc ),
+ JS_CFUNC_SPECIAL_DEF("sign", 1, f_f, js_math_sign ),
+ JS_CFUNC_SPECIAL_DEF("cosh", 1, f_f, cosh ),
+ JS_CFUNC_SPECIAL_DEF("sinh", 1, f_f, sinh ),
+ JS_CFUNC_SPECIAL_DEF("tanh", 1, f_f, tanh ),
+ JS_CFUNC_SPECIAL_DEF("acosh", 1, f_f, acosh ),
+ JS_CFUNC_SPECIAL_DEF("asinh", 1, f_f, asinh ),
+ JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),
+ JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),
+ JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),
+ JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2Wrapper ),
+ JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),
+ JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
+ JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
+ JS_CFUNC_DEF("random", 0, js_math_random ),
+ JS_CFUNC_SPECIAL_DEF("fround", 1, f_f, js_math_fround ),
+ JS_CFUNC_DEF("imul", 2, js_math_imul ),
+ JS_CFUNC_DEF("clz32", 1, js_math_clz32 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Math", JS_PROP_CONFIGURABLE ),
+ JS_PROP_DOUBLE_DEF("E", 2.718281828459045, 0 ),
+ JS_PROP_DOUBLE_DEF("LN10", 2.302585092994046, 0 ),
+ JS_PROP_DOUBLE_DEF("LN2", 0.6931471805599453, 0 ),
+ JS_PROP_DOUBLE_DEF("LOG2E", 1.4426950408889634, 0 ),
+ JS_PROP_DOUBLE_DEF("LOG10E", 0.4342944819032518, 0 ),
+ JS_PROP_DOUBLE_DEF("PI", 3.141592653589793, 0 ),
+ JS_PROP_DOUBLE_DEF("SQRT1_2", 0.7071067811865476, 0 ),
+ JS_PROP_DOUBLE_DEF("SQRT2", 1.4142135623730951, 0 ),
+};
+
+static const JSCFunctionListEntry js_math_obj[] = {
+ JS_OBJECT_DEF("Math", js_math_funcs, countof(js_math_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+};
+
+/* Date */
+
+#if 0
+/* OS dependent: return the UTC time in ms since 1970. */
+static JSValue js___date_now(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int64_t d;
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
+ return JS_NewInt64(ctx, d);
+}
+#endif
+
+/* OS dependent: return the UTC time in microseconds since 1970. */
+// FIXME: Unused, remove?
+static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int64_t d;
+#ifdef _MSC_VER
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, (FILETIME *) &d);
+ d /= 10;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+#endif
+ return JS_NewInt64(ctx, d);
+}
+
+/* OS dependent. d = argv[0] is in ms from 1970. Return the difference
+ between UTC time and local time 'd' in minutes */
+static int getTimezoneOffset(int64_t time) {
+#if defined(_WIN32)
+ /* XXX: TODO */
+ return 0;
+#else
+ time_t ti;
+ struct tm tm;
+
+ time /= 1000; /* convert to seconds */
+ if (sizeof(time_t) == 4) {
+ /* on 32-bit systems, we need to clamp the time value to the
+ range of `time_t`. This is better than truncating values to
+ 32 bits and hopefully provides the same result as 64-bit
+ implementation of localtime_r.
+ */
+ if ((time_t)-1 < 0) {
+ if (time < INT32_MIN) {
+ time = INT32_MIN;
+ } else if (time > INT32_MAX) {
+ time = INT32_MAX;
+ }
+ } else {
+ if (time < 0) {
+ time = 0;
+ } else if (time > UINT32_MAX) {
+ time = UINT32_MAX;
+ }
+ }
+ }
+ ti = time;
+ localtime_r(&ti, &tm);
+ return -tm.tm_gmtoff / 60;
+#endif
+}
+
+#if 0
+static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ double dd;
+
+ if (JS_ToFloat64(ctx, &dd, argv[0]))
+ return JS_EXCEPTION;
+ if (isnan(dd))
+ return __JS_NewFloat64(ctx, dd);
+ else
+ return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd));
+}
+
+static JSValue js_get_prototype_from_ctor(JSContext *ctx, JSValueConst ctor,
+ JSValueConst def_proto)
+{
+ JSValue proto;
+ proto = JS_GetProperty(ctx, ctor, JS_ATOM_prototype);
+ if (JS_IsException(proto))
+ return proto;
+ if (!JS_IsObject(proto)) {
+ JS_FreeValue(ctx, proto);
+ proto = JS_DupValue(ctx, def_proto);
+ }
+ return proto;
+}
+
+/* create a new date object */
+static JSValue js___date_create(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, proto;
+ proto = js_get_prototype_from_ctor(ctx, argv[0], argv[1]);
+ if (JS_IsException(proto))
+ return proto;
+ obj = JS_NewObjectProtoClass(ctx, proto, JS_CLASS_DATE);
+ JS_FreeValue(ctx, proto);
+ if (!JS_IsException(obj))
+ JS_SetObjectData(ctx, obj, JS_DupValue(ctx, argv[2]));
+ return obj;
+}
+#endif
+
+/* RegExp */
+
+static void js_regexp_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSRegExp *re = &p->u.regexp;
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->bytecode));
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_STRING, re->pattern));
+}
+
+/* create a string containing the RegExp bytecode */
+static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern,
+ JSValueConst flags)
+{
+ const char *str;
+ int re_flags, mask;
+ uint8_t *re_bytecode_buf;
+ size_t i, len;
+ int re_bytecode_len;
+ JSValue ret;
+ char error_msg[64];
+
+ re_flags = 0;
+ if (!JS_IsUndefined(flags)) {
+ str = JS_ToCStringLen(ctx, &len, flags);
+ if (!str)
+ return JS_EXCEPTION;
+ /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */
+ for (i = 0; i < len; i++) {
+ switch(str[i]) {
+ case 'g':
+ mask = LRE_FLAG_GLOBAL;
+ break;
+ case 'i':
+ mask = LRE_FLAG_IGNORECASE;
+ break;
+ case 'm':
+ mask = LRE_FLAG_MULTILINE;
+ break;
+ case 's':
+ mask = LRE_FLAG_DOTALL;
+ break;
+ case 'u':
+ mask = LRE_FLAG_UTF16;
+ break;
+ case 'y':
+ mask = LRE_FLAG_STICKY;
+ break;
+ default:
+ goto bad_flags;
+ }
+ if ((re_flags & mask) != 0) {
+ bad_flags:
+ JS_FreeCString(ctx, str);
+ return JS_ThrowSyntaxError(ctx, "invalid regular expression flags");
+ }
+ re_flags |= mask;
+ }
+ JS_FreeCString(ctx, str);
+ }
+
+ str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16));
+ if (!str)
+ return JS_EXCEPTION;
+ re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg,
+ sizeof(error_msg), str, len, re_flags, ctx);
+ JS_FreeCString(ctx, str);
+ if (!re_bytecode_buf) {
+ JS_ThrowSyntaxError(ctx, "%s", error_msg);
+ return JS_EXCEPTION;
+ }
+
+ ret = js_new_string8(ctx, re_bytecode_buf, re_bytecode_len);
+ js_free(ctx, re_bytecode_buf);
+ return ret;
+}
+
+/* create a RegExp object from a string containing the RegExp bytecode
+ and the source pattern */
+static JSValue js_regexp_constructor_internal(JSContext *ctx, JSValueConst ctor,
+ JSValue pattern, JSValue bc)
+{
+ JSValue obj;
+ JSObject *p;
+ JSRegExp *re;
+
+ /* sanity check */
+ if (JS_VALUE_GET_TAG(bc) != JS_TAG_STRING ||
+ JS_VALUE_GET_TAG(pattern) != JS_TAG_STRING) {
+ JS_ThrowTypeError(ctx, "string expected");
+ fail:
+ JS_FreeValue(ctx, bc);
+ JS_FreeValue(ctx, pattern);
+ return JS_EXCEPTION;
+ }
+
+ obj = js_create_from_ctor(ctx, ctor, JS_CLASS_REGEXP);
+ if (JS_IsException(obj))
+ goto fail;
+ p = JS_VALUE_GET_OBJ(obj);
+ re = &p->u.regexp;
+ re->pattern = JS_VALUE_GET_STRING(pattern);
+ re->bytecode = JS_VALUE_GET_STRING(bc);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0),
+ JS_PROP_WRITABLE);
+ return obj;
+}
+
+static JSRegExp *js_get_regexp(JSContext *ctx, JSValueConst obj, BOOL throw_error)
+{
+ if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id == JS_CLASS_REGEXP)
+ return &p->u.regexp;
+ }
+ if (throw_error) {
+ JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
+ }
+ return NULL;
+}
+
+/* return < 0 if exception or TRUE/FALSE */
+static int js_is_regexp(JSContext *ctx, JSValueConst obj)
+{
+ JSValue m;
+
+ if (!JS_IsObject(obj))
+ return FALSE;
+ m = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_match);
+ if (JS_IsException(m))
+ return -1;
+ if (!JS_IsUndefined(m))
+ return JS_ToBoolFree(ctx, m);
+ return js_get_regexp(ctx, obj, FALSE) != NULL;
+}
+
+static JSValue js_regexp_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue pattern, flags, bc, val;
+ JSValueConst pat, flags1;
+ JSRegExp *re;
+ int pat_is_regexp;
+
+ pat = argv[0];
+ flags1 = argv[1];
+ pat_is_regexp = js_is_regexp(ctx, pat);
+ if (pat_is_regexp < 0)
+ return JS_EXCEPTION;
+ if (JS_IsUndefined(new_target)) {
+ /* called as a function */
+ new_target = JS_GetActiveFunction(ctx);
+ if (pat_is_regexp && JS_IsUndefined(flags1)) {
+ JSValue ctor;
+ BOOL res;
+ ctor = JS_GetProperty(ctx, pat, JS_ATOM_constructor);
+ if (JS_IsException(ctor))
+ return ctor;
+ res = js_same_value(ctx, ctor, new_target);
+ JS_FreeValue(ctx, ctor);
+ if (res)
+ return JS_DupValue(ctx, pat);
+ }
+ }
+ re = js_get_regexp(ctx, pat, FALSE);
+ if (re) {
+ pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
+ if (JS_IsUndefined(flags1)) {
+ bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
+ goto no_compilation;
+ } else {
+ flags = JS_ToString(ctx, flags1);
+ if (JS_IsException(flags))
+ goto fail;
+ }
+ } else {
+ flags = JS_UNDEFINED;
+ if (pat_is_regexp) {
+ pattern = JS_GetProperty(ctx, pat, JS_ATOM_source);
+ if (JS_IsException(pattern))
+ goto fail;
+ if (JS_IsUndefined(flags1)) {
+ flags = JS_GetProperty(ctx, pat, JS_ATOM_flags);
+ if (JS_IsException(flags))
+ goto fail;
+ } else {
+ flags = JS_DupValue(ctx, flags1);
+ }
+ } else {
+ pattern = JS_DupValue(ctx, pat);
+ flags = JS_DupValue(ctx, flags1);
+ }
+ if (JS_IsUndefined(pattern)) {
+ pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ } else {
+ val = pattern;
+ pattern = JS_ToString(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (JS_IsException(pattern))
+ goto fail;
+ }
+ }
+ bc = js_compile_regexp(ctx, pattern, flags);
+ if (JS_IsException(bc))
+ goto fail;
+ JS_FreeValue(ctx, flags);
+ no_compilation:
+ return js_regexp_constructor_internal(ctx, new_target, pattern, bc);
+ fail:
+ JS_FreeValue(ctx, pattern);
+ JS_FreeValue(ctx, flags);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_regexp_compile(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSRegExp *re1, *re;
+ JSValueConst pattern1, flags1;
+ JSValue bc, pattern;
+
+ re = js_get_regexp(ctx, this_val, TRUE);
+ if (!re)
+ return JS_EXCEPTION;
+ pattern1 = argv[0];
+ flags1 = argv[1];
+ re1 = js_get_regexp(ctx, pattern1, FALSE);
+ if (re1) {
+ if (!JS_IsUndefined(flags1))
+ return JS_ThrowTypeError(ctx, "flags must be undefined");
+ pattern = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->pattern));
+ bc = JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re1->bytecode));
+ } else {
+ bc = JS_UNDEFINED;
+ if (JS_IsUndefined(pattern1))
+ pattern = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ else
+ pattern = JS_ToString(ctx, pattern1);
+ if (JS_IsException(pattern))
+ goto fail;
+ bc = js_compile_regexp(ctx, pattern, flags1);
+ if (JS_IsException(bc))
+ goto fail;
+ }
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, re->bytecode));
+ re->pattern = JS_VALUE_GET_STRING(pattern);
+ re->bytecode = JS_VALUE_GET_STRING(bc);
+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
+ JS_NewInt32(ctx, 0)) < 0)
+ return JS_EXCEPTION;
+ return JS_DupValue(ctx, this_val);
+ fail:
+ JS_FreeValue(ctx, pattern);
+ JS_FreeValue(ctx, bc);
+ return JS_EXCEPTION;
+}
+
+#if 0
+static JSValue js_regexp_get___source(JSContext *ctx, JSValueConst this_val)
+{
+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
+ if (!re)
+ return JS_EXCEPTION;
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, re->pattern));
+}
+
+static JSValue js_regexp_get___flags(JSContext *ctx, JSValueConst this_val)
+{
+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
+ int flags;
+
+ if (!re)
+ return JS_EXCEPTION;
+ flags = lre_get_flags(re->bytecode->u.str8);
+ return JS_NewInt32(ctx, flags);
+}
+#endif
+
+static JSValue js_regexp_get_source(JSContext *ctx, JSValueConst this_val)
+{
+ JSRegExp *re;
+ JSString *p;
+ StringBuffer b_s, *b = &b_s;
+ int i, n, c, c2, bra;
+
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
+ goto empty_regex;
+
+ re = js_get_regexp(ctx, this_val, TRUE);
+ if (!re)
+ return JS_EXCEPTION;
+
+ p = re->pattern;
+
+ if (p->len == 0) {
+ empty_regex:
+ return JS_NewString(ctx, "(?:)");
+ }
+ string_buffer_init2(ctx, b, p->len, p->is_wide_char);
+
+ /* Escape '/' and newline sequences as needed */
+ bra = 0;
+ for (i = 0, n = p->len; i < n;) {
+ c2 = -1;
+ switch (c = string_get(p, i++)) {
+ case '\\':
+ if (i < n)
+ c2 = string_get(p, i++);
+ break;
+ case ']':
+ bra = 0;
+ break;
+ case '[':
+ if (!bra) {
+ if (i < n && string_get(p, i) == ']')
+ c2 = string_get(p, i++);
+ bra = 1;
+ }
+ break;
+ case '\n':
+ c = '\\';
+ c2 = 'n';
+ break;
+ case '\r':
+ c = '\\';
+ c2 = 'r';
+ break;
+ case '/':
+ if (!bra) {
+ c = '\\';
+ c2 = '/';
+ }
+ break;
+ }
+ string_buffer_putc16(b, c);
+ if (c2 >= 0)
+ string_buffer_putc16(b, c2);
+ }
+ return string_buffer_end(b);
+}
+
+static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mask)
+{
+ JSRegExp *re;
+ int flags;
+
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ re = js_get_regexp(ctx, this_val, FALSE);
+ if (!re) {
+ if (js_same_value(ctx, this_val, ctx->class_proto[JS_CLASS_REGEXP]))
+ return JS_UNDEFINED;
+ else
+ return JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_REGEXP);
+ }
+
+ flags = lre_get_flags(re->bytecode->u.str8);
+ return JS_NewBool(ctx, (flags & mask) != 0);
+}
+
+static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val)
+{
+ char str[8], *p = str;
+ int res;
+
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'g';
+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "ignoreCase"));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'i';
+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "multiline"));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'm';
+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "dotAll"));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 's';
+ res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_unicode));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'u';
+ res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "sticky"));
+ if (res < 0)
+ goto exception;
+ if (res)
+ *p++ = 'y';
+ return JS_NewStringLen(ctx, str, p - str);
+
+exception:
+ return JS_EXCEPTION;
+}
+
+static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue pattern, flags;
+ StringBuffer b_s, *b = &b_s;
+
+ if (!JS_IsObject(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ string_buffer_init(ctx, b, 0);
+ string_buffer_putc8(b, '/');
+ pattern = JS_GetProperty(ctx, this_val, JS_ATOM_source);
+ if (string_buffer_concat_value_free(b, pattern))
+ goto fail;
+ string_buffer_putc8(b, '/');
+ flags = JS_GetProperty(ctx, this_val, JS_ATOM_flags);
+ if (string_buffer_concat_value_free(b, flags))
+ goto fail;
+ return string_buffer_end(b);
+
+fail:
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size)
+{
+ JSContext *ctx = opaque;
+ return js_check_stack_overflow(ctx->rt, alloca_size);
+}
+
+void *lre_realloc(void *opaque, void *ptr, size_t size)
+{
+ JSContext *ctx = opaque;
+ /* No JS exception is raised here */
+ return js_realloc_rt(ctx->rt, ptr, size);
+}
+
+static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
+ JSString *str;
+ JSValue str_val, obj, val, groups = JS_UNDEFINED;
+ uint8_t *re_bytecode;
+ int ret;
+ uint8_t **capture, *str_buf;
+ int capture_count, shift, i, re_flags;
+ int64_t last_index;
+ const char *group_name_ptr;
+
+ if (!re)
+ return JS_EXCEPTION;
+ str_val = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str_val))
+ return str_val;
+ val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
+ if (JS_IsException(val) ||
+ JS_ToLengthFree(ctx, &last_index, val)) {
+ JS_FreeValue(ctx, str_val);
+ return JS_EXCEPTION;
+ }
+ re_bytecode = re->bytecode->u.str8;
+ re_flags = lre_get_flags(re_bytecode);
+ if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
+ last_index = 0;
+ }
+ str = JS_VALUE_GET_STRING(str_val);
+ capture_count = lre_get_capture_count(re_bytecode);
+ capture = NULL;
+ if (capture_count > 0) {
+ capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
+ if (!capture) {
+ JS_FreeValue(ctx, str_val);
+ return JS_EXCEPTION;
+ }
+ }
+ shift = str->is_wide_char;
+ str_buf = str->u.str8;
+ if (last_index > str->len) {
+ ret = 2;
+ } else {
+ ret = lre_exec(capture, re_bytecode,
+ str_buf, last_index, str->len,
+ shift, ctx);
+ }
+ obj = JS_NULL;
+ if (ret != 1) {
+ if (ret >= 0) {
+ if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
+ JS_NewInt32(ctx, 0)) < 0)
+ goto fail;
+ }
+ } else {
+ JS_ThrowInternalError(ctx, "out of memory in regexp execution");
+ goto fail;
+ }
+ JS_FreeValue(ctx, str_val);
+ } else {
+ int prop_flags;
+ if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) {
+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
+ JS_NewInt32(ctx, (capture[1] - str_buf) >> shift)) < 0)
+ goto fail;
+ }
+ obj = JS_NewArray(ctx);
+ if (JS_IsException(obj))
+ goto fail;
+ prop_flags = JS_PROP_C_W_E | JS_PROP_THROW;
+ group_name_ptr = lre_get_groupnames(re_bytecode);
+ if (group_name_ptr) {
+ groups = JS_NewObjectProto(ctx, JS_NULL);
+ if (JS_IsException(groups))
+ goto fail;
+ }
+
+ for(i = 0; i < capture_count; i++) {
+ int start, end;
+ JSValue val;
+ if (capture[2 * i] == NULL ||
+ capture[2 * i + 1] == NULL) {
+ val = JS_UNDEFINED;
+ } else {
+ start = (capture[2 * i] - str_buf) >> shift;
+ end = (capture[2 * i + 1] - str_buf) >> shift;
+ val = js_sub_string(ctx, str, start, end);
+ if (JS_IsException(val))
+ goto fail;
+ }
+ if (group_name_ptr && i > 0) {
+ if (*group_name_ptr) {
+ if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr,
+ JS_DupValue(ctx, val),
+ prop_flags) < 0) {
+ JS_FreeValue(ctx, val);
+ goto fail;
+ }
+ }
+ group_name_ptr += strlen(group_name_ptr) + 1;
+ }
+ if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0)
+ goto fail;
+ }
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups,
+ groups, prop_flags) < 0)
+ goto fail;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index,
+ JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0)
+ goto fail;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0)
+ goto fail1;
+ }
+ js_free(ctx, capture);
+ return obj;
+fail:
+ JS_FreeValue(ctx, groups);
+ JS_FreeValue(ctx, str_val);
+fail1:
+ JS_FreeValue(ctx, obj);
+ js_free(ctx, capture);
+ return JS_EXCEPTION;
+}
+
+/* delete portions of a string that match a given regex */
+static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueConst arg)
+{
+ JSRegExp *re = js_get_regexp(ctx, this_val, TRUE);
+ JSString *str;
+ JSValue str_val, val;
+ uint8_t *re_bytecode;
+ int ret;
+ uint8_t **capture, *str_buf;
+ int capture_count, shift, re_flags;
+ int next_src_pos, start, end;
+ int64_t last_index;
+ StringBuffer b_s, *b = &b_s;
+
+ if (!re)
+ return JS_EXCEPTION;
+
+ string_buffer_init(ctx, b, 0);
+
+ capture = NULL;
+ str_val = JS_ToString(ctx, arg);
+ if (JS_IsException(str_val))
+ goto fail;
+ str = JS_VALUE_GET_STRING(str_val);
+ re_bytecode = re->bytecode->u.str8;
+ re_flags = lre_get_flags(re_bytecode);
+ if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) {
+ last_index = 0;
+ } else {
+ val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex);
+ if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val))
+ goto fail;
+ }
+ capture_count = lre_get_capture_count(re_bytecode);
+ if (capture_count > 0) {
+ capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2);
+ if (!capture)
+ goto fail;
+ }
+ shift = str->is_wide_char;
+ str_buf = str->u.str8;
+ next_src_pos = 0;
+ for (;;) {
+ if (last_index > str->len)
+ break;
+
+ ret = lre_exec(capture, re_bytecode,
+ str_buf, last_index, str->len, shift, ctx);
+ if (ret != 1) {
+ if (ret >= 0) {
+ if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) {
+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
+ JS_NewInt32(ctx, 0)) < 0)
+ goto fail;
+ }
+ } else {
+ JS_ThrowInternalError(ctx, "out of memory in regexp execution");
+ goto fail;
+ }
+ break;
+ }
+ start = (capture[0] - str_buf) >> shift;
+ end = (capture[1] - str_buf) >> shift;
+ last_index = end;
+ if (next_src_pos < start) {
+ if (string_buffer_concat(b, str, next_src_pos, start))
+ goto fail;
+ }
+ next_src_pos = end;
+ if (!(re_flags & LRE_FLAG_GLOBAL)) {
+ if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex,
+ JS_NewInt32(ctx, end)) < 0)
+ goto fail;
+ break;
+ }
+ if (end == start) {
+ if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) {
+ end++;
+ } else {
+ string_getc(str, &end);
+ }
+ }
+ last_index = end;
+ }
+ if (string_buffer_concat(b, str, next_src_pos, str->len))
+ goto fail;
+ JS_FreeValue(ctx, str_val);
+ js_free(ctx, capture);
+ return string_buffer_end(b);
+fail:
+ JS_FreeValue(ctx, str_val);
+ js_free(ctx, capture);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue JS_RegExpExec(JSContext *ctx, JSValueConst r, JSValueConst s)
+{
+ JSValue method, ret;
+
+ method = JS_GetProperty(ctx, r, JS_ATOM_exec);
+ if (JS_IsException(method))
+ return method;
+ if (JS_IsFunction(ctx, method)) {
+ ret = JS_CallFree(ctx, method, r, 1, &s);
+ if (JS_IsException(ret))
+ return ret;
+ if (!JS_IsObject(ret) && !JS_IsNull(ret)) {
+ JS_FreeValue(ctx, ret);
+ return JS_ThrowTypeError(ctx, "RegExp exec method must return an object or null");
+ }
+ return ret;
+ }
+ JS_FreeValue(ctx, method);
+ return js_regexp_exec(ctx, r, 1, &s);
+}
+
+#if 0
+static JSValue js_regexp___RegExpExec(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_RegExpExec(ctx, argv[0], argv[1]);
+}
+static JSValue js_regexp___RegExpDelete(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_RegExpDelete(ctx, argv[0], argv[1]);
+}
+#endif
+
+static JSValue js_regexp_test(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ BOOL ret;
+
+ val = JS_RegExpExec(ctx, this_val, argv[0]);
+ if (JS_IsException(val))
+ return JS_EXCEPTION;
+ ret = !JS_IsNull(val);
+ JS_FreeValue(ctx, val);
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // [Symbol.match](str)
+ JSValueConst rx = this_val;
+ JSValue A, S, result, matchStr;
+ int global, n, fullUnicode, isEmpty;
+ JSString *p;
+
+ if (!JS_IsObject(rx))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ A = JS_UNDEFINED;
+ result = JS_UNDEFINED;
+ matchStr = JS_UNDEFINED;
+ S = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(S))
+ goto exception;
+
+ global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));
+ if (global < 0)
+ goto exception;
+
+ if (!global) {
+ A = JS_RegExpExec(ctx, rx, S);
+ } else {
+ fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
+ if (fullUnicode < 0)
+ goto exception;
+
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
+ goto exception;
+ A = JS_NewArray(ctx);
+ if (JS_IsException(A))
+ goto exception;
+ n = 0;
+ for(;;) {
+ JS_FreeValue(ctx, result);
+ result = JS_RegExpExec(ctx, rx, S);
+ if (JS_IsException(result))
+ goto exception;
+ if (JS_IsNull(result))
+ break;
+ matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
+ if (JS_IsException(matchStr))
+ goto exception;
+ isEmpty = JS_IsEmptyString(matchStr);
+ if (JS_SetPropertyInt64(ctx, A, n++, matchStr) < 0)
+ goto exception;
+ if (isEmpty) {
+ int64_t thisIndex, nextIndex;
+ if (JS_ToLengthFree(ctx, &thisIndex,
+ JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
+ goto exception;
+ p = JS_VALUE_GET_STRING(S);
+ nextIndex = string_advance_index(p, thisIndex, fullUnicode);
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
+ goto exception;
+ }
+ }
+ if (n == 0) {
+ JS_FreeValue(ctx, A);
+ A = JS_NULL;
+ }
+ }
+ JS_FreeValue(ctx, result);
+ JS_FreeValue(ctx, S);
+ return A;
+
+exception:
+ JS_FreeValue(ctx, A);
+ JS_FreeValue(ctx, result);
+ JS_FreeValue(ctx, S);
+ return JS_EXCEPTION;
+}
+
+typedef struct JSRegExpStringIteratorData {
+ JSValue iterating_regexp;
+ JSValue iterated_string;
+ BOOL global;
+ BOOL unicode;
+ BOOL done;
+} JSRegExpStringIteratorData;
+
+static void js_regexp_string_iterator_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
+ if (it) {
+ JS_FreeValueRT(rt, it->iterating_regexp);
+ JS_FreeValueRT(rt, it->iterated_string);
+ js_free_rt(rt, it);
+ }
+}
+
+static void js_regexp_string_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSRegExpStringIteratorData *it = p->u.regexp_string_iterator_data;
+ if (it) {
+ JS_MarkValue(rt, it->iterating_regexp, mark_func);
+ JS_MarkValue(rt, it->iterated_string, mark_func);
+ }
+}
+
+static JSValue js_regexp_string_iterator_next(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic)
+{
+ JSRegExpStringIteratorData *it;
+ JSValueConst R, S;
+ JSValue matchStr = JS_UNDEFINED, match = JS_UNDEFINED;
+ JSString *sp;
+
+ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_REGEXP_STRING_ITERATOR);
+ if (!it)
+ goto exception;
+ if (it->done) {
+ *pdone = TRUE;
+ return JS_UNDEFINED;
+ }
+ R = it->iterating_regexp;
+ S = it->iterated_string;
+ match = JS_RegExpExec(ctx, R, S);
+ if (JS_IsException(match))
+ goto exception;
+ if (JS_IsNull(match)) {
+ it->done = TRUE;
+ *pdone = TRUE;
+ return JS_UNDEFINED;
+ } else if (it->global) {
+ matchStr = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, match, 0));
+ if (JS_IsException(matchStr))
+ goto exception;
+ if (JS_IsEmptyString(matchStr)) {
+ int64_t thisIndex, nextIndex;
+ if (JS_ToLengthFree(ctx, &thisIndex,
+ JS_GetProperty(ctx, R, JS_ATOM_lastIndex)) < 0)
+ goto exception;
+ sp = JS_VALUE_GET_STRING(S);
+ nextIndex = string_advance_index(sp, thisIndex, it->unicode);
+ if (JS_SetProperty(ctx, R, JS_ATOM_lastIndex,
+ JS_NewInt64(ctx, nextIndex)) < 0)
+ goto exception;
+ }
+ JS_FreeValue(ctx, matchStr);
+ } else {
+ it->done = TRUE;
+ }
+ *pdone = FALSE;
+ return match;
+ exception:
+ JS_FreeValue(ctx, match);
+ JS_FreeValue(ctx, matchStr);
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+}
+
+static JSValue js_regexp_Symbol_matchAll(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // [Symbol.matchAll](str)
+ JSValueConst R = this_val;
+ JSValue S, C, flags, matcher, iter;
+ JSValueConst args[2];
+ JSString *strp;
+ int64_t lastIndex;
+ JSRegExpStringIteratorData *it;
+
+ if (!JS_IsObject(R))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ C = JS_UNDEFINED;
+ flags = JS_UNDEFINED;
+ matcher = JS_UNDEFINED;
+ iter = JS_UNDEFINED;
+
+ S = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(S))
+ goto exception;
+ C = JS_SpeciesConstructor(ctx, R, ctx->regexp_ctor);
+ if (JS_IsException(C))
+ goto exception;
+ flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, R, JS_ATOM_flags));
+ if (JS_IsException(flags))
+ goto exception;
+ args[0] = R;
+ args[1] = flags;
+ matcher = JS_CallConstructor(ctx, C, 2, args);
+ if (JS_IsException(matcher))
+ goto exception;
+ if (JS_ToLengthFree(ctx, &lastIndex,
+ JS_GetProperty(ctx, R, JS_ATOM_lastIndex)))
+ goto exception;
+ if (JS_SetProperty(ctx, matcher, JS_ATOM_lastIndex,
+ JS_NewInt64(ctx, lastIndex)) < 0)
+ goto exception;
+
+ iter = JS_NewObjectClass(ctx, JS_CLASS_REGEXP_STRING_ITERATOR);
+ if (JS_IsException(iter))
+ goto exception;
+ it = js_malloc(ctx, sizeof(*it));
+ if (!it)
+ goto exception;
+ it->iterating_regexp = matcher;
+ it->iterated_string = S;
+ strp = JS_VALUE_GET_STRING(flags);
+ it->global = string_indexof_char(strp, 'g', 0) >= 0;
+ it->unicode = string_indexof_char(strp, 'u', 0) >= 0;
+ it->done = FALSE;
+ JS_SetOpaque(iter, it);
+
+ JS_FreeValue(ctx, C);
+ JS_FreeValue(ctx, flags);
+ return iter;
+ exception:
+ JS_FreeValue(ctx, S);
+ JS_FreeValue(ctx, C);
+ JS_FreeValue(ctx, flags);
+ JS_FreeValue(ctx, matcher);
+ JS_FreeValue(ctx, iter);
+ return JS_EXCEPTION;
+}
+
+typedef struct ValueBuffer {
+ JSContext *ctx;
+ JSValue *arr;
+ JSValue def[4];
+ int len;
+ int size;
+ int error_status;
+} ValueBuffer;
+
+static int value_buffer_init(JSContext *ctx, ValueBuffer *b)
+{
+ b->ctx = ctx;
+ b->len = 0;
+ b->size = 4;
+ b->error_status = 0;
+ b->arr = b->def;
+ return 0;
+}
+
+static void value_buffer_free(ValueBuffer *b)
+{
+ while (b->len > 0)
+ JS_FreeValue(b->ctx, b->arr[--b->len]);
+ if (b->arr != b->def)
+ js_free(b->ctx, b->arr);
+ b->arr = b->def;
+ b->size = 4;
+}
+
+static int value_buffer_append(ValueBuffer *b, JSValue val)
+{
+ if (b->error_status)
+ return -1;
+
+ if (b->len >= b->size) {
+ int new_size = (b->len + (b->len >> 1) + 31) & ~16;
+ size_t slack;
+ JSValue *new_arr;
+
+ if (b->arr == b->def) {
+ new_arr = js_realloc2(b->ctx, NULL, sizeof(*b->arr) * new_size, &slack);
+ if (new_arr)
+ memcpy(new_arr, b->def, sizeof b->def);
+ } else {
+ new_arr = js_realloc2(b->ctx, b->arr, sizeof(*b->arr) * new_size, &slack);
+ }
+ if (!new_arr) {
+ value_buffer_free(b);
+ JS_FreeValue(b->ctx, val);
+ b->error_status = -1;
+ return -1;
+ }
+ new_size += slack / sizeof(*new_arr);
+ b->arr = new_arr;
+ b->size = new_size;
+ }
+ b->arr[b->len++] = val;
+ return 0;
+}
+
+static int js_is_standard_regexp(JSContext *ctx, JSValueConst rx)
+{
+ JSValue val;
+ int res;
+
+ val = JS_GetProperty(ctx, rx, JS_ATOM_constructor);
+ if (JS_IsException(val))
+ return -1;
+ // rx.constructor === RegExp
+ res = js_same_value(ctx, val, ctx->regexp_ctor);
+ JS_FreeValue(ctx, val);
+ if (res) {
+ val = JS_GetProperty(ctx, rx, JS_ATOM_exec);
+ if (JS_IsException(val))
+ return -1;
+ // rx.exec === RE_exec
+ res = JS_IsCFunction(ctx, val, js_regexp_exec, 0);
+ JS_FreeValue(ctx, val);
+ }
+ return res;
+}
+
+static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // [Symbol.replace](str, rep)
+ JSValueConst rx = this_val, rep = argv[1];
+ JSValueConst args[6];
+ JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res;
+ JSString *sp, *rp;
+ StringBuffer b_s, *b = &b_s;
+ ValueBuffer v_b, *results = &v_b;
+ int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode;
+ uint32_t nCaptures;
+ int64_t position;
+
+ if (!JS_IsObject(rx))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ string_buffer_init(ctx, b, 0);
+ value_buffer_init(ctx, results);
+
+ rep_val = JS_UNDEFINED;
+ matched = JS_UNDEFINED;
+ tab = JS_UNDEFINED;
+ rep_str = JS_UNDEFINED;
+ namedCaptures = JS_UNDEFINED;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ goto exception;
+
+ sp = JS_VALUE_GET_STRING(str);
+ rp = NULL;
+ functionalReplace = JS_IsFunction(ctx, rep);
+ if (!functionalReplace) {
+ rep_val = JS_ToString(ctx, rep);
+ if (JS_IsException(rep_val))
+ goto exception;
+ rp = JS_VALUE_GET_STRING(rep_val);
+ }
+ fullUnicode = 0;
+ is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global));
+ if (is_global < 0)
+ goto exception;
+ if (is_global) {
+ fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode));
+ if (fullUnicode < 0)
+ goto exception;
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0)
+ goto exception;
+ }
+
+ if (rp && rp->len == 0 && is_global && js_is_standard_regexp(ctx, rx)) {
+ /* use faster version for simple cases */
+ res = JS_RegExpDelete(ctx, rx, str);
+ goto done;
+ }
+ for(;;) {
+ JSValue result;
+ result = JS_RegExpExec(ctx, rx, str);
+ if (JS_IsException(result))
+ goto exception;
+ if (JS_IsNull(result))
+ break;
+ if (value_buffer_append(results, result) < 0)
+ goto exception;
+ if (!is_global)
+ break;
+ JS_FreeValue(ctx, matched);
+ matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
+ if (JS_IsException(matched))
+ goto exception;
+ if (JS_IsEmptyString(matched)) {
+ /* always advance of at least one char */
+ int64_t thisIndex, nextIndex;
+ if (JS_ToLengthFree(ctx, &thisIndex, JS_GetProperty(ctx, rx, JS_ATOM_lastIndex)) < 0)
+ goto exception;
+ nextIndex = string_advance_index(sp, thisIndex, fullUnicode);
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt64(ctx, nextIndex)) < 0)
+ goto exception;
+ }
+ }
+ nextSourcePosition = 0;
+ for(j = 0; j < results->len; j++) {
+ JSValueConst result;
+ result = results->arr[j];
+ if (js_get_length32(ctx, &nCaptures, result) < 0)
+ goto exception;
+ JS_FreeValue(ctx, matched);
+ matched = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, result, 0));
+ if (JS_IsException(matched))
+ goto exception;
+ if (JS_ToLengthFree(ctx, &position, JS_GetProperty(ctx, result, JS_ATOM_index)))
+ goto exception;
+ if (position > sp->len)
+ position = sp->len;
+ else if (position < 0)
+ position = 0;
+ /* ignore substition if going backward (can happen
+ with custom regexp object) */
+ JS_FreeValue(ctx, tab);
+ tab = JS_NewArray(ctx);
+ if (JS_IsException(tab))
+ goto exception;
+ if (JS_DefinePropertyValueInt64(ctx, tab, 0, JS_DupValue(ctx, matched),
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ for(n = 1; n < nCaptures; n++) {
+ JSValue capN;
+ capN = JS_GetPropertyInt64(ctx, result, n);
+ if (JS_IsException(capN))
+ goto exception;
+ if (!JS_IsUndefined(capN)) {
+ capN = JS_ToStringFree(ctx, capN);
+ if (JS_IsException(capN))
+ goto exception;
+ }
+ if (JS_DefinePropertyValueInt64(ctx, tab, n, capN,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ JS_FreeValue(ctx, namedCaptures);
+ namedCaptures = JS_GetProperty(ctx, result, JS_ATOM_groups);
+ if (JS_IsException(namedCaptures))
+ goto exception;
+ if (functionalReplace) {
+ if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_NewInt32(ctx, position), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, str), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ if (!JS_IsUndefined(namedCaptures)) {
+ if (JS_DefinePropertyValueInt64(ctx, tab, n++, JS_DupValue(ctx, namedCaptures), JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ args[0] = JS_UNDEFINED;
+ args[1] = tab;
+ JS_FreeValue(ctx, rep_str);
+ rep_str = JS_ToStringFree(ctx, js_function_apply(ctx, rep, 2, args, 0));
+ } else {
+ JSValue namedCaptures1;
+ if (!JS_IsUndefined(namedCaptures)) {
+ namedCaptures1 = JS_ToObject(ctx, namedCaptures);
+ if (JS_IsException(namedCaptures1))
+ goto exception;
+ } else {
+ namedCaptures1 = JS_UNDEFINED;
+ }
+ args[0] = matched;
+ args[1] = str;
+ args[2] = JS_NewInt32(ctx, position);
+ args[3] = tab;
+ args[4] = namedCaptures1;
+ args[5] = rep_val;
+ JS_FreeValue(ctx, rep_str);
+ rep_str = js_string___GetSubstitution(ctx, JS_UNDEFINED, 6, args);
+ JS_FreeValue(ctx, namedCaptures1);
+ }
+ if (JS_IsException(rep_str))
+ goto exception;
+ if (position >= nextSourcePosition) {
+ string_buffer_concat(b, sp, nextSourcePosition, position);
+ string_buffer_concat_value(b, rep_str);
+ nextSourcePosition = position + JS_VALUE_GET_STRING(matched)->len;
+ }
+ }
+ string_buffer_concat(b, sp, nextSourcePosition, sp->len);
+ res = string_buffer_end(b);
+ goto done1;
+
+exception:
+ res = JS_EXCEPTION;
+done:
+ string_buffer_free(b);
+done1:
+ value_buffer_free(results);
+ JS_FreeValue(ctx, rep_val);
+ JS_FreeValue(ctx, matched);
+ JS_FreeValue(ctx, tab);
+ JS_FreeValue(ctx, rep_str);
+ JS_FreeValue(ctx, namedCaptures);
+ JS_FreeValue(ctx, str);
+ return res;
+}
+
+static JSValue js_regexp_Symbol_search(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst rx = this_val;
+ JSValue str, previousLastIndex, currentLastIndex, result, index;
+
+ if (!JS_IsObject(rx))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ result = JS_UNDEFINED;
+ currentLastIndex = JS_UNDEFINED;
+ previousLastIndex = JS_UNDEFINED;
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ goto exception;
+
+ previousLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
+ if (JS_IsException(previousLastIndex))
+ goto exception;
+
+ if (!js_same_value(ctx, previousLastIndex, JS_NewInt32(ctx, 0))) {
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) {
+ goto exception;
+ }
+ }
+ result = JS_RegExpExec(ctx, rx, str);
+ if (JS_IsException(result))
+ goto exception;
+ currentLastIndex = JS_GetProperty(ctx, rx, JS_ATOM_lastIndex);
+ if (JS_IsException(currentLastIndex))
+ goto exception;
+ if (js_same_value(ctx, currentLastIndex, previousLastIndex)) {
+ JS_FreeValue(ctx, previousLastIndex);
+ } else {
+ if (JS_SetProperty(ctx, rx, JS_ATOM_lastIndex, previousLastIndex) < 0) {
+ previousLastIndex = JS_UNDEFINED;
+ goto exception;
+ }
+ }
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, currentLastIndex);
+
+ if (JS_IsNull(result)) {
+ return JS_NewInt32(ctx, -1);
+ } else {
+ index = JS_GetProperty(ctx, result, JS_ATOM_index);
+ JS_FreeValue(ctx, result);
+ return index;
+ }
+
+exception:
+ JS_FreeValue(ctx, result);
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, currentLastIndex);
+ JS_FreeValue(ctx, previousLastIndex);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_regexp_Symbol_split(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // [Symbol.split](str, limit)
+ JSValueConst rx = this_val;
+ JSValueConst args[2];
+ JSValue str, ctor, splitter, A, flags, z, sub;
+ JSString *strp;
+ uint32_t lim, size, p, q;
+ int unicodeMatching;
+ int64_t lengthA, e, numberOfCaptures, i;
+
+ if (!JS_IsObject(rx))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ ctor = JS_UNDEFINED;
+ splitter = JS_UNDEFINED;
+ A = JS_UNDEFINED;
+ flags = JS_UNDEFINED;
+ z = JS_UNDEFINED;
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ goto exception;
+ ctor = JS_SpeciesConstructor(ctx, rx, ctx->regexp_ctor);
+ if (JS_IsException(ctor))
+ goto exception;
+ flags = JS_ToStringFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_flags));
+ if (JS_IsException(flags))
+ goto exception;
+ strp = JS_VALUE_GET_STRING(flags);
+ unicodeMatching = string_indexof_char(strp, 'u', 0) >= 0;
+ if (string_indexof_char(strp, 'y', 0) < 0) {
+ flags = JS_ConcatString3(ctx, "", flags, "y");
+ if (JS_IsException(flags))
+ goto exception;
+ }
+ args[0] = rx;
+ args[1] = flags;
+ splitter = JS_CallConstructor(ctx, ctor, 2, args);
+ if (JS_IsException(splitter))
+ goto exception;
+ A = JS_NewArray(ctx);
+ if (JS_IsException(A))
+ goto exception;
+ lengthA = 0;
+ if (JS_IsUndefined(argv[1])) {
+ lim = 0xffffffff;
+ } else {
+ if (JS_ToUint32(ctx, &lim, argv[1]) < 0)
+ goto exception;
+ if (lim == 0)
+ goto done;
+ }
+ strp = JS_VALUE_GET_STRING(str);
+ p = q = 0;
+ size = strp->len;
+ if (size == 0) {
+ z = JS_RegExpExec(ctx, splitter, str);
+ if (JS_IsException(z))
+ goto exception;
+ if (JS_IsNull(z))
+ goto add_tail;
+ goto done;
+ }
+ while (q < size) {
+ if (JS_SetProperty(ctx, splitter, JS_ATOM_lastIndex, JS_NewInt32(ctx, q)) < 0)
+ goto exception;
+ JS_FreeValue(ctx, z);
+ z = JS_RegExpExec(ctx, splitter, str);
+ if (JS_IsException(z))
+ goto exception;
+ if (JS_IsNull(z)) {
+ q = string_advance_index(strp, q, unicodeMatching);
+ } else {
+ if (JS_ToLengthFree(ctx, &e, JS_GetProperty(ctx, splitter, JS_ATOM_lastIndex)))
+ goto exception;
+ if (e > size)
+ e = size;
+ if (e == p) {
+ q = string_advance_index(strp, q, unicodeMatching);
+ } else {
+ sub = js_sub_string(ctx, strp, p, q);
+ if (JS_IsException(sub))
+ goto exception;
+ if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub,
+ JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ if (lengthA == lim)
+ goto done;
+ p = e;
+ if (js_get_length64(ctx, &numberOfCaptures, z))
+ goto exception;
+ for(i = 1; i < numberOfCaptures; i++) {
+ sub = JS_ToStringFree(ctx, JS_GetPropertyInt64(ctx, z, i));
+ if (JS_IsException(sub))
+ goto exception;
+ if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ if (lengthA == lim)
+ goto done;
+ }
+ q = p;
+ }
+ }
+ }
+add_tail:
+ if (p > size)
+ p = size;
+ sub = js_sub_string(ctx, strp, p, size);
+ if (JS_IsException(sub))
+ goto exception;
+ if (JS_DefinePropertyValueInt64(ctx, A, lengthA++, sub, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception;
+ goto done;
+exception:
+ JS_FreeValue(ctx, A);
+ A = JS_EXCEPTION;
+done:
+ JS_FreeValue(ctx, str);
+ JS_FreeValue(ctx, ctor);
+ JS_FreeValue(ctx, splitter);
+ JS_FreeValue(ctx, flags);
+ JS_FreeValue(ctx, z);
+ return A;
+}
+
+static const JSCFunctionListEntry js_regexp_funcs[] = {
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+ //JS_CFUNC_DEF("__RegExpExec", 2, js_regexp___RegExpExec ),
+ //JS_CFUNC_DEF("__RegExpDelete", 2, js_regexp___RegExpDelete ),
+};
+
+static const JSCFunctionListEntry js_regexp_proto_funcs[] = {
+ JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ),
+ JS_CGETSET_DEF("source", js_regexp_get_source, NULL ),
+ JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ),
+ JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ),
+ JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ),
+ JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ),
+ JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ),
+ JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ),
+ JS_CFUNC_DEF("exec", 1, js_regexp_exec ),
+ JS_CFUNC_DEF("compile", 2, js_regexp_compile ),
+ JS_CFUNC_DEF("test", 1, js_regexp_test ),
+ JS_CFUNC_DEF("toString", 0, js_regexp_toString ),
+ JS_CFUNC_DEF("[Symbol.replace]", 2, js_regexp_Symbol_replace ),
+ JS_CFUNC_DEF("[Symbol.match]", 1, js_regexp_Symbol_match ),
+ JS_CFUNC_DEF("[Symbol.matchAll]", 1, js_regexp_Symbol_matchAll ),
+ JS_CFUNC_DEF("[Symbol.search]", 1, js_regexp_Symbol_search ),
+ JS_CFUNC_DEF("[Symbol.split]", 2, js_regexp_Symbol_split ),
+ //JS_CGETSET_DEF("__source", js_regexp_get___source, NULL ),
+ //JS_CGETSET_DEF("__flags", js_regexp_get___flags, NULL ),
+};
+
+static const JSCFunctionListEntry js_regexp_string_iterator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 0, js_regexp_string_iterator_next, 0 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "RegExp String Iterator", JS_PROP_CONFIGURABLE ),
+};
+
+void JS_AddIntrinsicRegExpCompiler(JSContext *ctx)
+{
+ ctx->compile_regexp = js_compile_regexp;
+}
+
+void JS_AddIntrinsicRegExp(JSContext *ctx)
+{
+ JSValueConst obj;
+
+ JS_AddIntrinsicRegExpCompiler(ctx);
+
+ ctx->class_proto[JS_CLASS_REGEXP] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP], js_regexp_proto_funcs,
+ countof(js_regexp_proto_funcs));
+ obj = JS_NewGlobalCConstructor(ctx, "RegExp", js_regexp_constructor, 2,
+ ctx->class_proto[JS_CLASS_REGEXP]);
+ ctx->regexp_ctor = JS_DupValue(ctx, obj);
+ JS_SetPropertyFunctionList(ctx, obj, js_regexp_funcs, countof(js_regexp_funcs));
+
+ ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR] =
+ JS_NewObjectProto(ctx, ctx->iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_REGEXP_STRING_ITERATOR],
+ js_regexp_string_iterator_proto_funcs,
+ countof(js_regexp_string_iterator_proto_funcs));
+}
+
+/* JSON */
+
+static int json_parse_expect(JSParseState *s, int tok)
+{
+ if (s->token.val != tok) {
+ /* XXX: dump token correctly in all cases */
+ return js_parse_error(s, "expecting '%c'", tok);
+ }
+ return json_next_token(s);
+}
+
+static JSValue json_parse_value(JSParseState *s)
+{
+ JSContext *ctx = s->ctx;
+ JSValue val = JS_NULL;
+ int ret;
+
+ switch(s->token.val) {
+ case '{':
+ {
+ JSValue prop_val;
+ JSAtom prop_name;
+
+ if (json_next_token(s))
+ goto fail;
+ val = JS_NewObject(ctx);
+ if (JS_IsException(val))
+ goto fail;
+ if (s->token.val != '}') {
+ for(;;) {
+ if (s->token.val == TOK_STRING) {
+ prop_name = JS_ValueToAtom(ctx, s->token.u.str.str);
+ if (prop_name == JS_ATOM_NULL)
+ goto fail;
+ } else if (s->ext_json && s->token.val == TOK_IDENT) {
+ prop_name = JS_DupAtom(ctx, s->token.u.ident.atom);
+ } else {
+ js_parse_error(s, "expecting property name");
+ goto fail;
+ }
+ if (json_next_token(s))
+ goto fail1;
+ if (json_parse_expect(s, ':'))
+ goto fail1;
+ prop_val = json_parse_value(s);
+ if (JS_IsException(prop_val)) {
+ fail1:
+ JS_FreeAtom(ctx, prop_name);
+ goto fail;
+ }
+ ret = JS_DefinePropertyValue(ctx, val, prop_name,
+ prop_val, JS_PROP_C_W_E);
+ JS_FreeAtom(ctx, prop_name);
+ if (ret < 0)
+ goto fail;
+
+ if (s->token.val != ',')
+ break;
+ if (json_next_token(s))
+ goto fail;
+ if (s->ext_json && s->token.val == '}')
+ break;
+ }
+ }
+ if (json_parse_expect(s, '}'))
+ goto fail;
+ }
+ break;
+ case '[':
+ {
+ JSValue el;
+ uint32_t idx;
+
+ if (json_next_token(s))
+ goto fail;
+ val = JS_NewArray(ctx);
+ if (JS_IsException(val))
+ goto fail;
+ if (s->token.val != ']') {
+ idx = 0;
+ for(;;) {
+ el = json_parse_value(s);
+ if (JS_IsException(el))
+ goto fail;
+ ret = JS_DefinePropertyValueUint32(ctx, val, idx, el, JS_PROP_C_W_E);
+ if (ret < 0)
+ goto fail;
+ if (s->token.val != ',')
+ break;
+ if (json_next_token(s))
+ goto fail;
+ idx++;
+ if (s->ext_json && s->token.val == ']')
+ break;
+ }
+ }
+ if (json_parse_expect(s, ']'))
+ goto fail;
+ }
+ break;
+ case TOK_STRING:
+ val = JS_DupValue(ctx, s->token.u.str.str);
+ if (json_next_token(s))
+ goto fail;
+ break;
+ case TOK_NUMBER:
+ val = s->token.u.num.val;
+ if (json_next_token(s))
+ goto fail;
+ break;
+ case TOK_IDENT:
+ if (s->token.u.ident.atom == JS_ATOM_false ||
+ s->token.u.ident.atom == JS_ATOM_true) {
+ val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true));
+ } else if (s->token.u.ident.atom == JS_ATOM_null) {
+ val = JS_NULL;
+ } else {
+ goto def_token;
+ }
+ if (json_next_token(s))
+ goto fail;
+ break;
+ default:
+ def_token:
+ if (s->token.val == TOK_EOF) {
+ js_parse_error(s, "unexpected end of input");
+ } else {
+ js_parse_error(s, "unexpected token: '%.*s'",
+ (int)(s->buf_ptr - s->token.ptr), s->token.ptr);
+ }
+ goto fail;
+ }
+ return val;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename, int flags)
+{
+ JSParseState s1, *s = &s1;
+ JSValue val = JS_UNDEFINED;
+
+ js_parse_init(ctx, s, buf, buf_len, filename, 1);
+ s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
+ if (json_next_token(s))
+ goto fail;
+ val = json_parse_value(s);
+ if (JS_IsException(val))
+ goto fail;
+ if (s->token.val != TOK_EOF) {
+ if (js_parse_error(s, "unexpected data at the end"))
+ goto fail;
+ }
+ return val;
+ fail:
+ JS_FreeValue(ctx, val);
+ free_token(s, &s->token);
+ return JS_EXCEPTION;
+}
+
+JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename)
+{
+ return JS_ParseJSON2(ctx, buf, buf_len, filename, 0);
+}
+
+static JSValue internalize_json_property(JSContext *ctx, JSValueConst holder,
+ JSAtom name, JSValueConst reviver)
+{
+ JSValue val, new_el, name_val, res;
+ JSValueConst args[2];
+ int ret, is_array;
+ uint32_t i, len = 0;
+ JSAtom prop;
+ JSPropertyEnum *atoms = NULL;
+
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ return JS_ThrowStackOverflow(ctx);
+ }
+
+ val = JS_GetProperty(ctx, holder, name);
+ if (JS_IsException(val))
+ return val;
+ if (JS_IsObject(val)) {
+ is_array = JS_IsArray(ctx, val);
+ if (is_array < 0)
+ goto fail;
+ if (is_array) {
+ if (js_get_length32(ctx, &len, val))
+ goto fail;
+ } else {
+ ret = JS_GetOwnPropertyNamesInternal(ctx, &atoms, &len, JS_VALUE_GET_OBJ(val), JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
+ if (ret < 0)
+ goto fail;
+ }
+ for(i = 0; i < len; i++) {
+ if (is_array) {
+ prop = JS_NewAtomUInt32(ctx, i);
+ if (prop == JS_ATOM_NULL)
+ goto fail;
+ } else {
+ prop = JS_DupAtom(ctx, atoms[i].atom);
+ }
+ new_el = internalize_json_property(ctx, val, prop, reviver);
+ if (JS_IsException(new_el)) {
+ JS_FreeAtom(ctx, prop);
+ goto fail;
+ }
+ if (JS_IsUndefined(new_el)) {
+ ret = JS_DeleteProperty(ctx, val, prop, 0);
+ } else {
+ ret = JS_DefinePropertyValue(ctx, val, prop, new_el, JS_PROP_C_W_E);
+ }
+ JS_FreeAtom(ctx, prop);
+ if (ret < 0)
+ goto fail;
+ }
+ }
+ js_free_prop_enum(ctx, atoms, len);
+ atoms = NULL;
+ name_val = JS_AtomToValue(ctx, name);
+ if (JS_IsException(name_val))
+ goto fail;
+ args[0] = name_val;
+ args[1] = val;
+ res = JS_Call(ctx, reviver, holder, 2, args);
+ JS_FreeValue(ctx, name_val);
+ JS_FreeValue(ctx, val);
+ return res;
+ fail:
+ js_free_prop_enum(ctx, atoms, len);
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_json_parse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj, root;
+ JSValueConst reviver;
+ const char *str;
+ size_t len;
+
+ str = JS_ToCStringLen(ctx, &len, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ obj = JS_ParseJSON(ctx, str, len, "<input>");
+ JS_FreeCString(ctx, str);
+ if (JS_IsException(obj))
+ return obj;
+ if (argc > 1 && JS_IsFunction(ctx, argv[1])) {
+ reviver = argv[1];
+ root = JS_NewObject(ctx);
+ if (JS_IsException(root)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ if (JS_DefinePropertyValue(ctx, root, JS_ATOM_empty_string, obj,
+ JS_PROP_C_W_E) < 0) {
+ JS_FreeValue(ctx, root);
+ return JS_EXCEPTION;
+ }
+ obj = internalize_json_property(ctx, root, JS_ATOM_empty_string,
+ reviver);
+ JS_FreeValue(ctx, root);
+ }
+ return obj;
+}
+
+typedef struct JSONStringifyContext {
+ JSValueConst replacer_func;
+ JSValue stack;
+ JSValue property_list;
+ JSValue gap;
+ JSValue empty;
+ StringBuffer *b;
+} JSONStringifyContext;
+
+static JSValue JS_ToQuotedStringFree(JSContext *ctx, JSValue val) {
+ JSValue r = JS_ToQuotedString(ctx, val);
+ JS_FreeValue(ctx, val);
+ return r;
+}
+
+static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc,
+ JSValueConst holder, JSValue val, JSValueConst key)
+{
+ JSValue v;
+ JSValueConst args[2];
+
+ if (JS_IsObject(val)
+#ifdef CONFIG_BIGNUM
+ || JS_IsBigInt(ctx, val) /* XXX: probably useless */
+#endif
+ ) {
+ JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON);
+ if (JS_IsException(f))
+ goto exception;
+ if (JS_IsFunction(ctx, f)) {
+ v = JS_CallFree(ctx, f, val, 1, &key);
+ JS_FreeValue(ctx, val);
+ val = v;
+ if (JS_IsException(val))
+ goto exception;
+ } else {
+ JS_FreeValue(ctx, f);
+ }
+ }
+
+ if (!JS_IsUndefined(jsc->replacer_func)) {
+ args[0] = key;
+ args[1] = val;
+ v = JS_Call(ctx, jsc->replacer_func, holder, 2, args);
+ JS_FreeValue(ctx, val);
+ val = v;
+ if (JS_IsException(val))
+ goto exception;
+ }
+
+ switch (JS_VALUE_GET_NORM_TAG(val)) {
+ case JS_TAG_OBJECT:
+ if (JS_IsFunction(ctx, val))
+ break;
+ case JS_TAG_STRING:
+ case JS_TAG_INT:
+ case JS_TAG_FLOAT64:
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+#endif
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+#endif
+ case JS_TAG_EXCEPTION:
+ return val;
+ default:
+ break;
+ }
+ JS_FreeValue(ctx, val);
+ return JS_UNDEFINED;
+
+exception:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
+ JSValueConst holder, JSValue val,
+ JSValueConst indent)
+{
+ JSValue indent1, sep, sep1, tab, v, prop;
+ JSObject *p;
+ int64_t i, len;
+ int cl, ret;
+ BOOL has_content;
+
+ indent1 = JS_UNDEFINED;
+ sep = JS_UNDEFINED;
+ sep1 = JS_UNDEFINED;
+ tab = JS_UNDEFINED;
+ prop = JS_UNDEFINED;
+
+ switch (JS_VALUE_GET_NORM_TAG(val)) {
+ case JS_TAG_OBJECT:
+ p = JS_VALUE_GET_OBJ(val);
+ cl = p->class_id;
+ if (cl == JS_CLASS_STRING) {
+ val = JS_ToStringFree(ctx, val);
+ if (JS_IsException(val))
+ goto exception;
+ val = JS_ToQuotedStringFree(ctx, val);
+ if (JS_IsException(val))
+ goto exception;
+ return string_buffer_concat_value_free(jsc->b, val);
+ } else if (cl == JS_CLASS_NUMBER) {
+ val = JS_ToNumberFree(ctx, val);
+ if (JS_IsException(val))
+ goto exception;
+ return string_buffer_concat_value_free(jsc->b, val);
+ } else if (cl == JS_CLASS_BOOLEAN) {
+ ret = string_buffer_concat_value(jsc->b, p->u.object_data);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+#ifdef CONFIG_BIGNUM
+ else if (cl == JS_CLASS_BIG_FLOAT) {
+ return string_buffer_concat_value_free(jsc->b, val);
+ } else if (cl == JS_CLASS_BIG_INT) {
+ JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
+ goto exception;
+ }
+#endif
+ v = js_array_includes(ctx, jsc->stack, 1, &val);
+ if (JS_IsException(v))
+ goto exception;
+ if (JS_ToBoolFree(ctx, v)) {
+ JS_ThrowTypeError(ctx, "circular reference");
+ goto exception;
+ }
+ indent1 = JS_ConcatString(ctx, JS_DupValue(ctx, indent), JS_DupValue(ctx, jsc->gap));
+ if (JS_IsException(indent1))
+ goto exception;
+ if (!JS_IsEmptyString(jsc->gap)) {
+ sep = JS_ConcatString3(ctx, "\n", JS_DupValue(ctx, indent1), "");
+ if (JS_IsException(sep))
+ goto exception;
+ sep1 = JS_NewString(ctx, " ");
+ if (JS_IsException(sep1))
+ goto exception;
+ } else {
+ sep = JS_DupValue(ctx, jsc->empty);
+ sep1 = JS_DupValue(ctx, jsc->empty);
+ }
+ v = js_array_push(ctx, jsc->stack, 1, &val, 0);
+ if (check_exception_free(ctx, v))
+ goto exception;
+ ret = JS_IsArray(ctx, val);
+ if (ret < 0)
+ goto exception;
+ if (ret) {
+ if (js_get_length64(ctx, &len, val))
+ goto exception;
+ string_buffer_putc8(jsc->b, '[');
+ for(i = 0; i < len; i++) {
+ if (i > 0)
+ string_buffer_putc8(jsc->b, ',');
+ string_buffer_concat_value(jsc->b, sep);
+ v = JS_GetPropertyInt64(ctx, val, i);
+ if (JS_IsException(v))
+ goto exception;
+ /* XXX: could do this string conversion only when needed */
+ prop = JS_ToStringFree(ctx, JS_NewInt64(ctx, i));
+ if (JS_IsException(prop))
+ goto exception;
+ v = js_json_check(ctx, jsc, val, v, prop);
+ JS_FreeValue(ctx, prop);
+ prop = JS_UNDEFINED;
+ if (JS_IsException(v))
+ goto exception;
+ if (JS_IsUndefined(v))
+ v = JS_NULL;
+ if (js_json_to_str(ctx, jsc, val, v, indent1))
+ goto exception;
+ }
+ if (len > 0 && !JS_IsEmptyString(jsc->gap)) {
+ string_buffer_putc8(jsc->b, '\n');
+ string_buffer_concat_value(jsc->b, indent);
+ }
+ string_buffer_putc8(jsc->b, ']');
+ } else {
+ if (!JS_IsUndefined(jsc->property_list))
+ tab = JS_DupValue(ctx, jsc->property_list);
+ else
+ tab = js_object_keys(ctx, JS_UNDEFINED, 1, &val, JS_ITERATOR_KIND_KEY);
+ if (JS_IsException(tab))
+ goto exception;
+ if (js_get_length64(ctx, &len, tab))
+ goto exception;
+ string_buffer_putc8(jsc->b, '{');
+ has_content = FALSE;
+ for(i = 0; i < len; i++) {
+ JS_FreeValue(ctx, prop);
+ prop = JS_GetPropertyInt64(ctx, tab, i);
+ if (JS_IsException(prop))
+ goto exception;
+ v = JS_GetPropertyValue(ctx, val, JS_DupValue(ctx, prop));
+ if (JS_IsException(v))
+ goto exception;
+ v = js_json_check(ctx, jsc, val, v, prop);
+ if (JS_IsException(v))
+ goto exception;
+ if (!JS_IsUndefined(v)) {
+ if (has_content)
+ string_buffer_putc8(jsc->b, ',');
+ prop = JS_ToQuotedStringFree(ctx, prop);
+ if (JS_IsException(prop)) {
+ JS_FreeValue(ctx, v);
+ goto exception;
+ }
+ string_buffer_concat_value(jsc->b, sep);
+ string_buffer_concat_value(jsc->b, prop);
+ string_buffer_putc8(jsc->b, ':');
+ string_buffer_concat_value(jsc->b, sep1);
+ if (js_json_to_str(ctx, jsc, val, v, indent1))
+ goto exception;
+ has_content = TRUE;
+ }
+ }
+ if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) {
+ string_buffer_putc8(jsc->b, '\n');
+ string_buffer_concat_value(jsc->b, indent);
+ }
+ string_buffer_putc8(jsc->b, '}');
+ }
+ if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))
+ goto exception;
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, tab);
+ JS_FreeValue(ctx, sep);
+ JS_FreeValue(ctx, sep1);
+ JS_FreeValue(ctx, indent1);
+ JS_FreeValue(ctx, prop);
+ return 0;
+ case JS_TAG_STRING:
+ val = JS_ToQuotedStringFree(ctx, val);
+ if (JS_IsException(val))
+ goto exception;
+ goto concat_value;
+ case JS_TAG_FLOAT64:
+ if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {
+ val = JS_NULL;
+ }
+ goto concat_value;
+ case JS_TAG_INT:
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_FLOAT:
+#endif
+ case JS_TAG_BOOL:
+ case JS_TAG_NULL:
+ concat_value:
+ return string_buffer_concat_value_free(jsc->b, val);
+#ifdef CONFIG_BIGNUM
+ case JS_TAG_BIG_INT:
+ JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify");
+ goto exception;
+#endif
+ default:
+ JS_FreeValue(ctx, val);
+ return 0;
+ }
+
+exception:
+ JS_FreeValue(ctx, val);
+ JS_FreeValue(ctx, tab);
+ JS_FreeValue(ctx, sep);
+ JS_FreeValue(ctx, sep1);
+ JS_FreeValue(ctx, indent1);
+ JS_FreeValue(ctx, prop);
+ return -1;
+}
+
+JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
+ JSValueConst replacer, JSValueConst space0)
+{
+ StringBuffer b_s;
+ JSONStringifyContext jsc_s, *jsc = &jsc_s;
+ JSValue val, v, space, ret, wrapper;
+ int res;
+ int64_t i, j, n;
+
+ jsc->replacer_func = JS_UNDEFINED;
+ jsc->stack = JS_UNDEFINED;
+ jsc->property_list = JS_UNDEFINED;
+ jsc->gap = JS_UNDEFINED;
+ jsc->b = &b_s;
+ jsc->empty = JS_AtomToString(ctx, JS_ATOM_empty_string);
+ ret = JS_UNDEFINED;
+ wrapper = JS_UNDEFINED;
+
+ string_buffer_init(ctx, jsc->b, 0);
+ jsc->stack = JS_NewArray(ctx);
+ if (JS_IsException(jsc->stack))
+ goto exception;
+ if (JS_IsFunction(ctx, replacer)) {
+ jsc->replacer_func = replacer;
+ } else {
+ res = JS_IsArray(ctx, replacer);
+ if (res < 0)
+ goto exception;
+ if (res) {
+ /* XXX: enumeration is not fully correct */
+ jsc->property_list = JS_NewArray(ctx);
+ if (JS_IsException(jsc->property_list))
+ goto exception;
+ if (js_get_length64(ctx, &n, replacer))
+ goto exception;
+ for (i = j = 0; i < n; i++) {
+ JSValue present;
+ v = JS_GetPropertyInt64(ctx, replacer, i);
+ if (JS_IsException(v))
+ goto exception;
+ if (JS_IsObject(v)) {
+ JSObject *p = JS_VALUE_GET_OBJ(v);
+ if (p->class_id == JS_CLASS_STRING ||
+ p->class_id == JS_CLASS_NUMBER) {
+ v = JS_ToStringFree(ctx, v);
+ if (JS_IsException(v))
+ goto exception;
+ } else {
+ JS_FreeValue(ctx, v);
+ continue;
+ }
+ } else if (JS_IsNumber(v)) {
+ v = JS_ToStringFree(ctx, v);
+ if (JS_IsException(v))
+ goto exception;
+ } else if (!JS_IsString(v)) {
+ JS_FreeValue(ctx, v);
+ continue;
+ }
+ present = js_array_includes(ctx, jsc->property_list,
+ 1, &v);
+ if (JS_IsException(present)) {
+ JS_FreeValue(ctx, v);
+ goto exception;
+ }
+ if (!JS_ToBoolFree(ctx, present)) {
+ JS_SetPropertyInt64(ctx, jsc->property_list, j++, v);
+ } else {
+ JS_FreeValue(ctx, v);
+ }
+ }
+ }
+ }
+ space = JS_DupValue(ctx, space0);
+ if (JS_IsObject(space)) {
+ JSObject *p = JS_VALUE_GET_OBJ(space);
+ if (p->class_id == JS_CLASS_NUMBER) {
+ space = JS_ToNumberFree(ctx, space);
+ } else if (p->class_id == JS_CLASS_STRING) {
+ space = JS_ToStringFree(ctx, space);
+ }
+ if (JS_IsException(space)) {
+ JS_FreeValue(ctx, space);
+ goto exception;
+ }
+ }
+ if (JS_IsNumber(space)) {
+ int n;
+ if (JS_ToInt32Clamp(ctx, &n, space, 0, 10, 0))
+ goto exception;
+ jsc->gap = JS_NewStringLen(ctx, " ", n);
+ } else if (JS_IsString(space)) {
+ JSString *p = JS_VALUE_GET_STRING(space);
+ jsc->gap = js_sub_string(ctx, p, 0, min_int(p->len, 10));
+ } else {
+ jsc->gap = JS_DupValue(ctx, jsc->empty);
+ }
+ JS_FreeValue(ctx, space);
+ if (JS_IsException(jsc->gap))
+ goto exception;
+ wrapper = JS_NewObject(ctx);
+ if (JS_IsException(wrapper))
+ goto exception;
+ if (JS_DefinePropertyValue(ctx, wrapper, JS_ATOM_empty_string,
+ JS_DupValue(ctx, obj), JS_PROP_C_W_E) < 0)
+ goto exception;
+ val = JS_DupValue(ctx, obj);
+
+ val = js_json_check(ctx, jsc, wrapper, val, jsc->empty);
+ if (JS_IsException(val))
+ goto exception;
+ if (JS_IsUndefined(val)) {
+ ret = JS_UNDEFINED;
+ goto done1;
+ }
+ if (js_json_to_str(ctx, jsc, wrapper, val, jsc->empty))
+ goto exception;
+
+ ret = string_buffer_end(jsc->b);
+ goto done;
+
+exception:
+ ret = JS_EXCEPTION;
+done1:
+ string_buffer_free(jsc->b);
+done:
+ JS_FreeValue(ctx, wrapper);
+ JS_FreeValue(ctx, jsc->empty);
+ JS_FreeValue(ctx, jsc->gap);
+ JS_FreeValue(ctx, jsc->property_list);
+ JS_FreeValue(ctx, jsc->stack);
+ return ret;
+}
+
+static JSValue js_json_stringify(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // stringify(val, replacer, space)
+ return JS_JSONStringify(ctx, argv[0], argv[1], argv[2]);
+}
+
+static const JSCFunctionListEntry js_json_funcs[] = {
+ JS_CFUNC_DEF("parse", 2, js_json_parse ),
+ JS_CFUNC_DEF("stringify", 3, js_json_stringify ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "JSON", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_json_obj[] = {
+ JS_OBJECT_DEF("JSON", js_json_funcs, countof(js_json_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+};
+
+void JS_AddIntrinsicJSON(JSContext *ctx)
+{
+ /* add JSON as autoinit object */
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_json_obj, countof(js_json_obj));
+}
+
+/* Reflect */
+
+static JSValue js_reflect_apply(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_function_apply(ctx, argv[0], max_int(0, argc - 1), argv + 1, 2);
+}
+
+static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst func, array_arg, new_target;
+ JSValue *tab, ret;
+ uint32_t len;
+
+ func = argv[0];
+ array_arg = argv[1];
+ if (argc > 2) {
+ new_target = argv[2];
+ if (!JS_IsConstructor(ctx, new_target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ } else {
+ new_target = func;
+ }
+ tab = build_arg_list(ctx, &len, array_arg);
+ if (!tab)
+ return JS_EXCEPTION;
+ ret = JS_CallConstructor2(ctx, func, new_target, len, tab);
+ free_arg_list(ctx, tab, len);
+ return ret;
+}
+
+static JSValue js_reflect_deleteProperty(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj;
+ JSAtom atom;
+ int ret;
+
+ obj = argv[0];
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ atom = JS_ValueToAtom(ctx, argv[1]);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ ret = JS_DeleteProperty(ctx, obj, atom, 0);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_reflect_get(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj, prop, receiver;
+ JSAtom atom;
+ JSValue ret;
+
+ obj = argv[0];
+ prop = argv[1];
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ if (argc > 2)
+ receiver = argv[2];
+ else
+ receiver = obj;
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ ret = JS_GetPropertyInternal(ctx, obj, atom, receiver, FALSE);
+ JS_FreeAtom(ctx, atom);
+ return ret;
+}
+
+static JSValue js_reflect_has(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj, prop;
+ JSAtom atom;
+ int ret;
+
+ obj = argv[0];
+ prop = argv[1];
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ ret = JS_HasProperty(ctx, obj, atom);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj, prop, val, receiver;
+ int ret;
+ JSAtom atom;
+
+ obj = argv[0];
+ prop = argv[1];
+ val = argv[2];
+ if (argc > 3)
+ receiver = argv[3];
+ else
+ receiver = obj;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ atom = JS_ValueToAtom(ctx, prop);
+ if (unlikely(atom == JS_ATOM_NULL))
+ return JS_EXCEPTION;
+ ret = JS_SetPropertyGeneric(ctx, obj, atom,
+ JS_DupValue(ctx, val), receiver, 0);
+ JS_FreeAtom(ctx, atom);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_reflect_setPrototypeOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ int ret;
+ ret = JS_SetPrototypeInternal(ctx, argv[0], argv[1], FALSE);
+ if (ret < 0)
+ return JS_EXCEPTION;
+ else
+ return JS_NewBool(ctx, ret);
+}
+
+static JSValue js_reflect_ownKeys(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ return JS_GetOwnPropertyNames2(ctx, argv[0],
+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK,
+ JS_ITERATOR_KIND_KEY);
+}
+
+static const JSCFunctionListEntry js_reflect_funcs[] = {
+ JS_CFUNC_DEF("apply", 3, js_reflect_apply ),
+ JS_CFUNC_DEF("construct", 2, js_reflect_construct ),
+ JS_CFUNC_MAGIC_DEF("defineProperty", 3, js_object_defineProperty, 1 ),
+ JS_CFUNC_DEF("deleteProperty", 2, js_reflect_deleteProperty ),
+ JS_CFUNC_DEF("get", 2, js_reflect_get ),
+ JS_CFUNC_MAGIC_DEF("getOwnPropertyDescriptor", 2, js_object_getOwnPropertyDescriptor, 1 ),
+ JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 1 ),
+ JS_CFUNC_DEF("has", 2, js_reflect_has ),
+ JS_CFUNC_MAGIC_DEF("isExtensible", 1, js_object_isExtensible, 1 ),
+ JS_CFUNC_DEF("ownKeys", 1, js_reflect_ownKeys ),
+ JS_CFUNC_MAGIC_DEF("preventExtensions", 1, js_object_preventExtensions, 1 ),
+ JS_CFUNC_DEF("set", 3, js_reflect_set ),
+ JS_CFUNC_DEF("setPrototypeOf", 2, js_reflect_setPrototypeOf ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Reflect", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_reflect_obj[] = {
+ JS_OBJECT_DEF("Reflect", js_reflect_funcs, countof(js_reflect_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+};
+
+/* Proxy */
+
+static void js_proxy_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
+ if (s) {
+ JS_FreeValueRT(rt, s->target);
+ JS_FreeValueRT(rt, s->handler);
+ js_free_rt(rt, s);
+ }
+}
+
+static void js_proxy_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSProxyData *s = JS_GetOpaque(val, JS_CLASS_PROXY);
+ if (s) {
+ JS_MarkValue(rt, s->target, mark_func);
+ JS_MarkValue(rt, s->handler, mark_func);
+ }
+}
+
+static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "revoked proxy");
+}
+
+static JSProxyData *get_proxy_method(JSContext *ctx, JSValue *pmethod,
+ JSValueConst obj, JSAtom name)
+{
+ JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
+ JSValue method;
+
+ /* safer to test recursion in all proxy methods */
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ JS_ThrowStackOverflow(ctx);
+ return NULL;
+ }
+
+ /* 's' should never be NULL */
+ if (s->is_revoked) {
+ JS_ThrowTypeErrorRevokedProxy(ctx);
+ return NULL;
+ }
+ method = JS_GetProperty(ctx, s->handler, name);
+ if (JS_IsException(method))
+ return NULL;
+ if (JS_IsNull(method))
+ method = JS_UNDEFINED;
+ *pmethod = method;
+ return s;
+}
+
+static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
+{
+ JSProxyData *s;
+ JSValue method, ret, proto1;
+ int res;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_getPrototypeOf);
+ if (!s)
+ return JS_EXCEPTION;
+ if (JS_IsUndefined(method))
+ return JS_GetPrototype(ctx, s->target);
+ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return ret;
+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&
+ JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
+ goto fail;
+ }
+ res = JS_IsExtensible(ctx, s->target);
+ if (res < 0) {
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+ }
+ if (!res) {
+ /* check invariant */
+ proto1 = JS_GetPrototype(ctx, s->target);
+ if (JS_IsException(proto1)) {
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+ }
+ if (JS_VALUE_GET_OBJ(proto1) != JS_VALUE_GET_OBJ(ret)) {
+ JS_FreeValue(ctx, proto1);
+ fail:
+ JS_FreeValue(ctx, ret);
+ return JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
+ }
+ JS_FreeValue(ctx, proto1);
+ }
+ return ret;
+}
+
+static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
+ JSValueConst proto_val, BOOL throw_flag)
+{
+ JSProxyData *s;
+ JSValue method, ret, proto1;
+ JSValueConst args[2];
+ BOOL res;
+ int res2;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_setPrototypeOf);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_SetPrototypeInternal(ctx, s->target, proto_val, throw_flag);
+ args[0] = s->target;
+ args[1] = proto_val;
+ ret = JS_CallFree(ctx, method, s->handler, 2, args);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+ if (!res) {
+ if (throw_flag) {
+ JS_ThrowTypeError(ctx, "proxy: bad prototype");
+ return -1;
+ } else {
+ return FALSE;
+ }
+ }
+ res2 = JS_IsExtensible(ctx, s->target);
+ if (res2 < 0)
+ return -1;
+ if (!res2) {
+ proto1 = JS_GetPrototype(ctx, s->target);
+ if (JS_IsException(proto1))
+ return -1;
+ if (JS_VALUE_GET_OBJ(proto_val) != JS_VALUE_GET_OBJ(proto1)) {
+ JS_FreeValue(ctx, proto1);
+ JS_ThrowTypeError(ctx, "proxy: inconsistent prototype");
+ return -1;
+ }
+ JS_FreeValue(ctx, proto1);
+ }
+ return TRUE;
+}
+
+static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)
+{
+ JSProxyData *s;
+ JSValue method, ret;
+ BOOL res;
+ int res2;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_isExtensible);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_IsExtensible(ctx, s->target);
+ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+ res2 = JS_IsExtensible(ctx, s->target);
+ if (res2 < 0)
+ return res2;
+ if (res != res2) {
+ JS_ThrowTypeError(ctx, "proxy: inconsistent isExtensible");
+ return -1;
+ }
+ return res;
+}
+
+static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)
+{
+ JSProxyData *s;
+ JSValue method, ret;
+ BOOL res;
+ int res2;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_preventExtensions);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_PreventExtensions(ctx, s->target);
+ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+ if (res) {
+ res2 = JS_IsExtensible(ctx, s->target);
+ if (res2 < 0)
+ return res2;
+ if (res2) {
+ JS_ThrowTypeError(ctx, "proxy: inconsistent preventExtensions");
+ return -1;
+ }
+ }
+ return res;
+}
+
+static int js_proxy_has(JSContext *ctx, JSValueConst obj, JSAtom atom)
+{
+ JSProxyData *s;
+ JSValue method, ret1, atom_val;
+ int ret, res;
+ JSObject *p;
+ JSValueConst args[2];
+ BOOL res2;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_has);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_HasProperty(ctx, s->target, atom);
+ atom_val = JS_AtomToValue(ctx, atom);
+ if (JS_IsException(atom_val)) {
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ args[0] = s->target;
+ args[1] = atom_val;
+ ret1 = JS_CallFree(ctx, method, s->handler, 2, args);
+ JS_FreeValue(ctx, atom_val);
+ if (JS_IsException(ret1))
+ return -1;
+ ret = JS_ToBoolFree(ctx, ret1);
+ if (!ret) {
+ JSPropertyDescriptor desc;
+ p = JS_VALUE_GET_OBJ(s->target);
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, atom);
+ if (res < 0)
+ return -1;
+ if (res) {
+ res2 = !(desc.flags & JS_PROP_CONFIGURABLE);
+ js_free_desc(ctx, &desc);
+ if (res2 || !p->extensible) {
+ JS_ThrowTypeError(ctx, "proxy: inconsistent has");
+ return -1;
+ }
+ }
+ }
+ return ret;
+}
+
+static JSValue js_proxy_get(JSContext *ctx, JSValueConst obj, JSAtom atom,
+ JSValueConst receiver)
+{
+ JSProxyData *s;
+ JSValue method, ret, atom_val;
+ int res;
+ JSValueConst args[3];
+ JSPropertyDescriptor desc;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_get);
+ if (!s)
+ return JS_EXCEPTION;
+ /* Note: recursion is possible thru the prototype of s->target */
+ if (JS_IsUndefined(method))
+ return JS_GetPropertyInternal(ctx, s->target, atom, receiver, FALSE);
+ atom_val = JS_AtomToValue(ctx, atom);
+ if (JS_IsException(atom_val)) {
+ JS_FreeValue(ctx, method);
+ return JS_EXCEPTION;
+ }
+ args[0] = s->target;
+ args[1] = atom_val;
+ args[2] = receiver;
+ ret = JS_CallFree(ctx, method, s->handler, 3, args);
+ JS_FreeValue(ctx, atom_val);
+ if (JS_IsException(ret))
+ return JS_EXCEPTION;
+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (res) {
+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
+ if (!js_same_value(ctx, desc.value, ret)) {
+ goto fail;
+ }
+ } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET) {
+ if (JS_IsUndefined(desc.getter) && !JS_IsUndefined(ret)) {
+ fail:
+ js_free_desc(ctx, &desc);
+ JS_FreeValue(ctx, ret);
+ return JS_ThrowTypeError(ctx, "proxy: inconsistent get");
+ }
+ }
+ js_free_desc(ctx, &desc);
+ }
+ return ret;
+}
+
+static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom,
+ JSValueConst value, JSValueConst receiver, int flags)
+{
+ JSProxyData *s;
+ JSValue method, ret1, atom_val;
+ int ret, res;
+ JSValueConst args[4];
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_set);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method)) {
+ return JS_SetPropertyGeneric(ctx, s->target, atom,
+ JS_DupValue(ctx, value), receiver,
+ flags);
+ }
+ atom_val = JS_AtomToValue(ctx, atom);
+ if (JS_IsException(atom_val)) {
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ args[0] = s->target;
+ args[1] = atom_val;
+ args[2] = value;
+ args[3] = receiver;
+ ret1 = JS_CallFree(ctx, method, s->handler, 4, args);
+ JS_FreeValue(ctx, atom_val);
+ if (JS_IsException(ret1))
+ return -1;
+ ret = JS_ToBoolFree(ctx, ret1);
+ if (ret) {
+ JSPropertyDescriptor desc;
+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
+ if (res < 0)
+ return -1;
+ if (res) {
+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0) {
+ if (!js_same_value(ctx, desc.value, value)) {
+ goto fail;
+ }
+ } else if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) == JS_PROP_GETSET && JS_IsUndefined(desc.setter)) {
+ fail:
+ js_free_desc(ctx, &desc);
+ JS_ThrowTypeError(ctx, "proxy: inconsistent set");
+ return -1;
+ }
+ js_free_desc(ctx, &desc);
+ }
+ } else {
+ if ((flags & JS_PROP_THROW) ||
+ ((flags & JS_PROP_THROW_STRICT) && is_strict_mode(ctx))) {
+ JS_ThrowTypeError(ctx, "proxy: cannot set property");
+ return -1;
+ }
+ }
+ return ret;
+}
+
+static JSValue js_create_desc(JSContext *ctx, JSValueConst val,
+ JSValueConst getter, JSValueConst setter,
+ int flags)
+{
+ JSValue ret;
+ ret = JS_NewObject(ctx);
+ if (JS_IsException(ret))
+ return ret;
+ if (flags & JS_PROP_HAS_GET) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_get, JS_DupValue(ctx, getter),
+ JS_PROP_C_W_E);
+ }
+ if (flags & JS_PROP_HAS_SET) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_set, JS_DupValue(ctx, setter),
+ JS_PROP_C_W_E);
+ }
+ if (flags & JS_PROP_HAS_VALUE) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, val),
+ JS_PROP_C_W_E);
+ }
+ if (flags & JS_PROP_HAS_WRITABLE) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable,
+ JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0),
+ JS_PROP_C_W_E);
+ }
+ if (flags & JS_PROP_HAS_ENUMERABLE) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable,
+ JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0),
+ JS_PROP_C_W_E);
+ }
+ if (flags & JS_PROP_HAS_CONFIGURABLE) {
+ JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable,
+ JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0),
+ JS_PROP_C_W_E);
+ }
+ return ret;
+}
+
+static int js_proxy_get_own_property(JSContext *ctx, JSPropertyDescriptor *pdesc,
+ JSValueConst obj, JSAtom prop)
+{
+ JSProxyData *s;
+ JSValue method, trap_result_obj, prop_val;
+ int res, target_desc_ret, ret;
+ JSObject *p;
+ JSValueConst args[2];
+ JSPropertyDescriptor result_desc, target_desc;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_getOwnPropertyDescriptor);
+ if (!s)
+ return -1;
+ p = JS_VALUE_GET_OBJ(s->target);
+ if (JS_IsUndefined(method)) {
+ return JS_GetOwnPropertyInternal(ctx, pdesc, p, prop);
+ }
+ prop_val = JS_AtomToValue(ctx, prop);
+ if (JS_IsException(prop_val)) {
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ args[0] = s->target;
+ args[1] = prop_val;
+ trap_result_obj = JS_CallFree(ctx, method, s->handler, 2, args);
+ JS_FreeValue(ctx, prop_val);
+ if (JS_IsException(trap_result_obj))
+ return -1;
+ if (!JS_IsObject(trap_result_obj) && !JS_IsUndefined(trap_result_obj)) {
+ JS_FreeValue(ctx, trap_result_obj);
+ goto fail;
+ }
+ target_desc_ret = JS_GetOwnPropertyInternal(ctx, &target_desc, p, prop);
+ if (target_desc_ret < 0) {
+ JS_FreeValue(ctx, trap_result_obj);
+ return -1;
+ }
+ if (target_desc_ret)
+ js_free_desc(ctx, &target_desc);
+ if (JS_IsUndefined(trap_result_obj)) {
+ if (target_desc_ret) {
+ if (!(target_desc.flags & JS_PROP_CONFIGURABLE) || !p->extensible)
+ goto fail;
+ }
+ ret = FALSE;
+ } else {
+ int flags1, extensible_target;
+ extensible_target = JS_IsExtensible(ctx, s->target);
+ if (extensible_target < 0) {
+ JS_FreeValue(ctx, trap_result_obj);
+ return -1;
+ }
+ res = js_obj_to_desc(ctx, &result_desc, trap_result_obj);
+ JS_FreeValue(ctx, trap_result_obj);
+ if (res < 0)
+ return -1;
+
+ if (target_desc_ret) {
+ /* convert result_desc.flags to defineProperty flags */
+ flags1 = result_desc.flags | JS_PROP_HAS_CONFIGURABLE | JS_PROP_HAS_ENUMERABLE;
+ if (result_desc.flags & JS_PROP_GETSET)
+ flags1 |= JS_PROP_HAS_GET | JS_PROP_HAS_SET;
+ else
+ flags1 |= JS_PROP_HAS_VALUE | JS_PROP_HAS_WRITABLE;
+ /* XXX: not complete check: need to compare value &
+ getter/setter as in defineproperty */
+ if (!check_define_prop_flags(target_desc.flags, flags1))
+ goto fail1;
+ } else {
+ if (!extensible_target)
+ goto fail1;
+ }
+ if (!(result_desc.flags & JS_PROP_CONFIGURABLE)) {
+ if (!target_desc_ret || (target_desc.flags & JS_PROP_CONFIGURABLE))
+ goto fail1;
+ if ((result_desc.flags &
+ (JS_PROP_GETSET | JS_PROP_WRITABLE)) == 0 &&
+ target_desc_ret &&
+ (target_desc.flags & JS_PROP_WRITABLE) != 0) {
+ /* proxy-missing-checks */
+ fail1:
+ js_free_desc(ctx, &result_desc);
+ fail:
+ JS_ThrowTypeError(ctx, "proxy: inconsistent getOwnPropertyDescriptor");
+ return -1;
+ }
+ }
+ ret = TRUE;
+ if (pdesc) {
+ *pdesc = result_desc;
+ } else {
+ js_free_desc(ctx, &result_desc);
+ }
+ }
+ return ret;
+}
+
+static int js_proxy_define_own_property(JSContext *ctx, JSValueConst obj,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter,
+ int flags)
+{
+ JSProxyData *s;
+ JSValue method, ret1, prop_val, desc_val;
+ int res, ret;
+ JSObject *p;
+ JSValueConst args[3];
+ JSPropertyDescriptor desc;
+ BOOL setting_not_configurable;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_defineProperty);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method)) {
+ return JS_DefineProperty(ctx, s->target, prop, val, getter, setter, flags);
+ }
+ prop_val = JS_AtomToValue(ctx, prop);
+ if (JS_IsException(prop_val)) {
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ desc_val = js_create_desc(ctx, val, getter, setter, flags);
+ if (JS_IsException(desc_val)) {
+ JS_FreeValue(ctx, prop_val);
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ args[0] = s->target;
+ args[1] = prop_val;
+ args[2] = desc_val;
+ ret1 = JS_CallFree(ctx, method, s->handler, 3, args);
+ JS_FreeValue(ctx, prop_val);
+ JS_FreeValue(ctx, desc_val);
+ if (JS_IsException(ret1))
+ return -1;
+ ret = JS_ToBoolFree(ctx, ret1);
+ if (!ret) {
+ if (flags & JS_PROP_THROW) {
+ JS_ThrowTypeError(ctx, "proxy: defineProperty exception");
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ p = JS_VALUE_GET_OBJ(s->target);
+ res = JS_GetOwnPropertyInternal(ctx, &desc, p, prop);
+ if (res < 0)
+ return -1;
+ setting_not_configurable = ((flags & (JS_PROP_HAS_CONFIGURABLE |
+ JS_PROP_CONFIGURABLE)) ==
+ JS_PROP_HAS_CONFIGURABLE);
+ if (!res) {
+ if (!p->extensible || setting_not_configurable)
+ goto fail;
+ } else {
+ if (!check_define_prop_flags(desc.flags, flags) ||
+ ((desc.flags & JS_PROP_CONFIGURABLE) && setting_not_configurable)) {
+ goto fail1;
+ }
+ if (flags & (JS_PROP_HAS_GET | JS_PROP_HAS_SET)) {
+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE)) ==
+ JS_PROP_GETSET) {
+ if ((flags & JS_PROP_HAS_GET) &&
+ !js_same_value(ctx, getter, desc.getter)) {
+ goto fail1;
+ }
+ if ((flags & JS_PROP_HAS_SET) &&
+ !js_same_value(ctx, setter, desc.setter)) {
+ goto fail1;
+ }
+ }
+ } else if (flags & JS_PROP_HAS_VALUE) {
+ if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) ==
+ JS_PROP_WRITABLE && !(flags & JS_PROP_WRITABLE)) {
+ /* missing-proxy-check feature */
+ goto fail1;
+ } else if ((desc.flags & (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE)) == 0 &&
+ !js_same_value(ctx, val, desc.value)) {
+ goto fail1;
+ }
+ }
+ if (flags & JS_PROP_HAS_WRITABLE) {
+ if ((desc.flags & (JS_PROP_GETSET | JS_PROP_CONFIGURABLE |
+ JS_PROP_WRITABLE)) == JS_PROP_WRITABLE) {
+ /* proxy-missing-checks */
+ fail1:
+ js_free_desc(ctx, &desc);
+ fail:
+ JS_ThrowTypeError(ctx, "proxy: inconsistent defineProperty");
+ return -1;
+ }
+ }
+ js_free_desc(ctx, &desc);
+ }
+ return 1;
+}
+
+static int js_proxy_delete_property(JSContext *ctx, JSValueConst obj,
+ JSAtom atom)
+{
+ JSProxyData *s;
+ JSValue method, ret, atom_val;
+ int res, res2, is_extensible;
+ JSValueConst args[2];
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_deleteProperty);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method)) {
+ return JS_DeleteProperty(ctx, s->target, atom, 0);
+ }
+ atom_val = JS_AtomToValue(ctx, atom);;
+ if (JS_IsException(atom_val)) {
+ JS_FreeValue(ctx, method);
+ return -1;
+ }
+ args[0] = s->target;
+ args[1] = atom_val;
+ ret = JS_CallFree(ctx, method, s->handler, 2, args);
+ JS_FreeValue(ctx, atom_val);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+ if (res) {
+ JSPropertyDescriptor desc;
+ res2 = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target), atom);
+ if (res2 < 0)
+ return -1;
+ if (res2) {
+ if (!(desc.flags & JS_PROP_CONFIGURABLE))
+ goto fail;
+ is_extensible = JS_IsExtensible(ctx, s->target);
+ if (is_extensible < 0)
+ goto fail1;
+ if (!is_extensible) {
+ /* proxy-missing-checks */
+ fail:
+ JS_ThrowTypeError(ctx, "proxy: inconsistent deleteProperty");
+ fail1:
+ js_free_desc(ctx, &desc);
+ return -1;
+ }
+ js_free_desc(ctx, &desc);
+ }
+ }
+ return res;
+}
+
+/* return the index of the property or -1 if not found */
+static int find_prop_key(const JSPropertyEnum *tab, int n, JSAtom atom)
+{
+ int i;
+ for(i = 0; i < n; i++) {
+ if (tab[i].atom == atom)
+ return i;
+ }
+ return -1;
+}
+
+static int js_proxy_get_own_property_names(JSContext *ctx,
+ JSPropertyEnum **ptab,
+ uint32_t *plen,
+ JSValueConst obj)
+{
+ JSProxyData *s;
+ JSValue method, prop_array, val;
+ uint32_t len, i, len2;
+ JSPropertyEnum *tab, *tab2;
+ JSAtom atom;
+ JSPropertyDescriptor desc;
+ int res, is_extensible, idx;
+
+ s = get_proxy_method(ctx, &method, obj, JS_ATOM_ownKeys);
+ if (!s)
+ return -1;
+ if (JS_IsUndefined(method)) {
+ return JS_GetOwnPropertyNamesInternal(ctx, ptab, plen,
+ JS_VALUE_GET_OBJ(s->target),
+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);
+ }
+ prop_array = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(prop_array))
+ return -1;
+ tab = NULL;
+ len = 0;
+ tab2 = NULL;
+ len2 = 0;
+ if (js_get_length32(ctx, &len, prop_array))
+ goto fail;
+ if (len > 0) {
+ tab = js_mallocz(ctx, sizeof(tab[0]) * len);
+ if (!tab)
+ goto fail;
+ }
+ for(i = 0; i < len; i++) {
+ val = JS_GetPropertyUint32(ctx, prop_array, i);
+ if (JS_IsException(val))
+ goto fail;
+ if (!JS_IsString(val) && !JS_IsSymbol(val)) {
+ JS_FreeValue(ctx, val);
+ JS_ThrowTypeError(ctx, "proxy: properties must be strings or symbols");
+ goto fail;
+ }
+ atom = JS_ValueToAtom(ctx, val);
+ JS_FreeValue(ctx, val);
+ if (atom == JS_ATOM_NULL)
+ goto fail;
+ tab[i].atom = atom;
+ tab[i].is_enumerable = FALSE; /* XXX: redundant? */
+ }
+
+ /* check duplicate properties (XXX: inefficient, could store the
+ * properties an a temporary object to use the hash) */
+ for(i = 1; i < len; i++) {
+ if (find_prop_key(tab, i, tab[i].atom) >= 0) {
+ JS_ThrowTypeError(ctx, "proxy: duplicate property");
+ goto fail;
+ }
+ }
+
+ is_extensible = JS_IsExtensible(ctx, s->target);
+ if (is_extensible < 0)
+ goto fail;
+
+ /* check if there are non configurable properties */
+ if (s->is_revoked) {
+ JS_ThrowTypeErrorRevokedProxy(ctx);
+ goto fail;
+ }
+ if (JS_GetOwnPropertyNamesInternal(ctx, &tab2, &len2, JS_VALUE_GET_OBJ(s->target),
+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK))
+ goto fail;
+ for(i = 0; i < len2; i++) {
+ if (s->is_revoked) {
+ JS_ThrowTypeErrorRevokedProxy(ctx);
+ goto fail;
+ }
+ res = JS_GetOwnPropertyInternal(ctx, &desc, JS_VALUE_GET_OBJ(s->target),
+ tab2[i].atom);
+ if (res < 0)
+ goto fail;
+ if (res) { /* safety, property should be found */
+ js_free_desc(ctx, &desc);
+ if (!(desc.flags & JS_PROP_CONFIGURABLE) || !is_extensible) {
+ idx = find_prop_key(tab, len, tab2[i].atom);
+ if (idx < 0) {
+ JS_ThrowTypeError(ctx, "proxy: target property must be present in proxy ownKeys");
+ goto fail;
+ }
+ /* mark the property as found */
+ if (!is_extensible)
+ tab[idx].is_enumerable = TRUE;
+ }
+ }
+ }
+ if (!is_extensible) {
+ /* check that all property in 'tab' were checked */
+ for(i = 0; i < len; i++) {
+ if (!tab[i].is_enumerable) {
+ JS_ThrowTypeError(ctx, "proxy: property not present in target were returned by non extensible proxy");
+ goto fail;
+ }
+ }
+ }
+
+ js_free_prop_enum(ctx, tab2, len2);
+ JS_FreeValue(ctx, prop_array);
+ *ptab = tab;
+ *plen = len;
+ return 0;
+ fail:
+ js_free_prop_enum(ctx, tab2, len2);
+ js_free_prop_enum(ctx, tab, len);
+ JS_FreeValue(ctx, prop_array);
+ return -1;
+}
+
+static JSValue js_proxy_call_constructor(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSProxyData *s;
+ JSValue method, arg_array, ret;
+ JSValueConst args[3];
+
+ s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_construct);
+ if (!s)
+ return JS_EXCEPTION;
+ if (!JS_IsConstructor(ctx, s->target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ if (JS_IsUndefined(method))
+ return JS_CallConstructor2(ctx, s->target, new_target, argc, argv);
+ arg_array = js_create_array(ctx, argc, argv);
+ if (JS_IsException(arg_array)) {
+ ret = JS_EXCEPTION;
+ goto fail;
+ }
+ args[0] = s->target;
+ args[1] = arg_array;
+ args[2] = new_target;
+ ret = JS_Call(ctx, method, s->handler, 3, args);
+ if (!JS_IsException(ret) && JS_VALUE_GET_TAG(ret) != JS_TAG_OBJECT) {
+ JS_FreeValue(ctx, ret);
+ ret = JS_ThrowTypeErrorNotAnObject(ctx);
+ }
+ fail:
+ JS_FreeValue(ctx, method);
+ JS_FreeValue(ctx, arg_array);
+ return ret;
+}
+
+static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int flags)
+{
+ JSProxyData *s;
+ JSValue method, arg_array, ret;
+ JSValueConst args[3];
+
+ if (flags & JS_CALL_FLAG_CONSTRUCTOR)
+ return js_proxy_call_constructor(ctx, func_obj, this_obj, argc, argv);
+
+ s = get_proxy_method(ctx, &method, func_obj, JS_ATOM_apply);
+ if (!s)
+ return JS_EXCEPTION;
+ if (!s->is_func) {
+ JS_FreeValue(ctx, method);
+ return JS_ThrowTypeError(ctx, "not a function");
+ }
+ if (JS_IsUndefined(method))
+ return JS_Call(ctx, s->target, this_obj, argc, argv);
+ arg_array = js_create_array(ctx, argc, argv);
+ if (JS_IsException(arg_array)) {
+ ret = JS_EXCEPTION;
+ goto fail;
+ }
+ args[0] = s->target;
+ args[1] = this_obj;
+ args[2] = arg_array;
+ ret = JS_Call(ctx, method, s->handler, 3, args);
+ fail:
+ JS_FreeValue(ctx, method);
+ JS_FreeValue(ctx, arg_array);
+ return ret;
+}
+
+static int js_proxy_isArray(JSContext *ctx, JSValueConst obj)
+{
+ JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY);
+ if (!s)
+ return FALSE;
+ if (js_check_stack_overflow(ctx->rt, 0)) {
+ JS_ThrowStackOverflow(ctx);
+ return -1;
+ }
+ if (s->is_revoked) {
+ JS_ThrowTypeErrorRevokedProxy(ctx);
+ return -1;
+ }
+ return JS_IsArray(ctx, s->target);
+}
+
+static const JSClassExoticMethods js_proxy_exotic_methods = {
+ .get_own_property = js_proxy_get_own_property,
+ .define_own_property = js_proxy_define_own_property,
+ .delete_property = js_proxy_delete_property,
+ .get_own_property_names = js_proxy_get_own_property_names,
+ .has_property = js_proxy_has,
+ .get_property = js_proxy_get,
+ .set_property = js_proxy_set,
+};
+
+static JSValue js_proxy_constructor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst target, handler;
+ JSValue obj;
+ JSProxyData *s;
+
+ target = argv[0];
+ handler = argv[1];
+ if (JS_VALUE_GET_TAG(target) != JS_TAG_OBJECT ||
+ JS_VALUE_GET_TAG(handler) != JS_TAG_OBJECT)
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_PROXY);
+ if (JS_IsException(obj))
+ return obj;
+ s = js_malloc(ctx, sizeof(JSProxyData));
+ if (!s) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ s->target = JS_DupValue(ctx, target);
+ s->handler = JS_DupValue(ctx, handler);
+ s->is_func = JS_IsFunction(ctx, target);
+ s->is_revoked = FALSE;
+ JS_SetOpaque(obj, s);
+ JS_SetConstructorBit(ctx, obj, JS_IsConstructor(ctx, target));
+ return obj;
+}
+
+static JSValue js_proxy_revoke(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic,
+ JSValue *func_data)
+{
+ JSProxyData *s = JS_GetOpaque(func_data[0], JS_CLASS_PROXY);
+ if (s) {
+ /* We do not free the handler and target in case they are
+ referenced as constants in the C call stack */
+ s->is_revoked = TRUE;
+ JS_FreeValue(ctx, func_data[0]);
+ func_data[0] = JS_NULL;
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_proxy_revoke_constructor(JSContext *ctx,
+ JSValueConst proxy_obj)
+{
+ return JS_NewCFunctionData(ctx, js_proxy_revoke, 0, 0, 1, &proxy_obj);
+}
+
+static JSValue js_proxy_revocable(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue proxy_obj, revoke_obj = JS_UNDEFINED, obj;
+
+ proxy_obj = js_proxy_constructor(ctx, JS_UNDEFINED, argc, argv);
+ if (JS_IsException(proxy_obj))
+ goto fail;
+ revoke_obj = js_proxy_revoke_constructor(ctx, proxy_obj);
+ if (JS_IsException(revoke_obj))
+ goto fail;
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj))
+ goto fail;
+ // XXX: exceptions?
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_proxy, proxy_obj, JS_PROP_C_W_E);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_revoke, revoke_obj, JS_PROP_C_W_E);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, proxy_obj);
+ JS_FreeValue(ctx, revoke_obj);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_proxy_funcs[] = {
+ JS_CFUNC_DEF("revocable", 2, js_proxy_revocable ),
+};
+
+static const JSClassShortDef js_proxy_class_def[] = {
+ { JS_ATOM_Object, js_proxy_finalizer, js_proxy_mark }, /* JS_CLASS_PROXY */
+};
+
+void JS_AddIntrinsicProxy(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValue obj1;
+
+ if (!JS_IsRegisteredClass(rt, JS_CLASS_PROXY)) {
+ init_class_range(rt, js_proxy_class_def, JS_CLASS_PROXY,
+ countof(js_proxy_class_def));
+ rt->class_array[JS_CLASS_PROXY].exotic = &js_proxy_exotic_methods;
+ rt->class_array[JS_CLASS_PROXY].call = js_proxy_call;
+ }
+
+ obj1 = JS_NewCFunction2(ctx, js_proxy_constructor, "Proxy", 2,
+ JS_CFUNC_constructor, 0);
+ JS_SetConstructorBit(ctx, obj1, TRUE);
+ JS_SetPropertyFunctionList(ctx, obj1, js_proxy_funcs,
+ countof(js_proxy_funcs));
+ JS_DefinePropertyValueStr(ctx, ctx->global_obj, "Proxy",
+ obj1, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+}
+
+/* Symbol */
+
+static JSValue js_symbol_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ JSString *p;
+
+ if (!JS_IsUndefined(new_target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ if (argc == 0 || JS_IsUndefined(argv[0])) {
+ p = NULL;
+ } else {
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return JS_EXCEPTION;
+ p = JS_VALUE_GET_STRING(str);
+ }
+ return JS_NewSymbol(ctx, p, JS_ATOM_TYPE_SYMBOL);
+}
+
+static JSValue js_thisSymbolValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_SYMBOL)
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_SYMBOL) {
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_SYMBOL)
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a symbol");
+}
+
+static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ val = js_thisSymbolValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ /* XXX: use JS_ToStringInternal() with a flags */
+ ret = js_string_constructor(ctx, JS_UNDEFINED, 1, &val);
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static JSValue js_symbol_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisSymbolValue(ctx, this_val);
+}
+
+static JSValue js_symbol_get_description(JSContext *ctx, JSValueConst this_val)
+{
+ JSValue val, ret;
+ JSAtomStruct *p;
+
+ val = js_thisSymbolValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ p = JS_VALUE_GET_PTR(val);
+ if (p->len == 0 && p->is_wide_char != 0) {
+ ret = JS_UNDEFINED;
+ } else {
+ ret = JS_AtomToString(ctx, js_get_atom_index(ctx->rt, p));
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+}
+
+static const JSCFunctionListEntry js_symbol_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_symbol_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_symbol_valueOf ),
+ // XXX: should have writable: false
+ JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_symbol_valueOf ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Symbol", JS_PROP_CONFIGURABLE ),
+ JS_CGETSET_DEF("description", js_symbol_get_description, NULL ),
+};
+
+static JSValue js_symbol_for(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return JS_EXCEPTION;
+ return JS_NewSymbol(ctx, JS_VALUE_GET_STRING(str), JS_ATOM_TYPE_GLOBAL_SYMBOL);
+}
+
+static JSValue js_symbol_keyFor(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSAtomStruct *p;
+
+ if (!JS_IsSymbol(argv[0]))
+ return JS_ThrowTypeError(ctx, "not a symbol");
+ p = JS_VALUE_GET_PTR(argv[0]);
+ if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)
+ return JS_UNDEFINED;
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+}
+
+static const JSCFunctionListEntry js_symbol_funcs[] = {
+ JS_CFUNC_DEF("for", 1, js_symbol_for ),
+ JS_CFUNC_DEF("keyFor", 1, js_symbol_keyFor ),
+};
+
+/* Set/Map/WeakSet/WeakMap */
+
+typedef struct JSMapRecord {
+ int ref_count; /* used during enumeration to avoid freeing the record */
+ BOOL empty; /* TRUE if the record is deleted */
+ struct JSMapState *map;
+ struct JSMapRecord *next_weak_ref;
+ struct list_head link;
+ struct list_head hash_link;
+ JSValue key;
+ JSValue value;
+} JSMapRecord;
+
+typedef struct JSMapState {
+ BOOL is_weak; /* TRUE if WeakSet/WeakMap */
+ struct list_head records; /* list of JSMapRecord.link */
+ uint32_t record_count;
+ struct list_head *hash_table;
+ uint32_t hash_size; /* must be a power of two */
+ uint32_t record_count_threshold; /* count at which a hash table
+ resize is needed */
+} JSMapState;
+
+#define MAGIC_SET (1 << 0)
+#define MAGIC_WEAK (1 << 1)
+
+static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s;
+ JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
+ JSValueConst arr;
+ BOOL is_set, is_weak;
+
+ is_set = magic & MAGIC_SET;
+ is_weak = ((magic & MAGIC_WEAK) != 0);
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ goto fail;
+ init_list_head(&s->records);
+ s->is_weak = is_weak;
+ JS_SetOpaque(obj, s);
+ s->hash_size = 1;
+ s->hash_table = js_malloc(ctx, sizeof(s->hash_table[0]) * s->hash_size);
+ if (!s->hash_table)
+ goto fail;
+ init_list_head(&s->hash_table[0]);
+ s->record_count_threshold = 4;
+
+ arr = JS_UNDEFINED;
+ if (argc > 0)
+ arr = argv[0];
+ if (!JS_IsUndefined(arr) && !JS_IsNull(arr)) {
+ JSValue item, ret;
+ BOOL done;
+
+ adder = JS_GetProperty(ctx, obj, is_set ? JS_ATOM_add : JS_ATOM_set);
+ if (JS_IsException(adder))
+ goto fail;
+ if (!JS_IsFunction(ctx, adder)) {
+ JS_ThrowTypeError(ctx, "set/add is not a function");
+ goto fail;
+ }
+
+ iter = JS_GetIterator(ctx, arr, FALSE);
+ if (JS_IsException(iter))
+ goto fail;
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto fail;
+
+ for(;;) {
+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(item))
+ goto fail;
+ if (done) {
+ JS_FreeValue(ctx, item);
+ break;
+ }
+ if (is_set) {
+ ret = JS_Call(ctx, adder, obj, 1, &item);
+ if (JS_IsException(ret)) {
+ JS_FreeValue(ctx, item);
+ goto fail;
+ }
+ } else {
+ JSValue key, value;
+ JSValueConst args[2];
+ key = JS_UNDEFINED;
+ value = JS_UNDEFINED;
+ if (!JS_IsObject(item)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ goto fail1;
+ }
+ key = JS_GetPropertyUint32(ctx, item, 0);
+ if (JS_IsException(key))
+ goto fail1;
+ value = JS_GetPropertyUint32(ctx, item, 1);
+ if (JS_IsException(value))
+ goto fail1;
+ args[0] = key;
+ args[1] = value;
+ ret = JS_Call(ctx, adder, obj, 2, args);
+ if (JS_IsException(ret)) {
+ fail1:
+ JS_FreeValue(ctx, item);
+ JS_FreeValue(ctx, key);
+ JS_FreeValue(ctx, value);
+ goto fail;
+ }
+ JS_FreeValue(ctx, key);
+ JS_FreeValue(ctx, value);
+ }
+ JS_FreeValue(ctx, ret);
+ JS_FreeValue(ctx, item);
+ }
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, adder);
+ }
+ return obj;
+ fail:
+ if (JS_IsObject(iter)) {
+ /* close the iterator object, preserving pending exception */
+ JS_IteratorClose(ctx, iter, TRUE);
+ }
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, adder);
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+/* XXX: could normalize strings to speed up comparison */
+static JSValueConst map_normalize_key(JSContext *ctx, JSValueConst key)
+{
+ uint32_t tag = JS_VALUE_GET_TAG(key);
+ /* convert -0.0 to +0.0 */
+ if (JS_TAG_IS_FLOAT64(tag) && JS_VALUE_GET_FLOAT64(key) == 0.0) {
+ key = JS_NewInt32(ctx, 0);
+ }
+ return key;
+}
+
+/* XXX: better hash ? */
+static uint32_t map_hash_key(JSContext *ctx, JSValueConst key)
+{
+ uint32_t tag = JS_VALUE_GET_NORM_TAG(key);
+ uint32_t h;
+ double d;
+ JSFloat64Union u;
+
+ switch(tag) {
+ case JS_TAG_BOOL:
+ h = JS_VALUE_GET_INT(key);
+ break;
+ case JS_TAG_STRING:
+ h = hash_string(JS_VALUE_GET_STRING(key), 0);
+ break;
+ case JS_TAG_OBJECT:
+ case JS_TAG_SYMBOL:
+ h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163;
+ break;
+ case JS_TAG_INT:
+ d = JS_VALUE_GET_INT(key) * 3163;
+ goto hash_float64;
+ case JS_TAG_FLOAT64:
+ d = JS_VALUE_GET_FLOAT64(key);
+ /* normalize the NaN */
+ if (isnan(d))
+ d = JS_FLOAT64_NAN;
+ hash_float64:
+ u.d = d;
+ h = (u.u32[0] ^ u.u32[1]) * 3163;
+ break;
+ default:
+ h = 0; /* XXX: bignum support */
+ break;
+ }
+ h ^= tag;
+ return h;
+}
+
+static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
+ JSValueConst key)
+{
+ struct list_head *el;
+ JSMapRecord *mr;
+ uint32_t h;
+ h = map_hash_key(ctx, key) & (s->hash_size - 1);
+ list_for_each(el, &s->hash_table[h]) {
+ mr = list_entry(el, JSMapRecord, hash_link);
+ if (js_same_value_zero(ctx, mr->key, key))
+ return mr;
+ }
+ return NULL;
+}
+
+static void map_hash_resize(JSContext *ctx, JSMapState *s)
+{
+ uint32_t new_hash_size, i, h;
+ size_t slack;
+ struct list_head *new_hash_table, *el;
+ JSMapRecord *mr;
+
+ /* XXX: no reporting of memory allocation failure */
+ if (s->hash_size == 1)
+ new_hash_size = 4;
+ else
+ new_hash_size = s->hash_size * 2;
+ new_hash_table = js_realloc2(ctx, s->hash_table,
+ sizeof(new_hash_table[0]) * new_hash_size, &slack);
+ if (!new_hash_table)
+ return;
+ new_hash_size += slack / sizeof(*new_hash_table);
+
+ for(i = 0; i < new_hash_size; i++)
+ init_list_head(&new_hash_table[i]);
+
+ list_for_each(el, &s->records) {
+ mr = list_entry(el, JSMapRecord, link);
+ if (!mr->empty) {
+ h = map_hash_key(ctx, mr->key) & (new_hash_size - 1);
+ list_add_tail(&mr->hash_link, &new_hash_table[h]);
+ }
+ }
+ s->hash_table = new_hash_table;
+ s->hash_size = new_hash_size;
+ s->record_count_threshold = new_hash_size * 2;
+}
+
+static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
+ JSValueConst key)
+{
+ uint32_t h;
+ JSMapRecord *mr;
+
+ mr = js_malloc(ctx, sizeof(*mr));
+ if (!mr)
+ return NULL;
+ mr->ref_count = 1;
+ mr->map = s;
+ mr->empty = FALSE;
+ if (s->is_weak) {
+ JSObject *p = JS_VALUE_GET_OBJ(key);
+ /* Add the weak reference */
+ mr->next_weak_ref = p->first_weak_ref;
+ p->first_weak_ref = mr;
+ } else {
+ JS_DupValue(ctx, key);
+ }
+ mr->key = key;
+ h = map_hash_key(ctx, key) & (s->hash_size - 1);
+ list_add_tail(&mr->hash_link, &s->hash_table[h]);
+ list_add_tail(&mr->link, &s->records);
+ s->record_count++;
+ if (s->record_count >= s->record_count_threshold) {
+ map_hash_resize(ctx, s);
+ }
+ return mr;
+}
+
+/* Remove the weak reference from the object weak
+ reference list. we don't use a doubly linked list to
+ save space, assuming a given object has few weak
+ references to it */
+static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr)
+{
+ JSMapRecord **pmr, *mr1;
+ JSObject *p;
+
+ p = JS_VALUE_GET_OBJ(mr->key);
+ pmr = &p->first_weak_ref;
+ for(;;) {
+ mr1 = *pmr;
+ assert(mr1 != NULL);
+ if (mr1 == mr)
+ break;
+ pmr = &mr1->next_weak_ref;
+ }
+ *pmr = mr1->next_weak_ref;
+}
+
+static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
+{
+ if (mr->empty)
+ return;
+ list_del(&mr->hash_link);
+ if (s->is_weak) {
+ delete_weak_ref(rt, mr);
+ } else {
+ JS_FreeValueRT(rt, mr->key);
+ }
+ JS_FreeValueRT(rt, mr->value);
+ if (--mr->ref_count == 0) {
+ list_del(&mr->link);
+ js_free_rt(rt, mr);
+ } else {
+ /* keep a zombie record for iterators */
+ mr->empty = TRUE;
+ mr->key = JS_UNDEFINED;
+ mr->value = JS_UNDEFINED;
+ }
+ s->record_count--;
+}
+
+static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
+{
+ if (--mr->ref_count == 0) {
+ /* the record can be safely removed */
+ assert(mr->empty);
+ list_del(&mr->link);
+ js_free_rt(rt, mr);
+ }
+}
+
+static void reset_weak_ref(JSRuntime *rt, JSObject *p)
+{
+ JSMapRecord *mr, *mr_next;
+ JSMapState *s;
+
+ /* first pass to remove the records from the WeakMap/WeakSet
+ lists */
+ for(mr = p->first_weak_ref; mr != NULL; mr = mr->next_weak_ref) {
+ s = mr->map;
+ assert(s->is_weak);
+ assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
+ list_del(&mr->hash_link);
+ list_del(&mr->link);
+ }
+
+ /* second pass to free the values to avoid modifying the weak
+ reference list while traversing it. */
+ for(mr = p->first_weak_ref; mr != NULL; mr = mr_next) {
+ mr_next = mr->next_weak_ref;
+ JS_FreeValueRT(rt, mr->value);
+ js_free_rt(rt, mr);
+ }
+
+ p->first_weak_ref = NULL; /* fail safe */
+}
+
+static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ JSMapRecord *mr;
+ JSValueConst key, value;
+
+ if (!s)
+ return JS_EXCEPTION;
+ key = map_normalize_key(ctx, argv[0]);
+ if (s->is_weak && !JS_IsObject(key))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ if (magic & MAGIC_SET)
+ value = JS_UNDEFINED;
+ else
+ value = argv[1];
+ mr = map_find_record(ctx, s, key);
+ if (mr) {
+ JS_FreeValue(ctx, mr->value);
+ } else {
+ mr = map_add_record(ctx, s, key);
+ if (!mr)
+ return JS_EXCEPTION;
+ }
+ mr->value = JS_DupValue(ctx, value);
+ return JS_DupValue(ctx, this_val);
+}
+
+static JSValue js_map_get(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ JSMapRecord *mr;
+ JSValueConst key;
+
+ if (!s)
+ return JS_EXCEPTION;
+ key = map_normalize_key(ctx, argv[0]);
+ mr = map_find_record(ctx, s, key);
+ if (!mr)
+ return JS_UNDEFINED;
+ else
+ return JS_DupValue(ctx, mr->value);
+}
+
+static JSValue js_map_has(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ JSMapRecord *mr;
+ JSValueConst key;
+
+ if (!s)
+ return JS_EXCEPTION;
+ key = map_normalize_key(ctx, argv[0]);
+ mr = map_find_record(ctx, s, key);
+ return JS_NewBool(ctx, (mr != NULL));
+}
+
+static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ JSMapRecord *mr;
+ JSValueConst key;
+
+ if (!s)
+ return JS_EXCEPTION;
+ key = map_normalize_key(ctx, argv[0]);
+ mr = map_find_record(ctx, s, key);
+ if (!mr)
+ return JS_FALSE;
+ map_delete_record(ctx->rt, s, mr);
+ return JS_TRUE;
+}
+
+static JSValue js_map_clear(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ struct list_head *el, *el1;
+ JSMapRecord *mr;
+
+ if (!s)
+ return JS_EXCEPTION;
+ list_for_each_safe(el, el1, &s->records) {
+ mr = list_entry(el, JSMapRecord, link);
+ map_delete_record(ctx->rt, s, mr);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_map_get_size(JSContext *ctx, JSValueConst this_val, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ if (!s)
+ return JS_EXCEPTION;
+ return JS_NewUint32(ctx, s->record_count);
+}
+
+static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSMapState *s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ JSValueConst func, this_arg;
+ JSValue ret, args[3];
+ struct list_head *el;
+ JSMapRecord *mr;
+
+ if (!s)
+ return JS_EXCEPTION;
+ func = argv[0];
+ if (argc > 1)
+ this_arg = argv[1];
+ else
+ this_arg = JS_UNDEFINED;
+ if (check_function(ctx, func))
+ return JS_EXCEPTION;
+ /* Note: the list can be modified while traversing it, but the
+ current element is locked */
+ el = s->records.next;
+ while (el != &s->records) {
+ mr = list_entry(el, JSMapRecord, link);
+ if (!mr->empty) {
+ mr->ref_count++;
+ /* must duplicate in case the record is deleted */
+ args[1] = JS_DupValue(ctx, mr->key);
+ if (magic)
+ args[0] = args[1];
+ else
+ args[0] = JS_DupValue(ctx, mr->value);
+ args[2] = this_val;
+ ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
+ JS_FreeValue(ctx, args[0]);
+ if (!magic)
+ JS_FreeValue(ctx, args[1]);
+ el = el->next;
+ map_decref_record(ctx->rt, mr);
+ if (JS_IsException(ret))
+ return ret;
+ JS_FreeValue(ctx, ret);
+ } else {
+ el = el->next;
+ }
+ }
+ return JS_UNDEFINED;
+}
+
+static void js_map_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p;
+ JSMapState *s;
+ struct list_head *el, *el1;
+ JSMapRecord *mr;
+
+ p = JS_VALUE_GET_OBJ(val);
+ s = p->u.map_state;
+ if (s) {
+ /* if the object is deleted we are sure that no iterator is
+ using it */
+ list_for_each_safe(el, el1, &s->records) {
+ mr = list_entry(el, JSMapRecord, link);
+ if (!mr->empty) {
+ if (s->is_weak)
+ delete_weak_ref(rt, mr);
+ else
+ JS_FreeValueRT(rt, mr->key);
+ JS_FreeValueRT(rt, mr->value);
+ }
+ js_free_rt(rt, mr);
+ }
+ js_free_rt(rt, s->hash_table);
+ js_free_rt(rt, s);
+ }
+}
+
+static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSMapState *s;
+ struct list_head *el;
+ JSMapRecord *mr;
+
+ s = p->u.map_state;
+ if (s) {
+ list_for_each(el, &s->records) {
+ mr = list_entry(el, JSMapRecord, link);
+ if (!s->is_weak)
+ JS_MarkValue(rt, mr->key, mark_func);
+ JS_MarkValue(rt, mr->value, mark_func);
+ }
+ }
+}
+
+/* Map Iterator */
+
+typedef struct JSMapIteratorData {
+ JSValue obj;
+ JSIteratorKindEnum kind;
+ JSMapRecord *cur_record;
+} JSMapIteratorData;
+
+static void js_map_iterator_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p;
+ JSMapIteratorData *it;
+
+ p = JS_VALUE_GET_OBJ(val);
+ it = p->u.map_iterator_data;
+ if (it) {
+ /* During the GC sweep phase the Map finalizer may be
+ called before the Map iterator finalizer */
+ if (JS_IsLiveObject(rt, it->obj) && it->cur_record) {
+ map_decref_record(rt, it->cur_record);
+ }
+ JS_FreeValueRT(rt, it->obj);
+ js_free_rt(rt, it);
+ }
+}
+
+static void js_map_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSMapIteratorData *it;
+ it = p->u.map_iterator_data;
+ if (it) {
+ /* the record is already marked by the object */
+ JS_MarkValue(rt, it->obj, mark_func);
+ }
+}
+
+static JSValue js_create_map_iterator(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSIteratorKindEnum kind;
+ JSMapState *s;
+ JSMapIteratorData *it;
+ JSValue enum_obj;
+
+ kind = magic >> 2;
+ magic &= 3;
+ s = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP + magic);
+ if (!s)
+ return JS_EXCEPTION;
+ enum_obj = JS_NewObjectClass(ctx, JS_CLASS_MAP_ITERATOR + magic);
+ if (JS_IsException(enum_obj))
+ goto fail;
+ it = js_malloc(ctx, sizeof(*it));
+ if (!it) {
+ JS_FreeValue(ctx, enum_obj);
+ goto fail;
+ }
+ it->obj = JS_DupValue(ctx, this_val);
+ it->kind = kind;
+ it->cur_record = NULL;
+ JS_SetOpaque(enum_obj, it);
+ return enum_obj;
+ fail:
+ return JS_EXCEPTION;
+}
+
+static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ BOOL *pdone, int magic)
+{
+ JSMapIteratorData *it;
+ JSMapState *s;
+ JSMapRecord *mr;
+ struct list_head *el;
+
+ it = JS_GetOpaque2(ctx, this_val, JS_CLASS_MAP_ITERATOR + magic);
+ if (!it) {
+ *pdone = FALSE;
+ return JS_EXCEPTION;
+ }
+ if (JS_IsUndefined(it->obj))
+ goto done;
+ s = JS_GetOpaque(it->obj, JS_CLASS_MAP + magic);
+ assert(s != NULL);
+ if (!it->cur_record) {
+ el = s->records.next;
+ } else {
+ mr = it->cur_record;
+ el = mr->link.next;
+ map_decref_record(ctx->rt, mr); /* the record can be freed here */
+ }
+ for(;;) {
+ if (el == &s->records) {
+ /* no more record */
+ it->cur_record = NULL;
+ JS_FreeValue(ctx, it->obj);
+ it->obj = JS_UNDEFINED;
+ done:
+ /* end of enumeration */
+ *pdone = TRUE;
+ return JS_UNDEFINED;
+ }
+ mr = list_entry(el, JSMapRecord, link);
+ if (!mr->empty)
+ break;
+ /* get the next record */
+ el = mr->link.next;
+ }
+
+ /* lock the record so that it won't be freed */
+ mr->ref_count++;
+ it->cur_record = mr;
+ *pdone = FALSE;
+
+ if (it->kind == JS_ITERATOR_KIND_KEY) {
+ return JS_DupValue(ctx, mr->key);
+ } else {
+ JSValueConst args[2];
+ args[0] = mr->key;
+ if (magic)
+ args[1] = mr->key;
+ else
+ args[1] = mr->value;
+ if (it->kind == JS_ITERATOR_KIND_VALUE) {
+ return JS_DupValue(ctx, args[1]);
+ } else {
+ return js_create_array(ctx, 2, args);
+ }
+ }
+}
+
+static const JSCFunctionListEntry js_map_funcs[] = {
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+};
+
+static const JSCFunctionListEntry js_map_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, 0 ),
+ JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, 0 ),
+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, 0 ),
+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, 0 ),
+ JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, 0 ),
+ JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, 0),
+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, 0 ),
+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_VALUE << 2) | 0 ),
+ JS_CFUNC_MAGIC_DEF("keys", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | 0 ),
+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | 0 ),
+ JS_ALIAS_DEF("[Symbol.iterator]", "entries" ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_map_iterator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, 0 ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Map Iterator", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_set_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET ),
+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET ),
+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET ),
+ JS_CFUNC_MAGIC_DEF("clear", 0, js_map_clear, MAGIC_SET ),
+ JS_CGETSET_MAGIC_DEF("size", js_map_get_size, NULL, MAGIC_SET ),
+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_map_forEach, MAGIC_SET ),
+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY << 2) | MAGIC_SET ),
+ JS_ALIAS_DEF("keys", "values" ),
+ JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_map_iterator, (JS_ITERATOR_KIND_KEY_AND_VALUE << 2) | MAGIC_SET ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 0, js_map_iterator_next, MAGIC_SET ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_weak_map_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("set", 2, js_map_set, MAGIC_WEAK ),
+ JS_CFUNC_MAGIC_DEF("get", 1, js_map_get, MAGIC_WEAK ),
+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_WEAK ),
+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_WEAK ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakMap", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_weak_set_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("add", 1, js_map_set, MAGIC_SET | MAGIC_WEAK ),
+ JS_CFUNC_MAGIC_DEF("has", 1, js_map_has, MAGIC_SET | MAGIC_WEAK ),
+ JS_CFUNC_MAGIC_DEF("delete", 1, js_map_delete, MAGIC_SET | MAGIC_WEAK ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakSet", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
+ js_map_proto_funcs,
+ js_set_proto_funcs,
+ js_weak_map_proto_funcs,
+ js_weak_set_proto_funcs,
+ js_map_iterator_proto_funcs,
+ js_set_iterator_proto_funcs,
+};
+
+static const uint8_t js_map_proto_funcs_count[6] = {
+ countof(js_map_proto_funcs),
+ countof(js_set_proto_funcs),
+ countof(js_weak_map_proto_funcs),
+ countof(js_weak_set_proto_funcs),
+ countof(js_map_iterator_proto_funcs),
+ countof(js_set_iterator_proto_funcs),
+};
+
+void JS_AddIntrinsicMapSet(JSContext *ctx)
+{
+ int i;
+ JSValue obj1;
+ char buf[ATOM_GET_STR_BUF_SIZE];
+
+ for(i = 0; i < 4; i++) {
+ const char *name = JS_AtomGetStr(ctx, buf, sizeof(buf),
+ JS_ATOM_Map + i);
+ ctx->class_proto[JS_CLASS_MAP + i] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP + i],
+ js_map_proto_funcs_ptr[i],
+ js_map_proto_funcs_count[i]);
+ obj1 = JS_NewCFunctionMagic(ctx, js_map_constructor, name, 0,
+ JS_CFUNC_constructor_magic, i);
+ if (i < 2) {
+ JS_SetPropertyFunctionList(ctx, obj1, js_map_funcs,
+ countof(js_map_funcs));
+ }
+ JS_NewGlobalCConstructor2(ctx, obj1, name, ctx->class_proto[JS_CLASS_MAP + i]);
+ }
+
+ for(i = 0; i < 2; i++) {
+ ctx->class_proto[JS_CLASS_MAP_ITERATOR + i] =
+ JS_NewObjectProto(ctx, ctx->iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_MAP_ITERATOR + i],
+ js_map_proto_funcs_ptr[i + 4],
+ js_map_proto_funcs_count[i + 4]);
+ }
+}
+
+/* Generator */
+static const JSCFunctionListEntry js_generator_function_proto_funcs[] = {
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "GeneratorFunction", JS_PROP_CONFIGURABLE),
+};
+
+static const JSCFunctionListEntry js_generator_proto_funcs[] = {
+ JS_ITERATOR_NEXT_DEF("next", 1, js_generator_next, GEN_MAGIC_NEXT ),
+ JS_ITERATOR_NEXT_DEF("return", 1, js_generator_next, GEN_MAGIC_RETURN ),
+ JS_ITERATOR_NEXT_DEF("throw", 1, js_generator_next, GEN_MAGIC_THROW ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Generator", JS_PROP_CONFIGURABLE),
+};
+
+/* Promise */
+
+typedef enum JSPromiseStateEnum {
+ JS_PROMISE_PENDING,
+ JS_PROMISE_FULFILLED,
+ JS_PROMISE_REJECTED,
+} JSPromiseStateEnum;
+
+typedef struct JSPromiseData {
+ JSPromiseStateEnum promise_state;
+ /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */
+ struct list_head promise_reactions[2];
+ BOOL is_handled; /* Note: only useful to debug */
+ JSValue promise_result;
+} JSPromiseData;
+
+typedef struct JSPromiseFunctionDataResolved {
+ int ref_count;
+ BOOL already_resolved;
+} JSPromiseFunctionDataResolved;
+
+typedef struct JSPromiseFunctionData {
+ JSValue promise;
+ JSPromiseFunctionDataResolved *presolved;
+} JSPromiseFunctionData;
+
+typedef struct JSPromiseReactionData {
+ struct list_head link; /* not used in promise_reaction_job */
+ JSValue resolving_funcs[2];
+ JSValue handler;
+} JSPromiseReactionData;
+
+static int js_create_resolving_functions(JSContext *ctx, JSValue *args,
+ JSValueConst promise);
+
+static void promise_reaction_data_free(JSRuntime *rt,
+ JSPromiseReactionData *rd)
+{
+ JS_FreeValueRT(rt, rd->resolving_funcs[0]);
+ JS_FreeValueRT(rt, rd->resolving_funcs[1]);
+ JS_FreeValueRT(rt, rd->handler);
+ js_free_rt(rt, rd);
+}
+
+static JSValue promise_reaction_job(JSContext *ctx, int argc,
+ JSValueConst *argv)
+{
+ JSValueConst handler, arg, func;
+ JSValue res, res2;
+ BOOL is_reject;
+
+ assert(argc == 5);
+ handler = argv[2];
+ is_reject = JS_ToBool(ctx, argv[3]);
+ arg = argv[4];
+#ifdef DUMP_PROMISE
+ printf("promise_reaction_job: is_reject=%d\n", is_reject);
+#endif
+
+ if (JS_IsUndefined(handler)) {
+ if (is_reject) {
+ res = JS_Throw(ctx, JS_DupValue(ctx, arg));
+ } else {
+ res = JS_DupValue(ctx, arg);
+ }
+ } else {
+ res = JS_Call(ctx, handler, JS_UNDEFINED, 1, &arg);
+ }
+ is_reject = JS_IsException(res);
+ if (is_reject)
+ res = JS_GetException(ctx);
+ func = argv[is_reject];
+ /* as an extension, we support undefined as value to avoid
+ creating a dummy promise in the 'await' implementation of async
+ functions */
+ if (!JS_IsUndefined(func)) {
+ res2 = JS_Call(ctx, func, JS_UNDEFINED,
+ 1, &res);
+ } else {
+ res2 = JS_UNDEFINED;
+ }
+ JS_FreeValue(ctx, res);
+
+ return res2;
+}
+
+void JS_SetHostPromiseRejectionTracker(JSRuntime *rt,
+ JSHostPromiseRejectionTracker *cb,
+ void *opaque)
+{
+ rt->host_promise_rejection_tracker = cb;
+ rt->host_promise_rejection_tracker_opaque = opaque;
+}
+
+static void fulfill_or_reject_promise(JSContext *ctx, JSValueConst promise,
+ JSValueConst value, BOOL is_reject)
+{
+ JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
+ struct list_head *el, *el1;
+ JSPromiseReactionData *rd;
+ JSValueConst args[5];
+
+ if (!s || s->promise_state != JS_PROMISE_PENDING)
+ return; /* should never happen */
+ set_value(ctx, &s->promise_result, JS_DupValue(ctx, value));
+ s->promise_state = JS_PROMISE_FULFILLED + is_reject;
+#ifdef DUMP_PROMISE
+ printf("fulfill_or_reject_promise: is_reject=%d\n", is_reject);
+#endif
+ if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
+ JSRuntime *rt = ctx->rt;
+ if (rt->host_promise_rejection_tracker) {
+ rt->host_promise_rejection_tracker(ctx, promise, value, FALSE,
+ rt->host_promise_rejection_tracker_opaque);
+ }
+ }
+
+ list_for_each_safe(el, el1, &s->promise_reactions[is_reject]) {
+ rd = list_entry(el, JSPromiseReactionData, link);
+ args[0] = rd->resolving_funcs[0];
+ args[1] = rd->resolving_funcs[1];
+ args[2] = rd->handler;
+ args[3] = JS_NewBool(ctx, is_reject);
+ args[4] = value;
+ JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
+ list_del(&rd->link);
+ promise_reaction_data_free(ctx->rt, rd);
+ }
+
+ list_for_each_safe(el, el1, &s->promise_reactions[1 - is_reject]) {
+ rd = list_entry(el, JSPromiseReactionData, link);
+ list_del(&rd->link);
+ promise_reaction_data_free(ctx->rt, rd);
+ }
+}
+
+static void reject_promise(JSContext *ctx, JSValueConst promise,
+ JSValueConst value)
+{
+ fulfill_or_reject_promise(ctx, promise, value, TRUE);
+}
+
+static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst promise, thenable, then;
+ JSValue args[2], res;
+
+#ifdef DUMP_PROMISE
+ printf("js_promise_resolve_thenable_job\n");
+#endif
+ assert(argc == 3);
+ promise = argv[0];
+ thenable = argv[1];
+ then = argv[2];
+ if (js_create_resolving_functions(ctx, args, promise) < 0)
+ return JS_EXCEPTION;
+ res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);
+ if (JS_IsException(res)) {
+ JSValue error = JS_GetException(ctx);
+ res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ }
+ JS_FreeValue(ctx, args[0]);
+ JS_FreeValue(ctx, args[1]);
+ return res;
+}
+
+static void js_promise_resolve_function_free_resolved(JSRuntime *rt,
+ JSPromiseFunctionDataResolved *sr)
+{
+ if (--sr->ref_count == 0) {
+ js_free_rt(rt, sr);
+ }
+}
+
+static int js_create_resolving_functions(JSContext *ctx,
+ JSValue *resolving_funcs,
+ JSValueConst promise)
+
+{
+ JSValue obj;
+ JSPromiseFunctionData *s;
+ JSPromiseFunctionDataResolved *sr;
+ int i, ret;
+
+ sr = js_malloc(ctx, sizeof(*sr));
+ if (!sr)
+ return -1;
+ sr->ref_count = 1;
+ sr->already_resolved = FALSE; /* must be shared between the two functions */
+ ret = 0;
+ for(i = 0; i < 2; i++) {
+ obj = JS_NewObjectProtoClass(ctx, ctx->function_proto,
+ JS_CLASS_PROMISE_RESOLVE_FUNCTION + i);
+ if (JS_IsException(obj))
+ goto fail;
+ s = js_malloc(ctx, sizeof(*s));
+ if (!s) {
+ JS_FreeValue(ctx, obj);
+ fail:
+
+ if (i != 0)
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ ret = -1;
+ break;
+ }
+ sr->ref_count++;
+ s->presolved = sr;
+ s->promise = JS_DupValue(ctx, promise);
+ JS_SetOpaque(obj, s);
+ js_function_set_properties(ctx, obj, JS_ATOM_empty_string, 1);
+ resolving_funcs[i] = obj;
+ }
+ js_promise_resolve_function_free_resolved(ctx->rt, sr);
+ return ret;
+}
+
+static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
+ if (s) {
+ js_promise_resolve_function_free_resolved(rt, s->presolved);
+ JS_FreeValueRT(rt, s->promise);
+ js_free_rt(rt, s);
+ }
+}
+
+static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSPromiseFunctionData *s = JS_VALUE_GET_OBJ(val)->u.promise_function_data;
+ if (s) {
+ JS_MarkValue(rt, s->promise, mark_func);
+ }
+}
+
+static JSValue js_promise_resolve_function_call(JSContext *ctx,
+ JSValueConst func_obj,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int flags)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(func_obj);
+ JSPromiseFunctionData *s;
+ JSValueConst resolution, args[3];
+ JSValue then;
+ BOOL is_reject;
+
+ s = p->u.promise_function_data;
+ if (!s || s->presolved->already_resolved)
+ return JS_UNDEFINED;
+ s->presolved->already_resolved = TRUE;
+ is_reject = p->class_id - JS_CLASS_PROMISE_RESOLVE_FUNCTION;
+ if (argc > 0)
+ resolution = argv[0];
+ else
+ resolution = JS_UNDEFINED;
+#ifdef DUMP_PROMISE
+ printf("js_promise_resolving_function_call: is_reject=%d resolution=", is_reject);
+ JS_DumpValue(ctx, resolution);
+ printf("\n");
+#endif
+ if (is_reject || !JS_IsObject(resolution)) {
+ goto done;
+ } else if (js_same_value(ctx, resolution, s->promise)) {
+ JS_ThrowTypeError(ctx, "promise self resolution");
+ goto fail_reject;
+ }
+ then = JS_GetProperty(ctx, resolution, JS_ATOM_then);
+ if (JS_IsException(then)) {
+ JSValue error;
+ fail_reject:
+ error = JS_GetException(ctx);
+ reject_promise(ctx, s->promise, error);
+ JS_FreeValue(ctx, error);
+ } else if (!JS_IsFunction(ctx, then)) {
+ JS_FreeValue(ctx, then);
+ done:
+ fulfill_or_reject_promise(ctx, s->promise, resolution, is_reject);
+ } else {
+ args[0] = s->promise;
+ args[1] = resolution;
+ args[2] = then;
+ JS_EnqueueJob(ctx, js_promise_resolve_thenable_job, 3, args);
+ JS_FreeValue(ctx, then);
+ }
+ return JS_UNDEFINED;
+}
+
+static void js_promise_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
+ struct list_head *el, *el1;
+ int i;
+
+ if (!s)
+ return;
+ for(i = 0; i < 2; i++) {
+ list_for_each_safe(el, el1, &s->promise_reactions[i]) {
+ JSPromiseReactionData *rd =
+ list_entry(el, JSPromiseReactionData, link);
+ promise_reaction_data_free(rt, rd);
+ }
+ }
+ JS_FreeValueRT(rt, s->promise_result);
+ js_free_rt(rt, s);
+}
+
+static void js_promise_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSPromiseData *s = JS_GetOpaque(val, JS_CLASS_PROMISE);
+ struct list_head *el;
+ int i;
+
+ if (!s)
+ return;
+ for(i = 0; i < 2; i++) {
+ list_for_each(el, &s->promise_reactions[i]) {
+ JSPromiseReactionData *rd =
+ list_entry(el, JSPromiseReactionData, link);
+ JS_MarkValue(rt, rd->resolving_funcs[0], mark_func);
+ JS_MarkValue(rt, rd->resolving_funcs[1], mark_func);
+ JS_MarkValue(rt, rd->handler, mark_func);
+ }
+ }
+ JS_MarkValue(rt, s->promise_result, mark_func);
+}
+
+static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst executor;
+ JSValue obj;
+ JSPromiseData *s;
+ JSValue args[2], ret;
+ int i;
+
+ executor = argv[0];
+ if (check_function(ctx, executor))
+ return JS_EXCEPTION;
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_PROMISE);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s)
+ goto fail;
+ s->promise_state = JS_PROMISE_PENDING;
+ s->is_handled = FALSE;
+ for(i = 0; i < 2; i++)
+ init_list_head(&s->promise_reactions[i]);
+ s->promise_result = JS_UNDEFINED;
+ JS_SetOpaque(obj, s);
+ if (js_create_resolving_functions(ctx, args, obj))
+ goto fail;
+ ret = JS_Call(ctx, executor, JS_UNDEFINED, 2, (JSValueConst *)args);
+ if (JS_IsException(ret)) {
+ JSValue ret2, error;
+ error = JS_GetException(ctx);
+ ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret2))
+ goto fail1;
+ JS_FreeValue(ctx, ret2);
+ }
+ JS_FreeValue(ctx, ret);
+ JS_FreeValue(ctx, args[0]);
+ JS_FreeValue(ctx, args[1]);
+ return obj;
+ fail1:
+ JS_FreeValue(ctx, args[0]);
+ JS_FreeValue(ctx, args[1]);
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_promise_executor(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ int i;
+
+ for(i = 0; i < 2; i++) {
+ if (!JS_IsUndefined(func_data[i]))
+ return JS_ThrowTypeError(ctx, "resolving function already set");
+ func_data[i] = JS_DupValue(ctx, argv[i]);
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_promise_executor_new(JSContext *ctx)
+{
+ JSValueConst func_data[2];
+
+ func_data[0] = JS_UNDEFINED;
+ func_data[1] = JS_UNDEFINED;
+ return JS_NewCFunctionData(ctx, js_promise_executor, 2,
+ 0, 2, func_data);
+}
+
+static JSValue js_new_promise_capability(JSContext *ctx,
+ JSValue *resolving_funcs,
+ JSValueConst ctor)
+{
+ JSValue executor, result_promise;
+ JSCFunctionDataRecord *s;
+ int i;
+
+ executor = js_promise_executor_new(ctx);
+ if (JS_IsException(executor))
+ return executor;
+
+ if (JS_IsUndefined(ctor)) {
+ result_promise = js_promise_constructor(ctx, ctor, 1,
+ &executor);
+ } else {
+ result_promise = JS_CallConstructor(ctx, ctor, 1,
+ &executor);
+ }
+ if (JS_IsException(result_promise))
+ goto fail;
+ s = JS_GetOpaque(executor, JS_CLASS_C_FUNCTION_DATA);
+ for(i = 0; i < 2; i++) {
+ if (check_function(ctx, s->data[i]))
+ goto fail;
+ }
+ for(i = 0; i < 2; i++)
+ resolving_funcs[i] = JS_DupValue(ctx, s->data[i]);
+ JS_FreeValue(ctx, executor);
+ return result_promise;
+ fail:
+ JS_FreeValue(ctx, executor);
+ JS_FreeValue(ctx, result_promise);
+ return JS_EXCEPTION;
+}
+
+JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs)
+{
+ return js_new_promise_capability(ctx, resolving_funcs, JS_UNDEFINED);
+}
+
+static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue result_promise, resolving_funcs[2], ret;
+ BOOL is_reject = magic;
+
+ if (!JS_IsObject(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ if (!is_reject && JS_GetOpaque(argv[0], JS_CLASS_PROMISE)) {
+ JSValue ctor;
+ BOOL is_same;
+ ctor = JS_GetProperty(ctx, argv[0], JS_ATOM_constructor);
+ if (JS_IsException(ctor))
+ return ctor;
+ is_same = js_same_value(ctx, ctor, this_val);
+ JS_FreeValue(ctx, ctor);
+ if (is_same)
+ return JS_DupValue(ctx, argv[0]);
+ }
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
+ if (JS_IsException(result_promise))
+ return result_promise;
+ ret = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, 1, argv);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ if (JS_IsException(ret)) {
+ JS_FreeValue(ctx, result_promise);
+ return ret;
+ }
+ JS_FreeValue(ctx, ret);
+ return result_promise;
+}
+
+#if 0
+static JSValue js_promise___newPromiseCapability(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue result_promise, resolving_funcs[2], obj;
+ JSValueConst ctor;
+ ctor = argv[0];
+ if (!JS_IsObject(ctor))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
+ if (JS_IsException(result_promise))
+ return result_promise;
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj)) {
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ JS_FreeValue(ctx, result_promise);
+ return JS_EXCEPTION;
+ }
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_promise, result_promise, JS_PROP_C_W_E);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_resolve, resolving_funcs[0], JS_PROP_C_W_E);
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E);
+ return obj;
+}
+#endif
+
+static warn_unused int remainingElementsCount_add(JSContext *ctx,
+ JSValueConst resolve_element_env,
+ int addend)
+{
+ JSValue val;
+ int remainingElementsCount;
+
+ val = JS_GetPropertyUint32(ctx, resolve_element_env, 0);
+ if (JS_IsException(val))
+ return -1;
+ if (JS_ToInt32Free(ctx, &remainingElementsCount, val))
+ return -1;
+ remainingElementsCount += addend;
+ if (JS_SetPropertyUint32(ctx, resolve_element_env, 0,
+ JS_NewInt32(ctx, remainingElementsCount)) < 0)
+ return -1;
+ return (remainingElementsCount == 0);
+}
+
+#define PROMISE_MAGIC_all 0
+#define PROMISE_MAGIC_allSettled 1
+#define PROMISE_MAGIC_any 2
+
+static JSValue js_promise_all_resolve_element(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic,
+ JSValue *func_data)
+{
+ int resolve_type = magic & 3;
+ int is_reject = magic & 4;
+ BOOL alreadyCalled = JS_ToBool(ctx, func_data[0]);
+ JSValueConst values = func_data[2];
+ JSValueConst resolve = func_data[3];
+ JSValueConst resolve_element_env = func_data[4];
+ JSValue ret, obj;
+ int is_zero, index;
+
+ if (JS_ToInt32(ctx, &index, func_data[1]))
+ return JS_EXCEPTION;
+ if (alreadyCalled)
+ return JS_UNDEFINED;
+ func_data[0] = JS_NewBool(ctx, TRUE);
+
+ if (resolve_type == PROMISE_MAGIC_allSettled) {
+ JSValue str;
+
+ obj = JS_NewObject(ctx);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ str = JS_NewString(ctx, is_reject ? "rejected" : "fulfilled");
+ if (JS_IsException(str))
+ goto fail1;
+ if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_status,
+ str,
+ JS_PROP_C_W_E) < 0)
+ goto fail1;
+ if (JS_DefinePropertyValue(ctx, obj,
+ is_reject ? JS_ATOM_reason : JS_ATOM_value,
+ JS_DupValue(ctx, argv[0]),
+ JS_PROP_C_W_E) < 0) {
+ fail1:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ } else {
+ obj = JS_DupValue(ctx, argv[0]);
+ }
+ if (JS_DefinePropertyValueUint32(ctx, values, index,
+ obj, JS_PROP_C_W_E) < 0)
+ return JS_EXCEPTION;
+
+ is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
+ if (is_zero < 0)
+ return JS_EXCEPTION;
+ if (is_zero) {
+ if (resolve_type == PROMISE_MAGIC_any) {
+ JSValue error;
+ error = js_aggregate_error_constructor(ctx, values);
+ if (JS_IsException(error))
+ return JS_EXCEPTION;
+ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ } else {
+ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &values);
+ }
+ if (JS_IsException(ret))
+ return ret;
+ JS_FreeValue(ctx, ret);
+ }
+ return JS_UNDEFINED;
+}
+
+/* magic = 0: Promise.all 1: Promise.allSettled */
+static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
+ JSValue next_method = JS_UNDEFINED, values = JS_UNDEFINED;
+ JSValue resolve_element_env = JS_UNDEFINED, resolve_element, reject_element;
+ JSValue promise_resolve = JS_UNDEFINED, iter = JS_UNDEFINED;
+ JSValueConst then_args[2], resolve_element_data[5];
+ BOOL done;
+ int index, is_zero, is_promise_any = (magic == PROMISE_MAGIC_any);
+
+ if (!JS_IsObject(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
+ if (JS_IsException(result_promise))
+ return result_promise;
+ promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
+ if (JS_IsException(promise_resolve) ||
+ check_function(ctx, promise_resolve))
+ goto fail_reject;
+ iter = JS_GetIterator(ctx, argv[0], FALSE);
+ if (JS_IsException(iter)) {
+ JSValue error;
+ fail_reject:
+ error = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
+ &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret))
+ goto fail;
+ JS_FreeValue(ctx, ret);
+ } else {
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto fail_reject;
+ values = JS_NewArray(ctx);
+ if (JS_IsException(values))
+ goto fail_reject;
+ resolve_element_env = JS_NewArray(ctx);
+ if (JS_IsException(resolve_element_env))
+ goto fail_reject;
+ /* remainingElementsCount field */
+ if (JS_DefinePropertyValueUint32(ctx, resolve_element_env, 0,
+ JS_NewInt32(ctx, 1),
+ JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE | JS_PROP_WRITABLE) < 0)
+ goto fail_reject;
+
+ index = 0;
+ for(;;) {
+ /* XXX: conformance: should close the iterator if error on 'done'
+ access, but not on 'value' access */
+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(item))
+ goto fail_reject;
+ if (done)
+ break;
+ next_promise = JS_Call(ctx, promise_resolve,
+ this_val, 1, &item);
+ JS_FreeValue(ctx, item);
+ if (JS_IsException(next_promise)) {
+ fail_reject1:
+ JS_IteratorClose(ctx, iter, TRUE);
+ goto fail_reject;
+ }
+ resolve_element_data[0] = JS_NewBool(ctx, FALSE);
+ resolve_element_data[1] = JS_NewInt32(ctx, index);
+ resolve_element_data[2] = values;
+ resolve_element_data[3] = resolving_funcs[is_promise_any];
+ resolve_element_data[4] = resolve_element_env;
+ resolve_element =
+ JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
+ magic, 5, resolve_element_data);
+ if (JS_IsException(resolve_element)) {
+ JS_FreeValue(ctx, next_promise);
+ goto fail_reject1;
+ }
+
+ if (magic == PROMISE_MAGIC_allSettled) {
+ reject_element =
+ JS_NewCFunctionData(ctx, js_promise_all_resolve_element, 1,
+ magic | 4, 5, resolve_element_data);
+ if (JS_IsException(reject_element)) {
+ JS_FreeValue(ctx, next_promise);
+ goto fail_reject1;
+ }
+ } else if (magic == PROMISE_MAGIC_any) {
+ if (JS_DefinePropertyValueUint32(ctx, values, index,
+ JS_UNDEFINED, JS_PROP_C_W_E) < 0)
+ goto fail_reject1;
+ reject_element = resolve_element;
+ resolve_element = JS_DupValue(ctx, resolving_funcs[0]);
+ } else {
+ reject_element = JS_DupValue(ctx, resolving_funcs[1]);
+ }
+
+ if (remainingElementsCount_add(ctx, resolve_element_env, 1) < 0) {
+ JS_FreeValue(ctx, next_promise);
+ JS_FreeValue(ctx, resolve_element);
+ JS_FreeValue(ctx, reject_element);
+ goto fail_reject1;
+ }
+
+ then_args[0] = resolve_element;
+ then_args[1] = reject_element;
+ ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2, then_args);
+ JS_FreeValue(ctx, resolve_element);
+ JS_FreeValue(ctx, reject_element);
+ if (check_exception_free(ctx, ret))
+ goto fail_reject1;
+ index++;
+ }
+
+ is_zero = remainingElementsCount_add(ctx, resolve_element_env, -1);
+ if (is_zero < 0)
+ goto fail_reject;
+ if (is_zero) {
+ if (magic == PROMISE_MAGIC_any) {
+ JSValue error;
+ error = js_aggregate_error_constructor(ctx, values);
+ if (JS_IsException(error))
+ goto fail_reject;
+ JS_FreeValue(ctx, values);
+ values = error;
+ }
+ ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,
+ 1, &values);
+ if (check_exception_free(ctx, ret))
+ goto fail_reject;
+ }
+ }
+ done:
+ JS_FreeValue(ctx, promise_resolve);
+ JS_FreeValue(ctx, resolve_element_env);
+ JS_FreeValue(ctx, values);
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return result_promise;
+ fail:
+ JS_FreeValue(ctx, result_promise);
+ result_promise = JS_EXCEPTION;
+ goto done;
+}
+
+static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue result_promise, resolving_funcs[2], item, next_promise, ret;
+ JSValue next_method = JS_UNDEFINED, iter = JS_UNDEFINED;
+ JSValue promise_resolve = JS_UNDEFINED;
+ BOOL done;
+
+ if (!JS_IsObject(this_val))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val);
+ if (JS_IsException(result_promise))
+ return result_promise;
+ promise_resolve = JS_GetProperty(ctx, this_val, JS_ATOM_resolve);
+ if (JS_IsException(promise_resolve) ||
+ check_function(ctx, promise_resolve))
+ goto fail_reject;
+ iter = JS_GetIterator(ctx, argv[0], FALSE);
+ if (JS_IsException(iter)) {
+ JSValue error;
+ fail_reject:
+ error = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
+ &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret))
+ goto fail;
+ JS_FreeValue(ctx, ret);
+ } else {
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto fail_reject;
+
+ for(;;) {
+ /* XXX: conformance: should close the iterator if error on 'done'
+ access, but not on 'value' access */
+ item = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(item))
+ goto fail_reject;
+ if (done)
+ break;
+ next_promise = JS_Call(ctx, promise_resolve,
+ this_val, 1, &item);
+ JS_FreeValue(ctx, item);
+ if (JS_IsException(next_promise)) {
+ fail_reject1:
+ JS_IteratorClose(ctx, iter, TRUE);
+ goto fail_reject;
+ }
+ ret = JS_InvokeFree(ctx, next_promise, JS_ATOM_then, 2,
+ (JSValueConst *)resolving_funcs);
+ if (check_exception_free(ctx, ret))
+ goto fail_reject1;
+ }
+ }
+ done:
+ JS_FreeValue(ctx, promise_resolve);
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return result_promise;
+ fail:
+ //JS_FreeValue(ctx, next_method); // why not???
+ JS_FreeValue(ctx, result_promise);
+ result_promise = JS_EXCEPTION;
+ goto done;
+}
+
+static warn_unused int perform_promise_then(JSContext *ctx,
+ JSValueConst promise,
+ JSValueConst *resolve_reject,
+ JSValueConst *cap_resolving_funcs)
+{
+ JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE);
+ JSPromiseReactionData *rd_array[2], *rd;
+ int i, j;
+
+ rd_array[0] = NULL;
+ rd_array[1] = NULL;
+ for(i = 0; i < 2; i++) {
+ JSValueConst handler;
+ rd = js_mallocz(ctx, sizeof(*rd));
+ if (!rd) {
+ if (i == 1)
+ promise_reaction_data_free(ctx->rt, rd_array[0]);
+ return -1;
+ }
+ for(j = 0; j < 2; j++)
+ rd->resolving_funcs[j] = JS_DupValue(ctx, cap_resolving_funcs[j]);
+ handler = resolve_reject[i];
+ if (!JS_IsFunction(ctx, handler))
+ handler = JS_UNDEFINED;
+ rd->handler = JS_DupValue(ctx, handler);
+ rd_array[i] = rd;
+ }
+
+ if (s->promise_state == JS_PROMISE_PENDING) {
+ for(i = 0; i < 2; i++)
+ list_add_tail(&rd_array[i]->link, &s->promise_reactions[i]);
+ } else {
+ JSValueConst args[5];
+ if (s->promise_state == JS_PROMISE_REJECTED && !s->is_handled) {
+ JSRuntime *rt = ctx->rt;
+ if (rt->host_promise_rejection_tracker) {
+ rt->host_promise_rejection_tracker(ctx, promise, s->promise_result,
+ TRUE, rt->host_promise_rejection_tracker_opaque);
+ }
+ }
+ i = s->promise_state - JS_PROMISE_FULFILLED;
+ rd = rd_array[i];
+ args[0] = rd->resolving_funcs[0];
+ args[1] = rd->resolving_funcs[1];
+ args[2] = rd->handler;
+ args[3] = JS_NewBool(ctx, i);
+ args[4] = s->promise_result;
+ JS_EnqueueJob(ctx, promise_reaction_job, 5, args);
+ for(i = 0; i < 2; i++)
+ promise_reaction_data_free(ctx->rt, rd_array[i]);
+ }
+ s->is_handled = TRUE;
+ return 0;
+}
+
+static JSValue js_promise_then(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue ctor, result_promise, resolving_funcs[2];
+ JSPromiseData *s;
+ int i, ret;
+
+ s = JS_GetOpaque2(ctx, this_val, JS_CLASS_PROMISE);
+ if (!s)
+ return JS_EXCEPTION;
+
+ ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
+ if (JS_IsException(ctor))
+ return ctor;
+ result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor);
+ JS_FreeValue(ctx, ctor);
+ if (JS_IsException(result_promise))
+ return result_promise;
+ ret = perform_promise_then(ctx, this_val, argv,
+ (JSValueConst *)resolving_funcs);
+ for(i = 0; i < 2; i++)
+ JS_FreeValue(ctx, resolving_funcs[i]);
+ if (ret) {
+ JS_FreeValue(ctx, result_promise);
+ return JS_EXCEPTION;
+ }
+ return result_promise;
+}
+
+static JSValue js_promise_catch(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst args[2];
+ args[0] = JS_UNDEFINED;
+ args[1] = argv[0];
+ return JS_Invoke(ctx, this_val, JS_ATOM_then, 2, args);
+}
+
+static JSValue js_promise_finally_value_thunk(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ return JS_DupValue(ctx, func_data[0]);
+}
+
+static JSValue js_promise_finally_thrower(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ return JS_Throw(ctx, JS_DupValue(ctx, func_data[0]));
+}
+
+static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ JSValueConst ctor = func_data[0];
+ JSValueConst onFinally = func_data[1];
+ JSValue res, promise, ret, then_func;
+
+ res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);
+ if (JS_IsException(res))
+ return res;
+ promise = js_promise_resolve(ctx, ctor, 1, &res, 0);
+ JS_FreeValue(ctx, res);
+ if (JS_IsException(promise))
+ return promise;
+ if (magic == 0) {
+ then_func = JS_NewCFunctionData(ctx, js_promise_finally_value_thunk, 0,
+ 0, 1, argv);
+ } else {
+ then_func = JS_NewCFunctionData(ctx, js_promise_finally_thrower, 0,
+ 0, 1, argv);
+ }
+ if (JS_IsException(then_func)) {
+ JS_FreeValue(ctx, promise);
+ return then_func;
+ }
+ ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, &then_func);
+ JS_FreeValue(ctx, then_func);
+ return ret;
+}
+
+static JSValue js_promise_finally(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst onFinally = argv[0];
+ JSValue ctor, ret;
+ JSValue then_funcs[2];
+ JSValueConst func_data[2];
+ int i;
+
+ ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
+ if (JS_IsException(ctor))
+ return ctor;
+ if (!JS_IsFunction(ctx, onFinally)) {
+ then_funcs[0] = JS_DupValue(ctx, onFinally);
+ then_funcs[1] = JS_DupValue(ctx, onFinally);
+ } else {
+ func_data[0] = ctor;
+ func_data[1] = onFinally;
+ for(i = 0; i < 2; i++) {
+ then_funcs[i] = JS_NewCFunctionData(ctx, js_promise_then_finally_func, 1, i, 2, func_data);
+ if (JS_IsException(then_funcs[i])) {
+ if (i == 1)
+ JS_FreeValue(ctx, then_funcs[0]);
+ JS_FreeValue(ctx, ctor);
+ return JS_EXCEPTION;
+ }
+ }
+ }
+ JS_FreeValue(ctx, ctor);
+ ret = JS_Invoke(ctx, this_val, JS_ATOM_then, 2, (JSValueConst *)then_funcs);
+ JS_FreeValue(ctx, then_funcs[0]);
+ JS_FreeValue(ctx, then_funcs[1]);
+ return ret;
+}
+
+static const JSCFunctionListEntry js_promise_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("resolve", 1, js_promise_resolve, 0 ),
+ JS_CFUNC_MAGIC_DEF("reject", 1, js_promise_resolve, 1 ),
+ JS_CFUNC_MAGIC_DEF("all", 1, js_promise_all, PROMISE_MAGIC_all ),
+ JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ),
+ JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ),
+ JS_CFUNC_DEF("race", 1, js_promise_race ),
+ //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ),
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL),
+};
+
+static const JSCFunctionListEntry js_promise_proto_funcs[] = {
+ JS_CFUNC_DEF("then", 2, js_promise_then ),
+ JS_CFUNC_DEF("catch", 1, js_promise_catch ),
+ JS_CFUNC_DEF("finally", 1, js_promise_finally ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Promise", JS_PROP_CONFIGURABLE ),
+};
+
+/* AsyncFunction */
+static const JSCFunctionListEntry js_async_function_proto_funcs[] = {
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncFunction", JS_PROP_CONFIGURABLE ),
+};
+
+static JSValue js_async_from_sync_iterator_unwrap(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic, JSValue *func_data)
+{
+ return js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]),
+ JS_ToBool(ctx, func_data[0]));
+}
+
+static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
+ BOOL done)
+{
+ JSValueConst func_data[1];
+
+ func_data[0] = JS_NewBool(ctx, done);
+ return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,
+ 1, 0, 1, func_data);
+}
+
+/* AsyncIteratorPrototype */
+
+static const JSCFunctionListEntry js_async_iterator_proto_funcs[] = {
+ JS_CFUNC_DEF("[Symbol.asyncIterator]", 0, js_iterator_proto_iterator ),
+};
+
+/* AsyncFromSyncIteratorPrototype */
+
+typedef struct JSAsyncFromSyncIteratorData {
+ JSValue sync_iter;
+ JSValue next_method;
+} JSAsyncFromSyncIteratorData;
+
+static void js_async_from_sync_iterator_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSAsyncFromSyncIteratorData *s =
+ JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
+ if (s) {
+ JS_FreeValueRT(rt, s->sync_iter);
+ JS_FreeValueRT(rt, s->next_method);
+ js_free_rt(rt, s);
+ }
+}
+
+static void js_async_from_sync_iterator_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSAsyncFromSyncIteratorData *s =
+ JS_GetOpaque(val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
+ if (s) {
+ JS_MarkValue(rt, s->sync_iter, mark_func);
+ JS_MarkValue(rt, s->next_method, mark_func);
+ }
+}
+
+static JSValue JS_CreateAsyncFromSyncIterator(JSContext *ctx,
+ JSValueConst sync_iter)
+{
+ JSValue async_iter, next_method;
+ JSAsyncFromSyncIteratorData *s;
+
+ next_method = JS_GetProperty(ctx, sync_iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ return JS_EXCEPTION;
+ async_iter = JS_NewObjectClass(ctx, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
+ if (JS_IsException(async_iter)) {
+ JS_FreeValue(ctx, next_method);
+ return async_iter;
+ }
+ s = js_mallocz(ctx, sizeof(*s));
+ if (!s) {
+ JS_FreeValue(ctx, async_iter);
+ JS_FreeValue(ctx, next_method);
+ return JS_EXCEPTION;
+ }
+ s->sync_iter = JS_DupValue(ctx, sync_iter);
+ s->next_method = next_method;
+ JS_SetOpaque(async_iter, s);
+ return async_iter;
+}
+
+static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic)
+{
+ JSValue promise, resolving_funcs[2], value, err, method;
+ JSAsyncFromSyncIteratorData *s;
+ int done;
+ int is_reject;
+
+ promise = JS_NewPromiseCapability(ctx, resolving_funcs);
+ if (JS_IsException(promise))
+ return JS_EXCEPTION;
+ s = JS_GetOpaque(this_val, JS_CLASS_ASYNC_FROM_SYNC_ITERATOR);
+ if (!s) {
+ JS_ThrowTypeError(ctx, "not an Async-from-Sync Iterator");
+ goto reject;
+ }
+
+ if (magic == GEN_MAGIC_NEXT) {
+ method = JS_DupValue(ctx, s->next_method);
+ } else {
+ method = JS_GetProperty(ctx, s->sync_iter,
+ magic == GEN_MAGIC_RETURN ? JS_ATOM_return :
+ JS_ATOM_throw);
+ if (JS_IsException(method))
+ goto reject;
+ if (JS_IsUndefined(method) || JS_IsNull(method)) {
+ if (magic == GEN_MAGIC_RETURN) {
+ err = js_create_iterator_result(ctx, JS_DupValue(ctx, argv[0]), TRUE);
+ is_reject = 0;
+ } else {
+ err = JS_DupValue(ctx, argv[0]);
+ is_reject = 1;
+ }
+ goto done_resolve;
+ }
+ }
+ value = JS_IteratorNext2(ctx, s->sync_iter, method,
+ argc >= 1 ? 1 : 0, argv, &done);
+ JS_FreeValue(ctx, method);
+ if (JS_IsException(value))
+ goto reject;
+ if (done == 2) {
+ JSValue obj = value;
+ value = JS_IteratorGetCompleteValue(ctx, obj, &done);
+ JS_FreeValue(ctx, obj);
+ if (JS_IsException(value))
+ goto reject;
+ }
+
+ if (JS_IsException(value)) {
+ JSValue res2;
+ reject:
+ err = JS_GetException(ctx);
+ is_reject = 1;
+ done_resolve:
+ res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,
+ 1, &err);
+ JS_FreeValue(ctx, err);
+ JS_FreeValue(ctx, res2);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ return promise;
+ }
+ {
+ JSValue value_wrapper_promise, resolve_reject[2];
+ int res;
+
+ value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
+ 1, &value, 0);
+ if (JS_IsException(value_wrapper_promise)) {
+ JS_FreeValue(ctx, value);
+ goto reject;
+ }
+
+ resolve_reject[0] =
+ js_async_from_sync_iterator_unwrap_func_create(ctx, done);
+ if (JS_IsException(resolve_reject[0])) {
+ JS_FreeValue(ctx, value_wrapper_promise);
+ goto fail;
+ }
+ JS_FreeValue(ctx, value);
+ resolve_reject[1] = JS_UNDEFINED;
+
+ res = perform_promise_then(ctx, value_wrapper_promise,
+ (JSValueConst *)resolve_reject,
+ (JSValueConst *)resolving_funcs);
+ JS_FreeValue(ctx, resolve_reject[0]);
+ JS_FreeValue(ctx, value_wrapper_promise);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ if (res) {
+ JS_FreeValue(ctx, promise);
+ return JS_EXCEPTION;
+ }
+ }
+ return promise;
+ fail:
+ JS_FreeValue(ctx, value);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+ JS_FreeValue(ctx, resolving_funcs[1]);
+ JS_FreeValue(ctx, promise);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_async_from_sync_iterator_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("next", 1, js_async_from_sync_iterator_next, GEN_MAGIC_NEXT ),
+ JS_CFUNC_MAGIC_DEF("return", 1, js_async_from_sync_iterator_next, GEN_MAGIC_RETURN ),
+ JS_CFUNC_MAGIC_DEF("throw", 1, js_async_from_sync_iterator_next, GEN_MAGIC_THROW ),
+};
+
+/* AsyncGeneratorFunction */
+
+static const JSCFunctionListEntry js_async_generator_function_proto_funcs[] = {
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGeneratorFunction", JS_PROP_CONFIGURABLE ),
+};
+
+/* AsyncGenerator prototype */
+
+static const JSCFunctionListEntry js_async_generator_proto_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("next", 1, js_async_generator_next, GEN_MAGIC_NEXT ),
+ JS_CFUNC_MAGIC_DEF("return", 1, js_async_generator_next, GEN_MAGIC_RETURN ),
+ JS_CFUNC_MAGIC_DEF("throw", 1, js_async_generator_next, GEN_MAGIC_THROW ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "AsyncGenerator", JS_PROP_CONFIGURABLE ),
+};
+
+static JSClassShortDef const js_async_class_def[] = {
+ { JS_ATOM_Promise, js_promise_finalizer, js_promise_mark }, /* JS_CLASS_PROMISE */
+ { JS_ATOM_PromiseResolveFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_RESOLVE_FUNCTION */
+ { JS_ATOM_PromiseRejectFunction, js_promise_resolve_function_finalizer, js_promise_resolve_function_mark }, /* JS_CLASS_PROMISE_REJECT_FUNCTION */
+ { JS_ATOM_AsyncFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_FUNCTION */
+ { JS_ATOM_AsyncFunctionResolve, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_RESOLVE */
+ { JS_ATOM_AsyncFunctionReject, js_async_function_resolve_finalizer, js_async_function_resolve_mark }, /* JS_CLASS_ASYNC_FUNCTION_REJECT */
+ { JS_ATOM_empty_string, js_async_from_sync_iterator_finalizer, js_async_from_sync_iterator_mark }, /* JS_CLASS_ASYNC_FROM_SYNC_ITERATOR */
+ { JS_ATOM_AsyncGeneratorFunction, js_bytecode_function_finalizer, js_bytecode_function_mark }, /* JS_CLASS_ASYNC_GENERATOR_FUNCTION */
+ { JS_ATOM_AsyncGenerator, js_async_generator_finalizer, js_async_generator_mark }, /* JS_CLASS_ASYNC_GENERATOR */
+};
+
+void JS_AddIntrinsicPromise(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValue obj1;
+
+ if (!JS_IsRegisteredClass(rt, JS_CLASS_PROMISE)) {
+ init_class_range(rt, js_async_class_def, JS_CLASS_PROMISE,
+ countof(js_async_class_def));
+ rt->class_array[JS_CLASS_PROMISE_RESOLVE_FUNCTION].call = js_promise_resolve_function_call;
+ rt->class_array[JS_CLASS_PROMISE_REJECT_FUNCTION].call = js_promise_resolve_function_call;
+ rt->class_array[JS_CLASS_ASYNC_FUNCTION].call = js_async_function_call;
+ rt->class_array[JS_CLASS_ASYNC_FUNCTION_RESOLVE].call = js_async_function_resolve_call;
+ rt->class_array[JS_CLASS_ASYNC_FUNCTION_REJECT].call = js_async_function_resolve_call;
+ rt->class_array[JS_CLASS_ASYNC_GENERATOR_FUNCTION].call = js_async_generator_function_call;
+ }
+
+ /* Promise */
+ ctx->class_proto[JS_CLASS_PROMISE] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_PROMISE],
+ js_promise_proto_funcs,
+ countof(js_promise_proto_funcs));
+ obj1 = JS_NewCFunction2(ctx, js_promise_constructor, "Promise", 1,
+ JS_CFUNC_constructor, 0);
+ ctx->promise_ctor = JS_DupValue(ctx, obj1);
+ JS_SetPropertyFunctionList(ctx, obj1,
+ js_promise_funcs,
+ countof(js_promise_funcs));
+ JS_NewGlobalCConstructor2(ctx, obj1, "Promise",
+ ctx->class_proto[JS_CLASS_PROMISE]);
+
+ /* AsyncFunction */
+ ctx->class_proto[JS_CLASS_ASYNC_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
+ obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
+ "AsyncFunction", 1,
+ JS_CFUNC_constructor_or_func_magic, JS_FUNC_ASYNC,
+ ctx->function_ctor);
+ JS_SetPropertyFunctionList(ctx,
+ ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
+ js_async_function_proto_funcs,
+ countof(js_async_function_proto_funcs));
+ JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_FUNCTION],
+ 0, JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, obj1);
+
+ /* AsyncIteratorPrototype */
+ ctx->async_iterator_proto = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->async_iterator_proto,
+ js_async_iterator_proto_funcs,
+ countof(js_async_iterator_proto_funcs));
+
+ /* AsyncFromSyncIteratorPrototype */
+ ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR] =
+ JS_NewObjectProto(ctx, ctx->async_iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ASYNC_FROM_SYNC_ITERATOR],
+ js_async_from_sync_iterator_proto_funcs,
+ countof(js_async_from_sync_iterator_proto_funcs));
+
+ /* AsyncGeneratorPrototype */
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR] =
+ JS_NewObjectProto(ctx, ctx->async_iterator_proto);
+ JS_SetPropertyFunctionList(ctx,
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
+ js_async_generator_proto_funcs,
+ countof(js_async_generator_proto_funcs));
+
+ /* AsyncGeneratorFunction */
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION] =
+ JS_NewObjectProto(ctx, ctx->function_proto);
+ obj1 = JS_NewCFunction3(ctx, (JSCFunction *)js_function_constructor,
+ "AsyncGeneratorFunction", 1,
+ JS_CFUNC_constructor_or_func_magic,
+ JS_FUNC_ASYNC_GENERATOR,
+ ctx->function_ctor);
+ JS_SetPropertyFunctionList(ctx,
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
+ js_async_generator_function_proto_funcs,
+ countof(js_async_generator_function_proto_funcs));
+ JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
+ ctx->class_proto[JS_CLASS_ASYNC_GENERATOR],
+ JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
+ JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_ASYNC_GENERATOR_FUNCTION],
+ 0, JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, obj1);
+}
+
+/* URI handling */
+
+static int string_get_hex(JSString *p, int k, int n) {
+ int c = 0, h;
+ while (n-- > 0) {
+ if ((h = from_hex(string_get(p, k++))) < 0)
+ return -1;
+ c = (c << 4) | h;
+ }
+ return c;
+}
+
+static int isURIReserved(int c) {
+ return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
+}
+
+static int FORMAT_ATTR(2, 3) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ JS_ThrowError(ctx, JS_URI_ERROR, fmt, ap);
+ va_end(ap);
+ return -1;
+}
+
+static int hex_decode(JSContext *ctx, JSString *p, int k) {
+ int c;
+
+ if (k >= p->len || string_get(p, k) != '%')
+ return js_throw_URIError(ctx, "expecting %%");
+ if (k + 2 >= p->len || (c = string_get_hex(p, k + 1, 2)) < 0)
+ return js_throw_URIError(ctx, "expecting hex digit");
+
+ return c;
+}
+
+static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int isComponent)
+{
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int k, c, c1, n, c_min;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+
+ string_buffer_init(ctx, b, 0);
+
+ p = JS_VALUE_GET_STRING(str);
+ for (k = 0; k < p->len;) {
+ c = string_get(p, k);
+ if (c == '%') {
+ c = hex_decode(ctx, p, k);
+ if (c < 0)
+ goto fail;
+ k += 3;
+ if (c < 0x80) {
+ if (!isComponent && isURIReserved(c)) {
+ c = '%';
+ k -= 2;
+ }
+ } else {
+ /* Decode URI-encoded UTF-8 sequence */
+ if (c >= 0xc0 && c <= 0xdf) {
+ n = 1;
+ c_min = 0x80;
+ c &= 0x1f;
+ } else if (c >= 0xe0 && c <= 0xef) {
+ n = 2;
+ c_min = 0x800;
+ c &= 0xf;
+ } else if (c >= 0xf0 && c <= 0xf7) {
+ n = 3;
+ c_min = 0x10000;
+ c &= 0x7;
+ } else {
+ n = 0;
+ c_min = 1;
+ c = 0;
+ }
+ while (n-- > 0) {
+ c1 = hex_decode(ctx, p, k);
+ if (c1 < 0)
+ goto fail;
+ k += 3;
+ if ((c1 & 0xc0) != 0x80) {
+ c = 0;
+ break;
+ }
+ c = (c << 6) | (c1 & 0x3f);
+ }
+ if (c < c_min || c > 0x10FFFF ||
+ (c >= 0xd800 && c < 0xe000)) {
+ js_throw_URIError(ctx, "malformed UTF-8");
+ goto fail;
+ }
+ }
+ } else {
+ k++;
+ }
+ string_buffer_putc(b, c);
+ }
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+
+fail:
+ JS_FreeValue(ctx, str);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static int isUnescaped(int c) {
+ static char const unescaped_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "@*_+-./";
+ return c < 0x100 &&
+ memchr(unescaped_chars, c, sizeof(unescaped_chars) - 1);
+}
+
+static int isURIUnescaped(int c, int isComponent) {
+ return c < 0x100 &&
+ ((c >= 0x61 && c <= 0x7a) ||
+ (c >= 0x41 && c <= 0x5a) ||
+ (c >= 0x30 && c <= 0x39) ||
+ memchr("-_.!~*'()", c, sizeof("-_.!~*'()") - 1) != NULL ||
+ (!isComponent && isURIReserved(c)));
+}
+
+static int encodeURI_hex(StringBuffer *b, int c) {
+ uint8_t buf[6];
+ int n = 0;
+ const char *hex = "0123456789ABCDEF";
+
+ buf[n++] = '%';
+ if (c >= 256) {
+ buf[n++] = 'u';
+ buf[n++] = hex[(c >> 12) & 15];
+ buf[n++] = hex[(c >> 8) & 15];
+ }
+ buf[n++] = hex[(c >> 4) & 15];
+ buf[n++] = hex[(c >> 0) & 15];
+ return string_buffer_write8(b, buf, n);
+}
+
+static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int isComponent)
+{
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int k, c, c1;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+
+ p = JS_VALUE_GET_STRING(str);
+ string_buffer_init(ctx, b, p->len);
+ for (k = 0; k < p->len;) {
+ c = string_get(p, k);
+ k++;
+ if (isURIUnescaped(c, isComponent)) {
+ string_buffer_putc16(b, c);
+ } else {
+ if (c >= 0xdc00 && c <= 0xdfff) {
+ js_throw_URIError(ctx, "invalid character");
+ goto fail;
+ } else if (c >= 0xd800 && c <= 0xdbff) {
+ if (k >= p->len) {
+ js_throw_URIError(ctx, "expecting surrogate pair");
+ goto fail;
+ }
+ c1 = string_get(p, k);
+ k++;
+ if (c1 < 0xdc00 || c1 > 0xdfff) {
+ js_throw_URIError(ctx, "expecting surrogate pair");
+ goto fail;
+ }
+ c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000;
+ }
+ if (c < 0x80) {
+ encodeURI_hex(b, c);
+ } else {
+ /* XXX: use C UTF-8 conversion ? */
+ if (c < 0x800) {
+ encodeURI_hex(b, (c >> 6) | 0xc0);
+ } else {
+ if (c < 0x10000) {
+ encodeURI_hex(b, (c >> 12) | 0xe0);
+ } else {
+ encodeURI_hex(b, (c >> 18) | 0xf0);
+ encodeURI_hex(b, ((c >> 12) & 0x3f) | 0x80);
+ }
+ encodeURI_hex(b, ((c >> 6) & 0x3f) | 0x80);
+ }
+ encodeURI_hex(b, (c & 0x3f) | 0x80);
+ }
+ }
+ }
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+
+fail:
+ JS_FreeValue(ctx, str);
+ string_buffer_free(b);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_global_escape(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int i, len, c;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+
+ p = JS_VALUE_GET_STRING(str);
+ string_buffer_init(ctx, b, p->len);
+ for (i = 0, len = p->len; i < len; i++) {
+ c = string_get(p, i);
+ if (isUnescaped(c)) {
+ string_buffer_putc16(b, c);
+ } else {
+ encodeURI_hex(b, c);
+ }
+ }
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+}
+
+static JSValue js_global_unescape(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue str;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p;
+ int i, len, c, n;
+
+ str = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(str))
+ return str;
+
+ string_buffer_init(ctx, b, 0);
+ p = JS_VALUE_GET_STRING(str);
+ for (i = 0, len = p->len; i < len; i++) {
+ c = string_get(p, i);
+ if (c == '%') {
+ if (i + 6 <= len
+ && string_get(p, i + 1) == 'u'
+ && (n = string_get_hex(p, i + 2, 4)) >= 0) {
+ c = n;
+ i += 6 - 1;
+ } else
+ if (i + 3 <= len
+ && (n = string_get_hex(p, i + 1, 2)) >= 0) {
+ c = n;
+ i += 3 - 1;
+ }
+ }
+ string_buffer_putc16(b, c);
+ }
+ JS_FreeValue(ctx, str);
+ return string_buffer_end(b);
+}
+
+/* global object */
+
+static const JSCFunctionListEntry js_global_funcs[] = {
+ JS_CFUNC_DEF("parseInt", 2, js_parseInt ),
+ JS_CFUNC_DEF("parseFloat", 1, js_parseFloat ),
+ JS_CFUNC_DEF("isNaN", 1, js_global_isNaN ),
+ JS_CFUNC_DEF("isFinite", 1, js_global_isFinite ),
+
+ JS_CFUNC_MAGIC_DEF("decodeURI", 1, js_global_decodeURI, 0 ),
+ JS_CFUNC_MAGIC_DEF("decodeURIComponent", 1, js_global_decodeURI, 1 ),
+ JS_CFUNC_MAGIC_DEF("encodeURI", 1, js_global_encodeURI, 0 ),
+ JS_CFUNC_MAGIC_DEF("encodeURIComponent", 1, js_global_encodeURI, 1 ),
+ JS_CFUNC_DEF("escape", 1, js_global_escape ),
+ JS_CFUNC_DEF("unescape", 1, js_global_unescape ),
+ JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ),
+ JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ),
+ JS_PROP_UNDEFINED_DEF("undefined", 0 ),
+
+ /* for the 'Date' implementation */
+ JS_CFUNC_DEF("__date_clock", 0, js___date_clock ),
+ //JS_CFUNC_DEF("__date_now", 0, js___date_now ),
+ //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ),
+ //JS_CFUNC_DEF("__date_create", 3, js___date_create ),
+};
+
+/* Date */
+
+static int64_t math_mod(int64_t a, int64_t b) {
+ /* return positive modulo */
+ int64_t m = a % b;
+ return m + (m < 0) * b;
+}
+
+static int64_t floor_div(int64_t a, int64_t b) {
+ /* integer division rounding toward -Infinity */
+ int64_t m = a % b;
+ return (a - (m + (m < 0) * b)) / b;
+}
+
+static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+
+static warn_unused int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data))
+ return JS_ToFloat64(ctx, valp, p->u.object_data);
+ }
+ JS_ThrowTypeError(ctx, "not a Date object");
+ return -1;
+}
+
+static JSValue JS_SetThisTimeValue(JSContext *ctx, JSValueConst this_val, double v)
+{
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_DATE) {
+ JS_FreeValue(ctx, p->u.object_data);
+ p->u.object_data = JS_NewFloat64(ctx, v);
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a Date object");
+}
+
+static int64_t days_from_year(int64_t y) {
+ return 365 * (y - 1970) + floor_div(y - 1969, 4) -
+ floor_div(y - 1901, 100) + floor_div(y - 1601, 400);
+}
+
+static int64_t days_in_year(int64_t y) {
+ return 365 + !(y % 4) - !(y % 100) + !(y % 400);
+}
+
+/* return the year, update days */
+static int64_t year_from_days(int64_t *days) {
+ int64_t y, d1, nd, d = *days;
+ y = floor_div(d * 10000, 3652425) + 1970;
+ /* the initial approximation is very good, so only a few
+ iterations are necessary */
+ for(;;) {
+ d1 = d - days_from_year(y);
+ if (d1 < 0) {
+ y--;
+ d1 += days_in_year(y);
+ } else {
+ nd = days_in_year(y);
+ if (d1 < nd)
+ break;
+ d1 -= nd;
+ y++;
+ }
+ }
+ *days = d1;
+ return y;
+}
+
+static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+static char const day_names[] = "SunMonTueWedThuFriSat";
+
+static warn_unused int get_date_fields(JSContext *ctx, JSValueConst obj,
+ double fields[9], int is_local, int force)
+{
+ double dval;
+ int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0;
+
+ if (JS_ThisTimeValue(ctx, &dval, obj))
+ return -1;
+
+ if (isnan(dval)) {
+ if (!force)
+ return FALSE; /* NaN */
+ d = 0; /* initialize all fields to 0 */
+ } else {
+ d = dval;
+ if (is_local) {
+ tz = -getTimezoneOffset(d);
+ d += tz * 60000;
+ }
+ }
+
+ /* result is >= 0, we can use % */
+ h = math_mod(d, 86400000);
+ days = (d - h) / 86400000;
+ ms = h % 1000;
+ h = (h - ms) / 1000;
+ s = h % 60;
+ h = (h - s) / 60;
+ m = h % 60;
+ h = (h - m) / 60;
+ wd = math_mod(days + 4, 7); /* week day */
+ y = year_from_days(&days);
+
+ for(i = 0; i < 11; i++) {
+ md = month_days[i];
+ if (i == 1)
+ md += days_in_year(y) - 365;
+ if (days < md)
+ break;
+ days -= md;
+ }
+ fields[0] = y;
+ fields[1] = i;
+ fields[2] = days + 1;
+ fields[3] = h;
+ fields[4] = m;
+ fields[5] = s;
+ fields[6] = ms;
+ fields[7] = wd;
+ fields[8] = tz;
+ return TRUE;
+}
+
+static double time_clip(double t) {
+ if (t >= -8.64e15 && t <= 8.64e15)
+ return trunc(t) + 0.0; /* convert -0 to +0 */
+ else
+ return NAN;
+}
+
+/* The spec mandates the use of 'double' and it fixes the order
+ of the operations */
+static double set_date_fields(const double fields[], int is_local) {
+ int64_t y;
+ double days, d, h, m1;
+ int i, m, md;
+
+ m1 = fields[1];
+ m = fmod(m1, 12);
+ if (m < 0)
+ m += 12;
+ y = (int64_t)(fields[0] + floor(m1 / 12));
+ days = days_from_year(y);
+
+ for(i = 0; i < m; i++) {
+ md = month_days[i];
+ if (i == 1)
+ md += days_in_year(y) - 365;
+ days += md;
+ }
+ days += fields[2] - 1;
+ h = fields[3] * 3600000 + fields[4] * 60000 +
+ fields[5] * 1000 + fields[6];
+ d = days * 86400000 + h;
+ if (is_local)
+ d += getTimezoneOffset(d) * 60000;
+ return time_clip(d);
+}
+
+static JSValue get_date_field(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ // get_date_field(obj, n, is_local)
+ double fields[9];
+ int res, n, is_local;
+
+ is_local = magic & 0x0F;
+ n = (magic >> 4) & 0x0F;
+ res = get_date_fields(ctx, this_val, fields, is_local, 0);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (!res)
+ return JS_NAN;
+
+ if (magic & 0x100) { // getYear
+ fields[0] -= 1900;
+ }
+ return JS_NewFloat64(ctx, fields[n]);
+}
+
+static JSValue set_date_field(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ // _field(obj, first_field, end_field, args, is_local)
+ double fields[9];
+ int res, first_field, end_field, is_local, i, n;
+ double d, a;
+
+ d = NAN;
+ first_field = (magic >> 8) & 0x0F;
+ end_field = (magic >> 4) & 0x0F;
+ is_local = magic & 0x0F;
+
+ res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (res && argc > 0) {
+ n = end_field - first_field;
+ if (argc < n)
+ n = argc;
+ for(i = 0; i < n; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isfinite(a))
+ goto done;
+ fields[first_field + i] = trunc(a);
+ }
+ d = set_date_fields(fields, is_local);
+ }
+done:
+ return JS_SetThisTimeValue(ctx, this_val, d);
+}
+
+/* fmt:
+ 0: toUTCString: "Tue, 02 Jan 2018 23:04:46 GMT"
+ 1: toString: "Wed Jan 03 2018 00:05:22 GMT+0100 (CET)"
+ 2: toISOString: "2018-01-02T23:02:56.927Z"
+ 3: toLocaleString: "1/2/2018, 11:40:40 PM"
+ part: 1=date, 2=time 3=all
+ XXX: should use a variant of strftime().
+ */
+static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ // _string(obj, fmt, part)
+ char buf[64];
+ double fields[9];
+ int res, fmt, part, pos;
+ int y, mon, d, h, m, s, ms, wd, tz;
+
+ fmt = (magic >> 4) & 0x0F;
+ part = magic & 0x0F;
+
+ res = get_date_fields(ctx, this_val, fields, fmt & 1, 0);
+ if (res < 0)
+ return JS_EXCEPTION;
+ if (!res) {
+ if (fmt == 2)
+ return JS_ThrowRangeError(ctx, "Date value is NaN");
+ else
+ return JS_NewString(ctx, "Invalid Date");
+ }
+
+ y = fields[0];
+ mon = fields[1];
+ d = fields[2];
+ h = fields[3];
+ m = fields[4];
+ s = fields[5];
+ ms = fields[6];
+ wd = fields[7];
+ tz = fields[8];
+
+ pos = 0;
+
+ if (part & 1) { /* date part */
+ switch(fmt) {
+ case 0:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%.3s, %02d %.3s %0*d ",
+ day_names + wd * 3, d,
+ month_names + mon * 3, 4 + (y < 0), y);
+ break;
+ case 1:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%.3s %.3s %02d %0*d",
+ day_names + wd * 3,
+ month_names + mon * 3, d, 4 + (y < 0), y);
+ if (part == 3) {
+ buf[pos++] = ' ';
+ }
+ break;
+ case 2:
+ if (y >= 0 && y <= 9999) {
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%04d", y);
+ } else {
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%+07d", y);
+ }
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "-%02d-%02dT", mon + 1, d);
+ break;
+ case 3:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d/%02d/%0*d", mon + 1, d, 4 + (y < 0), y);
+ if (part == 3) {
+ buf[pos++] = ',';
+ buf[pos++] = ' ';
+ }
+ break;
+ }
+ }
+ if (part & 2) { /* time part */
+ switch(fmt) {
+ case 0:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d GMT", h, m, s);
+ break;
+ case 1:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d GMT", h, m, s);
+ if (tz < 0) {
+ buf[pos++] = '-';
+ tz = -tz;
+ } else {
+ buf[pos++] = '+';
+ }
+ /* tz is >= 0, can use % */
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d%02d", tz / 60, tz % 60);
+ /* XXX: tack the time zone code? */
+ break;
+ case 2:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d.%03dZ", h, m, s, ms);
+ break;
+ case 3:
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s,
+ (h < 12) ? 'A' : 'P');
+ break;
+ }
+ }
+ return JS_NewStringLen(ctx, buf, pos);
+}
+
+/* OS dependent: return the UTC time in ms since 1970. */
+static int64_t date_now(void) {
+#ifdef _MSC_VER
+ SYSTEMTIME st;
+ GetSystemTime(&st);
+ int64_t d;
+ SystemTimeToFileTime(&st, (FILETIME *) &d);
+ return (d - 116444736000000000ULL) / 10000;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
+#endif
+}
+
+static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ // Date(y, mon, d, h, m, s, ms)
+ JSValue rv;
+ int i, n;
+ double a, val;
+
+ if (JS_IsUndefined(new_target)) {
+ /* invoked as function */
+ argc = 0;
+ }
+ n = argc;
+ if (n == 0) {
+ val = date_now();
+ } else if (n == 1) {
+ JSValue v, dv;
+ if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
+ if (p->class_id == JS_CLASS_DATE && JS_IsNumber(p->u.object_data)) {
+ if (JS_ToFloat64(ctx, &val, p->u.object_data))
+ return JS_EXCEPTION;
+ val = time_clip(val);
+ goto has_val;
+ }
+ }
+ v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
+ if (JS_IsString(v)) {
+ dv = js_Date_parse(ctx, JS_UNDEFINED, 1, &v);
+ JS_FreeValue(ctx, v);
+ if (JS_IsException(dv))
+ return JS_EXCEPTION;
+ if (JS_ToFloat64Free(ctx, &val, dv))
+ return JS_EXCEPTION;
+ } else {
+ if (JS_ToFloat64Free(ctx, &val, v))
+ return JS_EXCEPTION;
+ }
+ val = time_clip(val);
+ } else {
+ double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
+ if (n > 7)
+ n = 7;
+ for(i = 0; i < n; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isfinite(a))
+ break;
+ fields[i] = trunc(a);
+ if (i == 0 && fields[0] >= 0 && fields[0] < 100)
+ fields[0] += 1900;
+ }
+ val = (i == n) ? set_date_fields(fields, 1) : NAN;
+ }
+has_val:
+#if 0
+ JSValueConst args[3];
+ args[0] = new_target;
+ args[1] = ctx->class_proto[JS_CLASS_DATE];
+ args[2] = JS_NewFloat64(ctx, val);
+ rv = js___date_create(ctx, JS_UNDEFINED, 3, args);
+#else
+ rv = js_create_from_ctor(ctx, new_target, JS_CLASS_DATE);
+ if (!JS_IsException(rv))
+ JS_SetObjectData(ctx, rv, JS_NewFloat64(ctx, val));
+#endif
+ if (!JS_IsException(rv) && JS_IsUndefined(new_target)) {
+ /* invoked as a function, return (new Date()).toString(); */
+ JSValue s;
+ s = get_date_string(ctx, rv, 0, NULL, 0x13);
+ JS_FreeValue(ctx, rv);
+ rv = s;
+ }
+ return rv;
+}
+
+static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // UTC(y, mon, d, h, m, s, ms)
+ double fields[] = { 0, 0, 1, 0, 0, 0, 0 };
+ int i, n;
+ double a;
+
+ n = argc;
+ if (n == 0)
+ return JS_NAN;
+ if (n > 7)
+ n = 7;
+ for(i = 0; i < n; i++) {
+ if (JS_ToFloat64(ctx, &a, argv[i]))
+ return JS_EXCEPTION;
+ if (!isfinite(a))
+ return JS_NAN;
+ fields[i] = trunc(a);
+ if (i == 0 && fields[0] >= 0 && fields[0] < 100)
+ fields[0] += 1900;
+ }
+ return JS_NewFloat64(ctx, set_date_fields(fields, 0));
+}
+
+static void string_skip_spaces(JSString *sp, int *pp) {
+ while (*pp < sp->len && string_get(sp, *pp) == ' ')
+ *pp += 1;
+}
+
+static void string_skip_non_spaces(JSString *sp, int *pp) {
+ while (*pp < sp->len && string_get(sp, *pp) != ' ')
+ *pp += 1;
+}
+
+/* parse a numeric field with an optional sign if accept_sign is TRUE */
+static int string_get_digits(JSString *sp, int *pp, int64_t *pval) {
+ int64_t v = 0;
+ int c, p = *pp, p_start;
+
+ if (p >= sp->len)
+ return -1;
+ p_start = p;
+ while (p < sp->len) {
+ c = string_get(sp, p);
+ if (!(c >= '0' && c <= '9')) {
+ if (p == p_start)
+ return -1;
+ else
+ break;
+ }
+ v = v * 10 + c - '0';
+ p++;
+ }
+ *pval = v;
+ *pp = p;
+ return 0;
+}
+
+static int string_get_signed_digits(JSString *sp, int *pp, int64_t *pval) {
+ int res, sgn, p = *pp;
+
+ if (p >= sp->len)
+ return -1;
+
+ sgn = string_get(sp, p);
+ if (sgn == '-' || sgn == '+')
+ p++;
+
+ res = string_get_digits(sp, &p, pval);
+ if (res == 0 && sgn == '-')
+ *pval = -*pval;
+ *pp = p;
+ return res;
+}
+
+/* parse a fixed width numeric field */
+static int string_get_fixed_width_digits(JSString *sp, int *pp, int n, int64_t *pval) {
+ int64_t v = 0;
+ int i, c, p = *pp;
+
+ for(i = 0; i < n; i++) {
+ if (p >= sp->len)
+ return -1;
+ c = string_get(sp, p);
+ if (!(c >= '0' && c <= '9'))
+ return -1;
+ v = v * 10 + c - '0';
+ p++;
+ }
+ *pval = v;
+ *pp = p;
+ return 0;
+}
+
+static int string_get_milliseconds(JSString *sp, int *pp, int64_t *pval) {
+ /* parse milliseconds as a fractional part, round to nearest */
+ /* XXX: the spec does not indicate which rounding should be used */
+ int mul = 1000, ms = 0, p = *pp, c, p_start;
+ if (p >= sp->len)
+ return -1;
+ p_start = p;
+ while (p < sp->len) {
+ c = string_get(sp, p);
+ if (!(c >= '0' && c <= '9')) {
+ if (p == p_start)
+ return -1;
+ else
+ break;
+ }
+ if (mul == 1 && c >= '5')
+ ms += 1;
+ ms += (c - '0') * (mul /= 10);
+ p++;
+ }
+ *pval = ms;
+ *pp = p;
+ return 0;
+}
+
+
+static int find_abbrev(JSString *sp, int p, const char *list, int count) {
+ int n, i;
+
+ if (p + 3 <= sp->len) {
+ for (n = 0; n < count; n++) {
+ for (i = 0; i < 3; i++) {
+ if (string_get(sp, p + i) != month_names[n * 3 + i])
+ goto next;
+ }
+ return n;
+ next:;
+ }
+ }
+ return -1;
+}
+
+static int string_get_month(JSString *sp, int *pp, int64_t *pval) {
+ int n;
+
+ string_skip_spaces(sp, pp);
+ n = find_abbrev(sp, *pp, month_names, 12);
+ if (n < 0)
+ return -1;
+
+ *pval = n;
+ *pp += 3;
+ return 0;
+}
+
+static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // parse(s)
+ JSValue s, rv;
+ int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 };
+ double fields1[7];
+ int64_t tz, hh, mm;
+ double d;
+ int p, i, c, sgn, l;
+ JSString *sp;
+ BOOL is_local;
+
+ rv = JS_NAN;
+
+ s = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(s))
+ return JS_EXCEPTION;
+
+ sp = JS_VALUE_GET_STRING(s);
+ p = 0;
+ if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) {
+ /* ISO format */
+ /* year field can be negative */
+ if (string_get_signed_digits(sp, &p, &fields[0]))
+ goto done;
+
+ for (i = 1; i < 7; i++) {
+ if (p >= sp->len)
+ break;
+ switch(i) {
+ case 1:
+ case 2:
+ c = '-';
+ break;
+ case 3:
+ c = 'T';
+ break;
+ case 4:
+ case 5:
+ c = ':';
+ break;
+ case 6:
+ c = '.';
+ break;
+ }
+ if (string_get(sp, p) != c)
+ break;
+ p++;
+ if (i == 6) {
+ if (string_get_milliseconds(sp, &p, &fields[i]))
+ goto done;
+ } else {
+ if (string_get_digits(sp, &p, &fields[i]))
+ goto done;
+ }
+ }
+ /* no time: UTC by default */
+ is_local = (i > 3);
+ fields[1] -= 1;
+
+ /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */
+ tz = 0;
+ if (p < sp->len) {
+ sgn = string_get(sp, p);
+ if (sgn == '+' || sgn == '-') {
+ p++;
+ l = sp->len - p;
+ if (l != 4 && l != 5)
+ goto done;
+ if (string_get_fixed_width_digits(sp, &p, 2, &hh))
+ goto done;
+ if (l == 5) {
+ if (string_get(sp, p) != ':')
+ goto done;
+ p++;
+ }
+ if (string_get_fixed_width_digits(sp, &p, 2, &mm))
+ goto done;
+ tz = hh * 60 + mm;
+ if (sgn == '-')
+ tz = -tz;
+ is_local = FALSE;
+ } else if (sgn == 'Z') {
+ p++;
+ is_local = FALSE;
+ } else {
+ goto done;
+ }
+ /* error if extraneous characters */
+ if (p != sp->len)
+ goto done;
+ }
+ } else {
+ /* toString or toUTCString format */
+ /* skip the day of the week */
+ string_skip_non_spaces(sp, &p);
+ string_skip_spaces(sp, &p);
+ if (p >= sp->len)
+ goto done;
+ c = string_get(sp, p);
+ if (c >= '0' && c <= '9') {
+ /* day of month first */
+ if (string_get_digits(sp, &p, &fields[2]))
+ goto done;
+ if (string_get_month(sp, &p, &fields[1]))
+ goto done;
+ } else {
+ /* month first */
+ if (string_get_month(sp, &p, &fields[1]))
+ goto done;
+ string_skip_spaces(sp, &p);
+ if (string_get_digits(sp, &p, &fields[2]))
+ goto done;
+ }
+ /* year */
+ string_skip_spaces(sp, &p);
+ if (string_get_signed_digits(sp, &p, &fields[0]))
+ goto done;
+
+ /* hour, min, seconds */
+ string_skip_spaces(sp, &p);
+ for(i = 0; i < 3; i++) {
+ if (i == 1 || i == 2) {
+ if (p >= sp->len)
+ goto done;
+ if (string_get(sp, p) != ':')
+ goto done;
+ p++;
+ }
+ if (string_get_digits(sp, &p, &fields[3 + i]))
+ goto done;
+ }
+ // XXX: parse optional milliseconds?
+
+ /* parse the time zone offset if present: [+-]HHmm */
+ is_local = FALSE;
+ tz = 0;
+ for (tz = 0; p < sp->len; p++) {
+ sgn = string_get(sp, p);
+ if (sgn == '+' || sgn == '-') {
+ p++;
+ if (string_get_fixed_width_digits(sp, &p, 2, &hh))
+ goto done;
+ if (string_get_fixed_width_digits(sp, &p, 2, &mm))
+ goto done;
+ tz = hh * 60 + mm;
+ if (sgn == '-')
+ tz = -tz;
+ break;
+ }
+ }
+ }
+ for(i = 0; i < 7; i++)
+ fields1[i] = fields[i];
+ d = set_date_fields(fields1, is_local) - tz * 60000;
+ rv = JS_NewFloat64(ctx, d);
+
+done:
+ JS_FreeValue(ctx, s);
+ return rv;
+}
+
+static JSValue js_Date_now(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // now()
+ return JS_NewInt64(ctx, date_now());
+}
+
+static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // Symbol_toPrimitive(hint)
+ JSValueConst obj = this_val;
+ JSAtom hint = JS_ATOM_NULL;
+ int hint_num;
+
+ if (!JS_IsObject(obj))
+ return JS_ThrowTypeErrorNotAnObject(ctx);
+
+ if (JS_IsString(argv[0])) {
+ hint = JS_ValueToAtom(ctx, argv[0]);
+ if (hint == JS_ATOM_NULL)
+ return JS_EXCEPTION;
+ JS_FreeAtom(ctx, hint);
+ }
+ switch (hint) {
+ case JS_ATOM_number:
+#ifdef CONFIG_BIGNUM
+ case JS_ATOM_integer:
+#endif
+ hint_num = HINT_NUMBER;
+ break;
+ case JS_ATOM_string:
+ case JS_ATOM_default:
+ hint_num = HINT_STRING;
+ break;
+ default:
+ return JS_ThrowTypeError(ctx, "invalid hint");
+ }
+ return JS_ToPrimitive(ctx, obj, hint_num | HINT_FORCE_ORDINARY);
+}
+
+static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // getTimezoneOffset()
+ double v;
+
+ if (JS_ThisTimeValue(ctx, &v, this_val))
+ return JS_EXCEPTION;
+ if (isnan(v))
+ return JS_NAN;
+ else
+ return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v)));
+}
+
+static JSValue js_date_getTime(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // getTime()
+ double v;
+
+ if (JS_ThisTimeValue(ctx, &v, this_val))
+ return JS_EXCEPTION;
+ return JS_NewFloat64(ctx, v);
+}
+
+static JSValue js_date_setTime(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // setTime(v)
+ double v;
+
+ if (JS_ThisTimeValue(ctx, &v, this_val) || JS_ToFloat64(ctx, &v, argv[0]))
+ return JS_EXCEPTION;
+ return JS_SetThisTimeValue(ctx, this_val, time_clip(v));
+}
+
+static JSValue js_date_setYear(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // setYear(y)
+ double y;
+ JSValueConst args[1];
+
+ if (JS_ThisTimeValue(ctx, &y, this_val) || JS_ToFloat64(ctx, &y, argv[0]))
+ return JS_EXCEPTION;
+ y = +y;
+ if (isfinite(y)) {
+ y = trunc(y);
+ if (y >= 0 && y < 100)
+ y += 1900;
+ }
+ args[0] = JS_NewFloat64(ctx, y);
+ return set_date_field(ctx, this_val, 1, args, 0x011);
+}
+
+static JSValue js_date_toJSON(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // toJSON(key)
+ JSValue obj, tv, method, rv;
+ double d;
+
+ rv = JS_EXCEPTION;
+ tv = JS_UNDEFINED;
+
+ obj = JS_ToObject(ctx, this_val);
+ tv = JS_ToPrimitive(ctx, obj, HINT_NUMBER);
+ if (JS_IsException(tv))
+ goto exception;
+ if (JS_IsNumber(tv)) {
+ if (JS_ToFloat64(ctx, &d, tv) < 0)
+ goto exception;
+ if (!isfinite(d)) {
+ rv = JS_NULL;
+ goto done;
+ }
+ }
+ method = JS_GetPropertyStr(ctx, obj, "toISOString");
+ if (JS_IsException(method))
+ goto exception;
+ if (!JS_IsFunction(ctx, method)) {
+ JS_ThrowTypeError(ctx, "object needs toISOString method");
+ JS_FreeValue(ctx, method);
+ goto exception;
+ }
+ rv = JS_CallFree(ctx, method, obj, 0, NULL);
+exception:
+done:
+ JS_FreeValue(ctx, obj);
+ JS_FreeValue(ctx, tv);
+ return rv;
+}
+
+static const JSCFunctionListEntry js_date_funcs[] = {
+ JS_CFUNC_DEF("now", 0, js_Date_now ),
+ JS_CFUNC_DEF("parse", 1, js_Date_parse ),
+ JS_CFUNC_DEF("UTC", 7, js_Date_UTC ),
+};
+
+static const JSCFunctionListEntry js_date_proto_funcs[] = {
+ JS_CFUNC_DEF("valueOf", 0, js_date_getTime ),
+ JS_CFUNC_MAGIC_DEF("toString", 0, get_date_string, 0x13 ),
+ JS_CFUNC_DEF("[Symbol.toPrimitive]", 1, js_date_Symbol_toPrimitive ),
+ JS_CFUNC_MAGIC_DEF("toUTCString", 0, get_date_string, 0x03 ),
+ JS_ALIAS_DEF("toGMTString", "toUTCString" ),
+ JS_CFUNC_MAGIC_DEF("toISOString", 0, get_date_string, 0x23 ),
+ JS_CFUNC_MAGIC_DEF("toDateString", 0, get_date_string, 0x11 ),
+ JS_CFUNC_MAGIC_DEF("toTimeString", 0, get_date_string, 0x12 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, get_date_string, 0x33 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleDateString", 0, get_date_string, 0x31 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleTimeString", 0, get_date_string, 0x32 ),
+ JS_CFUNC_DEF("getTimezoneOffset", 0, js_date_getTimezoneOffset ),
+ JS_CFUNC_DEF("getTime", 0, js_date_getTime ),
+ JS_CFUNC_MAGIC_DEF("getYear", 0, get_date_field, 0x101 ),
+ JS_CFUNC_MAGIC_DEF("getFullYear", 0, get_date_field, 0x01 ),
+ JS_CFUNC_MAGIC_DEF("getUTCFullYear", 0, get_date_field, 0x00 ),
+ JS_CFUNC_MAGIC_DEF("getMonth", 0, get_date_field, 0x11 ),
+ JS_CFUNC_MAGIC_DEF("getUTCMonth", 0, get_date_field, 0x10 ),
+ JS_CFUNC_MAGIC_DEF("getDate", 0, get_date_field, 0x21 ),
+ JS_CFUNC_MAGIC_DEF("getUTCDate", 0, get_date_field, 0x20 ),
+ JS_CFUNC_MAGIC_DEF("getHours", 0, get_date_field, 0x31 ),
+ JS_CFUNC_MAGIC_DEF("getUTCHours", 0, get_date_field, 0x30 ),
+ JS_CFUNC_MAGIC_DEF("getMinutes", 0, get_date_field, 0x41 ),
+ JS_CFUNC_MAGIC_DEF("getUTCMinutes", 0, get_date_field, 0x40 ),
+ JS_CFUNC_MAGIC_DEF("getSeconds", 0, get_date_field, 0x51 ),
+ JS_CFUNC_MAGIC_DEF("getUTCSeconds", 0, get_date_field, 0x50 ),
+ JS_CFUNC_MAGIC_DEF("getMilliseconds", 0, get_date_field, 0x61 ),
+ JS_CFUNC_MAGIC_DEF("getUTCMilliseconds", 0, get_date_field, 0x60 ),
+ JS_CFUNC_MAGIC_DEF("getDay", 0, get_date_field, 0x71 ),
+ JS_CFUNC_MAGIC_DEF("getUTCDay", 0, get_date_field, 0x70 ),
+ JS_CFUNC_DEF("setTime", 1, js_date_setTime ),
+ JS_CFUNC_MAGIC_DEF("setMilliseconds", 1, set_date_field, 0x671 ),
+ JS_CFUNC_MAGIC_DEF("setUTCMilliseconds", 1, set_date_field, 0x670 ),
+ JS_CFUNC_MAGIC_DEF("setSeconds", 2, set_date_field, 0x571 ),
+ JS_CFUNC_MAGIC_DEF("setUTCSeconds", 2, set_date_field, 0x570 ),
+ JS_CFUNC_MAGIC_DEF("setMinutes", 3, set_date_field, 0x471 ),
+ JS_CFUNC_MAGIC_DEF("setUTCMinutes", 3, set_date_field, 0x470 ),
+ JS_CFUNC_MAGIC_DEF("setHours", 4, set_date_field, 0x371 ),
+ JS_CFUNC_MAGIC_DEF("setUTCHours", 4, set_date_field, 0x370 ),
+ JS_CFUNC_MAGIC_DEF("setDate", 1, set_date_field, 0x231 ),
+ JS_CFUNC_MAGIC_DEF("setUTCDate", 1, set_date_field, 0x230 ),
+ JS_CFUNC_MAGIC_DEF("setMonth", 2, set_date_field, 0x131 ),
+ JS_CFUNC_MAGIC_DEF("setUTCMonth", 2, set_date_field, 0x130 ),
+ JS_CFUNC_DEF("setYear", 1, js_date_setYear ),
+ JS_CFUNC_MAGIC_DEF("setFullYear", 3, set_date_field, 0x031 ),
+ JS_CFUNC_MAGIC_DEF("setUTCFullYear", 3, set_date_field, 0x030 ),
+ JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ),
+};
+
+void JS_AddIntrinsicDate(JSContext *ctx)
+{
+ JSValueConst obj;
+
+ /* Date */
+ ctx->class_proto[JS_CLASS_DATE] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATE], js_date_proto_funcs,
+ countof(js_date_proto_funcs));
+ obj = JS_NewGlobalCConstructor(ctx, "Date", js_date_constructor, 7,
+ ctx->class_proto[JS_CLASS_DATE]);
+ JS_SetPropertyFunctionList(ctx, obj, js_date_funcs, countof(js_date_funcs));
+}
+
+/* eval */
+
+void JS_AddIntrinsicEval(JSContext *ctx)
+{
+ ctx->eval_internal = JS_EvalInternalImpl;
+}
+
+#ifdef CONFIG_BIGNUM
+
+/* Operators */
+
+static void js_operator_set_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
+ int i, j;
+ JSBinaryOperatorDefEntry *ent;
+
+ if (opset) {
+ for(i = 0; i < JS_OVOP_COUNT; i++) {
+ if (opset->self_ops[i])
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]));
+ }
+ for(j = 0; j < opset->left.count; j++) {
+ ent = &opset->left.tab[j];
+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
+ if (ent->ops[i])
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
+ }
+ }
+ js_free_rt(rt, opset->left.tab);
+ for(j = 0; j < opset->right.count; j++) {
+ ent = &opset->right.tab[j];
+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
+ if (ent->ops[i])
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]));
+ }
+ }
+ js_free_rt(rt, opset->right.tab);
+ js_free_rt(rt, opset);
+ }
+}
+
+static void js_operator_set_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSOperatorSetData *opset = JS_GetOpaque(val, JS_CLASS_OPERATOR_SET);
+ int i, j;
+ JSBinaryOperatorDefEntry *ent;
+
+ if (opset) {
+ for(i = 0; i < JS_OVOP_COUNT; i++) {
+ if (opset->self_ops[i])
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[i]),
+ mark_func);
+ }
+ for(j = 0; j < opset->left.count; j++) {
+ ent = &opset->left.tab[j];
+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
+ if (ent->ops[i])
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
+ mark_func);
+ }
+ }
+ for(j = 0; j < opset->right.count; j++) {
+ ent = &opset->right.tab[j];
+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
+ if (ent->ops[i])
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ent->ops[i]),
+ mark_func);
+ }
+ }
+ }
+}
+
+
+/* create an OperatorSet object */
+static JSValue js_operators_create_internal(JSContext *ctx,
+ int argc, JSValueConst *argv,
+ BOOL is_primitive)
+{
+ JSValue opset_obj, prop, obj;
+ JSOperatorSetData *opset, *opset1;
+ JSBinaryOperatorDef *def;
+ JSValueConst arg;
+ int i, j;
+ JSBinaryOperatorDefEntry *new_tab;
+ JSBinaryOperatorDefEntry *ent;
+ uint32_t op_count;
+
+ if (ctx->rt->operator_count == UINT32_MAX) {
+ return JS_ThrowTypeError(ctx, "too many operators");
+ }
+ opset_obj = JS_NewObjectProtoClass(ctx, JS_NULL, JS_CLASS_OPERATOR_SET);
+ if (JS_IsException(opset_obj))
+ goto fail;
+ opset = js_mallocz(ctx, sizeof(*opset));
+ if (!opset)
+ goto fail;
+ JS_SetOpaque(opset_obj, opset);
+ if (argc >= 1) {
+ arg = argv[0];
+ /* self operators */
+ for(i = 0; i < JS_OVOP_COUNT; i++) {
+ prop = JS_GetPropertyStr(ctx, arg, js_overloadable_operator_names[i]);
+ if (JS_IsException(prop))
+ goto fail;
+ if (!JS_IsUndefined(prop)) {
+ if (check_function(ctx, prop)) {
+ JS_FreeValue(ctx, prop);
+ goto fail;
+ }
+ opset->self_ops[i] = JS_VALUE_GET_OBJ(prop);
+ }
+ }
+ }
+ /* left & right operators */
+ for(j = 1; j < argc; j++) {
+ arg = argv[j];
+ prop = JS_GetPropertyStr(ctx, arg, "left");
+ if (JS_IsException(prop))
+ goto fail;
+ def = &opset->right;
+ if (JS_IsUndefined(prop)) {
+ prop = JS_GetPropertyStr(ctx, arg, "right");
+ if (JS_IsException(prop))
+ goto fail;
+ if (JS_IsUndefined(prop)) {
+ JS_ThrowTypeError(ctx, "left or right property must be present");
+ goto fail;
+ }
+ def = &opset->left;
+ }
+ /* get the operator set */
+ obj = JS_GetProperty(ctx, prop, JS_ATOM_prototype);
+ JS_FreeValue(ctx, prop);
+ if (JS_IsException(obj))
+ goto fail;
+ prop = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_operatorSet);
+ JS_FreeValue(ctx, obj);
+ if (JS_IsException(prop))
+ goto fail;
+ opset1 = JS_GetOpaque2(ctx, prop, JS_CLASS_OPERATOR_SET);
+ if (!opset1) {
+ JS_FreeValue(ctx, prop);
+ goto fail;
+ }
+ op_count = opset1->operator_counter;
+ JS_FreeValue(ctx, prop);
+
+ /* we assume there are few entries */
+ new_tab = js_realloc(ctx, def->tab,
+ (def->count + 1) * sizeof(def->tab[0]));
+ if (!new_tab)
+ goto fail;
+ def->tab = new_tab;
+ def->count++;
+ ent = def->tab + def->count - 1;
+ memset(ent, 0, sizeof(def->tab[0]));
+ ent->operator_index = op_count;
+
+ for(i = 0; i < JS_OVOP_BINARY_COUNT; i++) {
+ prop = JS_GetPropertyStr(ctx, arg,
+ js_overloadable_operator_names[i]);
+ if (JS_IsException(prop))
+ goto fail;
+ if (!JS_IsUndefined(prop)) {
+ if (check_function(ctx, prop)) {
+ JS_FreeValue(ctx, prop);
+ goto fail;
+ }
+ ent->ops[i] = JS_VALUE_GET_OBJ(prop);
+ }
+ }
+ }
+ opset->is_primitive = is_primitive;
+ opset->operator_counter = ctx->rt->operator_count++;
+ return opset_obj;
+ fail:
+ JS_FreeValue(ctx, opset_obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_operators_create(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_operators_create_internal(ctx, argc, argv, FALSE);
+}
+
+static JSValue js_operators_updateBigIntOperators(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue opset_obj, prop;
+ JSOperatorSetData *opset;
+ const JSOverloadableOperatorEnum ops[2] = { JS_OVOP_DIV, JS_OVOP_POW };
+ JSOverloadableOperatorEnum op;
+ int i;
+
+ opset_obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
+ JS_ATOM_Symbol_operatorSet);
+ if (JS_IsException(opset_obj))
+ goto fail;
+ opset = JS_GetOpaque2(ctx, opset_obj, JS_CLASS_OPERATOR_SET);
+ if (!opset)
+ goto fail;
+ for(i = 0; i < countof(ops); i++) {
+ op = ops[i];
+ prop = JS_GetPropertyStr(ctx, argv[0],
+ js_overloadable_operator_names[op]);
+ if (JS_IsException(prop))
+ goto fail;
+ if (!JS_IsUndefined(prop)) {
+ if (!JS_IsNull(prop) && check_function(ctx, prop)) {
+ JS_FreeValue(ctx, prop);
+ goto fail;
+ }
+ if (opset->self_ops[op])
+ JS_FreeValue(ctx, JS_MKPTR(JS_TAG_OBJECT, opset->self_ops[op]));
+ if (JS_IsNull(prop)) {
+ opset->self_ops[op] = NULL;
+ } else {
+ opset->self_ops[op] = JS_VALUE_GET_PTR(prop);
+ }
+ }
+ }
+ JS_FreeValue(ctx, opset_obj);
+ return JS_UNDEFINED;
+ fail:
+ JS_FreeValue(ctx, opset_obj);
+ return JS_EXCEPTION;
+}
+
+static int js_operators_set_default(JSContext *ctx, JSValueConst obj)
+{
+ JSValue opset_obj;
+
+ if (!JS_IsObject(obj)) /* in case the prototype is not defined */
+ return 0;
+ opset_obj = js_operators_create_internal(ctx, 0, NULL, TRUE);
+ if (JS_IsException(opset_obj))
+ return -1;
+ /* cannot be modified by the user */
+ JS_DefinePropertyValue(ctx, obj, JS_ATOM_Symbol_operatorSet,
+ opset_obj, 0);
+ return 0;
+}
+
+static JSValue js_dummy_operators_ctor(JSContext *ctx, JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ return js_create_from_ctor(ctx, new_target, JS_CLASS_OBJECT);
+}
+
+static JSValue js_global_operators(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue func_obj, proto, opset_obj;
+
+ func_obj = JS_UNDEFINED;
+ proto = JS_NewObject(ctx);
+ if (JS_IsException(proto))
+ return JS_EXCEPTION;
+ opset_obj = js_operators_create_internal(ctx, argc, argv, FALSE);
+ if (JS_IsException(opset_obj))
+ goto fail;
+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_Symbol_operatorSet,
+ opset_obj, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ func_obj = JS_NewCFunction2(ctx, js_dummy_operators_ctor, "Operators",
+ 0, JS_CFUNC_constructor, 0);
+ if (JS_IsException(func_obj))
+ goto fail;
+ JS_SetConstructor2(ctx, func_obj, proto,
+ 0, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, proto);
+ return func_obj;
+ fail:
+ JS_FreeValue(ctx, proto);
+ JS_FreeValue(ctx, func_obj);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_operators_funcs[] = {
+ JS_CFUNC_DEF("create", 1, js_operators_create ),
+ JS_CFUNC_DEF("updateBigIntOperators", 2, js_operators_updateBigIntOperators ),
+};
+
+/* must be called after all overloadable base types are initialized */
+void JS_AddIntrinsicOperators(JSContext *ctx)
+{
+ JSValue obj;
+
+ ctx->allow_operator_overloading = TRUE;
+ obj = JS_NewCFunction(ctx, js_global_operators, "Operators", 1);
+ JS_SetPropertyFunctionList(ctx, obj,
+ js_operators_funcs,
+ countof(js_operators_funcs));
+ JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_Operators,
+ obj,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ /* add default operatorSets */
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BOOLEAN]);
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_NUMBER]);
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_STRING]);
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_INT]);
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]);
+ js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
+}
+
+/* BigInt */
+
+static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val)
+{
+ uint32_t tag;
+
+ redo:
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_INT:
+ case JS_TAG_BOOL:
+ val = JS_NewBigInt64(ctx, JS_VALUE_GET_INT(val));
+ break;
+ case JS_TAG_BIG_INT:
+ break;
+ case JS_TAG_FLOAT64:
+ case JS_TAG_BIG_FLOAT:
+ {
+ bf_t *a, a_s;
+
+ a = JS_ToBigFloat(ctx, &a_s, val);
+ if (!bf_is_finite(a)) {
+ JS_FreeValue(ctx, val);
+ val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint");
+ } else {
+ JSValue val1 = JS_NewBigInt(ctx);
+ bf_t *r;
+ int ret;
+ if (JS_IsException(val1)) {
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+ }
+ r = JS_GetBigInt(val1);
+ ret = bf_set(r, a);
+ ret |= bf_rint(r, BF_RNDZ);
+ JS_FreeValue(ctx, val);
+ if (ret & BF_ST_MEM_ERROR) {
+ JS_FreeValue(ctx, val1);
+ val = JS_ThrowOutOfMemory(ctx);
+ } else if (ret & BF_ST_INEXACT) {
+ JS_FreeValue(ctx, val1);
+ val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer");
+ } else {
+ val = JS_CompactBigInt(ctx, val1);
+ }
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ val = JS_ToStringFree(ctx, val);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_STRING:
+ val = JS_StringToBigIntErr(ctx, val);
+ break;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ default:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert to bigint");
+ }
+ return val;
+}
+
+static JSValue js_bigint_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ if (!JS_IsUndefined(new_target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ return JS_ToBigIntCtorFree(ctx, JS_DupValue(ctx, argv[0]));
+}
+
+static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_IsBigInt(ctx, this_val))
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_BIG_INT) {
+ if (JS_IsBigInt(ctx, p->u.object_data))
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a bigint");
+}
+
+static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int base;
+ JSValue ret;
+
+ val = js_thisBigIntValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (argc == 0 || JS_IsUndefined(argv[0])) {
+ base = 10;
+ } else {
+ base = js_get_radix(ctx, argv[0]);
+ if (base < 0)
+ goto fail;
+ }
+ ret = js_bigint_to_string1(ctx, val, base);
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisBigIntValue(ctx, this_val);
+}
+
+static JSValue js_bigint_div(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ bf_t a_s, b_s, *a, *b, *r, *q;
+ int status;
+ JSValue q_val, r_val;
+
+ q_val = JS_NewBigInt(ctx);
+ if (JS_IsException(q_val))
+ return JS_EXCEPTION;
+ r_val = JS_NewBigInt(ctx);
+ if (JS_IsException(r_val))
+ goto fail;
+ b = NULL;
+ a = JS_ToBigInt(ctx, &a_s, argv[0]);
+ if (!a)
+ goto fail;
+ b = JS_ToBigInt(ctx, &b_s, argv[1]);
+ if (!b) {
+ JS_FreeBigInt(ctx, a, &a_s);
+ goto fail;
+ }
+ q = JS_GetBigInt(q_val);
+ r = JS_GetBigInt(r_val);
+ status = bf_divrem(q, r, a, b, BF_PREC_INF, BF_RNDZ, magic & 0xf);
+ JS_FreeBigInt(ctx, a, &a_s);
+ JS_FreeBigInt(ctx, b, &b_s);
+ if (unlikely(status)) {
+ throw_bf_exception(ctx, status);
+ goto fail;
+ }
+ q_val = JS_CompactBigInt(ctx, q_val);
+ if (magic & 0x10) {
+ JSValue ret;
+ ret = JS_NewArray(ctx);
+ if (JS_IsException(ret))
+ goto fail;
+ JS_SetPropertyUint32(ctx, ret, 0, q_val);
+ JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, r_val));
+ return ret;
+ } else {
+ JS_FreeValue(ctx, r_val);
+ return q_val;
+ }
+ fail:
+ JS_FreeValue(ctx, q_val);
+ JS_FreeValue(ctx, r_val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigint_sqrt(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ bf_t a_s, *a, *r, *rem;
+ int status;
+ JSValue r_val, rem_val;
+
+ r_val = JS_NewBigInt(ctx);
+ if (JS_IsException(r_val))
+ return JS_EXCEPTION;
+ rem_val = JS_NewBigInt(ctx);
+ if (JS_IsException(rem_val))
+ return JS_EXCEPTION;
+ r = JS_GetBigInt(r_val);
+ rem = JS_GetBigInt(rem_val);
+
+ a = JS_ToBigInt(ctx, &a_s, argv[0]);
+ if (!a)
+ goto fail;
+ status = bf_sqrtrem(r, rem, a);
+ JS_FreeBigInt(ctx, a, &a_s);
+ if (unlikely(status & ~BF_ST_INEXACT)) {
+ throw_bf_exception(ctx, status);
+ goto fail;
+ }
+ r_val = JS_CompactBigInt(ctx, r_val);
+ if (magic) {
+ JSValue ret;
+ ret = JS_NewArray(ctx);
+ if (JS_IsException(ret))
+ goto fail;
+ JS_SetPropertyUint32(ctx, ret, 0, r_val);
+ JS_SetPropertyUint32(ctx, ret, 1, JS_CompactBigInt(ctx, rem_val));
+ return ret;
+ } else {
+ JS_FreeValue(ctx, rem_val);
+ return r_val;
+ }
+ fail:
+ JS_FreeValue(ctx, r_val);
+ JS_FreeValue(ctx, rem_val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigint_op1(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv,
+ int magic)
+{
+ bf_t a_s, *a;
+ int64_t res;
+
+ a = JS_ToBigInt(ctx, &a_s, argv[0]);
+ if (!a)
+ return JS_EXCEPTION;
+ switch(magic) {
+ case 0: /* floorLog2 */
+ if (a->sign || a->expn <= 0) {
+ res = -1;
+ } else {
+ res = a->expn - 1;
+ }
+ break;
+ case 1: /* ctz */
+ if (bf_is_zero(a)) {
+ res = -1;
+ } else {
+ res = bf_get_exp_min(a);
+ }
+ break;
+ default:
+ abort();
+ }
+ JS_FreeBigInt(ctx, a, &a_s);
+ return JS_NewBigInt64(ctx, res);
+}
+
+static JSValue js_bigint_asUintN(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int asIntN)
+{
+ uint64_t bits;
+ bf_t a_s, *a = &a_s, *r, mask_s, *mask = &mask_s;
+ JSValue res;
+
+ if (JS_ToIndex(ctx, &bits, argv[0]))
+ return JS_EXCEPTION;
+ res = JS_NewBigInt(ctx);
+ if (JS_IsException(res))
+ return JS_EXCEPTION;
+ r = JS_GetBigInt(res);
+ a = JS_ToBigInt(ctx, &a_s, argv[1]);
+ if (!a) {
+ JS_FreeValue(ctx, res);
+ return JS_EXCEPTION;
+ }
+ /* XXX: optimize */
+ r = JS_GetBigInt(res);
+ bf_init(ctx->bf_ctx, mask);
+ bf_set_ui(mask, 1);
+ bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
+ bf_add_si(mask, mask, -1, BF_PREC_INF, BF_RNDZ);
+ bf_logic_and(r, a, mask);
+ if (asIntN && bits != 0) {
+ bf_set_ui(mask, 1);
+ bf_mul_2exp(mask, bits - 1, BF_PREC_INF, BF_RNDZ);
+ if (bf_cmpu(r, mask) >= 0) {
+ bf_set_ui(mask, 1);
+ bf_mul_2exp(mask, bits, BF_PREC_INF, BF_RNDZ);
+ bf_sub(r, r, mask, BF_PREC_INF, BF_RNDZ);
+ }
+ }
+ bf_delete(mask);
+ JS_FreeBigInt(ctx, a, &a_s);
+ return JS_CompactBigInt(ctx, res);
+}
+
+static const JSCFunctionListEntry js_bigint_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ),
+ JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ),
+ /* QuickJS extensions */
+ JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ),
+ JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ),
+ JS_CFUNC_MAGIC_DEF("cdiv", 2, js_bigint_div, BF_RNDU ),
+ JS_CFUNC_MAGIC_DEF("ediv", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN ),
+ JS_CFUNC_MAGIC_DEF("tdivrem", 2, js_bigint_div, BF_RNDZ | 0x10 ),
+ JS_CFUNC_MAGIC_DEF("fdivrem", 2, js_bigint_div, BF_RNDD | 0x10 ),
+ JS_CFUNC_MAGIC_DEF("cdivrem", 2, js_bigint_div, BF_RNDU | 0x10 ),
+ JS_CFUNC_MAGIC_DEF("edivrem", 2, js_bigint_div, BF_DIVREM_EUCLIDIAN | 0x10 ),
+ JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigint_sqrt, 0 ),
+ JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ),
+ JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ),
+ JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ),
+};
+
+static const JSCFunctionListEntry js_bigint_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_bigint_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_bigint_valueOf ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "BigInt", JS_PROP_CONFIGURABLE ),
+};
+
+void JS_AddIntrinsicBigInt(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValueConst obj1;
+
+ rt->bigint_ops.to_string = js_bigint_to_string;
+ rt->bigint_ops.from_string = js_string_to_bigint;
+ rt->bigint_ops.unary_arith = js_unary_arith_bigint;
+ rt->bigint_ops.binary_arith = js_binary_arith_bigint;
+ rt->bigint_ops.compare = js_compare_bigfloat;
+
+ ctx->class_proto[JS_CLASS_BIG_INT] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_INT],
+ js_bigint_proto_funcs,
+ countof(js_bigint_proto_funcs));
+ obj1 = JS_NewGlobalCConstructor(ctx, "BigInt", js_bigint_constructor, 1,
+ ctx->class_proto[JS_CLASS_BIG_INT]);
+ JS_SetPropertyFunctionList(ctx, obj1, js_bigint_funcs,
+ countof(js_bigint_funcs));
+}
+
+/* BigFloat */
+
+static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_IsBigFloat(this_val))
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_BIG_FLOAT) {
+ if (JS_IsBigFloat(p->u.object_data))
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a bigfloat");
+}
+
+static JSValue js_bigfloat_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ int base;
+ JSValue ret;
+
+ val = js_thisBigFloatValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (argc == 0 || JS_IsUndefined(argv[0])) {
+ base = 10;
+ } else {
+ base = js_get_radix(ctx, argv[0]);
+ if (base < 0)
+ goto fail;
+ }
+ ret = js_ftoa(ctx, val, base, 0, BF_RNDN | BF_FTOA_FORMAT_FREE_MIN);
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigfloat_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisBigFloatValue(ctx, this_val);
+}
+
+static int bigfloat_get_rnd_mode(JSContext *ctx, JSValueConst val)
+{
+ int rnd_mode;
+ if (JS_ToInt32Sat(ctx, &rnd_mode, val))
+ return -1;
+ if (rnd_mode < BF_RNDN || rnd_mode > BF_RNDF) {
+ JS_ThrowRangeError(ctx, "invalid rounding mode");
+ return -1;
+ }
+ return rnd_mode;
+}
+
+static JSValue js_bigfloat_toFixed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t f;
+ int rnd_mode, radix;
+
+ val = js_thisBigFloatValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToInt64Sat(ctx, &f, argv[0]))
+ goto fail;
+ if (f < 0 || f > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ radix = 10;
+ /* XXX: swap parameter order for rounding mode and radix */
+ if (argc > 1) {
+ rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ if (argc > 2) {
+ radix = js_get_radix(ctx, argv[2]);
+ if (radix < 0)
+ goto fail;
+ }
+ ret = js_ftoa(ctx, val, radix, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static BOOL js_bigfloat_is_finite(JSContext *ctx, JSValueConst val)
+{
+ BOOL res;
+ uint32_t tag;
+
+ tag = JS_VALUE_GET_NORM_TAG(val);
+ switch(tag) {
+ case JS_TAG_BIG_FLOAT:
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ res = bf_is_finite(&p->num);
+ }
+ break;
+ default:
+ res = FALSE;
+ break;
+ }
+ return res;
+}
+
+static JSValue js_bigfloat_toExponential(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t f;
+ int rnd_mode, radix;
+
+ val = js_thisBigFloatValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToInt64Sat(ctx, &f, argv[0]))
+ goto fail;
+ if (!js_bigfloat_is_finite(ctx, val)) {
+ ret = JS_ToString(ctx, val);
+ } else if (JS_IsUndefined(argv[0])) {
+ ret = js_ftoa(ctx, val, 10, 0,
+ BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
+ } else {
+ if (f < 0 || f > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ radix = 10;
+ if (argc > 1) {
+ rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ if (argc > 2) {
+ radix = js_get_radix(ctx, argv[2]);
+ if (radix < 0)
+ goto fail;
+ }
+ ret = js_ftoa(ctx, val, radix, f + 1,
+ rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigfloat_toPrecision(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t p;
+ int rnd_mode, radix;
+
+ val = js_thisBigFloatValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_IsUndefined(argv[0]))
+ goto to_string;
+ if (JS_ToInt64Sat(ctx, &p, argv[0]))
+ goto fail;
+ if (!js_bigfloat_is_finite(ctx, val)) {
+ to_string:
+ ret = JS_ToString(ctx, this_val);
+ } else {
+ if (p < 1 || p > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ radix = 10;
+ if (argc > 1) {
+ rnd_mode = bigfloat_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ if (argc > 2) {
+ radix = js_get_radix(ctx, argv[2]);
+ if (radix < 0)
+ goto fail;
+ }
+ ret = js_ftoa(ctx, val, radix, p, rnd_mode | BF_FTOA_FORMAT_FIXED);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_bigfloat_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_bigfloat_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_bigfloat_valueOf ),
+ JS_CFUNC_DEF("toPrecision", 1, js_bigfloat_toPrecision ),
+ JS_CFUNC_DEF("toFixed", 1, js_bigfloat_toFixed ),
+ JS_CFUNC_DEF("toExponential", 1, js_bigfloat_toExponential ),
+};
+
+static JSValue js_bigfloat_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ if (!JS_IsUndefined(new_target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ if (argc == 0) {
+ bf_t *r;
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ return val;
+ r = JS_GetBigFloat(val);
+ bf_set_zero(r, 0);
+ } else {
+ val = JS_DupValue(ctx, argv[0]);
+ redo:
+ switch(JS_VALUE_GET_NORM_TAG(val)) {
+ case JS_TAG_BIG_FLOAT:
+ break;
+ case JS_TAG_FLOAT64:
+ {
+ bf_t *r;
+ double d = JS_VALUE_GET_FLOAT64(val);
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigFloat(val);
+ if (bf_set_float64(r, d))
+ goto fail;
+ }
+ break;
+ case JS_TAG_INT:
+ {
+ bf_t *r;
+ int32_t v = JS_VALUE_GET_INT(val);
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigFloat(val);
+ if (bf_set_si(r, v))
+ goto fail;
+ }
+ break;
+ case JS_TAG_BIG_INT:
+ /* We keep the full precision of the integer */
+ {
+ JSBigFloat *p = JS_VALUE_GET_PTR(val);
+ val = JS_MKPTR(JS_TAG_BIG_FLOAT, p);
+ }
+ break;
+ case JS_TAG_BIG_DECIMAL:
+ val = JS_ToStringFree(ctx, val);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_STRING:
+ {
+ const char *str, *p;
+ size_t len;
+ int err;
+
+ str = JS_ToCStringLen(ctx, &len, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ if ((p - str) == len) {
+ bf_t *r;
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigFloat(val);
+ bf_set_zero(r, 0);
+ err = 0;
+ } else {
+ val = js_atof(ctx, p, &p, 0, ATOD_ACCEPT_BIN_OCT |
+ ATOD_TYPE_BIG_FLOAT |
+ ATOD_ACCEPT_PREFIX_AFTER_SIGN);
+ if (JS_IsException(val)) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ p += skip_spaces(p);
+ err = ((p - str) != len);
+ }
+ JS_FreeCString(ctx, str);
+ if (err) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowSyntaxError(ctx, "invalid bigfloat literal");
+ }
+ }
+ break;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_NULL:
+ case JS_TAG_UNDEFINED:
+ default:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert to bigfloat");
+ }
+ }
+ return val;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigfloat_get_const(JSContext *ctx,
+ JSValueConst this_val, int magic)
+{
+ bf_t *r;
+ JSValue val;
+ val = JS_NewBigFloat(ctx);
+ if (JS_IsException(val))
+ return val;
+ r = JS_GetBigFloat(val);
+ switch(magic) {
+ case 0: /* PI */
+ bf_const_pi(r, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case 1: /* LN2 */
+ bf_const_log2(r, ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ case 2: /* MIN_VALUE */
+ case 3: /* MAX_VALUE */
+ {
+ slimb_t e_range, e;
+ e_range = (limb_t)1 << (bf_get_exp_bits(ctx->fp_env.flags) - 1);
+ bf_set_ui(r, 1);
+ if (magic == 2) {
+ e = -e_range + 2;
+ if (ctx->fp_env.flags & BF_FLAG_SUBNORMAL)
+ e -= ctx->fp_env.prec - 1;
+ bf_mul_2exp(r, e, ctx->fp_env.prec, ctx->fp_env.flags);
+ } else {
+ bf_mul_2exp(r, ctx->fp_env.prec, ctx->fp_env.prec,
+ ctx->fp_env.flags);
+ bf_add_si(r, r, -1, ctx->fp_env.prec, ctx->fp_env.flags);
+ bf_mul_2exp(r, e_range - ctx->fp_env.prec, ctx->fp_env.prec,
+ ctx->fp_env.flags);
+ }
+ }
+ break;
+ case 4: /* EPSILON */
+ bf_set_ui(r, 1);
+ bf_mul_2exp(r, 1 - ctx->fp_env.prec,
+ ctx->fp_env.prec, ctx->fp_env.flags);
+ break;
+ default:
+ abort();
+ }
+ return val;
+}
+
+static JSValue js_bigfloat_parseFloat(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ bf_t *a;
+ const char *str;
+ JSValue ret;
+ int radix;
+ JSFloatEnv *fe;
+
+ str = JS_ToCString(ctx, argv[0]);
+ if (!str)
+ return JS_EXCEPTION;
+ if (JS_ToInt32(ctx, &radix, argv[1])) {
+ fail:
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ if (radix != 0 && (radix < 2 || radix > 36)) {
+ JS_ThrowRangeError(ctx, "radix must be between 2 and 36");
+ goto fail;
+ }
+ fe = &ctx->fp_env;
+ if (argc > 2) {
+ fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ goto fail;
+ }
+ ret = JS_NewBigFloat(ctx);
+ if (JS_IsException(ret))
+ goto done;
+ a = JS_GetBigFloat(ret);
+ /* XXX: use js_atof() */
+ bf_atof(a, str, NULL, radix, fe->prec, fe->flags);
+ done:
+ JS_FreeCString(ctx, str);
+ return ret;
+}
+
+static JSValue js_bigfloat_isFinite(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst val = argv[0];
+ JSBigFloat *p;
+
+ if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
+ return JS_FALSE;
+ p = JS_VALUE_GET_PTR(val);
+ return JS_NewBool(ctx, bf_is_finite(&p->num));
+}
+
+static JSValue js_bigfloat_isNaN(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst val = argv[0];
+ JSBigFloat *p;
+
+ if (JS_VALUE_GET_NORM_TAG(val) != JS_TAG_BIG_FLOAT)
+ return JS_FALSE;
+ p = JS_VALUE_GET_PTR(val);
+ return JS_NewBool(ctx, bf_is_nan(&p->num));
+}
+
+enum {
+ MATH_OP_ABS,
+ MATH_OP_FLOOR,
+ MATH_OP_CEIL,
+ MATH_OP_ROUND,
+ MATH_OP_TRUNC,
+ MATH_OP_SQRT,
+ MATH_OP_FPROUND,
+ MATH_OP_ACOS,
+ MATH_OP_ASIN,
+ MATH_OP_ATAN,
+ MATH_OP_ATAN2,
+ MATH_OP_COS,
+ MATH_OP_EXP,
+ MATH_OP_LOG,
+ MATH_OP_POW,
+ MATH_OP_SIN,
+ MATH_OP_TAN,
+ MATH_OP_FMOD,
+ MATH_OP_REM,
+ MATH_OP_SIGN,
+
+ MATH_OP_ADD,
+ MATH_OP_SUB,
+ MATH_OP_MUL,
+ MATH_OP_DIV,
+};
+
+static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ bf_t a_s, *a, *r;
+ JSFloatEnv *fe;
+ int rnd_mode;
+ JSValue op1, res;
+
+ op1 = JS_ToNumeric(ctx, argv[0]);
+ if (JS_IsException(op1))
+ return op1;
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ fe = &ctx->fp_env;
+ if (argc > 1) {
+ fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ goto fail;
+ }
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res)) {
+ fail:
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, op1);
+ return JS_EXCEPTION;
+ }
+ r = JS_GetBigFloat(res);
+ switch (magic) {
+ case MATH_OP_ABS:
+ bf_set(r, a);
+ r->sign = 0;
+ break;
+ case MATH_OP_FLOOR:
+ rnd_mode = BF_RNDD;
+ goto rint;
+ case MATH_OP_CEIL:
+ rnd_mode = BF_RNDU;
+ goto rint;
+ case MATH_OP_ROUND:
+ rnd_mode = BF_RNDNA;
+ goto rint;
+ case MATH_OP_TRUNC:
+ rnd_mode = BF_RNDZ;
+ rint:
+ bf_set(r, a);
+ fe->status |= bf_rint(r, rnd_mode);
+ break;
+ case MATH_OP_SQRT:
+ fe->status |= bf_sqrt(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_FPROUND:
+ bf_set(r, a);
+ fe->status |= bf_round(r, fe->prec, fe->flags);
+ break;
+ case MATH_OP_ACOS:
+ fe->status |= bf_acos(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_ASIN:
+ fe->status |= bf_asin(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_ATAN:
+ fe->status |= bf_atan(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_COS:
+ fe->status |= bf_cos(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_EXP:
+ fe->status |= bf_exp(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_LOG:
+ fe->status |= bf_log(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_SIN:
+ fe->status |= bf_sin(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_TAN:
+ fe->status |= bf_tan(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_SIGN:
+ if (bf_is_nan(a) || bf_is_zero(a)) {
+ bf_set(r, a);
+ } else {
+ bf_set_si(r, 1 - 2 * a->sign);
+ }
+ break;
+ default:
+ abort();
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ JS_FreeValue(ctx, op1);
+ return res;
+}
+
+static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ bf_t a_s, *a, b_s, *b, r_s, *r = &r_s;
+ JSFloatEnv *fe;
+ JSValue op1, op2, res;
+
+ op1 = JS_ToNumeric(ctx, argv[0]);
+ if (JS_IsException(op1))
+ return op1;
+ op2 = JS_ToNumeric(ctx, argv[1]);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ return op2;
+ }
+ a = JS_ToBigFloat(ctx, &a_s, op1);
+ b = JS_ToBigFloat(ctx, &b_s, op2);
+ fe = &ctx->fp_env;
+ if (argc > 2) {
+ fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ goto fail;
+ }
+ res = JS_NewBigFloat(ctx);
+ if (JS_IsException(res)) {
+ fail:
+ if (a == &a_s)
+ bf_delete(a);
+ if (b == &b_s)
+ bf_delete(b);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return JS_EXCEPTION;
+ }
+ r = JS_GetBigFloat(res);
+ switch (magic) {
+ case MATH_OP_ATAN2:
+ fe->status |= bf_atan2(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_POW:
+ fe->status |= bf_pow(r, a, b, fe->prec, fe->flags | BF_POW_JS_QUIRKS);
+ break;
+ case MATH_OP_FMOD:
+ fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
+ break;
+ case MATH_OP_REM:
+ fe->status |= bf_rem(r, a, b, fe->prec, fe->flags, BF_RNDN);
+ break;
+ case MATH_OP_ADD:
+ fe->status |= bf_add(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_SUB:
+ fe->status |= bf_sub(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_MUL:
+ fe->status |= bf_mul(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_DIV:
+ fe->status |= bf_div(r, a, b, fe->prec, fe->flags);
+ break;
+ default:
+ abort();
+ }
+ if (a == &a_s)
+ bf_delete(a);
+ if (b == &b_s)
+ bf_delete(b);
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return res;
+}
+
+static const JSCFunctionListEntry js_bigfloat_funcs[] = {
+ JS_CGETSET_MAGIC_DEF("PI", js_bigfloat_get_const, NULL, 0 ),
+ JS_CGETSET_MAGIC_DEF("LN2", js_bigfloat_get_const, NULL, 1 ),
+ JS_CGETSET_MAGIC_DEF("MIN_VALUE", js_bigfloat_get_const, NULL, 2 ),
+ JS_CGETSET_MAGIC_DEF("MAX_VALUE", js_bigfloat_get_const, NULL, 3 ),
+ JS_CGETSET_MAGIC_DEF("EPSILON", js_bigfloat_get_const, NULL, 4 ),
+ JS_CFUNC_DEF("parseFloat", 1, js_bigfloat_parseFloat ),
+ JS_CFUNC_DEF("isFinite", 1, js_bigfloat_isFinite ),
+ JS_CFUNC_DEF("isNaN", 1, js_bigfloat_isNaN ),
+ JS_CFUNC_MAGIC_DEF("abs", 1, js_bigfloat_fop, MATH_OP_ABS ),
+ JS_CFUNC_MAGIC_DEF("fpRound", 1, js_bigfloat_fop, MATH_OP_FPROUND ),
+ JS_CFUNC_MAGIC_DEF("floor", 1, js_bigfloat_fop, MATH_OP_FLOOR ),
+ JS_CFUNC_MAGIC_DEF("ceil", 1, js_bigfloat_fop, MATH_OP_CEIL ),
+ JS_CFUNC_MAGIC_DEF("round", 1, js_bigfloat_fop, MATH_OP_ROUND ),
+ JS_CFUNC_MAGIC_DEF("trunc", 1, js_bigfloat_fop, MATH_OP_TRUNC ),
+ JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigfloat_fop, MATH_OP_SQRT ),
+ JS_CFUNC_MAGIC_DEF("acos", 1, js_bigfloat_fop, MATH_OP_ACOS ),
+ JS_CFUNC_MAGIC_DEF("asin", 1, js_bigfloat_fop, MATH_OP_ASIN ),
+ JS_CFUNC_MAGIC_DEF("atan", 1, js_bigfloat_fop, MATH_OP_ATAN ),
+ JS_CFUNC_MAGIC_DEF("atan2", 2, js_bigfloat_fop2, MATH_OP_ATAN2 ),
+ JS_CFUNC_MAGIC_DEF("cos", 1, js_bigfloat_fop, MATH_OP_COS ),
+ JS_CFUNC_MAGIC_DEF("exp", 1, js_bigfloat_fop, MATH_OP_EXP ),
+ JS_CFUNC_MAGIC_DEF("log", 1, js_bigfloat_fop, MATH_OP_LOG ),
+ JS_CFUNC_MAGIC_DEF("pow", 2, js_bigfloat_fop2, MATH_OP_POW ),
+ JS_CFUNC_MAGIC_DEF("sin", 1, js_bigfloat_fop, MATH_OP_SIN ),
+ JS_CFUNC_MAGIC_DEF("tan", 1, js_bigfloat_fop, MATH_OP_TAN ),
+ JS_CFUNC_MAGIC_DEF("sign", 1, js_bigfloat_fop, MATH_OP_SIGN ),
+ JS_CFUNC_MAGIC_DEF("add", 2, js_bigfloat_fop2, MATH_OP_ADD ),
+ JS_CFUNC_MAGIC_DEF("sub", 2, js_bigfloat_fop2, MATH_OP_SUB ),
+ JS_CFUNC_MAGIC_DEF("mul", 2, js_bigfloat_fop2, MATH_OP_MUL ),
+ JS_CFUNC_MAGIC_DEF("div", 2, js_bigfloat_fop2, MATH_OP_DIV ),
+ JS_CFUNC_MAGIC_DEF("fmod", 2, js_bigfloat_fop2, MATH_OP_FMOD ),
+ JS_CFUNC_MAGIC_DEF("remainder", 2, js_bigfloat_fop2, MATH_OP_REM ),
+};
+
+/* FloatEnv */
+
+static JSValue js_float_env_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ JSFloatEnv *fe;
+ int64_t prec;
+ int flags, rndmode;
+
+ prec = ctx->fp_env.prec;
+ flags = ctx->fp_env.flags;
+ if (!JS_IsUndefined(argv[0])) {
+ if (JS_ToInt64Sat(ctx, &prec, argv[0]))
+ return JS_EXCEPTION;
+ if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
+ return JS_ThrowRangeError(ctx, "invalid precision");
+ flags = BF_RNDN; /* RNDN, max exponent size, no subnormal */
+ if (argc > 1 && !JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Sat(ctx, &rndmode, argv[1]))
+ return JS_EXCEPTION;
+ if (rndmode < BF_RNDN || rndmode > BF_RNDF)
+ return JS_ThrowRangeError(ctx, "invalid rounding mode");
+ flags = rndmode;
+ }
+ }
+
+ obj = JS_NewObjectClass(ctx, JS_CLASS_FLOAT_ENV);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ fe = js_malloc(ctx, sizeof(*fe));
+ if (!fe)
+ return JS_EXCEPTION;
+ fe->prec = prec;
+ fe->flags = flags;
+ fe->status = 0;
+ JS_SetOpaque(obj, fe);
+ return obj;
+}
+
+static void js_float_env_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSFloatEnv *fe = JS_GetOpaque(val, JS_CLASS_FLOAT_ENV);
+ js_free_rt(rt, fe);
+}
+
+static JSValue js_float_env_get_prec(JSContext *ctx, JSValueConst this_val)
+{
+ return JS_NewInt64(ctx, ctx->fp_env.prec);
+}
+
+static JSValue js_float_env_get_expBits(JSContext *ctx, JSValueConst this_val)
+{
+ return JS_NewInt32(ctx, bf_get_exp_bits(ctx->fp_env.flags));
+}
+
+static JSValue js_float_env_setPrec(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst func;
+ int exp_bits, flags, saved_flags;
+ JSValue ret;
+ limb_t saved_prec;
+ int64_t prec;
+
+ func = argv[0];
+ if (JS_ToInt64Sat(ctx, &prec, argv[1]))
+ return JS_EXCEPTION;
+ if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
+ return JS_ThrowRangeError(ctx, "invalid precision");
+ exp_bits = BF_EXP_BITS_MAX;
+
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt32Sat(ctx, &exp_bits, argv[2]))
+ return JS_EXCEPTION;
+ if (exp_bits < BF_EXP_BITS_MIN || exp_bits > BF_EXP_BITS_MAX)
+ return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
+ }
+
+ flags = BF_RNDN | BF_FLAG_SUBNORMAL | bf_set_exp_bits(exp_bits);
+
+ saved_prec = ctx->fp_env.prec;
+ saved_flags = ctx->fp_env.flags;
+
+ ctx->fp_env.prec = prec;
+ ctx->fp_env.flags = flags;
+
+ ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL);
+ /* always restore the floating point precision */
+ ctx->fp_env.prec = saved_prec;
+ ctx->fp_env.flags = saved_flags;
+ return ret;
+}
+
+#define FE_PREC (-1)
+#define FE_EXP (-2)
+#define FE_RNDMODE (-3)
+#define FE_SUBNORMAL (-4)
+
+static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_val, int magic)
+{
+ JSFloatEnv *fe;
+ fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ return JS_EXCEPTION;
+ switch(magic) {
+ case FE_PREC:
+ return JS_NewInt64(ctx, fe->prec);
+ case FE_EXP:
+ return JS_NewInt32(ctx, bf_get_exp_bits(fe->flags));
+ case FE_RNDMODE:
+ return JS_NewInt32(ctx, fe->flags & BF_RND_MASK);
+ case FE_SUBNORMAL:
+ return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0);
+ default:
+ return JS_NewBool(ctx, (fe->status & magic) != 0);
+ }
+}
+
+static JSValue js_float_env_proto_set_status(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic)
+{
+ JSFloatEnv *fe;
+ int b;
+ int64_t prec;
+
+ fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ return JS_EXCEPTION;
+ switch(magic) {
+ case FE_PREC:
+ if (JS_ToInt64Sat(ctx, &prec, val))
+ return JS_EXCEPTION;
+ if (prec < BF_PREC_MIN || prec > BF_PREC_MAX)
+ return JS_ThrowRangeError(ctx, "invalid precision");
+ fe->prec = prec;
+ break;
+ case FE_EXP:
+ if (JS_ToInt32Sat(ctx, &b, val))
+ return JS_EXCEPTION;
+ if (b < BF_EXP_BITS_MIN || b > BF_EXP_BITS_MAX)
+ return JS_ThrowRangeError(ctx, "invalid number of exponent bits");
+ fe->flags = (fe->flags & ~(BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT)) |
+ bf_set_exp_bits(b);
+ break;
+ case FE_RNDMODE:
+ b = bigfloat_get_rnd_mode(ctx, val);
+ if (b < 0)
+ return JS_EXCEPTION;
+ fe->flags = (fe->flags & ~BF_RND_MASK) | b;
+ break;
+ case FE_SUBNORMAL:
+ b = JS_ToBool(ctx, val);
+ fe->flags = (fe->flags & ~BF_FLAG_SUBNORMAL) | (b ? BF_FLAG_SUBNORMAL: 0);
+ break;
+ default:
+ b = JS_ToBool(ctx, val);
+ fe->status = (fe->status & ~magic) & ((-b) & magic);
+ break;
+ }
+ return JS_UNDEFINED;
+}
+
+static JSValue js_float_env_clearStatus(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSFloatEnv *fe = JS_GetOpaque2(ctx, this_val, JS_CLASS_FLOAT_ENV);
+ if (!fe)
+ return JS_EXCEPTION;
+ fe->status = 0;
+ return JS_UNDEFINED;
+}
+
+static const JSCFunctionListEntry js_float_env_funcs[] = {
+ JS_CGETSET_DEF("prec", js_float_env_get_prec, NULL ),
+ JS_CGETSET_DEF("expBits", js_float_env_get_expBits, NULL ),
+ JS_CFUNC_DEF("setPrec", 2, js_float_env_setPrec ),
+ JS_PROP_INT32_DEF("RNDN", BF_RNDN, 0 ),
+ JS_PROP_INT32_DEF("RNDZ", BF_RNDZ, 0 ),
+ JS_PROP_INT32_DEF("RNDU", BF_RNDU, 0 ),
+ JS_PROP_INT32_DEF("RNDD", BF_RNDD, 0 ),
+ JS_PROP_INT32_DEF("RNDNA", BF_RNDNA, 0 ),
+ JS_PROP_INT32_DEF("RNDA", BF_RNDA, 0 ),
+ JS_PROP_INT32_DEF("RNDF", BF_RNDF, 0 ),
+ JS_PROP_INT32_DEF("precMin", BF_PREC_MIN, 0 ),
+ JS_PROP_INT64_DEF("precMax", BF_PREC_MAX, 0 ),
+ JS_PROP_INT32_DEF("expBitsMin", BF_EXP_BITS_MIN, 0 ),
+ JS_PROP_INT32_DEF("expBitsMax", BF_EXP_BITS_MAX, 0 ),
+};
+
+static const JSCFunctionListEntry js_float_env_proto_funcs[] = {
+ JS_CGETSET_MAGIC_DEF("prec", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, FE_PREC ),
+ JS_CGETSET_MAGIC_DEF("expBits", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, FE_EXP ),
+ JS_CGETSET_MAGIC_DEF("rndMode", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, FE_RNDMODE ),
+ JS_CGETSET_MAGIC_DEF("subnormal", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, FE_SUBNORMAL ),
+ JS_CGETSET_MAGIC_DEF("invalidOperation", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, BF_ST_INVALID_OP ),
+ JS_CGETSET_MAGIC_DEF("divideByZero", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, BF_ST_DIVIDE_ZERO ),
+ JS_CGETSET_MAGIC_DEF("overflow", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, BF_ST_OVERFLOW ),
+ JS_CGETSET_MAGIC_DEF("underflow", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, BF_ST_UNDERFLOW ),
+ JS_CGETSET_MAGIC_DEF("inexact", js_float_env_proto_get_status,
+ js_float_env_proto_set_status, BF_ST_INEXACT ),
+ JS_CFUNC_DEF("clearStatus", 0, js_float_env_clearStatus ),
+};
+
+void JS_AddIntrinsicBigFloat(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValueConst obj1;
+
+ rt->bigfloat_ops.to_string = js_bigfloat_to_string;
+ rt->bigfloat_ops.from_string = js_string_to_bigfloat;
+ rt->bigfloat_ops.unary_arith = js_unary_arith_bigfloat;
+ rt->bigfloat_ops.binary_arith = js_binary_arith_bigfloat;
+ rt->bigfloat_ops.compare = js_compare_bigfloat;
+ rt->bigfloat_ops.mul_pow10_to_float64 = js_mul_pow10_to_float64;
+ rt->bigfloat_ops.mul_pow10 = js_mul_pow10;
+
+ ctx->class_proto[JS_CLASS_BIG_FLOAT] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT],
+ js_bigfloat_proto_funcs,
+ countof(js_bigfloat_proto_funcs));
+ obj1 = JS_NewGlobalCConstructor(ctx, "BigFloat", js_bigfloat_constructor, 1,
+ ctx->class_proto[JS_CLASS_BIG_FLOAT]);
+ JS_SetPropertyFunctionList(ctx, obj1, js_bigfloat_funcs,
+ countof(js_bigfloat_funcs));
+
+ ctx->class_proto[JS_CLASS_FLOAT_ENV] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FLOAT_ENV],
+ js_float_env_proto_funcs,
+ countof(js_float_env_proto_funcs));
+ obj1 = JS_NewGlobalCConstructorOnly(ctx, "BigFloatEnv",
+ js_float_env_constructor, 1,
+ ctx->class_proto[JS_CLASS_FLOAT_ENV]);
+ JS_SetPropertyFunctionList(ctx, obj1, js_float_env_funcs,
+ countof(js_float_env_funcs));
+}
+
+/* BigDecimal */
+
+static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
+ BOOL allow_null_or_undefined)
+{
+ redo:
+ switch(JS_VALUE_GET_NORM_TAG(val)) {
+ case JS_TAG_BIG_DECIMAL:
+ break;
+ case JS_TAG_NULL:
+ if (!allow_null_or_undefined)
+ goto fail;
+ /* fall thru */
+ case JS_TAG_BOOL:
+ case JS_TAG_INT:
+ {
+ bfdec_t *r;
+ int32_t v = JS_VALUE_GET_INT(val);
+
+ val = JS_NewBigDecimal(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigDecimal(val);
+ if (bfdec_set_si(r, v)) {
+ JS_FreeValue(ctx, val);
+ val = JS_EXCEPTION;
+ break;
+ }
+ }
+ break;
+ case JS_TAG_FLOAT64:
+ case JS_TAG_BIG_INT:
+ case JS_TAG_BIG_FLOAT:
+ val = JS_ToStringFree(ctx, val);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_STRING:
+ {
+ const char *str, *p;
+ size_t len;
+ int err;
+
+ str = JS_ToCStringLen(ctx, &len, val);
+ JS_FreeValue(ctx, val);
+ if (!str)
+ return JS_EXCEPTION;
+ p = str;
+ p += skip_spaces(p);
+ if ((p - str) == len) {
+ bfdec_t *r;
+ val = JS_NewBigDecimal(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigDecimal(val);
+ bfdec_set_zero(r, 0);
+ err = 0;
+ } else {
+ val = js_atof(ctx, p, &p, 0, ATOD_TYPE_BIG_DECIMAL);
+ if (JS_IsException(val)) {
+ JS_FreeCString(ctx, str);
+ return JS_EXCEPTION;
+ }
+ p += skip_spaces(p);
+ err = ((p - str) != len);
+ }
+ JS_FreeCString(ctx, str);
+ if (err) {
+ JS_FreeValue(ctx, val);
+ return JS_ThrowSyntaxError(ctx, "invalid bigdecimal literal");
+ }
+ }
+ break;
+ case JS_TAG_OBJECT:
+ val = JS_ToPrimitiveFree(ctx, val, HINT_NUMBER);
+ if (JS_IsException(val))
+ break;
+ goto redo;
+ case JS_TAG_UNDEFINED:
+ {
+ bfdec_t *r;
+ if (!allow_null_or_undefined)
+ goto fail;
+ val = JS_NewBigDecimal(ctx);
+ if (JS_IsException(val))
+ break;
+ r = JS_GetBigDecimal(val);
+ bfdec_set_nan(r);
+ }
+ break;
+ default:
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_ThrowTypeError(ctx, "cannot convert to bigdecimal");
+ }
+ return val;
+}
+
+static JSValue js_bigdecimal_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+ if (!JS_IsUndefined(new_target))
+ return JS_ThrowTypeError(ctx, "not a constructor");
+ if (argc == 0) {
+ bfdec_t *r;
+ val = JS_NewBigDecimal(ctx);
+ if (JS_IsException(val))
+ return val;
+ r = JS_GetBigDecimal(val);
+ bfdec_set_zero(r, 0);
+ } else {
+ val = JS_ToBigDecimalFree(ctx, JS_DupValue(ctx, argv[0]), FALSE);
+ }
+ return val;
+}
+
+static JSValue js_thisBigDecimalValue(JSContext *ctx, JSValueConst this_val)
+{
+ if (JS_IsBigDecimal(this_val))
+ return JS_DupValue(ctx, this_val);
+
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+ if (p->class_id == JS_CLASS_BIG_DECIMAL) {
+ if (JS_IsBigDecimal(p->u.object_data))
+ return JS_DupValue(ctx, p->u.object_data);
+ }
+ }
+ return JS_ThrowTypeError(ctx, "not a bigdecimal");
+}
+
+static JSValue js_bigdecimal_toString(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val;
+
+ val = js_thisBigDecimalValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ return JS_ToStringFree(ctx, val);
+}
+
+static JSValue js_bigdecimal_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_thisBigDecimalValue(ctx, this_val);
+}
+
+static int js_bigdecimal_get_rnd_mode(JSContext *ctx, JSValueConst obj)
+{
+ const char *str;
+ size_t size;
+ int rnd_mode;
+
+ str = JS_ToCStringLen(ctx, &size, obj);
+ if (!str)
+ return -1;
+ if (strlen(str) != size)
+ goto invalid_rounding_mode;
+ if (!strcmp(str, "floor")) {
+ rnd_mode = BF_RNDD;
+ } else if (!strcmp(str, "ceiling")) {
+ rnd_mode = BF_RNDU;
+ } else if (!strcmp(str, "down")) {
+ rnd_mode = BF_RNDZ;
+ } else if (!strcmp(str, "up")) {
+ rnd_mode = BF_RNDA;
+ } else if (!strcmp(str, "half-even")) {
+ rnd_mode = BF_RNDN;
+ } else if (!strcmp(str, "half-up")) {
+ rnd_mode = BF_RNDNA;
+ } else {
+ invalid_rounding_mode:
+ JS_FreeCString(ctx, str);
+ JS_ThrowTypeError(ctx, "invalid rounding mode");
+ return -1;
+ }
+ JS_FreeCString(ctx, str);
+ return rnd_mode;
+}
+
+typedef struct {
+ int64_t prec;
+ bf_flags_t flags;
+} BigDecimalEnv;
+
+static int js_bigdecimal_get_env(JSContext *ctx, BigDecimalEnv *fe,
+ JSValueConst obj)
+{
+ JSValue prop;
+ int64_t val;
+ BOOL has_prec;
+ int rnd_mode;
+
+ if (!JS_IsObject(obj)) {
+ JS_ThrowTypeErrorNotAnObject(ctx);
+ return -1;
+ }
+ prop = JS_GetProperty(ctx, obj, JS_ATOM_roundingMode);
+ if (JS_IsException(prop))
+ return -1;
+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, prop);
+ JS_FreeValue(ctx, prop);
+ if (rnd_mode < 0)
+ return -1;
+ fe->flags = rnd_mode;
+
+ prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumSignificantDigits);
+ if (JS_IsException(prop))
+ return -1;
+ has_prec = FALSE;
+ if (!JS_IsUndefined(prop)) {
+ if (JS_ToInt64SatFree(ctx, &val, prop))
+ return -1;
+ if (val < 1 || val > BF_PREC_MAX)
+ goto invalid_precision;
+ fe->prec = val;
+ has_prec = TRUE;
+ }
+
+ prop = JS_GetProperty(ctx, obj, JS_ATOM_maximumFractionDigits);
+ if (JS_IsException(prop))
+ return -1;
+ if (!JS_IsUndefined(prop)) {
+ if (has_prec) {
+ JS_FreeValue(ctx, prop);
+ JS_ThrowTypeError(ctx, "cannot provide both maximumSignificantDigits and maximumFractionDigits");
+ return -1;
+ }
+ if (JS_ToInt64SatFree(ctx, &val, prop))
+ return -1;
+ if (val < 0 || val > BF_PREC_MAX) {
+ invalid_precision:
+ JS_ThrowTypeError(ctx, "invalid precision");
+ return -1;
+ }
+ fe->prec = val;
+ fe->flags |= BF_FLAG_RADPNT_PREC;
+ has_prec = TRUE;
+ }
+ if (!has_prec) {
+ JS_ThrowTypeError(ctx, "precision must be present");
+ return -1;
+ }
+ return 0;
+}
+
+
+static JSValue js_bigdecimal_fop(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ bfdec_t *a, *b, r_s, *r = &r_s;
+ JSValue op1, op2, res;
+ BigDecimalEnv fe_s, *fe = &fe_s;
+ int op_count, ret;
+
+ if (magic == MATH_OP_SQRT ||
+ magic == MATH_OP_ROUND)
+ op_count = 1;
+ else
+ op_count = 2;
+
+ op1 = JS_ToNumeric(ctx, argv[0]);
+ if (JS_IsException(op1))
+ return op1;
+ a = JS_ToBigDecimal(ctx, op1);
+ if (!a) {
+ JS_FreeValue(ctx, op1);
+ return JS_EXCEPTION;
+ }
+ if (op_count >= 2) {
+ op2 = JS_ToNumeric(ctx, argv[1]);
+ if (JS_IsException(op2)) {
+ JS_FreeValue(ctx, op1);
+ return op2;
+ }
+ b = JS_ToBigDecimal(ctx, op2);
+ if (!b)
+ goto fail;
+ } else {
+ op2 = JS_UNDEFINED;
+ b = NULL;
+ }
+ fe->flags = BF_RNDZ;
+ fe->prec = BF_PREC_INF;
+ if (op_count < argc) {
+ if (js_bigdecimal_get_env(ctx, fe, argv[op_count]))
+ goto fail;
+ }
+
+ res = JS_NewBigDecimal(ctx);
+ if (JS_IsException(res)) {
+ fail:
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ return JS_EXCEPTION;
+ }
+ r = JS_GetBigDecimal(res);
+ switch (magic) {
+ case MATH_OP_ADD:
+ ret = bfdec_add(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_SUB:
+ ret = bfdec_sub(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_MUL:
+ ret = bfdec_mul(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_DIV:
+ ret = bfdec_div(r, a, b, fe->prec, fe->flags);
+ break;
+ case MATH_OP_FMOD:
+ ret = bfdec_rem(r, a, b, fe->prec, fe->flags, BF_RNDZ);
+ break;
+ case MATH_OP_SQRT:
+ ret = bfdec_sqrt(r, a, fe->prec, fe->flags);
+ break;
+ case MATH_OP_ROUND:
+ ret = bfdec_set(r, a);
+ if (!(ret & BF_ST_MEM_ERROR))
+ ret = bfdec_round(r, fe->prec, fe->flags);
+ break;
+ default:
+ abort();
+ }
+ JS_FreeValue(ctx, op1);
+ JS_FreeValue(ctx, op2);
+ ret &= BF_ST_MEM_ERROR | BF_ST_DIVIDE_ZERO | BF_ST_INVALID_OP |
+ BF_ST_OVERFLOW;
+ if (ret != 0) {
+ JS_FreeValue(ctx, res);
+ return throw_bf_exception(ctx, ret);
+ } else {
+ return res;
+ }
+}
+
+static JSValue js_bigdecimal_toFixed(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t f;
+ int rnd_mode;
+
+ val = js_thisBigDecimalValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToInt64Sat(ctx, &f, argv[0]))
+ goto fail;
+ if (f < 0 || f > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ if (argc > 1) {
+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ ret = js_bigdecimal_to_string1(ctx, val, f, rnd_mode | BF_FTOA_FORMAT_FRAC);
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigdecimal_toExponential(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t f;
+ int rnd_mode;
+
+ val = js_thisBigDecimalValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_ToInt64Sat(ctx, &f, argv[0]))
+ goto fail;
+ if (JS_IsUndefined(argv[0])) {
+ ret = js_bigdecimal_to_string1(ctx, val, 0,
+ BF_RNDN | BF_FTOA_FORMAT_FREE_MIN | BF_FTOA_FORCE_EXP);
+ } else {
+ if (f < 0 || f > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ if (argc > 1) {
+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ ret = js_bigdecimal_to_string1(ctx, val, f + 1,
+ rnd_mode | BF_FTOA_FORMAT_FIXED | BF_FTOA_FORCE_EXP);
+ }
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_bigdecimal_toPrecision(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue val, ret;
+ int64_t p;
+ int rnd_mode;
+
+ val = js_thisBigDecimalValue(ctx, this_val);
+ if (JS_IsException(val))
+ return val;
+ if (JS_IsUndefined(argv[0])) {
+ return JS_ToStringFree(ctx, val);
+ }
+ if (JS_ToInt64Sat(ctx, &p, argv[0]))
+ goto fail;
+ if (p < 1 || p > BF_PREC_MAX) {
+ JS_ThrowRangeError(ctx, "invalid number of digits");
+ goto fail;
+ }
+ rnd_mode = BF_RNDNA;
+ if (argc > 1) {
+ rnd_mode = js_bigdecimal_get_rnd_mode(ctx, argv[1]);
+ if (rnd_mode < 0)
+ goto fail;
+ }
+ ret = js_bigdecimal_to_string1(ctx, val, p,
+ rnd_mode | BF_FTOA_FORMAT_FIXED);
+ JS_FreeValue(ctx, val);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_bigdecimal_proto_funcs[] = {
+ JS_CFUNC_DEF("toString", 0, js_bigdecimal_toString ),
+ JS_CFUNC_DEF("valueOf", 0, js_bigdecimal_valueOf ),
+ JS_CFUNC_DEF("toPrecision", 1, js_bigdecimal_toPrecision ),
+ JS_CFUNC_DEF("toFixed", 1, js_bigdecimal_toFixed ),
+ JS_CFUNC_DEF("toExponential", 1, js_bigdecimal_toExponential ),
+};
+
+static const JSCFunctionListEntry js_bigdecimal_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("add", 2, js_bigdecimal_fop, MATH_OP_ADD ),
+ JS_CFUNC_MAGIC_DEF("sub", 2, js_bigdecimal_fop, MATH_OP_SUB ),
+ JS_CFUNC_MAGIC_DEF("mul", 2, js_bigdecimal_fop, MATH_OP_MUL ),
+ JS_CFUNC_MAGIC_DEF("div", 2, js_bigdecimal_fop, MATH_OP_DIV ),
+ JS_CFUNC_MAGIC_DEF("mod", 2, js_bigdecimal_fop, MATH_OP_FMOD ),
+ JS_CFUNC_MAGIC_DEF("round", 1, js_bigdecimal_fop, MATH_OP_ROUND ),
+ JS_CFUNC_MAGIC_DEF("sqrt", 1, js_bigdecimal_fop, MATH_OP_SQRT ),
+};
+
+void JS_AddIntrinsicBigDecimal(JSContext *ctx)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValueConst obj1;
+
+ rt->bigdecimal_ops.to_string = js_bigdecimal_to_string;
+ rt->bigdecimal_ops.from_string = js_string_to_bigdecimal;
+ rt->bigdecimal_ops.unary_arith = js_unary_arith_bigdecimal;
+ rt->bigdecimal_ops.binary_arith = js_binary_arith_bigdecimal;
+ rt->bigdecimal_ops.compare = js_compare_bigdecimal;
+
+ ctx->class_proto[JS_CLASS_BIG_DECIMAL] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL],
+ js_bigdecimal_proto_funcs,
+ countof(js_bigdecimal_proto_funcs));
+ obj1 = JS_NewGlobalCConstructor(ctx, "BigDecimal",
+ js_bigdecimal_constructor, 1,
+ ctx->class_proto[JS_CLASS_BIG_DECIMAL]);
+ JS_SetPropertyFunctionList(ctx, obj1, js_bigdecimal_funcs,
+ countof(js_bigdecimal_funcs));
+}
+
+void JS_EnableBignumExt(JSContext *ctx, BOOL enable)
+{
+ ctx->bignum_ext = enable;
+}
+
+#endif /* CONFIG_BIGNUM */
+
+static const char * const native_error_name[JS_NATIVE_ERROR_COUNT] = {
+ "EvalError", "RangeError", "ReferenceError",
+ "SyntaxError", "TypeError", "URIError",
+ "InternalError", "AggregateError",
+};
+
+/* Minimum amount of objects to be able to compile code and display
+ error messages. No JSAtom should be allocated by this function. */
+static void JS_AddIntrinsicBasicObjects(JSContext *ctx)
+{
+ JSValue proto;
+ int i;
+
+ ctx->class_proto[JS_CLASS_OBJECT] = JS_NewObjectProto(ctx, JS_NULL);
+ ctx->function_proto = JS_NewCFunction3(ctx, js_function_proto, "", 0,
+ JS_CFUNC_generic, 0,
+ ctx->class_proto[JS_CLASS_OBJECT]);
+ ctx->class_proto[JS_CLASS_BYTECODE_FUNCTION] = JS_DupValue(ctx, ctx->function_proto);
+ ctx->class_proto[JS_CLASS_ERROR] = JS_NewObject(ctx);
+#if 0
+ /* these are auto-initialized from js_error_proto_funcs,
+ but delaying might be a problem */
+ JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_name,
+ JS_AtomToString(ctx, JS_ATOM_Error),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ERROR], JS_ATOM_message,
+ JS_AtomToString(ctx, JS_ATOM_empty_string),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+#endif
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ERROR],
+ js_error_proto_funcs,
+ countof(js_error_proto_funcs));
+
+ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
+ proto = JS_NewObjectProto(ctx, ctx->class_proto[JS_CLASS_ERROR]);
+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_name,
+ JS_NewAtomString(ctx, native_error_name[i]),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ JS_DefinePropertyValue(ctx, proto, JS_ATOM_message,
+ JS_AtomToString(ctx, JS_ATOM_empty_string),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+ ctx->native_error_proto[i] = proto;
+ }
+
+ /* the array prototype is an array */
+ ctx->class_proto[JS_CLASS_ARRAY] =
+ JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_ARRAY);
+
+ ctx->array_shape = js_new_shape2(ctx, get_proto_obj(ctx->class_proto[JS_CLASS_ARRAY]),
+ JS_PROP_INITIAL_HASH_SIZE, 1);
+ add_shape_property(ctx, &ctx->array_shape, NULL,
+ JS_ATOM_length, JS_PROP_WRITABLE | JS_PROP_LENGTH);
+
+ /* XXX: could test it on first context creation to ensure that no
+ new atoms are created in JS_AddIntrinsicBasicObjects(). It is
+ necessary to avoid useless renumbering of atoms after
+ JS_EvalBinary() if it is done just after
+ JS_AddIntrinsicBasicObjects(). */
+ // assert(ctx->rt->atom_count == JS_ATOM_END);
+}
+
+void JS_AddIntrinsicBaseObjects(JSContext *ctx)
+{
+ int i;
+ JSValueConst obj, number_obj;
+ JSValue obj1;
+
+ ctx->throw_type_error = JS_NewCFunction(ctx, js_throw_type_error, NULL, 0);
+
+ /* add caller and arguments properties to throw a TypeError */
+ obj1 = JS_NewCFunction(ctx, js_function_proto_caller, NULL, 0);
+ JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_caller, JS_UNDEFINED,
+ obj1, ctx->throw_type_error,
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
+ JS_DefineProperty(ctx, ctx->function_proto, JS_ATOM_arguments, JS_UNDEFINED,
+ obj1, ctx->throw_type_error,
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, obj1);
+ JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, &ctx->throw_type_error, 1));
+
+ ctx->global_obj = JS_NewObject(ctx);
+ ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);
+
+ /* Object */
+ obj = JS_NewGlobalCConstructor(ctx, "Object", js_object_constructor, 1,
+ ctx->class_proto[JS_CLASS_OBJECT]);
+ JS_SetPropertyFunctionList(ctx, obj, js_object_funcs, countof(js_object_funcs));
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ js_object_proto_funcs, countof(js_object_proto_funcs));
+
+ /* Function */
+ JS_SetPropertyFunctionList(ctx, ctx->function_proto, js_function_proto_funcs, countof(js_function_proto_funcs));
+ ctx->function_ctor = JS_NewCFunctionMagic(ctx, js_function_constructor,
+ "Function", 1, JS_CFUNC_constructor_or_func_magic,
+ JS_FUNC_NORMAL);
+ JS_NewGlobalCConstructor2(ctx, JS_DupValue(ctx, ctx->function_ctor), "Function",
+ ctx->function_proto);
+
+ /* Error */
+ obj1 = JS_NewCFunctionMagic(ctx, js_error_constructor,
+ "Error", 1, JS_CFUNC_constructor_or_func_magic, -1);
+ JS_NewGlobalCConstructor2(ctx, obj1,
+ "Error", ctx->class_proto[JS_CLASS_ERROR]);
+
+ for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) {
+ JSValue func_obj;
+ int n_args;
+ n_args = 1 + (i == JS_AGGREGATE_ERROR);
+ func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor,
+ native_error_name[i], n_args,
+ JS_CFUNC_constructor_or_func_magic, i, obj1);
+ JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i],
+ ctx->native_error_proto[i]);
+ }
+
+ /* Iterator prototype */
+ ctx->iterator_proto = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->iterator_proto,
+ js_iterator_proto_funcs,
+ countof(js_iterator_proto_funcs));
+
+ /* Array */
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY],
+ js_array_proto_funcs,
+ countof(js_array_proto_funcs));
+
+ obj = JS_NewGlobalCConstructor(ctx, "Array", js_array_constructor, 1,
+ ctx->class_proto[JS_CLASS_ARRAY]);
+ ctx->array_ctor = JS_DupValue(ctx, obj);
+ JS_SetPropertyFunctionList(ctx, obj, js_array_funcs,
+ countof(js_array_funcs));
+
+ /* XXX: create auto_initializer */
+ {
+ /* initialize Array.prototype[Symbol.unscopables] */
+ char const unscopables[] = "copyWithin" "\0" "entries" "\0" "fill" "\0" "find" "\0"
+ "findIndex" "\0" "flat" "\0" "flatMap" "\0" "includes" "\0" "keys" "\0" "values" "\0";
+ const char *p = unscopables;
+ obj1 = JS_NewObjectProto(ctx, JS_NULL);
+ for(p = unscopables; *p; p += strlen(p) + 1) {
+ JS_DefinePropertyValueStr(ctx, obj1, p, JS_TRUE, JS_PROP_C_W_E);
+ }
+ JS_DefinePropertyValue(ctx, ctx->class_proto[JS_CLASS_ARRAY],
+ JS_ATOM_Symbol_unscopables, obj1,
+ JS_PROP_CONFIGURABLE);
+ }
+
+ /* needed to initialize arguments[Symbol.iterator] */
+ ctx->array_proto_values =
+ JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_values);
+
+ ctx->class_proto[JS_CLASS_ARRAY_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_ITERATOR],
+ js_array_iterator_proto_funcs,
+ countof(js_array_iterator_proto_funcs));
+
+ /* parseFloat and parseInteger must be defined before Number
+ because of the Number.parseFloat and Number.parseInteger
+ aliases */
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_global_funcs,
+ countof(js_global_funcs));
+
+ /* Number */
+ ctx->class_proto[JS_CLASS_NUMBER] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_NUMBER);
+ JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_NUMBER], JS_NewInt32(ctx, 0));
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_NUMBER],
+ js_number_proto_funcs,
+ countof(js_number_proto_funcs));
+ number_obj = JS_NewGlobalCConstructor(ctx, "Number", js_number_constructor, 1,
+ ctx->class_proto[JS_CLASS_NUMBER]);
+ JS_SetPropertyFunctionList(ctx, number_obj, js_number_funcs, countof(js_number_funcs));
+
+ /* Boolean */
+ ctx->class_proto[JS_CLASS_BOOLEAN] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_BOOLEAN);
+ JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], JS_NewBool(ctx, FALSE));
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_BOOLEAN], js_boolean_proto_funcs,
+ countof(js_boolean_proto_funcs));
+ JS_NewGlobalCConstructor(ctx, "Boolean", js_boolean_constructor, 1,
+ ctx->class_proto[JS_CLASS_BOOLEAN]);
+
+ /* String */
+ ctx->class_proto[JS_CLASS_STRING] = JS_NewObjectProtoClass(ctx, ctx->class_proto[JS_CLASS_OBJECT],
+ JS_CLASS_STRING);
+ JS_SetObjectData(ctx, ctx->class_proto[JS_CLASS_STRING], JS_AtomToString(ctx, JS_ATOM_empty_string));
+ obj = JS_NewGlobalCConstructor(ctx, "String", js_string_constructor, 1,
+ ctx->class_proto[JS_CLASS_STRING]);
+ JS_SetPropertyFunctionList(ctx, obj, js_string_funcs,
+ countof(js_string_funcs));
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING], js_string_proto_funcs,
+ countof(js_string_proto_funcs));
+
+ ctx->class_proto[JS_CLASS_STRING_ITERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_STRING_ITERATOR],
+ js_string_iterator_proto_funcs,
+ countof(js_string_iterator_proto_funcs));
+
+ /* Math: create as autoinit object */
+ js_random_init(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_math_obj, countof(js_math_obj));
+
+ /* ES6 Reflect: create as autoinit object */
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_reflect_obj, countof(js_reflect_obj));
+
+ /* ES6 Symbol */
+ ctx->class_proto[JS_CLASS_SYMBOL] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SYMBOL], js_symbol_proto_funcs,
+ countof(js_symbol_proto_funcs));
+ obj = JS_NewGlobalCConstructor(ctx, "Symbol", js_symbol_constructor, 0,
+ ctx->class_proto[JS_CLASS_SYMBOL]);
+ JS_SetPropertyFunctionList(ctx, obj, js_symbol_funcs,
+ countof(js_symbol_funcs));
+ for(i = JS_ATOM_Symbol_toPrimitive; i < JS_ATOM_END; i++) {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ const char *str, *p;
+ str = JS_AtomGetStr(ctx, buf, sizeof(buf), i);
+ /* skip "Symbol." */
+ p = strchr(str, '.');
+ if (p)
+ str = p + 1;
+ JS_DefinePropertyValueStr(ctx, obj, str, JS_AtomToValue(ctx, i), 0);
+ }
+
+ /* ES6 Generator */
+ ctx->class_proto[JS_CLASS_GENERATOR] = JS_NewObjectProto(ctx, ctx->iterator_proto);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_GENERATOR],
+ js_generator_proto_funcs,
+ countof(js_generator_proto_funcs));
+
+ ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION] = JS_NewObjectProto(ctx, ctx->function_proto);
+ obj1 = JS_NewCFunctionMagic(ctx, js_function_constructor,
+ "GeneratorFunction", 1,
+ JS_CFUNC_constructor_or_func_magic, JS_FUNC_GENERATOR);
+ JS_SetPropertyFunctionList(ctx,
+ ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
+ js_generator_function_proto_funcs,
+ countof(js_generator_function_proto_funcs));
+ JS_SetConstructor2(ctx, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
+ ctx->class_proto[JS_CLASS_GENERATOR],
+ JS_PROP_CONFIGURABLE, JS_PROP_CONFIGURABLE);
+ JS_SetConstructor2(ctx, obj1, ctx->class_proto[JS_CLASS_GENERATOR_FUNCTION],
+ 0, JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, obj1);
+
+ /* global properties */
+ ctx->eval_obj = JS_NewCFunction(ctx, js_global_eval, "eval", 1);
+ JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_eval,
+ JS_DupValue(ctx, ctx->eval_obj),
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ JS_DefinePropertyValue(ctx, ctx->global_obj, JS_ATOM_globalThis,
+ JS_DupValue(ctx, ctx->global_obj),
+ JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE);
+}
+
+/* Typed Arrays */
+
+static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = {
+ 0, 0, 0, 1, 1, 2, 2,
+#ifdef CONFIG_BIGNUM
+ 3, 3, /* BigInt64Array, BigUint64Array */
+#endif
+ 2, 3
+};
+
+static JSValue js_array_buffer_constructor3(JSContext *ctx,
+ JSValueConst new_target,
+ uint64_t len, JSClassID class_id,
+ uint8_t *buf,
+ JSFreeArrayBufferDataFunc *free_func,
+ void *opaque, BOOL alloc_flag)
+{
+ JSRuntime *rt = ctx->rt;
+ JSValue obj;
+ JSArrayBuffer *abuf = NULL;
+
+ obj = js_create_from_ctor(ctx, new_target, class_id);
+ if (JS_IsException(obj))
+ return obj;
+ /* XXX: we are currently limited to 2 GB */
+ if (len > INT32_MAX) {
+ JS_ThrowRangeError(ctx, "invalid array buffer length");
+ goto fail;
+ }
+ abuf = js_malloc(ctx, sizeof(*abuf));
+ if (!abuf)
+ goto fail;
+ abuf->byte_length = len;
+ if (alloc_flag) {
+ if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
+ rt->sab_funcs.sab_alloc) {
+ abuf->data = rt->sab_funcs.sab_alloc(rt->sab_funcs.sab_opaque,
+ max_int(len, 1));
+ if (!abuf->data)
+ goto fail;
+ memset(abuf->data, 0, len);
+ } else {
+ /* the allocation must be done after the object creation */
+ abuf->data = js_mallocz(ctx, max_int(len, 1));
+ if (!abuf->data)
+ goto fail;
+ }
+ } else {
+ if (class_id == JS_CLASS_SHARED_ARRAY_BUFFER &&
+ rt->sab_funcs.sab_dup) {
+ rt->sab_funcs.sab_dup(rt->sab_funcs.sab_opaque, buf);
+ }
+ abuf->data = buf;
+ }
+ init_list_head(&abuf->array_list);
+ abuf->detached = FALSE;
+ abuf->shared = (class_id == JS_CLASS_SHARED_ARRAY_BUFFER);
+ abuf->opaque = opaque;
+ abuf->free_func = free_func;
+ if (alloc_flag && buf)
+ memcpy(abuf->data, buf, len);
+ JS_SetOpaque(obj, abuf);
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ js_free(ctx, abuf);
+ return JS_EXCEPTION;
+}
+
+static void js_array_buffer_free(JSRuntime *rt, void *opaque, void *ptr)
+{
+ js_free_rt(rt, ptr);
+}
+
+static JSValue js_array_buffer_constructor2(JSContext *ctx,
+ JSValueConst new_target,
+ uint64_t len, JSClassID class_id)
+{
+ return js_array_buffer_constructor3(ctx, new_target, len, class_id,
+ NULL, js_array_buffer_free, NULL,
+ TRUE);
+}
+
+static JSValue js_array_buffer_constructor1(JSContext *ctx,
+ JSValueConst new_target,
+ uint64_t len)
+{
+ return js_array_buffer_constructor2(ctx, new_target, len,
+ JS_CLASS_ARRAY_BUFFER);
+}
+
+JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
+ JSFreeArrayBufferDataFunc *free_func, void *opaque,
+ BOOL is_shared)
+{
+ return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
+ is_shared ? JS_CLASS_SHARED_ARRAY_BUFFER : JS_CLASS_ARRAY_BUFFER,
+ buf, free_func, opaque, FALSE);
+}
+
+/* create a new ArrayBuffer of length 'len' and copy 'buf' to it */
+JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len)
+{
+ return js_array_buffer_constructor3(ctx, JS_UNDEFINED, len,
+ JS_CLASS_ARRAY_BUFFER,
+ (uint8_t *)buf,
+ js_array_buffer_free, NULL,
+ TRUE);
+}
+
+static JSValue js_array_buffer_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ uint64_t len;
+ if (JS_ToIndex(ctx, &len, argv[0]))
+ return JS_EXCEPTION;
+ return js_array_buffer_constructor1(ctx, new_target, len);
+}
+
+static JSValue js_shared_array_buffer_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ uint64_t len;
+ if (JS_ToIndex(ctx, &len, argv[0]))
+ return JS_EXCEPTION;
+ return js_array_buffer_constructor2(ctx, new_target, len,
+ JS_CLASS_SHARED_ARRAY_BUFFER);
+}
+
+/* also used for SharedArrayBuffer */
+static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSArrayBuffer *abuf = p->u.array_buffer;
+ if (abuf) {
+ /* The ArrayBuffer finalizer may be called before the typed
+ array finalizers using it, so abuf->array_list is not
+ necessarily empty. */
+ // assert(list_empty(&abuf->array_list));
+ if (abuf->shared && rt->sab_funcs.sab_free) {
+ rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data);
+ } else {
+ if (abuf->free_func)
+ abuf->free_func(rt, abuf->opaque, abuf->data);
+ }
+ js_free_rt(rt, abuf);
+ }
+}
+
+static JSValue js_array_buffer_isView(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ BOOL res;
+ res = FALSE;
+ if (JS_VALUE_GET_TAG(argv[0]) == JS_TAG_OBJECT) {
+ p = JS_VALUE_GET_OBJ(argv[0]);
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_DATAVIEW) {
+ res = TRUE;
+ }
+ }
+ return JS_NewBool(ctx, res);
+}
+
+static const JSCFunctionListEntry js_array_buffer_funcs[] = {
+ JS_CFUNC_DEF("isView", 1, js_array_buffer_isView ),
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+};
+
+static JSValue JS_ThrowTypeErrorDetachedArrayBuffer(JSContext *ctx)
+{
+ return JS_ThrowTypeError(ctx, "ArrayBuffer is detached");
+}
+
+static JSValue js_array_buffer_get_byteLength(JSContext *ctx,
+ JSValueConst this_val,
+ int class_id)
+{
+ JSArrayBuffer *abuf = JS_GetOpaque2(ctx, this_val, class_id);
+ if (!abuf)
+ return JS_EXCEPTION;
+ /* return 0 if detached */
+ return JS_NewUint32(ctx, abuf->byte_length);
+}
+
+void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj)
+{
+ JSArrayBuffer *abuf = JS_GetOpaque(obj, JS_CLASS_ARRAY_BUFFER);
+ struct list_head *el;
+
+ if (!abuf || abuf->detached)
+ return;
+ if (abuf->free_func)
+ abuf->free_func(ctx->rt, abuf->opaque, abuf->data);
+ abuf->data = NULL;
+ abuf->byte_length = 0;
+ abuf->detached = TRUE;
+
+ list_for_each(el, &abuf->array_list) {
+ JSTypedArray *ta;
+ JSObject *p;
+
+ ta = list_entry(el, JSTypedArray, link);
+ p = ta->obj;
+ /* Note: the typed array length and offset fields are not modified */
+ if (p->class_id != JS_CLASS_DATAVIEW) {
+ p->u.array.count = 0;
+ p->u.array.u.ptr = NULL;
+ }
+ }
+}
+
+/* get an ArrayBuffer or SharedArrayBuffer */
+static JSArrayBuffer *js_get_array_buffer(JSContext *ctx, JSValueConst obj)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ goto fail;
+ p = JS_VALUE_GET_OBJ(obj);
+ if (p->class_id != JS_CLASS_ARRAY_BUFFER &&
+ p->class_id != JS_CLASS_SHARED_ARRAY_BUFFER) {
+ fail:
+ JS_ThrowTypeErrorInvalidClass(ctx, JS_CLASS_ARRAY_BUFFER);
+ return NULL;
+ }
+ return p->u.array_buffer;
+}
+
+/* return NULL if exception. WARNING: any JS call can detach the
+ buffer and render the returned pointer invalid */
+uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj)
+{
+ JSArrayBuffer *abuf = js_get_array_buffer(ctx, obj);
+ if (!abuf)
+ goto fail;
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ *psize = abuf->byte_length;
+ return abuf->data;
+ fail:
+ *psize = 0;
+ return NULL;
+}
+
+static JSValue js_array_buffer_slice(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv, int class_id)
+{
+ JSArrayBuffer *abuf, *new_abuf;
+ int64_t len, start, end, new_len;
+ JSValue ctor, new_obj;
+
+ abuf = JS_GetOpaque2(ctx, this_val, class_id);
+ if (!abuf)
+ return JS_EXCEPTION;
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ len = abuf->byte_length;
+
+ if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len))
+ return JS_EXCEPTION;
+
+ end = len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt64Clamp(ctx, &end, argv[1], 0, len, len))
+ return JS_EXCEPTION;
+ }
+ new_len = max_int64(end - start, 0);
+ ctor = JS_SpeciesConstructor(ctx, this_val, JS_UNDEFINED);
+ if (JS_IsException(ctor))
+ return ctor;
+ if (JS_IsUndefined(ctor)) {
+ new_obj = js_array_buffer_constructor2(ctx, JS_UNDEFINED, new_len,
+ class_id);
+ } else {
+ JSValue args[1];
+ args[0] = JS_NewInt64(ctx, new_len);
+ new_obj = JS_CallConstructor(ctx, ctor, 1, (JSValueConst *)args);
+ JS_FreeValue(ctx, ctor);
+ JS_FreeValue(ctx, args[0]);
+ }
+ if (JS_IsException(new_obj))
+ return new_obj;
+ new_abuf = JS_GetOpaque2(ctx, new_obj, class_id);
+ if (!new_abuf)
+ goto fail;
+ if (js_same_value(ctx, new_obj, this_val)) {
+ JS_ThrowTypeError(ctx, "cannot use identical ArrayBuffer");
+ goto fail;
+ }
+ if (new_abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ if (new_abuf->byte_length < new_len) {
+ JS_ThrowTypeError(ctx, "new ArrayBuffer is too small");
+ goto fail;
+ }
+ /* must test again because of side effects */
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ memcpy(new_abuf->data, abuf->data + start, new_len);
+ return new_obj;
+ fail:
+ JS_FreeValue(ctx, new_obj);
+ return JS_EXCEPTION;
+}
+
+static const JSCFunctionListEntry js_array_buffer_proto_funcs[] = {
+ JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_ARRAY_BUFFER ),
+ JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_ARRAY_BUFFER ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "ArrayBuffer", JS_PROP_CONFIGURABLE ),
+};
+
+/* SharedArrayBuffer */
+
+static const JSCFunctionListEntry js_shared_array_buffer_funcs[] = {
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+};
+
+static const JSCFunctionListEntry js_shared_array_buffer_proto_funcs[] = {
+ JS_CGETSET_MAGIC_DEF("byteLength", js_array_buffer_get_byteLength, NULL, JS_CLASS_SHARED_ARRAY_BUFFER ),
+ JS_CFUNC_MAGIC_DEF("slice", 2, js_array_buffer_slice, JS_CLASS_SHARED_ARRAY_BUFFER ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "SharedArrayBuffer", JS_PROP_CONFIGURABLE ),
+};
+
+static JSObject *get_typed_array(JSContext *ctx,
+ JSValueConst this_val,
+ int is_dataview)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ goto fail;
+ p = JS_VALUE_GET_OBJ(this_val);
+ if (is_dataview) {
+ if (p->class_id != JS_CLASS_DATAVIEW)
+ goto fail;
+ } else {
+ if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY)) {
+ fail:
+ JS_ThrowTypeError(ctx, "not a %s", is_dataview ? "DataView" : "TypedArray");
+ return NULL;
+ }
+ }
+ return p;
+}
+
+/* WARNING: 'p' must be a typed array */
+static BOOL typed_array_is_detached(JSContext *ctx, JSObject *p)
+{
+ JSTypedArray *ta = p->u.typed_array;
+ JSArrayBuffer *abuf = ta->buffer->u.array_buffer;
+ /* XXX: could simplify test by ensuring that
+ p->u.array.u.ptr is NULL iff it is detached */
+ return abuf->detached;
+}
+
+/* WARNING: 'p' must be a typed array. Works even if the array buffer
+ is detached */
+static uint32_t typed_array_get_length(JSContext *ctx, JSObject *p)
+{
+ JSTypedArray *ta = p->u.typed_array;
+ int size_log2 = typed_array_size_log2(p->class_id);
+ return ta->length >> size_log2;
+}
+
+static int validate_typed_array(JSContext *ctx, JSValueConst this_val)
+{
+ JSObject *p;
+ p = get_typed_array(ctx, this_val, 0);
+ if (!p)
+ return -1;
+ if (typed_array_is_detached(ctx, p)) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ return -1;
+ }
+ return 0;
+}
+
+static JSValue js_typed_array_get_length(JSContext *ctx,
+ JSValueConst this_val)
+{
+ JSObject *p;
+ p = get_typed_array(ctx, this_val, 0);
+ if (!p)
+ return JS_EXCEPTION;
+ return JS_NewInt32(ctx, p->u.array.count);
+}
+
+static JSValue js_typed_array_get_buffer(JSContext *ctx,
+ JSValueConst this_val, int is_dataview)
+{
+ JSObject *p;
+ JSTypedArray *ta;
+ p = get_typed_array(ctx, this_val, is_dataview);
+ if (!p)
+ return JS_EXCEPTION;
+ ta = p->u.typed_array;
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
+}
+
+static JSValue js_typed_array_get_byteLength(JSContext *ctx,
+ JSValueConst this_val,
+ int is_dataview)
+{
+ JSObject *p;
+ JSTypedArray *ta;
+ p = get_typed_array(ctx, this_val, is_dataview);
+ if (!p)
+ return JS_EXCEPTION;
+ if (typed_array_is_detached(ctx, p)) {
+ if (is_dataview) {
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ } else {
+ return JS_NewInt32(ctx, 0);
+ }
+ }
+ ta = p->u.typed_array;
+ return JS_NewInt32(ctx, ta->length);
+}
+
+static JSValue js_typed_array_get_byteOffset(JSContext *ctx,
+ JSValueConst this_val,
+ int is_dataview)
+{
+ JSObject *p;
+ JSTypedArray *ta;
+ p = get_typed_array(ctx, this_val, is_dataview);
+ if (!p)
+ return JS_EXCEPTION;
+ if (typed_array_is_detached(ctx, p)) {
+ if (is_dataview) {
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ } else {
+ return JS_NewInt32(ctx, 0);
+ }
+ }
+ ta = p->u.typed_array;
+ return JS_NewInt32(ctx, ta->offset);
+}
+
+/* Return the buffer associated to the typed array or an exception if
+ it is not a typed array or if the buffer is detached. pbyte_offset,
+ pbyte_length or pbytes_per_element can be NULL. */
+JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
+ size_t *pbyte_offset,
+ size_t *pbyte_length,
+ size_t *pbytes_per_element)
+{
+ JSObject *p;
+ JSTypedArray *ta;
+ p = get_typed_array(ctx, obj, FALSE);
+ if (!p)
+ return JS_EXCEPTION;
+ if (typed_array_is_detached(ctx, p))
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ ta = p->u.typed_array;
+ if (pbyte_offset)
+ *pbyte_offset = ta->offset;
+ if (pbyte_length)
+ *pbyte_length = ta->length;
+ if (pbytes_per_element) {
+ *pbytes_per_element = 1 << typed_array_size_log2(p->class_id);
+ }
+ return JS_DupValue(ctx, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
+}
+
+static JSValue js_typed_array_get_toStringTag(JSContext *ctx,
+ JSValueConst this_val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT)
+ return JS_UNDEFINED;
+ p = JS_VALUE_GET_OBJ(this_val);
+ if (!(p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY))
+ return JS_UNDEFINED;
+ return JS_AtomToString(ctx, ctx->rt->class_array[p->class_id].class_name);
+}
+
+static JSValue js_typed_array_set_internal(JSContext *ctx,
+ JSValueConst dst,
+ JSValueConst src,
+ JSValueConst off)
+{
+ JSObject *p;
+ JSObject *src_p;
+ uint32_t i;
+ int64_t src_len, offset;
+ JSValue val, src_obj = JS_UNDEFINED;
+
+ p = get_typed_array(ctx, dst, 0);
+ if (!p)
+ goto fail;
+ if (JS_ToInt64Sat(ctx, &offset, off))
+ goto fail;
+ if (offset < 0)
+ goto range_error;
+ if (typed_array_is_detached(ctx, p)) {
+ detached:
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ src_obj = JS_ToObject(ctx, src);
+ if (JS_IsException(src_obj))
+ goto fail;
+ src_p = JS_VALUE_GET_OBJ(src_obj);
+ if (src_p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ src_p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ JSTypedArray *dest_ta = p->u.typed_array;
+ JSArrayBuffer *dest_abuf = dest_ta->buffer->u.array_buffer;
+ JSTypedArray *src_ta = src_p->u.typed_array;
+ JSArrayBuffer *src_abuf = src_ta->buffer->u.array_buffer;
+ int shift = typed_array_size_log2(p->class_id);
+
+ if (src_abuf->detached)
+ goto detached;
+
+ src_len = src_p->u.array.count;
+ if (offset > (int64_t)(p->u.array.count - src_len))
+ goto range_error;
+
+ /* copying between typed objects */
+ if (src_p->class_id == p->class_id) {
+ /* same type, use memmove */
+ memmove(dest_abuf->data + dest_ta->offset + (offset << shift),
+ src_abuf->data + src_ta->offset, src_len << shift);
+ goto done;
+ }
+ if (dest_abuf->data == src_abuf->data) {
+ /* copying between the same buffer using different types of mappings
+ would require a temporary buffer */
+ }
+ /* otherwise, default behavior is slow but correct */
+ } else {
+ if (js_get_length64(ctx, &src_len, src_obj))
+ goto fail;
+ if (offset > (int64_t)(p->u.array.count - src_len)) {
+ range_error:
+ JS_ThrowRangeError(ctx, "invalid array length");
+ goto fail;
+ }
+ }
+ for(i = 0; i < src_len; i++) {
+ val = JS_GetPropertyUint32(ctx, src_obj, i);
+ if (JS_IsException(val))
+ goto fail;
+ if (JS_SetPropertyUint32(ctx, dst, offset + i, val) < 0)
+ goto fail;
+ }
+done:
+ JS_FreeValue(ctx, src_obj);
+ return JS_UNDEFINED;
+fail:
+ JS_FreeValue(ctx, src_obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_set(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst offset = JS_UNDEFINED;
+ if (argc > 1) {
+ offset = argv[1];
+ }
+ return js_typed_array_set_internal(ctx, this_val, argv[0], offset);
+}
+
+static JSValue js_create_typed_array_iterator(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic)
+{
+ if (validate_typed_array(ctx, this_val))
+ return JS_EXCEPTION;
+ return js_create_array_iterator(ctx, this_val, argc, argv, magic);
+}
+
+/* return < 0 if exception */
+static int js_typed_array_get_length_internal(JSContext *ctx,
+ JSValueConst obj)
+{
+ JSObject *p;
+ p = get_typed_array(ctx, obj, 0);
+ if (!p)
+ return -1;
+ if (typed_array_is_detached(ctx, p)) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ return -1;
+ }
+ return p->u.array.count;
+}
+
+#if 0
+/* validate a typed array and return its length */
+static JSValue js_typed_array___getLength(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ BOOL ignore_detached = JS_ToBool(ctx, argv[1]);
+
+ if (ignore_detached) {
+ return js_typed_array_get_length(ctx, argv[0]);
+ } else {
+ int len;
+ len = js_typed_array_get_length_internal(ctx, argv[0]);
+ if (len < 0)
+ return JS_EXCEPTION;
+ return JS_NewInt32(ctx, len);
+ }
+}
+#endif
+
+static JSValue js_typed_array_create(JSContext *ctx, JSValueConst ctor,
+ int argc, JSValueConst *argv)
+{
+ JSValue ret;
+ int new_len;
+ int64_t len;
+
+ ret = JS_CallConstructor(ctx, ctor, argc, argv);
+ if (JS_IsException(ret))
+ return ret;
+ /* validate the typed array */
+ new_len = js_typed_array_get_length_internal(ctx, ret);
+ if (new_len < 0)
+ goto fail;
+ if (argc == 1) {
+ /* ensure that it is large enough */
+ if (JS_ToLengthFree(ctx, &len, JS_DupValue(ctx, argv[0])))
+ goto fail;
+ if (new_len < len) {
+ JS_ThrowTypeError(ctx, "TypedArray length is too small");
+ fail:
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+ }
+ }
+ return ret;
+}
+
+#if 0
+static JSValue js_typed_array___create(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return js_typed_array_create(ctx, argv[0], max_int(argc - 1, 0), argv + 1);
+}
+#endif
+
+static JSValue js_typed_array___speciesCreate(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst obj;
+ JSObject *p;
+ JSValue ctor, ret;
+ int argc1;
+
+ obj = argv[0];
+ p = get_typed_array(ctx, obj, 0);
+ if (!p)
+ return JS_EXCEPTION;
+ ctor = JS_SpeciesConstructor(ctx, obj, JS_UNDEFINED);
+ if (JS_IsException(ctor))
+ return ctor;
+ argc1 = max_int(argc - 1, 0);
+ if (JS_IsUndefined(ctor)) {
+ ret = js_typed_array_constructor(ctx, JS_UNDEFINED, argc1, argv + 1,
+ p->class_id);
+ } else {
+ ret = js_typed_array_create(ctx, ctor, argc1, argv + 1);
+ JS_FreeValue(ctx, ctor);
+ }
+ return ret;
+}
+
+static JSValue js_typed_array_from(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ // from(items, mapfn = void 0, this_arg = void 0)
+ JSValueConst items = argv[0], mapfn, this_arg;
+ JSValueConst args[2];
+ JSValue stack[2];
+ JSValue iter, arr, r, v, v2;
+ int64_t k, len;
+ int done, mapping;
+
+ mapping = FALSE;
+ mapfn = JS_UNDEFINED;
+ this_arg = JS_UNDEFINED;
+ r = JS_UNDEFINED;
+ arr = JS_UNDEFINED;
+ stack[0] = JS_UNDEFINED;
+ stack[1] = JS_UNDEFINED;
+
+ if (argc > 1) {
+ mapfn = argv[1];
+ if (!JS_IsUndefined(mapfn)) {
+ if (check_function(ctx, mapfn))
+ goto exception;
+ mapping = 1;
+ if (argc > 2)
+ this_arg = argv[2];
+ }
+ }
+ iter = JS_GetProperty(ctx, items, JS_ATOM_Symbol_iterator);
+ if (JS_IsException(iter))
+ goto exception;
+ if (!JS_IsUndefined(iter)) {
+ JS_FreeValue(ctx, iter);
+ arr = JS_NewArray(ctx);
+ if (JS_IsException(arr))
+ goto exception;
+ stack[0] = JS_DupValue(ctx, items);
+ if (js_for_of_start(ctx, &stack[1], FALSE))
+ goto exception;
+ for (k = 0;; k++) {
+ v = JS_IteratorNext(ctx, stack[0], stack[1], 0, NULL, &done);
+ if (JS_IsException(v))
+ goto exception_close;
+ if (done)
+ break;
+ if (JS_DefinePropertyValueInt64(ctx, arr, k, v, JS_PROP_C_W_E | JS_PROP_THROW) < 0)
+ goto exception_close;
+ }
+ } else {
+ arr = JS_ToObject(ctx, items);
+ if (JS_IsException(arr))
+ goto exception;
+ }
+ if (js_get_length64(ctx, &len, arr) < 0)
+ goto exception;
+ v = JS_NewInt64(ctx, len);
+ args[0] = v;
+ r = js_typed_array_create(ctx, this_val, 1, args);
+ JS_FreeValue(ctx, v);
+ if (JS_IsException(r))
+ goto exception;
+ for(k = 0; k < len; k++) {
+ v = JS_GetPropertyInt64(ctx, arr, k);
+ if (JS_IsException(v))
+ goto exception;
+ if (mapping) {
+ args[0] = v;
+ args[1] = JS_NewInt32(ctx, k);
+ v2 = JS_Call(ctx, mapfn, this_arg, 2, args);
+ JS_FreeValue(ctx, v);
+ v = v2;
+ if (JS_IsException(v))
+ goto exception;
+ }
+ if (JS_SetPropertyInt64(ctx, r, k, v) < 0)
+ goto exception;
+ }
+ goto done;
+
+ exception_close:
+ if (!JS_IsUndefined(stack[0]))
+ JS_IteratorClose(ctx, stack[0], TRUE);
+ exception:
+ JS_FreeValue(ctx, r);
+ r = JS_EXCEPTION;
+ done:
+ JS_FreeValue(ctx, arr);
+ JS_FreeValue(ctx, stack[0]);
+ JS_FreeValue(ctx, stack[1]);
+ return r;
+}
+
+static JSValue js_typed_array_of(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValue obj;
+ JSValueConst args[1];
+ int i;
+
+ args[0] = JS_NewInt32(ctx, argc);
+ obj = js_typed_array_create(ctx, this_val, 1, args);
+ if (JS_IsException(obj))
+ return obj;
+
+ for(i = 0; i < argc; i++) {
+ if (JS_SetPropertyUint32(ctx, obj, i, JS_DupValue(ctx, argv[i])) < 0) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ }
+ return obj;
+}
+
+static JSValue js_typed_array_copyWithin(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ int len, to, from, final, count, shift;
+
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ return JS_EXCEPTION;
+
+ if (JS_ToInt32Clamp(ctx, &to, argv[0], 0, len, len))
+ return JS_EXCEPTION;
+
+ if (JS_ToInt32Clamp(ctx, &from, argv[1], 0, len, len))
+ return JS_EXCEPTION;
+
+ final = len;
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
+ return JS_EXCEPTION;
+ }
+
+ count = min_int(final - from, len - to);
+ if (count > 0) {
+ p = JS_VALUE_GET_OBJ(this_val);
+ if (typed_array_is_detached(ctx, p))
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ shift = typed_array_size_log2(p->class_id);
+ memmove(p->u.array.u.uint8_ptr + (to << shift),
+ p->u.array.u.uint8_ptr + (from << shift),
+ count << shift);
+ }
+ return JS_DupValue(ctx, this_val);
+}
+
+static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ int len, k, final, shift;
+ uint64_t v64;
+
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ return JS_EXCEPTION;
+ p = JS_VALUE_GET_OBJ(this_val);
+
+ if (p->class_id == JS_CLASS_UINT8C_ARRAY) {
+ int32_t v;
+ if (JS_ToUint8ClampFree(ctx, &v, JS_DupValue(ctx, argv[0])))
+ return JS_EXCEPTION;
+ v64 = v;
+ } else if (p->class_id <= JS_CLASS_UINT32_ARRAY) {
+ uint32_t v;
+ if (JS_ToUint32(ctx, &v, argv[0]))
+ return JS_EXCEPTION;
+ v64 = v;
+ } else
+#ifdef CONFIG_BIGNUM
+ if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
+ if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0]))
+ return JS_EXCEPTION;
+ } else
+#endif
+ {
+ double d;
+ if (JS_ToFloat64(ctx, &d, argv[0]))
+ return JS_EXCEPTION;
+ if (p->class_id == JS_CLASS_FLOAT32_ARRAY) {
+ union {
+ float f;
+ uint32_t u32;
+ } u;
+ u.f = d;
+ v64 = u.u32;
+ } else {
+ JSFloat64Union u;
+ u.d = d;
+ v64 = u.u64;
+ }
+ }
+
+ k = 0;
+ if (argc > 1) {
+ if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
+ return JS_EXCEPTION;
+ }
+
+ final = len;
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ if (JS_ToInt32Clamp(ctx, &final, argv[2], 0, len, len))
+ return JS_EXCEPTION;
+ }
+
+ if (typed_array_is_detached(ctx, p))
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+
+ shift = typed_array_size_log2(p->class_id);
+ switch(shift) {
+ case 0:
+ if (k < final) {
+ memset(p->u.array.u.uint8_ptr + k, v64, final - k);
+ }
+ break;
+ case 1:
+ for(; k < final; k++) {
+ p->u.array.u.uint16_ptr[k] = v64;
+ }
+ break;
+ case 2:
+ for(; k < final; k++) {
+ p->u.array.u.uint32_ptr[k] = v64;
+ }
+ break;
+ case 3:
+ for(; k < final; k++) {
+ p->u.array.u.uint64_ptr[k] = v64;
+ }
+ break;
+ default:
+ abort();
+ }
+ return JS_DupValue(ctx, this_val);
+}
+
+static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int findIndex)
+{
+ JSValueConst func, this_arg;
+ JSValueConst args[3];
+ JSValue val, index_val, res;
+ int len, k;
+
+ val = JS_UNDEFINED;
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ goto exception;
+
+ func = argv[0];
+ if (check_function(ctx, func))
+ goto exception;
+
+ this_arg = JS_UNDEFINED;
+ if (argc > 1)
+ this_arg = argv[1];
+
+ for(k = 0; k < len; k++) {
+ index_val = JS_NewInt32(ctx, k);
+ val = JS_GetPropertyValue(ctx, this_val, index_val);
+ if (JS_IsException(val))
+ goto exception;
+ args[0] = val;
+ args[1] = index_val;
+ args[2] = this_val;
+ res = JS_Call(ctx, func, this_arg, 3, args);
+ if (JS_IsException(res))
+ goto exception;
+ if (JS_ToBoolFree(ctx, res)) {
+ if (findIndex) {
+ JS_FreeValue(ctx, val);
+ return index_val;
+ } else {
+ return val;
+ }
+ }
+ JS_FreeValue(ctx, val);
+ }
+ if (findIndex)
+ return JS_NewInt32(ctx, -1);
+ else
+ return JS_UNDEFINED;
+
+exception:
+ JS_FreeValue(ctx, val);
+ return JS_EXCEPTION;
+}
+
+#define special_indexOf 0
+#define special_lastIndexOf 1
+#define special_includes (-1)
+
+static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int special)
+{
+ JSObject *p;
+ int len, tag, is_int, is_bigint, k, stop, inc, res = -1;
+ int64_t v64;
+ double d;
+ float f;
+
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ goto exception;
+ if (len == 0)
+ goto done;
+
+ if (special == special_lastIndexOf) {
+ k = len - 1;
+ if (argc > 1) {
+ if (JS_ToFloat64(ctx, &d, argv[1]))
+ goto exception;
+ if (isnan(d)) {
+ k = 0;
+ } else {
+ if (d >= 0) {
+ if (d < k) {
+ k = d;
+ }
+ } else {
+ d += len;
+ if (d < 0)
+ goto done;
+ k = d;
+ }
+ }
+ }
+ stop = -1;
+ inc = -1;
+ } else {
+ k = 0;
+ if (argc > 1) {
+ if (JS_ToInt32Clamp(ctx, &k, argv[1], 0, len, len))
+ goto exception;
+ }
+ stop = len;
+ inc = 1;
+ }
+
+ p = JS_VALUE_GET_OBJ(this_val);
+ /* if the array was detached, no need to go further (but no
+ exception is raised) */
+ if (typed_array_is_detached(ctx, p)) {
+ /* "includes" scans all the properties, so "undefined" can match */
+ if (special == special_includes && JS_IsUndefined(argv[0]) && len > 0)
+ res = 0;
+ goto done;
+ }
+
+ is_bigint = 0;
+ is_int = 0; /* avoid warning */
+ v64 = 0; /* avoid warning */
+ tag = JS_VALUE_GET_NORM_TAG(argv[0]);
+ if (tag == JS_TAG_INT) {
+ is_int = 1;
+ v64 = JS_VALUE_GET_INT(argv[0]);
+ d = v64;
+ } else if (tag == JS_TAG_FLOAT64) {
+ d = JS_VALUE_GET_FLOAT64(argv[0]);
+ v64 = d;
+ is_int = (v64 == d);
+ } else
+#ifdef CONFIG_BIGNUM
+ if (tag == JS_TAG_BIG_INT) {
+ JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]);
+
+ if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) {
+ if (bf_get_int64(&v64, &p1->num, 0) != 0)
+ goto done;
+ } else if (p->class_id == JS_CLASS_BIG_UINT64_ARRAY) {
+ if (bf_get_uint64((uint64_t *)&v64, &p1->num) != 0)
+ goto done;
+ } else {
+ goto done;
+ }
+ d = 0;
+ is_bigint = 1;
+ } else
+#endif
+ {
+ goto done;
+ }
+
+ switch (p->class_id) {
+ case JS_CLASS_INT8_ARRAY:
+ if (is_int && (int8_t)v64 == v64)
+ goto scan8;
+ break;
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ if (is_int && (uint8_t)v64 == v64) {
+ const uint8_t *pv, *pp;
+ uint16_t v;
+ scan8:
+ pv = p->u.array.u.uint8_ptr;
+ v = v64;
+ if (inc > 0) {
+ pp = memchr(pv + k, v, len - k);
+ if (pp)
+ res = pp - pv;
+ } else {
+ for (; k != stop; k += inc) {
+ if (pv[k] == v) {
+ res = k;
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case JS_CLASS_INT16_ARRAY:
+ if (is_int && (int16_t)v64 == v64)
+ goto scan16;
+ break;
+ case JS_CLASS_UINT16_ARRAY:
+ if (is_int && (uint16_t)v64 == v64) {
+ const uint16_t *pv;
+ uint16_t v;
+ scan16:
+ pv = p->u.array.u.uint16_ptr;
+ v = v64;
+ for (; k != stop; k += inc) {
+ if (pv[k] == v) {
+ res = k;
+ break;
+ }
+ }
+ }
+ break;
+ case JS_CLASS_INT32_ARRAY:
+ if (is_int && (int32_t)v64 == v64)
+ goto scan32;
+ break;
+ case JS_CLASS_UINT32_ARRAY:
+ if (is_int && (uint32_t)v64 == v64) {
+ const uint32_t *pv;
+ uint32_t v;
+ scan32:
+ pv = p->u.array.u.uint32_ptr;
+ v = v64;
+ for (; k != stop; k += inc) {
+ if (pv[k] == v) {
+ res = k;
+ break;
+ }
+ }
+ }
+ break;
+ case JS_CLASS_FLOAT32_ARRAY:
+ if (is_bigint)
+ break;
+ if (isnan(d)) {
+ const float *pv = p->u.array.u.float_ptr;
+ /* special case: indexOf returns -1, includes finds NaN */
+ if (special != special_includes)
+ goto done;
+ for (; k != stop; k += inc) {
+ if (isnan(pv[k])) {
+ res = k;
+ break;
+ }
+ }
+ } else if ((f = (float)d) == d) {
+ const float *pv = p->u.array.u.float_ptr;
+ for (; k != stop; k += inc) {
+ if (pv[k] == f) {
+ res = k;
+ break;
+ }
+ }
+ }
+ break;
+ case JS_CLASS_FLOAT64_ARRAY:
+ if (is_bigint)
+ break;
+ if (isnan(d)) {
+ const double *pv = p->u.array.u.double_ptr;
+ /* special case: indexOf returns -1, includes finds NaN */
+ if (special != special_includes)
+ goto done;
+ for (; k != stop; k += inc) {
+ if (isnan(pv[k])) {
+ res = k;
+ break;
+ }
+ }
+ } else {
+ const double *pv = p->u.array.u.double_ptr;
+ for (; k != stop; k += inc) {
+ if (pv[k] == d) {
+ res = k;
+ break;
+ }
+ }
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ if (is_bigint || (is_math_mode(ctx) && is_int &&
+ v64 >= -MAX_SAFE_INTEGER &&
+ v64 <= MAX_SAFE_INTEGER)) {
+ goto scan64;
+ }
+ break;
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ if (is_bigint || (is_math_mode(ctx) && is_int &&
+ v64 >= 0 && v64 <= MAX_SAFE_INTEGER)) {
+ const uint64_t *pv;
+ uint64_t v;
+ scan64:
+ pv = p->u.array.u.uint64_ptr;
+ v = v64;
+ for (; k != stop; k += inc) {
+ if (pv[k] == v) {
+ res = k;
+ break;
+ }
+ }
+ }
+ break;
+#endif
+ }
+
+done:
+ if (special == special_includes)
+ return JS_NewBool(ctx, res >= 0);
+ else
+ return JS_NewInt32(ctx, res);
+
+exception:
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_join(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int toLocaleString)
+{
+ JSValue sep = JS_UNDEFINED, el;
+ StringBuffer b_s, *b = &b_s;
+ JSString *p = NULL;
+ int i, n;
+ int c;
+
+ n = js_typed_array_get_length_internal(ctx, this_val);
+ if (n < 0)
+ goto exception;
+
+ c = ','; /* default separator */
+ if (!toLocaleString && argc > 0 && !JS_IsUndefined(argv[0])) {
+ sep = JS_ToString(ctx, argv[0]);
+ if (JS_IsException(sep))
+ goto exception;
+ p = JS_VALUE_GET_STRING(sep);
+ if (p->len == 1 && !p->is_wide_char)
+ c = p->u.str8[0];
+ else
+ c = -1;
+ }
+ string_buffer_init(ctx, b, 0);
+
+ /* XXX: optimize with direct access */
+ for(i = 0; i < n; i++) {
+ if (i > 0) {
+ if (c >= 0) {
+ if (string_buffer_putc8(b, c))
+ goto fail;
+ } else {
+ if (string_buffer_concat(b, p, 0, p->len))
+ goto fail;
+ }
+ }
+ el = JS_GetPropertyUint32(ctx, this_val, i);
+ /* Can return undefined for example if the typed array is detached */
+ if (!JS_IsNull(el) && !JS_IsUndefined(el)) {
+ if (JS_IsException(el))
+ goto fail;
+ if (toLocaleString) {
+ el = JS_ToLocaleStringFree(ctx, el);
+ }
+ if (string_buffer_concat_value_free(b, el))
+ goto fail;
+ }
+ }
+ JS_FreeValue(ctx, sep);
+ return string_buffer_end(b);
+
+fail:
+ string_buffer_free(b);
+ JS_FreeValue(ctx, sep);
+exception:
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ int len;
+
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ return JS_EXCEPTION;
+ if (len > 0) {
+ p = JS_VALUE_GET_OBJ(this_val);
+ switch (typed_array_size_log2(p->class_id)) {
+ case 0:
+ {
+ uint8_t *p1 = p->u.array.u.uint8_ptr;
+ uint8_t *p2 = p1 + len - 1;
+ while (p1 < p2) {
+ uint8_t v = *p1;
+ *p1++ = *p2;
+ *p2-- = v;
+ }
+ }
+ break;
+ case 1:
+ {
+ uint16_t *p1 = p->u.array.u.uint16_ptr;
+ uint16_t *p2 = p1 + len - 1;
+ while (p1 < p2) {
+ uint16_t v = *p1;
+ *p1++ = *p2;
+ *p2-- = v;
+ }
+ }
+ break;
+ case 2:
+ {
+ uint32_t *p1 = p->u.array.u.uint32_ptr;
+ uint32_t *p2 = p1 + len - 1;
+ while (p1 < p2) {
+ uint32_t v = *p1;
+ *p1++ = *p2;
+ *p2-- = v;
+ }
+ }
+ break;
+ case 3:
+ {
+ uint64_t *p1 = p->u.array.u.uint64_ptr;
+ uint64_t *p2 = p1 + len - 1;
+ while (p1 < p2) {
+ uint64_t v = *p1;
+ *p1++ = *p2;
+ *p2-- = v;
+ }
+ }
+ break;
+ default:
+ abort();
+ }
+ }
+ return JS_DupValue(ctx, this_val);
+}
+
+static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst args[2];
+ JSValue arr, val;
+ JSObject *p, *p1;
+ int n, len, start, final, count, shift;
+
+ arr = JS_UNDEFINED;
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ goto exception;
+
+ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
+ goto exception;
+ final = len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
+ goto exception;
+ }
+ count = max_int(final - start, 0);
+
+ p = get_typed_array(ctx, this_val, 0);
+ if (p == NULL)
+ goto exception;
+ shift = typed_array_size_log2(p->class_id);
+
+ args[0] = this_val;
+ args[1] = JS_NewInt32(ctx, count);
+ arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 2, args);
+ if (JS_IsException(arr))
+ goto exception;
+
+ if (count > 0) {
+ if (validate_typed_array(ctx, this_val)
+ || validate_typed_array(ctx, arr))
+ goto exception;
+
+ p1 = get_typed_array(ctx, arr, 0);
+ if (p1 != NULL && p->class_id == p1->class_id &&
+ typed_array_get_length(ctx, p1) >= count &&
+ typed_array_get_length(ctx, p) >= start + count) {
+ memcpy(p1->u.array.u.uint8_ptr,
+ p->u.array.u.uint8_ptr + (start << shift),
+ count << shift);
+ } else {
+ for (n = 0; n < count; n++) {
+ val = JS_GetPropertyValue(ctx, this_val, JS_NewInt32(ctx, start + n));
+ if (JS_IsException(val))
+ goto exception;
+ if (JS_SetPropertyValue(ctx, arr, JS_NewInt32(ctx, n), val,
+ JS_PROP_THROW) < 0)
+ goto exception;
+ }
+ }
+ }
+ return arr;
+
+ exception:
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_subarray(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSValueConst args[4];
+ JSValue arr, byteOffset, ta_buffer;
+ JSObject *p;
+ int len, start, final, count, shift, offset;
+
+ p = get_typed_array(ctx, this_val, 0);
+ if (!p)
+ goto exception;
+ len = p->u.array.count;
+ if (JS_ToInt32Clamp(ctx, &start, argv[0], 0, len, len))
+ goto exception;
+
+ final = len;
+ if (!JS_IsUndefined(argv[1])) {
+ if (JS_ToInt32Clamp(ctx, &final, argv[1], 0, len, len))
+ goto exception;
+ }
+ count = max_int(final - start, 0);
+ byteOffset = js_typed_array_get_byteOffset(ctx, this_val, 0);
+ if (JS_IsException(byteOffset))
+ goto exception;
+ shift = typed_array_size_log2(p->class_id);
+ offset = JS_VALUE_GET_INT(byteOffset) + (start << shift);
+ JS_FreeValue(ctx, byteOffset);
+ ta_buffer = js_typed_array_get_buffer(ctx, this_val, 0);
+ if (JS_IsException(ta_buffer))
+ goto exception;
+ args[0] = this_val;
+ args[1] = ta_buffer;
+ args[2] = JS_NewInt32(ctx, offset);
+ args[3] = JS_NewInt32(ctx, count);
+ arr = js_typed_array___speciesCreate(ctx, JS_UNDEFINED, 4, args);
+ JS_FreeValue(ctx, ta_buffer);
+ return arr;
+
+ exception:
+ return JS_EXCEPTION;
+}
+
+/* TypedArray.prototype.sort */
+
+static int js_cmp_doubles(double x, double y)
+{
+ if (isnan(x)) return isnan(y) ? 0 : +1;
+ if (isnan(y)) return -1;
+ if (x < y) return -1;
+ if (x > y) return 1;
+ if (x != 0) return 0;
+ if (signbit(x)) return signbit(y) ? 0 : -1;
+ else return signbit(y) ? 1 : 0;
+}
+
+static int js_TA_cmp_int8(const void *a, const void *b, void *opaque) {
+ return *(const int8_t *)a - *(const int8_t *)b;
+}
+
+static int js_TA_cmp_uint8(const void *a, const void *b, void *opaque) {
+ return *(const uint8_t *)a - *(const uint8_t *)b;
+}
+
+static int js_TA_cmp_int16(const void *a, const void *b, void *opaque) {
+ return *(const int16_t *)a - *(const int16_t *)b;
+}
+
+static int js_TA_cmp_uint16(const void *a, const void *b, void *opaque) {
+ return *(const uint16_t *)a - *(const uint16_t *)b;
+}
+
+static int js_TA_cmp_int32(const void *a, const void *b, void *opaque) {
+ int32_t x = *(const int32_t *)a;
+ int32_t y = *(const int32_t *)b;
+ return (y < x) - (y > x);
+}
+
+static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) {
+ uint32_t x = *(const uint32_t *)a;
+ uint32_t y = *(const uint32_t *)b;
+ return (y < x) - (y > x);
+}
+
+#ifdef CONFIG_BIGNUM
+static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) {
+ int64_t x = *(const int64_t *)a;
+ int64_t y = *(const int64_t *)b;
+ return (y < x) - (y > x);
+}
+
+static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) {
+ uint64_t x = *(const uint64_t *)a;
+ uint64_t y = *(const uint64_t *)b;
+ return (y < x) - (y > x);
+}
+#endif
+
+static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) {
+ return js_cmp_doubles(*(const float *)a, *(const float *)b);
+}
+
+static int js_TA_cmp_float64(const void *a, const void *b, void *opaque) {
+ return js_cmp_doubles(*(const double *)a, *(const double *)b);
+}
+
+static JSValue js_TA_get_int8(JSContext *ctx, const void *a) {
+ return JS_NewInt32(ctx, *(const int8_t *)a);
+}
+
+static JSValue js_TA_get_uint8(JSContext *ctx, const void *a) {
+ return JS_NewInt32(ctx, *(const uint8_t *)a);
+}
+
+static JSValue js_TA_get_int16(JSContext *ctx, const void *a) {
+ return JS_NewInt32(ctx, *(const int16_t *)a);
+}
+
+static JSValue js_TA_get_uint16(JSContext *ctx, const void *a) {
+ return JS_NewInt32(ctx, *(const uint16_t *)a);
+}
+
+static JSValue js_TA_get_int32(JSContext *ctx, const void *a) {
+ return JS_NewInt32(ctx, *(const int32_t *)a);
+}
+
+static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) {
+ return JS_NewUint32(ctx, *(const uint32_t *)a);
+}
+
+#ifdef CONFIG_BIGNUM
+static JSValue js_TA_get_int64(JSContext *ctx, const void *a) {
+ return JS_NewBigInt64(ctx, *(int64_t *)a);
+}
+
+static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {
+ return JS_NewBigUint64(ctx, *(uint64_t *)a);
+}
+#endif
+
+static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {
+ return JS_NewFloat64Impl(ctx, *(const float *)a);
+}
+
+static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
+ return JS_NewFloat64Impl(ctx, *(const double *)a);
+}
+
+struct TA_sort_context {
+ JSContext *ctx;
+ int exception;
+ JSValueConst arr;
+ JSValueConst cmp;
+ JSValue (*getfun)(JSContext *ctx, const void *a);
+ uint8_t *array_ptr; /* cannot change unless the array is detached */
+ int elt_size;
+};
+
+static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
+ struct TA_sort_context *psc = opaque;
+ JSContext *ctx = psc->ctx;
+ uint32_t a_idx, b_idx;
+ JSValueConst argv[2];
+ JSValue res;
+ int cmp;
+
+ cmp = 0;
+ if (!psc->exception) {
+ a_idx = *(uint32_t *)a;
+ b_idx = *(uint32_t *)b;
+ argv[0] = psc->getfun(ctx, psc->array_ptr +
+ a_idx * (size_t)psc->elt_size);
+ argv[1] = psc->getfun(ctx, psc->array_ptr +
+ b_idx * (size_t)(psc->elt_size));
+ res = JS_Call(ctx, psc->cmp, JS_UNDEFINED, 2, argv);
+ if (JS_IsException(res)) {
+ psc->exception = 1;
+ goto done;
+ }
+ if (JS_VALUE_GET_TAG(res) == JS_TAG_INT) {
+ int val = JS_VALUE_GET_INT(res);
+ cmp = (val > 0) - (val < 0);
+ } else {
+ double val;
+ if (JS_ToFloat64Free(ctx, &val, res) < 0) {
+ psc->exception = 1;
+ goto done;
+ } else {
+ cmp = (val > 0) - (val < 0);
+ }
+ }
+ if (cmp == 0) {
+ /* make sort stable: compare array offsets */
+ cmp = (a_idx > b_idx) - (a_idx < b_idx);
+ }
+ if (validate_typed_array(ctx, psc->arr) < 0) {
+ psc->exception = 1;
+ }
+ done:
+ JS_FreeValue(ctx, argv[0]);
+ JS_FreeValue(ctx, argv[1]);
+ }
+ return cmp;
+}
+
+static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ JSObject *p;
+ int len;
+ size_t elt_size;
+ struct TA_sort_context tsc;
+ void *array_ptr;
+ int (*cmpfun)(const void *a, const void *b, void *opaque);
+
+ tsc.ctx = ctx;
+ tsc.exception = 0;
+ tsc.arr = this_val;
+ tsc.cmp = argv[0];
+
+ len = js_typed_array_get_length_internal(ctx, this_val);
+ if (len < 0)
+ return JS_EXCEPTION;
+ if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp))
+ return JS_EXCEPTION;
+
+ if (len > 1) {
+ p = JS_VALUE_GET_OBJ(this_val);
+ switch (p->class_id) {
+ case JS_CLASS_INT8_ARRAY:
+ tsc.getfun = js_TA_get_int8;
+ cmpfun = js_TA_cmp_int8;
+ break;
+ case JS_CLASS_UINT8C_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ tsc.getfun = js_TA_get_uint8;
+ cmpfun = js_TA_cmp_uint8;
+ break;
+ case JS_CLASS_INT16_ARRAY:
+ tsc.getfun = js_TA_get_int16;
+ cmpfun = js_TA_cmp_int16;
+ break;
+ case JS_CLASS_UINT16_ARRAY:
+ tsc.getfun = js_TA_get_uint16;
+ cmpfun = js_TA_cmp_uint16;
+ break;
+ case JS_CLASS_INT32_ARRAY:
+ tsc.getfun = js_TA_get_int32;
+ cmpfun = js_TA_cmp_int32;
+ break;
+ case JS_CLASS_UINT32_ARRAY:
+ tsc.getfun = js_TA_get_uint32;
+ cmpfun = js_TA_cmp_uint32;
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ tsc.getfun = js_TA_get_int64;
+ cmpfun = js_TA_cmp_int64;
+ break;
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ tsc.getfun = js_TA_get_uint64;
+ cmpfun = js_TA_cmp_uint64;
+ break;
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ tsc.getfun = js_TA_get_float32;
+ cmpfun = js_TA_cmp_float32;
+ break;
+ case JS_CLASS_FLOAT64_ARRAY:
+ tsc.getfun = js_TA_get_float64;
+ cmpfun = js_TA_cmp_float64;
+ break;
+ default:
+ abort();
+ }
+ array_ptr = p->u.array.u.ptr;
+ elt_size = 1 << typed_array_size_log2(p->class_id);
+ if (!JS_IsUndefined(tsc.cmp)) {
+ uint32_t *array_idx;
+ void *array_tmp;
+ size_t i, j;
+
+ /* XXX: a stable sort would use less memory */
+ array_idx = js_malloc(ctx, len * sizeof(array_idx[0]));
+ if (!array_idx)
+ return JS_EXCEPTION;
+ for(i = 0; i < len; i++)
+ array_idx[i] = i;
+ tsc.array_ptr = array_ptr;
+ tsc.elt_size = elt_size;
+ rqsort(array_idx, len, sizeof(array_idx[0]),
+ js_TA_cmp_generic, &tsc);
+ if (tsc.exception)
+ goto fail;
+ array_tmp = js_malloc(ctx, len * elt_size);
+ if (!array_tmp) {
+ fail:
+ js_free(ctx, array_idx);
+ return JS_EXCEPTION;
+ }
+ memcpy(array_tmp, array_ptr, len * elt_size);
+ switch(elt_size) {
+ case 1:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j];
+ }
+ break;
+ case 2:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j];
+ }
+ break;
+ case 4:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j];
+ }
+ break;
+ case 8:
+ for(i = 0; i < len; i++) {
+ j = array_idx[i];
+ ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j];
+ }
+ break;
+ default:
+ abort();
+ }
+ js_free(ctx, array_tmp);
+ js_free(ctx, array_idx);
+ } else {
+ rqsort(array_ptr, len, elt_size, cmpfun, &tsc);
+ if (tsc.exception)
+ return JS_EXCEPTION;
+ }
+ }
+ return JS_DupValue(ctx, this_val);
+}
+
+static const JSCFunctionListEntry js_typed_array_base_funcs[] = {
+ JS_CFUNC_DEF("from", 1, js_typed_array_from ),
+ JS_CFUNC_DEF("of", 0, js_typed_array_of ),
+ JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ),
+ //JS_CFUNC_DEF("__getLength", 2, js_typed_array___getLength ),
+ //JS_CFUNC_DEF("__create", 2, js_typed_array___create ),
+ //JS_CFUNC_DEF("__speciesCreate", 2, js_typed_array___speciesCreate ),
+};
+
+static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = {
+ JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ),
+ JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ),
+ JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ),
+ JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ),
+ JS_CFUNC_DEF("set", 1, js_typed_array_set ),
+ JS_CFUNC_MAGIC_DEF("values", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_VALUE ),
+ JS_ALIAS_DEF("[Symbol.iterator]", "values" ),
+ JS_CFUNC_MAGIC_DEF("keys", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY ),
+ JS_CFUNC_MAGIC_DEF("entries", 0, js_create_typed_array_iterator, JS_ITERATOR_KIND_KEY_AND_VALUE ),
+ JS_CGETSET_DEF("[Symbol.toStringTag]", js_typed_array_get_toStringTag, NULL ),
+ JS_CFUNC_DEF("copyWithin", 2, js_typed_array_copyWithin ),
+ JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every | special_TA ),
+ JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some | special_TA ),
+ JS_CFUNC_MAGIC_DEF("forEach", 1, js_array_every, special_forEach | special_TA ),
+ JS_CFUNC_MAGIC_DEF("map", 1, js_array_every, special_map | special_TA ),
+ JS_CFUNC_MAGIC_DEF("filter", 1, js_array_every, special_filter | special_TA ),
+ JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ),
+ JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ),
+ JS_CFUNC_DEF("fill", 1, js_typed_array_fill ),
+ JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, 0 ),
+ JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, 1 ),
+ JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ),
+ JS_CFUNC_DEF("slice", 2, js_typed_array_slice ),
+ JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ),
+ JS_CFUNC_DEF("sort", 1, js_typed_array_sort ),
+ JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ),
+ JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ),
+ JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ),
+ JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_typed_array_indexOf, special_lastIndexOf ),
+ JS_CFUNC_MAGIC_DEF("includes", 1, js_typed_array_indexOf, special_includes ),
+ //JS_ALIAS_BASE_DEF("toString", "toString", 2 /* Array.prototype. */), @@@
+};
+
+static JSValue js_typed_array_base_constructor(JSContext *ctx,
+ JSValueConst this_val,
+ int argc, JSValueConst *argv)
+{
+ return JS_ThrowTypeError(ctx, "cannot be called");
+}
+
+/* 'obj' must be an allocated typed array object */
+static int typed_array_init(JSContext *ctx, JSValueConst obj,
+ JSValue buffer, uint64_t offset, uint64_t len)
+{
+ JSTypedArray *ta;
+ JSObject *p, *pbuffer;
+ JSArrayBuffer *abuf;
+ int size_log2;
+
+ p = JS_VALUE_GET_OBJ(obj);
+ size_log2 = typed_array_size_log2(p->class_id);
+ ta = js_malloc(ctx, sizeof(*ta));
+ if (!ta) {
+ JS_FreeValue(ctx, buffer);
+ return -1;
+ }
+ pbuffer = JS_VALUE_GET_OBJ(buffer);
+ abuf = pbuffer->u.array_buffer;
+ ta->obj = p;
+ ta->buffer = pbuffer;
+ ta->offset = offset;
+ ta->length = len << size_log2;
+ list_add_tail(&ta->link, &abuf->array_list);
+ p->u.typed_array = ta;
+ p->u.array.count = len;
+ p->u.array.u.ptr = abuf->data + offset;
+ return 0;
+}
+
+
+static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
+ JSValueConst obj, JSValueConst method)
+{
+ JSValue arr, iter, next_method = JS_UNDEFINED, val;
+ BOOL done;
+ uint32_t k;
+
+ *plen = 0;
+ arr = JS_NewArray(ctx);
+ if (JS_IsException(arr))
+ return arr;
+ iter = JS_GetIterator2(ctx, obj, method);
+ if (JS_IsException(iter))
+ goto fail;
+ next_method = JS_GetProperty(ctx, iter, JS_ATOM_next);
+ if (JS_IsException(next_method))
+ goto fail;
+ k = 0;
+ for(;;) {
+ val = JS_IteratorNext(ctx, iter, next_method, 0, NULL, &done);
+ if (JS_IsException(val))
+ goto fail;
+ if (done) {
+ JS_FreeValue(ctx, val);
+ break;
+ }
+ if (JS_CreateDataPropertyUint32(ctx, arr, k, val, JS_PROP_THROW) < 0)
+ goto fail;
+ k++;
+ }
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ *plen = k;
+ return arr;
+ fail:
+ JS_FreeValue(ctx, next_method);
+ JS_FreeValue(ctx, iter);
+ JS_FreeValue(ctx, arr);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_constructor_obj(JSContext *ctx,
+ JSValueConst new_target,
+ JSValueConst obj,
+ int classid)
+{
+ JSValue iter, ret, arr = JS_UNDEFINED, val, buffer;
+ uint32_t i;
+ int size_log2;
+ int64_t len;
+
+ size_log2 = typed_array_size_log2(classid);
+ ret = js_create_from_ctor(ctx, new_target, classid);
+ if (JS_IsException(ret))
+ return JS_EXCEPTION;
+
+ iter = JS_GetProperty(ctx, obj, JS_ATOM_Symbol_iterator);
+ if (JS_IsException(iter))
+ goto fail;
+ if (!JS_IsUndefined(iter) && !JS_IsNull(iter)) {
+ uint32_t len1;
+ arr = js_array_from_iterator(ctx, &len1, obj, iter);
+ JS_FreeValue(ctx, iter);
+ if (JS_IsException(arr))
+ goto fail;
+ len = len1;
+ } else {
+ if (js_get_length64(ctx, &len, obj))
+ goto fail;
+ arr = JS_DupValue(ctx, obj);
+ }
+
+ buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
+ len << size_log2);
+ if (JS_IsException(buffer))
+ goto fail;
+ if (typed_array_init(ctx, ret, buffer, 0, len))
+ goto fail;
+
+ for(i = 0; i < len; i++) {
+ val = JS_GetPropertyUint32(ctx, arr, i);
+ if (JS_IsException(val))
+ goto fail;
+ if (JS_SetPropertyUint32(ctx, ret, i, val) < 0)
+ goto fail;
+ }
+ JS_FreeValue(ctx, arr);
+ return ret;
+ fail:
+ JS_FreeValue(ctx, arr);
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_constructor_ta(JSContext *ctx,
+ JSValueConst new_target,
+ JSValueConst src_obj,
+ int classid)
+{
+ JSObject *p, *src_buffer;
+ JSTypedArray *ta;
+ JSValue ctor, obj, buffer;
+ uint32_t len, i;
+ int size_log2;
+ JSArrayBuffer *src_abuf, *abuf;
+
+ obj = js_create_from_ctor(ctx, new_target, classid);
+ if (JS_IsException(obj))
+ return obj;
+ p = JS_VALUE_GET_OBJ(src_obj);
+ if (typed_array_is_detached(ctx, p)) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ ta = p->u.typed_array;
+ len = p->u.array.count;
+ src_buffer = ta->buffer;
+ src_abuf = src_buffer->u.array_buffer;
+ if (!src_abuf->shared) {
+ ctor = JS_SpeciesConstructor(ctx, JS_MKPTR(JS_TAG_OBJECT, src_buffer),
+ JS_UNDEFINED);
+ if (JS_IsException(ctor))
+ goto fail;
+ } else {
+ /* force ArrayBuffer default constructor */
+ ctor = JS_UNDEFINED;
+ }
+ size_log2 = typed_array_size_log2(classid);
+ buffer = js_array_buffer_constructor1(ctx, ctor,
+ (uint64_t)len << size_log2);
+ JS_FreeValue(ctx, ctor);
+ if (JS_IsException(buffer))
+ goto fail;
+ /* necessary because it could have been detached */
+ if (typed_array_is_detached(ctx, p)) {
+ JS_FreeValue(ctx, buffer);
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ abuf = JS_GetOpaque(buffer, JS_CLASS_ARRAY_BUFFER);
+ if (typed_array_init(ctx, obj, buffer, 0, len))
+ goto fail;
+ if (p->class_id == classid) {
+ /* same type: copy the content */
+ memcpy(abuf->data, src_abuf->data + ta->offset, abuf->byte_length);
+ } else {
+ for(i = 0; i < len; i++) {
+ JSValue val;
+ val = JS_GetPropertyUint32(ctx, src_obj, i);
+ if (JS_IsException(val))
+ goto fail;
+ if (JS_SetPropertyUint32(ctx, obj, i, val) < 0)
+ goto fail;
+ }
+ }
+ return obj;
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+}
+
+static JSValue js_typed_array_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv,
+ int classid)
+{
+ JSValue buffer, obj;
+ JSArrayBuffer *abuf;
+ int size_log2;
+ uint64_t len, offset;
+
+ size_log2 = typed_array_size_log2(classid);
+ if (JS_VALUE_GET_TAG(argv[0]) != JS_TAG_OBJECT) {
+ if (JS_ToIndex(ctx, &len, argv[0]))
+ return JS_EXCEPTION;
+ buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED,
+ len << size_log2);
+ if (JS_IsException(buffer))
+ return JS_EXCEPTION;
+ offset = 0;
+ } else {
+ JSObject *p = JS_VALUE_GET_OBJ(argv[0]);
+ if (p->class_id == JS_CLASS_ARRAY_BUFFER ||
+ p->class_id == JS_CLASS_SHARED_ARRAY_BUFFER) {
+ abuf = p->u.array_buffer;
+ if (JS_ToIndex(ctx, &offset, argv[1]))
+ return JS_EXCEPTION;
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if ((offset & ((1 << size_log2) - 1)) != 0 ||
+ offset > abuf->byte_length)
+ return JS_ThrowRangeError(ctx, "invalid offset");
+ if (JS_IsUndefined(argv[2])) {
+ if ((abuf->byte_length & ((1 << size_log2) - 1)) != 0)
+ goto invalid_length;
+ len = (abuf->byte_length - offset) >> size_log2;
+ } else {
+ if (JS_ToIndex(ctx, &len, argv[2]))
+ return JS_EXCEPTION;
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if ((offset + (len << size_log2)) > abuf->byte_length) {
+ invalid_length:
+ return JS_ThrowRangeError(ctx, "invalid length");
+ }
+ }
+ buffer = JS_DupValue(ctx, argv[0]);
+ } else {
+ if (p->class_id >= JS_CLASS_UINT8C_ARRAY &&
+ p->class_id <= JS_CLASS_FLOAT64_ARRAY) {
+ return js_typed_array_constructor_ta(ctx, new_target, argv[0], classid);
+ } else {
+ return js_typed_array_constructor_obj(ctx, new_target, argv[0], classid);
+ }
+ }
+ }
+
+ obj = js_create_from_ctor(ctx, new_target, classid);
+ if (JS_IsException(obj)) {
+ JS_FreeValue(ctx, buffer);
+ return JS_EXCEPTION;
+ }
+ if (typed_array_init(ctx, obj, buffer, offset, len)) {
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ return obj;
+}
+
+static void js_typed_array_finalizer(JSRuntime *rt, JSValue val)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSTypedArray *ta = p->u.typed_array;
+ if (ta) {
+ /* during the GC the finalizers are called in an arbitrary
+ order so the ArrayBuffer finalizer may have been called */
+ if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) {
+ list_del(&ta->link);
+ }
+ JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer));
+ js_free_rt(rt, ta);
+ }
+}
+
+static void js_typed_array_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func)
+{
+ JSObject *p = JS_VALUE_GET_OBJ(val);
+ JSTypedArray *ta = p->u.typed_array;
+ if (ta) {
+ JS_MarkValue(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer), mark_func);
+ }
+}
+
+static JSValue js_dataview_constructor(JSContext *ctx,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv)
+{
+ JSArrayBuffer *abuf;
+ uint64_t offset;
+ uint32_t len;
+ JSValueConst buffer;
+ JSValue obj;
+ JSTypedArray *ta;
+ JSObject *p;
+
+ buffer = argv[0];
+ abuf = js_get_array_buffer(ctx, buffer);
+ if (!abuf)
+ return JS_EXCEPTION;
+ offset = 0;
+ if (argc > 1) {
+ if (JS_ToIndex(ctx, &offset, argv[1]))
+ return JS_EXCEPTION;
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if (offset > abuf->byte_length)
+ return JS_ThrowRangeError(ctx, "invalid byteOffset");
+ len = abuf->byte_length - offset;
+ if (argc > 2 && !JS_IsUndefined(argv[2])) {
+ uint64_t l;
+ if (JS_ToIndex(ctx, &l, argv[2]))
+ return JS_EXCEPTION;
+ if (l > len)
+ return JS_ThrowRangeError(ctx, "invalid byteLength");
+ len = l;
+ }
+
+ obj = js_create_from_ctor(ctx, new_target, JS_CLASS_DATAVIEW);
+ if (JS_IsException(obj))
+ return JS_EXCEPTION;
+ if (abuf->detached) {
+ /* could have been detached in js_create_from_ctor() */
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ goto fail;
+ }
+ ta = js_malloc(ctx, sizeof(*ta));
+ if (!ta) {
+ fail:
+ JS_FreeValue(ctx, obj);
+ return JS_EXCEPTION;
+ }
+ p = JS_VALUE_GET_OBJ(obj);
+ ta->obj = p;
+ ta->buffer = JS_VALUE_GET_OBJ(JS_DupValue(ctx, buffer));
+ ta->offset = offset;
+ ta->length = len;
+ list_add_tail(&ta->link, &abuf->array_list);
+ p->u.typed_array = ta;
+ return obj;
+}
+
+static JSValue js_dataview_getValue(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int class_id)
+{
+ JSTypedArray *ta;
+ JSArrayBuffer *abuf;
+ int is_swap, size;
+ uint8_t *ptr;
+ uint32_t v;
+ uint64_t pos;
+
+ ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
+ if (!ta)
+ return JS_EXCEPTION;
+ size = 1 << typed_array_size_log2(class_id);
+ if (JS_ToIndex(ctx, &pos, argv[0]))
+ return JS_EXCEPTION;
+ is_swap = FALSE;
+ if (argc > 1)
+ is_swap = JS_ToBool(ctx, argv[1]);
+#ifndef WORDS_BIGENDIAN
+ is_swap ^= 1;
+#endif
+ abuf = ta->buffer->u.array_buffer;
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if ((pos + size) > ta->length)
+ return JS_ThrowRangeError(ctx, "out of bound");
+ ptr = abuf->data + ta->offset + pos;
+
+ switch(class_id) {
+ case JS_CLASS_INT8_ARRAY:
+ return JS_NewInt32(ctx, *(int8_t *)ptr);
+ case JS_CLASS_UINT8_ARRAY:
+ return JS_NewInt32(ctx, *ptr);
+ case JS_CLASS_INT16_ARRAY:
+ v = get_u16(ptr);
+ if (is_swap)
+ v = bswap16(v);
+ return JS_NewInt32(ctx, (int16_t)v);
+ case JS_CLASS_UINT16_ARRAY:
+ v = get_u16(ptr);
+ if (is_swap)
+ v = bswap16(v);
+ return JS_NewInt32(ctx, v);
+ case JS_CLASS_INT32_ARRAY:
+ v = get_u32(ptr);
+ if (is_swap)
+ v = bswap32(v);
+ return JS_NewInt32(ctx, v);
+ case JS_CLASS_UINT32_ARRAY:
+ v = get_u32(ptr);
+ if (is_swap)
+ v = bswap32(v);
+ return JS_NewUint32(ctx, v);
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ {
+ uint64_t v;
+ v = get_u64(ptr);
+ if (is_swap)
+ v = bswap64(v);
+ return JS_NewBigInt64(ctx, v);
+ }
+ break;
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ {
+ uint64_t v;
+ v = get_u64(ptr);
+ if (is_swap)
+ v = bswap64(v);
+ return JS_NewBigUint64(ctx, v);
+ }
+ break;
+#endif
+ case JS_CLASS_FLOAT32_ARRAY:
+ {
+ union {
+ float f;
+ uint32_t i;
+ } u;
+ v = get_u32(ptr);
+ if (is_swap)
+ v = bswap32(v);
+ u.i = v;
+ return JS_NewFloat64Impl(ctx, u.f);
+ }
+ case JS_CLASS_FLOAT64_ARRAY:
+ {
+ union {
+ double f;
+ uint64_t i;
+ } u;
+ u.i = get_u64(ptr);
+ if (is_swap)
+ u.i = bswap64(u.i);
+ return JS_NewFloat64Impl(ctx, u.f);
+ }
+ default:
+ abort();
+ }
+}
+
+static JSValue js_dataview_setValue(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int class_id)
+{
+ JSTypedArray *ta;
+ JSArrayBuffer *abuf;
+ int is_swap, size;
+ uint8_t *ptr;
+ uint64_t v64;
+ uint32_t v;
+ uint64_t pos;
+ JSValueConst val;
+
+ ta = JS_GetOpaque2(ctx, this_obj, JS_CLASS_DATAVIEW);
+ if (!ta)
+ return JS_EXCEPTION;
+ size = 1 << typed_array_size_log2(class_id);
+ if (JS_ToIndex(ctx, &pos, argv[0]))
+ return JS_EXCEPTION;
+ val = argv[1];
+ v = 0; /* avoid warning */
+ v64 = 0; /* avoid warning */
+ if (class_id <= JS_CLASS_UINT32_ARRAY) {
+ if (JS_ToUint32(ctx, &v, val))
+ return JS_EXCEPTION;
+ } else
+#ifdef CONFIG_BIGNUM
+ if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) {
+ if (JS_ToBigInt64(ctx, (int64_t *)&v64, val))
+ return JS_EXCEPTION;
+ } else
+#endif
+ {
+ double d;
+ if (JS_ToFloat64(ctx, &d, val))
+ return JS_EXCEPTION;
+ if (class_id == JS_CLASS_FLOAT32_ARRAY) {
+ union {
+ float f;
+ uint32_t i;
+ } u;
+ u.f = d;
+ v = u.i;
+ } else {
+ JSFloat64Union u;
+ u.d = d;
+ v64 = u.u64;
+ }
+ }
+ is_swap = FALSE;
+ if (argc > 2)
+ is_swap = JS_ToBool(ctx, argv[2]);
+#ifndef WORDS_BIGENDIAN
+ is_swap ^= 1;
+#endif
+ abuf = ta->buffer->u.array_buffer;
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ if ((pos + size) > ta->length)
+ return JS_ThrowRangeError(ctx, "out of bound");
+ ptr = abuf->data + ta->offset + pos;
+
+ switch(class_id) {
+ case JS_CLASS_INT8_ARRAY:
+ case JS_CLASS_UINT8_ARRAY:
+ *ptr = v;
+ break;
+ case JS_CLASS_INT16_ARRAY:
+ case JS_CLASS_UINT16_ARRAY:
+ if (is_swap)
+ v = bswap16(v);
+ put_u16(ptr, v);
+ break;
+ case JS_CLASS_INT32_ARRAY:
+ case JS_CLASS_UINT32_ARRAY:
+ case JS_CLASS_FLOAT32_ARRAY:
+ if (is_swap)
+ v = bswap32(v);
+ put_u32(ptr, v);
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ case JS_CLASS_BIG_UINT64_ARRAY:
+#endif
+ case JS_CLASS_FLOAT64_ARRAY:
+ if (is_swap)
+ v64 = bswap64(v64);
+ put_u64(ptr, v64);
+ break;
+ default:
+ abort();
+ }
+ return JS_UNDEFINED;
+}
+
+static const JSCFunctionListEntry js_dataview_proto_funcs[] = {
+ JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 1 ),
+ JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 1 ),
+ JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 1 ),
+ JS_CFUNC_MAGIC_DEF("getInt8", 1, js_dataview_getValue, JS_CLASS_INT8_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getUint8", 1, js_dataview_getValue, JS_CLASS_UINT8_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getInt16", 1, js_dataview_getValue, JS_CLASS_INT16_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ),
+#ifdef CONFIG_BIGNUM
+ JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ),
+#endif
+ JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setUint8", 2, js_dataview_setValue, JS_CLASS_UINT8_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setInt16", 2, js_dataview_setValue, JS_CLASS_INT16_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ),
+#ifdef CONFIG_BIGNUM
+ JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ),
+#endif
+ JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ),
+ JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ),
+};
+
+/* Atomics */
+#ifdef CONFIG_ATOMICS
+
+typedef enum AtomicsOpEnum {
+ ATOMICS_OP_ADD,
+ ATOMICS_OP_AND,
+ ATOMICS_OP_OR,
+ ATOMICS_OP_SUB,
+ ATOMICS_OP_XOR,
+ ATOMICS_OP_EXCHANGE,
+ ATOMICS_OP_COMPARE_EXCHANGE,
+ ATOMICS_OP_LOAD,
+} AtomicsOpEnum;
+
+static void *js_atomics_get_ptr(JSContext *ctx,
+ JSArrayBuffer **pabuf,
+ int *psize_log2, JSClassID *pclass_id,
+ JSValueConst obj, JSValueConst idx_val,
+ int is_waitable)
+{
+ JSObject *p;
+ JSTypedArray *ta;
+ JSArrayBuffer *abuf;
+ void *ptr;
+ uint64_t idx;
+ BOOL err;
+ int size_log2;
+
+ if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)
+ goto fail;
+ p = JS_VALUE_GET_OBJ(obj);
+#ifdef CONFIG_BIGNUM
+ if (is_waitable)
+ err = (p->class_id != JS_CLASS_INT32_ARRAY &&
+ p->class_id != JS_CLASS_BIG_INT64_ARRAY);
+ else
+ err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
+ p->class_id <= JS_CLASS_BIG_UINT64_ARRAY);
+#else
+ if (is_waitable)
+ err = (p->class_id != JS_CLASS_INT32_ARRAY);
+ else
+ err = !(p->class_id >= JS_CLASS_INT8_ARRAY &&
+ p->class_id <= JS_CLASS_UINT32_ARRAY);
+#endif
+ if (err) {
+ fail:
+ JS_ThrowTypeError(ctx, "integer TypedArray expected");
+ return NULL;
+ }
+ ta = p->u.typed_array;
+ abuf = ta->buffer->u.array_buffer;
+ if (!abuf->shared) {
+ if (is_waitable == 2) {
+ JS_ThrowTypeError(ctx, "not a SharedArrayBuffer TypedArray");
+ return NULL;
+ }
+ if (abuf->detached) {
+ JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ return NULL;
+ }
+ }
+ if (JS_ToIndex(ctx, &idx, idx_val)) {
+ return NULL;
+ }
+ /* if the array buffer is detached, p->u.array.count = 0 */
+ if (idx >= p->u.array.count) {
+ JS_ThrowRangeError(ctx, "out-of-bound access");
+ return NULL;
+ }
+ size_log2 = typed_array_size_log2(p->class_id);
+ ptr = p->u.array.u.uint8_ptr + ((uintptr_t)idx << size_log2);
+ if (pabuf)
+ *pabuf = abuf;
+ if (psize_log2)
+ *psize_log2 = size_log2;
+ if (pclass_id)
+ *pclass_id = p->class_id;
+ return ptr;
+}
+
+static JSValue js_atomics_op(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv, int op)
+{
+ int size_log2;
+#ifdef CONFIG_BIGNUM
+ uint64_t v, a, rep_val;
+#else
+ uint32_t v, a, rep_val;
+#endif
+ void *ptr;
+ JSValue ret;
+ JSClassID class_id;
+ JSArrayBuffer *abuf;
+
+ ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, &class_id,
+ argv[0], argv[1], 0);
+ if (!ptr)
+ return JS_EXCEPTION;
+ rep_val = 0;
+ if (op == ATOMICS_OP_LOAD) {
+ v = 0;
+ } else {
+#ifdef CONFIG_BIGNUM
+ if (size_log2 == 3) {
+ int64_t v64;
+ if (JS_ToBigInt64(ctx, &v64, argv[2]))
+ return JS_EXCEPTION;
+ v = v64;
+ if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
+ if (JS_ToBigInt64(ctx, &v64, argv[3]))
+ return JS_EXCEPTION;
+ rep_val = v64;
+ }
+ } else
+#endif
+ {
+ uint32_t v32;
+ if (JS_ToUint32(ctx, &v32, argv[2]))
+ return JS_EXCEPTION;
+ v = v32;
+ if (op == ATOMICS_OP_COMPARE_EXCHANGE) {
+ if (JS_ToUint32(ctx, &v32, argv[3]))
+ return JS_EXCEPTION;
+ rep_val = v32;
+ }
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ }
+
+ switch(op | (size_log2 << 3)) {
+
+#ifdef CONFIG_BIGNUM
+#define OP(op_name, func_name) \
+ case ATOMICS_OP_ ## op_name | (0 << 3): \
+ a = func_name((_Atomic(uint8_t) *)ptr, v); \
+ break; \
+ case ATOMICS_OP_ ## op_name | (1 << 3): \
+ a = func_name((_Atomic(uint16_t) *)ptr, v); \
+ break; \
+ case ATOMICS_OP_ ## op_name | (2 << 3): \
+ a = func_name((_Atomic(uint32_t) *)ptr, v); \
+ break; \
+ case ATOMICS_OP_ ## op_name | (3 << 3): \
+ a = func_name((_Atomic(uint64_t) *)ptr, v); \
+ break;
+#else
+#define OP(op_name, func_name) \
+ case ATOMICS_OP_ ## op_name | (0 << 3): \
+ a = func_name((_Atomic(uint8_t) *)ptr, v); \
+ break; \
+ case ATOMICS_OP_ ## op_name | (1 << 3): \
+ a = func_name((_Atomic(uint16_t) *)ptr, v); \
+ break; \
+ case ATOMICS_OP_ ## op_name | (2 << 3): \
+ a = func_name((_Atomic(uint32_t) *)ptr, v); \
+ break;
+#endif
+ OP(ADD, atomic_fetch_add)
+ OP(AND, atomic_fetch_and)
+ OP(OR, atomic_fetch_or)
+ OP(SUB, atomic_fetch_sub)
+ OP(XOR, atomic_fetch_xor)
+ OP(EXCHANGE, atomic_exchange)
+#undef OP
+
+ case ATOMICS_OP_LOAD | (0 << 3):
+ a = atomic_load((_Atomic(uint8_t) *)ptr);
+ break;
+ case ATOMICS_OP_LOAD | (1 << 3):
+ a = atomic_load((_Atomic(uint16_t) *)ptr);
+ break;
+ case ATOMICS_OP_LOAD | (2 << 3):
+ a = atomic_load((_Atomic(uint32_t) *)ptr);
+ break;
+#ifdef CONFIG_BIGNUM
+ case ATOMICS_OP_LOAD | (3 << 3):
+ a = atomic_load((_Atomic(uint64_t) *)ptr);
+ break;
+#endif
+
+ case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3):
+ {
+ uint8_t v1 = v;
+ atomic_compare_exchange_strong((_Atomic(uint8_t) *)ptr, &v1, rep_val);
+ a = v1;
+ }
+ break;
+ case ATOMICS_OP_COMPARE_EXCHANGE | (1 << 3):
+ {
+ uint16_t v1 = v;
+ atomic_compare_exchange_strong((_Atomic(uint16_t) *)ptr, &v1, rep_val);
+ a = v1;
+ }
+ break;
+ case ATOMICS_OP_COMPARE_EXCHANGE | (2 << 3):
+ {
+ uint32_t v1 = v;
+ atomic_compare_exchange_strong((_Atomic(uint32_t) *)ptr, &v1, rep_val);
+ a = v1;
+ }
+ break;
+#ifdef CONFIG_BIGNUM
+ case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3):
+ {
+ uint64_t v1 = v;
+ atomic_compare_exchange_strong((_Atomic(uint64_t) *)ptr, &v1, rep_val);
+ a = v1;
+ }
+ break;
+#endif
+ default:
+ abort();
+ }
+
+ switch(class_id) {
+ case JS_CLASS_INT8_ARRAY:
+ a = (int8_t)a;
+ goto done;
+ case JS_CLASS_UINT8_ARRAY:
+ a = (uint8_t)a;
+ goto done;
+ case JS_CLASS_INT16_ARRAY:
+ a = (int16_t)a;
+ goto done;
+ case JS_CLASS_UINT16_ARRAY:
+ a = (uint16_t)a;
+ goto done;
+ case JS_CLASS_INT32_ARRAY:
+ done:
+ ret = JS_NewInt32(ctx, a);
+ break;
+ case JS_CLASS_UINT32_ARRAY:
+ ret = JS_NewUint32(ctx, a);
+ break;
+#ifdef CONFIG_BIGNUM
+ case JS_CLASS_BIG_INT64_ARRAY:
+ ret = JS_NewBigInt64(ctx, a);
+ break;
+ case JS_CLASS_BIG_UINT64_ARRAY:
+ ret = JS_NewBigUint64(ctx, a);
+ break;
+#endif
+ default:
+ abort();
+ }
+ return ret;
+}
+
+static JSValue js_atomics_store(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ int size_log2;
+ void *ptr;
+ JSValue ret;
+ JSArrayBuffer *abuf;
+
+ ptr = js_atomics_get_ptr(ctx, &abuf, &size_log2, NULL,
+ argv[0], argv[1], 0);
+ if (!ptr)
+ return JS_EXCEPTION;
+#ifdef CONFIG_BIGNUM
+ if (size_log2 == 3) {
+ int64_t v64;
+ ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2]));
+ if (JS_IsException(ret))
+ return ret;
+ if (JS_ToBigInt64(ctx, &v64, ret)) {
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ atomic_store((_Atomic(uint64_t) *)ptr, v64);
+ } else
+#endif
+ {
+ uint32_t v;
+ /* XXX: spec, would be simpler to return the written value */
+ ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2]));
+ if (JS_IsException(ret))
+ return ret;
+ if (JS_ToUint32(ctx, &v, ret)) {
+ JS_FreeValue(ctx, ret);
+ return JS_EXCEPTION;
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+ switch(size_log2) {
+ case 0:
+ atomic_store((_Atomic(uint8_t) *)ptr, v);
+ break;
+ case 1:
+ atomic_store((_Atomic(uint16_t) *)ptr, v);
+ break;
+ case 2:
+ atomic_store((_Atomic(uint32_t) *)ptr, v);
+ break;
+ default:
+ abort();
+ }
+ }
+ return ret;
+}
+
+static JSValue js_atomics_isLockFree(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ int v, ret;
+ if (JS_ToInt32Sat(ctx, &v, argv[0]))
+ return JS_EXCEPTION;
+ ret = (v == 1 || v == 2 || v == 4
+#ifdef CONFIG_BIGNUM
+ || v == 8
+#endif
+ );
+ return JS_NewBool(ctx, ret);
+}
+
+typedef struct JSAtomicsWaiter {
+ struct list_head link;
+ BOOL linked;
+ pthread_cond_t cond;
+ int32_t *ptr;
+} JSAtomicsWaiter;
+
+static pthread_mutex_t js_atomics_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct list_head js_atomics_waiter_list =
+ LIST_HEAD_INIT(js_atomics_waiter_list);
+
+static JSValue js_atomics_wait(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ int64_t v;
+ int32_t v32;
+ void *ptr;
+ int64_t timeout;
+ struct timespec ts;
+ JSAtomicsWaiter waiter_s, *waiter;
+ int ret, size_log2, res;
+ double d;
+
+ ptr = js_atomics_get_ptr(ctx, NULL, &size_log2, NULL,
+ argv[0], argv[1], 2);
+ if (!ptr)
+ return JS_EXCEPTION;
+#ifdef CONFIG_BIGNUM
+ if (size_log2 == 3) {
+ if (JS_ToBigInt64(ctx, &v, argv[2]))
+ return JS_EXCEPTION;
+ } else
+#endif
+ {
+ if (JS_ToInt32(ctx, &v32, argv[2]))
+ return JS_EXCEPTION;
+ v = v32;
+ }
+ if (JS_ToFloat64(ctx, &d, argv[3]))
+ return JS_EXCEPTION;
+ if (isnan(d) || d > INT64_MAX)
+ timeout = INT64_MAX;
+ else if (d < 0)
+ timeout = 0;
+ else
+ timeout = (int64_t)d;
+ if (!ctx->rt->can_block)
+ return JS_ThrowTypeError(ctx, "cannot block in this thread");
+
+ /* XXX: inefficient if large number of waiters, should hash on
+ 'ptr' value */
+ /* XXX: use Linux futexes when available ? */
+ pthread_mutex_lock(&js_atomics_mutex);
+ if (size_log2 == 3) {
+ res = *(int64_t *)ptr != v;
+ } else {
+ res = *(int32_t *)ptr != v;
+ }
+ if (res) {
+ pthread_mutex_unlock(&js_atomics_mutex);
+ return JS_AtomToString(ctx, JS_ATOM_not_equal);
+ }
+
+ waiter = &waiter_s;
+ waiter->ptr = ptr;
+ pthread_cond_init(&waiter->cond, NULL);
+ waiter->linked = TRUE;
+ list_add_tail(&waiter->link, &js_atomics_waiter_list);
+
+ if (timeout == INT64_MAX) {
+ pthread_cond_wait(&waiter->cond, &js_atomics_mutex);
+ ret = 0;
+ } else {
+ /* XXX: use clock monotonic */
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_sec += timeout / 1000;
+ ts.tv_nsec += (timeout % 1000) * 1000000;
+ if (ts.tv_nsec >= 1000000000) {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec++;
+ }
+ ret = pthread_cond_timedwait(&waiter->cond, &js_atomics_mutex,
+ &ts);
+ }
+ if (waiter->linked)
+ list_del(&waiter->link);
+ pthread_mutex_unlock(&js_atomics_mutex);
+ pthread_cond_destroy(&waiter->cond);
+ if (ret == ETIMEDOUT) {
+ return JS_AtomToString(ctx, JS_ATOM_timed_out);
+ } else {
+ return JS_AtomToString(ctx, JS_ATOM_ok);
+ }
+}
+
+static JSValue js_atomics_notify(JSContext *ctx,
+ JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+{
+ struct list_head *el, *el1, waiter_list;
+ int32_t count, n;
+ void *ptr;
+ JSAtomicsWaiter *waiter;
+ JSArrayBuffer *abuf;
+
+ ptr = js_atomics_get_ptr(ctx, &abuf, NULL, NULL, argv[0], argv[1], 1);
+ if (!ptr)
+ return JS_EXCEPTION;
+
+ if (JS_IsUndefined(argv[2])) {
+ count = INT32_MAX;
+ } else {
+ if (JS_ToInt32Clamp(ctx, &count, argv[2], 0, INT32_MAX, 0))
+ return JS_EXCEPTION;
+ }
+ if (abuf->detached)
+ return JS_ThrowTypeErrorDetachedArrayBuffer(ctx);
+
+ n = 0;
+ if (abuf->shared && count > 0) {
+ pthread_mutex_lock(&js_atomics_mutex);
+ init_list_head(&waiter_list);
+ list_for_each_safe(el, el1, &js_atomics_waiter_list) {
+ waiter = list_entry(el, JSAtomicsWaiter, link);
+ if (waiter->ptr == ptr) {
+ list_del(&waiter->link);
+ waiter->linked = FALSE;
+ list_add_tail(&waiter->link, &waiter_list);
+ n++;
+ if (n >= count)
+ break;
+ }
+ }
+ list_for_each(el, &waiter_list) {
+ waiter = list_entry(el, JSAtomicsWaiter, link);
+ pthread_cond_signal(&waiter->cond);
+ }
+ pthread_mutex_unlock(&js_atomics_mutex);
+ }
+ return JS_NewInt32(ctx, n);
+}
+
+static const JSCFunctionListEntry js_atomics_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("add", 3, js_atomics_op, ATOMICS_OP_ADD ),
+ JS_CFUNC_MAGIC_DEF("and", 3, js_atomics_op, ATOMICS_OP_AND ),
+ JS_CFUNC_MAGIC_DEF("or", 3, js_atomics_op, ATOMICS_OP_OR ),
+ JS_CFUNC_MAGIC_DEF("sub", 3, js_atomics_op, ATOMICS_OP_SUB ),
+ JS_CFUNC_MAGIC_DEF("xor", 3, js_atomics_op, ATOMICS_OP_XOR ),
+ JS_CFUNC_MAGIC_DEF("exchange", 3, js_atomics_op, ATOMICS_OP_EXCHANGE ),
+ JS_CFUNC_MAGIC_DEF("compareExchange", 4, js_atomics_op, ATOMICS_OP_COMPARE_EXCHANGE ),
+ JS_CFUNC_MAGIC_DEF("load", 2, js_atomics_op, ATOMICS_OP_LOAD ),
+ JS_CFUNC_DEF("store", 3, js_atomics_store ),
+ JS_CFUNC_DEF("isLockFree", 1, js_atomics_isLockFree ),
+ JS_CFUNC_DEF("wait", 4, js_atomics_wait ),
+ JS_CFUNC_DEF("notify", 3, js_atomics_notify ),
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Atomics", JS_PROP_CONFIGURABLE ),
+};
+
+static const JSCFunctionListEntry js_atomics_obj[] = {
+ JS_OBJECT_DEF("Atomics", js_atomics_funcs, countof(js_atomics_funcs), JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE ),
+};
+
+void JS_AddIntrinsicAtomics(JSContext *ctx)
+{
+ /* add Atomics as autoinit object */
+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_atomics_obj, countof(js_atomics_obj));
+}
+
+#endif /* CONFIG_ATOMICS */
+
+void JS_AddIntrinsicTypedArrays(JSContext *ctx)
+{
+ JSValue typed_array_base_proto, typed_array_base_func;
+ JSValueConst array_buffer_func, shared_array_buffer_func;
+ int i;
+
+ ctx->class_proto[JS_CLASS_ARRAY_BUFFER] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_ARRAY_BUFFER],
+ js_array_buffer_proto_funcs,
+ countof(js_array_buffer_proto_funcs));
+
+ array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "ArrayBuffer",
+ js_array_buffer_constructor, 1,
+ ctx->class_proto[JS_CLASS_ARRAY_BUFFER]);
+ JS_SetPropertyFunctionList(ctx, array_buffer_func,
+ js_array_buffer_funcs,
+ countof(js_array_buffer_funcs));
+
+ ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER],
+ js_shared_array_buffer_proto_funcs,
+ countof(js_shared_array_buffer_proto_funcs));
+
+ shared_array_buffer_func = JS_NewGlobalCConstructorOnly(ctx, "SharedArrayBuffer",
+ js_shared_array_buffer_constructor, 1,
+ ctx->class_proto[JS_CLASS_SHARED_ARRAY_BUFFER]);
+ JS_SetPropertyFunctionList(ctx, shared_array_buffer_func,
+ js_shared_array_buffer_funcs,
+ countof(js_shared_array_buffer_funcs));
+
+ typed_array_base_proto = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, typed_array_base_proto,
+ js_typed_array_base_proto_funcs,
+ countof(js_typed_array_base_proto_funcs));
+
+ /* TypedArray.prototype.toString must be the same object as Array.prototype.toString */
+ JSValue obj = JS_GetProperty(ctx, ctx->class_proto[JS_CLASS_ARRAY], JS_ATOM_toString);
+ /* XXX: should use alias method in JSCFunctionListEntry */ //@@@
+ JS_DefinePropertyValue(ctx, typed_array_base_proto, JS_ATOM_toString, obj,
+ JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE);
+
+ typed_array_base_func = JS_NewCFunction(ctx, js_typed_array_base_constructor,
+ "TypedArray", 0);
+ JS_SetPropertyFunctionList(ctx, typed_array_base_func,
+ js_typed_array_base_funcs,
+ countof(js_typed_array_base_funcs));
+ JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto);
+
+ for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) {
+ JSValue func_obj;
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ const char *name;
+
+ ctx->class_proto[i] = JS_NewObjectProto(ctx, typed_array_base_proto);
+ JS_DefinePropertyValueStr(ctx, ctx->class_proto[i],
+ "BYTES_PER_ELEMENT",
+ JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
+ 0);
+ name = JS_AtomGetStr(ctx, buf, sizeof(buf),
+ JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY);
+ func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_typed_array_constructor,
+ name, 3, JS_CFUNC_constructor_magic, i,
+ typed_array_base_func);
+ JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]);
+ JS_DefinePropertyValueStr(ctx, func_obj,
+ "BYTES_PER_ELEMENT",
+ JS_NewInt32(ctx, 1 << typed_array_size_log2(i)),
+ 0);
+ }
+ JS_FreeValue(ctx, typed_array_base_proto);
+ JS_FreeValue(ctx, typed_array_base_func);
+
+ /* DataView */
+ ctx->class_proto[JS_CLASS_DATAVIEW] = JS_NewObject(ctx);
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_DATAVIEW],
+ js_dataview_proto_funcs,
+ countof(js_dataview_proto_funcs));
+ JS_NewGlobalCConstructorOnly(ctx, "DataView",
+ js_dataview_constructor, 1,
+ ctx->class_proto[JS_CLASS_DATAVIEW]);
+ /* Atomics */
+#ifdef CONFIG_ATOMICS
+ JS_AddIntrinsicAtomics(ctx);
+#endif
+}
+
+#ifndef NDEBUG
+static void *watchedRefCount = NULL;
+
+void notifyRefCountIncrease(JSRefCountHeader *p)
+{
+ if (p == watchedRefCount)
+ fprintf(stderr, "increasing ref count %d for %p\n", p->ref_count, watchedRefCount);
+}
+
+void notifyRefCountDecrease(JSRefCountHeader *p)
+{
+ if (p == watchedRefCount)
+ fprintf(stderr, "decreasing ref count %d for %p\n", p->ref_count, watchedRefCount);
+}
+
+void watchRefCount(void *p)
+{
+ watchedRefCount = p;
+}
+#endif
+
+void setScopeLookup(JSContext *ctx, ScopeLookup *scopeLookup)
+{
+ ctx->scopeLookup = scopeLookup;
+}
+
+void setFoundUndefinedHandler(JSContext *ctx, FoundUndefinedHandler *handler)
+{
+ ctx->handleUndefined = handler;
+}
+
+void setFunctionEnteredHandler(JSContext *ctx, FunctionEnteredHandler *handler)
+{
+ ctx->handleFunctionEntered = handler;
+}
+
+void setFunctionExitedHandler(JSContext *ctx, FunctionExitedHandler *handler)
+{
+ ctx->handleFunctionExited = handler;
+}
+
+int isSimpleValue(JSValue v)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT)
+ return 1;
+ p = JS_VALUE_GET_OBJ(v);
+ return p->class_id >= JS_CLASS_OBJECT && p->class_id <= JS_CLASS_BOOLEAN;
+}
+
+JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func,
+ const char *name, int length,
+ JSCFunctionEnum cproto, int magic)
+{
+ return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto,
+ magic);
+}
+
+#ifdef JS_NAN_BOXING
+JSValue mkVal(int32_t tag, int32_t val)
+{
+ return ((uint64_t)(tag) << 32) | (uint32_t)(val);
+}
+
+JSValue mkPtr(int32_t tag, void *p)
+{
+ return ((uint64_t)(tag) << 32) | (uintptr_t)(p);
+}
+#else
+JSValue mkVal(int32_t tag, int32_t val)
+{
+ return (JSValue){ (JSValueUnion){ .int32 = val }, tag };
+}
+
+JSValue mkPtr(int32_t tag, void *p)
+{
+ return (JSValue){ (JSValueUnion){ .ptr = p }, tag };
+}
+#endif
+
+void JS_FreeValue(JSContext *ctx, JSValue v) {
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+#ifndef NDEBUG
+ notifyRefCountDecrease(p);
+#endif
+ if (--p->ref_count <= 0) {
+ JS_FreeValueImpl(ctx, v);
+ }
+ }
+}
+void JS_FreeValueRT(JSRuntime *rt, JSValue v) {
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+#ifndef NDEBUG
+ notifyRefCountDecrease(p);
+#endif
+ if (--p->ref_count <= 0) {
+ JS_FreeValueRTImpl(rt, v);
+ }
+ }
+}
+JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) {
+ (void)ctx;
+ return JS_MKVAL(JS_TAG_BOOL, (val != 0));
+}
+JSValue JS_NewInt32(JSContext *ctx, int32_t val) {
+ (void)ctx;
+ return JS_MKVAL(JS_TAG_INT, val);
+}
+JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) {
+ (void)ctx;
+ return JS_MKVAL(JS_TAG_CATCH_OFFSET, val);
+}
+JSValue JS_NewInt64(JSContext *ctx, int64_t val) {
+ JSValue v;
+ if (val == (int32_t)val) {
+ v = JS_NewInt32(ctx, (int32_t)val);
+ } else {
+ v = JS_NewFloat64Impl(ctx, (double)val);
+ }
+ return v;
+}
+JSValue JS_NewUint32(JSContext *ctx, uint32_t val) {
+ JSValue v;
+ if (val <= 0x7fffffff) {
+ v = JS_NewInt32(ctx, val);
+ } else {
+ v = JS_NewFloat64Impl(ctx, val);
+ }
+ return v;
+}
+JSValue JS_NewFloat64(JSContext *ctx, double d) {
+ JSValue v;
+ int32_t val;
+ union {
+ double d;
+ uint64_t u;
+ } u, t;
+ u.d = d;
+ val = (int32_t)d;
+ t.d = val;
+ /* -0 cannot be represented as integer, so we compare the bit
+ representation */
+ if (u.u == t.u) {
+ v = JS_MKVAL(JS_TAG_INT, val);
+ } else {
+ v = JS_NewFloat64Impl(ctx, d);
+ }
+ return v;
+}
+JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
+ int length) {
+ return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0);
+}
+
+JS_BOOL JS_IsRegExp(JSContext *ctx, JSValue val)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
+ return FALSE;
+ return JS_VALUE_GET_OBJ(val)->class_id == JS_CLASS_REGEXP;
+}
+
+int JS_IsDate(JSValue v)
+{
+ JSObject *p;
+ if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT)
+ return FALSE;
+ return JS_VALUE_GET_OBJ(v)->class_id == JS_CLASS_DATE;
+}
+
+JSValue JS_NewDate(JSContext *ctx, const char *s)
+{
+ JSValue dateString = JS_NewString(ctx, s);
+ JSAtom constrAtom = JS_NewAtom(ctx, "Date");
+ JSValue constr = JS_GetGlobalVar(ctx, constrAtom, FALSE);
+ JSValue date = js_date_constructor(ctx, constr, 1, &dateString);
+ JS_FreeValue(ctx, constr);
+ JS_FreeValue(ctx, dateString);
+ JS_FreeAtom(ctx, constrAtom);
+ return date;
+}
diff --git a/src/shared/quickjs/quickjs.diff b/src/shared/quickjs/quickjs.diff
new file mode 100644
index 000000000..e87999050
--- /dev/null
+++ b/src/shared/quickjs/quickjs.diff
@@ -0,0 +1,4061 @@
+diff --git a/cutils.c b/cutils.c
+index a02fb76..1f66fff 100644
+--- a/cutils.c
++++ b/cutils.c
+@@ -166,8 +166,7 @@ int dbuf_putstr(DynBuf *s, const char *str)
+ return dbuf_put(s, (const uint8_t *)str, strlen(str));
+ }
+
+-int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
+- const char *fmt, ...)
++int FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, const char *fmt, ...)
+ {
+ va_list ap;
+ char buf[128];
+diff --git a/cutils.h b/cutils.h
+index 31f7cd8..ee0ce4a 100644
+--- a/cutils.h
++++ b/cutils.h
+@@ -28,14 +28,33 @@
+ #include <stdlib.h>
+ #include <inttypes.h>
+
++#if defined(_MSC_VER)
++#include <BaseTsd.h>
++typedef SSIZE_T ssize_t;
++#else
++#include <sys/types.h>
++#endif
++
+ /* set if CPU is big endian */
+ #undef WORDS_BIGENDIAN
+
++#ifdef __GNUC__
+ #define likely(x) __builtin_expect(!!(x), 1)
+ #define unlikely(x) __builtin_expect(!!(x), 0)
+ #define force_inline inline __attribute__((always_inline))
+ #define no_inline __attribute__((noinline))
+-#define __maybe_unused __attribute__((unused))
++#define maybe_unused __attribute__((unused))
++#else
++#define likely(x) (x)
++#define unlikely(x) (x)
++#define force_inline
++#define no_inline
++#define maybe_unused
++#endif
++
++#ifdef _MSC_VER
++#define alloca _alloca
++#endif
+
+ #define xglue(x, y) x ## y
+ #define glue(x, y) xglue(x, y)
+@@ -114,38 +133,24 @@ static inline int64_t min_int64(int64_t a, int64_t b)
+ /* WARNING: undefined if a = 0 */
+ static inline int clz32(unsigned int a)
+ {
++#ifdef _MSC_VER
++ return (int) __lzcnt(a);
++#else
+ return __builtin_clz(a);
++#endif
+ }
+
+-/* WARNING: undefined if a = 0 */
+-static inline int clz64(uint64_t a)
+-{
+- return __builtin_clzll(a);
+-}
+-
+-/* WARNING: undefined if a = 0 */
+-static inline int ctz32(unsigned int a)
+-{
+- return __builtin_ctz(a);
+-}
+-
+-/* WARNING: undefined if a = 0 */
+-static inline int ctz64(uint64_t a)
+-{
+- return __builtin_ctzll(a);
+-}
+-
+-struct __attribute__((packed)) packed_u64 {
++#pragma pack(push, 1)
++struct packed_u64 {
+ uint64_t v;
+ };
+-
+-struct __attribute__((packed)) packed_u32 {
++struct packed_u32 {
+ uint32_t v;
+ };
+-
+-struct __attribute__((packed)) packed_u16 {
++struct packed_u16 {
+ uint16_t v;
+ };
++#pragma pack(pop)
+
+ static inline uint64_t get_u64(const uint8_t *tab)
+ {
+@@ -262,8 +267,15 @@ static inline int dbuf_put_u64(DynBuf *s, uint64_t val)
+ {
+ return dbuf_put(s, (uint8_t *)&val, 8);
+ }
+-int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s,
+- const char *fmt, ...);
++
++#ifdef __GNUC__
++#define FORMAT_ATTR(x, y) __attribute__((format(printf, x, y)))
++#else
++#define FORMAT_ATTR(x, y)
++#endif
++
++int FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, const char *fmt, ...);
++
+ void dbuf_free(DynBuf *s);
+ static inline BOOL dbuf_error(DynBuf *s) {
+ return s->error;
+diff --git a/libregexp.c b/libregexp.c
+index 379bfc7..ad91f78 100644
+--- a/libregexp.c
++++ b/libregexp.c
+@@ -271,7 +271,7 @@ static int cr_canonicalize(CharRange *cr)
+ }
+
+ #ifdef DUMP_REOP
+-static __maybe_unused void lre_dump_bytecode(const uint8_t *buf,
++static MAYBE_UNUSED void lre_dump_bytecode(const uint8_t *buf,
+ int buf_len)
+ {
+ int pos, len, opcode, bc_len, re_flags, i;
+@@ -427,7 +427,7 @@ static void re_emit_op_u16(REParseState *s, int op, uint32_t val)
+ dbuf_put_u16(&s->byte_code, val);
+ }
+
+-static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, const char *fmt, ...)
++static int FORMAT_ATTR(2, 3) re_parse_error(REParseState *s, const char *fmt, ...)
+ {
+ va_list ap;
+ va_start(ap, fmt);
+@@ -1472,7 +1472,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir)
+ default:
+ parse_class_atom:
+ c = get_class_atom(s, cr, &p, FALSE);
+- if ((int)c < 0)
++ if (c < 0)
+ return -1;
+ normal_char:
+ last_atom_start = s->byte_code.size;
+@@ -1924,17 +1924,17 @@ static BOOL is_word_char(uint32_t c)
+ #define GET_CHAR(c, cptr, cbuf_end) \
+ do { \
+ if (cbuf_type == 0) { \
+- c = *cptr++; \
++ (c) = *(cptr)++; \
+ } else { \
+ uint32_t __c1; \
+- c = *(uint16_t *)cptr; \
+- cptr += 2; \
+- if (c >= 0xd800 && c < 0xdc00 && \
+- cbuf_type == 2 && cptr < cbuf_end) { \
+- __c1 = *(uint16_t *)cptr; \
++ (c) = *(uint16_t *)(cptr); \
++ (cptr) += 2; \
++ if ((c) >= 0xd800 && (c) < 0xdc00 && \
++ cbuf_type == 2 && (cptr) < (cbuf_end)) { \
++ __c1 = *(uint16_t *)(cptr); \
+ if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
+- c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
+- cptr += 2; \
++ (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
++ (cptr) += 2; \
+ } \
+ } \
+ } \
+@@ -1943,15 +1943,15 @@ static BOOL is_word_char(uint32_t c)
+ #define PEEK_CHAR(c, cptr, cbuf_end) \
+ do { \
+ if (cbuf_type == 0) { \
+- c = cptr[0]; \
++ (c) = (cptr)[0]; \
+ } else { \
+ uint32_t __c1; \
+- c = ((uint16_t *)cptr)[0]; \
+- if (c >= 0xd800 && c < 0xdc00 && \
+- cbuf_type == 2 && (cptr + 2) < cbuf_end) { \
+- __c1 = ((uint16_t *)cptr)[1]; \
++ (c) = ((uint16_t *)(cptr))[0]; \
++ if ((c) >= 0xd800 && (c) < 0xdc00 && \
++ cbuf_type == 2 && ((cptr) + 2) < (cbuf_end)) { \
++ __c1 = ((uint16_t *)(cptr))[1]; \
+ if (__c1 >= 0xdc00 && __c1 < 0xe000) { \
+- c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
++ (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+@@ -1960,15 +1960,15 @@ static BOOL is_word_char(uint32_t c)
+ #define PEEK_PREV_CHAR(c, cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+- c = cptr[-1]; \
++ (c) = (cptr)[-1]; \
+ } else { \
+ uint32_t __c1; \
+- c = ((uint16_t *)cptr)[-1]; \
+- if (c >= 0xdc00 && c < 0xe000 && \
+- cbuf_type == 2 && (cptr - 4) >= cbuf_start) { \
+- __c1 = ((uint16_t *)cptr)[-2]; \
++ (c) = ((uint16_t *)(cptr))[-1]; \
++ if ((c) >= 0xdc00 && (c) < 0xe000 && \
++ cbuf_type == 2 && ((cptr) - 4) >= (cbuf_start)) { \
++ __c1 = ((uint16_t *)(cptr))[-2]; \
+ if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
+- c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \
++ (c) = (((__c1 & 0x3ff) << 10) | ((c) & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+@@ -1977,18 +1977,18 @@ static BOOL is_word_char(uint32_t c)
+ #define GET_PREV_CHAR(c, cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+- cptr--; \
+- c = cptr[0]; \
++ (cptr)--; \
++ (c) = (cptr)[0]; \
+ } else { \
+ uint32_t __c1; \
+- cptr -= 2; \
+- c = ((uint16_t *)cptr)[0]; \
+- if (c >= 0xdc00 && c < 0xe000 && \
+- cbuf_type == 2 && cptr > cbuf_start) { \
+- __c1 = ((uint16_t *)cptr)[-1]; \
++ (cptr) -= 2; \
++ (c) = ((uint16_t *)(cptr))[0]; \
++ if ((c) >= 0xdc00 && (c) < 0xe000 && \
++ cbuf_type == 2 && (cptr) > (cbuf_start)) { \
++ __c1 = ((uint16_t *)(cptr))[-1]; \
+ if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \
+- cptr -= 2; \
+- c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \
++ (cptr) -= 2; \
++ (c) = (((__c1 & 0x3ff) << 10) | ((c) & 0x3ff)) + 0x10000; \
+ } \
+ } \
+ } \
+@@ -1997,15 +1997,15 @@ static BOOL is_word_char(uint32_t c)
+ #define PREV_CHAR(cptr, cbuf_start) \
+ do { \
+ if (cbuf_type == 0) { \
+- cptr--; \
++ (cptr)--; \
+ } else { \
+- cptr -= 2; \
++ (cptr) -= 2; \
+ if (cbuf_type == 2) { \
+- c = ((uint16_t *)cptr)[0]; \
+- if (c >= 0xdc00 && c < 0xe000 && cptr > cbuf_start) { \
+- c = ((uint16_t *)cptr)[-1]; \
++ c = ((uint16_t *)(cptr))[0]; \
++ if (c >= 0xdc00 && c < 0xe000 && (cptr) > (cbuf_start)) { \
++ c = ((uint16_t *)(cptr))[-1]; \
+ if (c >= 0xd800 && c < 0xdc00) \
+- cptr -= 2; \
++ (cptr) -= 2; \
+ } \
+ } \
+ } \
+@@ -2049,7 +2049,7 @@ typedef struct {
+
+ static int push_state(REExecContext *s,
+ uint8_t **capture,
+- StackInt *stack, size_t stack_len,
++ const StackInt *stack, size_t stack_len,
+ const uint8_t *pc, const uint8_t *cptr,
+ REExecStateEnum type, size_t count)
+ {
+diff --git a/libunicode.c b/libunicode.c
+index 63c12a0..112da72 100644
+--- a/libunicode.c
++++ b/libunicode.c
+@@ -271,7 +271,7 @@ BOOL lre_is_case_ignorable(uint32_t c)
+
+ /* character range */
+
+-static __maybe_unused void cr_dump(CharRange *cr)
++static maybe_unused void cr_dump(CharRange *cr)
+ {
+ int i;
+ for(i = 0; i < cr->len; i++)
+@@ -1315,11 +1315,13 @@ static int unicode_prop_ops(CharRange *cr, ...)
+ }
+ }
+ done:
++ va_end(ap);
+ assert(stack_len == 1);
+ ret = cr_copy(cr, &stack[0]);
+ cr_free(&stack[0]);
+ return ret;
+ fail:
++ va_end(ap);
+ for(i = 0; i < stack_len; i++)
+ cr_free(&stack[i]);
+ return -1;
+diff --git a/list.h b/list.h
+index 0a1bc5a..e7f51a9 100644
+--- a/list.h
++++ b/list.h
+@@ -46,7 +46,7 @@ static inline void init_list_head(struct list_head *head)
+ }
+
+ /* insert 'el' between 'prev' and 'next' */
+-static inline void __list_add(struct list_head *el,
++static inline void list_add_impl(struct list_head *el,
+ struct list_head *prev, struct list_head *next)
+ {
+ prev->next = el;
+@@ -58,13 +58,13 @@ static inline void __list_add(struct list_head *el,
+ /* add 'el' at the head of the list 'head' (= after element head) */
+ static inline void list_add(struct list_head *el, struct list_head *head)
+ {
+- __list_add(el, head, head->next);
++ list_add_impl(el, head, head->next);
+ }
+
+ /* add 'el' at the end of the list 'head' (= before element head) */
+ static inline void list_add_tail(struct list_head *el, struct list_head *head)
+ {
+- __list_add(el, head->prev, head);
++ list_add_impl(el, head->prev, head);
+ }
+
+ static inline void list_del(struct list_head *el)
+diff --git a/quickjs.c b/quickjs.c
+index 7916013..f90fb9e 100644
+--- a/quickjs.c
++++ b/quickjs.c
+@@ -28,7 +28,9 @@
+ #include <inttypes.h>
+ #include <string.h>
+ #include <assert.h>
++#ifndef _MSC_VER
+ #include <sys/time.h>
++#endif
+ #include <time.h>
+ #include <fenv.h>
+ #include <math.h>
+@@ -40,6 +42,18 @@
+ #include <malloc_np.h>
+ #endif
+
++#ifdef _MSC_VER
++#include <intrin.h>
++#include <windows.h>
++#endif
++
++#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
++ || defined(__APPLE__)
++#include <xlocale.h>
++#else
++#include <locale.h>
++#endif
++
+ #include "cutils.h"
+ #include "list.h"
+ #include "quickjs.h"
+@@ -48,9 +62,9 @@
+ #include "libbf.h"
+ #endif
+
+-#define OPTIMIZE 1
++#define OPTIMIZE 0
+ #define SHORT_OPCODES 1
+-#if defined(EMSCRIPTEN)
++#if defined(EMSCRIPTEN) || defined(_MSC_VER)
+ #define DIRECT_DISPATCH 0
+ #else
+ #define DIRECT_DISPATCH 1
+@@ -69,7 +83,7 @@
+
+ /* define to include Atomics.* operations which depend on the OS
+ threads */
+-#if !defined(EMSCRIPTEN)
++#if !defined(EMSCRIPTEN) && !defined(_MSC_VER)
+ #define CONFIG_ATOMICS
+ #endif
+
+@@ -78,7 +92,6 @@
+ #define CONFIG_STACK_CHECK
+ #endif
+
+-
+ /* dump object free */
+ //#define DUMP_FREE
+ //#define DUMP_CLOSURE
+@@ -115,6 +128,25 @@
+ #include <errno.h>
+ #endif
+
++static double safe_strtod(const char *restrict nptr, char **restrict endptr)
++{
++#if defined(_MSC_VER) || defined(__MINGW32__)
++ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
++ setlocale(LC_NUMERIC, "C");
++#else
++ const locale_t tempLoc = newlocale(LC_NUMERIC_MASK, "C", 0);
++ uselocale(tempLoc);
++#endif
++ double d = strtod(nptr, endptr);
++#if defined(_MSC_VER) || defined(__MINGW32__)
++ _configthreadlocale(_DISABLE_PER_THREAD_LOCALE);
++#else
++ uselocale(LC_GLOBAL_LOCALE);
++ freelocale(tempLoc);
++#endif
++ return d;
++}
++
+ enum {
+ /* classid tag */ /* union usage | properties */
+ JS_CLASS_OBJECT = 1, /* must be first */
+@@ -204,7 +236,11 @@ typedef enum JSErrorEnum {
+ #define JS_STACK_SIZE_MAX 65534
+ #define JS_STRING_LEN_MAX ((1 << 30) - 1)
+
+-#define __exception __attribute__((warn_unused_result))
++#ifdef __GNUC__
++#define warn_unused __attribute__((warn_unused_result))
++#else
++#define warn_unused
++#endif
+
+ typedef struct JSShape JSShape;
+ typedef struct JSString JSString;
+@@ -362,8 +398,8 @@ typedef struct JSVarRef {
+ union {
+ JSGCObjectHeader header; /* must come first */
+ struct {
+- int __gc_ref_count; /* corresponds to header.ref_count */
+- uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
++ int _gc_ref_count; /* corresponds to header.ref_count */
++ uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */
+
+ /* 0 : the JSVarRef is on the stack. header.link is an element
+ of JSStackFrame.var_ref_list.
+@@ -455,8 +491,12 @@ struct JSContext {
+ /* if NULL, eval is not supported */
+ JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int flags, int scope_idx);
++ const char *filename, int line, int flags, int scope_idx);
+ void *user_opaque;
++ ScopeLookup *scopeLookup;
++ FoundUndefinedHandler *handleUndefined;
++ FunctionEnteredHandler *handleFunctionEntered;
++ FunctionExitedHandler *handleFunctionExited;
+ };
+
+ typedef union JSFloat64Union {
+@@ -867,8 +907,8 @@ struct JSObject {
+ union {
+ JSGCObjectHeader header;
+ struct {
+- int __gc_ref_count; /* corresponds to header.ref_count */
+- uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */
++ int _gc_ref_count; /* corresponds to header.ref_count */
++ uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */
+
+ uint8_t extensible : 1;
+ uint8_t free_mark : 1; /* only used when freeing objects with cycles */
+@@ -950,7 +990,7 @@ struct JSObject {
+ /* byte sizes: 40/48/72 */
+ };
+ enum {
+- __JS_ATOM_NULL = JS_ATOM_NULL,
++ JS_ATOM_NULL_ = JS_ATOM_NULL,
+ #define DEF(name, str) JS_ATOM_ ## name,
+ #include "quickjs-atom.h"
+ #undef DEF
+@@ -996,7 +1036,7 @@ enum OPCodeEnum {
+ };
+
+ static int JS_InitAtoms(JSRuntime *rt);
+-static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
++static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len,
+ int atom_type);
+ static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p);
+ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);
+@@ -1017,24 +1057,23 @@ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_o
+ int argc, JSValueConst *argv);
+ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
+ int argc, JSValueConst *argv);
+-static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
++static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
+ JSValue val, BOOL is_array_ctor);
+ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
+ JSValueConst val, int flags, int scope_idx);
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
+-static __maybe_unused void JS_DumpAtoms(JSRuntime *rt);
+-static __maybe_unused void JS_DumpString(JSRuntime *rt,
++static maybe_unused void JS_DumpAtoms(JSRuntime *rt);
++static maybe_unused void JS_DumpString(JSRuntime *rt,
+ const JSString *p);
+-static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
+-static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
+-static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
+-static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
++static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt);
++static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p);
++static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p);
++static maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ JSValueConst val);
+-static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
+-static __maybe_unused void JS_PrintValue(JSContext *ctx,
++static maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val);
++static maybe_unused void JS_PrintValue(JSContext *ctx,
+ const char *str,
+ JSValueConst val);
+-static __maybe_unused void JS_DumpShapes(JSRuntime *rt);
++static maybe_unused void JS_DumpShapes(JSRuntime *rt);
+ static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int magic);
+ static void js_array_finalizer(JSRuntime *rt, JSValue val);
+@@ -1148,7 +1187,6 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val,
+ BOOL allow_null_or_undefined);
+ static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val);
+ #endif
+-JSValue JS_ThrowOutOfMemory(JSContext *ctx);
+ static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx);
+ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj);
+ static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj,
+@@ -1187,7 +1225,7 @@ static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+ static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int flags, int scope_idx);
++ const char *filename, int line, int flags, int scope_idx);
+ static void js_free_module_def(JSContext *ctx, JSModuleDef *m);
+ static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m,
+ JS_MarkFunc *mark_func);
+@@ -1197,7 +1235,7 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref);
+ static JSValue js_new_promise_capability(JSContext *ctx,
+ JSValue *resolving_funcs,
+ JSValueConst ctor);
+-static __exception int perform_promise_then(JSContext *ctx,
++static warn_unused int perform_promise_then(JSContext *ctx,
+ JSValueConst promise,
+ JSValueConst *resolve_reject,
+ JSValueConst *cap_resolving_funcs);
+@@ -1222,9 +1260,9 @@ static void js_free_shape_null(JSRuntime *rt, JSShape *sh);
+ static int js_shape_prepare_update(JSContext *ctx, JSObject *p,
+ JSShapeProperty **pprs);
+ static int init_shape_hash(JSRuntime *rt);
+-static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
++static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres,
+ JSValueConst obj);
+-static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
++static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres,
+ JSValueConst obj);
+ static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len);
+ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen,
+@@ -1582,10 +1620,27 @@ static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
+ return FALSE;
+ }
+ #else
+-/* Note: OS and CPU dependent */
++// Uses code from LLVM project.
+ static inline uintptr_t js_get_stack_pointer(void)
+ {
++#ifdef _MSC_VER
++ return (uintptr_t) _AddressOfReturnAddress();
++#elif defined __has_builtin
++#if __has_builtin(__builtin_frame_address)
+ return (uintptr_t) __builtin_frame_address(0);
++#endif
++#elif defined __GNUC__
++ return (uintptr_t) __builtin_frame_address(0);
++#else
++ char CharOnStack = 0;
++ // The volatile store here is intended to escape the local variable, to
++ // prevent the compiler from optimizing CharOnStack into anything other
++ // than a char on the stack.
++ //
++ // Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19.
++ char *volatile Ptr = &CharOnStack;
++ return (uintptr_t) Ptr;
++#endif
+ }
+
+ static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size)
+@@ -1980,6 +2035,7 @@ void JS_FreeRuntime(JSRuntime *rt)
+ printf("Secondary object leaks: %d\n", count);
+ }
+ #endif
++ fflush(stdout);
+ assert(list_empty(&rt->gc_obj_list));
+
+ /* free the classes */
+@@ -2386,7 +2442,7 @@ static inline BOOL is_math_mode(JSContext *ctx)
+ /* return the max count from the hash size */
+ #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2)
+
+-static inline BOOL __JS_AtomIsConst(JSAtom v)
++static inline BOOL JS_AtomIsConst(JSAtom v)
+ {
+ #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1
+ return (int32_t)v <= 0;
+@@ -2395,17 +2451,17 @@ static inline BOOL __JS_AtomIsConst(JSAtom v)
+ #endif
+ }
+
+-static inline BOOL __JS_AtomIsTaggedInt(JSAtom v)
++static inline BOOL JS_AtomIsTaggedInt(JSAtom v)
+ {
+ return (v & JS_ATOM_TAG_INT) != 0;
+ }
+
+-static inline JSAtom __JS_AtomFromUInt32(uint32_t v)
++static inline JSAtom JS_AtomFromUInt32(uint32_t v)
+ {
+ return v | JS_ATOM_TAG_INT;
+ }
+
+-static inline uint32_t __JS_AtomToUInt32(JSAtom atom)
++static inline uint32_t JS_AtomToUInt32(JSAtom atom)
+ {
+ return atom & ~JS_ATOM_TAG_INT;
+ }
+@@ -2485,7 +2541,7 @@ static uint32_t hash_string(const JSString *str, uint32_t h)
+ return h;
+ }
+
+-static __maybe_unused void JS_DumpString(JSRuntime *rt,
++static maybe_unused void JS_DumpString(JSRuntime *rt,
+ const JSString *p)
+ {
+ int i, c, sep;
+@@ -2517,7 +2573,7 @@ static __maybe_unused void JS_DumpString(JSRuntime *rt,
+ putchar(sep);
+ }
+
+-static __maybe_unused void JS_DumpAtoms(JSRuntime *rt)
++static maybe_unused void JS_DumpAtoms(JSRuntime *rt)
+ {
+ JSAtomStruct *p;
+ int h, i;
+@@ -2604,7 +2660,7 @@ static int JS_InitAtoms(JSRuntime *rt)
+ else
+ atom_type = JS_ATOM_TYPE_STRING;
+ len = strlen(p);
+- if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL)
++ if (JS_NewAtomInitImpl(rt, p, len, atom_type) == JS_ATOM_NULL)
+ return -1;
+ p = p + len + 1;
+ }
+@@ -2615,7 +2671,7 @@ static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v)
+ {
+ JSAtomStruct *p;
+
+- if (!__JS_AtomIsConst(v)) {
++ if (!JS_AtomIsConst(v)) {
+ p = rt->atom_array[v];
+ p->header.ref_count++;
+ }
+@@ -2627,7 +2683,7 @@ JSAtom JS_DupAtom(JSContext *ctx, JSAtom v)
+ JSRuntime *rt;
+ JSAtomStruct *p;
+
+- if (!__JS_AtomIsConst(v)) {
++ if (!JS_AtomIsConst(v)) {
+ rt = ctx->rt;
+ p = rt->atom_array[v];
+ p->header.ref_count++;
+@@ -2641,7 +2697,7 @@ static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v)
+ JSAtomStruct *p;
+
+ rt = ctx->rt;
+- if (__JS_AtomIsTaggedInt(v))
++ if (JS_AtomIsTaggedInt(v))
+ return JS_ATOM_KIND_STRING;
+ p = rt->atom_array[v];
+ switch(p->atom_type) {
+@@ -2687,7 +2743,7 @@ static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p)
+
+ /* string case (internal). Return JS_ATOM_NULL if error. 'str' is
+ freed. */
+-static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
++static JSAtom JS_NewAtomImpl(JSRuntime *rt, JSString *str, int atom_type)
+ {
+ uint32_t h, h1, i;
+ JSAtomStruct *p;
+@@ -2702,7 +2758,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
+ /* str is the atom, return its index */
+ i = js_get_atom_index(rt, str);
+ /* reduce string refcount and increase atom's unless constant */
+- if (__JS_AtomIsConst(i))
++ if (JS_AtomIsConst(i))
+ str->header.ref_count--;
+ return i;
+ }
+@@ -2718,7 +2774,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
+ p->atom_type == atom_type &&
+ p->len == len &&
+ js_string_memcmp(p, str, len) == 0) {
+- if (!__JS_AtomIsConst(i))
++ if (!JS_AtomIsConst(i))
+ p->header.ref_count++;
+ goto done;
+ }
+@@ -2843,7 +2899,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type)
+ }
+
+ /* only works with zero terminated 8 bit strings */
+-static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
++static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len,
+ int atom_type)
+ {
+ JSString *p;
+@@ -2852,10 +2908,10 @@ static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len,
+ return JS_ATOM_NULL;
+ memcpy(p->u.str8, str, len);
+ p->u.str8[len] = '\0';
+- return __JS_NewAtom(rt, p, atom_type);
++ return JS_NewAtomImpl(rt, p, atom_type);
+ }
+
+-static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
++static JSAtom JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
+ int atom_type)
+ {
+ uint32_t h, h1, i;
+@@ -2872,7 +2928,7 @@ static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len,
+ p->len == len &&
+ p->is_wide_char == 0 &&
+ memcmp(p->u.str8, str, len) == 0) {
+- if (!__JS_AtomIsConst(i))
++ if (!JS_AtomIsConst(i))
+ p->header.ref_count++;
+ return i;
+ }
+@@ -2924,7 +2980,7 @@ static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p)
+ assert(rt->atom_count >= 0);
+ }
+
+-static void __JS_FreeAtom(JSRuntime *rt, uint32_t i)
++static void JS_FreeAtomImpl(JSRuntime *rt, uint32_t i)
+ {
+ JSAtomStruct *p;
+
+@@ -2942,11 +2998,11 @@ static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p)
+ if (is_num_string(&n, p)) {
+ if (n <= JS_ATOM_MAX_INT) {
+ js_free_string(rt, p);
+- return __JS_AtomFromUInt32(n);
++ return JS_AtomFromUInt32(n);
+ }
+ }
+ /* XXX: should generate an exception */
+- return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING);
++ return JS_NewAtomImpl(rt, p, JS_ATOM_TYPE_STRING);
+ }
+
+ JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
+@@ -2954,7 +3010,7 @@ JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len)
+ JSValue val;
+
+ if (len == 0 || !is_digit(*str)) {
+- JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
++ JSAtom atom = JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING);
+ if (atom)
+ return atom;
+ }
+@@ -2972,7 +3028,7 @@ JSAtom JS_NewAtom(JSContext *ctx, const char *str)
+ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
+ {
+ if (n <= JS_ATOM_MAX_INT) {
+- return __JS_AtomFromUInt32(n);
++ return JS_AtomFromUInt32(n);
+ } else {
+ char buf[11];
+ JSValue val;
+@@ -2980,7 +3036,7 @@ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
+ val = JS_NewString(ctx, buf);
+ if (JS_IsException(val))
+ return JS_ATOM_NULL;
+- return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
++ return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val),
+ JS_ATOM_TYPE_STRING);
+ }
+ }
+@@ -2988,7 +3044,7 @@ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n)
+ static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
+ {
+ if ((uint64_t)n <= JS_ATOM_MAX_INT) {
+- return __JS_AtomFromUInt32((uint32_t)n);
++ return JS_AtomFromUInt32((uint32_t)n);
+ } else {
+ char buf[24];
+ JSValue val;
+@@ -2996,7 +3052,7 @@ static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n)
+ val = JS_NewString(ctx, buf);
+ if (JS_IsException(val))
+ return JS_ATOM_NULL;
+- return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val),
++ return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val),
+ JS_ATOM_TYPE_STRING);
+ }
+ }
+@@ -3006,7 +3062,7 @@ static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type)
+ {
+ JSRuntime *rt = ctx->rt;
+ JSAtom atom;
+- atom = __JS_NewAtom(rt, p, atom_type);
++ atom = JS_NewAtomImpl(rt, p, atom_type);
+ if (atom == JS_ATOM_NULL)
+ return JS_ThrowOutOfMemory(ctx);
+ return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]);
+@@ -3019,7 +3075,7 @@ static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
+ JSRuntime *rt = ctx->rt;
+ JSString *p;
+
+- assert(!__JS_AtomIsTaggedInt(descr));
++ assert(!JS_AtomIsTaggedInt(descr));
+ assert(descr < rt->atom_size);
+ p = rt->atom_array[descr];
+ JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p));
+@@ -3032,8 +3088,8 @@ static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr,
+ static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size,
+ JSAtom atom)
+ {
+- if (__JS_AtomIsTaggedInt(atom)) {
+- snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom));
++ if (JS_AtomIsTaggedInt(atom)) {
++ snprintf(buf, buf_size, "%u", JS_AtomToUInt32(atom));
+ } else {
+ JSAtomStruct *p;
+ assert(atom < rt->atom_size);
+@@ -3083,12 +3139,12 @@ static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom
+ return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom);
+ }
+
+-static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
++static JSValue JS_AtomToValueImpl(JSContext *ctx, JSAtom atom, BOOL force_string)
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+
+- if (__JS_AtomIsTaggedInt(atom)) {
+- snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom));
++ if (JS_AtomIsTaggedInt(atom)) {
++ snprintf(buf, sizeof(buf), "%u", JS_AtomToUInt32(atom));
+ return JS_NewString(ctx, buf);
+ } else {
+ JSRuntime *rt = ctx->rt;
+@@ -3112,20 +3168,20 @@ static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string)
+
+ JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom)
+ {
+- return __JS_AtomToValue(ctx, atom, FALSE);
++ return JS_AtomToValueImpl(ctx, atom, FALSE);
+ }
+
+ JSValue JS_AtomToString(JSContext *ctx, JSAtom atom)
+ {
+- return __JS_AtomToValue(ctx, atom, TRUE);
++ return JS_AtomToValueImpl(ctx, atom, TRUE);
+ }
+
+ /* return TRUE if the atom is an array index (i.e. 0 <= index <=
+ 2^32-2 and return its value */
+ static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom)
+ {
+- if (__JS_AtomIsTaggedInt(atom)) {
+- *pval = __JS_AtomToUInt32(atom);
++ if (JS_AtomIsTaggedInt(atom)) {
++ *pval = JS_AtomToUInt32(atom);
+ return TRUE;
+ } else {
+ JSRuntime *rt = ctx->rt;
+@@ -3156,8 +3212,8 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
+ int c, len, ret;
+ JSValue num, str;
+
+- if (__JS_AtomIsTaggedInt(atom))
+- return JS_NewInt32(ctx, __JS_AtomToUInt32(atom));
++ if (JS_AtomIsTaggedInt(atom))
++ return JS_NewInt32(ctx, JS_AtomToUInt32(atom));
+ assert(atom < rt->atom_size);
+ p1 = rt->atom_array[atom];
+ if (p1->atom_type != JS_ATOM_TYPE_STRING)
+@@ -3199,7 +3255,7 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom)
+ /* -0 case is specific */
+ if (c == '0' && len == 2) {
+ minus_zero:
+- return __JS_NewFloat64(ctx, -0.0);
++ return JS_NewFloat64Impl(ctx, -0.0);
+ }
+ }
+ if (!is_num(c)) {
+@@ -3244,14 +3300,14 @@ static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom)
+
+ void JS_FreeAtom(JSContext *ctx, JSAtom v)
+ {
+- if (!__JS_AtomIsConst(v))
+- __JS_FreeAtom(ctx->rt, v);
++ if (!JS_AtomIsConst(v))
++ JS_FreeAtomImpl(ctx->rt, v);
+ }
+
+ void JS_FreeAtomRT(JSRuntime *rt, JSAtom v)
+ {
+- if (!__JS_AtomIsConst(v))
+- __JS_FreeAtom(rt, v);
++ if (!JS_AtomIsConst(v))
++ JS_FreeAtomImpl(rt, v);
+ }
+
+ /* return TRUE if 'v' is a symbol with a string description */
+@@ -3261,7 +3317,7 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
+ JSAtomStruct *p;
+
+ rt = ctx->rt;
+- if (__JS_AtomIsTaggedInt(v))
++ if (JS_AtomIsTaggedInt(v))
+ return FALSE;
+ p = rt->atom_array[v];
+ return (((p->atom_type == JS_ATOM_TYPE_SYMBOL &&
+@@ -3270,7 +3326,7 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v)
+ !(p->len == 0 && p->is_wide_char != 0));
+ }
+
+-static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
++static maybe_unused void print_atom(JSContext *ctx, JSAtom atom)
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ const char *p;
+@@ -3445,9 +3501,9 @@ int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def)
+ JSAtom name;
+
+ len = strlen(class_def->class_name);
+- name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
++ name = JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
+ if (name == JS_ATOM_NULL) {
+- name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
++ name = JS_NewAtomInitImpl(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING);
+ if (name == JS_ATOM_NULL)
+ return -1;
+ }
+@@ -4614,7 +4670,7 @@ static int add_shape_property(JSContext *ctx, JSShape **psh,
+ pr = &prop[sh->prop_count++];
+ pr->atom = JS_DupAtom(ctx, atom);
+ pr->flags = prop_flags;
+- sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom);
++ sh->has_small_array_index |= JS_AtomIsTaggedInt(atom);
+ /* add in hash table */
+ hash_mask = sh->prop_hash_mask;
+ h = atom & hash_mask;
+@@ -4675,7 +4731,7 @@ static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh,
+ return NULL;
+ }
+
+-static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
++static maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
+ {
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+ int j;
+@@ -4691,7 +4747,7 @@ static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh)
+ printf("\n");
+ }
+
+-static __maybe_unused void JS_DumpShapes(JSRuntime *rt)
++static maybe_unused void JS_DumpShapes(JSRuntime *rt)
+ {
+ int i;
+ JSShape *sh;
+@@ -5471,7 +5527,7 @@ static void free_zero_refcount(JSRuntime *rt)
+ }
+
+ /* called with the ref_count of 'v' reaches zero. */
+-void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
++static void JS_FreeValueRTImpl(JSRuntime *rt, JSValue v)
+ {
+ uint32_t tag = JS_VALUE_GET_TAG(v);
+
+@@ -5546,9 +5602,9 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v)
+ }
+ }
+
+-void __JS_FreeValue(JSContext *ctx, JSValue v)
++static void JS_FreeValueImpl(JSContext *ctx, JSValue v)
+ {
+- __JS_FreeValueRT(ctx->rt, v);
++ JS_FreeValueRTImpl(ctx->rt, v);
+ }
+
+ /* garbage collection */
+@@ -6469,7 +6525,7 @@ static const char *get_func_name(JSContext *ctx, JSValueConst func)
+
+ /* if filename != NULL, an additional level is added with the filename
+ and line number information (used for parse error). */
+-static void build_backtrace(JSContext *ctx, JSValueConst error_obj,
++void build_backtrace(JSContext *ctx, JSValueConst error_obj,
+ const char *filename, int line_num,
+ int backtrace_flags)
+ {
+@@ -6569,7 +6625,7 @@ JSValue JS_NewError(JSContext *ctx)
+ static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num,
+ const char *fmt, va_list ap, BOOL add_backtrace)
+ {
+- char buf[256];
++ char buf[8192];
+ JSValue obj, ret;
+
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+@@ -6604,7 +6660,7 @@ static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num,
+ return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace);
+ }
+
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
++JSValue FORMAT_ATTR(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...)
+ {
+ JSValue val;
+ va_list ap;
+@@ -6615,7 +6671,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx
+ return val;
+ }
+
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
++JSValue FORMAT_ATTR(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...)
+ {
+ JSValue val;
+ va_list ap;
+@@ -6626,7 +6682,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx,
+ return val;
+ }
+
+-static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
++static int FORMAT_ATTR(3, 4) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...)
+ {
+ va_list ap;
+
+@@ -6642,7 +6698,7 @@ static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSCont
+ }
+
+ /* never use it directly */
+-static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
++static JSValue FORMAT_ATTR(3, 4) JS_ThrowTypeErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...)
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowTypeError(ctx, fmt,
+@@ -6650,7 +6706,7 @@ static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSC
+ }
+
+ /* never use it directly */
+-static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...)
++static JSValue FORMAT_ATTR(3, 4) JS_ThrowSyntaxErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...)
+ {
+ char buf[ATOM_GET_STR_BUF_SIZE];
+ return JS_ThrowSyntaxError(ctx, fmt,
+@@ -6659,8 +6715,8 @@ static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(J
+
+ /* %s is replaced by 'atom'. The macro is used so that gcc can check
+ the format string. */
+-#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "")
+-#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "")
++#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) JS_ThrowTypeErrorAtomImpl(ctx, atom, fmt, "")
++#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) JS_ThrowSyntaxErrorAtomImpl(ctx, atom, fmt, "")
+
+ static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
+ {
+@@ -6673,7 +6729,7 @@ static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom)
+ }
+ }
+
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
++JSValue FORMAT_ATTR(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...)
+ {
+ JSValue val;
+ va_list ap;
+@@ -6684,7 +6740,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *
+ return val;
+ }
+
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
++JSValue FORMAT_ATTR(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...)
+ {
+ JSValue val;
+ va_list ap;
+@@ -6695,7 +6751,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx,
+ return val;
+ }
+
+-JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
++JSValue FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...)
+ {
+ JSValue val;
+ va_list ap;
+@@ -6770,7 +6826,7 @@ static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id)
+ return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name);
+ }
+
+-static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
++static no_inline warn_unused int js_poll_interrupts_impl(JSContext *ctx)
+ {
+ JSRuntime *rt = ctx->rt;
+ ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT;
+@@ -6785,10 +6841,10 @@ static no_inline __exception int __js_poll_interrupts(JSContext *ctx)
+ return 0;
+ }
+
+-static inline __exception int js_poll_interrupts(JSContext *ctx)
++static inline warn_unused int js_poll_interrupts(JSContext *ctx)
+ {
+ if (unlikely(--ctx->interrupt_counter <= 0)) {
+- return __js_poll_interrupts(ctx);
++ return js_poll_interrupts_impl(ctx);
+ } else {
+ return 0;
+ }
+@@ -7092,9 +7148,9 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
+ case JS_TAG_STRING:
+ {
+ JSString *p1 = JS_VALUE_GET_STRING(obj);
+- if (__JS_AtomIsTaggedInt(prop)) {
++ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx, ch;
+- idx = __JS_AtomToUInt32(prop);
++ idx = JS_AtomToUInt32(prop);
+ if (idx < p1->len) {
+ if (p1->is_wide_char)
+ ch = p1->u.str16[idx];
+@@ -7144,14 +7200,16 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
+ continue;
+ }
+ } else {
++ if (JS_IsUndefined(pr->u.value) && ctx->handleUndefined)
++ ctx->handleUndefined(ctx);
+ return JS_DupValue(ctx, pr->u.value);
+ }
+ }
+ if (unlikely(p->is_exotic)) {
+ /* exotic behaviors */
+ if (p->fast_array) {
+- if (__JS_AtomIsTaggedInt(prop)) {
+- uint32_t idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ /* we avoid duplicating the code */
+ return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx);
+@@ -7242,7 +7300,7 @@ static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj,
+ JS_ThrowTypeErrorNotASymbol(ctx);
+ goto fail;
+ }
+- prop = js_symbol_to_atom(ctx, (JSValue)name);
++ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (prs) {
+@@ -7273,7 +7331,7 @@ static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj,
+ /* safety check */
+ if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL))
+ return JS_ThrowTypeErrorNotASymbol(ctx);
+- prop = js_symbol_to_atom(ctx, (JSValue)name);
++ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (!prs) {
+@@ -7300,7 +7358,7 @@ static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj,
+ JS_ThrowTypeErrorNotASymbol(ctx);
+ goto fail;
+ }
+- prop = js_symbol_to_atom(ctx, (JSValue)name);
++ prop = js_symbol_to_atom(ctx, name);
+ p = JS_VALUE_GET_OBJ(obj);
+ prs = find_own_property(&pr, p, prop);
+ if (!prs) {
+@@ -7390,7 +7448,7 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func)
+ if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT))
+ goto not_obj;
+ p = JS_VALUE_GET_OBJ(obj);
+- prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand));
++ prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand));
+ if (!prs) {
+ JS_ThrowTypeError(ctx, "invalid brand on object");
+ return -1;
+@@ -7445,7 +7503,7 @@ static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len)
+
+ /* return < 0 in case if exception, 0 if OK. ptab and its atoms must
+ be freed by the user. */
+-static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
++static int warn_unused JS_GetOwnPropertyNamesInternal(JSContext *ctx,
+ JSPropertyEnum **ptab,
+ uint32_t *plen,
+ JSObject *p, int flags)
+@@ -7595,7 +7653,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx,
+ len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p));
+ add_array_keys:
+ for(i = 0; i < len; i++) {
+- tab_atom[num_index].atom = __JS_AtomFromUInt32(i);
++ tab_atom[num_index].atom = JS_AtomFromUInt32(i);
+ if (tab_atom[num_index].atom == JS_ATOM_NULL) {
+ js_free_prop_enum(ctx, tab_atom, num_index);
+ return -1;
+@@ -7703,9 +7761,9 @@ retry:
+ if (p->is_exotic) {
+ if (p->fast_array) {
+ /* specific case for fast arrays */
+- if (__JS_AtomIsTaggedInt(prop)) {
++ if (JS_AtomIsTaggedInt(prop)) {
+ uint32_t idx;
+- idx = __JS_AtomToUInt32(prop);
++ idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ if (desc) {
+ desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE |
+@@ -7825,7 +7883,7 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val)
+ if (tag == JS_TAG_INT &&
+ (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) {
+ /* fast path for integer values */
+- atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
++ atom = JS_AtomFromUInt32(JS_VALUE_GET_INT(val));
+ } else if (tag == JS_TAG_SYMBOL) {
+ JSAtomStruct *p = JS_VALUE_GET_PTR(val);
+ atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p));
+@@ -7856,7 +7914,7 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ /* fast path for array access */
+ p = JS_VALUE_GET_OBJ(this_obj);
+ idx = JS_VALUE_GET_INT(prop);
+- len = (uint32_t)p->u.array.count;
++ len = p->u.array.count;
+ if (unlikely(idx >= len))
+ goto slow_path;
+ switch(p->class_id) {
+@@ -7883,9 +7941,9 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]);
+ #endif
+ case JS_CLASS_FLOAT32_ARRAY:
+- return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]);
++ return JS_NewFloat64Impl(ctx, p->u.array.u.float_ptr[idx]);
+ case JS_CLASS_FLOAT64_ARRAY:
+- return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]);
++ return JS_NewFloat64Impl(ctx, p->u.array.u.double_ptr[idx]);
+ default:
+ goto slow_path;
+ }
+@@ -7920,7 +7978,7 @@ static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx,
+
+ if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) {
+ /* fast path */
+- present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx));
++ present = JS_HasProperty(ctx, obj, JS_AtomFromUInt32(idx));
+ if (present > 0) {
+ val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx));
+ if (unlikely(JS_IsException(val)))
+@@ -8017,7 +8075,7 @@ static JSProperty *add_property(JSContext *ctx,
+
+ /* can be called on Array or Arguments objects. return < 0 if
+ memory alloc error. */
+-static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,
++static no_inline warn_unused int convert_fast_array_to_array(JSContext *ctx,
+ JSObject *p)
+ {
+ JSProperty *pr;
+@@ -8039,8 +8097,8 @@ static no_inline __exception int convert_fast_array_to_array(JSContext *ctx,
+ tab = p->u.array.u.values;
+ for(i = 0; i < len; i++) {
+ /* add_property cannot fail here but
+- __JS_AtomFromUInt32(i) fails for i > INT32_MAX */
+- pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E);
++ JS_AtomFromUInt32(i) fails for i > INT32_MAX */
++ pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E);
+ pr->u.value = *tab++;
+ }
+ js_free(ctx, p->u.array.u.values);
+@@ -8145,7 +8203,7 @@ static int call_setter(JSContext *ctx, JSObject *setter,
+ func = JS_MKPTR(JS_TAG_OBJECT, setter);
+ /* Note: the field could be removed in the setter */
+ func = JS_DupValue(ctx, func);
+- ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val);
++ ret = JS_CallFree(ctx, func, this_obj, 1, &val);
+ JS_FreeValue(ctx, val);
+ if (JS_IsException(ret))
+ return -1;
+@@ -8489,8 +8547,8 @@ retry:
+ for(;;) {
+ if (p1->is_exotic) {
+ if (p1->fast_array) {
+- if (__JS_AtomIsTaggedInt(prop)) {
+- uint32_t idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx < p1->u.array.count) {
+ if (unlikely(p == p1))
+ return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags);
+@@ -8610,8 +8668,8 @@ retry:
+
+ if (p->is_exotic) {
+ if (p->class_id == JS_CLASS_ARRAY && p->fast_array &&
+- __JS_AtomIsTaggedInt(prop)) {
+- uint32_t idx = __JS_AtomToUInt32(prop);
++ JS_AtomIsTaggedInt(prop)) {
++ uint32_t idx = JS_AtomToUInt32(prop);
+ if (idx == p->u.array.count) {
+ /* fast case */
+ return add_fast_array_element(ctx, p, val, flags);
+@@ -8662,7 +8720,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSShape *sh1;
+
+ /* fast path to add an element to the array */
+- if (idx != (uint32_t)p->u.array.count ||
++ if (idx != p->u.array.count ||
+ !p->fast_array || !p->extensible)
+ goto slow_path;
+ /* check if prototype chain has a numeric property */
+@@ -8837,8 +8895,8 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p,
+ uint32_t idx, len;
+
+ if (p->fast_array) {
+- if (__JS_AtomIsTaggedInt(prop)) {
+- idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ idx = JS_AtomToUInt32(prop);
+ if (idx == p->u.array.count) {
+ if (!p->extensible)
+ goto not_extensible;
+@@ -9042,7 +9100,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ return -1;
+ }
+ /* this code relies on the fact that Uint32 are never allocated */
+- val = (JSValueConst)JS_NewUint32(ctx, array_length);
++ val = JS_NewUint32(ctx, array_length);
+ /* prs may have been modified */
+ prs = find_own_property(&pr, p, prop);
+ assert(prs != NULL);
+@@ -9214,8 +9272,8 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx;
+ uint32_t prop_flags;
+ if (p->class_id == JS_CLASS_ARRAY) {
+- if (__JS_AtomIsTaggedInt(prop)) {
+- idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ idx = JS_AtomToUInt32(prop);
+ if (idx < p->u.array.count) {
+ prop_flags = get_prop_flags(flags, JS_PROP_C_W_E);
+ if (prop_flags != JS_PROP_C_W_E)
+@@ -9238,7 +9296,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ JSValue num;
+ int ret;
+
+- if (!__JS_AtomIsTaggedInt(prop)) {
++ if (!JS_AtomIsTaggedInt(prop)) {
+ /* slow path with to handle all numeric indexes */
+ num = JS_AtomIsNumericIndex1(ctx, prop);
+ if (JS_IsUndefined(num))
+@@ -9259,10 +9317,10 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ if (ret) {
+ return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array");
+ }
+- if (!__JS_AtomIsTaggedInt(prop))
++ if (!JS_AtomIsTaggedInt(prop))
+ goto typed_array_oob;
+ }
+- idx = __JS_AtomToUInt32(prop);
++ idx = JS_AtomToUInt32(prop);
+ /* if the typed array is detached, p->u.array.count = 0 */
+ if (idx >= typed_array_get_length(ctx, p)) {
+ typed_array_oob:
+@@ -9563,6 +9621,11 @@ static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop,
+ return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom);
+ return JS_DupValue(ctx, pr->u.value);
+ }
++ if (ctx->scopeLookup) {
++ struct LookupResult result = ctx->scopeLookup(ctx, prop);
++ if (result.useResult)
++ return result.value;
++ }
+ return JS_GetPropertyInternal(ctx, ctx->global_obj, prop,
+ ctx->global_obj, throw_ref_error);
+ }
+@@ -9658,6 +9721,15 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val,
+ flags = JS_PROP_THROW_STRICT;
+ if (is_strict_mode(ctx))
+ flags |= JS_PROP_NO_ADD;
++
++ if (ctx->scopeLookup) {
++ struct LookupResult result = ctx->scopeLookup(ctx, prop);
++ if (result.useResult) {
++ JS_FreeValue(ctx, result.value);
++ return JS_SetPropertyInternal(ctx, result.scope, prop, val, flags);
++ }
++ }
++
+ return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags);
+ }
+
+@@ -9693,7 +9765,7 @@ int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int fl
+
+ if ((uint64_t)idx <= JS_ATOM_MAX_INT) {
+ /* fast path for fast arrays */
+- return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags);
++ return JS_DeleteProperty(ctx, obj, JS_AtomFromUInt32(idx), flags);
+ }
+ prop = JS_NewAtomInt64(ctx, idx);
+ if (prop == JS_ATOM_NULL)
+@@ -9853,7 +9925,7 @@ static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint)
+ break;
+ }
+ arg = JS_AtomToString(ctx, atom);
+- ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg);
++ ret = JS_CallFree(ctx, method, val, 1, &arg);
+ JS_FreeValue(ctx, arg);
+ if (JS_IsException(ret))
+ goto exception;
+@@ -10057,7 +10129,7 @@ static double js_strtod(const char *p, int radix, BOOL is_float)
+ if (is_neg)
+ d = -d;
+ } else {
+- d = strtod(p, NULL);
++ d = safe_strtod(p, NULL);
+ }
+ return d;
+ }
+@@ -10237,7 +10309,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
+ } else
+ #endif
+ {
+- double d = 1.0 / 0.0;
++ double d = INFINITY;
+ if (is_neg)
+ d = -d;
+ val = JS_NewFloat64(ctx, d);
+@@ -10270,11 +10342,8 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp,
+ ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) {
+ const char *p1 = p + 1;
+ is_float = TRUE;
+- if (*p1 == '+') {
+- p1++;
+- } else if (*p1 == '-') {
++ if (*p1 == '+' || *p1 == '-')
+ p1++;
+- }
+ if (is_digit((uint8_t)*p1)) {
+ p = p1 + 1;
+ while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1])))
+@@ -10509,7 +10578,7 @@ static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val)
+ return JS_ToNumericFree(ctx, JS_DupValue(ctx, val));
+ }
+
+-static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres,
++static warn_unused int JS_ToFloat64FreeImpl(JSContext *ctx, double *pres,
+ JSValue val)
+ {
+ double d;
+@@ -10560,7 +10629,7 @@ static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val)
+ *pres = JS_VALUE_GET_FLOAT64(val);
+ return 0;
+ } else {
+- return __JS_ToFloat64Free(ctx, pres, val);
++ return JS_ToFloat64FreeImpl(ctx, pres, val);
+ }
+ }
+
+@@ -10575,7 +10644,7 @@ static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val)
+ }
+
+ /* same as JS_ToNumber() but return 0 in case of NaN/Undefined */
+-static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
++static maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val)
+ {
+ uint32_t tag;
+ JSValue ret;
+@@ -10990,7 +11059,7 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val)
+ return 0;
+ }
+
+-static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
++static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen,
+ JSValue val, BOOL is_array_ctor)
+ {
+ uint32_t tag, len;
+@@ -11092,7 +11161,7 @@ int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val)
+
+ /* convert a value to a length between 0 and MAX_SAFE_INTEGER.
+ return -1 for exception */
+-static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
++static warn_unused int JS_ToLengthFree(JSContext *ctx, int64_t *plen,
+ JSValue val)
+ {
+ int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0);
+@@ -11338,7 +11407,7 @@ static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf,
+ n_digits = (n_digits_min + n_digits_max) / 2;
+ js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST,
+ buf_tmp, sizeof(buf_tmp));
+- if (strtod(buf_tmp, NULL) == d) {
++ if (safe_strtod(buf_tmp, NULL) == d) {
+ /* no need to keep the trailing zeros */
+ while (n_digits >= 2 && buf[n_digits - 1] == '0')
+ n_digits--;
+@@ -11700,14 +11769,14 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1)
+ return JS_EXCEPTION;
+ }
+
+-static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
++static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt)
+ {
+ printf("%14s %4s %4s %14s %10s %s\n",
+ "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS");
+ }
+
+ /* for debug only: dump an object without side effect */
+-static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
++static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
+ {
+ uint32_t i;
+ char atom_buf[ATOM_GET_STR_BUF_SIZE];
+@@ -11784,7 +11853,7 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
+ printf("[autoinit %p %d %p]",
+ (void *)js_autoinit_get_realm(pr),
+ js_autoinit_get_id(pr),
+- (void *)pr->u.init.opaque);
++ pr->u.init.opaque);
+ } else {
+ JS_DumpValueShort(rt, pr->u.value);
+ }
+@@ -11813,7 +11882,7 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p)
+ printf("\n");
+ }
+
+-static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
++static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
+ {
+ if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) {
+ JS_DumpObject(rt, (JSObject *)p);
+@@ -11845,7 +11914,7 @@ static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p)
+ }
+ }
+
+-static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
++static maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ JSValueConst val)
+ {
+ uint32_t tag = JS_VALUE_GET_NORM_TAG(val);
+@@ -11950,13 +12019,13 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt,
+ }
+ }
+
+-static __maybe_unused void JS_DumpValue(JSContext *ctx,
++static maybe_unused void JS_DumpValue(JSContext *ctx,
+ JSValueConst val)
+ {
+ JS_DumpValueShort(ctx->rt, val);
+ }
+
+-static __maybe_unused void JS_PrintValue(JSContext *ctx,
++static maybe_unused void JS_PrintValue(JSContext *ctx,
+ const char *str,
+ JSValueConst val)
+ {
+@@ -12439,7 +12508,7 @@ static JSObject *find_binary_op(JSBinaryOperatorDef *def,
+
+ /* return -1 if exception, 0 if no operator overloading, 1 if
+ overloaded operator called */
+-static __exception int js_call_binary_op_fallback(JSContext *ctx,
++static WARN_UNUSED int js_call_binary_op_fallback(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst op1,
+ JSValueConst op2,
+@@ -12568,7 +12637,7 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx,
+
+ /* try to call the operation on the operatorSet field of 'obj'. Only
+ used for "/" and "**" on the BigInt prototype in math mode */
+-static __exception int js_call_binary_op_simple(JSContext *ctx,
++static WARN_UNUSED int js_call_binary_op_simple(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst obj,
+ JSValueConst op1,
+@@ -12625,7 +12694,7 @@ static __exception int js_call_binary_op_simple(JSContext *ctx,
+
+ /* return -1 if exception, 0 if no operator overloading, 1 if
+ overloaded operator called */
+-static __exception int js_call_unary_op_fallback(JSContext *ctx,
++static WARN_UNUSED int js_call_unary_op_fallback(JSContext *ctx,
+ JSValue *pret,
+ JSValueConst op1,
+ OPCodeEnum op)
+@@ -12835,7 +12904,7 @@ static int js_unary_arith_bigdecimal(JSContext *ctx,
+ return 0;
+ }
+
+-static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
++static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+ {
+@@ -12933,7 +13002,7 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
+ return -1;
+ }
+
+-static __exception int js_post_inc_slow(JSContext *ctx,
++static WARN_UNUSED int js_post_inc_slow(JSContext *ctx,
+ JSValue *sp, OPCodeEnum op)
+ {
+ JSValue op1;
+@@ -13288,7 +13357,7 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op,
+ return -1;
+ }
+
+-static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
++static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+ {
+ JSValue op1, op2, res;
+@@ -13452,7 +13521,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
+ return -1;
+ }
+
+-static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
++static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp)
+ {
+ JSValue op1, op2, res;
+ uint32_t tag1, tag2;
+@@ -13566,7 +13635,7 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
+ return -1;
+ }
+
+-static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
++static no_inline WARN_UNUSED int js_binary_logic_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+ {
+@@ -13916,7 +13985,7 @@ static BOOL tag_is_number(uint32_t tag)
+ tag == JS_TAG_BIG_DECIMAL);
+ }
+
+-static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
++static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp,
+ BOOL is_neq)
+ {
+ JSValue op1, op2, ret;
+@@ -14192,7 +14261,7 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val)
+ return -1;
+ }
+
+-static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
++static no_inline warn_unused int js_unary_arith_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+ {
+@@ -14224,7 +14293,7 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx,
+ }
+
+ /* specific case necessary for correct return value semantics */
+-static __exception int js_post_inc_slow(JSContext *ctx,
++static warn_unused int js_post_inc_slow(JSContext *ctx,
+ JSValue *sp, OPCodeEnum op)
+ {
+ JSValue op1;
+@@ -14241,7 +14310,7 @@ static __exception int js_post_inc_slow(JSContext *ctx,
+ return 0;
+ }
+
+-static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
++static no_inline warn_unused int js_binary_arith_slow(JSContext *ctx, JSValue *sp,
+ OPCodeEnum op)
+ {
+ JSValue op1, op2;
+@@ -14283,7 +14352,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s
+ return -1;
+ }
+
+-static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
++static no_inline warn_unused int js_add_slow(JSContext *ctx, JSValue *sp)
+ {
+ JSValue op1, op2;
+ uint32_t tag1, tag2;
+@@ -14331,7 +14400,7 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp)
+ return -1;
+ }
+
+-static no_inline __exception int js_binary_logic_slow(JSContext *ctx,
++static no_inline warn_unused int js_binary_logic_slow(JSContext *ctx,
+ JSValue *sp,
+ OPCodeEnum op)
+ {
+@@ -14458,7 +14527,7 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp,
+ return -1;
+ }
+
+-static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp,
++static no_inline warn_unused int js_eq_slow(JSContext *ctx, JSValue *sp,
+ BOOL is_neq)
+ {
+ JSValue op1, op2;
+@@ -14744,7 +14813,7 @@ static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp,
+ return 0;
+ }
+
+-static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
++static warn_unused int js_operator_in(JSContext *ctx, JSValue *sp)
+ {
+ JSValue op1, op2;
+ JSAtom atom;
+@@ -14770,7 +14839,7 @@ static __exception int js_operator_in(JSContext *ctx, JSValue *sp)
+ return 0;
+ }
+
+-static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
++static warn_unused int js_has_unscopable(JSContext *ctx, JSValueConst obj,
+ JSAtom atom)
+ {
+ JSValue arr, val;
+@@ -14788,7 +14857,7 @@ static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj,
+ return ret;
+ }
+
+-static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)
++static warn_unused int js_operator_instanceof(JSContext *ctx, JSValue *sp)
+ {
+ JSValue op1, op2;
+ BOOL ret;
+@@ -14804,7 +14873,7 @@ static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp)
+ return 0;
+ }
+
+-static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
++static warn_unused int js_operator_typeof(JSContext *ctx, JSValueConst op1)
+ {
+ JSAtom atom;
+ uint32_t tag;
+@@ -14861,7 +14930,7 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1)
+ return atom;
+ }
+
+-static __exception int js_operator_delete(JSContext *ctx, JSValue *sp)
++static warn_unused int js_operator_delete(JSContext *ctx, JSValue *sp)
+ {
+ JSValue op1, op2;
+ JSAtom atom;
+@@ -15016,7 +15085,7 @@ static JSValue js_build_mapped_arguments(JSContext *ctx, int argc,
+ var_ref = get_var_ref(ctx, sf, i, TRUE);
+ if (!var_ref)
+ goto fail;
+- pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
++ pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF);
+ if (!pr) {
+ free_var_ref(ctx->rt, var_ref);
+ goto fail;
+@@ -15188,7 +15257,7 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj)
+ }
+
+ /* obj -> enum_obj */
+-static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
++static warn_unused int js_for_in_start(JSContext *ctx, JSValue *sp)
+ {
+ sp[-1] = build_for_in_iterator(ctx, sp[-1]);
+ if (JS_IsException(sp[-1]))
+@@ -15197,7 +15266,7 @@ static __exception int js_for_in_start(JSContext *ctx, JSValue *sp)
+ }
+
+ /* enum_obj -> enum_obj value done */
+-static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
++static warn_unused int js_for_in_next(JSContext *ctx, JSValue *sp)
+ {
+ JSValueConst enum_obj;
+ JSObject *p;
+@@ -15218,7 +15287,7 @@ static __exception int js_for_in_next(JSContext *ctx, JSValue *sp)
+ if (it->is_array) {
+ if (it->idx >= it->array_length)
+ goto done;
+- prop = __JS_AtomFromUInt32(it->idx);
++ prop = JS_AtomFromUInt32(it->idx);
+ it->idx++;
+ } else {
+ JSShape *sh = p->shape;
+@@ -15411,7 +15480,7 @@ static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj,
+ }
+
+ /* obj -> enum_rec (3 slots) */
+-static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,
++static warn_unused int js_for_of_start(JSContext *ctx, JSValue *sp,
+ BOOL is_async)
+ {
+ JSValue op1, obj, method;
+@@ -15432,7 +15501,7 @@ static __exception int js_for_of_start(JSContext *ctx, JSValue *sp,
+ objs. If 'done' is true or in case of exception, 'enum_rec' is set
+ to undefined. If 'done' is true, 'value' is always set to
+ undefined. */
+-static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
++static warn_unused int js_for_of_next(JSContext *ctx, JSValue *sp, int offset)
+ {
+ JSValue value = JS_UNDEFINED;
+ int done = 1;
+@@ -15478,7 +15547,7 @@ static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj,
+ return JS_EXCEPTION;
+ }
+
+-static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
++static warn_unused int js_iterator_get_value_done(JSContext *ctx, JSValue *sp)
+ {
+ JSValue obj, value;
+ BOOL done;
+@@ -15554,7 +15623,7 @@ static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj,
+ return FALSE;
+ }
+
+-static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp)
++static warn_unused int js_append_enumerate(JSContext *ctx, JSValue *sp)
+ {
+ JSValue iterator, enumobj, method, value;
+ int is_array_iterator;
+@@ -15634,7 +15703,7 @@ exception:
+ return -1;
+ }
+
+-static __exception int JS_CopyDataProperties(JSContext *ctx,
++static warn_unused int JS_CopyDataProperties(JSContext *ctx,
+ JSValueConst target,
+ JSValueConst source,
+ JSValueConst excluded,
+@@ -16043,7 +16112,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
+ #else
+ sf->js_mode = 0;
+ #endif
+- sf->cur_func = (JSValue)func_obj;
++ sf->cur_func = func_obj;
+ sf->arg_count = argc;
+ arg_buf = argv;
+
+@@ -16056,7 +16125,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj,
+ arg_buf[i] = JS_UNDEFINED;
+ sf->arg_count = arg_count;
+ }
+- sf->arg_buf = (JSValue*)arg_buf;
++ sf->arg_buf = arg_buf;
+
+ func = p->u.cfunc.c_function;
+ switch(cproto) {
+@@ -16226,7 +16295,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ #include "quickjs-opcode.h"
+ [ OP_COUNT ... 255 ] = &&case_default
+ };
+-#define SWITCH(pc) goto *dispatch_table[opcode = *pc++];
++#define SWITCH(pc) goto *dispatch_table[opcode = *(pc)++];
+ #define CASE(op) case_ ## op
+ #define DEFAULT case_default
+ #define BREAK SWITCH(pc)
+@@ -16269,7 +16338,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ return JS_ThrowTypeError(caller_ctx, "not a function");
+ }
+ return call_func(caller_ctx, func_obj, this_obj, argc,
+- (JSValueConst *)argv, flags);
++ argv, flags);
+ }
+ b = p->u.func.function_bytecode;
+
+@@ -16287,7 +16356,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ sf->js_mode = b->js_mode;
+ arg_buf = argv;
+ sf->arg_count = argc;
+- sf->cur_func = (JSValue)func_obj;
++ sf->cur_func = func_obj;
+ init_list_head(&sf->var_ref_list);
+ var_refs = p->u.func.var_refs;
+
+@@ -16315,6 +16384,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ rt->current_stack_frame = sf;
+ ctx = b->realm; /* set the current realm */
+
++ if (ctx->handleFunctionEntered)
++ ctx->handleFunctionEntered(ctx, this_obj);
++
+ restart:
+ for(;;) {
+ int call_argc;
+@@ -16420,12 +16492,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ int arg = *pc++;
+ switch(arg) {
+ case OP_SPECIAL_OBJECT_ARGUMENTS:
+- *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv);
++ *sp++ = js_build_arguments(ctx, argc, argv);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ break;
+ case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS:
+- *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv,
++ *sp++ = js_build_mapped_arguments(ctx, argc, argv,
+ sf, min_int(argc, b->arg_count));
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+@@ -16465,7 +16537,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ {
+ int first = get_u16(pc);
+ pc += 2;
+- *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv);
++ *sp++ = js_build_rest(ctx, first, argc, argv);
+ if (unlikely(JS_IsException(sp[-1])))
+ goto exception;
+ }
+@@ -16698,7 +16770,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ goto exception;
+ call_argv = sp - call_argc;
+ for(i = 0; i < call_argc; i++) {
+- ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i],
++ ret = JS_DefinePropertyValue(ctx, ret_val, JS_AtomFromUInt32(i), call_argv[i],
+ JS_PROP_C_W_E | JS_PROP_THROW);
+ call_argv[i] = JS_UNDEFINED;
+ if (ret < 0) {
+@@ -16717,7 +16789,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ magic = get_u16(pc);
+ pc += 2;
+
+- ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic);
++ ret_val = js_function_apply(ctx, sp[-3], 2, &sp[-2], magic);
+ if (unlikely(JS_IsException(ret_val)))
+ goto exception;
+ JS_FreeValue(ctx, sp[-3]);
+@@ -16850,7 +16922,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ JS_EVAL_TYPE_DIRECT, scope_idx);
+ } else {
+ ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len,
+- (JSValueConst *)tab);
++ tab);
+ }
+ free_arg_list(ctx, tab, len);
+ if (unlikely(JS_IsException(ret_val)))
+@@ -17474,7 +17546,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ {
+ JSValue ret;
+ ret = JS_Call(ctx, sp[-3], sp[-4],
+- 1, (JSValueConst *)(sp - 1));
++ 1, (sp - 1));
+ if (JS_IsException(ret))
+ goto exception;
+ JS_FreeValue(ctx, sp[-1]);
+@@ -17502,7 +17574,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ 0, NULL);
+ } else {
+ ret = JS_CallFree(ctx, method, sp[-4],
+- 1, (JSValueConst *)(sp - 1));
++ 1, (sp - 1));
+ }
+ if (JS_IsException(ret))
+ goto exception;
+@@ -17925,7 +17997,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+- sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) +
++ sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) +
+ JS_VALUE_GET_FLOAT64(op2));
+ sp--;
+ } else {
+@@ -17990,7 +18062,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ sp[-2] = JS_NewInt32(ctx, r);
+ sp--;
+ } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) {
+- sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) -
++ sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) -
+ JS_VALUE_GET_FLOAT64(op2));
+ sp--;
+ } else {
+@@ -18033,7 +18105,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ #endif
+ d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2);
+ mul_fp_res:
+- sp[-2] = __JS_NewFloat64(ctx, d);
++ sp[-2] = JS_NewFloat64Impl(ctx, d);
+ sp--;
+ } else {
+ goto binary_arith_slow;
+@@ -18125,7 +18197,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ } else if (JS_TAG_IS_FLOAT64(tag)) {
+ d = -JS_VALUE_GET_FLOAT64(op1);
+ neg_fp_res:
+- sp[-1] = __JS_NewFloat64(ctx, d);
++ sp[-1] = JS_NewFloat64Impl(ctx, d);
+ } else {
+ if (js_unary_arith_slow(ctx, sp, opcode))
+ goto exception;
+@@ -18659,6 +18731,13 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ }
+ }
+ exception:
++ if (JS_IsString(rt->current_exception)) {
++ JSValue error_obj = JS_NewError(ctx);
++ JSAtom msgProp = JS_NewAtom(ctx, "message");
++ JS_DefinePropertyValue(ctx, error_obj, msgProp, rt->current_exception, 0);
++ rt->current_exception = error_obj;
++ JS_FreeAtom(ctx, msgProp);
++ }
+ if (is_backtrace_needed(ctx, rt->current_exception)) {
+ /* add the backtrace information now (it is not done
+ before if the exception happens in a bytecode
+@@ -18706,6 +18785,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj,
+ }
+ }
+ rt->current_stack_frame = sf->prev_frame;
++ if (ctx->handleFunctionExited)
++ ctx->handleFunctionExited(ctx);
+ return ret_val;
+ }
+
+@@ -18713,14 +18794,14 @@ JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+ {
+ return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
+- argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
++ argc, argv, JS_CALL_FLAG_COPY_ARGV);
+ }
+
+ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+ {
+ JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED,
+- argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV);
++ argc, argv, JS_CALL_FLAG_COPY_ARGV);
+ JS_FreeValue(ctx, func_obj);
+ return res;
+ }
+@@ -18825,7 +18906,7 @@ static JSValue JS_CallConstructorInternal(JSContext *ctx,
+ return JS_ThrowTypeError(ctx, "not a function");
+ }
+ return call_func(ctx, func_obj, new_target, argc,
+- (JSValueConst *)argv, flags);
++ argv, flags);
+ }
+
+ b = p->u.func.function_bytecode;
+@@ -18854,7 +18935,7 @@ JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
+ int argc, JSValueConst *argv)
+ {
+ return JS_CallConstructorInternal(ctx, func_obj, new_target,
+- argc, (JSValue *)argv,
++ argc, argv,
+ JS_CALL_FLAG_COPY_ARGV);
+ }
+
+@@ -18862,7 +18943,7 @@ JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
+ int argc, JSValueConst *argv)
+ {
+ return JS_CallConstructorInternal(ctx, func_obj, func_obj,
+- argc, (JSValue *)argv,
++ argc, argv,
+ JS_CALL_FLAG_COPY_ARGV);
+ }
+
+@@ -18885,7 +18966,7 @@ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom,
+ }
+
+ /* JSAsyncFunctionState (used by generator and async functions) */
+-static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
++static warn_unused int async_func_init(JSContext *ctx, JSAsyncFunctionState *s,
+ JSValueConst func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv)
+ {
+@@ -19227,7 +19308,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
+ fail:
+ error = JS_GetException(ctx);
+ ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED,
+- 1, (JSValueConst *)&error);
++ 1, &error);
+ JS_FreeValue(ctx, error);
+ js_async_function_terminate(ctx->rt, s);
+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
+@@ -19238,7 +19319,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
+ if (JS_IsUndefined(func_ret)) {
+ /* function returned */
+ ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED,
+- 1, (JSValueConst *)&value);
++ 1, &value);
+ JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, value);
+ js_async_function_terminate(ctx->rt, s);
+@@ -19249,7 +19330,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s)
+ /* await */
+ JS_FreeValue(ctx, func_ret); /* not used */
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+- 1, (JSValueConst *)&value, 0);
++ 1, &value, 0);
+ JS_FreeValue(ctx, value);
+ if (JS_IsException(promise))
+ goto fail;
+@@ -19531,7 +19612,7 @@ static int js_async_generator_completed_return(JSContext *ctx,
+ int res;
+
+ promise = js_promise_resolve(ctx, ctx->promise_ctor,
+- 1, (JSValueConst *)&value, 0);
++ 1, &value, 0);
+ if (JS_IsException(promise))
+ return -1;
+ if (js_async_generator_resolve_function_create(ctx,
+@@ -19703,7 +19784,7 @@ static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val,
+ JS_ThrowTypeError(ctx, "not an AsyncGenerator object");
+ err = JS_GetException(ctx);
+ res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
+- 1, (JSValueConst *)&err);
++ 1, &err);
+ JS_FreeValue(ctx, err);
+ JS_FreeValue(ctx, res2);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+@@ -20138,7 +20219,7 @@ static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = {
+ #define short_opcode_info(op) opcode_info[op]
+ #endif
+
+-static __exception int next_token(JSParseState *s);
++static warn_unused int next_token(JSParseState *s);
+
+ static void free_token(JSParseState *s, JSToken *token)
+ {
+@@ -20169,7 +20250,7 @@ static void free_token(JSParseState *s, JSToken *token)
+ }
+ }
+
+-static void __attribute((unused)) dump_token(JSParseState *s,
++static void maybe_unused dump_token(JSParseState *s,
+ const JSToken *token)
+ {
+ switch(token->val) {
+@@ -20230,7 +20311,7 @@ static void __attribute((unused)) dump_token(JSParseState *s,
+ }
+ }
+
+-int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...)
++int FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, const char *fmt, ...)
+ {
+ JSContext *ctx = s->ctx;
+ va_list ap;
+@@ -20276,7 +20357,7 @@ static int js_parse_error_reserved_identifier(JSParseState *s)
+ s->token.u.ident.atom));
+ }
+
+-static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
++static warn_unused int js_parse_template_part(JSParseState *s, const uint8_t *p)
+ {
+ uint32_t c;
+ StringBuffer b_s, *b = &b_s;
+@@ -20337,7 +20418,7 @@ static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p)
+ return -1;
+ }
+
+-static __exception int js_parse_string(JSParseState *s, int sep,
++static warn_unused int js_parse_string(JSParseState *s, int sep,
+ BOOL do_throw, const uint8_t *p,
+ JSToken *token, const uint8_t **pp)
+ {
+@@ -20482,7 +20563,7 @@ static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) {
+ !s->token.u.ident.has_escape;
+ }
+
+-static __exception int js_parse_regexp(JSParseState *s)
++static warn_unused int js_parse_regexp(JSParseState *s)
+ {
+ const uint8_t *p;
+ BOOL in_class;
+@@ -20580,8 +20661,8 @@ static __exception int js_parse_regexp(JSParseState *s)
+ return -1;
+ }
+
+-static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
+- char *static_buf)
++static warn_unused int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize,
++ const char *static_buf)
+ {
+ char *buf, *new_buf;
+ size_t size, new_size;
+@@ -20656,7 +20737,7 @@ static JSAtom parse_ident(JSParseState *s, const uint8_t **pp,
+ }
+
+
+-static __exception int next_token(JSParseState *s)
++static warn_unused int next_token(JSParseState *s)
+ {
+ const uint8_t *p;
+ int c;
+@@ -21204,7 +21285,7 @@ static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c)
+ return atom;
+ }
+
+-static __exception int json_next_token(JSParseState *s)
++static warn_unused int json_next_token(JSParseState *s)
+ {
+ const uint8_t *p;
+ int c;
+@@ -21644,7 +21725,7 @@ static int cpool_add(JSParseState *s, JSValue val)
+ return fd->cpool_count - 1;
+ }
+
+-static __exception int emit_push_const(JSParseState *s, JSValueConst val,
++static warn_unused int emit_push_const(JSParseState *s, JSValueConst val,
+ BOOL as_atom)
+ {
+ int idx;
+@@ -21654,7 +21735,7 @@ static __exception int emit_push_const(JSParseState *s, JSValueConst val,
+ /* warning: JS_NewAtomStr frees the string value */
+ JS_DupValue(s->ctx, val);
+ atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val));
+- if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) {
++ if (atom != JS_ATOM_NULL && !JS_AtomIsTaggedInt(atom)) {
+ emit_op(s, OP_push_atom_value);
+ emit_u32(s, atom);
+ return 0;
+@@ -22118,14 +22199,14 @@ static int add_private_class_field(JSParseState *s, JSFunctionDef *fd,
+ return idx;
+ }
+
+-static __exception int js_parse_expr(JSParseState *s);
+-static __exception int js_parse_function_decl(JSParseState *s,
++static warn_unused int js_parse_expr(JSParseState *s);
++static warn_unused int js_parse_function_decl(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name, const uint8_t *ptr,
+ int start_line);
+ static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s);
+-static __exception int js_parse_function_decl2(JSParseState *s,
++static warn_unused int js_parse_function_decl2(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+@@ -22133,9 +22214,9 @@ static __exception int js_parse_function_decl2(JSParseState *s,
+ int function_line_num,
+ JSParseExportEnum export_flag,
+ JSFunctionDef **pfd);
+-static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags);
+-static __exception int js_parse_assign_expr(JSParseState *s);
+-static __exception int js_parse_unary(JSParseState *s, int parse_flags);
++static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags);
++static warn_unused int js_parse_assign_expr(JSParseState *s);
++static warn_unused int js_parse_unary(JSParseState *s, int parse_flags);
+ static void push_break_entry(JSFunctionDef *fd, BlockEnv *be,
+ JSAtom label_name,
+ int label_break, int label_cont,
+@@ -22162,7 +22243,7 @@ static int seal_template_obj(JSContext *ctx, JSValueConst obj)
+ return 0;
+ }
+
+-static __exception int js_parse_template(JSParseState *s, int call, int *argc)
++static warn_unused int js_parse_template(JSParseState *s, int call, int *argc)
+ {
+ JSContext *ctx = s->ctx;
+ JSValue raw_array, template_object;
+@@ -22293,7 +22374,7 @@ static BOOL token_is_ident(int tok)
+ }
+
+ /* if the property is an expression, name = JS_ATOM_NULL */
+-static int __exception js_parse_property_name(JSParseState *s,
++static int warn_unused js_parse_property_name(JSParseState *s,
+ JSAtom *pname,
+ BOOL allow_method, BOOL allow_var,
+ BOOL allow_private)
+@@ -22435,7 +22516,7 @@ static int js_parse_get_pos(JSParseState *s, JSParsePos *sp)
+ return 0;
+ }
+
+-static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
++static warn_unused int js_parse_seek_token(JSParseState *s, const JSParsePos *sp)
+ {
+ s->token.line_num = sp->last_line_num;
+ s->line_num = sp->line_num;
+@@ -22480,7 +22561,8 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_
+ size_t level = 0;
+ JSParsePos pos;
+ int last_tok, tok = TOK_EOF;
+- int c, tok_len, bits = 0;
++ int tok_len, bits = 0;
++ char c;
+
+ /* protect from underflow */
+ state[level++] = 0;
+@@ -22640,7 +22722,7 @@ static void set_object_name_computed(JSParseState *s)
+ }
+ }
+
+-static __exception int js_parse_object_literal(JSParseState *s)
++static warn_unused int js_parse_object_literal(JSParseState *s)
+ {
+ JSAtom name = JS_ATOM_NULL;
+ const uint8_t *start_ptr;
+@@ -22765,15 +22847,15 @@ static __exception int js_parse_object_literal(JSParseState *s)
+ /* forbid the exponentiation operator in js_parse_unary() */
+ #define PF_POW_FORBIDDEN (1 << 4)
+
+-static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags);
++static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags);
+
+-static __exception int js_parse_left_hand_side_expr(JSParseState *s)
++static warn_unused int js_parse_left_hand_side_expr(JSParseState *s)
+ {
+ return js_parse_postfix_expr(s, PF_POSTFIX_CALL);
+ }
+
+ /* XXX: could generate specific bytecode */
+-static __exception int js_parse_class_default_ctor(JSParseState *s,
++static warn_unused int js_parse_class_default_ctor(JSParseState *s,
+ BOOL has_super,
+ JSFunctionDef **pfd)
+ {
+@@ -22864,7 +22946,7 @@ typedef struct {
+ int brand_push_pos;
+ } ClassFieldsDef;
+
+-static __exception int emit_class_init_start(JSParseState *s,
++static warn_unused int emit_class_init_start(JSParseState *s,
+ ClassFieldsDef *cf)
+ {
+ int label_add_brand;
+@@ -22897,7 +22979,7 @@ static __exception int emit_class_init_start(JSParseState *s,
+ return 0;
+ }
+
+-static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf)
++static warn_unused int add_brand(JSParseState *s, ClassFieldsDef *cf)
+ {
+ if (!cf->has_brand) {
+ /* define the brand field in 'this' of the initializer */
+@@ -22929,7 +23011,7 @@ static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf)
+ }
+
+
+-static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
++static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr,
+ JSParseExportEnum export_flag)
+ {
+ JSContext *ctx = s->ctx;
+@@ -23374,7 +23456,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr,
+ return -1;
+ }
+
+-static __exception int js_parse_array_literal(JSParseState *s)
++static warn_unused int js_parse_array_literal(JSParseState *s)
+ {
+ uint32_t idx;
+ BOOL need_length;
+@@ -23410,7 +23492,7 @@ static __exception int js_parse_array_literal(JSParseState *s)
+ if (js_parse_assign_expr(s))
+ return -1;
+ emit_op(s, OP_define_field);
+- emit_u32(s, __JS_AtomFromUInt32(idx));
++ emit_u32(s, JS_AtomFromUInt32(idx));
+ need_length = FALSE;
+ }
+ idx++;
+@@ -23520,7 +23602,7 @@ static BOOL has_with_scope(JSFunctionDef *s, int scope_level)
+ return FALSE;
+ }
+
+-static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope,
++static warn_unused int get_lvalue(JSParseState *s, int *popcode, int *pscope,
+ JSAtom *pname, int *plabel, int *pdepth, BOOL keep,
+ int tok)
+ {
+@@ -23760,7 +23842,7 @@ static void put_lvalue(JSParseState *s, int opcode, int scope,
+ }
+ }
+
+-static __exception int js_parse_expr_paren(JSParseState *s)
++static warn_unused int js_parse_expr_paren(JSParseState *s)
+ {
+ if (js_parse_expect(s, '('))
+ return -1;
+@@ -23778,7 +23860,7 @@ static int js_unsupported_keyword(JSParseState *s, JSAtom atom)
+ JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom));
+ }
+
+-static __exception int js_define_var(JSParseState *s, JSAtom name, int tok)
++static warn_unused int js_define_var(JSParseState *s, JSAtom name, int tok)
+ {
+ JSFunctionDef *fd = s->cur_func;
+ JSVarDefEnum var_def_type;
+@@ -24327,7 +24409,7 @@ static void optional_chain_test(JSParseState *s, int *poptional_chaining_label,
+ }
+
+ /* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */
+-static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags)
+ {
+ FuncCallType call_type;
+ int optional_chaining_label;
+@@ -24963,17 +25045,16 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags)
+ return 0;
+ }
+
+-static __exception int js_parse_delete(JSParseState *s)
++static warn_unused int js_parse_delete(JSParseState *s)
+ {
+ JSFunctionDef *fd = s->cur_func;
+ JSAtom name;
+- int opcode;
+
+ if (next_token(s))
+ return -1;
+ if (js_parse_unary(s, PF_POW_FORBIDDEN))
+ return -1;
+- switch(opcode = get_prev_opcode(fd)) {
++ switch (get_prev_opcode(fd)) {
+ case OP_get_field:
+ {
+ JSValue val;
+@@ -25024,7 +25105,7 @@ static __exception int js_parse_delete(JSParseState *s)
+ }
+
+ /* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */
+-static __exception int js_parse_unary(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_unary(JSParseState *s, int parse_flags)
+ {
+ int op;
+
+@@ -25172,7 +25253,7 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags)
+ }
+
+ /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+-static __exception int js_parse_expr_binary(JSParseState *s, int level,
++static warn_unused int js_parse_expr_binary(JSParseState *s, int level,
+ int parse_flags)
+ {
+ int op, opcode;
+@@ -25319,7 +25400,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level,
+ }
+
+ /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+-static __exception int js_parse_logical_and_or(JSParseState *s, int op,
++static warn_unused int js_parse_logical_and_or(JSParseState *s, int op,
+ int parse_flags)
+ {
+ int label1;
+@@ -25361,7 +25442,7 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op,
+ return 0;
+ }
+
+-static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
+ {
+ int label1;
+
+@@ -25389,7 +25470,7 @@ static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags)
+ }
+
+ /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */
+-static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_cond_expr(JSParseState *s, int parse_flags)
+ {
+ int label1, label2;
+
+@@ -25420,7 +25501,7 @@ static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags)
+ static void emit_return(JSParseState *s, BOOL hasval);
+
+ /* allowed parse_flags: PF_IN_ACCEPTED */
+-static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags)
+ {
+ int opcode, op, scope;
+ JSAtom name0 = JS_ATOM_NULL;
+@@ -25666,13 +25747,13 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags)
+ return 0;
+ }
+
+-static __exception int js_parse_assign_expr(JSParseState *s)
++static warn_unused int js_parse_assign_expr(JSParseState *s)
+ {
+ return js_parse_assign_expr2(s, PF_IN_ACCEPTED);
+ }
+
+ /* allowed parse_flags: PF_IN_ACCEPTED */
+-static __exception int js_parse_expr2(JSParseState *s, int parse_flags)
++static warn_unused int js_parse_expr2(JSParseState *s, int parse_flags)
+ {
+ BOOL comma = FALSE;
+ for(;;) {
+@@ -25696,7 +25777,7 @@ static __exception int js_parse_expr2(JSParseState *s, int parse_flags)
+ return 0;
+ }
+
+-static __exception int js_parse_expr(JSParseState *s)
++static warn_unused int js_parse_expr(JSParseState *s)
+ {
+ return js_parse_expr2(s, PF_IN_ACCEPTED);
+ }
+@@ -25724,7 +25805,7 @@ static void pop_break_entry(JSFunctionDef *fd)
+ fd->top_break = be->prev;
+ }
+
+-static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont)
++static warn_unused int emit_break(JSParseState *s, JSAtom name, int is_cont)
+ {
+ BlockEnv *top;
+ int i, scope_level;
+@@ -25873,15 +25954,15 @@ static void emit_return(JSParseState *s, BOOL hasval)
+ #define DECL_MASK_OTHER (1 << 2) /* all other declarations */
+ #define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER)
+
+-static __exception int js_parse_statement_or_decl(JSParseState *s,
++static warn_unused int js_parse_statement_or_decl(JSParseState *s,
+ int decl_mask);
+
+-static __exception int js_parse_statement(JSParseState *s)
++static warn_unused int js_parse_statement(JSParseState *s)
+ {
+ return js_parse_statement_or_decl(s, 0);
+ }
+
+-static __exception int js_parse_block(JSParseState *s)
++static warn_unused int js_parse_block(JSParseState *s)
+ {
+ if (js_parse_expect(s, '{'))
+ return -1;
+@@ -25901,7 +25982,7 @@ static __exception int js_parse_block(JSParseState *s)
+ }
+
+ /* allowed parse_flags: PF_IN_ACCEPTED */
+-static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok,
++static warn_unused int js_parse_var(JSParseState *s, int parse_flags, int tok,
+ BOOL export_flag)
+ {
+ JSContext *ctx = s->ctx;
+@@ -26052,7 +26133,7 @@ static int is_let(JSParseState *s, int decl_mask)
+
+ /* XXX: handle IteratorClose when exiting the loop before the
+ enumeration is done */
+-static __exception int js_parse_for_in_of(JSParseState *s, int label_name,
++static warn_unused int js_parse_for_in_of(JSParseState *s, int label_name,
+ BOOL is_async)
+ {
+ JSContext *ctx = s->ctx;
+@@ -26283,7 +26364,7 @@ static void set_eval_ret_undefined(JSParseState *s)
+ }
+ }
+
+-static __exception int js_parse_statement_or_decl(JSParseState *s,
++static warn_unused int js_parse_statement_or_decl(JSParseState *s,
+ int decl_mask)
+ {
+ JSContext *ctx = s->ctx;
+@@ -27603,7 +27684,7 @@ static int find_exported_name(GetExportNamesState *s, JSAtom name)
+ return -1;
+ }
+
+-static __exception int get_exported_names(JSContext *ctx,
++static warn_unused int get_exported_names(JSContext *ctx,
+ GetExportNamesState *s,
+ JSModuleDef *m, BOOL from_star)
+ {
+@@ -27744,7 +27825,6 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m)
+ en->u.var_ref = res_me->u.local.var_ref;
+ } else {
+ JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
+- p1 = JS_VALUE_GET_OBJ(res_m->func_obj);
+ en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx];
+ }
+ }
+@@ -28237,7 +28317,7 @@ static JSValue js_dynamic_import_job(JSContext *ctx,
+ goto exception;
+
+ ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED,
+- 1, (JSValueConst *)&ns);
++ 1, &ns);
+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, ns);
+ JS_FreeCString(ctx, basename);
+@@ -28246,7 +28326,7 @@ static JSValue js_dynamic_import_job(JSContext *ctx,
+
+ err = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED,
+- 1, (JSValueConst *)&err);
++ 1, &err);
+ JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */
+ JS_FreeValue(ctx, err);
+ JS_FreeCString(ctx, basename);
+@@ -28343,7 +28423,7 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m)
+ return ret_val;
+ }
+
+-static __exception JSAtom js_parse_from_clause(JSParseState *s)
++static warn_unused JSAtom js_parse_from_clause(JSParseState *s)
+ {
+ JSAtom module_name;
+ if (!token_is_pseudo_keyword(s, JS_ATOM_from)) {
+@@ -28366,7 +28446,7 @@ static __exception JSAtom js_parse_from_clause(JSParseState *s)
+ return module_name;
+ }
+
+-static __exception int js_parse_export(JSParseState *s)
++static warn_unused int js_parse_export(JSParseState *s)
+ {
+ JSContext *ctx = s->ctx;
+ JSModuleDef *m = s->cur_func->module;
+@@ -28569,7 +28649,7 @@ static int add_import(JSParseState *s, JSModuleDef *m,
+ return 0;
+ }
+
+-static __exception int js_parse_import(JSParseState *s)
++static warn_unused int js_parse_import(JSParseState *s)
+ {
+ JSContext *ctx = s->ctx;
+ JSModuleDef *m = s->cur_func->module;
+@@ -28684,7 +28764,7 @@ static __exception int js_parse_import(JSParseState *s)
+ return js_parse_expect_semi(s);
+ }
+
+-static __exception int js_parse_source_element(JSParseState *s)
++static warn_unused int js_parse_source_element(JSParseState *s)
+ {
+ JSFunctionDef *fd = s->cur_func;
+ int tok;
+@@ -29608,8 +29688,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
+ }
+ var_idx = idx;
+ break;
+- } else
+- if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
++ } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) {
+ dbuf_putc(bc, OP_get_loc);
+ dbuf_put_u16(bc, idx);
+ var_object_test(ctx, s, var_name, op, bc, &label_done, 1);
+@@ -30359,7 +30438,7 @@ static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv,
+
+ /* for direct eval compilation: add references to the variables of the
+ calling function */
+-static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
++static warn_unused int add_closure_variables(JSContext *ctx, JSFunctionDef *s,
+ JSFunctionBytecode *b, int scope_idx)
+ {
+ int i, count;
+@@ -30779,7 +30858,7 @@ static int get_label_pos(JSFunctionDef *s, int label)
+
+ /* convert global variable accesses to local variables or closure
+ variables when necessary */
+-static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s)
++static warn_unused int resolve_variables(JSContext *ctx, JSFunctionDef *s)
+ {
+ int pos, pos_next, bc_len, op, len, i, idx, line_num;
+ uint8_t *bc_buf;
+@@ -31343,7 +31422,7 @@ static void put_short_code(DynBuf *bc_out, int op, int idx)
+ }
+
+ /* peephole optimizations and resolve goto/labels */
+-static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
++static warn_unused int resolve_labels(JSContext *ctx, JSFunctionDef *s)
+ {
+ int pos, pos_next, bc_len, op, op1, len, i, line_num;
+ const uint8_t *bc_buf;
+@@ -32152,8 +32231,7 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s)
+ bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false);
+ }
+ goto shrink;
+- } else
+- if (diff == (int16_t)diff && op == OP_goto) {
++ } else if (diff == (int16_t)diff && op == OP_goto) {
+ //put_u16(bc_out.buf + pos, diff);
+ jp->size = 2;
+ delta = 2;
+@@ -32236,7 +32314,7 @@ typedef struct StackSizeState {
+ } StackSizeState;
+
+ /* 'op' is only used for error indication */
+-static __exception int ss_check(JSContext *ctx, StackSizeState *s,
++static warn_unused int ss_check(JSContext *ctx, StackSizeState *s,
+ int pos, int op, int stack_len)
+ {
+ if ((unsigned)pos >= s->bc_len) {
+@@ -32272,7 +32350,7 @@ static __exception int ss_check(JSContext *ctx, StackSizeState *s,
+ return 0;
+ }
+
+-static __exception int compute_stack_size(JSContext *ctx,
++static warn_unused int compute_stack_size(JSContext *ctx,
+ JSFunctionDef *fd,
+ int *pstack_size)
+ {
+@@ -32593,7 +32671,9 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
+ }
+ } else {
+ b->vardefs = (void *)((uint8_t*)b + vardefs_offset);
++ if (fd->arg_count)
+ memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0]));
++ if (fd->var_count)
+ memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0]));
+ }
+ b->var_count = fd->var_count;
+@@ -32721,7 +32801,7 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b)
+ }
+ }
+
+-static __exception int js_parse_directives(JSParseState *s)
++static warn_unused int js_parse_directives(JSParseState *s)
+ {
+ char str[20];
+ JSParsePos pos;
+@@ -32895,7 +32975,7 @@ static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s)
+
+ /* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and
+ JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */
+-static __exception int js_parse_function_decl2(JSParseState *s,
++static warn_unused int js_parse_function_decl2(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+@@ -33443,7 +33523,7 @@ done:
+ return -1;
+ }
+
+-static __exception int js_parse_function_decl(JSParseState *s,
++static warn_unused int js_parse_function_decl(JSParseState *s,
+ JSParseFunctionEnum func_type,
+ JSFunctionKindEnum func_kind,
+ JSAtom func_name,
+@@ -33455,7 +33535,7 @@ static __exception int js_parse_function_decl(JSParseState *s,
+ NULL);
+ }
+
+-static __exception int js_parse_program(JSParseState *s)
++static warn_unused int js_parse_program(JSParseState *s)
+ {
+ JSFunctionDef *fd = s->cur_func;
+ int idx;
+@@ -33497,12 +33577,12 @@ static __exception int js_parse_program(JSParseState *s)
+
+ static void js_parse_init(JSContext *ctx, JSParseState *s,
+ const char *input, size_t input_len,
+- const char *filename)
++ const char *filename, int line)
+ {
+ memset(s, 0, sizeof(*s));
+ s->ctx = ctx;
+ s->filename = filename;
+- s->line_num = 1;
++ s->line_num = line;
+ s->buf_ptr = (const uint8_t *)input;
+ s->buf_end = s->buf_ptr + input_len;
+ s->token.val = ' ';
+@@ -33573,9 +33653,9 @@ static void skip_shebang(JSParseState *s)
+ }
+
+ /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
+-static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
++static JSValue JS_EvalInternalImpl(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int flags, int scope_idx)
++ const char *filename, int line, int flags, int scope_idx)
+ {
+ JSParseState s1, *s = &s1;
+ int err, js_mode, eval_type;
+@@ -33586,7 +33666,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ JSFunctionDef *fd;
+ JSModuleDef *m;
+
+- js_parse_init(ctx, s, input, input_len, filename);
++ js_parse_init(ctx, s, input, input_len, filename, line);
+ skip_shebang(s);
+
+ eval_type = flags & JS_EVAL_TYPE_MASK;
+@@ -33620,7 +33700,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ js_mode |= JS_MODE_STRICT;
+ }
+ }
+- fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1);
++ fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, line);
+ if (!fd)
+ goto fail1;
+ s->cur_func = fd;
+@@ -33686,12 +33766,12 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ /* the indirection is needed to make 'eval' optional */
+ static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int flags, int scope_idx)
++ const char *filename, int line, int flags, int scope_idx)
+ {
+ if (unlikely(!ctx->eval_internal)) {
+ return JS_ThrowTypeError(ctx, "eval is not supported");
+ }
+- return ctx->eval_internal(ctx, this_obj, input, input_len, filename,
++ return ctx->eval_internal(ctx, this_obj, input, input_len, filename, line,
+ flags, scope_idx);
+ }
+
+@@ -33707,7 +33787,7 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
+ str = JS_ToCStringLen(ctx, &len, val);
+ if (!str)
+ return JS_EXCEPTION;
+- ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx);
++ ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", 1, flags, scope_idx);
+ JS_FreeCString(ctx, str);
+ return ret;
+
+@@ -33715,14 +33795,14 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj,
+
+ JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int eval_flags)
++ const char *filename, int line, int eval_flags)
+ {
+ int eval_type = eval_flags & JS_EVAL_TYPE_MASK;
+ JSValue ret;
+
+ assert(eval_type == JS_EVAL_TYPE_GLOBAL ||
+ eval_type == JS_EVAL_TYPE_MODULE);
+- ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename,
++ ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, line,
+ eval_flags, -1);
+ return ret;
+ }
+@@ -33730,7 +33810,7 @@ JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
+ JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
+ const char *filename, int eval_flags)
+ {
+- return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename,
++ return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, 1,
+ eval_flags);
+ }
+
+@@ -33948,7 +34028,7 @@ static void bc_put_u16(BCWriterState *s, uint16_t v)
+ dbuf_put_u16(&s->dbuf, v);
+ }
+
+-static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
++static maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v)
+ {
+ if (s->byte_swap)
+ v = bswap32(v);
+@@ -33982,7 +34062,7 @@ static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom)
+ {
+ uint32_t v;
+
+- if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) {
++ if (atom < s->first_atom || JS_AtomIsTaggedInt(atom)) {
+ *pres = atom;
+ return 0;
+ }
+@@ -34022,8 +34102,8 @@ static int bc_put_atom(BCWriterState *s, JSAtom atom)
+ {
+ uint32_t v;
+
+- if (__JS_AtomIsTaggedInt(atom)) {
+- v = (__JS_AtomToUInt32(atom) << 1) | 1;
++ if (JS_AtomIsTaggedInt(atom)) {
++ v = (JS_AtomToUInt32(atom) << 1) | 1;
+ } else {
+ if (bc_atom_to_idx(s, &v, atom))
+ return -1;
+@@ -34142,7 +34222,7 @@ static int JS_WriteFunctionBytecode(BCWriterState *s,
+ static void JS_WriteString(BCWriterState *s, JSString *p)
+ {
+ int i;
+- bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char);
++ bc_put_leb128(s, (p->len << 1) | p->is_wide_char);
+ if (p->is_wide_char) {
+ for(i = 0; i < p->len; i++)
+ bc_put_u16(s, p->u.str16[i]);
+@@ -34800,7 +34880,7 @@ typedef struct BCReaderState {
+ } BCReaderState;
+
+ #ifdef DUMP_READ_OBJECT
+-static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
++static void FORMAT_ATTR(2, 3) bc_read_trace(BCReaderState *s, const char *fmt, ...) {
+ va_list ap;
+ int i, n, n0;
+
+@@ -34863,7 +34943,7 @@ static int bc_get_u16(BCReaderState *s, uint16_t *pval)
+ return 0;
+ }
+
+-static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
++static maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval)
+ {
+ if (unlikely(s->buf_end - s->ptr < 4)) {
+ *pval = 0; /* avoid warning */
+@@ -34937,7 +35017,7 @@ static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx)
+ {
+ JSAtom atom;
+
+- if (__JS_AtomIsTaggedInt(idx)) {
++ if (JS_AtomIsTaggedInt(idx)) {
+ atom = idx;
+ } else if (idx < s->first_atom) {
+ atom = JS_DupAtom(s->ctx, idx);
+@@ -34961,7 +35041,7 @@ static int bc_get_atom(BCReaderState *s, JSAtom *patom)
+ if (bc_get_leb128(s, &v))
+ return -1;
+ if (v & 1) {
+- *patom = __JS_AtomFromUInt32(v >> 1);
++ *patom = JS_AtomFromUInt32(v >> 1);
+ return 0;
+ } else {
+ return bc_idx_to_atom(s, patom, v >> 1);
+@@ -35776,7 +35856,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s)
+ if (bc_get_u64(s, &u.u64))
+ return JS_EXCEPTION;
+ bc_read_trace(s, "%g\n", u.d);
+- obj = __JS_NewFloat64(ctx, u.d);
++ obj = JS_NewFloat64Impl(ctx, u.d);
+ }
+ break;
+ case BC_TAG_STRING:
+@@ -36084,7 +36164,7 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj,
+ val = JS_NewInt64(ctx, e->u.i64);
+ break;
+ case JS_DEF_PROP_DOUBLE:
+- val = __JS_NewFloat64(ctx, e->u.f64);
++ val = JS_NewFloat64Impl(ctx, e->u.f64);
+ break;
+ case JS_DEF_PROP_UNDEFINED:
+ val = JS_UNDEFINED;
+@@ -36148,7 +36228,7 @@ int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
+ val = JS_NewInt64(ctx, e->u.i64);
+ break;
+ case JS_DEF_PROP_DOUBLE:
+- val = __JS_NewFloat64(ctx, e->u.f64);
++ val = JS_NewFloat64Impl(ctx, e->u.f64);
+ break;
+ case JS_DEF_OBJECT:
+ val = JS_NewObject(ctx);
+@@ -36381,7 +36461,7 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d,
+ return -1;
+ }
+
+-static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
++static warn_unused int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
+ JSAtom prop, JSValueConst desc,
+ int flags)
+ {
+@@ -36397,7 +36477,7 @@ static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj,
+ return ret;
+ }
+
+-static __exception int JS_ObjectDefineProperties(JSContext *ctx,
++static warn_unused int JS_ObjectDefineProperties(JSContext *ctx,
+ JSValueConst obj,
+ JSValueConst properties)
+ {
+@@ -36882,32 +36962,6 @@ static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val,
+ return JS_NewBool(ctx, ret);
+ }
+
+-static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val,
+- int argc, JSValueConst *argv)
+-{
+- JSValue obj;
+- JSAtom atom;
+- JSObject *p;
+- BOOL ret;
+-
+- obj = JS_ToObject(ctx, argv[0]);
+- if (JS_IsException(obj))
+- return obj;
+- atom = JS_ValueToAtom(ctx, argv[1]);
+- if (unlikely(atom == JS_ATOM_NULL)) {
+- JS_FreeValue(ctx, obj);
+- return JS_EXCEPTION;
+- }
+- p = JS_VALUE_GET_OBJ(obj);
+- ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
+- JS_FreeAtom(ctx, atom);
+- JS_FreeValue(ctx, obj);
+- if (ret < 0)
+- return JS_EXCEPTION;
+- else
+- return JS_NewBool(ctx, ret);
+-}
+-
+ static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+@@ -37447,6 +37501,32 @@ exception:
+ return res;
+ }
+
++static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val,
++ int argc, JSValueConst *argv)
++{
++ JSValue obj;
++ JSAtom atom;
++ JSObject *p;
++ BOOL ret;
++
++ obj = JS_ToObject(ctx, argv[0]);
++ if (JS_IsException(obj))
++ return obj;
++ atom = JS_ValueToAtom(ctx, argv[1]);
++ if (unlikely(atom == JS_ATOM_NULL)) {
++ JS_FreeValue(ctx, obj);
++ return JS_EXCEPTION;
++ }
++ p = JS_VALUE_GET_OBJ(obj);
++ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom);
++ JS_FreeAtom(ctx, atom);
++ JS_FreeValue(ctx, obj);
++ if (ret < 0)
++ return JS_EXCEPTION;
++ else
++ return JS_NewBool(ctx, ret);
++}
++
+ static const JSCFunctionListEntry js_object_funcs[] = {
+ JS_CFUNC_DEF("create", 2, js_object_create ),
+ JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ),
+@@ -37576,7 +37656,7 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target,
+ return JS_EXCEPTION;
+ }
+
+-static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
++static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres,
+ JSValueConst obj)
+ {
+ JSValue len_val;
+@@ -37588,7 +37668,7 @@ static __exception int js_get_length32(JSContext *ctx, uint32_t *pres,
+ return JS_ToUint32Free(ctx, pres, len_val);
+ }
+
+-static __exception int js_get_length64(JSContext *ctx, int64_t *pres,
++static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres,
+ JSValueConst obj)
+ {
+ JSValue len_val;
+@@ -37673,9 +37753,9 @@ static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val,
+ if (!tab)
+ return JS_EXCEPTION;
+ if (magic & 1) {
+- ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab);
++ ret = JS_CallConstructor2(ctx, this_val, this_arg, len, tab);
+ } else {
+- ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab);
++ ret = JS_Call(ctx, this_val, this_arg, len, tab);
+ }
+ free_arg_list(ctx, tab, len);
+ return ret;
+@@ -38889,8 +38969,7 @@ static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val,
+ method = JS_GetProperty(ctx, obj, JS_ATOM_join);
+ if (JS_IsException(method)) {
+ ret = JS_EXCEPTION;
+- } else
+- if (!JS_IsFunction(ctx, method)) {
++ } else if (!JS_IsFunction(ctx, method)) {
+ /* Use intrinsic Object.prototype.toString */
+ JS_FreeValue(ctx, method);
+ ret = js_object_toString(ctx, obj, 0, NULL);
+@@ -39138,8 +39217,7 @@ static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val,
+ if (argc == 0) {
+ item_count = 0;
+ del_count = 0;
+- } else
+- if (argc == 1) {
++ } else if (argc == 1) {
+ item_count = 0;
+ del_count = len - start;
+ } else {
+@@ -39283,8 +39361,8 @@ static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target,
+ if (!JS_IsUndefined(mapperFunction)) {
+ JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source };
+ element = JS_Call(ctx, mapperFunction, thisArg, 3, args);
+- JS_FreeValue(ctx, (JSValue)args[0]);
+- JS_FreeValue(ctx, (JSValue)args[1]);
++ JS_FreeValue(ctx, args[0]);
++ JS_FreeValue(ctx, args[1]);
+ if (JS_IsException(element))
+ return -1;
+ }
+@@ -39915,7 +39993,7 @@ static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val,
+ if (f < 0 || f > 100)
+ return JS_ThrowRangeError(ctx, "invalid number of digits");
+ if (fabs(d) >= 1e21) {
+- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
++ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ } else {
+ return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT);
+ }
+@@ -39936,7 +40014,7 @@ static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val,
+ if (JS_ToInt32Sat(ctx, &f, argv[0]))
+ return JS_EXCEPTION;
+ if (!isfinite(d)) {
+- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
++ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ }
+ if (JS_IsUndefined(argv[0])) {
+ flags = 0;
+@@ -39968,7 +40046,7 @@ static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val,
+ return JS_EXCEPTION;
+ if (!isfinite(d)) {
+ to_string:
+- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d));
++ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d));
+ }
+ if (p < 1 || p > 100)
+ return JS_ThrowRangeError(ctx, "invalid number of digits");
+@@ -40089,11 +40167,11 @@ static int js_string_get_own_property(JSContext *ctx,
+ uint32_t idx, ch;
+
+ /* This is a class exotic method: obj class_id is JS_CLASS_STRING */
+- if (__JS_AtomIsTaggedInt(prop)) {
++ if (JS_AtomIsTaggedInt(prop)) {
+ p = JS_VALUE_GET_OBJ(obj);
+ if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) {
+ p1 = JS_VALUE_GET_STRING(p->u.object_data);
+- idx = __JS_AtomToUInt32(prop);
++ idx = JS_AtomToUInt32(prop);
+ if (idx < p1->len) {
+ if (desc) {
+ if (p1->is_wide_char)
+@@ -40122,8 +40200,8 @@ static int js_string_define_own_property(JSContext *ctx,
+ JSObject *p;
+ JSString *p1, *p2;
+
+- if (__JS_AtomIsTaggedInt(prop)) {
+- idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ idx = JS_AtomToUInt32(prop);
+ p = JS_VALUE_GET_OBJ(this_obj);
+ if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING)
+ goto def;
+@@ -40157,8 +40235,8 @@ static int js_string_delete_property(JSContext *ctx,
+ {
+ uint32_t idx;
+
+- if (__JS_AtomIsTaggedInt(prop)) {
+- idx = __JS_AtomToUInt32(prop);
++ if (JS_AtomIsTaggedInt(prop)) {
++ idx = JS_AtomToUInt32(prop);
+ if (idx < js_string_obj_get_length(ctx, obj)) {
+ return FALSE;
+ }
+@@ -40701,7 +40779,7 @@ static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
+ str = JS_NewString(ctx, "g");
+ if (JS_IsException(str))
+ goto fail;
+- args[args_len++] = (JSValueConst)str;
++ args[args_len++] = str;
+ }
+ rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args);
+ JS_FreeValue(ctx, str);
+@@ -40710,7 +40788,7 @@ static JSValue js_string_match(JSContext *ctx, JSValueConst this_val,
+ JS_FreeValue(ctx, S);
+ return JS_EXCEPTION;
+ }
+- result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S);
++ result = JS_InvokeFree(ctx, rx, atom, 1, &S);
+ JS_FreeValue(ctx, S);
+ return result;
+ }
+@@ -41759,7 +41837,7 @@ static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val,
+ uint32_t tag;
+
+ if (unlikely(argc == 0)) {
+- return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0);
++ return JS_NewFloat64Impl(ctx, is_max ? INFINITY : -INFINITY);
+ }
+
+ tag = JS_VALUE_GET_TAG(argv[0]);
+@@ -41911,9 +41989,13 @@ static uint64_t xorshift64star(uint64_t *pstate)
+
+ static void js_random_init(JSContext *ctx)
+ {
++#ifdef _MSC_VER
++ ctx->random_state = time(NULL);
++#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec;
++#endif
+ /* the state must be non zero */
+ if (ctx->random_state == 0)
+ ctx->random_state = 1;
+@@ -41928,15 +42010,21 @@ static JSValue js_math_random(JSContext *ctx, JSValueConst this_val,
+ v = xorshift64star(&ctx->random_state);
+ /* 1.0 <= u.d < 2 */
+ u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12);
+- return __JS_NewFloat64(ctx, u.d - 1.0);
++ return JS_NewFloat64Impl(ctx, u.d - 1.0);
+ }
+
++// MSVC inexplicably refuses to initialize the array below with
++// these functions, so use wrappers.
++static double floorWrapper(double x) { return floor(x); }
++static double ceilWrapper(double x) { return ceil(x); }
++static double log2Wrapper(double x) { return log2(x); }
++
+ static const JSCFunctionListEntry js_math_funcs[] = {
+ JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ),
+ JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ),
+ JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ),
+- JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ),
+- JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ),
++ JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floorWrapper ),
++ JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceilWrapper ),
+ JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ),
+ JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ),
+
+@@ -41961,7 +42049,7 @@ static const JSCFunctionListEntry js_math_funcs[] = {
+ JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ),
+ JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ),
+ JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ),
+- JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ),
++ JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2Wrapper ),
+ JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ),
+ JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ),
+ JS_CFUNC_DEF("hypot", 2, js_math_hypot ),
+@@ -42004,9 +42092,16 @@ static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv)
+ {
+ int64_t d;
++#ifdef _MSC_VER
++ SYSTEMTIME st;
++ GetSystemTime(&st);
++ SystemTimeToFileTime(&st, (FILETIME *) &d);
++ d /= 10;
++#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
++#endif
+ return JS_NewInt64(ctx, d);
+ }
+
+@@ -43686,7 +43781,7 @@ JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
+ JSParseState s1, *s = &s1;
+ JSValue val = JS_UNDEFINED;
+
+- js_parse_init(ctx, s, buf, buf_len, filename);
++ js_parse_init(ctx, s, buf, buf_len, filename, 1);
+ s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0);
+ if (json_next_token(s))
+ goto fail;
+@@ -43937,7 +44032,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
+ goto exception;
+ }
+ #endif
+- v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val);
++ v = js_array_includes(ctx, jsc->stack, 1, &val);
+ if (JS_IsException(v))
+ goto exception;
+ if (JS_ToBoolFree(ctx, v)) {
+@@ -43958,7 +44053,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
+ sep = JS_DupValue(ctx, jsc->empty);
+ sep1 = JS_DupValue(ctx, jsc->empty);
+ }
+- v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0);
++ v = js_array_push(ctx, jsc->stack, 1, &val, 0);
+ if (check_exception_free(ctx, v))
+ goto exception;
+ ret = JS_IsArray(ctx, val);
+@@ -43998,7 +44093,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
+ if (!JS_IsUndefined(jsc->property_list))
+ tab = JS_DupValue(ctx, jsc->property_list);
+ else
+- tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY);
++ tab = js_object_keys(ctx, JS_UNDEFINED, 1, &val, JS_ITERATOR_KIND_KEY);
+ if (JS_IsException(tab))
+ goto exception;
+ if (js_get_length64(ctx, &len, tab))
+@@ -44146,7 +44241,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
+ continue;
+ }
+ present = js_array_includes(ctx, jsc->property_list,
+- 1, (JSValueConst *)&v);
++ 1, &v);
+ if (JS_IsException(present)) {
+ JS_FreeValue(ctx, v);
+ goto exception;
+@@ -44270,7 +44365,7 @@ static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val,
+ tab = build_arg_list(ctx, &len, array_arg);
+ if (!tab)
+ return JS_EXCEPTION;
+- ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab);
++ ret = JS_CallConstructor2(ctx, func, new_target, len, tab);
+ free_arg_list(ctx, tab, len);
+ return ret;
+ }
+@@ -44475,7 +44570,7 @@ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj)
+ return JS_EXCEPTION;
+ if (JS_IsUndefined(method))
+ return JS_GetPrototype(ctx, s->target);
+- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
++ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return ret;
+ if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL &&
+@@ -44562,7 +44657,7 @@ static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_IsExtensible(ctx, s->target);
+- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
++ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+@@ -44588,7 +44683,7 @@ static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj)
+ return -1;
+ if (JS_IsUndefined(method))
+ return JS_PreventExtensions(ctx, s->target);
+- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
++ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(ret))
+ return -1;
+ res = JS_ToBoolFree(ctx, ret);
+@@ -45072,7 +45167,7 @@ static int js_proxy_get_own_property_names(JSContext *ctx,
+ JS_VALUE_GET_OBJ(s->target),
+ JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK);
+ }
+- prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target);
++ prop_array = JS_CallFree(ctx, method, s->handler, 1, &s->target);
+ if (JS_IsException(prop_array))
+ return -1;
+ tab = NULL;
+@@ -45408,7 +45503,7 @@ static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val,
+ if (JS_IsException(val))
+ return val;
+ /* XXX: use JS_ToStringInternal() with a flags */
+- ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val);
++ ret = js_string_constructor(ctx, JS_UNDEFINED, 1, &val);
+ JS_FreeValue(ctx, val);
+ return ret;
+ }
+@@ -45558,7 +45653,7 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
+ break;
+ }
+ if (is_set) {
+- ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item);
++ ret = JS_Call(ctx, adder, obj, 1, &item);
+ if (JS_IsException(ret)) {
+ JS_FreeValue(ctx, item);
+ goto fail;
+@@ -45729,7 +45824,7 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
+ } else {
+ JS_DupValue(ctx, key);
+ }
+- mr->key = (JSValue)key;
++ mr->key = key;
+ h = map_hash_key(ctx, key) & (s->hash_size - 1);
+ list_add_tail(&mr->hash_link, &s->hash_table[h]);
+ list_add_tail(&mr->link, &s->records);
+@@ -45951,7 +46046,7 @@ static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val,
+ args[0] = args[1];
+ else
+ args[0] = JS_DupValue(ctx, mr->value);
+- args[2] = (JSValue)this_val;
++ args[2] = this_val;
+ ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args);
+ JS_FreeValue(ctx, args[0]);
+ if (!magic)
+@@ -46343,7 +46438,7 @@ static JSValue promise_reaction_job(JSContext *ctx, int argc,
+ functions */
+ if (!JS_IsUndefined(func)) {
+ res2 = JS_Call(ctx, func, JS_UNDEFINED,
+- 1, (JSValueConst *)&res);
++ 1, &res);
+ } else {
+ res2 = JS_UNDEFINED;
+ }
+@@ -46426,7 +46521,7 @@ static JSValue js_promise_resolve_thenable_job(JSContext *ctx,
+ res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args);
+ if (JS_IsException(res)) {
+ JSValue error = JS_GetException(ctx);
+- res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
++ res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ }
+ JS_FreeValue(ctx, args[0]);
+@@ -46626,7 +46721,7 @@ static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target,
+ if (JS_IsException(ret)) {
+ JSValue ret2, error;
+ error = JS_GetException(ctx);
+- ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error);
++ ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret2))
+ goto fail1;
+@@ -46683,10 +46778,10 @@ static JSValue js_new_promise_capability(JSContext *ctx,
+
+ if (JS_IsUndefined(ctor)) {
+ result_promise = js_promise_constructor(ctx, ctor, 1,
+- (JSValueConst *)&executor);
++ &executor);
+ } else {
+ result_promise = JS_CallConstructor(ctx, ctor, 1,
+- (JSValueConst *)&executor);
++ &executor);
+ }
+ if (JS_IsException(result_promise))
+ goto fail;
+@@ -46770,7 +46865,7 @@ static JSValue js_promise___newPromiseCapability(JSContext *ctx,
+ }
+ #endif
+
+-static __exception int remainingElementsCount_add(JSContext *ctx,
++static warn_unused int remainingElementsCount_add(JSContext *ctx,
+ JSValueConst resolve_element_env,
+ int addend)
+ {
+@@ -46851,10 +46946,10 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx,
+ error = js_aggregate_error_constructor(ctx, values);
+ if (JS_IsException(error))
+ return JS_EXCEPTION;
+- ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error);
++ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &error);
+ JS_FreeValue(ctx, error);
+ } else {
+- ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values);
++ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &values);
+ }
+ if (JS_IsException(ret))
+ return ret;
+@@ -46890,7 +46985,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
+ fail_reject:
+ error = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
+- (JSValueConst *)&error);
++ &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret))
+ goto fail;
+@@ -46921,7 +47016,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
+ if (done)
+ break;
+ next_promise = JS_Call(ctx, promise_resolve,
+- this_val, 1, (JSValueConst *)&item);
++ this_val, 1, &item);
+ JS_FreeValue(ctx, item);
+ if (JS_IsException(next_promise)) {
+ fail_reject1:
+@@ -46929,7 +47024,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
+ goto fail_reject;
+ }
+ resolve_element_data[0] = JS_NewBool(ctx, FALSE);
+- resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index);
++ resolve_element_data[1] = JS_NewInt32(ctx, index);
+ resolve_element_data[2] = values;
+ resolve_element_data[3] = resolving_funcs[is_promise_any];
+ resolve_element_data[4] = resolve_element_env;
+@@ -46989,7 +47084,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val,
+ values = error;
+ }
+ ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED,
+- 1, (JSValueConst *)&values);
++ 1, &values);
+ if (check_exception_free(ctx, ret))
+ goto fail_reject;
+ }
+@@ -47032,7 +47127,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
+ fail_reject:
+ error = JS_GetException(ctx);
+ ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1,
+- (JSValueConst *)&error);
++ &error);
+ JS_FreeValue(ctx, error);
+ if (JS_IsException(ret))
+ goto fail;
+@@ -47051,7 +47146,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
+ if (done)
+ break;
+ next_promise = JS_Call(ctx, promise_resolve,
+- this_val, 1, (JSValueConst *)&item);
++ this_val, 1, &item);
+ JS_FreeValue(ctx, item);
+ if (JS_IsException(next_promise)) {
+ fail_reject1:
+@@ -47078,7 +47173,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val,
+ goto done;
+ }
+
+-static __exception int perform_promise_then(JSContext *ctx,
++static warn_unused int perform_promise_then(JSContext *ctx,
+ JSValueConst promise,
+ JSValueConst *resolve_reject,
+ JSValueConst *cap_resolving_funcs)
+@@ -47196,7 +47291,7 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va
+ res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL);
+ if (JS_IsException(res))
+ return res;
+- promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0);
++ promise = js_promise_resolve(ctx, ctor, 1, &res, 0);
+ JS_FreeValue(ctx, res);
+ if (JS_IsException(promise))
+ return promise;
+@@ -47211,7 +47306,7 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va
+ JS_FreeValue(ctx, promise);
+ return then_func;
+ }
+- ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func);
++ ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, &then_func);
+ JS_FreeValue(ctx, then_func);
+ return ret;
+ }
+@@ -47288,7 +47383,7 @@ static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx,
+ {
+ JSValueConst func_data[1];
+
+- func_data[0] = (JSValueConst)JS_NewBool(ctx, done);
++ func_data[0] = JS_NewBool(ctx, done);
+ return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap,
+ 1, 0, 1, func_data);
+ }
+@@ -47411,7 +47506,7 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi
+ is_reject = 1;
+ done_resolve:
+ res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED,
+- 1, (JSValueConst *)&err);
++ 1, &err);
+ JS_FreeValue(ctx, err);
+ JS_FreeValue(ctx, res2);
+ JS_FreeValue(ctx, resolving_funcs[0]);
+@@ -47423,7 +47518,7 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi
+ int res;
+
+ value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor,
+- 1, (JSValueConst *)&value, 0);
++ 1, &value, 0);
+ if (JS_IsException(value_wrapper_promise)) {
+ JS_FreeValue(ctx, value);
+ goto reject;
+@@ -47593,7 +47688,7 @@ static int isURIReserved(int c) {
+ return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL;
+ }
+
+-static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
++static int FORMAT_ATTR(2, 3) js_throw_URIError(JSContext *ctx, const char *fmt, ...)
+ {
+ va_list ap;
+
+@@ -47894,7 +47989,7 @@ static int64_t floor_div(int64_t a, int64_t b) {
+ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+
+-static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
++static warn_unused int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val)
+ {
+ if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) {
+ JSObject *p = JS_VALUE_GET_OBJ(this_val);
+@@ -47954,7 +48049,7 @@ static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
+ static char const day_names[] = "SunMonTueWedThuFriSat";
+
+-static __exception int get_date_fields(JSContext *ctx, JSValueConst obj,
++static warn_unused int get_date_fields(JSContext *ctx, JSValueConst obj,
+ double fields[9], int is_local, int force)
+ {
+ double dval;
+@@ -48016,7 +48111,7 @@ static double time_clip(double t) {
+
+ /* The spec mandates the use of 'double' and it fixes the order
+ of the operations */
+-static double set_date_fields(double fields[], int is_local) {
++static double set_date_fields(const double fields[], int is_local) {
+ int64_t y;
+ double days, d, h, m1;
+ int i, m, md;
+@@ -48213,9 +48308,17 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val,
+
+ /* OS dependent: return the UTC time in ms since 1970. */
+ static int64_t date_now(void) {
++#ifdef _MSC_VER
++ SYSTEMTIME st;
++ GetSystemTime(&st);
++ int64_t d;
++ SystemTimeToFileTime(&st, (FILETIME *) &d);
++ return d /= 10000;
++#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
++#endif
+ }
+
+ static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
+@@ -48246,7 +48349,7 @@ static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target,
+ }
+ v = JS_ToPrimitive(ctx, argv[0], HINT_NONE);
+ if (JS_IsString(v)) {
+- dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v);
++ dv = js_Date_parse(ctx, JS_UNDEFINED, 1, &v);
+ JS_FreeValue(ctx, v);
+ if (JS_IsException(dv))
+ return JS_EXCEPTION;
+@@ -48814,7 +48917,7 @@ void JS_AddIntrinsicDate(JSContext *ctx)
+
+ void JS_AddIntrinsicEval(JSContext *ctx)
+ {
+- ctx->eval_internal = __JS_EvalInternal;
++ ctx->eval_internal = JS_EvalInternalImpl;
+ }
+
+ #ifdef CONFIG_BIGNUM
+@@ -50934,7 +51037,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
+ JS_PROP_HAS_GET | JS_PROP_HAS_SET |
+ JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE);
+ JS_FreeValue(ctx, obj1);
+- JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1));
++ JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, &ctx->throw_type_error, 1));
+
+ ctx->global_obj = JS_NewObject(ctx);
+ ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL);
+@@ -52090,7 +52193,7 @@ exception:
+
+ #define special_indexOf 0
+ #define special_lastIndexOf 1
+-#define special_includes -1
++#define special_includes (-1)
+
+ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int special)
+@@ -52157,8 +52260,7 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val,
+ is_int = 1;
+ v64 = JS_VALUE_GET_INT(argv[0]);
+ d = v64;
+- } else
+- if (tag == JS_TAG_FLOAT64) {
++ } else if (tag == JS_TAG_FLOAT64) {
+ d = JS_VALUE_GET_FLOAT64(argv[0]);
+ v64 = d;
+ is_int = (v64 == d);
+@@ -52659,11 +52761,11 @@ static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) {
+ #endif
+
+ static JSValue js_TA_get_float32(JSContext *ctx, const void *a) {
+- return __JS_NewFloat64(ctx, *(const float *)a);
++ return JS_NewFloat64Impl(ctx, *(const float *)a);
+ }
+
+ static JSValue js_TA_get_float64(JSContext *ctx, const void *a) {
+- return __JS_NewFloat64(ctx, *(const double *)a);
++ return JS_NewFloat64Impl(ctx, *(const double *)a);
+ }
+
+ struct TA_sort_context {
+@@ -52717,8 +52819,8 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) {
+ psc->exception = 1;
+ }
+ done:
+- JS_FreeValue(ctx, (JSValue)argv[0]);
+- JS_FreeValue(ctx, (JSValue)argv[1]);
++ JS_FreeValue(ctx, argv[0]);
++ JS_FreeValue(ctx, argv[1]);
+ }
+ return cmp;
+ }
+@@ -53285,7 +53387,7 @@ static JSValue js_dataview_getValue(JSContext *ctx,
+ case JS_CLASS_INT8_ARRAY:
+ return JS_NewInt32(ctx, *(int8_t *)ptr);
+ case JS_CLASS_UINT8_ARRAY:
+- return JS_NewInt32(ctx, *(uint8_t *)ptr);
++ return JS_NewInt32(ctx, *ptr);
+ case JS_CLASS_INT16_ARRAY:
+ v = get_u16(ptr);
+ if (is_swap)
+@@ -53336,7 +53438,7 @@ static JSValue js_dataview_getValue(JSContext *ctx,
+ if (is_swap)
+ v = bswap32(v);
+ u.i = v;
+- return __JS_NewFloat64(ctx, u.f);
++ return JS_NewFloat64Impl(ctx, u.f);
+ }
+ case JS_CLASS_FLOAT64_ARRAY:
+ {
+@@ -53347,7 +53449,7 @@ static JSValue js_dataview_getValue(JSContext *ctx,
+ u.i = get_u64(ptr);
+ if (is_swap)
+ u.i = bswap64(u.i);
+- return __JS_NewFloat64(ctx, u.f);
++ return JS_NewFloat64Impl(ctx, u.f);
+ }
+ default:
+ abort();
+@@ -54059,3 +54161,155 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx)
+ JS_AddIntrinsicAtomics(ctx);
+ #endif
+ }
++
++#ifndef NDEBUG
++static void *watchedRefCount = NULL;
++
++void notifyRefCountIncrease(JSRefCountHeader *p)
++{
++ if (p == watchedRefCount)
++ fprintf(stderr, "increasing ref count %d for %p\n", p->ref_count, watchedRefCount);
++}
++
++void notifyRefCountDecrease(JSRefCountHeader *p)
++{
++ if (p == watchedRefCount)
++ fprintf(stderr, "decreasing ref count %d for %p\n", p->ref_count, watchedRefCount);
++}
++
++void watchRefCount(void *p)
++{
++ watchedRefCount = p;
++}
++#endif
++
++void setScopeLookup(JSContext *ctx, ScopeLookup *scopeLookup)
++{
++ ctx->scopeLookup = scopeLookup;
++}
++
++void setFoundUndefinedHandler(JSContext *ctx, FoundUndefinedHandler *handler)
++{
++ ctx->handleUndefined = handler;
++}
++
++void setFunctionEnteredHandler(JSContext *ctx, FunctionEnteredHandler *handler)
++{
++ ctx->handleFunctionEntered = handler;
++}
++
++void setFunctionExitedHandler(JSContext *ctx, FunctionExitedHandler *handler)
++{
++ ctx->handleFunctionExited = handler;
++}
++
++int isSimpleValue(JSValue v)
++{
++ JSObject *p;
++ if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT)
++ return 1;
++ p = JS_VALUE_GET_OBJ(v);
++ return p->class_id >= JS_CLASS_OBJECT && p->class_id <= JS_CLASS_BOOLEAN;
++}
++
++JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func,
++ const char *name, int length,
++ JSCFunctionEnum cproto, int magic)
++{
++ return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto,
++ magic);
++}
++
++JSValue mkVal(int32_t tag, int32_t val)
++{
++ return (JSValue){ (JSValueUnion){ .int32 = val }, tag };
++}
++
++JSValue mkPtr(int32_t tag, void *p)
++{
++ return (JSValue){ (JSValueUnion){ .ptr = p }, tag };
++}
++
++void JS_FreeValue(JSContext *ctx, JSValue v) {
++ if (JS_VALUE_HAS_REF_COUNT(v)) {
++ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
++#ifndef NDEBUG
++ notifyRefCountDecrease(p);
++#endif
++ if (--p->ref_count <= 0) {
++ JS_FreeValueImpl(ctx, v);
++ }
++ }
++}
++void JS_FreeValueRT(JSRuntime *rt, JSValue v) {
++ if (JS_VALUE_HAS_REF_COUNT(v)) {
++ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
++#ifndef NDEBUG
++ notifyRefCountDecrease(p);
++#endif
++ if (--p->ref_count <= 0) {
++ JS_FreeValueRTImpl(rt, v);
++ }
++ }
++}
++JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) {
++ (void)ctx;
++ return JS_MKVAL(JS_TAG_BOOL, (val != 0));
++}
++JSValue JS_NewInt32(JSContext *ctx, int32_t val) {
++ (void)ctx;
++ return JS_MKVAL(JS_TAG_INT, val);
++}
++JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) {
++ (void)ctx;
++ return JS_MKVAL(JS_TAG_CATCH_OFFSET, val);
++}
++JSValue JS_NewInt64(JSContext *ctx, int64_t val) {
++ JSValue v;
++ if (val == (int32_t)val) {
++ v = JS_NewInt32(ctx, (int32_t)val);
++ } else {
++ v = JS_NewFloat64Impl(ctx, (double)val);
++ }
++ return v;
++}
++JSValue JS_NewUint32(JSContext *ctx, uint32_t val) {
++ JSValue v;
++ if (val <= 0x7fffffff) {
++ v = JS_NewInt32(ctx, val);
++ } else {
++ v = JS_NewFloat64Impl(ctx, val);
++ }
++ return v;
++}
++JSValue JS_NewFloat64(JSContext *ctx, double d) {
++ JSValue v;
++ int32_t val;
++ union {
++ double d;
++ uint64_t u;
++ } u, t;
++ u.d = d;
++ val = (int32_t)d;
++ t.d = val;
++ /* -0 cannot be represented as integer, so we compare the bit
++ representation */
++ if (u.u == t.u) {
++ v = JS_MKVAL(JS_TAG_INT, val);
++ } else {
++ v = JS_NewFloat64Impl(ctx, d);
++ }
++ return v;
++}
++JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
++ int length) {
++ return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0);
++}
++
++JS_BOOL JS_IsRegExp(JSContext *ctx, JSValue val)
++{
++ JSObject *p;
++ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT)
++ return FALSE;
++ return JS_VALUE_GET_OBJ(val)->class_id == JS_CLASS_REGEXP;
++}
+diff --git a/quickjs.h b/quickjs.h
+index d4a5cd3..a5adf0c 100644
+--- a/quickjs.h
++++ b/quickjs.h
+@@ -215,15 +215,19 @@ typedef struct JSValue {
+ #define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
+ #define JS_VALUE_GET_PTR(v) ((v).u.ptr)
+
+-#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag }
+-#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag }
++JSValue mkVal(int32_t tag, int32_t val);
++JSValue mkPtr(int32_t tag, void *p);
++
++#define JS_MKVAL(tag, val) mkVal(tag, val)
++#define JS_MKPTR(tag, p) mkPtr(tag, p)
+
+ #define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
+
+ #define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
+
+-static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
++static inline JSValue JS_NewFloat64Impl(JSContext *ctx, double d)
+ {
++ (void) ctx;
+ JSValue v;
+ v.tag = JS_TAG_FLOAT64;
+ v.u.float64 = d;
+@@ -502,66 +506,14 @@ int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
+
+ /* value handling */
+
+-static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val)
+-{
+- return JS_MKVAL(JS_TAG_BOOL, (val != 0));
+-}
+-
+-static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val)
+-{
+- return JS_MKVAL(JS_TAG_INT, val);
+-}
+-
+-static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val)
+-{
+- return JS_MKVAL(JS_TAG_CATCH_OFFSET, val);
+-}
+-
+-static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val)
+-{
+- JSValue v;
+- if (val == (int32_t)val) {
+- v = JS_NewInt32(ctx, val);
+- } else {
+- v = __JS_NewFloat64(ctx, val);
+- }
+- return v;
+-}
+-
+-static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val)
+-{
+- JSValue v;
+- if (val <= 0x7fffffff) {
+- v = JS_NewInt32(ctx, val);
+- } else {
+- v = __JS_NewFloat64(ctx, val);
+- }
+- return v;
+-}
+-
++JSValue JS_NewBool(JSContext *ctx, JS_BOOL val);
++JSValue JS_NewInt32(JSContext *ctx, int32_t val);
++JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val);
++JSValue JS_NewInt64(JSContext *ctx, int64_t val);
++JSValue JS_NewUint32(JSContext *ctx, uint32_t val);
+ JSValue JS_NewBigInt64(JSContext *ctx, int64_t v);
+ JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v);
+-
+-static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d)
+-{
+- JSValue v;
+- int32_t val;
+- union {
+- double d;
+- uint64_t u;
+- } u, t;
+- u.d = d;
+- val = (int32_t)d;
+- t.d = val;
+- /* -0 cannot be represented as integer, so we compare the bit
+- representation */
+- if (u.u == t.u) {
+- v = JS_MKVAL(JS_TAG_INT, val);
+- } else {
+- v = __JS_NewFloat64(ctx, d);
+- }
+- return v;
+-}
++JSValue JS_NewFloat64(JSContext *ctx, double d);
+
+ static inline JS_BOOL JS_IsNumber(JSValueConst v)
+ {
+@@ -571,6 +523,7 @@ static inline JS_BOOL JS_IsNumber(JSValueConst v)
+
+ static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)
+ {
++ (void) ctx;
+ int tag = JS_VALUE_GET_TAG(v);
+ return tag == JS_TAG_BIG_INT;
+ }
+@@ -639,43 +592,38 @@ JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fm
+ JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
+ JSValue JS_ThrowOutOfMemory(JSContext *ctx);
+
+-void __JS_FreeValue(JSContext *ctx, JSValue v);
+-static inline void JS_FreeValue(JSContext *ctx, JSValue v)
+-{
+- if (JS_VALUE_HAS_REF_COUNT(v)) {
+- JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+- if (--p->ref_count <= 0) {
+- __JS_FreeValue(ctx, v);
+- }
+- }
+-}
+-void __JS_FreeValueRT(JSRuntime *rt, JSValue v);
+-static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v)
+-{
+- if (JS_VALUE_HAS_REF_COUNT(v)) {
+- JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+- if (--p->ref_count <= 0) {
+- __JS_FreeValueRT(rt, v);
+- }
+- }
+-}
++#ifndef NDEBUG
++void notifyRefCountIncrease(JSRefCountHeader *p);
++void notifyRefCountDecrease(JSRefCountHeader *p);
++#endif
++
++void JS_FreeValue(JSContext *ctx, JSValue v);
++void JS_FreeValueRT(JSRuntime *rt, JSValue v);
+
+ static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
+ {
++ (void) ctx;
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
++#ifndef NDEBUG
++ notifyRefCountIncrease(p);
++#endif
+ p->ref_count++;
+ }
+- return (JSValue)v;
++ return v;
+ }
+
+ static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
+ {
++ (void) rt;
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
++#ifndef NDEBUG
++ notifyRefCountIncrease(p);
++#endif
+ p->ref_count++;
+ }
+- return (JSValue)v;
++ return v;
+ }
+
+ int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */
+@@ -714,6 +662,7 @@ JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto);
+ JSValue JS_NewObject(JSContext *ctx);
+
+ JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val);
++JS_BOOL JS_IsRegExp(JSContext* ctx, JSValueConst val);
+ JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val);
+ JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val);
+
+@@ -783,7 +732,7 @@ JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
+ /* same as JS_Eval() but with an explicit 'this_obj' parameter */
+ JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+- const char *filename, int eval_flags);
++ const char *filename, int line, int eval_flags);
+ JSValue JS_GetGlobalObject(JSContext *ctx);
+ int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj);
+ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+@@ -945,18 +894,11 @@ JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
+ int length, int magic, int data_len,
+ JSValueConst *data);
+
+-static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
+- int length)
+-{
+- return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0);
+-}
++JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
++ int length);
+
+-static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func,
+- const char *name,
+- int length, JSCFunctionEnum cproto, int magic)
+-{
+- return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic);
+-}
++JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func,
++ const char *name, int length, JSCFunctionEnum cproto, int magic);
+ void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst proto);
+
+@@ -1039,6 +981,35 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
+ int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
+ const JSCFunctionListEntry *tab, int len);
+
++
++/* Qbs extensions */
++struct LookupResult
++{
++ JSValue value;
++ JSValue scope;
++ int useResult;
++};
++typedef struct LookupResult ScopeLookup(JSContext *ctx, JSAtom prop);
++void setScopeLookup(JSContext *ctx, ScopeLookup *scopeLookup);
++
++// Alternative: Request with throw in script engine
++typedef void FoundUndefinedHandler(JSContext *ctx);
++void setFoundUndefinedHandler(JSContext *ctx, FoundUndefinedHandler *handler);
++
++typedef void FunctionEnteredHandler(JSContext *ctx, JSValue this_val);
++typedef void FunctionExitedHandler(JSContext *ctx);
++void setFunctionEnteredHandler(JSContext *ctx, FunctionEnteredHandler *handler);
++void setFunctionExitedHandler(JSContext *ctx, FunctionExitedHandler *handler);
++int isSimpleValue(JSValue v);
++
++#ifndef NDEBUG
++void watchRefCount(void *p);
++#endif
++
++void build_backtrace(JSContext *ctx, JSValueConst error_obj,
++ const char *filename, int line_num,
++ int backtrace_flags);
++
+ #undef js_unlikely
+ #undef js_force_inline
+
diff --git a/src/shared/quickjs/quickjs.h b/src/shared/quickjs/quickjs.h
new file mode 100644
index 000000000..aa4a74c9c
--- /dev/null
+++ b/src/shared/quickjs/quickjs.h
@@ -0,0 +1,1027 @@
+/*
+ * QuickJS Javascript Engine
+ *
+ * Copyright (c) 2017-2021 Fabrice Bellard
+ * Copyright (c) 2017-2021 Charlie Gordon
+ *
+ * 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 QUICKJS_H
+#define QUICKJS_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define js_likely(x) __builtin_expect(!!(x), 1)
+#define js_unlikely(x) __builtin_expect(!!(x), 0)
+#define js_force_inline inline __attribute__((always_inline))
+#define __js_printf_like(f, a) __attribute__((format(printf, f, a)))
+#else
+#define js_likely(x) (x)
+#define js_unlikely(x) (x)
+#define js_force_inline inline
+#define __js_printf_like(a, b)
+#endif
+
+#define JS_BOOL int
+
+typedef struct JSRuntime JSRuntime;
+typedef struct JSContext JSContext;
+typedef struct JSObject JSObject;
+typedef struct JSClass JSClass;
+typedef uint32_t JSClassID;
+typedef uint32_t JSAtom;
+
+#if INTPTR_MAX >= INT64_MAX
+#define JS_PTR64
+#define JS_PTR64_DEF(a) a
+#else
+#define JS_PTR64_DEF(a)
+#endif
+
+#ifndef JS_PTR64
+#define JS_NAN_BOXING
+#endif
+
+enum {
+ /* all tags with a reference count are negative */
+ JS_TAG_FIRST = -11, /* first negative tag */
+ JS_TAG_BIG_DECIMAL = -11,
+ JS_TAG_BIG_INT = -10,
+ JS_TAG_BIG_FLOAT = -9,
+ JS_TAG_SYMBOL = -8,
+ JS_TAG_STRING = -7,
+ JS_TAG_MODULE = -3, /* used internally */
+ JS_TAG_FUNCTION_BYTECODE = -2, /* used internally */
+ JS_TAG_OBJECT = -1,
+
+ JS_TAG_INT = 0,
+ JS_TAG_BOOL = 1,
+ JS_TAG_NULL = 2,
+ JS_TAG_UNDEFINED = 3,
+ JS_TAG_UNINITIALIZED = 4,
+ JS_TAG_CATCH_OFFSET = 5,
+ JS_TAG_EXCEPTION = 6,
+ JS_TAG_FLOAT64 = 7,
+ /* any larger tag is FLOAT64 if JS_NAN_BOXING */
+};
+
+typedef struct JSRefCountHeader {
+ int ref_count;
+} JSRefCountHeader;
+
+#define JS_FLOAT64_NAN NAN
+
+#ifdef CONFIG_CHECK_JSVALUE
+/* JSValue consistency : it is not possible to run the code in this
+ mode, but it is useful to detect simple reference counting
+ errors. It would be interesting to modify a static C analyzer to
+ handle specific annotations (clang has such annotations but only
+ for objective C) */
+typedef struct __JSValue *JSValue;
+typedef const struct __JSValue *JSValueConst;
+
+#define JS_VALUE_GET_TAG(v) (int)((uintptr_t)(v) & 0xf)
+/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
+#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v)
+#define JS_VALUE_GET_INT(v) (int)((intptr_t)(v) >> 4)
+#define JS_VALUE_GET_BOOL(v) JS_VALUE_GET_INT(v)
+#define JS_VALUE_GET_FLOAT64(v) (double)JS_VALUE_GET_INT(v)
+#define JS_VALUE_GET_PTR(v) (void *)((intptr_t)(v) & ~0xf)
+
+#define JS_MKVAL(tag, val) (JSValue)(intptr_t)(((val) << 4) | (tag))
+#define JS_MKPTR(tag, p) (JSValue)((intptr_t)(p) | (tag))
+
+#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
+
+#define JS_NAN JS_MKVAL(JS_TAG_FLOAT64, 1)
+
+static inline JSValue __JS_NewFloat64(JSContext *ctx, double d)
+{
+ return JS_MKVAL(JS_TAG_FLOAT64, (int)d);
+}
+
+static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
+{
+ return 0;
+}
+
+#elif defined(JS_NAN_BOXING)
+
+typedef uint64_t JSValue;
+
+#define JSValueConst JSValue
+
+#define JS_VALUE_GET_TAG(v) (int)((v) >> 32)
+#define JS_VALUE_GET_INT(v) (int)(v)
+#define JS_VALUE_GET_BOOL(v) (int)(v)
+#define JS_VALUE_GET_PTR(v) (void *)(intptr_t)(v)
+
+#define JS_MKVAL(tag, val) (((uint64_t)(tag) << 32) | (uint32_t)(val))
+#define JS_MKPTR(tag, ptr) (((uint64_t)(tag) << 32) | (uintptr_t)(ptr))
+
+#define JS_FLOAT64_TAG_ADDEND (0x7ff80000 - JS_TAG_FIRST + 1) /* quiet NaN encoding */
+
+static inline double JS_VALUE_GET_FLOAT64(JSValue v)
+{
+ union {
+ JSValue v;
+ double d;
+ } u;
+ u.v = v;
+ u.v += (uint64_t)JS_FLOAT64_TAG_ADDEND << 32;
+ return u.d;
+}
+
+#define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32))
+
+static inline JSValue JS_NewFloat64Impl(JSContext *ctx, double d)
+{
+ (void) ctx;
+ union {
+ double d;
+ uint64_t u64;
+ } u;
+ JSValue v;
+ u.d = d;
+ /* normalize NaN */
+ if (js_unlikely((u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000))
+ v = JS_NAN;
+ else
+ v = u.u64 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32);
+ return v;
+}
+
+#define JS_TAG_IS_FLOAT64(tag) ((unsigned)((tag) - JS_TAG_FIRST) >= (JS_TAG_FLOAT64 - JS_TAG_FIRST))
+
+/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
+static inline int JS_VALUE_GET_NORM_TAG(JSValue v)
+{
+ uint32_t tag;
+ tag = JS_VALUE_GET_TAG(v);
+ if (JS_TAG_IS_FLOAT64(tag))
+ return JS_TAG_FLOAT64;
+ else
+ return tag;
+}
+
+static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
+{
+ uint32_t tag;
+ tag = JS_VALUE_GET_TAG(v);
+ return tag == (JS_NAN >> 32);
+}
+
+#else /* !JS_NAN_BOXING */
+
+typedef union JSValueUnion {
+ int32_t int32;
+ double float64;
+ void *ptr;
+} JSValueUnion;
+
+typedef struct JSValue {
+ JSValueUnion u;
+ int64_t tag;
+} JSValue;
+
+#define JSValueConst JSValue
+
+#define JS_VALUE_GET_TAG(v) ((int32_t)(v).tag)
+/* same as JS_VALUE_GET_TAG, but return JS_TAG_FLOAT64 with NaN boxing */
+#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG(v)
+#define JS_VALUE_GET_INT(v) ((v).u.int32)
+#define JS_VALUE_GET_BOOL(v) ((v).u.int32)
+#define JS_VALUE_GET_FLOAT64(v) ((v).u.float64)
+#define JS_VALUE_GET_PTR(v) ((v).u.ptr)
+
+JSValue mkVal(int32_t tag, int32_t val);
+JSValue mkPtr(int32_t tag, void *p);
+
+#define JS_MKVAL(tag, val) mkVal(tag, val)
+#define JS_MKPTR(tag, p) mkPtr(tag, p)
+
+#define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64)
+
+#define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 }
+
+static inline JSValue JS_NewFloat64Impl(JSContext *ctx, double d)
+{
+ (void) ctx;
+ JSValue v;
+ v.tag = JS_TAG_FLOAT64;
+ v.u.float64 = d;
+ return v;
+}
+
+static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v)
+{
+ union {
+ double d;
+ uint64_t u64;
+ } u;
+ if (v.tag != JS_TAG_FLOAT64)
+ return 0;
+ u.d = v.u.float64;
+ return (u.u64 & 0x7fffffffffffffff) > 0x7ff0000000000000;
+}
+
+#endif /* !JS_NAN_BOXING */
+
+#define JS_VALUE_IS_BOTH_INT(v1, v2) ((JS_VALUE_GET_TAG(v1) | JS_VALUE_GET_TAG(v2)) == 0)
+#define JS_VALUE_IS_BOTH_FLOAT(v1, v2) (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v1)) && JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(v2)))
+
+#define JS_VALUE_GET_OBJ(v) ((JSObject *)JS_VALUE_GET_PTR(v))
+#define JS_VALUE_GET_STRING(v) ((JSString *)JS_VALUE_GET_PTR(v))
+#define JS_VALUE_HAS_REF_COUNT(v) ((unsigned)JS_VALUE_GET_TAG(v) >= (unsigned)JS_TAG_FIRST)
+
+/* special values */
+#define JS_NULL JS_MKVAL(JS_TAG_NULL, 0)
+#define JS_UNDEFINED JS_MKVAL(JS_TAG_UNDEFINED, 0)
+#define JS_FALSE JS_MKVAL(JS_TAG_BOOL, 0)
+#define JS_TRUE JS_MKVAL(JS_TAG_BOOL, 1)
+#define JS_EXCEPTION JS_MKVAL(JS_TAG_EXCEPTION, 0)
+#define JS_UNINITIALIZED JS_MKVAL(JS_TAG_UNINITIALIZED, 0)
+
+/* flags for object properties */
+#define JS_PROP_CONFIGURABLE (1 << 0)
+#define JS_PROP_WRITABLE (1 << 1)
+#define JS_PROP_ENUMERABLE (1 << 2)
+#define JS_PROP_C_W_E (JS_PROP_CONFIGURABLE | JS_PROP_WRITABLE | JS_PROP_ENUMERABLE)
+#define JS_PROP_LENGTH (1 << 3) /* used internally in Arrays */
+#define JS_PROP_TMASK (3 << 4) /* mask for NORMAL, GETSET, VARREF, AUTOINIT */
+#define JS_PROP_NORMAL (0 << 4)
+#define JS_PROP_GETSET (1 << 4)
+#define JS_PROP_VARREF (2 << 4) /* used internally */
+#define JS_PROP_AUTOINIT (3 << 4) /* used internally */
+
+/* flags for JS_DefineProperty */
+#define JS_PROP_HAS_SHIFT 8
+#define JS_PROP_HAS_CONFIGURABLE (1 << 8)
+#define JS_PROP_HAS_WRITABLE (1 << 9)
+#define JS_PROP_HAS_ENUMERABLE (1 << 10)
+#define JS_PROP_HAS_GET (1 << 11)
+#define JS_PROP_HAS_SET (1 << 12)
+#define JS_PROP_HAS_VALUE (1 << 13)
+
+/* throw an exception if false would be returned
+ (JS_DefineProperty/JS_SetProperty) */
+#define JS_PROP_THROW (1 << 14)
+/* throw an exception if false would be returned in strict mode
+ (JS_SetProperty) */
+#define JS_PROP_THROW_STRICT (1 << 15)
+
+#define JS_PROP_NO_ADD (1 << 16) /* internal use */
+#define JS_PROP_NO_EXOTIC (1 << 17) /* internal use */
+
+#define JS_DEFAULT_STACK_SIZE (256 * 1024)
+
+/* JS_Eval() flags */
+#define JS_EVAL_TYPE_GLOBAL (0 << 0) /* global code (default) */
+#define JS_EVAL_TYPE_MODULE (1 << 0) /* module code */
+#define JS_EVAL_TYPE_DIRECT (2 << 0) /* direct call (internal use) */
+#define JS_EVAL_TYPE_INDIRECT (3 << 0) /* indirect call (internal use) */
+#define JS_EVAL_TYPE_MASK (3 << 0)
+
+#define JS_EVAL_FLAG_STRICT (1 << 3) /* force 'strict' mode */
+#define JS_EVAL_FLAG_STRIP (1 << 4) /* force 'strip' mode */
+/* compile but do not run. The result is an object with a
+ JS_TAG_FUNCTION_BYTECODE or JS_TAG_MODULE tag. It can be executed
+ with JS_EvalFunction(). */
+#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)
+/* don't include the stack frames before this eval in the Error() backtraces */
+#define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6)
+
+typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
+typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
+typedef JSValue JSCFunctionData(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic, JSValue *func_data);
+
+typedef struct JSMallocState {
+ size_t malloc_count;
+ size_t malloc_size;
+ size_t malloc_limit;
+ void *opaque; /* user opaque */
+} JSMallocState;
+
+typedef struct JSMallocFunctions {
+ void *(*js_malloc)(JSMallocState *s, size_t size);
+ void (*js_free)(JSMallocState *s, void *ptr);
+ void *(*js_realloc)(JSMallocState *s, void *ptr, size_t size);
+ size_t (*js_malloc_usable_size)(const void *ptr);
+} JSMallocFunctions;
+
+typedef struct JSGCObjectHeader JSGCObjectHeader;
+
+JSRuntime *JS_NewRuntime(void);
+/* info lifetime must exceed that of rt */
+void JS_SetRuntimeInfo(JSRuntime *rt, const char *info);
+void JS_SetMemoryLimit(JSRuntime *rt, size_t limit);
+void JS_SetGCThreshold(JSRuntime *rt, size_t gc_threshold);
+/* use 0 to disable maximum stack size check */
+void JS_SetMaxStackSize(JSRuntime *rt, size_t stack_size);
+/* should be called when changing thread to update the stack top value
+ used to check stack overflow. */
+void JS_UpdateStackTop(JSRuntime *rt);
+JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque);
+void JS_FreeRuntime(JSRuntime *rt);
+void *JS_GetRuntimeOpaque(JSRuntime *rt);
+void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque);
+typedef void JS_MarkFunc(JSRuntime *rt, JSGCObjectHeader *gp);
+void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func);
+void JS_RunGC(JSRuntime *rt);
+JS_BOOL JS_IsLiveObject(JSRuntime *rt, JSValueConst obj);
+
+JSContext *JS_NewContext(JSRuntime *rt);
+void JS_FreeContext(JSContext *s);
+JSContext *JS_DupContext(JSContext *ctx);
+void *JS_GetContextOpaque(JSContext *ctx);
+void JS_SetContextOpaque(JSContext *ctx, void *opaque);
+JSRuntime *JS_GetRuntime(JSContext *ctx);
+void JS_SetClassProto(JSContext *ctx, JSClassID class_id, JSValue obj);
+JSValue JS_GetClassProto(JSContext *ctx, JSClassID class_id);
+
+/* the following functions are used to select the intrinsic object to
+ save memory */
+JSContext *JS_NewContextRaw(JSRuntime *rt);
+void JS_AddIntrinsicBaseObjects(JSContext *ctx);
+void JS_AddIntrinsicDate(JSContext *ctx);
+void JS_AddIntrinsicEval(JSContext *ctx);
+void JS_AddIntrinsicStringNormalize(JSContext *ctx);
+void JS_AddIntrinsicRegExpCompiler(JSContext *ctx);
+void JS_AddIntrinsicRegExp(JSContext *ctx);
+void JS_AddIntrinsicJSON(JSContext *ctx);
+void JS_AddIntrinsicProxy(JSContext *ctx);
+void JS_AddIntrinsicMapSet(JSContext *ctx);
+void JS_AddIntrinsicTypedArrays(JSContext *ctx);
+void JS_AddIntrinsicPromise(JSContext *ctx);
+void JS_AddIntrinsicBigInt(JSContext *ctx);
+void JS_AddIntrinsicBigFloat(JSContext *ctx);
+void JS_AddIntrinsicBigDecimal(JSContext *ctx);
+/* enable operator overloading */
+void JS_AddIntrinsicOperators(JSContext *ctx);
+/* enable "use math" */
+void JS_EnableBignumExt(JSContext *ctx, JS_BOOL enable);
+
+JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv);
+
+void *js_malloc_rt(JSRuntime *rt, size_t size);
+void js_free_rt(JSRuntime *rt, void *ptr);
+void *js_realloc_rt(JSRuntime *rt, void *ptr, size_t size);
+size_t js_malloc_usable_size_rt(JSRuntime *rt, const void *ptr);
+void *js_mallocz_rt(JSRuntime *rt, size_t size);
+
+void *js_malloc(JSContext *ctx, size_t size);
+void js_free(JSContext *ctx, void *ptr);
+void *js_realloc(JSContext *ctx, void *ptr, size_t size);
+size_t js_malloc_usable_size(JSContext *ctx, const void *ptr);
+void *js_realloc2(JSContext *ctx, void *ptr, size_t size, size_t *pslack);
+void *js_mallocz(JSContext *ctx, size_t size);
+char *js_strdup(JSContext *ctx, const char *str);
+char *js_strndup(JSContext *ctx, const char *s, size_t n);
+
+typedef struct JSMemoryUsage {
+ int64_t malloc_size, malloc_limit, memory_used_size;
+ int64_t malloc_count;
+ int64_t memory_used_count;
+ int64_t atom_count, atom_size;
+ int64_t str_count, str_size;
+ int64_t obj_count, obj_size;
+ int64_t prop_count, prop_size;
+ int64_t shape_count, shape_size;
+ int64_t js_func_count, js_func_size, js_func_code_size;
+ int64_t js_func_pc2line_count, js_func_pc2line_size;
+ int64_t c_func_count, array_count;
+ int64_t fast_array_count, fast_array_elements;
+ int64_t binary_object_count, binary_object_size;
+} JSMemoryUsage;
+
+void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s);
+void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt);
+
+/* atom support */
+#define JS_ATOM_NULL 0
+
+JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len);
+JSAtom JS_NewAtom(JSContext *ctx, const char *str);
+JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n);
+JSAtom JS_DupAtom(JSContext *ctx, JSAtom v);
+void JS_FreeAtom(JSContext *ctx, JSAtom v);
+void JS_FreeAtomRT(JSRuntime *rt, JSAtom v);
+JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom);
+JSValue JS_AtomToString(JSContext *ctx, JSAtom atom);
+const char *JS_AtomToCString(JSContext *ctx, JSAtom atom);
+JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val);
+
+/* object class support */
+
+typedef struct JSPropertyEnum {
+ JS_BOOL is_enumerable;
+ JSAtom atom;
+} JSPropertyEnum;
+
+typedef struct JSPropertyDescriptor {
+ int flags;
+ JSValue value;
+ JSValue getter;
+ JSValue setter;
+} JSPropertyDescriptor;
+
+typedef struct JSClassExoticMethods {
+ /* Return -1 if exception (can only happen in case of Proxy object),
+ FALSE if the property does not exists, TRUE if it exists. If 1 is
+ returned, the property descriptor 'desc' is filled if != NULL. */
+ int (*get_own_property)(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop);
+ /* '*ptab' should hold the '*plen' property keys. Return 0 if OK,
+ -1 if exception. The 'is_enumerable' field is ignored.
+ */
+ int (*get_own_property_names)(JSContext *ctx, JSPropertyEnum **ptab,
+ uint32_t *plen,
+ JSValueConst obj);
+ /* return < 0 if exception, or TRUE/FALSE */
+ int (*delete_property)(JSContext *ctx, JSValueConst obj, JSAtom prop);
+ /* return < 0 if exception or TRUE/FALSE */
+ int (*define_own_property)(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter,
+ int flags);
+ /* The following methods can be emulated with the previous ones,
+ so they are usually not needed */
+ /* return < 0 if exception or TRUE/FALSE */
+ int (*has_property)(JSContext *ctx, JSValueConst obj, JSAtom atom);
+ JSValue (*get_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,
+ JSValueConst receiver);
+ /* return < 0 if exception or TRUE/FALSE */
+ int (*set_property)(JSContext *ctx, JSValueConst obj, JSAtom atom,
+ JSValueConst value, JSValueConst receiver, int flags);
+} JSClassExoticMethods;
+
+typedef void JSClassFinalizer(JSRuntime *rt, JSValue val);
+typedef void JSClassGCMark(JSRuntime *rt, JSValueConst val,
+ JS_MarkFunc *mark_func);
+#define JS_CALL_FLAG_CONSTRUCTOR (1 << 0)
+typedef JSValue JSClassCall(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst this_val, int argc, JSValueConst *argv,
+ int flags);
+
+typedef struct JSClassDef {
+ const char *class_name;
+ JSClassFinalizer *finalizer;
+ JSClassGCMark *gc_mark;
+ /* if call != NULL, the object is a function. If (flags &
+ JS_CALL_FLAG_CONSTRUCTOR) != 0, the function is called as a
+ constructor. In this case, 'this_val' is new.target. A
+ constructor call only happens if the object constructor bit is
+ set (see JS_SetConstructorBit()). */
+ JSClassCall *call;
+ /* XXX: suppress this indirection ? It is here only to save memory
+ because only a few classes need these methods */
+ JSClassExoticMethods *exotic;
+} JSClassDef;
+
+JSClassID JS_NewClassID(JSClassID *pclass_id);
+int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def);
+int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id);
+
+/* value handling */
+
+JSValue JS_NewBool(JSContext *ctx, JS_BOOL val);
+JSValue JS_NewInt32(JSContext *ctx, int32_t val);
+JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val);
+JSValue JS_NewInt64(JSContext *ctx, int64_t val);
+JSValue JS_NewUint32(JSContext *ctx, uint32_t val);
+JSValue JS_NewBigInt64(JSContext *ctx, int64_t v);
+JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v);
+JSValue JS_NewFloat64(JSContext *ctx, double d);
+
+static inline JS_BOOL JS_IsNumber(JSValueConst v)
+{
+ int tag = JS_VALUE_GET_TAG(v);
+ return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64(tag);
+}
+
+static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v)
+{
+ (void) ctx;
+ int tag = JS_VALUE_GET_TAG(v);
+ return tag == JS_TAG_BIG_INT;
+}
+
+static inline JS_BOOL JS_IsBigFloat(JSValueConst v)
+{
+ int tag = JS_VALUE_GET_TAG(v);
+ return tag == JS_TAG_BIG_FLOAT;
+}
+
+static inline JS_BOOL JS_IsBigDecimal(JSValueConst v)
+{
+ int tag = JS_VALUE_GET_TAG(v);
+ return tag == JS_TAG_BIG_DECIMAL;
+}
+
+static inline JS_BOOL JS_IsBool(JSValueConst v)
+{
+ return JS_VALUE_GET_TAG(v) == JS_TAG_BOOL;
+}
+
+static inline JS_BOOL JS_IsNull(JSValueConst v)
+{
+ return JS_VALUE_GET_TAG(v) == JS_TAG_NULL;
+}
+
+static inline JS_BOOL JS_IsUndefined(JSValueConst v)
+{
+ return JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED;
+}
+
+static inline JS_BOOL JS_IsException(JSValueConst v)
+{
+ return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_EXCEPTION);
+}
+
+static inline JS_BOOL JS_IsUninitialized(JSValueConst v)
+{
+ return js_unlikely(JS_VALUE_GET_TAG(v) == JS_TAG_UNINITIALIZED);
+}
+
+static inline JS_BOOL JS_IsString(JSValueConst v)
+{
+ return JS_VALUE_GET_TAG(v) == JS_TAG_STRING;
+}
+
+static inline JS_BOOL JS_IsSymbol(JSValueConst v)
+{
+ return JS_VALUE_GET_TAG(v) == JS_TAG_SYMBOL;
+}
+
+static inline JS_BOOL JS_IsObject(JSValueConst v)
+{
+ return JS_VALUE_GET_TAG(v) == JS_TAG_OBJECT;
+}
+
+JSValue JS_Throw(JSContext *ctx, JSValue obj);
+JSValue JS_GetException(JSContext *ctx);
+JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val);
+void JS_ResetUncatchableError(JSContext *ctx);
+JSValue JS_NewError(JSContext *ctx);
+JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...);
+JSValue __js_printf_like(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...);
+JSValue __js_printf_like(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...);
+JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...);
+JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...);
+JSValue JS_ThrowOutOfMemory(JSContext *ctx);
+
+#ifndef NDEBUG
+void notifyRefCountIncrease(JSRefCountHeader *p);
+void notifyRefCountDecrease(JSRefCountHeader *p);
+#endif
+
+void JS_FreeValue(JSContext *ctx, JSValue v);
+void JS_FreeValueRT(JSRuntime *rt, JSValue v);
+
+static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v)
+{
+ (void) ctx;
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+#ifndef NDEBUG
+ notifyRefCountIncrease(p);
+#endif
+ p->ref_count++;
+ }
+ return v;
+}
+
+static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v)
+{
+ (void) rt;
+ if (JS_VALUE_HAS_REF_COUNT(v)) {
+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v);
+#ifndef NDEBUG
+ notifyRefCountIncrease(p);
+#endif
+ p->ref_count++;
+ }
+ return v;
+}
+
+int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */
+int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val);
+static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val)
+{
+ return JS_ToInt32(ctx, (int32_t*)pres, val);
+}
+int JS_ToInt64(JSContext *ctx, int64_t *pres, JSValueConst val);
+int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val);
+int JS_ToFloat64(JSContext *ctx, double *pres, JSValueConst val);
+/* return an exception if 'val' is a Number */
+int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val);
+/* same as JS_ToInt64() but allow BigInt */
+int JS_ToInt64Ext(JSContext *ctx, int64_t *pres, JSValueConst val);
+
+JSValue JS_NewStringLen(JSContext *ctx, const char *str1, size_t len1);
+JSValue JS_NewString(JSContext *ctx, const char *str);
+JSValue JS_NewAtomString(JSContext *ctx, const char *str);
+JSValue JS_ToString(JSContext *ctx, JSValueConst val);
+JSValue JS_ToPropertyKey(JSContext *ctx, JSValueConst val);
+const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, JS_BOOL cesu8);
+static inline const char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1)
+{
+ return JS_ToCStringLen2(ctx, plen, val1, 0);
+}
+static inline const char *JS_ToCString(JSContext *ctx, JSValueConst val1)
+{
+ return JS_ToCStringLen2(ctx, NULL, val1, 0);
+}
+void JS_FreeCString(JSContext *ctx, const char *ptr);
+
+JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto, JSClassID class_id);
+JSValue JS_NewObjectClass(JSContext *ctx, int class_id);
+JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto);
+JSValue JS_NewObject(JSContext *ctx);
+
+JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val);
+JS_BOOL JS_IsRegExp(JSContext* ctx, JSValueConst val);
+JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val);
+JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val);
+JS_BOOL JS_IsArrayBuffer(JSValueConst v);
+
+JSValue JS_NewDate(JSContext *ctx, const char *s);
+JS_BOOL JS_IsDate(JSValueConst v);
+
+JSValue JS_NewArray(JSContext *ctx);
+int JS_IsArray(JSContext *ctx, JSValueConst val);
+
+JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
+ JSAtom prop, JSValueConst receiver,
+ JS_BOOL throw_ref_error);
+static js_force_inline JSValue JS_GetProperty(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop)
+{
+ return JS_GetPropertyInternal(ctx, this_obj, prop, this_obj, 0);
+}
+JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj,
+ const char *prop);
+JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx);
+
+int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValue val,
+ int flags);
+static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValue val)
+{
+ return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW);
+}
+int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx, JSValue val);
+int JS_SetPropertyInt64(JSContext *ctx, JSValueConst this_obj,
+ int64_t idx, JSValue val);
+int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,
+ const char *prop, JSValue val);
+int JS_HasProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop);
+int JS_IsExtensible(JSContext *ctx, JSValueConst obj);
+int JS_PreventExtensions(JSContext *ctx, JSValueConst obj);
+int JS_DeleteProperty(JSContext *ctx, JSValueConst obj, JSAtom prop, int flags);
+int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val);
+JSValue JS_GetPrototype(JSContext *ctx, JSValueConst val);
+
+JSValue JS_ObjectSeal(JSContext *ctx, JSValueConst obj, int freeze);
+
+#define JS_GPN_STRING_MASK (1 << 0)
+#define JS_GPN_SYMBOL_MASK (1 << 1)
+#define JS_GPN_PRIVATE_MASK (1 << 2)
+/* only include the enumerable properties */
+#define JS_GPN_ENUM_ONLY (1 << 4)
+/* set theJSPropertyEnum.is_enumerable field */
+#define JS_GPN_SET_ENUM (1 << 5)
+
+int JS_GetOwnPropertyNames(JSContext *ctx, JSPropertyEnum **ptab,
+ uint32_t *plen, JSValueConst obj, int flags);
+int JS_GetOwnProperty(JSContext *ctx, JSPropertyDescriptor *desc,
+ JSValueConst obj, JSAtom prop);
+
+JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj,
+ int argc, JSValueConst *argv);
+JSValue JS_Invoke(JSContext *ctx, JSValueConst this_val, JSAtom atom,
+ int argc, JSValueConst *argv);
+JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj,
+ int argc, JSValueConst *argv);
+JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst new_target,
+ int argc, JSValueConst *argv);
+JS_BOOL JS_DetectModule(const char *input, size_t input_len);
+/* 'input' must be zero terminated i.e. input[input_len] = '\0'. */
+JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
+ const char *filename, int eval_flags);
+/* same as JS_Eval() but with an explicit 'this_obj' parameter */
+JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj,
+ const char *input, size_t input_len,
+ const char *filename, int line, int eval_flags);
+JSValue JS_GetGlobalObject(JSContext *ctx);
+int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj);
+int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValueConst val,
+ JSValueConst getter, JSValueConst setter, int flags);
+int JS_DefinePropertyValue(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValue val, int flags);
+int JS_DefinePropertyValueUint32(JSContext *ctx, JSValueConst this_obj,
+ uint32_t idx, JSValue val, int flags);
+int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj,
+ const char *prop, JSValue val, int flags);
+int JS_DefinePropertyGetSet(JSContext *ctx, JSValueConst this_obj,
+ JSAtom prop, JSValue getter, JSValue setter,
+ int flags);
+void JS_SetOpaque(JSValue obj, void *opaque);
+void *JS_GetOpaque(JSValueConst obj, JSClassID class_id);
+void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID class_id);
+
+/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */
+JSValue JS_ParseJSON(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename);
+#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */
+JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len,
+ const char *filename, int flags);
+JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj,
+ JSValueConst replacer, JSValueConst space0);
+
+typedef void JSFreeArrayBufferDataFunc(JSRuntime *rt, void *opaque, void *ptr);
+JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len,
+ JSFreeArrayBufferDataFunc *free_func, void *opaque,
+ JS_BOOL is_shared);
+JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len);
+void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj);
+uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj);
+JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj,
+ size_t *pbyte_offset,
+ size_t *pbyte_length,
+ size_t *pbytes_per_element);
+typedef struct {
+ void *(*sab_alloc)(void *opaque, size_t size);
+ void (*sab_free)(void *opaque, void *ptr);
+ void (*sab_dup)(void *opaque, void *ptr);
+ void *sab_opaque;
+} JSSharedArrayBufferFunctions;
+void JS_SetSharedArrayBufferFunctions(JSRuntime *rt,
+ const JSSharedArrayBufferFunctions *sf);
+
+JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs);
+
+/* is_handled = TRUE means that the rejection is handled */
+typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise,
+ JSValueConst reason,
+ JS_BOOL is_handled, void *opaque);
+void JS_SetHostPromiseRejectionTracker(JSRuntime *rt, JSHostPromiseRejectionTracker *cb, void *opaque);
+
+/* return != 0 if the JS code needs to be interrupted */
+typedef int JSInterruptHandler(JSRuntime *rt, void *opaque);
+void JS_SetInterruptHandler(JSRuntime *rt, JSInterruptHandler *cb, void *opaque);
+/* if can_block is TRUE, Atomics.wait() can be used */
+void JS_SetCanBlock(JSRuntime *rt, JS_BOOL can_block);
+/* set the [IsHTMLDDA] internal slot */
+void JS_SetIsHTMLDDA(JSContext *ctx, JSValueConst obj);
+
+typedef struct JSModuleDef JSModuleDef;
+
+/* return the module specifier (allocated with js_malloc()) or NULL if
+ exception */
+typedef char *JSModuleNormalizeFunc(JSContext *ctx,
+ const char *module_base_name,
+ const char *module_name, void *opaque);
+typedef JSModuleDef *JSModuleLoaderFunc(JSContext *ctx,
+ const char *module_name, void *opaque);
+
+/* module_normalize = NULL is allowed and invokes the default module
+ filename normalizer */
+void JS_SetModuleLoaderFunc(JSRuntime *rt,
+ JSModuleNormalizeFunc *module_normalize,
+ JSModuleLoaderFunc *module_loader, void *opaque);
+/* return the import.meta object of a module */
+JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m);
+JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m);
+
+/* JS Job support */
+
+typedef JSValue JSJobFunc(JSContext *ctx, int argc, JSValueConst *argv);
+int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func, int argc, JSValueConst *argv);
+
+JS_BOOL JS_IsJobPending(JSRuntime *rt);
+int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx);
+
+/* Object Writer/Reader (currently only used to handle precompiled code) */
+#define JS_WRITE_OBJ_BYTECODE (1 << 0) /* allow function/module */
+#define JS_WRITE_OBJ_BSWAP (1 << 1) /* byte swapped output */
+#define JS_WRITE_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
+#define JS_WRITE_OBJ_REFERENCE (1 << 3) /* allow object references to
+ encode arbitrary object
+ graph */
+uint8_t *JS_WriteObject(JSContext *ctx, size_t *psize, JSValueConst obj,
+ int flags);
+uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj,
+ int flags, uint8_t ***psab_tab, size_t *psab_tab_len);
+
+#define JS_READ_OBJ_BYTECODE (1 << 0) /* allow function/module */
+#define JS_READ_OBJ_ROM_DATA (1 << 1) /* avoid duplicating 'buf' data */
+#define JS_READ_OBJ_SAB (1 << 2) /* allow SharedArrayBuffer */
+#define JS_READ_OBJ_REFERENCE (1 << 3) /* allow object references */
+JSValue JS_ReadObject(JSContext *ctx, const uint8_t *buf, size_t buf_len,
+ int flags);
+/* instantiate and evaluate a bytecode function. Only used when
+ reading a script or module with JS_ReadObject() */
+JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj);
+/* load the dependencies of the module 'obj'. Useful when JS_ReadObject()
+ returns a module. */
+int JS_ResolveModule(JSContext *ctx, JSValueConst obj);
+
+/* only exported for os.Worker() */
+JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels);
+/* only exported for os.Worker() */
+JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename,
+ const char *filename);
+
+/* C function definition */
+typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */
+ JS_CFUNC_generic,
+ JS_CFUNC_generic_magic,
+ JS_CFUNC_constructor,
+ JS_CFUNC_constructor_magic,
+ JS_CFUNC_constructor_or_func,
+ JS_CFUNC_constructor_or_func_magic,
+ JS_CFUNC_f_f,
+ JS_CFUNC_f_f_f,
+ JS_CFUNC_getter,
+ JS_CFUNC_setter,
+ JS_CFUNC_getter_magic,
+ JS_CFUNC_setter_magic,
+ JS_CFUNC_iterator_next,
+} JSCFunctionEnum;
+
+typedef union JSCFunctionType {
+ JSCFunction *generic;
+ JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
+ JSCFunction *constructor;
+ JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic);
+ JSCFunction *constructor_or_func;
+ double (*f_f)(double);
+ double (*f_f_f)(double, double);
+ JSValue (*getter)(JSContext *ctx, JSValueConst this_val);
+ JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val);
+ JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic);
+ JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic);
+ JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val,
+ int argc, JSValueConst *argv, int *pdone, int magic);
+} JSCFunctionType;
+
+JSValue JS_NewCFunction2(JSContext *ctx, JSCFunction *func,
+ const char *name,
+ int length, JSCFunctionEnum cproto, int magic);
+JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func,
+ int length, int magic, int data_len,
+ JSValueConst *data);
+
+JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name,
+ int length);
+
+JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func,
+ const char *name, int length, JSCFunctionEnum cproto, int magic);
+void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj,
+ JSValueConst proto);
+
+/* C property definition */
+
+typedef struct JSCFunctionListEntry {
+ const char *name;
+ uint8_t prop_flags;
+ uint8_t def_type;
+ int16_t magic;
+ union {
+ struct {
+ uint8_t length; /* XXX: should move outside union */
+ uint8_t cproto; /* XXX: should move outside union */
+ JSCFunctionType cfunc;
+ } func;
+ struct {
+ JSCFunctionType get;
+ JSCFunctionType set;
+ } getset;
+ struct {
+ const char *name;
+ int base;
+ } alias;
+ struct {
+ const struct JSCFunctionListEntry *tab;
+ int len;
+ } prop_list;
+ const char *str;
+ int32_t i32;
+ int64_t i64;
+ double f64;
+ } u;
+} JSCFunctionListEntry;
+
+#define JS_DEF_CFUNC 0
+#define JS_DEF_CGETSET 1
+#define JS_DEF_CGETSET_MAGIC 2
+#define JS_DEF_PROP_STRING 3
+#define JS_DEF_PROP_INT32 4
+#define JS_DEF_PROP_INT64 5
+#define JS_DEF_PROP_DOUBLE 6
+#define JS_DEF_PROP_UNDEFINED 7
+#define JS_DEF_OBJECT 8
+#define JS_DEF_ALIAS 9
+
+/* Note: c++ does not like nested designators */
+#define JS_CFUNC_DEF(name, length, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_generic, { .generic = func1 } } } }
+#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } } } }
+#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, .u = { .func = { length, JS_CFUNC_ ## cproto, { .cproto = func1 } } } }
+#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, magic, .u = { .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } } } }
+#define JS_CGETSET_DEF(name, fgetter, fsetter) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET, 0, .u = { .getset = { .get = { .getter = fgetter }, .set = { .setter = fsetter } } } }
+#define JS_CGETSET_MAGIC_DEF(name, fgetter, fsetter, magic) { name, JS_PROP_CONFIGURABLE, JS_DEF_CGETSET_MAGIC, magic, .u = { .getset = { .get = { .getter_magic = fgetter }, .set = { .setter_magic = fsetter } } } }
+#define JS_PROP_STRING_DEF(name, cstr, prop_flags) { name, prop_flags, JS_DEF_PROP_STRING, 0, .u = { .str = cstr } }
+#define JS_PROP_INT32_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT32, 0, .u = { .i32 = val } }
+#define JS_PROP_INT64_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_INT64, 0, .u = { .i64 = val } }
+#define JS_PROP_DOUBLE_DEF(name, val, prop_flags) { name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = { .f64 = val } }
+#define JS_PROP_UNDEFINED_DEF(name, prop_flags) { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } }
+#define JS_OBJECT_DEF(name, tab, len, prop_flags) { name, prop_flags, JS_DEF_OBJECT, 0, .u = { .prop_list = { tab, len } } }
+#define JS_ALIAS_DEF(name, from) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, -1 } } }
+#define JS_ALIAS_BASE_DEF(name, from, base) { name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0, .u = { .alias = { from, base } } }
+
+void JS_SetPropertyFunctionList(JSContext *ctx, JSValueConst obj,
+ const JSCFunctionListEntry *tab,
+ int len);
+
+/* C module definition */
+
+typedef int JSModuleInitFunc(JSContext *ctx, JSModuleDef *m);
+
+JSModuleDef *JS_NewCModule(JSContext *ctx, const char *name_str,
+ JSModuleInitFunc *func);
+/* can only be called before the module is instantiated */
+int JS_AddModuleExport(JSContext *ctx, JSModuleDef *m, const char *name_str);
+int JS_AddModuleExportList(JSContext *ctx, JSModuleDef *m,
+ const JSCFunctionListEntry *tab, int len);
+/* can only be called after the module is instantiated */
+int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name,
+ JSValue val);
+int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m,
+ const JSCFunctionListEntry *tab, int len);
+
+
+/* Qbs extensions */
+struct LookupResult
+{
+ JSValue value;
+ JSValue scope;
+ int useResult;
+};
+typedef struct LookupResult ScopeLookup(JSContext *ctx, JSAtom prop);
+void setScopeLookup(JSContext *ctx, ScopeLookup *scopeLookup);
+
+// Alternative: Request with throw in script engine
+typedef void FoundUndefinedHandler(JSContext *ctx);
+void setFoundUndefinedHandler(JSContext *ctx, FoundUndefinedHandler *handler);
+
+typedef void FunctionEnteredHandler(JSContext *ctx, JSValue this_val);
+typedef void FunctionExitedHandler(JSContext *ctx);
+void setFunctionEnteredHandler(JSContext *ctx, FunctionEnteredHandler *handler);
+void setFunctionExitedHandler(JSContext *ctx, FunctionExitedHandler *handler);
+int isSimpleValue(JSValue v);
+
+#ifndef NDEBUG
+void watchRefCount(void *p);
+#endif
+
+void build_backtrace(JSContext *ctx, JSValueConst error_obj,
+ const char *filename, int line_num,
+ int backtrace_flags);
+
+#undef js_unlikely
+#undef js_force_inline
+
+#ifdef __cplusplus
+} /* extern "C" { */
+#endif
+
+#endif /* QUICKJS_H */
diff --git a/src/shared/quickjs/quickjs.qbs b/src/shared/quickjs/quickjs.qbs
new file mode 100644
index 000000000..4c8112ac9
--- /dev/null
+++ b/src/shared/quickjs/quickjs.qbs
@@ -0,0 +1,33 @@
+StaticLibrary {
+ name: "quickjs"
+
+ Depends { name: "qbsbuildconfig" }
+ Depends { name: "cpp" }
+
+ cpp.cLanguageVersion: qbs.toolchain.contains("msvc") ? "c99" : "gnu99"
+ cpp.defines: ['CONFIG_VERSION="2021-03-27"'].concat(qbsbuildconfig.dumpJsLeaks
+ ? "DUMP_LEAKS" : [])
+ cpp.warningLevel: "none"
+
+ files: [
+ "cutils.c",
+ "cutils.h",
+ "libregexp-opcode.h",
+ "libregexp.c",
+ "libregexp.h",
+ "libunicode-table.h",
+ "libunicode.c",
+ "libunicode.h",
+ "list.h",
+ "quickjs-atom.h",
+ "quickjs-opcode.h",
+ "quickjs.c",
+ "quickjs.diff",
+ "quickjs.h",
+ ]
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ }
+}
diff --git a/src/shared/variant/CMakeLists.txt b/src/shared/variant/CMakeLists.txt
new file mode 100644
index 000000000..ac73231d4
--- /dev/null
+++ b/src/shared/variant/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_library(qbsvariant INTERFACE)
+target_include_directories(
+ qbsvariant
+ INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>)
diff --git a/src/shared/variant/LICENSE.md b/src/shared/variant/LICENSE.md
new file mode 100644
index 000000000..36b7cd93c
--- /dev/null
+++ b/src/shared/variant/LICENSE.md
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/src/shared/variant/README.md b/src/shared/variant/README.md
new file mode 100644
index 000000000..6644286d2
--- /dev/null
+++ b/src/shared/variant/README.md
@@ -0,0 +1,37 @@
+# MPark.Variant
+
+> __C++17__ `std::variant` for __C++11__/__14__/__17__
+
+[![release][badge.release]][release]
+[![header][badge.header]][header]
+[![travis][badge.travis]][travis]
+[![appveyor][badge.appveyor]][appveyor]
+[![license][badge.license]][license]
+[![godbolt][badge.godbolt]][godbolt]
+[![wandbox][badge.wandbox]][wandbox]
+
+[badge.release]: https://img.shields.io/github/release/mpark/variant.svg
+[badge.header]: https://img.shields.io/badge/single%20header-master-blue.svg
+[badge.travis]: https://travis-ci.org/mpark/variant.svg?branch=master
+[badge.appveyor]: https://ci.appveyor.com/api/projects/status/github/mpark/variant?branch=master&svg=true
+[badge.license]: https://img.shields.io/badge/license-boost-blue.svg
+[badge.godbolt]: https://img.shields.io/badge/try%20it-on%20godbolt-222266.svg
+[badge.wandbox]: https://img.shields.io/badge/try%20it-on%20wandbox-5cb85c.svg
+
+[release]: https://github.com/mpark/variant/releases/latest
+[header]: https://github.com/mpark/variant/blob/single-header/master/variant.hpp
+[travis]: https://travis-ci.org/mpark/variant
+[appveyor]: https://ci.appveyor.com/project/mpark/variant
+[license]: https://github.com/mpark/variant/blob/master/LICENSE.md
+[godbolt]: https://godbolt.org/g/1qYDAK
+[wandbox]: https://wandbox.org/permlink/QV3gZ2KQQNwgoFIB
+
+## Single Header
+
+This branch provides a standalone `variant.hpp` file for each
+[release](https://github.com/mpark/variant/releases).
+Copy it and `#include` away!
+
+## License
+
+Distributed under the [Boost Software License, Version 1.0](LICENSE.md).
diff --git a/src/shared/variant/variant.h b/src/shared/variant/variant.h
new file mode 100644
index 000000000..ecaf81ca6
--- /dev/null
+++ b/src/shared/variant/variant.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://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 https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+/*
+ See std(::experimental)::variant.
+*/
+
+// std::variant from Apple's Clang supports methods that throw std::bad_optional_access only
+// with deployment target >= macOS 10.14
+// TODO: Use std::variant everywhere when we can require macOS 10.14
+#if !defined(__apple_build_version__)
+#include <variant>
+
+namespace qbs {
+namespace Variant {
+using std::get;
+using std::get_if;
+using std::holds_alternative;
+using std::variant;
+using std::variant_alternative_t;
+using std::visit;
+} // namespace Variant
+} // namespace qbs
+
+#else
+#include "variant.hpp"
+
+namespace qbs {
+namespace Variant {
+using mpark::get;
+using mpark::get_if;
+using mpark::holds_alternative;
+using mpark::variant;
+using mpark::variant_alternative_t;
+using mpark::visit;
+} // namespace Variant
+} // namespace qbs
+
+#endif
diff --git a/src/shared/variant/variant.hpp b/src/shared/variant/variant.hpp
new file mode 100644
index 000000000..dca26986c
--- /dev/null
+++ b/src/shared/variant/variant.hpp
@@ -0,0 +1,2465 @@
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_VARIANT_HPP
+#define MPARK_VARIANT_HPP
+
+#if defined(__GNUC__) && __GNUC__ >= 9
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-copy"
+#endif
+
+/*
+ variant synopsis
+
+namespace std {
+
+ // 20.7.2, class template variant
+ template <class... Types>
+ class variant {
+ public:
+
+ // 20.7.2.1, constructors
+ constexpr variant() noexcept(see below);
+ variant(const variant&);
+ variant(variant&&) noexcept(see below);
+
+ template <class T> constexpr variant(T&&) noexcept(see below);
+
+ template <class T, class... Args>
+ constexpr explicit variant(in_place_type_t<T>, Args&&...);
+
+ template <class T, class U, class... Args>
+ constexpr explicit variant(
+ in_place_type_t<T>, initializer_list<U>, Args&&...);
+
+ template <size_t I, class... Args>
+ constexpr explicit variant(in_place_index_t<I>, Args&&...);
+
+ template <size_t I, class U, class... Args>
+ constexpr explicit variant(
+ in_place_index_t<I>, initializer_list<U>, Args&&...);
+
+ // 20.7.2.2, destructor
+ ~variant();
+
+ // 20.7.2.3, assignment
+ variant& operator=(const variant&);
+ variant& operator=(variant&&) noexcept(see below);
+
+ template <class T> variant& operator=(T&&) noexcept(see below);
+
+ // 20.7.2.4, modifiers
+ template <class T, class... Args>
+ T& emplace(Args&&...);
+
+ template <class T, class U, class... Args>
+ T& emplace(initializer_list<U>, Args&&...);
+
+ template <size_t I, class... Args>
+ variant_alternative<I, variant>& emplace(Args&&...);
+
+ template <size_t I, class U, class... Args>
+ variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...);
+
+ // 20.7.2.5, value status
+ constexpr bool valueless_by_exception() const noexcept;
+ constexpr size_t index() const noexcept;
+
+ // 20.7.2.6, swap
+ void swap(variant&) noexcept(see below);
+ };
+
+ // 20.7.3, variant helper classes
+ template <class T> struct variant_size; // undefined
+
+ template <class T>
+ constexpr size_t variant_size_v = variant_size<T>::value;
+
+ template <class T> struct variant_size<const T>;
+ template <class T> struct variant_size<volatile T>;
+ template <class T> struct variant_size<const volatile T>;
+
+ template <class... Types>
+ struct variant_size<variant<Types...>>;
+
+ template <size_t I, class T> struct variant_alternative; // undefined
+
+ template <size_t I, class T>
+ using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+ template <size_t I, class T> struct variant_alternative<I, const T>;
+ template <size_t I, class T> struct variant_alternative<I, volatile T>;
+ template <size_t I, class T> struct variant_alternative<I, const volatile T>;
+
+ template <size_t I, class... Types>
+ struct variant_alternative<I, variant<Types...>>;
+
+ constexpr size_t variant_npos = -1;
+
+ // 20.7.4, value access
+ template <class T, class... Types>
+ constexpr bool holds_alternative(const variant<Types...>&) noexcept;
+
+ template <size_t I, class... Types>
+ constexpr variant_alternative_t<I, variant<Types...>>&
+ get(variant<Types...>&);
+
+ template <size_t I, class... Types>
+ constexpr variant_alternative_t<I, variant<Types...>>&&
+ get(variant<Types...>&&);
+
+ template <size_t I, class... Types>
+ constexpr variant_alternative_t<I, variant<Types...>> const&
+ get(const variant<Types...>&);
+
+ template <size_t I, class... Types>
+ constexpr variant_alternative_t<I, variant<Types...>> const&&
+ get(const variant<Types...>&&);
+
+ template <class T, class... Types>
+ constexpr T& get(variant<Types...>&);
+
+ template <class T, class... Types>
+ constexpr T&& get(variant<Types...>&&);
+
+ template <class T, class... Types>
+ constexpr const T& get(const variant<Types...>&);
+
+ template <class T, class... Types>
+ constexpr const T&& get(const variant<Types...>&&);
+
+ template <size_t I, class... Types>
+ constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>>
+ get_if(variant<Types...>*) noexcept;
+
+ template <size_t I, class... Types>
+ constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>>
+ get_if(const variant<Types...>*) noexcept;
+
+ template <class T, class... Types>
+ constexpr add_pointer_t<T>
+ get_if(variant<Types...>*) noexcept;
+
+ template <class T, class... Types>
+ constexpr add_pointer_t<const T>
+ get_if(const variant<Types...>*) noexcept;
+
+ // 20.7.5, relational operators
+ template <class... Types>
+ constexpr bool operator==(const variant<Types...>&, const variant<Types...>&);
+
+ template <class... Types>
+ constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&);
+
+ template <class... Types>
+ constexpr bool operator<(const variant<Types...>&, const variant<Types...>&);
+
+ template <class... Types>
+ constexpr bool operator>(const variant<Types...>&, const variant<Types...>&);
+
+ template <class... Types>
+ constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&);
+
+ template <class... Types>
+ constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&);
+
+ // 20.7.6, visitation
+ template <class Visitor, class... Variants>
+ constexpr see below visit(Visitor&&, Variants&&...);
+
+ // 20.7.7, class monostate
+ struct monostate;
+
+ // 20.7.8, monostate relational operators
+ constexpr bool operator<(monostate, monostate) noexcept;
+ constexpr bool operator>(monostate, monostate) noexcept;
+ constexpr bool operator<=(monostate, monostate) noexcept;
+ constexpr bool operator>=(monostate, monostate) noexcept;
+ constexpr bool operator==(monostate, monostate) noexcept;
+ constexpr bool operator!=(monostate, monostate) noexcept;
+
+ // 20.7.9, specialized algorithms
+ template <class... Types>
+ void swap(variant<Types...>&, variant<Types...>&) noexcept(see below);
+
+ // 20.7.10, class bad_variant_access
+ class bad_variant_access;
+
+ // 20.7.11, hash support
+ template <class T> struct hash;
+ template <class... Types> struct hash<variant<Types...>>;
+ template <> struct hash<monostate>;
+
+} // namespace std
+
+*/
+
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <new>
+#include <type_traits>
+#include <utility>
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_CONFIG_HPP
+#define MPARK_CONFIG_HPP
+
+// MSVC 2015 Update 3.
+#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210)
+#error "MPark.Variant requires C++11 support."
+#endif
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#ifndef __has_include
+#define __has_include(x) 0
+#endif
+
+#ifndef __has_feature
+#define __has_feature(x) 0
+#endif
+
+#if __has_builtin(__builtin_addressof) || \
+ (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER)
+#define MPARK_BUILTIN_ADDRESSOF
+#endif
+
+#if __has_builtin(__builtin_unreachable)
+#define MPARK_BUILTIN_UNREACHABLE
+#endif
+
+#if __has_builtin(__type_pack_element)
+#define MPARK_TYPE_PACK_ELEMENT
+#endif
+
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+#if !defined(_MSC_VER) || _MSC_VER < 1915 // compile issue in msvc 2017 update 8
+#define MPARK_CPP14_CONSTEXPR
+#endif
+#endif
+
+#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \
+ (defined(_MSC_VER) && defined(_CPPUNWIND))
+#define MPARK_EXCEPTIONS
+#endif
+
+#if defined(__cpp_generic_lambdas) || defined(_MSC_VER)
+#define MPARK_GENERIC_LAMBDAS
+#endif
+
+#if defined(__cpp_lib_integer_sequence)
+#define MPARK_INTEGER_SEQUENCE
+#endif
+
+#if defined(__cpp_return_type_deduction) || defined(_MSC_VER)
+#define MPARK_RETURN_TYPE_DEDUCTION
+#endif
+
+#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER)
+#define MPARK_TRANSPARENT_OPERATORS
+#endif
+
+#if defined(__cpp_variable_templates) || defined(_MSC_VER)
+#define MPARK_VARIABLE_TEMPLATES
+#endif
+
+#if !defined(__GLIBCXX__) || __has_include(<codecvt>) // >= libstdc++-5
+#define MPARK_TRIVIALITY_TYPE_TRAITS
+#endif
+
+#endif // MPARK_CONFIG_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_IN_PLACE_HPP
+#define MPARK_IN_PLACE_HPP
+
+#include <cstddef>
+
+
+namespace mpark {
+
+ struct in_place_t { explicit in_place_t() = default; };
+
+ template <std::size_t I>
+ struct in_place_index_t { explicit in_place_index_t() = default; };
+
+ template <typename T>
+ struct in_place_type_t { explicit in_place_type_t() = default; };
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+ constexpr in_place_t in_place{};
+
+ template <std::size_t I> constexpr in_place_index_t<I> in_place_index{};
+
+ template <typename T> constexpr in_place_type_t<T> in_place_type{};
+#endif
+
+} // namespace mpark
+
+#endif // MPARK_IN_PLACE_HPP
+
+// MPark.Variant
+//
+// Copyright Michael Park, 2015-2017
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
+
+#ifndef MPARK_LIB_HPP
+#define MPARK_LIB_HPP
+
+#include <memory>
+#include <functional>
+#include <type_traits>
+#include <utility>
+
+
+#define RETURN(...) \
+ noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { \
+ return __VA_ARGS__; \
+ }
+
+namespace mpark {
+ namespace lib {
+ template <typename T>
+ struct identity { using type = T; };
+
+ inline namespace cpp14 {
+ template <typename T, std::size_t N>
+ struct array {
+ constexpr const T &operator[](std::size_t index) const {
+ return data[index];
+ }
+
+ T data[N == 0 ? 1 : N];
+ };
+
+ template <typename T>
+ using add_pointer_t = typename std::add_pointer<T>::type;
+
+ template <typename... Ts>
+ using common_type_t = typename std::common_type<Ts...>::type;
+
+ template <typename T>
+ using decay_t = typename std::decay<T>::type;
+
+ template <bool B, typename T = void>
+ using enable_if_t = typename std::enable_if<B, T>::type;
+
+ template <typename T>
+ using remove_const_t = typename std::remove_const<T>::type;
+
+ template <typename T>
+ using remove_reference_t = typename std::remove_reference<T>::type;
+
+ template <typename T>
+ inline constexpr T &&forward(remove_reference_t<T> &t) noexcept {
+ return static_cast<T &&>(t);
+ }
+
+ template <typename T>
+ inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept {
+ static_assert(!std::is_lvalue_reference<T>::value,
+ "can not forward an rvalue as an lvalue");
+ return static_cast<T &&>(t);
+ }
+
+ template <typename T>
+ inline constexpr remove_reference_t<T> &&move(T &&t) noexcept {
+ return static_cast<remove_reference_t<T> &&>(t);
+ }
+
+#ifdef MPARK_INTEGER_SEQUENCE
+ using std::integer_sequence;
+ using std::index_sequence;
+ using std::make_index_sequence;
+ using std::index_sequence_for;
+#else
+ template <typename T, T... Is>
+ struct integer_sequence {
+ using value_type = T;
+ static constexpr std::size_t size() noexcept { return sizeof...(Is); }
+ };
+
+ template <std::size_t... Is>
+ using index_sequence = integer_sequence<std::size_t, Is...>;
+
+ template <typename Lhs, typename Rhs>
+ struct make_index_sequence_concat;
+
+ template <std::size_t... Lhs, std::size_t... Rhs>
+ struct make_index_sequence_concat<index_sequence<Lhs...>,
+ index_sequence<Rhs...>>
+ : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {};
+
+ template <std::size_t N>
+ struct make_index_sequence_impl;
+
+ template <std::size_t N>
+ using make_index_sequence = typename make_index_sequence_impl<N>::type;
+
+ template <std::size_t N>
+ struct make_index_sequence_impl
+ : make_index_sequence_concat<make_index_sequence<N / 2>,
+ make_index_sequence<N - (N / 2)>> {};
+
+ template <>
+ struct make_index_sequence_impl<0> : identity<index_sequence<>> {};
+
+ template <>
+ struct make_index_sequence_impl<1> : identity<index_sequence<0>> {};
+
+ template <typename... Ts>
+ using index_sequence_for = make_index_sequence<sizeof...(Ts)>;
+#endif
+
+ // <functional>
+#ifdef MPARK_TRANSPARENT_OPERATORS
+ using equal_to = std::equal_to<>;
+#else
+ struct equal_to {
+ template <typename Lhs, typename Rhs>
+ inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+ RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs))
+ };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+ using not_equal_to = std::not_equal_to<>;
+#else
+ struct not_equal_to {
+ template <typename Lhs, typename Rhs>
+ inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+ RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs))
+ };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+ using less = std::less<>;
+#else
+ struct less {
+ template <typename Lhs, typename Rhs>
+ inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+ RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs))
+ };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+ using greater = std::greater<>;
+#else
+ struct greater {
+ template <typename Lhs, typename Rhs>
+ inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+ RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs))
+ };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+ using less_equal = std::less_equal<>;
+#else
+ struct less_equal {
+ template <typename Lhs, typename Rhs>
+ inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+ RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs))
+ };
+#endif
+
+#ifdef MPARK_TRANSPARENT_OPERATORS
+ using greater_equal = std::greater_equal<>;
+#else
+ struct greater_equal {
+ template <typename Lhs, typename Rhs>
+ inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const
+ RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs))
+ };
+#endif
+ } // namespace cpp14
+
+ inline namespace cpp17 {
+
+ // <type_traits>
+ template <bool B>
+ using bool_constant = std::integral_constant<bool, B>;
+
+ template <typename...>
+ struct voider : identity<void> {};
+
+ template <typename... Ts>
+ using void_t = typename voider<Ts...>::type;
+
+ namespace detail {
+ namespace swappable {
+
+ using std::swap;
+
+ template <typename T>
+ struct is_swappable {
+ private:
+ template <typename U,
+ typename = decltype(swap(std::declval<U &>(),
+ std::declval<U &>()))>
+ inline static std::true_type test(int);
+
+ template <typename U>
+ inline static std::false_type test(...);
+
+ public:
+ static constexpr bool value = decltype(test<T>(0))::value;
+ };
+
+ template <typename T, bool = is_swappable<T>::value>
+ struct is_nothrow_swappable {
+ static constexpr bool value =
+ noexcept(swap(std::declval<T &>(), std::declval<T &>()));
+ };
+
+ template <typename T>
+ struct is_nothrow_swappable<T, false> : std::false_type {};
+
+ } // namespace swappable
+ } // namespace detail
+
+ using detail::swappable::is_swappable;
+ using detail::swappable::is_nothrow_swappable;
+
+ // <functional>
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+ template <typename F, typename... As>
+ inline constexpr auto invoke(F &&f, As &&... as)
+ RETURN(lib::forward<F>(f)(lib::forward<As>(as)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ template <typename B, typename T, typename D>
+ inline constexpr auto invoke(T B::*pmv, D &&d)
+ RETURN(lib::forward<D>(d).*pmv)
+
+ template <typename Pmv, typename Ptr>
+ inline constexpr auto invoke(Pmv pmv, Ptr &&ptr)
+ RETURN((*lib::forward<Ptr>(ptr)).*pmv)
+
+ template <typename B, typename T, typename D, typename... As>
+ inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as)
+ RETURN((lib::forward<D>(d).*pmf)(lib::forward<As>(as)...))
+
+ template <typename Pmf, typename Ptr, typename... As>
+ inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as)
+ RETURN(((*lib::forward<Ptr>(ptr)).*pmf)(lib::forward<As>(as)...))
+
+ namespace detail {
+
+ template <typename Void, typename, typename...>
+ struct invoke_result {};
+
+ template <typename F, typename... Args>
+ struct invoke_result<void_t<decltype(lib::invoke(
+ std::declval<F>(), std::declval<Args>()...))>,
+ F,
+ Args...>
+ : identity<decltype(
+ lib::invoke(std::declval<F>(), std::declval<Args>()...))> {};
+
+ } // namespace detail
+
+ template <typename F, typename... Args>
+ using invoke_result = detail::invoke_result<void, F, Args...>;
+
+ template <typename F, typename... Args>
+ using invoke_result_t = typename invoke_result<F, Args...>::type;
+
+ namespace detail {
+
+ template <typename Void, typename, typename...>
+ struct is_invocable : std::false_type {};
+
+ template <typename F, typename... Args>
+ struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...>
+ : std::true_type {};
+
+ template <typename Void, typename, typename, typename...>
+ struct is_invocable_r : std::false_type {};
+
+ template <typename R, typename F, typename... Args>
+ struct is_invocable_r<void_t<invoke_result_t<F, Args...>>,
+ R,
+ F,
+ Args...>
+ : std::is_convertible<invoke_result_t<F, Args...>, R> {};
+
+ } // namespace detail
+
+ template <typename F, typename... Args>
+ using is_invocable = detail::is_invocable<void, F, Args...>;
+
+ template <typename R, typename F, typename... Args>
+ using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>;
+
+ // <memory>
+#ifdef MPARK_BUILTIN_ADDRESSOF
+ template <typename T>
+ inline constexpr T *addressof(T &arg) {
+ return __builtin_addressof(arg);
+ }
+#else
+ namespace detail {
+
+ namespace has_addressof_impl {
+
+ struct fail;
+
+ template <typename T>
+ inline fail operator&(T &&);
+
+ template <typename T>
+ inline static constexpr bool impl() {
+ return (std::is_class<T>::value || std::is_union<T>::value) &&
+ !std::is_same<decltype(&std::declval<T &>()), fail>::value;
+ }
+
+ } // namespace has_addressof_impl
+
+ template <typename T>
+ using has_addressof = bool_constant<has_addressof_impl::impl<T>()>;
+
+ template <typename T>
+ inline constexpr T *addressof(T &arg, std::true_type) {
+ return std::addressof(arg);
+ }
+
+ template <typename T>
+ inline constexpr T *addressof(T &arg, std::false_type) {
+ return &arg;
+ }
+
+ } // namespace detail
+
+ template <typename T>
+ inline constexpr T *addressof(T &arg) {
+ return detail::addressof(arg, detail::has_addressof<T>{});
+ }
+#endif
+
+ template <typename T>
+ inline constexpr T *addressof(const T &&) = delete;
+
+ } // namespace cpp17
+
+ template <typename T>
+ struct remove_all_extents : identity<T> {};
+
+ template <typename T, std::size_t N>
+ struct remove_all_extents<array<T, N>> : remove_all_extents<T> {};
+
+ template <typename T>
+ using remove_all_extents_t = typename remove_all_extents<T>::type;
+
+ template <std::size_t N>
+ using size_constant = std::integral_constant<std::size_t, N>;
+
+ template <std::size_t I, typename T>
+ struct indexed_type : size_constant<I>, identity<T> {};
+
+ template <bool... Bs>
+ using all = std::is_same<integer_sequence<bool, true, Bs...>,
+ integer_sequence<bool, Bs..., true>>;
+
+#ifdef MPARK_TYPE_PACK_ELEMENT
+ template <std::size_t I, typename... Ts>
+ using type_pack_element_t = __type_pack_element<I, Ts...>;
+#else
+ template <std::size_t I, typename... Ts>
+ struct type_pack_element_impl {
+ private:
+ template <typename>
+ struct set;
+
+ template <std::size_t... Is>
+ struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {};
+
+ template <typename T>
+ inline static std::enable_if<true, T> impl(indexed_type<I, T>);
+
+ inline static std::enable_if<false> impl(...);
+
+ public:
+ using type = decltype(impl(set<index_sequence_for<Ts...>>{}));
+ };
+
+ template <std::size_t I, typename... Ts>
+ using type_pack_element = typename type_pack_element_impl<I, Ts...>::type;
+
+ template <std::size_t I, typename... Ts>
+ using type_pack_element_t = typename type_pack_element<I, Ts...>::type;
+#endif
+
+#ifdef MPARK_TRIVIALITY_TYPE_TRAITS
+ using std::is_trivially_copy_constructible;
+ using std::is_trivially_move_constructible;
+ using std::is_trivially_copy_assignable;
+ using std::is_trivially_move_assignable;
+#else
+ template <typename T>
+ struct is_trivially_copy_constructible
+ : bool_constant<
+ std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {};
+
+ template <typename T>
+ struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {};
+
+ template <typename T>
+ struct is_trivially_copy_assignable
+ : bool_constant<
+ std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {};
+
+ template <typename T>
+ struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {};
+#endif
+
+ template <typename T, bool>
+ struct dependent_type : T {};
+
+ template <typename Is, std::size_t J>
+ struct push_back;
+
+ template <typename Is, std::size_t J>
+ using push_back_t = typename push_back<Is, J>::type;
+
+ template <std::size_t... Is, std::size_t J>
+ struct push_back<index_sequence<Is...>, J> {
+ using type = index_sequence<Is..., J>;
+ };
+
+ } // namespace lib
+} // namespace mpark
+
+#undef RETURN
+
+#endif // MPARK_LIB_HPP
+
+
+namespace mpark {
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+
+#define AUTO auto
+#define AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto &&
+#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; }
+
+#define DECLTYPE_AUTO decltype(auto)
+#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; }
+
+#else
+
+#define AUTO auto
+#define AUTO_RETURN(...) \
+ -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; }
+
+#define AUTO_REFREF auto
+#define AUTO_REFREF_RETURN(...) \
+ -> decltype((__VA_ARGS__)) { \
+ static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \
+ return __VA_ARGS__; \
+ }
+
+#define DECLTYPE_AUTO auto
+#define DECLTYPE_AUTO_RETURN(...) \
+ -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
+
+#endif
+
+ class bad_variant_access : public std::exception {
+ public:
+ virtual const char *what() const noexcept { return "bad_variant_access"; }
+ };
+
+ [[noreturn]] inline void throw_bad_variant_access() {
+#ifdef MPARK_EXCEPTIONS
+ throw bad_variant_access{};
+#else
+ std::terminate();
+#ifdef MPARK_BUILTIN_UNREACHABLE
+ __builtin_unreachable();
+#endif
+#endif
+ }
+
+ template <typename... Ts>
+ class variant;
+
+ template <typename T>
+ struct variant_size;
+
+#ifdef MPARK_VARIABLE_TEMPLATES
+ template <typename T>
+ constexpr std::size_t variant_size_v = variant_size<T>::value;
+#endif
+
+ template <typename T>
+ struct variant_size<const T> : variant_size<T> {};
+
+ template <typename T>
+ struct variant_size<volatile T> : variant_size<T> {};
+
+ template <typename T>
+ struct variant_size<const volatile T> : variant_size<T> {};
+
+ template <typename... Ts>
+ struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {};
+
+ template <std::size_t I, typename T>
+ struct variant_alternative;
+
+ template <std::size_t I, typename T>
+ using variant_alternative_t = typename variant_alternative<I, T>::type;
+
+ template <std::size_t I, typename T>
+ struct variant_alternative<I, const T>
+ : std::add_const<variant_alternative_t<I, T>> {};
+
+ template <std::size_t I, typename T>
+ struct variant_alternative<I, volatile T>
+ : std::add_volatile<variant_alternative_t<I, T>> {};
+
+ template <std::size_t I, typename T>
+ struct variant_alternative<I, const volatile T>
+ : std::add_cv<variant_alternative_t<I, T>> {};
+
+ template <std::size_t I, typename... Ts>
+ struct variant_alternative<I, variant<Ts...>> {
+ static_assert(I < sizeof...(Ts),
+ "Index out of bounds in std::variant_alternative<>");
+ using type = lib::type_pack_element_t<I, Ts...>;
+ };
+
+ constexpr std::size_t variant_npos = static_cast<std::size_t>(-1);
+
+ namespace detail {
+
+ constexpr std::size_t not_found = static_cast<std::size_t>(-1);
+ constexpr std::size_t ambiguous = static_cast<std::size_t>(-2);
+
+#ifdef MPARK_CPP14_CONSTEXPR
+ template <typename T, typename... Ts>
+ inline constexpr std::size_t find_index() {
+ constexpr lib::array<bool, sizeof...(Ts)> matches = {
+ {std::is_same<T, Ts>::value...}
+ };
+ std::size_t result = not_found;
+ for (std::size_t i = 0; i < sizeof...(Ts); ++i) {
+ if (matches[i]) {
+ if (result != not_found) {
+ return ambiguous;
+ }
+ result = i;
+ }
+ }
+ return result;
+ }
+#else
+ inline constexpr std::size_t find_index_impl(std::size_t result,
+ std::size_t) {
+ return result;
+ }
+
+ template <typename... Bs>
+ inline constexpr std::size_t find_index_impl(std::size_t result,
+ std::size_t idx,
+ bool b,
+ Bs... bs) {
+ return b ? (result != not_found ? ambiguous
+ : find_index_impl(idx, idx + 1, bs...))
+ : find_index_impl(result, idx + 1, bs...);
+ }
+
+ template <typename T, typename... Ts>
+ inline constexpr std::size_t find_index() {
+ return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...);
+ }
+#endif
+
+ template <std::size_t I>
+ using find_index_sfinae_impl =
+ lib::enable_if_t<I != not_found && I != ambiguous,
+ lib::size_constant<I>>;
+
+ template <typename T, typename... Ts>
+ using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>;
+
+ template <std::size_t I>
+ struct find_index_checked_impl : lib::size_constant<I> {
+ static_assert(I != not_found, "the specified type is not found.");
+ static_assert(I != ambiguous, "the specified type is ambiguous.");
+ };
+
+ template <typename T, typename... Ts>
+ using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>;
+
+ struct valueless_t {};
+
+ enum class Trait { TriviallyAvailable, Available, Unavailable };
+
+ template <typename T,
+ template <typename> class IsTriviallyAvailable,
+ template <typename> class IsAvailable>
+ inline constexpr Trait trait() {
+ return IsTriviallyAvailable<T>::value
+ ? Trait::TriviallyAvailable
+ : IsAvailable<T>::value ? Trait::Available
+ : Trait::Unavailable;
+ }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+ template <typename... Traits>
+ inline constexpr Trait common_trait(Traits... traits) {
+ Trait result = Trait::TriviallyAvailable;
+ for (Trait t : {traits...}) {
+ if (static_cast<int>(t) > static_cast<int>(result)) {
+ result = t;
+ }
+ }
+ return result;
+ }
+#else
+ inline constexpr Trait common_trait_impl(Trait result) { return result; }
+
+ template <typename... Traits>
+ inline constexpr Trait common_trait_impl(Trait result,
+ Trait t,
+ Traits... ts) {
+ return static_cast<int>(t) > static_cast<int>(result)
+ ? common_trait_impl(t, ts...)
+ : common_trait_impl(result, ts...);
+ }
+
+ template <typename... Traits>
+ inline constexpr Trait common_trait(Traits... ts) {
+ return common_trait_impl(Trait::TriviallyAvailable, ts...);
+ }
+#endif
+
+ template <typename... Ts>
+ struct traits {
+ static constexpr Trait copy_constructible_trait =
+ common_trait(trait<Ts,
+ lib::is_trivially_copy_constructible,
+ std::is_copy_constructible>()...);
+
+ static constexpr Trait move_constructible_trait =
+ common_trait(trait<Ts,
+ lib::is_trivially_move_constructible,
+ std::is_move_constructible>()...);
+
+ static constexpr Trait copy_assignable_trait =
+ common_trait(copy_constructible_trait,
+ trait<Ts,
+ lib::is_trivially_copy_assignable,
+ std::is_copy_assignable>()...);
+
+ static constexpr Trait move_assignable_trait =
+ common_trait(move_constructible_trait,
+ trait<Ts,
+ lib::is_trivially_move_assignable,
+ std::is_move_assignable>()...);
+
+ static constexpr Trait destructible_trait =
+ common_trait(trait<Ts,
+ std::is_trivially_destructible,
+ std::is_destructible>()...);
+ };
+
+ namespace access {
+
+ struct recursive_union {
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+ template <typename V>
+ inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) {
+ return lib::forward<V>(v).head_;
+ }
+
+ template <typename V, std::size_t I>
+ inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) {
+ return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{});
+ }
+#else
+ template <std::size_t I, bool Dummy = true>
+ struct get_alt_impl {
+ template <typename V>
+ inline constexpr AUTO_REFREF operator()(V &&v) const
+ AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_))
+ };
+
+ template <bool Dummy>
+ struct get_alt_impl<0, Dummy> {
+ template <typename V>
+ inline constexpr AUTO_REFREF operator()(V &&v) const
+ AUTO_REFREF_RETURN(lib::forward<V>(v).head_)
+ };
+
+ template <typename V, std::size_t I>
+ inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>)
+ AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v)))
+#endif
+ };
+
+ struct base {
+ template <std::size_t I, typename V>
+ inline static constexpr AUTO_REFREF get_alt(V &&v)
+ AUTO_REFREF_RETURN(recursive_union::get_alt(
+ data(lib::forward<V>(v)), in_place_index_t<I>{}))
+ };
+
+ struct variant {
+ template <std::size_t I, typename V>
+ inline static constexpr AUTO_REFREF get_alt(V &&v)
+ AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_))
+ };
+
+ } // namespace access
+
+ namespace visitation {
+
+ struct base {
+ template <typename T>
+ inline static constexpr const T &at(const T &elem) {
+ return elem;
+ }
+
+ template <typename T, std::size_t N, typename... Is>
+ inline static constexpr const lib::remove_all_extents_t<T> &at(
+ const lib::array<T, N> &elems, std::size_t i, Is... is) {
+ return at(elems[i], is...);
+ }
+
+ template <typename F, typename... Fs>
+ inline static constexpr int visit_visitor_return_type_check() {
+ static_assert(lib::all<std::is_same<F, Fs>::value...>::value,
+ "`mpark::visit` requires the visitor to have a single "
+ "return type.");
+ return 0;
+ }
+
+ template <typename... Fs>
+ inline static constexpr lib::array<
+ lib::common_type_t<lib::decay_t<Fs>...>,
+ sizeof...(Fs)>
+ make_farray(Fs &&... fs) {
+ using result = lib::array<lib::common_type_t<lib::decay_t<Fs>...>,
+ sizeof...(Fs)>;
+ return visit_visitor_return_type_check<lib::decay_t<Fs>...>(),
+ result{{lib::forward<Fs>(fs)...}};
+ }
+
+ template <std::size_t... Is>
+ struct dispatcher {
+ template <typename F, typename... Vs>
+ struct impl {
+ inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs)
+ DECLTYPE_AUTO_RETURN(lib::invoke(
+ static_cast<F>(f),
+ access::base::get_alt<Is>(static_cast<Vs>(vs))...))
+ };
+ };
+
+ template <typename F, typename... Vs, std::size_t... Is>
+ inline static constexpr AUTO make_dispatch(lib::index_sequence<Is...>)
+ AUTO_RETURN(&dispatcher<Is...>::template impl<F, Vs...>::dispatch)
+
+ template <std::size_t I, typename F, typename... Vs>
+ inline static constexpr AUTO make_fdiagonal_impl()
+ AUTO_RETURN(make_dispatch<F, Vs...>(
+ lib::index_sequence<lib::indexed_type<I, Vs>::value...>{}))
+
+ template <typename F, typename... Vs, std::size_t... Is>
+ inline static constexpr AUTO make_fdiagonal_impl(
+ lib::index_sequence<Is...>)
+ AUTO_RETURN(make_farray(make_fdiagonal_impl<Is, F, Vs...>()...))
+
+ template <typename F, typename V, typename... Vs>
+ inline static constexpr /* auto * */ auto make_fdiagonal()
+ -> decltype(make_fdiagonal_impl<F, V, Vs...>(
+ lib::make_index_sequence<lib::decay_t<V>::size()>{})) {
+ static_assert(lib::all<(lib::decay_t<V>::size() ==
+ lib::decay_t<Vs>::size())...>::value,
+ "all of the variants must be the same size.");
+ return make_fdiagonal_impl<F, V, Vs...>(
+ lib::make_index_sequence<lib::decay_t<V>::size()>{});
+ }
+
+#ifdef MPARK_RETURN_TYPE_DEDUCTION
+ template <typename F, typename... Vs, typename Is>
+ inline static constexpr auto make_fmatrix_impl(Is is) {
+ return make_dispatch<F, Vs...>(is);
+ }
+
+ template <typename F,
+ typename... Vs,
+ typename Is,
+ std::size_t... Js,
+ typename... Ls>
+ inline static constexpr auto make_fmatrix_impl(
+ Is, lib::index_sequence<Js...>, Ls... ls) {
+ return make_farray(make_fmatrix_impl<F, Vs...>(
+ lib::push_back_t<Is, Js>{}, ls...)...);
+ }
+
+ template <typename F, typename... Vs>
+ inline static constexpr auto make_fmatrix() {
+ return make_fmatrix_impl<F, Vs...>(
+ lib::index_sequence<>{},
+ lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...);
+ }
+#else
+ template <typename F, typename... Vs>
+ struct make_fmatrix_impl {
+ template <typename...>
+ struct impl;
+
+ template <typename Is>
+ struct impl<Is> {
+ inline constexpr AUTO operator()() const
+ AUTO_RETURN(make_dispatch<F, Vs...>(Is{}))
+ };
+
+ template <typename Is, std::size_t... Js, typename... Ls>
+ struct impl<Is, lib::index_sequence<Js...>, Ls...> {
+ inline constexpr AUTO operator()() const
+ AUTO_RETURN(
+ make_farray(impl<lib::push_back_t<Is, Js>, Ls...>{}()...))
+ };
+ };
+
+ template <typename F, typename... Vs>
+ inline static constexpr AUTO make_fmatrix()
+ AUTO_RETURN(
+ typename make_fmatrix_impl<F, Vs...>::template impl<
+ lib::index_sequence<>,
+ lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}())
+#endif
+ }; // namespace base
+
+ template <typename F, typename... Vs>
+ using FDiagonal = decltype(base::make_fdiagonal<F, Vs...>());
+
+ template <typename F, typename... Vs>
+ struct fdiagonal {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4268)
+#endif
+ static constexpr FDiagonal<F, Vs...> value =
+ base::make_fdiagonal<F, Vs...>();
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ };
+
+ template <typename F, typename... Vs>
+ constexpr FDiagonal<F, Vs...> fdiagonal<F, Vs...>::value;
+
+ template <typename F, typename... Vs>
+ using FMatrix = decltype(base::make_fmatrix<F, Vs...>());
+
+ template <typename F, typename... Vs>
+ struct fmatrix {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4268)
+#endif
+ static constexpr FMatrix<F, Vs...> value =
+ base::make_fmatrix<F, Vs...>();
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ };
+
+ template <typename F, typename... Vs>
+ constexpr FMatrix<F, Vs...> fmatrix<F, Vs...>::value;
+
+ struct alt {
+ template <typename Visitor, typename... Vs>
+ inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+ Visitor &&visitor,
+ Vs &&... vs)
+ DECLTYPE_AUTO_RETURN(base::at(
+ fdiagonal<Visitor &&,
+ decltype(as_base(lib::forward<Vs>(vs)))...>::value,
+ index)(lib::forward<Visitor>(visitor),
+ as_base(lib::forward<Vs>(vs))...))
+
+ template <typename Visitor, typename... Vs>
+ inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+ Vs &&... vs)
+ DECLTYPE_AUTO_RETURN(base::at(
+ fmatrix<Visitor &&,
+ decltype(as_base(lib::forward<Vs>(vs)))...>::value,
+ vs.index()...)(lib::forward<Visitor>(visitor),
+ as_base(lib::forward<Vs>(vs))...))
+ };
+
+ struct variant {
+ private:
+ template <typename Visitor, typename... Values>
+ struct visit_exhaustive_visitor_check {
+ static_assert(
+ lib::is_invocable<Visitor, Values...>::value,
+ "`mpark::visit` requires the visitor to be exhaustive.");
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+ inline constexpr DECLTYPE_AUTO operator()(Visitor &&visitor,
+ Values &&... values) const
+ DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor),
+ lib::forward<Values>(values)...))
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ };
+
+ template <typename Visitor>
+ struct value_visitor {
+ Visitor &&visitor_;
+
+ template <typename... Alts>
+ inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const
+ DECLTYPE_AUTO_RETURN(
+ visit_exhaustive_visitor_check<
+ Visitor,
+ decltype((lib::forward<Alts>(alts).value))...>{}(
+ lib::forward<Visitor>(visitor_),
+ lib::forward<Alts>(alts).value...))
+ };
+
+ template <typename Visitor>
+ inline static constexpr AUTO make_value_visitor(Visitor &&visitor)
+ AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)})
+
+ public:
+ template <typename Visitor, typename... Vs>
+ inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index,
+ Visitor &&visitor,
+ Vs &&... vs)
+ DECLTYPE_AUTO_RETURN(
+ alt::visit_alt_at(index,
+ lib::forward<Visitor>(visitor),
+ lib::forward<Vs>(vs).impl_...))
+
+ template <typename Visitor, typename... Vs>
+ inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor,
+ Vs &&... vs)
+ DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward<Visitor>(visitor),
+ lib::forward<Vs>(vs).impl_...))
+
+ template <typename Visitor, typename... Vs>
+ inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index,
+ Visitor &&visitor,
+ Vs &&... vs)
+ DECLTYPE_AUTO_RETURN(
+ visit_alt_at(index,
+ make_value_visitor(lib::forward<Visitor>(visitor)),
+ lib::forward<Vs>(vs)...))
+
+ template <typename Visitor, typename... Vs>
+ inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor,
+ Vs &&... vs)
+ DECLTYPE_AUTO_RETURN(
+ visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)),
+ lib::forward<Vs>(vs)...))
+ };
+
+ } // namespace visitation
+
+ template <std::size_t Index, typename T>
+ struct alt {
+ using value_type = T;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+ template <typename... Args>
+ inline explicit constexpr alt(in_place_t, Args &&... args)
+ : value(lib::forward<Args>(args)...) {}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ T value;
+ };
+
+ template <Trait DestructibleTrait, std::size_t Index, typename... Ts>
+ union recursive_union;
+
+ template <Trait DestructibleTrait, std::size_t Index>
+ union recursive_union<DestructibleTrait, Index> {};
+
+#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor) \
+ template <std::size_t Index, typename T, typename... Ts> \
+ union recursive_union<destructible_trait, Index, T, Ts...> { \
+ public: \
+ inline explicit constexpr recursive_union(valueless_t) noexcept \
+ : dummy_{} {} \
+ \
+ template <typename... Args> \
+ inline explicit constexpr recursive_union(in_place_index_t<0>, \
+ Args &&... args) \
+ : head_(in_place_t{}, lib::forward<Args>(args)...) {} \
+ \
+ template <std::size_t I, typename... Args> \
+ inline explicit constexpr recursive_union(in_place_index_t<I>, \
+ Args &&... args) \
+ : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \
+ \
+ recursive_union(const recursive_union &) = default; \
+ recursive_union(recursive_union &&) = default; \
+ \
+ destructor \
+ \
+ recursive_union &operator=(const recursive_union &) = default; \
+ recursive_union &operator=(recursive_union &&) = default; \
+ \
+ private: \
+ char dummy_; \
+ alt<Index, T> head_; \
+ recursive_union<destructible_trait, Index + 1, Ts...> tail_; \
+ \
+ friend struct access::recursive_union; \
+ }
+
+ MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable,
+ ~recursive_union() = default;);
+ MPARK_VARIANT_RECURSIVE_UNION(Trait::Available,
+ ~recursive_union() {});
+ MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable,
+ ~recursive_union() = delete;);
+
+#undef MPARK_VARIANT_RECURSIVE_UNION
+
+ using index_t = unsigned int;
+
+ template <Trait DestructibleTrait, typename... Ts>
+ class base {
+ public:
+ inline explicit constexpr base(valueless_t tag) noexcept
+ : data_(tag), index_(static_cast<index_t>(-1)) {}
+
+ template <std::size_t I, typename... Args>
+ inline explicit constexpr base(in_place_index_t<I>, Args &&... args)
+ : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...),
+ index_(I) {}
+
+ inline constexpr bool valueless_by_exception() const noexcept {
+ return index_ == static_cast<index_t>(-1);
+ }
+
+ inline constexpr std::size_t index() const noexcept {
+ return valueless_by_exception() ? variant_npos : index_;
+ }
+
+ protected:
+ using data_t = recursive_union<DestructibleTrait, 0, Ts...>;
+
+ friend inline constexpr base &as_base(base &b) { return b; }
+ friend inline constexpr const base &as_base(const base &b) { return b; }
+ friend inline constexpr base &&as_base(base &&b) { return lib::move(b); }
+ friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); }
+
+ friend inline constexpr data_t &data(base &b) { return b.data_; }
+ friend inline constexpr const data_t &data(const base &b) { return b.data_; }
+ friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; }
+ friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; }
+
+ inline static constexpr std::size_t size() { return sizeof...(Ts); }
+
+ data_t data_;
+ index_t index_;
+
+ friend struct access::base;
+ friend struct visitation::base;
+ };
+
+ struct dtor {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+ template <typename Alt>
+ inline void operator()(Alt &alt) const noexcept { alt.~Alt(); }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ };
+
+#if defined(_MSC_VER) && _MSC_VER < 1910
+#define INHERITING_CTOR(type, base) \
+ template <typename... Args> \
+ inline explicit constexpr type(Args &&... args) \
+ : base(lib::forward<Args>(args)...) {}
+#else
+#define INHERITING_CTOR(type, base) using base::base;
+#endif
+
+ template <typename Traits, Trait = Traits::destructible_trait>
+ class destructor;
+
+#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \
+ template <typename... Ts> \
+ class destructor<traits<Ts...>, destructible_trait> \
+ : public base<destructible_trait, Ts...> { \
+ using super = base<destructible_trait, Ts...>; \
+ \
+ public: \
+ INHERITING_CTOR(destructor, super) \
+ using super::operator=; \
+ \
+ destructor(const destructor &) = default; \
+ destructor(destructor &&) = default; \
+ definition \
+ destructor &operator=(const destructor &) = default; \
+ destructor &operator=(destructor &&) = default; \
+ \
+ protected: \
+ destroy \
+ }
+
+ MPARK_VARIANT_DESTRUCTOR(
+ Trait::TriviallyAvailable,
+ ~destructor() = default;,
+ inline void destroy() noexcept {
+ this->index_ = static_cast<index_t>(-1);
+ });
+
+ MPARK_VARIANT_DESTRUCTOR(
+ Trait::Available,
+ ~destructor() { destroy(); },
+ inline void destroy() noexcept {
+ if (!this->valueless_by_exception()) {
+ visitation::alt::visit_alt(dtor{}, *this);
+ }
+ this->index_ = static_cast<index_t>(-1);
+ });
+
+ MPARK_VARIANT_DESTRUCTOR(
+ Trait::Unavailable,
+ ~destructor() = delete;,
+ inline void destroy() noexcept = delete;);
+
+#undef MPARK_VARIANT_DESTRUCTOR
+
+ template <typename Traits>
+ class constructor : public destructor<Traits> {
+ using super = destructor<Traits>;
+
+ public:
+ INHERITING_CTOR(constructor, super)
+ using super::operator=;
+
+ protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+ struct ctor {
+ template <typename LhsAlt, typename RhsAlt>
+ inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const {
+ constructor::construct_alt(lhs_alt,
+ lib::forward<RhsAlt>(rhs_alt).value);
+ }
+ };
+#endif
+
+ template <std::size_t I, typename T, typename... Args>
+ inline static T &construct_alt(alt<I, T> &a, Args &&... args) {
+ ::new (static_cast<void *>(lib::addressof(a)))
+ alt<I, T>(in_place_t{}, lib::forward<Args>(args)...);
+ return a.value;
+ }
+
+ template <typename Rhs>
+ inline static void generic_construct(constructor &lhs, Rhs &&rhs) {
+ lhs.destroy();
+ if (!rhs.valueless_by_exception()) {
+ visitation::alt::visit_alt_at(
+ rhs.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+ [](auto &lhs_alt, auto &&rhs_alt) {
+ constructor::construct_alt(
+ lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value);
+ }
+#else
+ ctor{}
+#endif
+ ,
+ lhs,
+ lib::forward<Rhs>(rhs));
+ lhs.index_ = rhs.index_;
+ }
+ }
+ };
+
+ template <typename Traits, Trait = Traits::move_constructible_trait>
+ class move_constructor;
+
+#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \
+ template <typename... Ts> \
+ class move_constructor<traits<Ts...>, move_constructible_trait> \
+ : public constructor<traits<Ts...>> { \
+ using super = constructor<traits<Ts...>>; \
+ \
+ public: \
+ INHERITING_CTOR(move_constructor, super) \
+ using super::operator=; \
+ \
+ move_constructor(const move_constructor &) = default; \
+ definition \
+ ~move_constructor() = default; \
+ move_constructor &operator=(const move_constructor &) = default; \
+ move_constructor &operator=(move_constructor &&) = default; \
+ }
+
+ MPARK_VARIANT_MOVE_CONSTRUCTOR(
+ Trait::TriviallyAvailable,
+ move_constructor(move_constructor &&that) = default;);
+
+ MPARK_VARIANT_MOVE_CONSTRUCTOR(
+ Trait::Available,
+ move_constructor(move_constructor &&that) noexcept(
+ lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value)
+ : move_constructor(valueless_t{}) {
+ this->generic_construct(*this, lib::move(that));
+ });
+
+ MPARK_VARIANT_MOVE_CONSTRUCTOR(
+ Trait::Unavailable,
+ move_constructor(move_constructor &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_CONSTRUCTOR
+
+ template <typename Traits, Trait = Traits::copy_constructible_trait>
+ class copy_constructor;
+
+#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \
+ template <typename... Ts> \
+ class copy_constructor<traits<Ts...>, copy_constructible_trait> \
+ : public move_constructor<traits<Ts...>> { \
+ using super = move_constructor<traits<Ts...>>; \
+ \
+ public: \
+ INHERITING_CTOR(copy_constructor, super) \
+ using super::operator=; \
+ \
+ definition \
+ copy_constructor(copy_constructor &&) = default; \
+ ~copy_constructor() = default; \
+ copy_constructor &operator=(const copy_constructor &) = default; \
+ copy_constructor &operator=(copy_constructor &&) = default; \
+ }
+
+ MPARK_VARIANT_COPY_CONSTRUCTOR(
+ Trait::TriviallyAvailable,
+ copy_constructor(const copy_constructor &that) = default;);
+
+ MPARK_VARIANT_COPY_CONSTRUCTOR(
+ Trait::Available,
+ copy_constructor(const copy_constructor &that)
+ : copy_constructor(valueless_t{}) {
+ this->generic_construct(*this, that);
+ });
+
+ MPARK_VARIANT_COPY_CONSTRUCTOR(
+ Trait::Unavailable,
+ copy_constructor(const copy_constructor &) = delete;);
+
+#undef MPARK_VARIANT_COPY_CONSTRUCTOR
+
+ template <typename Traits>
+ class assignment : public copy_constructor<Traits> {
+ using super = copy_constructor<Traits>;
+
+ public:
+ INHERITING_CTOR(assignment, super)
+ using super::operator=;
+
+ template <std::size_t I, typename... Args>
+ inline /* auto & */ auto emplace(Args &&... args)
+ -> decltype(this->construct_alt(access::base::get_alt<I>(*this),
+ lib::forward<Args>(args)...)) {
+ this->destroy();
+ auto &result = this->construct_alt(access::base::get_alt<I>(*this),
+ lib::forward<Args>(args)...);
+ this->index_ = I;
+ return result;
+ }
+
+ protected:
+#ifndef MPARK_GENERIC_LAMBDAS
+ template <typename That>
+ struct assigner {
+ template <typename ThisAlt, typename ThatAlt>
+ inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const {
+ self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value);
+ }
+ assignment *self;
+ };
+#endif
+
+ template <std::size_t I, typename T, typename Arg>
+ inline void assign_alt(alt<I, T> &a, Arg &&arg) {
+ if (this->index() == I) {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4244)
+#endif
+ a.value = lib::forward<Arg>(arg);
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ } else {
+ struct {
+ void operator()(std::true_type) const {
+ this_->emplace<I>(lib::forward<Arg>(arg_));
+ }
+ void operator()(std::false_type) const {
+ this_->emplace<I>(T(lib::forward<Arg>(arg_)));
+ }
+ assignment *this_;
+ Arg &&arg_;
+ } impl{this, lib::forward<Arg>(arg)};
+ impl(lib::bool_constant<
+ std::is_nothrow_constructible<T, Arg>::value ||
+ !std::is_nothrow_move_constructible<T>::value>{});
+ }
+ }
+
+ template <typename That>
+ inline void generic_assign(That &&that) {
+ if (this->valueless_by_exception() && that.valueless_by_exception()) {
+ // do nothing.
+ } else if (that.valueless_by_exception()) {
+ this->destroy();
+ } else {
+ visitation::alt::visit_alt_at(
+ that.index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+ [this](auto &this_alt, auto &&that_alt) {
+ this->assign_alt(
+ this_alt, lib::forward<decltype(that_alt)>(that_alt).value);
+ }
+#else
+ assigner<That>{this}
+#endif
+ ,
+ *this,
+ lib::forward<That>(that));
+ }
+ }
+ };
+
+ template <typename Traits, Trait = Traits::move_assignable_trait>
+ class move_assignment;
+
+#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \
+ template <typename... Ts> \
+ class move_assignment<traits<Ts...>, move_assignable_trait> \
+ : public assignment<traits<Ts...>> { \
+ using super = assignment<traits<Ts...>>; \
+ \
+ public: \
+ INHERITING_CTOR(move_assignment, super) \
+ using super::operator=; \
+ \
+ move_assignment(const move_assignment &) = default; \
+ move_assignment(move_assignment &&) = default; \
+ ~move_assignment() = default; \
+ move_assignment &operator=(const move_assignment &) = default; \
+ definition \
+ }
+
+ MPARK_VARIANT_MOVE_ASSIGNMENT(
+ Trait::TriviallyAvailable,
+ move_assignment &operator=(move_assignment &&that) = default;);
+
+ MPARK_VARIANT_MOVE_ASSIGNMENT(
+ Trait::Available,
+ move_assignment &
+ operator=(move_assignment &&that) noexcept(
+ lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+ std::is_nothrow_move_assignable<Ts>::value)...>::value) {
+ this->generic_assign(lib::move(that));
+ return *this;
+ });
+
+ MPARK_VARIANT_MOVE_ASSIGNMENT(
+ Trait::Unavailable,
+ move_assignment &operator=(move_assignment &&) = delete;);
+
+#undef MPARK_VARIANT_MOVE_ASSIGNMENT
+
+ template <typename Traits, Trait = Traits::copy_assignable_trait>
+ class copy_assignment;
+
+#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \
+ template <typename... Ts> \
+ class copy_assignment<traits<Ts...>, copy_assignable_trait> \
+ : public move_assignment<traits<Ts...>> { \
+ using super = move_assignment<traits<Ts...>>; \
+ \
+ public: \
+ INHERITING_CTOR(copy_assignment, super) \
+ using super::operator=; \
+ \
+ copy_assignment(const copy_assignment &) = default; \
+ copy_assignment(copy_assignment &&) = default; \
+ ~copy_assignment() = default; \
+ definition \
+ copy_assignment &operator=(copy_assignment &&) = default; \
+ }
+
+ MPARK_VARIANT_COPY_ASSIGNMENT(
+ Trait::TriviallyAvailable,
+ copy_assignment &operator=(const copy_assignment &that) = default;);
+
+ MPARK_VARIANT_COPY_ASSIGNMENT(
+ Trait::Available,
+ copy_assignment &operator=(const copy_assignment &that) {
+ this->generic_assign(that);
+ return *this;
+ });
+
+ MPARK_VARIANT_COPY_ASSIGNMENT(
+ Trait::Unavailable,
+ copy_assignment &operator=(const copy_assignment &) = delete;);
+
+#undef MPARK_VARIANT_COPY_ASSIGNMENT
+
+ template <typename... Ts>
+ class impl : public copy_assignment<traits<Ts...>> {
+ using super = copy_assignment<traits<Ts...>>;
+
+ public:
+ INHERITING_CTOR(impl, super)
+ using super::operator=;
+
+ template <std::size_t I, typename Arg>
+ inline void assign(Arg &&arg) {
+ this->assign_alt(access::base::get_alt<I>(*this),
+ lib::forward<Arg>(arg));
+ }
+
+ inline void swap(impl &that) {
+ if (this->valueless_by_exception() && that.valueless_by_exception()) {
+ // do nothing.
+ } else if (this->index() == that.index()) {
+ visitation::alt::visit_alt_at(this->index(),
+#ifdef MPARK_GENERIC_LAMBDAS
+ [](auto &this_alt, auto &that_alt) {
+ using std::swap;
+ swap(this_alt.value,
+ that_alt.value);
+ }
+#else
+ swapper{}
+#endif
+ ,
+ *this,
+ that);
+ } else {
+ impl *lhs = this;
+ impl *rhs = lib::addressof(that);
+ if (lhs->move_nothrow() && !rhs->move_nothrow()) {
+ std::swap(lhs, rhs);
+ }
+ impl tmp(lib::move(*rhs));
+#ifdef MPARK_EXCEPTIONS
+ // EXTENSION: When the move construction of `lhs` into `rhs` throws
+ // and `tmp` is nothrow move constructible then we move `tmp` back
+ // into `rhs` and provide the strong exception safety guarantee.
+ try {
+ this->generic_construct(*rhs, lib::move(*lhs));
+ } catch (...) {
+ if (tmp.move_nothrow()) {
+ this->generic_construct(*rhs, lib::move(tmp));
+ }
+ throw;
+ }
+#else
+ this->generic_construct(*rhs, lib::move(*lhs));
+#endif
+ this->generic_construct(*lhs, lib::move(tmp));
+ }
+ }
+
+ private:
+#ifndef MPARK_GENERIC_LAMBDAS
+ struct swapper {
+ template <typename ThisAlt, typename ThatAlt>
+ inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const {
+ using std::swap;
+ swap(this_alt.value, that_alt.value);
+ }
+ };
+#endif
+
+ inline constexpr bool move_nothrow() const {
+ return this->valueless_by_exception() ||
+ lib::array<bool, sizeof...(Ts)>{
+ {std::is_nothrow_move_constructible<Ts>::value...}
+ }[this->index()];
+ }
+ };
+
+ template <std::size_t I, typename T>
+ struct overload_leaf {
+ using F = lib::size_constant<I> (*)(T);
+ operator F() const { return nullptr; }
+ };
+
+ template <typename... Ts>
+ struct overload_impl {
+ private:
+ template <typename>
+ struct impl;
+
+ template <std::size_t... Is>
+ struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>... {};
+
+ public:
+ using type = impl<lib::index_sequence_for<Ts...>>;
+ };
+
+ template <typename... Ts>
+ using overload = typename overload_impl<Ts...>::type;
+
+ template <typename T, typename... Ts>
+ using best_match = lib::invoke_result_t<overload<Ts...>, T &&>;
+
+ template <typename T>
+ struct is_in_place_index : std::false_type {};
+
+ template <std::size_t I>
+ struct is_in_place_index<in_place_index_t<I>> : std::true_type {};
+
+ template <typename T>
+ struct is_in_place_type : std::false_type {};
+
+ template <typename T>
+ struct is_in_place_type<in_place_type_t<T>> : std::true_type {};
+
+ } // detail
+
+ template <typename... Ts>
+ class variant {
+ static_assert(0 < sizeof...(Ts),
+ "variant must consist of at least one alternative.");
+
+ static_assert(lib::all<!std::is_array<Ts>::value...>::value,
+ "variant can not have an array type as an alternative.");
+
+ static_assert(lib::all<!std::is_reference<Ts>::value...>::value,
+ "variant can not have a reference type as an alternative.");
+
+ static_assert(lib::all<!std::is_void<Ts>::value...>::value,
+ "variant can not have a void type as an alternative.");
+
+ public:
+ template <
+ typename Front = lib::type_pack_element_t<0, Ts...>,
+ lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0>
+ inline constexpr variant()
+ : impl_(in_place_index_t<0>{}) {}
+
+ variant(const variant &) = default;
+ variant(variant &&) = default;
+
+ template <
+ typename Arg,
+ typename Decayed = lib::decay_t<Arg>,
+ lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0,
+ lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0,
+ lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0,
+ std::size_t I = detail::best_match<Arg, Ts...>::value,
+ typename T = lib::type_pack_element_t<I, Ts...>,
+ lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0>
+ inline constexpr variant(Arg &&arg) noexcept(
+ std::is_nothrow_constructible<T, Arg>::value)
+ : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {}
+
+ template <
+ std::size_t I,
+ typename... Args,
+ typename T = lib::type_pack_element_t<I, Ts...>,
+ lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+ inline explicit constexpr variant(
+ in_place_index_t<I>,
+ Args &&... args) noexcept(std::is_nothrow_constructible<T,
+ Args...>::value)
+ : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+ template <
+ std::size_t I,
+ typename Up,
+ typename... Args,
+ typename T = lib::type_pack_element_t<I, Ts...>,
+ lib::enable_if_t<std::is_constructible<T,
+ std::initializer_list<Up> &,
+ Args...>::value,
+ int> = 0>
+ inline explicit constexpr variant(
+ in_place_index_t<I>,
+ std::initializer_list<Up> il,
+ Args &&... args) noexcept(std::
+ is_nothrow_constructible<
+ T,
+ std::initializer_list<Up> &,
+ Args...>::value)
+ : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+ template <
+ typename T,
+ typename... Args,
+ std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+ lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+ inline explicit constexpr variant(
+ in_place_type_t<T>,
+ Args &&... args) noexcept(std::is_nothrow_constructible<T,
+ Args...>::value)
+ : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {}
+
+ template <
+ typename T,
+ typename Up,
+ typename... Args,
+ std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+ lib::enable_if_t<std::is_constructible<T,
+ std::initializer_list<Up> &,
+ Args...>::value,
+ int> = 0>
+ inline explicit constexpr variant(
+ in_place_type_t<T>,
+ std::initializer_list<Up> il,
+ Args &&... args) noexcept(std::
+ is_nothrow_constructible<
+ T,
+ std::initializer_list<Up> &,
+ Args...>::value)
+ : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {}
+
+ ~variant() = default;
+
+ variant &operator=(const variant &) = default;
+ variant &operator=(variant &&) = default;
+
+ template <typename Arg,
+ lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value,
+ int> = 0,
+ std::size_t I = detail::best_match<Arg, Ts...>::value,
+ typename T = lib::type_pack_element_t<I, Ts...>,
+ lib::enable_if_t<(std::is_assignable<T &, Arg>::value &&
+ std::is_constructible<T, Arg>::value),
+ int> = 0>
+ inline variant &operator=(Arg &&arg) {
+ impl_.template assign<I>(lib::forward<Arg>(arg));
+ return *this;
+ }
+
+ template <
+ std::size_t I,
+ typename... Args,
+ typename T = lib::type_pack_element_t<I, Ts...>,
+ lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+ inline T &emplace(Args &&... args) {
+ return impl_.template emplace<I>(lib::forward<Args>(args)...);
+ }
+
+ template <
+ std::size_t I,
+ typename Up,
+ typename... Args,
+ typename T = lib::type_pack_element_t<I, Ts...>,
+ lib::enable_if_t<std::is_constructible<T,
+ std::initializer_list<Up> &,
+ Args...>::value,
+ int> = 0>
+ inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+ return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+ }
+
+ template <
+ typename T,
+ typename... Args,
+ std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+ lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0>
+ inline T &emplace(Args &&... args) {
+ return impl_.template emplace<I>(lib::forward<Args>(args)...);
+ }
+
+ template <
+ typename T,
+ typename Up,
+ typename... Args,
+ std::size_t I = detail::find_index_sfinae<T, Ts...>::value,
+ lib::enable_if_t<std::is_constructible<T,
+ std::initializer_list<Up> &,
+ Args...>::value,
+ int> = 0>
+ inline T &emplace(std::initializer_list<Up> il, Args &&... args) {
+ return impl_.template emplace<I>(il, lib::forward<Args>(args)...);
+ }
+
+ inline constexpr bool valueless_by_exception() const noexcept {
+ return impl_.valueless_by_exception();
+ }
+
+ inline constexpr std::size_t index() const noexcept {
+ return impl_.index();
+ }
+
+ template <bool Dummy = true,
+ lib::enable_if_t<
+ lib::all<Dummy,
+ (lib::dependent_type<std::is_move_constructible<Ts>,
+ Dummy>::value &&
+ lib::dependent_type<lib::is_swappable<Ts>,
+ Dummy>::value)...>::value,
+ int> = 0>
+ inline void swap(variant &that) noexcept(
+ lib::all<(std::is_nothrow_move_constructible<Ts>::value &&
+ lib::is_nothrow_swappable<Ts>::value)...>::value) {
+ impl_.swap(that.impl_);
+ }
+
+ private:
+ detail::impl<Ts...> impl_;
+
+ friend struct detail::access::variant;
+ friend struct detail::visitation::variant;
+ };
+
+ template <std::size_t I, typename... Ts>
+ inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+ return v.index() == I;
+ }
+
+ template <typename T, typename... Ts>
+ inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept {
+ return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v);
+ }
+
+ namespace detail {
+ template <std::size_t I, typename V>
+ struct generic_get_impl {
+ constexpr generic_get_impl(int) {}
+
+ constexpr AUTO_REFREF operator()(V &&v) const
+ AUTO_REFREF_RETURN(
+ access::variant::get_alt<I>(lib::forward<V>(v)).value)
+ };
+
+ template <std::size_t I, typename V>
+ inline constexpr AUTO_REFREF generic_get(V &&v)
+ AUTO_REFREF_RETURN(generic_get_impl<I, V>(
+ holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))(
+ lib::forward<V>(v)))
+ } // namespace detail
+
+ template <std::size_t I, typename... Ts>
+ inline constexpr variant_alternative_t<I, variant<Ts...>> &get(
+ variant<Ts...> &v) {
+ return detail::generic_get<I>(v);
+ }
+
+ template <std::size_t I, typename... Ts>
+ inline constexpr variant_alternative_t<I, variant<Ts...>> &&get(
+ variant<Ts...> &&v) {
+ return detail::generic_get<I>(lib::move(v));
+ }
+
+ template <std::size_t I, typename... Ts>
+ inline constexpr const variant_alternative_t<I, variant<Ts...>> &get(
+ const variant<Ts...> &v) {
+ return detail::generic_get<I>(v);
+ }
+
+ template <std::size_t I, typename... Ts>
+ inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get(
+ const variant<Ts...> &&v) {
+ return detail::generic_get<I>(lib::move(v));
+ }
+
+ template <typename T, typename... Ts>
+ inline constexpr T &get(variant<Ts...> &v) {
+ return get<detail::find_index_checked<T, Ts...>::value>(v);
+ }
+
+ template <typename T, typename... Ts>
+ inline constexpr T &&get(variant<Ts...> &&v) {
+ return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+ }
+
+ template <typename T, typename... Ts>
+ inline constexpr const T &get(const variant<Ts...> &v) {
+ return get<detail::find_index_checked<T, Ts...>::value>(v);
+ }
+
+ template <typename T, typename... Ts>
+ inline constexpr const T &&get(const variant<Ts...> &&v) {
+ return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v));
+ }
+
+ namespace detail {
+
+ template <std::size_t I, typename V>
+ inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept
+ AUTO_RETURN(v && holds_alternative<I>(*v)
+ ? lib::addressof(access::variant::get_alt<I>(*v).value)
+ : nullptr)
+
+ } // namespace detail
+
+ template <std::size_t I, typename... Ts>
+ inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
+ get_if(variant<Ts...> *v) noexcept {
+ return detail::generic_get_if<I>(v);
+ }
+
+ template <std::size_t I, typename... Ts>
+ inline constexpr lib::add_pointer_t<
+ const variant_alternative_t<I, variant<Ts...>>>
+ get_if(const variant<Ts...> *v) noexcept {
+ return detail::generic_get_if<I>(v);
+ }
+
+ template <typename T, typename... Ts>
+ inline constexpr lib::add_pointer_t<T>
+ get_if(variant<Ts...> *v) noexcept {
+ return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+ }
+
+ template <typename T, typename... Ts>
+ inline constexpr lib::add_pointer_t<const T>
+ get_if(const variant<Ts...> *v) noexcept {
+ return get_if<detail::find_index_checked<T, Ts...>::value>(v);
+ }
+
+ template <typename... Ts>
+ inline constexpr bool operator==(const variant<Ts...> &lhs,
+ const variant<Ts...> &rhs) {
+ using detail::visitation::variant;
+ using lib::equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+ if (lhs.index() != rhs.index()) return false;
+ if (lhs.valueless_by_exception()) return true;
+ return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs);
+#else
+ return lhs.index() == rhs.index() &&
+ (lhs.valueless_by_exception() ||
+ variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs));
+#endif
+ }
+
+ template <typename... Ts>
+ inline constexpr bool operator!=(const variant<Ts...> &lhs,
+ const variant<Ts...> &rhs) {
+ using detail::visitation::variant;
+ using lib::not_equal_to;
+#ifdef MPARK_CPP14_CONSTEXPR
+ if (lhs.index() != rhs.index()) return true;
+ if (lhs.valueless_by_exception()) return false;
+ return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs);
+#else
+ return lhs.index() != rhs.index() ||
+ (!lhs.valueless_by_exception() &&
+ variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs));
+#endif
+ }
+
+ template <typename... Ts>
+ inline constexpr bool operator<(const variant<Ts...> &lhs,
+ const variant<Ts...> &rhs) {
+ using detail::visitation::variant;
+ using lib::less;
+#ifdef MPARK_CPP14_CONSTEXPR
+ if (rhs.valueless_by_exception()) return false;
+ if (lhs.valueless_by_exception()) return true;
+ if (lhs.index() < rhs.index()) return true;
+ if (lhs.index() > rhs.index()) return false;
+ return variant::visit_value_at(lhs.index(), less{}, lhs, rhs);
+#else
+ return !rhs.valueless_by_exception() &&
+ (lhs.valueless_by_exception() || lhs.index() < rhs.index() ||
+ (lhs.index() == rhs.index() &&
+ variant::visit_value_at(lhs.index(), less{}, lhs, rhs)));
+#endif
+ }
+
+ template <typename... Ts>
+ inline constexpr bool operator>(const variant<Ts...> &lhs,
+ const variant<Ts...> &rhs) {
+ using detail::visitation::variant;
+ using lib::greater;
+#ifdef MPARK_CPP14_CONSTEXPR
+ if (lhs.valueless_by_exception()) return false;
+ if (rhs.valueless_by_exception()) return true;
+ if (lhs.index() > rhs.index()) return true;
+ if (lhs.index() < rhs.index()) return false;
+ return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs);
+#else
+ return !lhs.valueless_by_exception() &&
+ (rhs.valueless_by_exception() || lhs.index() > rhs.index() ||
+ (lhs.index() == rhs.index() &&
+ variant::visit_value_at(lhs.index(), greater{}, lhs, rhs)));
+#endif
+ }
+
+ template <typename... Ts>
+ inline constexpr bool operator<=(const variant<Ts...> &lhs,
+ const variant<Ts...> &rhs) {
+ using detail::visitation::variant;
+ using lib::less_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+ if (lhs.valueless_by_exception()) return true;
+ if (rhs.valueless_by_exception()) return false;
+ if (lhs.index() < rhs.index()) return true;
+ if (lhs.index() > rhs.index()) return false;
+ return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs);
+#else
+ return lhs.valueless_by_exception() ||
+ (!rhs.valueless_by_exception() &&
+ (lhs.index() < rhs.index() ||
+ (lhs.index() == rhs.index() &&
+ variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs))));
+#endif
+ }
+
+ template <typename... Ts>
+ inline constexpr bool operator>=(const variant<Ts...> &lhs,
+ const variant<Ts...> &rhs) {
+ using detail::visitation::variant;
+ using lib::greater_equal;
+#ifdef MPARK_CPP14_CONSTEXPR
+ if (rhs.valueless_by_exception()) return true;
+ if (lhs.valueless_by_exception()) return false;
+ if (lhs.index() > rhs.index()) return true;
+ if (lhs.index() < rhs.index()) return false;
+ return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs);
+#else
+ return rhs.valueless_by_exception() ||
+ (!lhs.valueless_by_exception() &&
+ (lhs.index() > rhs.index() ||
+ (lhs.index() == rhs.index() &&
+ variant::visit_value_at(
+ lhs.index(), greater_equal{}, lhs, rhs))));
+#endif
+ }
+
+ struct monostate {};
+
+ inline constexpr bool operator<(monostate, monostate) noexcept {
+ return false;
+ }
+
+ inline constexpr bool operator>(monostate, monostate) noexcept {
+ return false;
+ }
+
+ inline constexpr bool operator<=(monostate, monostate) noexcept {
+ return true;
+ }
+
+ inline constexpr bool operator>=(monostate, monostate) noexcept {
+ return true;
+ }
+
+ inline constexpr bool operator==(monostate, monostate) noexcept {
+ return true;
+ }
+
+ inline constexpr bool operator!=(monostate, monostate) noexcept {
+ return false;
+ }
+
+#ifdef MPARK_CPP14_CONSTEXPR
+ namespace detail {
+
+ inline constexpr bool all(std::initializer_list<bool> bs) {
+ for (bool b : bs) {
+ if (!b) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ } // namespace detail
+
+ template <typename Visitor, typename... Vs>
+ inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) {
+ return (detail::all({!vs.valueless_by_exception()...})
+ ? (void)0
+ : throw_bad_variant_access()),
+ detail::visitation::variant::visit_value(
+ lib::forward<Visitor>(visitor), lib::forward<Vs>(vs)...);
+ }
+#else
+ namespace detail {
+
+ template <std::size_t N>
+ inline constexpr bool all_impl(const lib::array<bool, N> &bs,
+ std::size_t idx) {
+ return idx >= N || (bs[idx] && all_impl(bs, idx + 1));
+ }
+
+ template <std::size_t N>
+ inline constexpr bool all(const lib::array<bool, N> &bs) {
+ return all_impl(bs, 0);
+ }
+
+ } // namespace detail
+
+ template <typename Visitor, typename... Vs>
+ inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs)
+ DECLTYPE_AUTO_RETURN(
+ (detail::all(
+ lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}})
+ ? (void)0
+ : throw_bad_variant_access()),
+ detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor),
+ lib::forward<Vs>(vs)...))
+#endif
+
+ template <typename... Ts>
+ inline auto swap(variant<Ts...> &lhs,
+ variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs)))
+ -> decltype(lhs.swap(rhs)) {
+ lhs.swap(rhs);
+ }
+
+ namespace detail {
+
+ template <typename T, typename...>
+ using enabled_type = T;
+
+ namespace hash {
+
+ template <typename H, typename K>
+ constexpr bool meets_requirements() {
+ return std::is_copy_constructible<H>::value &&
+ std::is_move_constructible<H>::value &&
+ lib::is_invocable_r<std::size_t, H, const K &>::value;
+ }
+
+ template <typename K>
+ constexpr bool is_enabled() {
+ using H = std::hash<K>;
+ return meets_requirements<H, K>() &&
+ std::is_default_constructible<H>::value &&
+ std::is_copy_assignable<H>::value &&
+ std::is_move_assignable<H>::value;
+ }
+
+ } // namespace hash
+
+ } // namespace detail
+
+#undef AUTO
+#undef AUTO_RETURN
+
+#undef AUTO_REFREF
+#undef AUTO_REFREF_RETURN
+
+#undef DECLTYPE_AUTO
+#undef DECLTYPE_AUTO_RETURN
+
+} // namespace mpark
+
+namespace std {
+
+ template <typename... Ts>
+ struct hash<mpark::detail::enabled_type<
+ mpark::variant<Ts...>,
+ mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled<
+ mpark::lib::remove_const_t<Ts>>()...>::value>>> {
+ using argument_type = mpark::variant<Ts...>;
+ using result_type = std::size_t;
+
+ inline result_type operator()(const argument_type &v) const {
+ using mpark::detail::visitation::variant;
+ std::size_t result =
+ v.valueless_by_exception()
+ ? 299792458 // Random value chosen by the universe upon creation
+ : variant::visit_alt(
+#ifdef MPARK_GENERIC_LAMBDAS
+ [](const auto &alt) {
+ using alt_type = mpark::lib::decay_t<decltype(alt)>;
+ using value_type = mpark::lib::remove_const_t<
+ typename alt_type::value_type>;
+ return hash<value_type>{}(alt.value);
+ }
+#else
+ hasher{}
+#endif
+ ,
+ v);
+ return hash_combine(result, hash<std::size_t>{}(v.index()));
+ }
+
+ private:
+#ifndef MPARK_GENERIC_LAMBDAS
+ struct hasher {
+ template <typename Alt>
+ inline std::size_t operator()(const Alt &alt) const {
+ using alt_type = mpark::lib::decay_t<Alt>;
+ using value_type =
+ mpark::lib::remove_const_t<typename alt_type::value_type>;
+ return hash<value_type>{}(alt.value);
+ }
+ };
+#endif
+
+ static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
+ return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
+ }
+ };
+
+ template <>
+ struct hash<mpark::monostate> {
+ using argument_type = mpark::monostate;
+ using result_type = std::size_t;
+
+ inline result_type operator()(const argument_type &) const noexcept {
+ return 66740831; // return a fundamentally attractive random value.
+ }
+ };
+
+} // namespace std
+
+#if defined(__GNUC__) && __GNUC__ >= 9
+#pragma GCC diagnostic pop
+#endif
+
+#endif // MPARK_VARIANT_HPP
diff --git a/src/shared/variant/variant.qbs b/src/shared/variant/variant.qbs
new file mode 100644
index 000000000..3a95553b7
--- /dev/null
+++ b/src/shared/variant/variant.qbs
@@ -0,0 +1,13 @@
+Product {
+ name: "qbsvariant"
+ files: [
+ "LICENSE.md",
+ "README.md",
+ "variant.h",
+ "variant.hpp"
+ ]
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: "."
+ }
+}
diff --git a/src/src.qbs b/src/src.qbs
index ae58f41ae..f5a5ae5a1 100644
--- a/src/src.qbs
+++ b/src/src.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
references: [
"app/apps.qbs",
@@ -7,7 +5,10 @@ Project {
"libexec/libexec.qbs",
"packages/packages.qbs",
"plugins/plugins.qbs",
- "shared/json/json.qbs",
"shared/bundledqt/bundledqt.qbs",
+ "shared/json/json.qbs",
+ "shared/lsp/lsp.qbs",
+ "shared/quickjs/quickjs.qbs",
+ "shared/variant/variant.qbs",
]
}
diff --git a/static-res.pro b/static-res.pro
deleted file mode 100644
index cbf9aa9d2..000000000
--- a/static-res.pro
+++ /dev/null
@@ -1,74 +0,0 @@
-TEMPLATE = aux
-
-!isEmpty(QBS_APPS_DESTDIR): qbsbindir = $${QBS_APPS_DESTDIR}
-else: qbsbindir = bin
-
-envSpec =
-unix:qbs_disable_rpath {
- include(src/library_dirname.pri)
- !isEmpty(QBS_DESTDIR): qbslibdir = $$QBS_DESTDIR
- else: qbslibdir = $$OUT_PWD/$$QBS_LIBRARY_DIRNAME
- macos: envVar = DYLD_LIBRARY_PATH
- else: envVar = LD_LIBRARY_PATH
- oldVal = $$getenv($$envVar)
- newVal = $$qbslibdir
- !isEmpty(oldVal): newVal = $$newVal:$$oldVal
- envSpec = $$envVar=$$newVal
-}
-
-builddirname = qbsres
-typedescdir = share/qbs/qml-type-descriptions
-typedescdir_src = $$builddirname/default/install-root/$$typedescdir
-!isEmpty(QBS_QML_TYPE_DESCRIPTIONS_BUILD_DIR): \
- typedescdir_dst = $$QBS_QML_TYPE_DESCRIPTIONS_BUILD_DIR
-else:!isEmpty(QBS_RESOURCES_BUILD_DIR): \
- typedescdir_dst = $$QBS_RESOURCES_BUILD_DIR/$$typedescdir
-else: \
- typedescdir_dst = $$typedescdir
-
-qbsres.target = $$builddirname/default/default.bg
-qbsres.commands = \
- $$envSpec $$shell_quote($$shell_path($$qbsbindir/qbs)) \
- build \
- --settings-dir $$shell_quote($$builddirname/settings) \
- -f $$shell_quote($$PWD/qbs.qbs) \
- -d $$shell_quote($$builddirname) \
- -p $$shell_quote("qbs resources") \
- qbs.installPrefix:undefined \
- project.withCode:false \
- project.withDocumentation:false \
- profile:none
-
-qbsqmltypes.target = $$typedescdir_dst/qbs.qmltypes
-qbsqmltypes.commands = \
- $$sprintf($$QMAKE_MKDIR_CMD, \
- $$shell_quote($$shell_path($$typedescdir_dst))) $$escape_expand(\\n\\t) \
- $$QMAKE_COPY \
- $$shell_quote($$shell_path($$typedescdir_src/qbs.qmltypes)) \
- $$shell_quote($$shell_path($$typedescdir_dst/qbs.qmltypes))
-qbsqmltypes.depends += qbsres
-
-qbsbundle.target = $$typedescdir_dst/qbs-bundle.json
-qbsbundle.commands = \
- $$sprintf($$QMAKE_MKDIR_CMD, \
- $$shell_quote($$shell_path($$typedescdir_dst))) $$escape_expand(\\n\\t) \
- $$QMAKE_COPY \
- $$shell_quote($$shell_path($$typedescdir_src/qbs-bundle.json)) \
- $$shell_quote($$shell_path($$typedescdir_dst/qbs-bundle.json))
-qbsbundle.depends += qbsres
-
-QMAKE_EXTRA_TARGETS += qbsres qbsqmltypes qbsbundle
-
-PRE_TARGETDEPS += $$qbsqmltypes.target $$qbsbundle.target
-
-include(src/install_prefix.pri)
-
-qbstypedescfiles.files = $$qbsqmltypes.target $$qbsbundle.target
-!isEmpty(QBS_QML_TYPE_DESCRIPTIONS_INSTALL_DIR): \
- installPrefix = $${QBS_QML_TYPE_DESCRIPTIONS_INSTALL_DIR}
-else:!isEmpty(QBS_RESOURCES_INSTALL_DIR): \
- installPrefix = $${QBS_RESOURCES_INSTALL_DIR}/$$typedescdir
-else: \
- installPrefix = $${QBS_INSTALL_PREFIX}/$$typedescdir
-qbstypedescfiles.path = $${installPrefix}
-INSTALLS += qbstypedescfiles
diff --git a/static.pro b/static.pro
deleted file mode 100644
index 86a9db24f..000000000
--- a/static.pro
+++ /dev/null
@@ -1,83 +0,0 @@
-TEMPLATE = aux
-
-DATA_DIRS = share/qbs/imports share/qbs/modules share/qbs/module-providers
-PYTHON_DATA_DIRS = src/3rdparty/python/lib
-win32:DATA_FILES = $$PWD/bin/ibmsvc.xml $$PWD/bin/ibqbs.bat
-LIBEXEC_FILES = $$PWD/src/3rdparty/python/bin/dmgbuild
-
-# For use in custom compilers which just copy files
-defineReplace(stripSrcDir) {
- return($$relative_path($$absolute_path($$1, $$OUT_PWD), $$_PRO_FILE_PWD_))
-}
-
-defineReplace(stripPythonSrcDir) {
- return($$relative_path($$absolute_path($$1, $$OUT_PWD), \
- $$_PRO_FILE_PWD_/src/3rdparty/python/lib/python2.7/site-packages))
-}
-
-for(data_dir, DATA_DIRS) {
- files = $$files($$PWD/$$data_dir/*, true)
- for(file, files):!exists($$file/*):FILES += $$file
-}
-FILES += $$DATA_FILES
-
-for(data_dir, PYTHON_DATA_DIRS) {
- files = $$files($$PWD/$$data_dir/*, true)
- for(file, files):!exists($$file/*):PYTHON_FILES += $$file
-}
-PYTHON_FILES += $$PYTHON_DATA_FILES
-
-OTHER_FILES += $$FILES $$LIBEXEC_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
-}
-
-copy2build_python.input = PYTHON_FILES
-!isEmpty(QBS_RESOURCES_BUILD_DIR): \
- copy2build_python.output = \
- $${QBS_RESOURCES_BUILD_DIR}/share/qbs/python/${QMAKE_FUNC_FILE_IN_stripPythonSrcDir}
-else: \
- copy2build_python.output = share/qbs/python/${QMAKE_FUNC_FILE_IN_stripPythonSrcDir}
-copy2build_python.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT}
-copy2build_python.name = COPY ${QMAKE_FILE_IN}
-copy2build_python.CONFIG += no_link target_predeps
-QMAKE_EXTRA_COMPILERS += copy2build_python
-
-libexec_copy.input = LIBEXEC_FILES
-!isEmpty(QBS_LIBEXEC_DESTDIR): \
- libexec_copy.output = $${QBS_LIBEXEC_DESTDIR}/${QMAKE_FILE_IN_BASE}${QMAKE_FILE_EXT}
-else: \
- libexec_copy.output = libexec/qbs/${QMAKE_FILE_IN_BASE}${QMAKE_FILE_EXT}
-libexec_copy.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT}
-libexec_copy.name = COPY ${QMAKE_FILE_IN}
-libexec_copy.CONFIG += no_link target_predeps
-QMAKE_EXTRA_COMPILERS += libexec_copy
-
-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
-python_bin.files = $$files(src/3rdparty/python/bin/*)
-!isEmpty(QBS_LIBEXEC_INSTALL_DIR): \
- python_bin.path = $${QBS_LIBEXEC_INSTALL_DIR}
-else: \
- python_bin.path = $${QBS_INSTALL_PREFIX}/libexec/qbs
-python.files = $$files(src/3rdparty/python/lib/python2.7/site-packages/*.py, true)
-python.base = $$PWD/src/3rdparty/python/lib/python2.7/site-packages
-python.path = $${share.path}/qbs/python
-INSTALLS += share examples python_bin python
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 000000000..1ff088061
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_subdirectory(auto)
+add_subdirectory(benchmarker)
+add_subdirectory(clang-format-test)
+add_subdirectory(fuzzy-test)
+add_subdirectory(lspclient)
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt
new file mode 100644
index 000000000..8b1a124aa
--- /dev/null
+++ b/tests/auto/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_subdirectory(api)
+add_subdirectory(cmdlineparser)
+add_subdirectory(blackbox)
+
+if(WITH_UNIT_TESTS)
+ add_subdirectory(buildgraph)
+ add_subdirectory(language)
+ add_subdirectory(pkgconfig)
+ add_subdirectory(tools)
+endif()
diff --git a/tests/auto/api/CMakeLists.txt b/tests/auto/api/CMakeLists.txt
new file mode 100644
index 000000000..c1f89972d
--- /dev/null
+++ b/tests/auto/api/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_qbs_test(api
+ DEFINES
+ "QBS_RELATIVE_LIBEXEC_PATH=\"${QBS_RELATIVE_LIBEXEC_PATH}\""
+ "QBS_RELATIVE_SEARCH_PATH=\"${QBS_RELATIVE_SEARCH_PATH}\""
+ "QBS_RELATIVE_PLUGINS_PATH=\"${QBS_RELATIVE_PLUGINS_PATH}\""
+ SOURCES
+ tst_api.cpp
+ tst_api.h
+ )
diff --git a/tests/auto/api/api.pro b/tests/auto/api/api.pro
deleted file mode 100644
index d9c42e7bb..000000000
--- a/tests/auto/api/api.pro
+++ /dev/null
@@ -1,28 +0,0 @@
-TARGET = tst_api
-
-HEADERS = tst_api.h
-SOURCES = tst_api.cpp
-
-include(../../../src/library_dirname.pri)
-isEmpty(QBS_RELATIVE_LIBEXEC_PATH) {
- win32:QBS_RELATIVE_LIBEXEC_PATH=.
- else: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
index 09e0af7dc..10064c888 100644
--- a/tests/auto/api/api.qbs
+++ b/tests/auto/api/api.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.Utilities
QbsAutotest {
@@ -9,7 +8,7 @@ QbsAutotest {
"QBS_RELATIVE_LIBEXEC_PATH=" + Utilities.cStringQuote(qbsbuildconfig.relativeLibexecPath),
"QBS_RELATIVE_SEARCH_PATH=" + Utilities.cStringQuote(qbsbuildconfig.relativeSearchPath),
"QBS_RELATIVE_PLUGINS_PATH=" + Utilities.cStringQuote(qbsbuildconfig.relativePluginsPath)
- ]).concat(qbsbuildconfig.enableProjectFileUpdates ? ["QBS_ENABLE_PROJECT_FILE_UPDATES"] : [])
+ ])
Group {
name: "testdata"
diff --git a/tests/auto/api/testdata/QBS-728/QBS-728.qbs b/tests/auto/api/testdata/QBS-728/QBS-728.qbs
index 5969e13cc..cdeb7c6db 100644
--- a/tests/auto/api/testdata/QBS-728/QBS-728.qbs
+++ b/tests/auto/api/testdata/QBS-728/QBS-728.qbs
@@ -1,5 +1,5 @@
Product {
- property bool isBlubbOS: qbs.targetOS.contains("blubb-OS")
+ property bool isBlubbOS: qbs.targetOS.includes("blubb-OS")
qbs.profiles: isBlubbOS ? ["blubb-profile"] : [project.profile]
qbs.architecture: "blubb-arch"
}
diff --git a/tests/auto/api/testdata/app-without-sources/app-without-sources.qbs b/tests/auto/api/testdata/app-without-sources/app-without-sources.qbs
index 4cc25ad66..db707f515 100644
--- a/tests/auto/api/testdata/app-without-sources/app-without-sources.qbs
+++ b/tests/auto/api/testdata/app-without-sources/app-without-sources.qbs
@@ -26,7 +26,7 @@ Project {
// HACK: cpp.entryPoint currently not working 100% with gcc
Properties {
- condition: qbs.toolchain.contains("msvc")
+ condition: qbs.toolchain.includes("msvc")
cpp.entryPoint: "main"
cpp.dynamicLibraries: ["ucrt", "kernel32"]
}
diff --git a/tests/auto/api/testdata/build-properties-source/build-properties-source.qbs b/tests/auto/api/testdata/build-properties-source/build-properties-source.qbs
index 571feced8..3237e90dc 100644
--- a/tests/auto/api/testdata/build-properties-source/build-properties-source.qbs
+++ b/tests/auto/api/testdata/build-properties-source/build-properties-source.qbs
@@ -6,11 +6,6 @@ Project {
Depends { name: 'cpp' }
- Properties {
- condition: qbs.toolchain.contains("gcc")
- cpp.cxxFlags: "-march=native"
- }
-
Group {
cpp.defines: ['WORLD="BANANA"']
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
index f830ee1fd..89ec55463 100644
--- a/tests/auto/api/testdata/build-properties-source/main.cpp
+++ b/tests/auto/api/testdata/build-properties-source/main.cpp
@@ -26,7 +26,7 @@
**
****************************************************************************/
-#include <stdio.h>
+#include <cstdio>
#ifndef WORLD
# error WORLD is not defined
@@ -34,5 +34,5 @@
int main()
{
- puts("Hello " WORLD "!");
+ std::puts("Hello " WORLD "!");
}
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
index 222dc4476..cb925eeca 100644
--- a/tests/auto/api/testdata/change-dependent-lib/change-dependent-lib.qbs
+++ b/tests/auto/api/testdata/change-dependent-lib/change-dependent-lib.qbs
@@ -18,7 +18,7 @@ Project {
cpp.defines: ["XXXX"]
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
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
index 8ad414c46..9334da1ef 100644
--- a/tests/auto/api/testdata/change-dependent-lib/main.cpp
+++ b/tests/auto/api/testdata/change-dependent-lib/main.cpp
@@ -28,13 +28,12 @@
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_IMPORT int mylib_hello();
int main()
{
- puts("application says hello!");
+ std::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
index 28cb69f95..b8c3e9082 100644
--- a/tests/auto/api/testdata/change-dependent-lib/mylib.cpp
+++ b/tests/auto/api/testdata/change-dependent-lib/mylib.cpp
@@ -28,10 +28,10 @@
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT int mylib_hello()
{
- puts("mylib says hello!");
+ std::puts("mylib says hello!");
return 0;
}
diff --git a/tests/auto/api/testdata/codegen/codegen.qbs b/tests/auto/api/testdata/codegen/codegen.qbs
index d8a217572..42e4c6b08 100644
--- a/tests/auto/api/testdata/codegen/codegen.qbs
+++ b/tests/auto/api/testdata/codegen/codegen.qbs
@@ -43,7 +43,7 @@ Project {
// check whether multipart module name translation is working
var actual = product.moduleProperty("Qt.core", "mocName");
- if (!actual || !actual.contains("moc"))
+ if (!actual || !actual.includes("moc"))
throw "multipart module name translation is broken";
// check whether we can access project properties here
@@ -57,7 +57,7 @@ Project {
code = expandMacros(code, product.replacements);
var args = ['echo ' + code + '>' + output.filePath]
var cmd
- if (product.moduleProperty("qbs", "hostOS").contains('windows')) {
+ if (product.moduleProperty("qbs", "hostOS").includes('windows')) {
cmd = new Command(product.qbs.windowsShellPath, ['/C'].concat(args));
} else {
args[0] = args[0].replace(/\(/g, '\\(')
@@ -65,7 +65,7 @@ Project {
args[0] = args[0].replace(/;/g, '\\;')
cmd = new Command(product.qbs.shellPath, ['-c'].concat(args))
}
- cmd.description = 'generate\t' + FileInfo.fileName(output.filePath);
+ cmd.description = 'generating ' + FileInfo.fileName(output.filePath);
cmd.highlight = 'codegen';
return cmd;
}
diff --git a/tests/auto/api/testdata/dependency-on-multiplexed-type/dependency-on-multiplexed-type.qbs b/tests/auto/api/testdata/dependency-on-multiplexed-type/dependency-on-multiplexed-type.qbs
index cfc2769d2..f0ffebe39 100644
--- a/tests/auto/api/testdata/dependency-on-multiplexed-type/dependency-on-multiplexed-type.qbs
+++ b/tests/auto/api/testdata/dependency-on-multiplexed-type/dependency-on-multiplexed-type.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
Product { name: "dep"; type: "x" }
Product {
diff --git a/tests/auto/api/testdata/disabled-product/disabled-product.qbs b/tests/auto/api/testdata/disabled-product/disabled-product.qbs
index dad8f5d4c..e7eea7380 100644
--- a/tests/auto/api/testdata/disabled-product/disabled-product.qbs
+++ b/tests/auto/api/testdata/disabled-product/disabled-product.qbs
@@ -2,7 +2,7 @@ CppApplication {
condition: false
files: "main.cpp"
Group {
- condition: qbs.targetOS.contains("stuff")
+ condition: qbs.targetOS.includes("stuff")
qbs.install: false
}
}
diff --git a/tests/auto/api/testdata/disappeared-wildcard-file/disappeared-wildcard-file.qbs b/tests/auto/api/testdata/disappeared-wildcard-file/disappeared-wildcard-file.qbs
new file mode 100644
index 000000000..3c38e18a7
--- /dev/null
+++ b/tests/auto/api/testdata/disappeared-wildcard-file/disappeared-wildcard-file.qbs
@@ -0,0 +1,4 @@
+Product {
+ name: "dummy"
+ files: "*.txt"
+}
diff --git a/tests/auto/blackbox/testdata/concurrent-executor/dummy1.input b/tests/auto/api/testdata/disappeared-wildcard-file/file1.txt
index e69de29bb..e69de29bb 100644
--- a/tests/auto/blackbox/testdata/concurrent-executor/dummy1.input
+++ b/tests/auto/api/testdata/disappeared-wildcard-file/file1.txt
diff --git a/tests/auto/blackbox/testdata/concurrent-executor/dummy2.input b/tests/auto/api/testdata/disappeared-wildcard-file/file2.txt
index e69de29bb..e69de29bb 100644
--- a/tests/auto/blackbox/testdata/concurrent-executor/dummy2.input
+++ b/tests/auto/api/testdata/disappeared-wildcard-file/file2.txt
diff --git a/tests/auto/api/testdata/explicitly-depends-on/explicitly-depends-on.qbs b/tests/auto/api/testdata/explicitly-depends-on/explicitly-depends-on.qbs
index 05886a99e..0ebb73413 100644
--- a/tests/auto/api/testdata/explicitly-depends-on/explicitly-depends-on.qbs
+++ b/tests/auto/api/testdata/explicitly-depends-on/explicitly-depends-on.qbs
@@ -1,4 +1,5 @@
import qbs.FileInfo
+import qbs.Host
import qbs.TextFile
Project {
@@ -11,6 +12,12 @@ Project {
}
}
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "p"
type: ["mytype"]
diff --git a/tests/auto/api/testdata/export-simple/export-simple.qbs b/tests/auto/api/testdata/export-simple/export-simple.qbs
index 01177049d..84ce5541d 100644
--- a/tests/auto/api/testdata/export-simple/export-simple.qbs
+++ b/tests/auto/api/testdata/export-simple/export-simple.qbs
@@ -43,7 +43,7 @@ Project {
}
Depends { name: "cpp" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
diff --git a/tests/auto/api/testdata/export-simple/lib1.cpp b/tests/auto/api/testdata/export-simple/lib1.cpp
index 2b22d4993..7981edf92 100644
--- a/tests/auto/api/testdata/export-simple/lib1.cpp
+++ b/tests/auto/api/testdata/export-simple/lib1.cpp
@@ -27,10 +27,10 @@
****************************************************************************/
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT int lib1_hello()
{
- puts("lib1 says hello!");
+ std::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
index 820e6f3bc..060037b82 100644
--- a/tests/auto/api/testdata/export-simple/main.cpp
+++ b/tests/auto/api/testdata/export-simple/main.cpp
@@ -27,13 +27,12 @@
****************************************************************************/
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_IMPORT int lib1_hello();
int main()
{
- puts("application says hello!");
+ std::puts("application says hello!");
return lib1_hello();
}
-
diff --git a/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs b/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs
index bb616a9e2..224e90ca6 100644
--- a/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs
+++ b/tests/auto/api/testdata/infinite-loop-js/infinite-loop.qbs
@@ -8,7 +8,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Running infinite loop";
+ cmd.description = "running infinite loop";
cmd.sourceCode = function() {
while (true)
;
diff --git a/tests/auto/api/testdata/infinite-loop-process/infinite-loop.qbs b/tests/auto/api/testdata/infinite-loop-process/infinite-loop.qbs
index f4ea8bf83..687955f2f 100644
--- a/tests/auto/api/testdata/infinite-loop-process/infinite-loop.qbs
+++ b/tests/auto/api/testdata/infinite-loop-process/infinite-loop.qbs
@@ -1,3 +1,5 @@
+import qbs.Host
+
Project {
CppApplication {
type: "application"
@@ -6,7 +8,7 @@ Project {
name: "infinite-loop"
cpp.cxxLanguageVersion: "c++11"
Properties {
- condition: qbs.toolchain.contains("gcc")
+ condition: qbs.toolchain.includes("gcc")
cpp.driverFlags: "-pthread"
}
}
@@ -17,14 +19,14 @@ Project {
Depends { name: "infinite-loop" }
Depends {
name: "cpp" // Make sure build environment is set up properly.
- condition: qbs.hostOS.contains("windows") && qbs.toolchain.contains("gcc")
+ condition: Host.os().includes("windows") && qbs.toolchain.includes("gcc")
}
Rule {
inputsFromDependencies: "application"
outputFileTags: "mytype"
prepare: {
var cmd = new Command(inputs["application"][0].filePath);
- cmd.description = "Calling application that runs forever";
+ cmd.description = "calling application that runs forever";
return cmd;
}
}
diff --git a/tests/auto/blackbox/testdata/innosetup/inc/qbsinc.iss b/tests/auto/api/testdata/infinite-loop-scanning-scan/file.in
index e69de29bb..e69de29bb 100644
--- a/tests/auto/blackbox/testdata/innosetup/inc/qbsinc.iss
+++ b/tests/auto/api/testdata/infinite-loop-scanning-scan/file.in
diff --git a/tests/auto/api/testdata/infinite-loop-scanning-scan/infinite-loop.qbs b/tests/auto/api/testdata/infinite-loop-scanning-scan/infinite-loop.qbs
new file mode 100644
index 000000000..5e3e33b6e
--- /dev/null
+++ b/tests/auto/api/testdata/infinite-loop-scanning-scan/infinite-loop.qbs
@@ -0,0 +1,21 @@
+Product {
+ type: "t"
+ Depends { name: "m" }
+ Group {
+ files: "file.in"
+ fileTags: "i"
+ }
+ Rule {
+ inputs: "i"
+ Artifact {
+ filePath: "dummy"
+ fileTags: "t"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.sourceCode = function() {};
+ return cmd;
+ }
+ }
+}
diff --git a/tests/auto/api/testdata/infinite-loop-scanning-scan/modules/m/m.qbs b/tests/auto/api/testdata/infinite-loop-scanning-scan/modules/m/m.qbs
new file mode 100644
index 000000000..9c8a18072
--- /dev/null
+++ b/tests/auto/api/testdata/infinite-loop-scanning-scan/modules/m/m.qbs
@@ -0,0 +1,6 @@
+Module {
+ Scanner {
+ inputs: "i"
+ scan: { while (true); }
+ }
+} \ No newline at end of file
diff --git a/tests/auto/api/testdata/infinite-loop-scanning-searchpaths/file.in b/tests/auto/api/testdata/infinite-loop-scanning-searchpaths/file.in
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/api/testdata/infinite-loop-scanning-searchpaths/file.in
diff --git a/tests/auto/api/testdata/infinite-loop-scanning-searchpaths/infinite-loop.qbs b/tests/auto/api/testdata/infinite-loop-scanning-searchpaths/infinite-loop.qbs
new file mode 100644
index 000000000..5e3e33b6e
--- /dev/null
+++ b/tests/auto/api/testdata/infinite-loop-scanning-searchpaths/infinite-loop.qbs
@@ -0,0 +1,21 @@
+Product {
+ type: "t"
+ Depends { name: "m" }
+ Group {
+ files: "file.in"
+ fileTags: "i"
+ }
+ Rule {
+ inputs: "i"
+ Artifact {
+ filePath: "dummy"
+ fileTags: "t"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.sourceCode = function() {};
+ return cmd;
+ }
+ }
+}
diff --git a/tests/auto/api/testdata/infinite-loop-scanning-searchpaths/modules/m/m.qbs b/tests/auto/api/testdata/infinite-loop-scanning-searchpaths/modules/m/m.qbs
new file mode 100644
index 000000000..0476478b9
--- /dev/null
+++ b/tests/auto/api/testdata/infinite-loop-scanning-searchpaths/modules/m/m.qbs
@@ -0,0 +1,7 @@
+Module {
+ Scanner {
+ inputs: "i"
+ searchPaths: { while (true); }
+ scan: []
+ }
+}
diff --git a/tests/auto/api/testdata/installed-artifact/installed-artifact.qbs b/tests/auto/api/testdata/installed-artifact/installed-artifact.qbs
index d3f91662a..e4e050213 100644
--- a/tests/auto/api/testdata/installed-artifact/installed-artifact.qbs
+++ b/tests/auto/api/testdata/installed-artifact/installed-artifact.qbs
@@ -17,6 +17,7 @@ Project {
}
qbs.installPrefix: "/usr"
install: true
+ installDebugInformation: false
installDir: "bin"
Group {
fileTagsFilter: "obj"
diff --git a/tests/auto/api/testdata/is-runnable/is-runnable.qbs b/tests/auto/api/testdata/is-runnable/is-runnable.qbs
index 870279294..e93cc38aa 100644
--- a/tests/auto/api/testdata/is-runnable/is-runnable.qbs
+++ b/tests/auto/api/testdata/is-runnable/is-runnable.qbs
@@ -5,7 +5,7 @@ Project {
DynamicLibrary {
name: "lib"
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
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
index 47e1013f9..c9602f59f 100644
--- a/tests/auto/api/testdata/lib-same-source/main.cpp
+++ b/tests/auto/api/testdata/lib-same-source/main.cpp
@@ -26,9 +26,9 @@
**
****************************************************************************/
-#include <stdio.h>
+#include <cstdio>
int main()
{
- puts("Hello WOrld!");
+ std::puts("Hello WOrld!");
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/dynamic1.cpp b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/dynamic1.cpp
index ed5f822fb..555c91afe 100644
--- a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/dynamic1.cpp
+++ b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/dynamic1.cpp
@@ -1,4 +1,4 @@
-#include <stdio.h>
+#include <cstdio>
#include "../dllexport.h"
@@ -7,6 +7,6 @@ void static1_hello();
DLL_EXPORT int dynamic1_hello()
{
static1_hello();
- puts("dynamic1 says hello!");
+ std::puts("dynamic1 says hello!");
return 0;
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/dynamic2.cpp b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/dynamic2.cpp
index b2db5d23e..d5fcbaab4 100644
--- a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/dynamic2.cpp
+++ b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/dynamic2.cpp
@@ -1,10 +1,10 @@
#include "../dllexport.h"
#include "static2.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT void dynamic2_hello()
{
TestMe tm;
tm.hello();
- puts("dynamic2 says hello!");
+ std::puts("dynamic2 says hello!");
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/link-dynamiclibs-staticlibs.qbs b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/link-dynamiclibs-staticlibs.qbs
index b2a54080c..0c86d05c6 100644
--- a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/link-dynamiclibs-staticlibs.qbs
+++ b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/link-dynamiclibs-staticlibs.qbs
@@ -12,7 +12,7 @@ Project {
Depends { name: "cpp" }
Depends { name: "static1" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -31,7 +31,7 @@ Project {
Depends { name: "static2" }
cpp.visibility: 'hidden'
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -40,6 +40,11 @@ Project {
name: "static2"
files: [ "static2.cpp", "static2.h" ]
Depends { name: "cpp" }
+ Probe {
+ id: tcPrinter
+ property bool isGcc: qbs.toolchain.contains("gcc")
+ configure: { console.info("is gcc: " + isGcc); }
+ }
}
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/main.cpp b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/main.cpp
index 73c0922d6..9b0a190b4 100644
--- a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/main.cpp
+++ b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/main.cpp
@@ -1,11 +1,11 @@
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_IMPORT int dynamic1_hello();
int main()
{
int result = dynamic1_hello();
- puts("application says hello!");
+ std::puts("application says hello!");
return result;
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/static1.cpp b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/static1.cpp
index 81c53bd4e..d07910f96 100644
--- a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/static1.cpp
+++ b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/static1.cpp
@@ -1,10 +1,10 @@
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_IMPORT void dynamic2_hello();
void static1_hello()
{
dynamic2_hello();
- puts("static1 says hello!");
+ std::puts("static1 says hello!");
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/static2.cpp b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/static2.cpp
index 073b13609..99a60be0c 100644
--- a/tests/auto/api/testdata/link-dynamiclibs-staticlibs/static2.cpp
+++ b/tests/auto/api/testdata/link-dynamiclibs-staticlibs/static2.cpp
@@ -1,7 +1,7 @@
#include "static2.h"
-#include <stdio.h>
+#include <cstdio>
void TestMe::hello() const
{
- puts("static2 says hello!");
+ std::puts("static2 says hello!");
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs/lib1.cpp b/tests/auto/api/testdata/link-dynamiclibs/lib1.cpp
index 14666cf58..e2e943b37 100644
--- a/tests/auto/api/testdata/link-dynamiclibs/lib1.cpp
+++ b/tests/auto/api/testdata/link-dynamiclibs/lib1.cpp
@@ -27,13 +27,13 @@
****************************************************************************/
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_IMPORT void lib2_hello();
DLL_EXPORT int lib1_hello()
{
- puts("lib1 says hello!");
+ std::puts("lib1 says hello!");
lib2_hello();
return 0;
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs/lib2.cpp b/tests/auto/api/testdata/link-dynamiclibs/lib2.cpp
index 01938b16b..f8bf990a3 100644
--- a/tests/auto/api/testdata/link-dynamiclibs/lib2.cpp
+++ b/tests/auto/api/testdata/link-dynamiclibs/lib2.cpp
@@ -27,13 +27,13 @@
****************************************************************************/
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_IMPORT void lib3_hello();
DLL_EXPORT void lib2_hello()
{
- puts("lib2 says hello!");
+ std::puts("lib2 says hello!");
lib3_hello();
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs/lib3.cpp b/tests/auto/api/testdata/link-dynamiclibs/lib3.cpp
index b90a99981..ff2d351ba 100644
--- a/tests/auto/api/testdata/link-dynamiclibs/lib3.cpp
+++ b/tests/auto/api/testdata/link-dynamiclibs/lib3.cpp
@@ -27,11 +27,11 @@
****************************************************************************/
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT void lib3_hello()
{
- puts("lib3 says hello!");
+ std::puts("lib3 says hello!");
}
DLL_EXPORT char* lib3_greeting()
diff --git a/tests/auto/api/testdata/link-dynamiclibs/lib4.cpp b/tests/auto/api/testdata/link-dynamiclibs/lib4.cpp
index 2d66e40f6..ebb97afc9 100644
--- a/tests/auto/api/testdata/link-dynamiclibs/lib4.cpp
+++ b/tests/auto/api/testdata/link-dynamiclibs/lib4.cpp
@@ -34,10 +34,10 @@ TestMe::TestMe()
void TestMe::hello1() const
{
- puts("lib4 says hello!");
+ std::puts("lib4 says hello!");
}
void TestMe::hello2Impl() const
{
- puts("lib4 says hello inline!");
+ std::puts("lib4 says hello inline!");
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs/lib4.h b/tests/auto/api/testdata/link-dynamiclibs/lib4.h
index 63258b2ea..69bde04ec 100644
--- a/tests/auto/api/testdata/link-dynamiclibs/lib4.h
+++ b/tests/auto/api/testdata/link-dynamiclibs/lib4.h
@@ -30,7 +30,7 @@
#define LIB4_H
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
#ifdef TEST_LIB
# define LIB_EXPORT DLL_EXPORT
diff --git a/tests/auto/api/testdata/link-dynamiclibs/link-dynamiclibs.qbs b/tests/auto/api/testdata/link-dynamiclibs/link-dynamiclibs.qbs
index cc86a4402..e0bce7264 100644
--- a/tests/auto/api/testdata/link-dynamiclibs/link-dynamiclibs.qbs
+++ b/tests/auto/api/testdata/link-dynamiclibs/link-dynamiclibs.qbs
@@ -17,7 +17,7 @@ Project {
Depends { name: "cpp" }
Depends { name: "lib2" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -31,7 +31,7 @@ Project {
Depends { name: "cpp" }
Depends { name: "lib3" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -44,7 +44,7 @@ Project {
}
Depends { name: "cpp" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -58,13 +58,13 @@ Project {
}
Depends { name: "cpp" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
Export {
Depends { name: "cpp" }
- cpp.includePaths: [product.sourceDirectory]
+ cpp.includePaths: [exportingProduct.sourceDirectory]
}
}
}
diff --git a/tests/auto/api/testdata/link-dynamiclibs/main.cpp b/tests/auto/api/testdata/link-dynamiclibs/main.cpp
index 64f7948cb..b6165d30f 100644
--- a/tests/auto/api/testdata/link-dynamiclibs/main.cpp
+++ b/tests/auto/api/testdata/link-dynamiclibs/main.cpp
@@ -26,14 +26,14 @@
**
****************************************************************************/
-#include <stdio.h>
+#include <cstdio>
#include <lib4.h>
DLL_IMPORT int lib1_hello();
int main()
{
- puts("application says hello!");
+ std::puts("application says hello!");
TestMe test;
test.hello1();
test.hello2();
diff --git a/tests/auto/api/testdata/link-static-lib/link-static-lib.qbs b/tests/auto/api/testdata/link-static-lib/link-static-lib.qbs
index 6960c7d17..829aa5f3e 100644
--- a/tests/auto/api/testdata/link-static-lib/link-static-lib.qbs
+++ b/tests/auto/api/testdata/link-static-lib/link-static-lib.qbs
@@ -25,7 +25,7 @@ Project {
Depends { name: "helper2" }
Export {
Depends { name: "cpp" }
- cpp.includePaths: [product.sourceDirectory + '/helper1']
+ cpp.includePaths: [exportingProduct.sourceDirectory + '/helper1']
}
}
@@ -38,7 +38,7 @@ Project {
Depends { name: "cpp" }
Export {
Depends { name: "cpp" }
- cpp.includePaths: [product.sourceDirectory + '/helper2']
+ cpp.includePaths: [exportingProduct.sourceDirectory + '/helper2']
}
}
}
diff --git a/tests/auto/api/testdata/link-static-lib/main.cpp b/tests/auto/api/testdata/link-static-lib/main.cpp
index 5f6aed0b9..2d94a4b0b 100644
--- a/tests/auto/api/testdata/link-static-lib/main.cpp
+++ b/tests/auto/api/testdata/link-static-lib/main.cpp
@@ -26,7 +26,7 @@
**
****************************************************************************/
-#include <stdio.h>
+#include <cstdio>
int bla();
diff --git a/tests/auto/api/testdata/link-static-lib/mystaticlib.cpp b/tests/auto/api/testdata/link-static-lib/mystaticlib.cpp
index 71777ef05..0b5d02fd4 100644
--- a/tests/auto/api/testdata/link-static-lib/mystaticlib.cpp
+++ b/tests/auto/api/testdata/link-static-lib/mystaticlib.cpp
@@ -26,12 +26,13 @@
**
****************************************************************************/
-#include <stdio.h>
#include <helper1.h>
+#include <cstdio>
+
int bla()
{
int n = getSomeNumber();
- printf("Hello World! The magic number is %d.", n);
+ std::printf("Hello World! The magic number is %d.", n);
return n;
}
diff --git a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/dynamic1.cpp b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/dynamic1.cpp
index 3f8a5f8d7..b4379f8c9 100644
--- a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/dynamic1.cpp
+++ b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/dynamic1.cpp
@@ -1,11 +1,11 @@
#include "../dllexport.h"
#include "static2.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT int dynamic1_hello()
{
TestMe tm;
tm.hello();
- puts("dynamic1 says hello!");
+ std::puts("dynamic1 says hello!");
return 1;
}
diff --git a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/dynamic2.cpp b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/dynamic2.cpp
index 75594185e..3511b4759 100644
--- a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/dynamic2.cpp
+++ b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/dynamic2.cpp
@@ -1,7 +1,7 @@
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT void dynamic2_hello()
{
- puts("dynamic2 says hello!");
+ std::puts("dynamic2 says hello!");
}
diff --git a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/link-staticlibs-dynamiclibs.qbs b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/link-staticlibs-dynamiclibs.qbs
index d7ed6c862..c30cf40f9 100644
--- a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/link-staticlibs-dynamiclibs.qbs
+++ b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/link-staticlibs-dynamiclibs.qbs
@@ -14,9 +14,13 @@ Project {
Probe {
id: osCheck
- property bool isNormalUnix: qbs.targetOS.contains("unix")
- && !qbs.targetOS.contains("darwin")
- configure: { console.info("is normal unix: " + (isNormalUnix ? "yes" : "no")); }
+ property bool isNormalUnix: qbs.targetOS.includes("unix")
+ && !qbs.targetOS.includes("darwin")
+ property bool isGcc: qbs.toolchain.contains("gcc")
+ configure: {
+ console.info("is normal unix: " + (isNormalUnix ? "yes" : "no"));
+ console.info("is gcc: " + isGcc);
+ }
}
}
@@ -26,7 +30,7 @@ Project {
Depends { name: "cpp" }
Depends { name: "static2" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -44,7 +48,7 @@ Project {
Depends { name: "cpp" }
cpp.visibility: 'hidden'
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
diff --git a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/main.cpp b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/main.cpp
index 4b8193341..b9e522ac4 100644
--- a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/main.cpp
+++ b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/main.cpp
@@ -1,10 +1,10 @@
-#include <stdio.h>
+#include <cstdio>
void static1_hello();
int main()
{
static1_hello();
- puts("application says hello!");
+ std::puts("application says hello!");
return 0;
}
diff --git a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/static1.cpp b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/static1.cpp
index a3058c63b..320344f91 100644
--- a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/static1.cpp
+++ b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/static1.cpp
@@ -1,10 +1,11 @@
#include "../dllexport.h"
-#include <stdio.h>
+
+#include <cstdio>
DLL_IMPORT int dynamic1_hello();
void static1_hello()
{
int n = dynamic1_hello();
- printf("static%d says hello!\n", n);
+ std::printf("static%d says hello!\n", n);
}
diff --git a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/static2.cpp b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/static2.cpp
index 374bf7ceb..88c6d2a3f 100644
--- a/tests/auto/api/testdata/link-staticlibs-dynamiclibs/static2.cpp
+++ b/tests/auto/api/testdata/link-staticlibs-dynamiclibs/static2.cpp
@@ -1,11 +1,11 @@
#include "../dllexport.h"
#include "static2.h"
-#include <stdio.h>
+#include <cstdio>
DLL_IMPORT void dynamic2_hello();
void TestMe::hello() const
{
dynamic2_hello();
- puts("static2 says hello!");
+ std::puts("static2 says hello!");
}
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
index 427f9907d..e1701dc47 100644
--- 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
@@ -33,6 +33,6 @@
int main()
{
ObjectNarf obj;
- puts("...");
+ std::puts("...");
}
diff --git a/tests/auto/api/testdata/moc-hpp-included/moc-hpp-included.qbs b/tests/auto/api/testdata/moc-hpp-included/moc-hpp-included.qbs
index a484b9c22..38d3d88ca 100644
--- a/tests/auto/api/testdata/moc-hpp-included/moc-hpp-included.qbs
+++ b/tests/auto/api/testdata/moc-hpp-included/moc-hpp-included.qbs
@@ -11,7 +11,7 @@ Project {
files: ["object.cpp", "object.h"]
Group {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
files: ["object2.mm", "object2.h"]
}
}
diff --git a/tests/auto/api/testdata/moc-hpp-included/object.cpp b/tests/auto/api/testdata/moc-hpp-included/object.cpp
index 4f1502af5..dd0b237cf 100644
--- a/tests/auto/api/testdata/moc-hpp-included/object.cpp
+++ b/tests/auto/api/testdata/moc-hpp-included/object.cpp
@@ -38,6 +38,6 @@ Object::Object(QObject *parent)
int main()
{
Object obj;
- printf("Hello World\n");
+ std::printf("Hello World\n");
}
diff --git a/tests/auto/api/testdata/moc-hpp/object.cpp b/tests/auto/api/testdata/moc-hpp/object.cpp
index 601893c34..4c0e59393 100644
--- a/tests/auto/api/testdata/moc-hpp/object.cpp
+++ b/tests/auto/api/testdata/moc-hpp/object.cpp
@@ -36,6 +36,6 @@ Object::Object(QObject *parent)
int main()
{
Object obj;
- printf("Hello World\n");
+ std::printf("Hello World\n");
}
diff --git a/tests/auto/api/testdata/multiplexing/multiplexing.qbs b/tests/auto/api/testdata/multiplexing/multiplexing.qbs
index 243c73d46..75958ed60 100644
--- a/tests/auto/api/testdata/multiplexing/multiplexing.qbs
+++ b/tests/auto/api/testdata/multiplexing/multiplexing.qbs
@@ -74,6 +74,13 @@ Project {
qbs.architectures: ["TRS-80", "C64"]
qbs.buildVariants: ["debug", "release"]
}
+ Product {
+ name: "multiplex-without-aggregator-4-depends-2"
+ multiplexByQbsProperties: ["architectures", "buildVariants"]
+ qbs.architectures: ["TRS-80", "C64"]
+ qbs.buildVariants: ["debug", "release"]
+ Depends { name: "multiplex-without-aggregator-2" }
+ }
}
Product {
diff --git a/tests/auto/api/testdata/new-output-artifact-in-dependency/new-output-artifact-in-dependency.qbs b/tests/auto/api/testdata/new-output-artifact-in-dependency/new-output-artifact-in-dependency.qbs
index 5c3e475b0..e87af67c7 100644
--- a/tests/auto/api/testdata/new-output-artifact-in-dependency/new-output-artifact-in-dependency.qbs
+++ b/tests/auto/api/testdata/new-output-artifact-in-dependency/new-output-artifact-in-dependency.qbs
@@ -4,7 +4,7 @@ Project {
name: "lib"
files: "lib.cpp"
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
diff --git a/tests/auto/api/testdata/objc/objc.qbs b/tests/auto/api/testdata/objc/objc.qbs
index 845eb8d32..2fba48d9b 100644
--- a/tests/auto/api/testdata/objc/objc.qbs
+++ b/tests/auto/api/testdata/objc/objc.qbs
@@ -1,6 +1,6 @@
Project {
CppApplication {
- condition: qbs.targetOS.contains("macos")
+ condition: qbs.targetOS.includes("macos")
files: "main.mm"
cpp.frameworks: [ "Foundation" ]
}
diff --git a/tests/auto/api/testdata/precompiled-header-dynamic/precompiled-header-dynamic.qbs b/tests/auto/api/testdata/precompiled-header-dynamic/precompiled-header-dynamic.qbs
index 2fd58d24e..1b0728669 100644
--- a/tests/auto/api/testdata/precompiled-header-dynamic/precompiled-header-dynamic.qbs
+++ b/tests/auto/api/testdata/precompiled-header-dynamic/precompiled-header-dynamic.qbs
@@ -21,7 +21,7 @@ CppApplication {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Generating " + output.fileName;
+ cmd.description = "generating " + output.fileName;
cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); }
return [cmd];
}
diff --git a/tests/auto/api/testdata/process-result/process-result.qbs b/tests/auto/api/testdata/process-result/process-result.qbs
index 84706ace8..dc9ff2839 100644
--- a/tests/auto/api/testdata/process-result/process-result.qbs
+++ b/tests/auto/api/testdata/process-result/process-result.qbs
@@ -1,9 +1,18 @@
+import qbs.Host
+
Project {
CppApplication {
name: "app"
+ consoleApplication: true
files: ["main.cpp"]
}
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "app-caller"
type: "mytype"
Depends { name: "app" }
@@ -20,7 +29,7 @@ Project {
cmd.stdoutFilePath = product.buildDirectory + "/stdout.txt";
if (product.redirectStderr)
cmd.stderrFilePath = product.buildDirectory + "/stderr.txt";
- cmd.description = "Building app-caller";
+ cmd.description = "building app-caller";
return [cmd];
}
}
diff --git a/tests/auto/api/testdata/project-with-properties-item/project-with-properties-item.qbs b/tests/auto/api/testdata/project-with-properties-item/project-with-properties-item.qbs
index 866ec4ecb..812c6a65e 100644
--- a/tests/auto/api/testdata/project-with-properties-item/project-with-properties-item.qbs
+++ b/tests/auto/api/testdata/project-with-properties-item/project-with-properties-item.qbs
@@ -3,7 +3,7 @@ Project {
property string libPath: "/usr/lib"
Properties {
- condition: qbs.targetOS.contains("macos")
+ condition: qbs.targetOS.includes("macos")
binPath: "/Users/boo"
libPath: "/Libraries/foo"
}
diff --git a/tests/auto/api/testdata/properties-blocks/main.cpp b/tests/auto/api/testdata/properties-blocks/main.cpp
index 5473bffa7..a01a5850e 100644
--- a/tests/auto/api/testdata/properties-blocks/main.cpp
+++ b/tests/auto/api/testdata/properties-blocks/main.cpp
@@ -26,7 +26,7 @@
**
****************************************************************************/
-#include <stdio.h>
+#include <cstdio>
#ifndef HAVE_MAIN_CPP
# error missing define HAVE_MAIN_CPP
@@ -38,9 +38,8 @@
int main()
{
#ifdef _DEBUG
- puts("Hello World! (debug version)");
+ std::puts("Hello World! (debug version)");
#else
- puts("Hello World! (release version)");
+ std::puts("Hello World! (release version)");
#endif
}
-
diff --git a/tests/auto/api/testdata/properties-blocks/properties-blocks.qbs b/tests/auto/api/testdata/properties-blocks/properties-blocks.qbs
index dda4652d2..c234a6b41 100644
--- a/tests/auto/api/testdata/properties-blocks/properties-blocks.qbs
+++ b/tests/auto/api/testdata/properties-blocks/properties-blocks.qbs
@@ -14,7 +14,7 @@ Product {
}
Properties {
- condition: qbs.targetOS.contains("weird")
+ condition: qbs.targetOS.includes("weird")
cpp.staticLibraries: "abc"
}
diff --git a/tests/auto/api/testdata/qt5-plugin/qt5-plugin.qbs b/tests/auto/api/testdata/qt5-plugin/qt5-plugin.qbs
index 204711625..1558e3bc9 100644
--- a/tests/auto/api/testdata/qt5-plugin/qt5-plugin.qbs
+++ b/tests/auto/api/testdata/qt5-plugin/qt5-plugin.qbs
@@ -1,4 +1,3 @@
-import qbs.base
import qbs.File
import qbs.FileInfo
@@ -8,7 +7,7 @@ DynamicLibrary {
Depends { name: "Qt.core" }
Depends { name: "cpp" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
diff --git a/tests/auto/api/testdata/remove-file-dependency/main.cpp b/tests/auto/api/testdata/remove-file-dependency/main.cpp
index 5c0b03938..47cd716d7 100644
--- a/tests/auto/api/testdata/remove-file-dependency/main.cpp
+++ b/tests/auto/api/testdata/remove-file-dependency/main.cpp
@@ -31,7 +31,7 @@
int main()
{
- printf("The magic value is %d.\n", magicValue());
+ std::printf("The magic value is %d.\n", magicValue());
return 0;
}
diff --git a/tests/auto/api/testdata/rename-product/rename.qbs b/tests/auto/api/testdata/rename-product/rename.qbs
index 9d23bf804..aa59d9650 100644
--- a/tests/auto/api/testdata/rename-product/rename.qbs
+++ b/tests/auto/api/testdata/rename-product/rename.qbs
@@ -11,7 +11,7 @@ Project {
cpp.defines: "MY_EXPORT=DLL_EXPORT"
files: "lib.cpp"
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
diff --git a/tests/auto/api/testdata/rename-target-artifact/rename.qbs b/tests/auto/api/testdata/rename-target-artifact/rename.qbs
index 810b0eb08..aea4b8d9e 100644
--- a/tests/auto/api/testdata/rename-target-artifact/rename.qbs
+++ b/tests/auto/api/testdata/rename-target-artifact/rename.qbs
@@ -14,7 +14,7 @@ Project {
qbs.buildVariant: "release"
files: "lib.cpp"
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
diff --git a/tests/auto/api/testdata/renamed-qbs-source-file/renamed-qbs-source-file.qbs b/tests/auto/api/testdata/renamed-qbs-source-file/renamed-qbs-source-file.qbs
new file mode 100644
index 000000000..d295d43ea
--- /dev/null
+++ b/tests/auto/api/testdata/renamed-qbs-source-file/renamed-qbs-source-file.qbs
@@ -0,0 +1,9 @@
+Project {
+ references: "the-product/the-prodduct.qbs"
+ Product {
+ Group {
+ files: "the-product/*.qbs"
+ fileTags: []
+ }
+ }
+}
diff --git a/tests/auto/api/testdata/renamed-qbs-source-file/the-product/the-prodduct.qbs b/tests/auto/api/testdata/renamed-qbs-source-file/the-product/the-prodduct.qbs
new file mode 100644
index 000000000..86718b571
--- /dev/null
+++ b/tests/auto/api/testdata/renamed-qbs-source-file/the-product/the-prodduct.qbs
@@ -0,0 +1 @@
+Product { }
diff --git a/tests/auto/api/testdata/restored-warnings/restored-warnings.qbs b/tests/auto/api/testdata/restored-warnings/restored-warnings.qbs
index bbdfbeadb..9d4abb757 100644
--- a/tests/auto/api/testdata/restored-warnings/restored-warnings.qbs
+++ b/tests/auto/api/testdata/restored-warnings/restored-warnings.qbs
@@ -1,14 +1,25 @@
import qbs.Process 1.5
-CppApplication {
- name: "theProduct"
+Project {
+ CppApplication {
+ name: "theProduct"
- property bool moreFiles: false
- cpp.blubb: true
+ cpp.blubb: true
- files: ["file.cpp", "main.cpp"]
- Group {
- condition: moreFiles
- files: ["blubb.cpp"]
+ files: ["file.cpp", "main.cpp"]
+ }
+
+ Product {
+ name: "theOtherProduct"
+ property bool dummy: { throw "this one comes from a thread"; }
+ }
+
+ Product {
+ name: "aThirdProduct"
+ property bool moreFiles: false
+ Group {
+ condition: moreFiles
+ files: ["blubb.txt"]
+ }
}
}
diff --git a/tests/auto/api/testdata/same-base-name/same-base-name.qbs b/tests/auto/api/testdata/same-base-name/same-base-name.qbs
index 8448d04fd..ba0adfaac 100644
--- a/tests/auto/api/testdata/same-base-name/same-base-name.qbs
+++ b/tests/auto/api/testdata/same-base-name/same-base-name.qbs
@@ -16,7 +16,7 @@ Project {
]
Group {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
files: [
"lib.m",
"lib.mm"
@@ -25,7 +25,7 @@ Project {
Export {
Depends { name: "cpp" }
- cpp.frameworks: qbs.targetOS.contains("darwin") ? "Foundation" : undefined
+ cpp.frameworks: qbs.targetOS.includes("darwin") ? "Foundation" : undefined
}
}
}
diff --git a/tests/auto/api/testdata/static-lib-deps/static-lib-deps.qbs b/tests/auto/api/testdata/static-lib-deps/static-lib-deps.qbs
index b584b4d41..67800b8eb 100644
--- a/tests/auto/api/testdata/static-lib-deps/static-lib-deps.qbs
+++ b/tests/auto/api/testdata/static-lib-deps/static-lib-deps.qbs
@@ -44,23 +44,23 @@ Project {
]
Group {
- condition: qbs.targetOS.contains("macos")
+ condition: qbs.targetOS.includes("macos")
files: ["d.mm"]
}
Properties {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
cpp.defines: ["WITH_SETUPAPI"]
cpp.staticLibraries: ["setupapi"]
}
Properties {
- condition: qbs.targetOS.contains("macos")
+ condition: qbs.targetOS.includes("macos")
cpp.defines: ["WITH_LEX_YACC"]
cpp.staticLibraries: ["l", "y"]
cpp.frameworks: ["Foundation"]
}
Properties {
- condition: qbs.targetOS.contains("linux")
+ condition: qbs.targetOS.includes("linux")
cpp.defines: ["WITH_PTHREAD"]
cpp.staticLibraries: ["pthread"]
}
@@ -84,7 +84,7 @@ Project {
Depends { name: "e" }
Properties {
- condition: qbs.targetOS.contains("linux")
+ condition: qbs.targetOS.includes("linux")
cpp.driverFlags: ["-static"]
}
diff --git a/tests/auto/api/testdata/timeout-js/timeout.qbs b/tests/auto/api/testdata/timeout-js/timeout.qbs
index 26aa4ce87..72c72573b 100644
--- a/tests/auto/api/testdata/timeout-js/timeout.qbs
+++ b/tests/auto/api/testdata/timeout-js/timeout.qbs
@@ -8,7 +8,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Running infinite loop";
+ cmd.description = "running infinite loop";
cmd.sourceCode = function() {
while (true)
;
diff --git a/tests/auto/api/testdata/timeout-process/timeout.qbs b/tests/auto/api/testdata/timeout-process/timeout.qbs
index bb8deac9b..403506eed 100644
--- a/tests/auto/api/testdata/timeout-process/timeout.qbs
+++ b/tests/auto/api/testdata/timeout-process/timeout.qbs
@@ -1,3 +1,5 @@
+import qbs.Host
+
Project {
CppApplication {
type: "application"
@@ -7,25 +9,31 @@ Project {
cpp.cxxLanguageVersion: "c++11"
cpp.minimumOsxVersion: "10.8" // For <chrono>
Properties {
- condition: qbs.toolchain.contains("gcc")
+ condition: qbs.toolchain.includes("gcc")
cpp.driverFlags: "-pthread"
}
}
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
type: "product-under-test"
name: "caller"
Depends { name: "infinite-loop" }
Depends {
name: "cpp" // Make sure build environment is set up properly.
- condition: qbs.hostOS.contains("windows") && qbs.toolchain.contains("gcc")
+ condition: Host.os().includes("windows") && qbs.toolchain.includes("gcc")
}
Rule {
inputsFromDependencies: "application"
outputFileTags: "product-under-test"
prepare: {
var cmd = new Command(inputs["application"][0].filePath);
- cmd.description = "Calling application that runs forever";
+ cmd.description = "calling application that runs forever";
cmd.timeout = 3;
return cmd;
}
diff --git a/tests/auto/api/testdata/tool-in-module/use-outside-project/modules/thetool/thetool.qbs b/tests/auto/api/testdata/tool-in-module/use-outside-project/modules/thetool/thetool.qbs
index d72ebda17..e2b1ec6df 100644
--- a/tests/auto/api/testdata/tool-in-module/use-outside-project/modules/thetool/thetool.qbs
+++ b/tests/auto/api/testdata/tool-in-module/use-outside-project/modules/thetool/thetool.qbs
@@ -1,11 +1,12 @@
import qbs.FileInfo
+import qbs.Host
Module {
Depends { name: "cpp" }
Group {
name: "thetool binary"
files: FileInfo.cleanPath(FileInfo.joinPaths(path, "..", "..",
- "thetool" + (qbs.hostOS.contains("windows") ? ".exe" : "")));
+ "thetool" + (Host.os().includes("windows") ? ".exe" : "")));
fileTags: ["thetool.thetool"]
filesAreTargets: true
}
diff --git a/tests/auto/api/testdata/tool-in-module/use-within-project/use-within-project.qbs b/tests/auto/api/testdata/tool-in-module/use-within-project/use-within-project.qbs
index 575f4020e..ab4e07170 100644
--- a/tests/auto/api/testdata/tool-in-module/use-within-project/use-within-project.qbs
+++ b/tests/auto/api/testdata/tool-in-module/use-within-project/use-within-project.qbs
@@ -1,9 +1,18 @@
+import qbs.Host
+
Project {
CppApplication {
name: "thetool"
consoleApplication: true
files: "main.cpp"
+ property bool skip: {
+ var result = qbs.targetPlatform !== Host.platform();
+ if (result)
+ console.info("Skip this test");
+ return result;
+ }
+
install: true
installDir: ""
qbs.installPrefix: ""
diff --git a/tests/auto/api/testdata/transformer-data/transformer-data.qbs b/tests/auto/api/testdata/transformer-data/transformer-data.qbs
index f9433ed73..d29dbdbae 100644
--- a/tests/auto/api/testdata/transformer-data/transformer-data.qbs
+++ b/tests/auto/api/testdata/transformer-data/transformer-data.qbs
@@ -11,7 +11,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.sourceCode = function() {
var f = new TextFile(output.filePath, TextFile.WriteOnly);
f.close();
@@ -27,7 +27,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); };
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
index 8ac7b75a3..67f34ed66 100644
--- 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
@@ -12,7 +12,7 @@ Module {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.sourceCode = function() {
var f = new TextFile(output.filePath, TextFile.WriteOnly);
f.close();
diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp
index 235f2ac04..3816eca2b 100644
--- a/tests/auto/api/tst_api.cpp
+++ b/tests/auto/api/tst_api.cpp
@@ -144,7 +144,7 @@ static bool waitForFinished(qbs::AbstractJob *job, int timeout = 0)
TestApi::TestApi()
: m_logSink(new LogSink)
- , m_sourceDataDir(QDir::cleanPath(SRCDIR "/testdata"))
+ , m_sourceDataDir(testDataSourceDir(SRCDIR "/testdata"))
, m_workingDataDir(testWorkDir(QStringLiteral("api")))
{
}
@@ -196,7 +196,7 @@ void TestApi::addedFilePersistent()
// On the initial run, linking will fail.
const QString relProjectFilePath = "added-file-persistent";
ProcessResultReceiver receiver;
- qbs::ErrorInfo errorInfo = doBuildProject(relProjectFilePath, 0, &receiver);
+ qbs::ErrorInfo errorInfo = doBuildProject(relProjectFilePath, nullptr, &receiver);
QVERIFY(errorInfo.hasError());
QVERIFY2(isAboutUndefinedSymbols(receiver.output), qPrintable((receiver.output)));
receiver.output.clear();
@@ -206,7 +206,7 @@ void TestApi::addedFilePersistent()
const qbs::SetupProjectParameters params = defaultSetupParameters(relProjectFilePath);
REPLACE_IN_FILE(params.projectFilePath(), "/* 'file.cpp' */", "'file.cpp'");
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(params, m_logSink,
- 0));
+ nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
setupJob.reset(nullptr);
@@ -215,14 +215,14 @@ void TestApi::addedFilePersistent()
// Consequently, the linking step must fail as in the initial run.
WAIT_FOR_NEW_TIMESTAMP();
REPLACE_IN_FILE(params.projectFilePath(), "'file.cpp'", "/* 'file.cpp' */");
- errorInfo = doBuildProject(relProjectFilePath, 0, &receiver);
+ errorInfo = doBuildProject(relProjectFilePath, nullptr, &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();
REPLACE_IN_FILE(params.projectFilePath(), "/* 'file.cpp' */", "'file.cpp'");
- setupJob.reset(qbs::Project().setupProject(params, m_logSink, 0));
+ setupJob.reset(qbs::Project().setupProject(params, m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
setupJob.reset(nullptr);
@@ -249,7 +249,7 @@ void TestApi::buildGraphInfo()
setupParams.setTopLevelProfile(p.p.name());
setupParams.setOverriddenValues({std::make_pair("qbs.architecture", "arm")});
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
const QString bgFilePath = setupParams.buildRoot() + QLatin1Char('/')
@@ -295,14 +295,14 @@ void TestApi::buildGraphLocking()
qbs::SetupProjectParameters setupParams
= defaultSetupParameters("buildgraph-locking");
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
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));
+ setupJob.reset(qbs::Project().setupProject(setupParams, m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY(setupJob->error().hasError());
QVERIFY2(setupJob->error().toString().contains("lock"),
@@ -311,7 +311,7 @@ void TestApi::buildGraphLocking()
// 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));
+ setupJob.reset(qbs::Project().setupProject(setupParams2, m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
const QString buildDirName = relativeBuildDir(setupParams2.configurationName());
@@ -320,7 +320,7 @@ void TestApi::buildGraphLocking()
QVERIFY2(QFileInfo(lockFile).isFile(), qPrintable(lockFile));
qbs::Project project2 = setupJob->project();
QVERIFY(project2.isValid());
- setupJob.reset(project2.setupProject(setupParams, m_logSink, 0));
+ setupJob.reset(project2.setupProject(setupParams, m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY(setupJob->error().hasError());
QVERIFY2(setupJob->error().toString().contains("lock"),
@@ -330,7 +330,7 @@ void TestApi::buildGraphLocking()
// Case 3: Changing the build directory of an existing project to something non-competing.
qbs::SetupProjectParameters setupParams3 = setupParams2;
setupParams3.setBuildRoot(setupParams.buildRoot() + "/3");
- setupJob.reset(qbs::Project().setupProject(setupParams3, m_logSink, 0));
+ setupJob.reset(qbs::Project().setupProject(setupParams3, m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
project2 = qbs::Project();
@@ -342,7 +342,7 @@ void TestApi::buildGraphLocking()
QVERIFY(project3.isValid());
// Case 4: Changing the build directory again, but cancelling the job.
- setupJob.reset(project3.setupProject(setupParams2, m_logSink, 0));
+ setupJob.reset(project3.setupProject(setupParams2, m_logSink, nullptr));
QThread::sleep(1);
setupJob->cancel();
waitForFinished(setupJob.get());
@@ -362,7 +362,10 @@ void TestApi::buildProject()
+ QLatin1String(".qbs");
qbs::SetupProjectParameters params = defaultSetupParameters(projectFilePath);
removeBuildDir(params);
- qbs::ErrorInfo errorInfo = doBuildProject(projectFilePath);
+ ProcessResultReceiver resultReceiver;
+ qbs::ErrorInfo errorInfo = doBuildProject(projectFilePath, nullptr, &resultReceiver);
+ if (resultReceiver.output.contains("mingw32_gt_pch_use_address"))
+ QSKIP("https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91440");
VERIFY_NO_ERROR(errorInfo);
QVERIFY(regularFileExists(relativeBuildGraphFilePath()));
if (!productFileName.isEmpty()) {
@@ -373,7 +376,7 @@ void TestApi::buildProject()
WAIT_FOR_NEW_TIMESTAMP();
qbs::BuildOptions options;
options.setForceTimestampCheck(true);
- errorInfo = doBuildProject(projectFilePath, 0, 0, 0, options);
+ errorInfo = doBuildProject(projectFilePath, nullptr, nullptr, nullptr, options);
VERIFY_NO_ERROR(errorInfo);
if (!productFileName.isEmpty())
QVERIFY2(regularFileExists(productFileName), qPrintable(productFileName));
@@ -457,7 +460,7 @@ void TestApi::buildProjectDryRun()
removeBuildDir(params);
qbs::BuildOptions options;
options.setDryRun(true);
- const qbs::ErrorInfo errorInfo = doBuildProject(projectFilePath, 0, 0, 0, options);
+ const qbs::ErrorInfo errorInfo = doBuildProject(projectFilePath, nullptr, nullptr, nullptr, options);
VERIFY_NO_ERROR(errorInfo);
QVERIFY2(!QFileInfo::exists(relativeBuildDir()), qPrintable(QDir(relativeBuildDir())
.entryList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::System).join(", ")));
@@ -473,7 +476,7 @@ void TestApi::buildSingleFile()
qbs::SetupProjectParameters setupParams
= defaultSetupParameters("build-single-file");
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
qbs::Project project = setupJob->project();
@@ -483,9 +486,14 @@ void TestApi::buildSingleFile()
m_logSink->setLogLevel(qbs::LoggerMaxLevel);
std::unique_ptr<qbs::BuildJob> buildJob(project.buildAllProducts(options));
BuildDescriptionReceiver receiver;
+ ProcessResultReceiver resultReceiver;
+ connect(buildJob.get(), &qbs::BuildJob::reportProcessResult,
+ &resultReceiver, &ProcessResultReceiver::handleProcessResult);
connect(buildJob.get(), &qbs::BuildJob::reportCommandDescription, &receiver,
&BuildDescriptionReceiver::handleDescription);
waitForFinished(buildJob.get());
+ if (resultReceiver.output.contains("mingw32_gt_pch_use_address"))
+ QSKIP("https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91440");
QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString()));
QCOMPARE(receiver.descriptions.count("compiling"), 2);
QCOMPARE(receiver.descriptions.count("precompiling"), 1);
@@ -502,6 +510,8 @@ void TestApi::canonicalToolchainList()
QStringList({"xcode", "clang", "llvm", "gcc"}));
QCOMPARE(qbs::canonicalToolchain(QStringList({"clang", "llvm", "gcc"})),
QStringList({"clang", "llvm", "gcc"}));
+ QCOMPARE(qbs::canonicalToolchain(QStringList({"clang-cl", "msvc"})),
+ QStringList({"clang-cl", "msvc"}));
QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm", "gcc"})),
QStringList({"llvm", "gcc"}));
QCOMPARE(qbs::canonicalToolchain(QStringList({"mingw", "gcc"})),
@@ -516,6 +526,8 @@ void TestApi::canonicalToolchainList()
QStringList({"xcode", "clang", "llvm", "gcc"}));
QCOMPARE(qbs::canonicalToolchain(QStringList({"clang"})),
QStringList({"clang", "llvm", "gcc"}));
+ QCOMPARE(qbs::canonicalToolchain(QStringList({"clang-cl"})),
+ QStringList({"clang-cl", "msvc"}));
QCOMPARE(qbs::canonicalToolchain(QStringList({"llvm"})),
QStringList({"llvm", "gcc"}));
QCOMPARE(qbs::canonicalToolchain(QStringList({"mingw"})),
@@ -592,7 +604,7 @@ void TestApi::checkOutputs()
qbs::BuildOptions options;
options.setForceOutputCheck(check);
removeBuildDir(params);
- qbs::ErrorInfo errorInfo = doBuildProject("/check-outputs", 0, 0, 0, options);
+ qbs::ErrorInfo errorInfo = doBuildProject("/check-outputs", nullptr, nullptr, nullptr, options);
if (check)
QVERIFY(errorInfo.hasError());
else
@@ -616,37 +628,29 @@ qbs::GroupData findGroup(const qbs::ProductData &product, const QString &name)
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)
-{
- const auto products = project.products();
- for (const qbs::ProductData &p : products) {
- qDebug(" Product '%s' at %s", qPrintable(p.name()), qPrintable(p.location().toString()));
- const auto groups = p.groups();
- for (const qbs::GroupData &g : 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");
- std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
- waitForFinished(job.get());
- QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
- qbs::Project project = job->project();
- qbs::ProjectData projectData = project.projectData();
- QCOMPARE(projectData.allProducts().size(), 1);
- qbs::ProductData product = projectData.allProducts().front();
+ std::unique_ptr<qbs::SetupProjectJob> job;
+ qbs::Project project;
+ qbs::ProjectData projectData;
+ qbs::ProductData product;
+
+ const auto resolve = [&] {
+ job.reset(project.setupProject(setupParams, m_logSink, 0));
+ waitForFinished(job.get());
+ QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
+ project = job->project();
+ projectData = project.projectData();
+ QCOMPARE(projectData.allProducts().size(), 1);
+ product = projectData.allProducts().front();
+ };
+ resolve();
QVERIFY(product.groups().size() >= 8);
// Error handling: Invalid product.
@@ -659,12 +663,16 @@ void TestApi::changeContent()
QVERIFY(errorInfo.hasError());
QVERIFY(errorInfo.toString().contains("empty"));
+ WAIT_FOR_NEW_TIMESTAMP();
errorInfo = project.addGroup(product, "New Group 1");
VERIFY_NO_ERROR(errorInfo);
errorInfo = project.addGroup(product, "New Group 2");
VERIFY_NO_ERROR(errorInfo);
+ resolve();
+ QVERIFY(product.groups().size() >= 10);
+
// Error handling: Group already inserted.
errorInfo = project.addGroup(product, "New Group 1");
QVERIFY(errorInfo.hasError());
@@ -677,20 +685,14 @@ void TestApi::changeContent()
QVERIFY2(errorInfo.toString().contains("more than once"), qPrintable(errorInfo.toString()));
// Add files to empty array literal.
- projectData = project.projectData();
- QVERIFY(projectData.products().size() == 1);
- product = projectData.products().front();
- QVERIFY(product.groups().size() >= 10);
+ WAIT_FOR_NEW_TIMESTAMP();
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().size() == 1);
- product = projectData.products().front();
- QVERIFY(product.groups().size() >= 10);
+ resolve();
group = findGroup(product, "New Group 1");
QVERIFY(group.isValid());
errorInfo = project.addFiles(product, group, QStringList() << "file.cpp");
@@ -698,14 +700,12 @@ void TestApi::changeContent()
QVERIFY2(errorInfo.toString().contains("already"), qPrintable(errorInfo.toString()));
// Remove one of the newly added files again.
+ WAIT_FOR_NEW_TIMESTAMP();
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().size() == 1);
- product = projectData.products().front();
- QVERIFY(product.groups().size() >= 10);
+ resolve();
group = findGroup(product, "New Group 1");
QVERIFY(group.isValid());
errorInfo = project.removeFiles(product, group, QStringList() << "file.h");
@@ -720,38 +720,34 @@ void TestApi::changeContent()
QVERIFY2(errorInfo.toString().contains("complex"), qPrintable(errorInfo.toString()));
// Remove file from product's 'files' binding.
+ WAIT_FOR_NEW_TIMESTAMP();
errorInfo = project.removeFiles(product, qbs::GroupData(), QStringList("main.cpp"));
VERIFY_NO_ERROR(errorInfo);
+ resolve();
// Add file to non-empty array literal.
- projectData = project.projectData();
- QVERIFY(projectData.products().size() == 1);
- product = projectData.products().front();
+ WAIT_FOR_NEW_TIMESTAMP();
group = findGroup(product, "Existing Group 1");
QVERIFY(group.isValid());
errorInfo = project.addFiles(product, group, QStringList() << "newfile1.txt");
VERIFY_NO_ERROR(errorInfo);
+ resolve();
// Add files to list represented as a single string.
- projectData = project.projectData();
- QVERIFY(projectData.products().size() == 1);
- product = projectData.products().front();
+ WAIT_FOR_NEW_TIMESTAMP();
errorInfo = project.addFiles(product, qbs::GroupData(), QStringList() << "newfile2.txt");
VERIFY_NO_ERROR(errorInfo);
+ resolve();
// Add files to list represented as an identifier.
- projectData = project.projectData();
- QVERIFY(projectData.products().size() == 1);
- product = projectData.products().front();
+ WAIT_FOR_NEW_TIMESTAMP();
group = findGroup(product, "Existing Group 2");
QVERIFY(group.isValid());
errorInfo = project.addFiles(product, group, QStringList() << "newfile3.txt");
VERIFY_NO_ERROR(errorInfo);
+ resolve();
// Add files to list represented as a block of code (not yet implemented).
- projectData = project.projectData();
- QVERIFY(projectData.products().size() == 1);
- product = projectData.products().front();
group = findGroup(product, "Existing Group 3");
QVERIFY(group.isValid());
errorInfo = project.addFiles(product, group, QStringList() << "newfile4.txt");
@@ -759,18 +755,14 @@ void TestApi::changeContent()
QVERIFY2(errorInfo.toString().contains("complex"), qPrintable(errorInfo.toString()));
// Add file to group with directory prefix.
- projectData = project.projectData();
- QVERIFY(projectData.products().size() == 1);
- product = projectData.products().front();
+ WAIT_FOR_NEW_TIMESTAMP();
group = findGroup(product, "Existing Group 4");
QVERIFY(group.isValid());
errorInfo = project.addFiles(product, group, QStringList() << "file.txt");
VERIFY_NO_ERROR(errorInfo);
+ resolve();
// Error handling: Add file to group with non-directory prefix.
- projectData = project.projectData();
- QVERIFY(projectData.products().size() == 1);
- product = projectData.products().front();
group = findGroup(product, "Existing Group 5");
QVERIFY(group.isValid());
errorInfo = project.addFiles(product, group, QStringList() << "newfile1.txt");
@@ -778,16 +770,12 @@ void TestApi::changeContent()
QVERIFY2(errorInfo.toString().contains("prefix"), qPrintable(errorInfo.toString()));
// Remove group.
- projectData = project.projectData();
- QVERIFY(projectData.products().size() == 1);
- product = projectData.products().front();
+ WAIT_FOR_NEW_TIMESTAMP();
group = findGroup(product, "Existing Group 5");
QVERIFY(group.isValid());
errorInfo = project.removeGroup(product, group);
VERIFY_NO_ERROR(errorInfo);
- projectData = project.projectData();
- QVERIFY(projectData.products().size() == 1);
- QVERIFY(projectData.products().front().groups().size() >= 9);
+ resolve();
// Error handling: Try to remove the same group again.
errorInfo = project.removeGroup(product, group);
@@ -805,9 +793,7 @@ void TestApi::changeContent()
newFile.close();
errorInfo = project.addFiles(product, group, QStringList() << newFile.fileName());
VERIFY_NO_ERROR(errorInfo);
- projectData = project.projectData();
- QVERIFY(projectData.products().size() == 1);
- product = projectData.products().front();
+ resolve();
group = findGroup(product, "Group with wildcards");
QVERIFY(group.isValid());
QCOMPARE(group.sourceArtifactsFromWildcards().size(), 1);
@@ -838,46 +824,13 @@ void TestApi::changeContent()
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.get());
- 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().size() == 1
- && projectData.products().size() == 1
- && newProjectData.products().front().groups() == projectData.products().front().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.get(), &qbs::BuildJob::reportCommandDescription,
- &rcvr, &BuildDescriptionReceiver::handleDescription);
- waitForFinished(buildJob.get());
- 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().front(), "blubb");
+ errorInfo = project.addGroup(projectData.products().front(), "blubb");
QVERIFY(errorInfo.hasError());
QVERIFY2(errorInfo.toString().contains("in progress"), qPrintable(errorInfo.toString()));
waitForFinished(buildJob.get());
- errorInfo = project.addGroup(newProjectData.products().front(), "blubb");
+ errorInfo = project.addGroup(projectData.products().front(), "blubb");
VERIFY_NO_ERROR(errorInfo);
project = qbs::Project();
@@ -888,16 +841,11 @@ void TestApi::changeContent()
setupParams.setProjectFilePath(QDir::cleanPath(m_workingDataDir +
"/project-editing/project-with-no-files.qbs"));
- job.reset(project.setupProject(setupParams, m_logSink, 0));
- waitForFinished(job.get());
- QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
- project = job->project();
- projectData = project.projectData();
- QCOMPARE(projectData.allProducts().size(), 1);
- product = projectData.allProducts().front();
+ resolve();
+ WAIT_FOR_NEW_TIMESTAMP();
errorInfo = project.addFiles(product, qbs::GroupData(), QStringList("main.cpp"));
VERIFY_NO_ERROR(errorInfo);
- projectData = project.projectData();
+ resolve();
rcvr.descriptions.clear();
buildJob.reset(project.buildAllProducts(buildOptions, defaultProducts(), this));
connect(buildJob.get(), &qbs::BuildJob::reportCommandDescription,
@@ -908,27 +856,13 @@ void TestApi::changeContent()
job.reset(project.setupProject(setupParams, m_logSink, 0));
waitForFinished(job.get());
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().size() == 1
- && projectData.products().size() == 1
- && newProjectData.products().front().groups() == projectData.products().front().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");
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
qbs::Project project = setupJob->project();
@@ -971,7 +905,7 @@ void TestApi::dependencyOnMultiplexedType()
qbs::SetupProjectParameters setupParams
= defaultSetupParameters("/dependency-on-multiplexed-type");
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
qbs::Project project = setupJob->project();
@@ -994,16 +928,12 @@ void TestApi::dependencyOnMultiplexedType()
} else {
QVERIFY(p.name() == "p2");
++p2Count;
-
- // FIXME: This is an odd effect of our current algorithm: We collect the products
- // matching the requested type and add Depends items with their names ("p1" in
- // this case). Later, the algorithm checking for compatibility regarding the
- // multiplexing axes picks the aggregate. However, the aggregate does not have
- // a matching type... It's not entirely clear what the real expected
- // result should be here.
- QCOMPARE(p.dependencies().size(), 2);
+ QVERIFY(p.dependencies().contains("dep"));
}
}
+ QCOMPARE(depCount, 1);
+ QCOMPARE(p1Count, 3);
+ QCOMPARE(p2Count, 1);
std::unique_ptr<qbs::BuildJob> buildJob(project.buildAllProducts(qbs::BuildOptions()));
waitForFinished(buildJob.get());
QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString()));
@@ -1060,7 +990,7 @@ void TestApi::errorInSetupRunEnvironment()
qbs::SetupProjectParameters setupParams
= defaultSetupParameters("error-in-setup-run-environment");
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
const qbs::Project project = job->project();
@@ -1076,7 +1006,7 @@ void TestApi::errorInSetupRunEnvironment()
qbs::ErrorInfo error;
const QProcessEnvironment env = runEnv.runEnvironment(&error);
QVERIFY(error.hasError());
- QVERIFY(error.toString().contains("trallala"));
+ QVERIFY2(error.toString().contains("trallala"), qPrintable(error.toString()));
} catch (const qbs::ErrorInfo &) {
exceptionCaught = true;
}
@@ -1087,7 +1017,7 @@ void TestApi::excludedInputs()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters("excluded-inputs");
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
const qbs::Project project = job->project();
@@ -1095,10 +1025,11 @@ void TestApi::excludedInputs()
waitForFinished(buildJob.get());
QVERIFY2(!buildJob->error().hasError(), qPrintable(job->error().toString()));
QVERIFY(project.isValid());
- QCOMPARE(project.projectData().products().size(), 2);
+ const qbs::ProjectData projectData = project.projectData();
+ QCOMPARE(projectData.products().size(), 2);
qbs::ProductData depProduct;
qbs::ProductData pProduct;
- for (qbs::ProductData &p : project.projectData().products()) {
+ for (const qbs::ProductData &p : projectData.products()) {
if (p.name() == "dep")
depProduct = p;
else if (p.name() == "p")
@@ -1129,7 +1060,7 @@ void TestApi::excludedInputs()
QCOMPARE(dummyCount, 3);
}
-static qbs::ErrorInfo forceRuleEvaluation(const qbs::Project project)
+static qbs::ErrorInfo forceRuleEvaluation(const qbs::Project &project)
{
qbs::BuildOptions buildOptions;
buildOptions.setDryRun(true);
@@ -1143,7 +1074,7 @@ void TestApi::disabledInstallGroup()
qbs::SetupProjectParameters setupParams
= defaultSetupParameters("disabled_install_group");
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
const qbs::Project project = job->project();
@@ -1175,6 +1106,95 @@ void TestApi::disabledProject()
VERIFY_NO_ERROR(errorInfo);
}
+void TestApi::disappearedWildcardFile()
+{
+ const qbs::SetupProjectParameters setupParams
+ = defaultSetupParameters("disappeared-wildcard-file/disappeared-wildcard-file.qbs");
+ std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
+ m_logSink, nullptr));
+ QVERIFY(waitForFinished(setupJob.get()));
+ VERIFY_NO_ERROR(setupJob->error());
+
+ qbs::Project project = setupJob->project();
+ qbs::ProjectData projectData = project.projectData();
+ QVERIFY(projectData.isValid());
+ QList<qbs::ProductData> products = projectData.allProducts();
+ QCOMPARE(products.size(), 1);
+ QCOMPARE(products.first().groups().size(), 1);
+ QCOMPARE(products.first().groups().first().allFilePaths().size(), 2);
+
+ std::unique_ptr<qbs::BuildJob> buildJob(project.buildAllProducts({}));
+ QVERIFY(waitForFinished(buildJob.get()));
+ VERIFY_NO_ERROR(buildJob->error());
+
+ WAIT_FOR_NEW_TIMESTAMP();
+ const QString fileToRemove = QFileInfo(setupParams.projectFilePath()).path() + "/file2.txt";
+ QVERIFY(QFile::remove(fileToRemove));
+ buildJob.reset(project.buildAllProducts({}));
+ QVERIFY(waitForFinished(buildJob.get()));
+ QVERIFY(buildJob->error().hasError());
+ QVERIFY2(buildJob->error().toString().contains(
+ tr("Source file '%1' has disappeared.")
+ .arg(fileToRemove)), qPrintable(buildJob->error().toString()));
+
+ setupJob.reset(project.setupProject(setupParams, m_logSink, nullptr));
+ QVERIFY(waitForFinished(setupJob.get()));
+ VERIFY_NO_ERROR(setupJob->error());
+
+ project = setupJob->project();
+ projectData = project.projectData();
+ QVERIFY(projectData.isValid());
+ products = projectData.allProducts();
+ QCOMPARE(products.size(), 1);
+ QCOMPARE(products.first().groups().size(), 1);
+ QCOMPARE(products.first().groups().first().allFilePaths().size(), 1);
+
+ buildJob.reset(project.buildAllProducts({}));
+ QVERIFY(waitForFinished(buildJob.get()));
+ VERIFY_NO_ERROR(buildJob->error());
+}
+
+void TestApi::renamedQbsSource()
+{
+ const qbs::SetupProjectParameters setupParams
+ = defaultSetupParameters("renamed-qbs-source-file/renamed-qbs-source-file.qbs");
+ std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
+ m_logSink, nullptr));
+ QVERIFY(waitForFinished(setupJob.get()));
+ VERIFY_NO_ERROR(setupJob->error());
+ qbs::Project project = setupJob->project();
+ QCOMPARE(project.projectData().allProducts().size(), 2);
+
+ std::unique_ptr<qbs::BuildJob> buildJob(project.buildAllProducts({}));
+ QVERIFY(waitForFinished(buildJob.get()));
+ VERIFY_NO_ERROR(buildJob->error());
+
+ WAIT_FOR_NEW_TIMESTAMP();
+ const QString oldFilePath = QFileInfo(setupParams.projectFilePath()).path()
+ + "/the-product/the-prodduct.qbs";
+ const QString newFilePath = QFileInfo(setupParams.projectFilePath()).path()
+ + "/the-product/the-product.qbs";
+ QVERIFY(QFile::rename(oldFilePath, newFilePath));
+ REPLACE_IN_FILE(setupParams.projectFilePath(), "prodduct", "product");
+ buildJob.reset(project.buildAllProducts({}));
+ QVERIFY(waitForFinished(buildJob.get()));
+ QVERIFY(buildJob->error().hasError());
+ QVERIFY2(buildJob->error().toString().contains(
+ tr("Source file '%1' has disappeared.")
+ .arg(oldFilePath)), qPrintable(buildJob->error().toString()));
+
+ setupJob.reset(project.setupProject(setupParams, m_logSink, nullptr));
+ QVERIFY(waitForFinished(setupJob.get()));
+ VERIFY_NO_ERROR(setupJob->error());
+
+ project = setupJob->project();
+ QCOMPARE(project.projectData().allProducts().size(), 2);
+
+ buildJob.reset(project.buildAllProducts({}));
+ QVERIFY(waitForFinished(buildJob.get()));
+ VERIFY_NO_ERROR(buildJob->error());
+}
+
void TestApi::duplicateProductNames()
{
QFETCH(QString, projectFileName);
@@ -1210,6 +1230,8 @@ void TestApi::explicitlyDependsOn()
BuildDescriptionReceiver receiver;
qbs::ErrorInfo errorInfo = doBuildProject("explicitly-depends-on", &receiver);
VERIFY_NO_ERROR(errorInfo);
+ if (m_logSink->output.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QVERIFY2(receiver.descriptions.contains("compiling compiler.cpp"),
qPrintable(receiver.descriptions));
QVERIFY2(receiver.descriptions.contains("compiling a.in"), qPrintable(receiver.descriptions));
@@ -1266,7 +1288,7 @@ void TestApi::fallbackGcc()
QVERIFY(project.isValid());
QList<qbs::ProductData> products = project.allProducts();
QCOMPARE(products.size(), 2);
- for (const qbs::ProductData &p : qAsConst(products)) {
+ for (const qbs::ProductData &p : std::as_const(products)) {
if (p.profile() == "unixProfile") {
qbs::PropertyMap moduleProps = p.moduleProperties();
QCOMPARE(moduleProps.getModuleProperty("qbs", "targetOS").toStringList(),
@@ -1305,7 +1327,7 @@ void TestApi::fileTagsFilterOverride()
qbs::SetupProjectParameters setupParams
= defaultSetupParameters("filetagsfilter_override");
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
qbs::Project project = job->project();
@@ -1326,7 +1348,7 @@ void TestApi::generatedFilesList()
qbs::SetupProjectParameters setupParams
= defaultSetupParameters("generated-files-list");
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
QVERIFY(waitForFinished(setupJob.get()));
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
qbs::Project project = setupJob->project();
@@ -1385,7 +1407,7 @@ void TestApi::infiniteLoopBuilding()
qbs::SetupProjectParameters setupParams
= defaultSetupParameters(projectDirName + "/infinite-loop.qbs");
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
qbs::Project project = setupJob->project();
@@ -1400,13 +1422,16 @@ void TestApi::infiniteLoopBuilding_data()
QTest::addColumn<QString>("projectDirName");
QTest::newRow("JS Command") << QString("infinite-loop-js");
QTest::newRow("Process Command") << QString("infinite-loop-process");
+ QTest::newRow("Scanner (scan property)") << QString("infinite-loop-scanning-scan");
+ QTest::newRow("Scanner (searchPaths property)")
+ << QString("infinite-loop-scanning-searchpaths");
}
void TestApi::infiniteLoopResolving()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters("infinite-loop-resolving");
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
QTimer::singleShot(1000, setupJob.get(), &qbs::AbstractJob::cancel);
QVERIFY(waitForFinished(setupJob.get(), testTimeoutInMsecs()));
QVERIFY2(setupJob->error().toString().toLower().contains("cancel"),
@@ -1435,7 +1460,7 @@ void TestApi::inheritQbsSearchPaths()
QVariantMap overriddenValues;
overriddenValues.insert("project.qbsSearchPaths",
QStringList() << m_workingDataDir + "/inherit-qbs-search-paths/subdir");
- errorInfo = doBuildProject(projectFilePath, 0, 0, 0, qbs::BuildOptions(), overriddenValues);
+ errorInfo = doBuildProject(projectFilePath, nullptr, nullptr, nullptr, qbs::BuildOptions(), overriddenValues);
VERIFY_NO_ERROR(errorInfo);
}
@@ -1452,7 +1477,7 @@ void TestApi::installableFiles()
overriddenValues.insert(QStringLiteral("qbs.installRoot"), QStringLiteral("/tmp"));
setupParams.setOverriddenValues(overriddenValues);
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
qbs::Project project = job->project();
@@ -1481,7 +1506,7 @@ void TestApi::installableFiles()
setupParams = defaultSetupParameters("recursive-wildcards");
setupParams.setOverriddenValues(overriddenValues);
- job.reset(project.setupProject(setupParams, m_logSink, 0));
+ job.reset(project.setupProject(setupParams, m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
project = job->project();
@@ -1502,7 +1527,7 @@ void TestApi::isRunnable()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters("is-runnable");
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
qbs::Project project = job->project();
@@ -1528,27 +1553,30 @@ void TestApi::linkDynamicAndStaticLibs()
BuildDescriptionReceiver bdr;
qbs::BuildOptions options;
options.setEchoMode(qbs::CommandEchoModeCommandLine);
+ m_logSink->output.clear();
const qbs::ErrorInfo errorInfo = doBuildProject("link-dynamiclibs-staticlibs", &bdr, nullptr,
nullptr, options);
VERIFY_NO_ERROR(errorInfo);
+ const bool isGcc = m_logSink->output.contains("is gcc: true");
+ const bool isNotGcc = m_logSink->output.contains("is gcc: false");
+ if (isNotGcc)
+ QSKIP("The remainder of this test applies only to GCC");
+ QVERIFY(isGcc);
+
// The dependent static libs should not appear in the link command for the executable.
- const SettingsPtr s = settings();
- const qbs::Profile buildProfile(profileName(), s.get());
- if (buildProfile.value("qbs.toolchain").toStringList().contains("gcc")) {
- static const std::regex appLinkCmdRex(" -o [^ ]*/HelloWorld" QBS_HOST_EXE_SUFFIX " ");
- QString appLinkCmd;
- for (const QString &line : qAsConst(bdr.descriptionLines)) {
- const auto ln = line.toStdString();
- if (std::regex_search(ln, appLinkCmdRex)) {
- appLinkCmd = line;
- break;
- }
+ static const std::regex appLinkCmdRex(" -o [^ ]*/HelloWorld" QBS_HOST_EXE_SUFFIX " ");
+ QString appLinkCmd;
+ for (const QString &line : std::as_const(bdr.descriptionLines)) {
+ const auto ln = line.toStdString();
+ if (std::regex_search(ln, appLinkCmdRex)) {
+ appLinkCmd = line;
+ break;
}
- QVERIFY(!appLinkCmd.isEmpty());
- QVERIFY(!appLinkCmd.contains("static1"));
- QVERIFY(!appLinkCmd.contains("static2"));
}
+ QVERIFY(!appLinkCmd.isEmpty());
+ QVERIFY(!appLinkCmd.contains("static1"));
+ QVERIFY(!appLinkCmd.contains("static2"));
}
void TestApi::linkStaticAndDynamicLibs()
@@ -1563,31 +1591,32 @@ void TestApi::linkStaticAndDynamicLibs()
const bool isNormalUnix = m_logSink->output.contains("is normal unix: yes");
const bool isNotNormalUnix = m_logSink->output.contains("is normal unix: no");
QVERIFY2(isNormalUnix != isNotNormalUnix, qPrintable(m_logSink->output));
+ const bool isGcc = m_logSink->output.contains("is gcc: true");
+ const bool isNotGcc = m_logSink->output.contains("is gcc: false");
+ if (isNotGcc)
+ QSKIP("The remainder of this test applies only to GCC");
+ QVERIFY(isGcc);
// The dependencies libdynamic1.so and libstatic2.a must not appear in the link command for the
// executable. The -rpath-link line for libdynamic1.so must be there.
- const SettingsPtr s = settings();
- const qbs::Profile buildProfile(profileName(), s.get());
- if (buildProfile.value("qbs.toolchain").toStringList().contains("gcc")) {
- static const std::regex appLinkCmdRex(" -o [^ ]*/HelloWorld" QBS_HOST_EXE_SUFFIX " ");
- QString appLinkCmd;
- for (const QString &line : qAsConst(bdr.descriptionLines)) {
- const auto ln = line.toStdString();
- if (std::regex_search(ln, appLinkCmdRex)) {
- appLinkCmd = line;
- break;
- }
- }
- QVERIFY(!appLinkCmd.isEmpty());
- if (isNormalUnix) {
- const std::regex rpathLinkRex("-rpath-link=\\S*/"
- + relativeProductBuildDir("dynamic2").toStdString());
- const auto ln = appLinkCmd.toStdString();
- QVERIFY(std::regex_search(ln, rpathLinkRex));
+ static const std::regex appLinkCmdRex(" -o [^ ]*/HelloWorld" QBS_HOST_EXE_SUFFIX " ");
+ QString appLinkCmd;
+ for (const QString &line : std::as_const(bdr.descriptionLines)) {
+ const auto ln = line.toStdString();
+ if (std::regex_search(ln, appLinkCmdRex)) {
+ appLinkCmd = line;
+ break;
}
- QVERIFY(!appLinkCmd.contains("libstatic2.a"));
- QVERIFY(!appLinkCmd.contains("libdynamic2.so"));
}
+ QVERIFY(!appLinkCmd.isEmpty());
+ if (isNormalUnix) {
+ const std::regex rpathLinkRex("-rpath-link=\\S*/"
+ + relativeProductBuildDir("dynamic2").toStdString());
+ const auto ln = appLinkCmd.toStdString();
+ QVERIFY(std::regex_search(ln, rpathLinkRex));
+ }
+ QVERIFY(!appLinkCmd.contains("libstatic2.a"));
+ QVERIFY(!appLinkCmd.contains("libdynamic2.so"));
}
void TestApi::listBuildSystemFiles()
@@ -1595,15 +1624,14 @@ void TestApi::listBuildSystemFiles()
qbs::SetupProjectParameters setupParams
= defaultSetupParameters("subprojects/toplevelproject.qbs");
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
- const auto buildSystemFiles = qbs::Internal::Set<QString>::fromStdSet(
- 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"));
+ const auto buildSystemFiles = job->project().buildSystemFiles();
+ QVERIFY(buildSystemFiles.count(setupParams.projectFilePath()));
+ QVERIFY(buildSystemFiles.count(setupParams.buildRoot() + "/subproject2/subproject2.qbs"));
+ QVERIFY(buildSystemFiles.count(setupParams.buildRoot()
+ + "/subproject2/subproject3/subproject3.qbs"));
}
void TestApi::localProfiles()
@@ -1614,7 +1642,7 @@ void TestApi::localProfiles()
setupParams.setOverriddenValues(
{std::make_pair(QString("project.enableProfiles"), enableProfiles)});
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
QString taskDescriptions;
const auto taskDescHandler = [&taskDescriptions](const QString &desc, int, qbs::AbstractJob *) {
taskDescriptions += '\n' + desc;
@@ -1636,7 +1664,7 @@ void TestApi::localProfiles()
qbs::ProductData libClang;
qbs::ProductData appDebug;
qbs::ProductData appRelease;
- for (const qbs::ProductData &p : qAsConst(products)) {
+ for (const qbs::ProductData &p : std::as_const(products)) {
if (p.name() == "lib") {
if (p.profile() == "mingwProfile")
libMingw = p;
@@ -1666,7 +1694,7 @@ void TestApi::localProfiles()
QStringList({"mingw", "gcc"}));
if (moduleProps.getModuleProperty("cpp", "present").toBool()) {
QCOMPARE(moduleProps.getModuleProperty("cpp", "cxxCompilerName").toString(),
- QString("g++"));
+ qbs::Internal::HostOsInfo::appendExecutableSuffix(QString("g++")));
}
moduleProps = libClang.moduleProperties();
QCOMPARE(moduleProps.getModuleProperty("qbs", "targetOS").toStringList(),
@@ -1675,7 +1703,7 @@ void TestApi::localProfiles()
QStringList({"clang", "llvm", "gcc"}));
if (moduleProps.getModuleProperty("cpp", "present").toBool()) {
QCOMPARE(moduleProps.getModuleProperty("cpp", "cxxCompilerName").toString(),
- QString("clang++"));
+ qbs::Internal::HostOsInfo::appendExecutableSuffix(QString("clang++")));
}
moduleProps = appDebug.moduleProperties();
if (moduleProps.getModuleProperty("cpp", "present").toBool())
@@ -1685,7 +1713,7 @@ void TestApi::localProfiles()
QCOMPARE(moduleProps.getModuleProperty("cpp", "optimization").toString(), QString("fast"));
taskDescriptions.clear();
- job.reset(qbs::Project().setupProject(setupParams, m_logSink, 0));
+ job.reset(qbs::Project().setupProject(setupParams, m_logSink, nullptr));
connect(job.get(), &qbs::AbstractJob::taskStarted, taskDescHandler);
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
@@ -1699,20 +1727,20 @@ void TestApi::localProfiles()
projectFile.resize(0);
projectFile.write(content);
projectFile.close();
- job.reset(qbs::Project().setupProject(setupParams, m_logSink, 0));
+ job.reset(qbs::Project().setupProject(setupParams, m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
project = job->project().projectData();
products = project.allProducts();
QCOMPARE(products.size(), 4);
int clangProfiles = 0;
- for (const qbs::ProductData &p : qAsConst(products)) {
+ for (const qbs::ProductData &p : std::as_const(products)) {
if (p.profile() == "clangProfile") {
++clangProfiles;
moduleProps = p.moduleProperties();
if (moduleProps.getModuleProperty("cpp", "present").toBool()) {
QCOMPARE(moduleProps.getModuleProperty("cpp", "cxxCompilerName").toString(),
- QString("g++"));
+ qbs::Internal::HostOsInfo::appendExecutableSuffix(QString("g++")));
}
}
}
@@ -1733,7 +1761,7 @@ void TestApi::missingSourceFile()
setupParams.setProductErrorMode(qbs::ErrorHandlingMode::Relaxed);
m_logSink->setLogLevel(qbs::LoggerMinLevel);
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
qbs::ProjectData project = job->project().projectData();
@@ -1744,7 +1772,7 @@ void TestApi::missingSourceFile()
QCOMPARE(group.allSourceArtifacts().size(), 2);
QFile::rename("file2.txt.missing", "file2.txt");
- job.reset(qbs::Project().setupProject(setupParams, m_logSink, 0));
+ job.reset(qbs::Project().setupProject(setupParams, m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
project = job->project().projectData();
@@ -1797,12 +1825,13 @@ void TestApi::multiArch()
overriddenValues.insert("project.targetProfile", targetProfile.name());
setupParams.setOverriddenValues(overriddenValues);
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
qbs::Project project = setupJob->project();
QCOMPARE(project.profile(), profileName());
- const QList<qbs::ProductData> &products = project.projectData().products();
+ const qbs::ProjectData projectData = project.projectData();
+ const QList<qbs::ProductData> &products = projectData.products();
QCOMPARE(products.size(), 3);
QList<qbs::ProductData> hostProducts;
QList<qbs::ProductData> targetProducts;
@@ -1861,26 +1890,23 @@ void TestApi::multiArch()
QFile p2ArtifactInstalled(installRoot + "/host/host-tool.output");
QVERIFY2(p2ArtifactInstalled.exists(), qPrintable(p2ArtifactInstalled.fileName()));
- // Error check: Try to build for the same profile twice.
+ // Specifying the same profile twice should not result in an attempt to multiplex.
overriddenValues.insert("project.targetProfile", hostProfile.name());
setupParams.setOverriddenValues(overriddenValues);
- setupJob.reset(project.setupProject(setupParams, m_logSink, 0));
+ setupJob.reset(project.setupProject(setupParams, m_logSink, nullptr));
waitForFinished(setupJob.get());
- QVERIFY(setupJob->error().hasError());
- QVERIFY2(setupJob->error().toString().contains("Duplicate entry 'host' in qbs.profiles."),
- qPrintable(setupJob->error().toString()));
+ QVERIFY(!setupJob->error().hasError());
+ QCOMPARE(int(setupJob->project().projectData().products().size()), 2);
- // Error check: Try to build for the same profile twice, this time attaching
- // the properties via the product name.
+ // The same, but this time attaching the properties via the product name.
overriddenValues.remove(QStringLiteral("project.targetProfile"));
overriddenValues.insert("products.p1.myProfiles",
targetProfile.name() + ',' + targetProfile.name());
setupParams.setOverriddenValues(overriddenValues);
- setupJob.reset(project.setupProject(setupParams, m_logSink, 0));
+ setupJob.reset(project.setupProject(setupParams, m_logSink, nullptr));
waitForFinished(setupJob.get());
- QVERIFY(setupJob->error().hasError());
- QVERIFY2(setupJob->error().toString().contains("Duplicate entry 'target' in qbs.profiles."),
- qPrintable(setupJob->error().toString()));
+ QVERIFY(!setupJob->error().hasError());
+ QCOMPARE(int(setupJob->project().projectData().products().size()), 2);
}
struct ProductDataSelector
@@ -1899,7 +1925,8 @@ struct ProductDataSelector
bool qbsPropertiesMatch(const qbs::ProductData &p) const
{
for (auto it = qbsProperties.begin(); it != qbsProperties.end(); ++it) {
- if (it.value() != p.moduleProperties().getModuleProperty("qbs", it.key()))
+ if (!qbs::qVariantsEqual(
+ it.value(), p.moduleProperties().getModuleProperty("qbs", it.key())))
return false;
}
return true;
@@ -1926,7 +1953,7 @@ void TestApi::multiplexing()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters("multiplexing");
std::unique_ptr<qbs::SetupProjectJob> setupJob(
- qbs::Project().setupProject(setupParams, m_logSink, 0));
+ qbs::Project().setupProject(setupParams, m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
qbs::Project project = setupJob->project();
@@ -2035,6 +2062,31 @@ void TestApi::multiplexing()
QVERIFY(product.dependencies().empty());
selector.clear();
+ selector.name = "multiplex-without-aggregator-4-depends-2";
+ selector.qbsProperties["architecture"] = "C64";
+ selector.qbsProperties["buildVariant"] = "debug";
+ product = takeMatchingProduct(products, selector);
+ QVERIFY(product.isValid());
+ QVERIFY(product.isMultiplexed());
+ QCOMPARE(product.dependencies().size(), 1);
+ selector.qbsProperties["buildVariant"] = "release";
+ product = takeMatchingProduct(products, selector);
+ QVERIFY(product.isValid());
+ QVERIFY(product.isMultiplexed());
+ QCOMPARE(product.dependencies().size(), 1);
+ selector.qbsProperties["architecture"] = "TRS-80";
+ selector.qbsProperties["buildVariant"] = "debug";
+ product = takeMatchingProduct(products, selector);
+ QVERIFY(product.isValid());
+ QVERIFY(product.isMultiplexed());
+ QCOMPARE(product.dependencies().size(), 1);
+ selector.qbsProperties["buildVariant"] = "release";
+ product = takeMatchingProduct(products, selector);
+ QVERIFY(product.isValid());
+ QVERIFY(product.isMultiplexed());
+ QCOMPARE(product.dependencies().size(), 1);
+
+ selector.clear();
selector.name = "multiplex-with-aggregator-2";
selector.qbsProperties["architecture"] = "C64";
product = takeMatchingProduct(products, selector);
@@ -2137,12 +2189,12 @@ void TestApi::newOutputArtifactInDependency()
void TestApi::newPatternMatch()
{
TaskReceiver receiver;
- qbs::ErrorInfo errorInfo = doBuildProject("new-pattern-match", 0, 0, &receiver);
+ qbs::ErrorInfo errorInfo = doBuildProject("new-pattern-match", nullptr, nullptr, &receiver);
VERIFY_NO_ERROR(errorInfo);
QVERIFY2(receiver.taskDescriptions.contains("Resolving"), qPrintable(m_logSink->output));
receiver.taskDescriptions.clear();
- errorInfo = doBuildProject("new-pattern-match", 0, 0, &receiver);
+ errorInfo = doBuildProject("new-pattern-match", nullptr, nullptr, &receiver);
VERIFY_NO_ERROR(errorInfo);
QVERIFY(!receiver.taskDescriptions.contains("Resolving"));
@@ -2150,18 +2202,18 @@ void TestApi::newPatternMatch()
QFile f("test.txt");
QVERIFY2(f.open(QIODevice::WriteOnly), qPrintable(f.errorString()));
f.close();
- errorInfo = doBuildProject("new-pattern-match", 0, 0, &receiver);
+ errorInfo = doBuildProject("new-pattern-match", nullptr, nullptr, &receiver);
VERIFY_NO_ERROR(errorInfo);
QVERIFY(receiver.taskDescriptions.contains("Resolving"));
receiver.taskDescriptions.clear();
- errorInfo = doBuildProject("new-pattern-match", 0, 0, &receiver);
+ errorInfo = doBuildProject("new-pattern-match", nullptr, nullptr, &receiver);
VERIFY_NO_ERROR(errorInfo);
QVERIFY(!receiver.taskDescriptions.contains("Resolving"));
WAIT_FOR_NEW_TIMESTAMP();
f.remove();
- errorInfo = doBuildProject("new-pattern-match", 0, 0, &receiver);
+ errorInfo = doBuildProject("new-pattern-match", nullptr, nullptr, &receiver);
VERIFY_NO_ERROR(errorInfo);
QVERIFY(receiver.taskDescriptions.contains("Resolving"));
}
@@ -2169,9 +2221,9 @@ void TestApi::newPatternMatch()
void TestApi::nonexistingProjectPropertyFromProduct()
{
qbs::SetupProjectParameters setupParams
- = defaultSetupParameters("nonexistingprojectproperties");
+ = defaultSetupParameters("nonexistingprojectproperties/invalidaccessfromproduct.qbs");
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QEXPECT_FAIL("", "QBS-432", Abort);
QVERIFY(job->error().hasError());
@@ -2188,7 +2240,7 @@ void TestApi::nonexistingProjectPropertyFromCommandLine()
projectProperties.insert(QStringLiteral("project.blubb"), QStringLiteral("true"));
setupParams.setOverriddenValues(projectProperties);
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY(job->error().hasError());
QVERIFY2(job->error().toString().contains(QLatin1String("blubb")),
@@ -2206,7 +2258,7 @@ void TestApi::projectDataAfterProductInvalidation()
qbs::SetupProjectParameters setupParams = defaultSetupParameters("project-data-after-"
"product-invalidation/project-data-after-product-invalidation.qbs");
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
qbs::Project project = setupJob->project();
@@ -2228,7 +2280,7 @@ void TestApi::projectDataAfterProductInvalidation()
projectFile.resize(0);
projectFile.write(content);
projectFile.flush();
- setupJob.reset(project.setupProject(setupParams, m_logSink, 0));
+ setupJob.reset(project.setupProject(setupParams, m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
QVERIFY(!project.isValid());
@@ -2260,6 +2312,8 @@ void TestApi::processResult()
ProcessResultReceiver resultReceiver;
const qbs::ErrorInfo errorInfo = doBuildProject("process-result",
nullptr, &resultReceiver, nullptr, qbs::BuildOptions(), overridden);
+ if (m_logSink->output.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(expectedExitCode != 0, errorInfo.hasError());
QVERIFY(resultReceiver.results.size() > 1);
const qbs::ProcessResult &result = resultReceiver.results.back();
@@ -2311,20 +2365,20 @@ void TestApi::projectInvalidation()
qbs::SetupProjectParameters setupParams = defaultSetupParameters("project-invalidation");
QVERIFY(QFile::copy("project.no-error.qbs", "project-invalidation.qbs"));
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
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-invalidation.qbs");
- setupJob.reset(project.setupProject(setupParams, m_logSink, 0));
+ setupJob.reset(project.setupProject(setupParams, m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY(setupJob->error().hasError());
QVERIFY(project.isValid()); // Error in Loader, old project still valid.
WAIT_FOR_NEW_TIMESTAMP();
copyFileAndUpdateTimestamp("project.late-error.qbs", "project-invalidation.qbs");
- setupJob.reset(project.setupProject(setupParams, m_logSink, 0));
+ setupJob.reset(project.setupProject(setupParams, m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY(setupJob->error().hasError());
QVERIFY(!project.isValid()); // Error in build data re-resolving, old project not valid anymore.
@@ -2334,13 +2388,13 @@ void TestApi::projectLocking()
{
qbs::SetupProjectParameters setupParams = defaultSetupParameters("project-locking");
std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
qbs::Project project = setupJob->project();
- setupJob.reset(project.setupProject(setupParams, m_logSink, 0));
+ setupJob.reset(project.setupProject(setupParams, m_logSink, nullptr));
std::unique_ptr<qbs::SetupProjectJob> setupJob2(project.setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob2.get());
QVERIFY(setupJob2->error().hasError());
QVERIFY2(setupJob2->error().toString()
@@ -2357,14 +2411,14 @@ void TestApi::projectPropertiesByName()
QVERIFY(errorInfo.hasError());
QVariantMap overridden;
overridden.insert("project.theDefines", QStringList() << "SUB1" << "SUB2");
- errorInfo = doBuildProject(projectFile, 0, 0, 0, qbs::BuildOptions(), overridden);
+ errorInfo = doBuildProject(projectFile, nullptr, nullptr, nullptr, qbs::BuildOptions(), overridden);
QVERIFY(errorInfo.hasError());
overridden.clear();
overridden.insert("projects.subproject1.theDefines", QStringList() << "SUB1");
- errorInfo = doBuildProject(projectFile, 0, 0, 0, qbs::BuildOptions(), overridden);
+ errorInfo = doBuildProject(projectFile, nullptr, nullptr, nullptr, qbs::BuildOptions(), overridden);
QVERIFY(errorInfo.hasError());
overridden.insert("projects.subproject2.theDefines", QStringList() << "SUB2");
- errorInfo = doBuildProject(projectFile, 0, 0, 0, qbs::BuildOptions(), overridden);
+ errorInfo = doBuildProject(projectFile, nullptr, nullptr, nullptr, qbs::BuildOptions(), overridden);
VERIFY_NO_ERROR(errorInfo);
}
@@ -2424,7 +2478,7 @@ void TestApi::referencedFileErrors()
params.setProductErrorMode(relaxedMode ? qbs::ErrorHandlingMode::Relaxed
: qbs::ErrorHandlingMode::Strict);
m_logSink->setLogLevel(qbs::LoggerMinLevel);
- std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(params, m_logSink, 0));
+ std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(params, m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(job->error().hasError() != relaxedMode, qPrintable(job->error().toString()));
const qbs::Project project = job->project();
@@ -2459,7 +2513,9 @@ qbs::SetupProjectParameters TestApi::defaultSetupParameters(const QString &proje
}
qbs::SetupProjectParameters setupParams;
- setupParams.setEnvironment(QProcessEnvironment::systemEnvironment());
+ auto environment = QProcessEnvironment::systemEnvironment();
+ environment.insert("QBS_AUTOTEST_CODE_SIGNING_REQUIRED", "0");
+ setupParams.setEnvironment(environment);
setupParams.setProjectFilePath(projectFilePath);
setupParams.setPropertyCheckingMode(qbs::ErrorHandlingMode::Strict);
setupParams.setOverrideBuildGraphData(true);
@@ -2474,6 +2530,7 @@ qbs::SetupProjectParameters TestApi::defaultSetupParameters(const QString &proje
setupParams.setLibexecPath(QDir::cleanPath(QCoreApplication::applicationDirPath()
+ QLatin1String("/" QBS_RELATIVE_LIBEXEC_PATH)));
setupParams.setTopLevelProfile(profileName());
+ setupParams.setMaxJobCount(2);
setupParams.setConfigurationName(QStringLiteral("default"));
setupParams.setSettingsDirectory(settings()->baseDirectory());
return setupParams;
@@ -2484,21 +2541,21 @@ void TestApi::references()
qbs::SetupProjectParameters setupParams = defaultSetupParameters("references/invalid1.qbs");
const QString projectDir = QDir::cleanPath(m_workingDataDir + "/references");
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
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));
+ job.reset(qbs::Project().setupProject(setupParams, m_logSink, nullptr));
waitForFinished(job.get());
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));
+ job.reset(qbs::Project().setupProject(setupParams, m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
const qbs::ProjectData topLevelProject = job->project().projectData();
@@ -2514,7 +2571,7 @@ void TestApi::relaxedModeRecovery()
setupParams.setProductErrorMode(qbs::ErrorHandlingMode::Relaxed);
setupParams.setPropertyCheckingMode(qbs::ErrorHandlingMode::Relaxed);
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
if (m_logSink->warnings.size() != 4) {
@@ -2600,7 +2657,7 @@ void TestApi::removeFileDependency()
QFile::remove("someheader.h");
ProcessResultReceiver receiver;
- errorInfo = doBuildProject("remove-file-dependency/removeFileDependency.qbs", 0, &receiver);
+ errorInfo = doBuildProject("remove-file-dependency/removeFileDependency.qbs", nullptr, &receiver);
QVERIFY(errorInfo.hasError());
QVERIFY2(receiver.output.contains("someheader.h"), qPrintable(receiver.output));
}
@@ -2613,7 +2670,7 @@ void TestApi::resolveProject()
const qbs::SetupProjectParameters params = defaultSetupParameters(projectSubDir);
removeBuildDir(params);
const std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(params,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
VERIFY_NO_ERROR(setupJob->error());
QVERIFY2(!QFile::exists(productFileName), qPrintable(productFileName));
@@ -2634,7 +2691,7 @@ void TestApi::resolveProjectDryRun()
params.setDryRun(true);
removeBuildDir(params);
const std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(params,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
VERIFY_NO_ERROR(setupJob->error());
QVERIFY2(!QFile::exists(productFileName), qPrintable(productFileName));
@@ -2654,44 +2711,51 @@ void TestApi::restoredWarnings()
// Initial resolving: Errors are new.
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
job.reset(nullptr);
- QCOMPARE(toSet(m_logSink->warnings).size(), 2);
+ QCOMPARE(toSet(m_logSink->warnings).size(), 5);
const auto beforeErrors = m_logSink->warnings;
for (const qbs::ErrorInfo &e : beforeErrors) {
const QString msg = e.toString();
QVERIFY2(msg.contains("Superfluous version")
- || msg.contains("Property 'blubb' is not declared"),
+ || msg.contains("Property 'blubb' is not declared")
+ || msg.contains("this one comes from a thread")
+ || msg.contains("Product 'theOtherProduct' had errors and was disabled")
+ || msg.contains("Product 'theProduct' had errors and was disabled"),
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));
+ job.reset(qbs::Project().setupProject(setupParams, m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
job.reset(nullptr);
- QCOMPARE(toSet(m_logSink->warnings).size(), 2);
+ QCOMPARE(toSet(m_logSink->warnings).size(), 5);
m_logSink->warnings.clear();
// Re-resolving with changes: Errors come from the re-resolving, stored ones must be suppressed.
QVariantMap overridenValues;
- overridenValues.insert("products.theProduct.moreFiles", true);
+ overridenValues.insert("products.aThirdProduct.moreFiles", true);
setupParams.setOverriddenValues(overridenValues);
- job.reset(qbs::Project().setupProject(setupParams, m_logSink, 0));
+ job.reset(qbs::Project().setupProject(setupParams, m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
job.reset(nullptr);
- QCOMPARE(toSet(m_logSink->warnings).size(), 3); // One more for the additional group
+ QCOMPARE(toSet(m_logSink->warnings).size(), 6); // One more for the additional group
const auto afterErrors = m_logSink->warnings;
for (const qbs::ErrorInfo &e : afterErrors) {
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));
+ QVERIFY2(
+ msg.contains("Superfluous version") || msg.contains("Property 'blubb' is not declared")
+ || msg.contains("blubb.txt' does not exist")
+ || msg.contains("this one comes from a thread")
+ || msg.contains("Product 'theOtherProduct' had errors and was disabled")
+ || msg.contains("Product 'theThirdProduct' had errors and was disabled")
+ || msg.contains("Product 'theProduct' had errors and was disabled"),
+ qPrintable(msg));
}
m_logSink->warnings.clear();
}
@@ -2710,7 +2774,7 @@ void TestApi::runEnvForDisabledProduct()
const qbs::SetupProjectParameters params
= defaultSetupParameters("run-disabled-product/run-disabled-product.qbs");
const std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(params,
- m_logSink, 0));
+ m_logSink, nullptr));
QVERIFY(waitForFinished(setupJob.get()));
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
const qbs::Project project = setupJob->project();
@@ -2747,7 +2811,7 @@ void TestApi::sourceFileInBuildDir()
const QString generatedFile = relativeProductBuildDir("theProduct") + "/generated.cpp";
QVERIFY2(regularFileExists(generatedFile), qPrintable(generatedFile));
std::unique_ptr<qbs::SetupProjectJob> job(qbs::Project().setupProject(setupParams,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(job.get());
QVERIFY2(!job->error().hasError(), qPrintable(job->error().toString()));
const qbs::ProjectData projectData = job->project().projectData();
@@ -2820,7 +2884,7 @@ void TestApi::targetArtifactStatus()
= defaultSetupParameters("target-artifact-status/target-artifact-status.qbs");
params.setOverriddenValues({std::make_pair("products.p.enableTagging", enableTagging)});
const std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(params,
- m_logSink, 0));
+ m_logSink, nullptr));
waitForFinished(setupJob.get());
VERIFY_NO_ERROR(setupJob->error());
const qbs::Project project = setupJob->project();
@@ -2842,6 +2906,8 @@ void TestApi::timeout()
std::unique_ptr<qbs::SetupProjectJob> setupJob{
qbs::Project().setupProject(setupParams, m_logSink, nullptr)};
waitForFinished(setupJob.get());
+ if (m_logSink->output.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
auto project = setupJob->project();
const auto products = project.projectData().products();
@@ -2881,29 +2947,39 @@ void TestApi::toolInModule()
{
QVariantMap overrides({std::make_pair("qbs.installRoot", m_workingDataDir
+ "/tool-in-module/use-outside-project")});
- const qbs::ErrorInfo error
- = doBuildProject("tool-in-module/use-within-project/use-within-project.qbs", nullptr,
- nullptr, nullptr, qbs::BuildOptions(), overrides);
- QVERIFY2(!error.hasError(), qPrintable(error.toString()));
+
+ qbs::SetupProjectParameters params
+ = defaultSetupParameters("tool-in-module/use-within-project/use-within-project.qbs");
+ params.setOverriddenValues(overrides);
+ std::unique_ptr<qbs::SetupProjectJob> setupJob(
+ qbs::Project().setupProject(params, m_logSink, nullptr));
+ QVERIFY(waitForFinished(setupJob.get()));
+ QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
+ if (m_logSink->output.contains("Skip this test"))
+ QSKIP("Skip this test");
+
+ std::unique_ptr<qbs::BuildJob> buildJob(setupJob->project()
+ .buildAllProducts(qbs::BuildOptions()));
+
+ QVERIFY(waitForFinished(buildJob.get()));
+ QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString()));
+
const QString toolOutput = relativeProductBuildDir("user-in-project") + "/tool-output.txt";
QVERIFY2(QFile::exists(toolOutput), qPrintable(toolOutput));
- const qbs::SetupProjectParameters params
- = defaultSetupParameters("tool-in-module/use-outside-project/use-outside-project.qbs");
- const std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(params,
- m_logSink, 0));
+ params = defaultSetupParameters("tool-in-module/use-outside-project/use-outside-project.qbs");
+ setupJob.reset(qbs::Project().setupProject(params, m_logSink, nullptr));
QVERIFY(waitForFinished(setupJob.get()));
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
- const qbs::Project project = setupJob->project();
- const qbs::ProjectData projectData = project.projectData();
- const QList<qbs::ProductData> products = projectData.products();
+ const auto project = setupJob->project();
+ const auto projectData = project.projectData();
+ const auto products = projectData.products();
QCOMPARE(products.size(), 1);
const qbs::ProductData product = products.front();
const auto groups = product.groups();
for (const qbs::GroupData &group : groups)
QVERIFY(group.name() != "thetool binary");
- const std::unique_ptr<qbs::BuildJob> buildJob(setupJob->project()
- .buildAllProducts(qbs::BuildOptions()));
+ buildJob.reset(setupJob->project().buildAllProducts(qbs::BuildOptions()));
QVERIFY(waitForFinished(buildJob.get()));
QVERIFY2(!buildJob->error().hasError(), qPrintable(buildJob->error().toString()));
const QString toolOutput2 = relativeProductBuildDir("user-outside-project")
@@ -2917,18 +2993,18 @@ void TestApi::trackAddQObjectHeader()
= 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"
+ qbsFile.write("CppApplication {\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);
+ = doBuildProject("missing-qobject-header/missingheader.qbs", nullptr, &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"
+ qbsFile.write("CppApplication {\n Depends { name: 'Qt.core' }\n"
" files: ['main.cpp', 'myobject.cpp','myobject.h']\n}");
qbsFile.close();
errorInfo = doBuildProject("missing-qobject-header/missingheader.qbs");
@@ -2942,7 +3018,7 @@ void TestApi::trackRemoveQObjectHeader()
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"
+ qbsFile.write("CppApplication {\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");
@@ -2950,11 +3026,11 @@ void TestApi::trackRemoveQObjectHeader()
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"
+ qbsFile.write("CppApplication {\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);
+ errorInfo = doBuildProject("missing-qobject-header/missingheader.qbs", nullptr, &receiver);
QVERIFY(errorInfo.hasError());
QVERIFY2(isAboutUndefinedSymbols(receiver.output), qPrintable(receiver.output));
}
@@ -2964,7 +3040,7 @@ void TestApi::transformerData()
const qbs::SetupProjectParameters params
= defaultSetupParameters("transformer-data/transformer-data.qbs");
const std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(params,
- m_logSink, 0));
+ m_logSink, nullptr));
QVERIFY(waitForFinished(setupJob.get()));
QVERIFY2(!setupJob->error().hasError(), qPrintable(setupJob->error().toString()));
const qbs::Project project = setupJob->project();
@@ -3038,13 +3114,13 @@ void TestApi::uic()
qbs::ErrorInfo TestApi::doBuildProject(
const QString &projectFilePath, BuildDescriptionReceiver *buildDescriptionReceiver,
ProcessResultReceiver *procResultReceiver, TaskReceiver *taskReceiver,
- const qbs::BuildOptions &options, const QVariantMap overriddenValues)
+ const qbs::BuildOptions &options, const QVariantMap &overriddenValues)
{
qbs::SetupProjectParameters params = defaultSetupParameters(projectFilePath);
params.setOverriddenValues(overriddenValues);
params.setDryRun(options.dryRun());
const std::unique_ptr<qbs::SetupProjectJob> setupJob(qbs::Project().setupProject(params,
- m_logSink, 0));
+ m_logSink, nullptr));
if (taskReceiver) {
connect(setupJob.get(), &qbs::AbstractJob::taskStarted,
taskReceiver, &TaskReceiver::handleTaskStart);
diff --git a/tests/auto/api/tst_api.h b/tests/auto/api/tst_api.h
index aa00ddc99..cca6d4970 100644
--- a/tests/auto/api/tst_api.h
+++ b/tests/auto/api/tst_api.h
@@ -69,9 +69,7 @@ private slots:
void buildProjectDryRun_data();
void buildSingleFile();
void canonicalToolchainList();
-#ifdef QBS_ENABLE_PROJECT_FILE_UPDATES
void changeContent();
-#endif
void changeDependentLib();
void checkOutputs();
void checkOutputs_data();
@@ -80,6 +78,7 @@ private slots:
void disabledInstallGroup();
void disabledProduct();
void disabledProject();
+ void disappearedWildcardFile();
void duplicateProductNames();
void duplicateProductNames_data();
void emptyFileTagList();
@@ -132,6 +131,7 @@ private slots:
void removeFileDependency();
void renameProduct();
void renameTargetArtifact();
+ void renamedQbsSource();
void resolveProject();
void resolveProject_data();
void resolveProjectDryRun();
@@ -161,7 +161,7 @@ private:
ProcessResultReceiver *procResultReceiver = 0,
TaskReceiver *taskReceiver = 0,
const qbs::BuildOptions &options = qbs::BuildOptions(),
- const QVariantMap overriddenValues = QVariantMap());
+ const QVariantMap &overriddenValues = QVariantMap());
LogSink * const m_logSink;
const QString m_sourceDataDir;
diff --git a/tests/auto/auto.pri b/tests/auto/auto.pri
deleted file mode 100644
index fd8afad50..000000000
--- a/tests/auto/auto.pri
+++ /dev/null
@@ -1,18 +0,0 @@
-TEMPLATE = app
-DESTDIR = ../../../bin
-DEFINES += SRCDIR=\\\"$$_PRO_FILE_PWD_\\\"
-qbs_test_suite_name = $$replace(_PRO_FILE_, ^.*/([^/.]+)\\.pro$, \\1)
-qbs_test_suite_name = $$upper($$replace(qbs_test_suite_name, -, _))
-DEFINES += QBS_TEST_SUITE_NAME=\\\"$${qbs_test_suite_name}\\\"
-INCLUDEPATH += $$PWD/../../src $$PWD/../../src/app/shared
-
-QT = core testlib
-CONFIG += testcase console
-CONFIG -= app_bundle
-CONFIG += c++14
-target.CONFIG += no_default_install
-
-dev_lib_frameworks=$$QMAKE_XCODE_DEVELOPER_PATH/Library/Frameworks
-exists($$dev_lib_frameworks): LIBS += -F$$dev_lib_frameworks
-
-include(../../src/lib/corelib/use_corelib.pri)
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
deleted file mode 100644
index 1afe48176..000000000
--- a/tests/auto/auto.pro
+++ /dev/null
@@ -1,19 +0,0 @@
-TEMPLATE=subdirs
-
-qbs_enable_unit_tests {
- SUBDIRS += \
- buildgraph \
- language \
- tools \
-}
-
-SUBDIRS += \
- cmdlineparser \
- blackbox/blackbox.pro \
- blackbox/blackbox-android.pro \
- blackbox/blackbox-apple.pro \
- blackbox/blackbox-clangdb.pro \
- blackbox/blackbox-java.pro \
- blackbox/blackbox-joblimits.pro \
- blackbox/blackbox-qt.pro \
- api
diff --git a/tests/auto/auto.qbs b/tests/auto/auto.qbs
index bf75d0f23..b042d180a 100644
--- a/tests/auto/auto.qbs
+++ b/tests/auto/auto.qbs
@@ -1,19 +1,23 @@
-import qbs
-
Project {
name: "Autotests"
references: [
"api/api.qbs",
- "blackbox/blackbox.qbs",
"blackbox/blackbox-android.qbs",
"blackbox/blackbox-apple.qbs",
+ "blackbox/blackbox-baremetal.qbs",
"blackbox/blackbox-clangdb.qbs",
+ "blackbox/blackbox-examples.qbs",
"blackbox/blackbox-java.qbs",
"blackbox/blackbox-joblimits.qbs",
+ "blackbox/blackbox-providers.qbs",
"blackbox/blackbox-qt.qbs",
+ "blackbox/blackbox-tutorial.qbs",
+ "blackbox/blackbox-windows.qbs",
+ "blackbox/blackbox.qbs",
"buildgraph/buildgraph.qbs",
"cmdlineparser/cmdlineparser.qbs",
"language/language.qbs",
+ "pkgconfig/pkgconfig.qbs",
"tools/tools.qbs",
]
}
diff --git a/tests/auto/blackbox/CMakeLists.txt b/tests/auto/blackbox/CMakeLists.txt
new file mode 100644
index 000000000..88e19acdf
--- /dev/null
+++ b/tests/auto/blackbox/CMakeLists.txt
@@ -0,0 +1,99 @@
+add_qbs_test(blackbox
+ DEFINES
+ ${QBS_UNIT_TESTS_DEFINES}
+ "QBS_VERSION=\"${QBS_VERSION}\""
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_blackbox.cpp
+ tst_blackbox.h
+ )
+
+add_qbs_test(blackbox-android
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_blackboxandroid.cpp
+ tst_blackboxandroid.h
+ )
+
+add_qbs_test(blackbox-apple
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_blackboxapple.cpp
+ tst_blackboxapple.h
+ )
+
+add_qbs_test(blackbox-baremetal
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_blackboxbaremetal.cpp
+ tst_blackboxbaremetal.h
+ )
+
+add_qbs_test(blackbox-clangdb
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_clangdb.cpp
+ tst_clangdb.h
+ )
+
+add_qbs_test(blackbox-java
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_blackboxjava.cpp
+ tst_blackboxjava.h
+ )
+
+add_qbs_test(blackbox-joblimits
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_blackboxjoblimits.cpp
+ )
+
+add_qbs_test(blackbox-providers
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_blackboxproviders.cpp
+ )
+
+add_qbs_test(blackbox-qt
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_blackboxqt.cpp
+ tst_blackboxqt.h
+ )
+
+add_qbs_test(blackbox-tutorial
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_blackboxtutorial.h
+ tst_blackboxtutorial.cpp
+ )
+
+add_qbs_test(blackbox-windows
+ SOURCES
+ ../shared.h
+ tst_blackboxbase.cpp
+ tst_blackboxbase.h
+ tst_blackboxwindows.cpp
+ tst_blackboxwindows.h
+ )
diff --git a/tests/auto/blackbox/blackbox-android.pro b/tests/auto/blackbox/blackbox-android.pro
deleted file mode 100644
index 7aca99e8d..000000000
--- a/tests/auto/blackbox/blackbox-android.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-TARGET = tst_blackbox-android
-
-HEADERS = tst_blackboxandroid.h tst_blackboxbase.h
-SOURCES = tst_blackboxandroid.cpp tst_blackboxbase.cpp
-OBJECTS_DIR = android
-MOC_DIR = $${OBJECTS_DIR}-moc
-
-include(../auto.pri)
-
-DATA_DIRS = testdata-android ../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
-
-DISTFILES += \
- testdata/texttemplate/expected-output-one.txt
diff --git a/tests/auto/blackbox/blackbox-apple.pro b/tests/auto/blackbox/blackbox-apple.pro
deleted file mode 100644
index 1a009e222..000000000
--- a/tests/auto/blackbox/blackbox-apple.pro
+++ /dev/null
@@ -1,20 +0,0 @@
-TARGET = tst_blackbox-apple
-
-HEADERS = tst_blackboxapple.h tst_blackboxbase.h
-SOURCES = tst_blackboxapple.cpp tst_blackboxbase.cpp
-OBJECTS_DIR = apple
-MOC_DIR = $${OBJECTS_DIR}-moc
-
-include(../auto.pri)
-
-QT += xml
-
-DATA_DIRS = testdata-apple ../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-baremetal.qbs b/tests/auto/blackbox/blackbox-baremetal.qbs
new file mode 100644
index 000000000..18ae588fe
--- /dev/null
+++ b/tests/auto/blackbox/blackbox-baremetal.qbs
@@ -0,0 +1,21 @@
+import qbs.Utilities
+
+QbsAutotest {
+ testName: "blackbox-baremetal"
+ Depends { name: "qbs_app" }
+ Depends { name: "qbs-setup-toolchains" }
+ Group {
+ name: "testdata"
+ prefix: "testdata-baremetal/"
+ files: ["**/*"]
+ fileTags: []
+ }
+ files: [
+ "../shared.h",
+ "tst_blackboxbase.cpp",
+ "tst_blackboxbase.h",
+ "tst_blackboxbaremetal.cpp",
+ "tst_blackboxbaremetal.h",
+ ]
+ cpp.defines: base.concat(["SRCDIR=" + Utilities.cStringQuote(path)])
+}
diff --git a/tests/auto/blackbox/blackbox-clangdb.pro b/tests/auto/blackbox/blackbox-clangdb.pro
deleted file mode 100644
index 6e4075175..000000000
--- a/tests/auto/blackbox/blackbox-clangdb.pro
+++ /dev/null
@@ -1,18 +0,0 @@
-TARGET = tst_blackbox-clangdb
-
-HEADERS = tst_blackboxbase.h tst_clangdb.h
-SOURCES = tst_blackboxbase.cpp tst_clangdb.cpp
-OBJECTS_DIR = clangdb
-MOC_DIR = $${OBJECTS_DIR}-moc
-
-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-examples.qbs b/tests/auto/blackbox/blackbox-examples.qbs
new file mode 100644
index 000000000..77d32636c
--- /dev/null
+++ b/tests/auto/blackbox/blackbox-examples.qbs
@@ -0,0 +1,21 @@
+import qbs.Utilities
+
+QbsAutotest {
+ testName: "blackbox-examples"
+ Depends { name: "qbs_app" }
+ Depends { name: "qbs-setup-toolchains" }
+ Group {
+ name: "testdata"
+ prefix: "../../../examples/"
+ files: ["**/*"]
+ fileTags: []
+ }
+ files: [
+ "../shared.h",
+ "tst_blackboxexamples.cpp",
+ "tst_blackboxexamples.h",
+ "tst_blackboxbase.cpp",
+ "tst_blackboxbase.h",
+ ]
+ cpp.defines: base.concat(["SRCDIR=" + Utilities.cStringQuote(path)])
+}
diff --git a/tests/auto/blackbox/blackbox-java.pro b/tests/auto/blackbox/blackbox-java.pro
deleted file mode 100644
index d297d9e09..000000000
--- a/tests/auto/blackbox/blackbox-java.pro
+++ /dev/null
@@ -1,18 +0,0 @@
-TARGET = tst_blackbox-java
-
-HEADERS = tst_blackboxjava.h tst_blackboxbase.h
-SOURCES = tst_blackboxjava.cpp tst_blackboxbase.cpp
-OBJECTS_DIR = java
-MOC_DIR = $${OBJECTS_DIR}-moc
-
-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-joblimits.pro b/tests/auto/blackbox/blackbox-joblimits.pro
deleted file mode 100644
index 85413473e..000000000
--- a/tests/auto/blackbox/blackbox-joblimits.pro
+++ /dev/null
@@ -1,18 +0,0 @@
-TARGET = tst_blackbox-joblimits
-
-HEADERS = tst_blackboxbase.h
-SOURCES = tst_blackboxjoblimits.cpp tst_blackboxbase.cpp
-OBJECTS_DIR = joblimits
-MOC_DIR = $${OBJECTS_DIR}-moc
-
-include(../auto.pri)
-
-DATA_DIRS = testdata-joblimits ../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-providers.qbs b/tests/auto/blackbox/blackbox-providers.qbs
new file mode 100644
index 000000000..95ebaa423
--- /dev/null
+++ b/tests/auto/blackbox/blackbox-providers.qbs
@@ -0,0 +1,21 @@
+import qbs.Utilities
+
+QbsAutotest {
+ testName: "blackbox-providers"
+ Depends { name: "qbs_app" }
+ Depends { name: "qbs-setup-toolchains" }
+ Group {
+ name: "testdata"
+ prefix: "testdata-providers/"
+ files: ["**/*"]
+ fileTags: []
+ }
+ files: [
+ "../shared.h",
+ "tst_blackboxbase.cpp",
+ "tst_blackboxbase.h",
+ "tst_blackboxproviders.cpp",
+ "tst_blackboxproviders.h",
+ ]
+ cpp.defines: base.concat(["SRCDIR=" + Utilities.cStringQuote(path)])
+}
diff --git a/tests/auto/blackbox/blackbox-qt.pro b/tests/auto/blackbox/blackbox-qt.pro
deleted file mode 100644
index e17a04a7e..000000000
--- a/tests/auto/blackbox/blackbox-qt.pro
+++ /dev/null
@@ -1,18 +0,0 @@
-TARGET = tst_blackbox-qt
-
-HEADERS = tst_blackboxqt.h tst_blackboxbase.h
-SOURCES = tst_blackboxqt.cpp tst_blackboxbase.cpp
-OBJECTS_DIR = qt
-MOC_DIR = $${OBJECTS_DIR}-moc
-
-include(../auto.pri)
-
-DATA_DIRS = testdata-qt ../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-tutorial.qbs b/tests/auto/blackbox/blackbox-tutorial.qbs
new file mode 100644
index 000000000..174821ffa
--- /dev/null
+++ b/tests/auto/blackbox/blackbox-tutorial.qbs
@@ -0,0 +1,21 @@
+import qbs.Utilities
+
+QbsAutotest {
+ testName: "blackbox-tutorial"
+ Depends { name: "qbs_app" }
+ Depends { name: "qbs-setup-toolchains" }
+ Group {
+ name: "testdata"
+ prefix: "../../../tutorial/"
+ files: ["**/*"]
+ fileTags: []
+ }
+ files: [
+ "../shared.h",
+ "tst_blackboxtutorial.cpp",
+ "tst_blackboxtutorial.h",
+ "tst_blackboxbase.cpp",
+ "tst_blackboxbase.h",
+ ]
+ cpp.defines: base.concat(["SRCDIR=" + Utilities.cStringQuote(path)])
+}
diff --git a/tests/auto/blackbox/blackbox-windows.qbs b/tests/auto/blackbox/blackbox-windows.qbs
new file mode 100644
index 000000000..e32421e3b
--- /dev/null
+++ b/tests/auto/blackbox/blackbox-windows.qbs
@@ -0,0 +1,21 @@
+import qbs.Utilities
+
+QbsAutotest {
+ testName: "blackbox-windows"
+ Depends { name: "qbs_app" }
+ Depends { name: "qbs-setup-toolchains" }
+ Group {
+ name: "testdata"
+ prefix: "testdata-windows/"
+ files: ["**/*"]
+ fileTags: []
+ }
+ files: [
+ "../shared.h",
+ "tst_blackboxbase.cpp",
+ "tst_blackboxbase.h",
+ "tst_blackboxwindows.cpp",
+ "tst_blackboxwindows.h",
+ ]
+ cpp.defines: base.concat(["SRCDIR=" + Utilities.cStringQuote(path)])
+}
diff --git a/tests/auto/blackbox/blackbox.pro b/tests/auto/blackbox/blackbox.pro
deleted file mode 100644
index 42848d077..000000000
--- a/tests/auto/blackbox/blackbox.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-TARGET = tst_blackbox
-
-HEADERS = tst_blackbox.h tst_blackboxbase.h
-SOURCES = tst_blackbox.cpp tst_blackboxbase.cpp
-OBJECTS_DIR = generic
-MOC_DIR = $${OBJECTS_DIR}-moc
-qbs_enable_unit_tests:DEFINES += QBS_ENABLE_UNIT_TESTS
-
-include(../auto.pri)
-
-QT += xml
-
-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
index 3f0ff959a..ac6bf750e 100644
--- a/tests/auto/blackbox/blackbox.qbs
+++ b/tests/auto/blackbox/blackbox.qbs
@@ -25,4 +25,5 @@ QbsAutotest {
]
cpp.defines: base.concat(["SRCDIR=" + Utilities.cStringQuote(path)])
.concat(qbsbuildconfig.enableUnitTests ? ["QBS_ENABLE_UNIT_TESTS"] : [])
+ .concat("QBS_VERSION=" + Utilities.cStringQuote(qbsversion.version))
}
diff --git a/tests/auto/blackbox/find/find-android.qbs b/tests/auto/blackbox/find/find-android.qbs
index 26dedc60f..1cde0ed84 100644
--- a/tests/auto/blackbox/find/find-android.qbs
+++ b/tests/auto/blackbox/find/find-android.qbs
@@ -3,12 +3,23 @@ import qbs.TextFile
Product {
property string packageName: ""
qbs.targetPlatform: "android"
+ multiplexByQbsProperties: ["architectures"]
+
+ Properties {
+ condition: qbs.architectures && qbs.architectures.length > 1
+ aggregate: true
+ multiplexedType: "json_arch"
+ }
Depends { name: "Android.sdk"; required: false }
Depends { name: "Android.ndk"; required: false }
type: ["json"]
+
Rule {
multiplex: true
+ property stringList inputTags: "json_arch"
+ inputsFromDependencies: inputTags
+ inputs: product.aggregate ? [] : inputTags
Artifact {
filePath: ["android.json"]
fileTags: ["json"]
@@ -18,17 +29,51 @@ Product {
cmd.description = output.filePath;
cmd.sourceCode = function() {
var tools = {};
+
+ for (var i in inputs["json_arch"]) {
+ var tf = new TextFile(inputs["json_arch"][i].filePath, TextFile.ReadOnly);
+ var json = JSON.parse(tf.readAll());
+ tools["ndk"] = json["ndk"];
+ tools["ndk-samples"] = json["ndk-samples"];
+ tf.close();
+ }
+
if (product.moduleProperty("Android.sdk", "present")) {
tools["sdk"] = product.moduleProperty("Android.sdk", "sdkDir");
tools["sdk-build-tools-dx"] = product.Android.sdk.dxFilePath;
+ tools["sdk-build-tools-d8"] = product.Android.sdk.d8FilePath;
}
+ if (product.java && product.java.present)
+ tools["jar"] = product.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;
+ }
+ }
+ Rule {
+ multiplex: true
+ Artifact {
+ filePath: ["android_arch.json"]
+ fileTags: ["json_arch"]
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = output.filePath;
+ cmd.sourceCode = function() {
+ var tools = {};
if (product.moduleProperty("Android.ndk", "present")) {
tools["ndk"] = product.moduleProperty("Android.ndk", "ndkDir");
tools["ndk-samples"] = product.Android.ndk.ndkSamplesDir;
}
- if (product.java && product.java.present)
- tools["jar"] = product.java.jarFilePath;
var tf;
try {
@@ -43,3 +88,4 @@ Product {
}
}
}
+
diff --git a/tests/auto/blackbox/find/find-xcode.qbs b/tests/auto/blackbox/find/find-xcode.qbs
index bb6ee9718..15c2aa17e 100644
--- a/tests/auto/blackbox/find/find-xcode.qbs
+++ b/tests/auto/blackbox/find/find-xcode.qbs
@@ -14,7 +14,9 @@ Product {
cmd.description = output.filePath;
cmd.sourceCode = function() {
var tools = {};
- if (product.moduleProperty("xcode", "present")) {
+ var present = product.moduleProperty("xcode", "present");
+ tools["present"] = !!present;
+ if (present) {
var keys = [
"developerPath",
"version"
diff --git a/tests/auto/blackbox/testdata-android/aidl/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/aidl/AndroidManifest.xml
index e8a950847..1d27681ac 100644
--- a/tests/auto/blackbox/testdata-android/aidl/AndroidManifest.xml
+++ b/tests/auto/blackbox/testdata-android/aidl/AndroidManifest.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:versionName="1.0" android:versionCode="1"
package="io.qbs.aidltest">
<application android:label="AidlTest">
<activity android:name="MainActivity">
diff --git a/tests/auto/blackbox/testdata-android/minimal-native/minimal-native.qbs b/tests/auto/blackbox/testdata-android/minimal-native/minimal-native.qbs
index 570152707..7231f7e62 100644
--- a/tests/auto/blackbox/testdata-android/minimal-native/minimal-native.qbs
+++ b/tests/auto/blackbox/testdata-android/minimal-native/minimal-native.qbs
@@ -1,7 +1,7 @@
CppApplication {
name: "minimalnative"
qbs.buildVariant: "release"
- Properties { condition: qbs.toolchain.contains("clang"); Android.ndk.appStl: "c++_shared" }
+ Properties { condition: qbs.toolchain.includes("clang"); Android.ndk.appStl: "c++_shared" }
Android.sdk.packageName: "my.minimalnative"
Android.sdk.apkBaseName: name
Android.ndk.appStl: "stlport_shared"
diff --git a/tests/auto/blackbox/testdata-android/minimal-native/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/minimal-native/src/main/AndroidManifest.xml
index 575e95e8d..f61dc9850 100644
--- a/tests/auto/blackbox/testdata-android/minimal-native/src/main/AndroidManifest.xml
+++ b/tests/auto/blackbox/testdata-android/minimal-native/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ android:versionName="1.0" android:versionCode="1"
package="somedefault">
<application android:label="MinimalNative">
<activity android:name="MainActivity">
diff --git a/tests/auto/blackbox/testdata-android/minimal-native/src/main/native/native.c b/tests/auto/blackbox/testdata-android/minimal-native/src/main/native/native.c
index 6b625858b..f49b4f90f 100644
--- a/tests/auto/blackbox/testdata-android/minimal-native/src/main/native/native.c
+++ b/tests/auto/blackbox/testdata-android/minimal-native/src/main/native/native.c
@@ -4,5 +4,6 @@
jstring
Java_minimalnative_MinimalNative_stringFromNative(JNIEnv* env, jobject thiz)
{
+ (void)thiz;
return (*env)->NewStringUTF(env, "This message comes from native code.");
}
diff --git a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/product1.qbs b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/product1.qbs
index c4a78a30b..c7b9b3de2 100644
--- a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/product1.qbs
+++ b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/product1.qbs
@@ -5,9 +5,12 @@ Project {
name: "p1lib1"
files: ["src/main/jni/lib1.cpp"]
qbs.targetPlatform: "android"
- Properties { condition: qbs.toolchain.contains("clang"); Android.ndk.appStl: "c++_shared" }
+ Properties { condition: qbs.toolchain.includes("clang"); Android.ndk.appStl: "c++_shared" }
Android.ndk.appStl: "stlport_shared"
- qbs.architectures: !qbs.architecture ? ["armv7a", "x86"] : undefined
+ Properties {
+ qbs.architectures: !qbs.architecture ? ["armv7a", "x86"] : undefined
+ overrideListProperties: true
+ }
cpp.useRPaths: false
}
@@ -17,7 +20,7 @@ Project {
name: "p1lib2"
files: ["src/main/jni/lib2.cpp"]
qbs.targetPlatform: "android"
- Properties { condition: qbs.toolchain.contains("clang"); Android.ndk.appStl: "c++_shared" }
+ Properties { condition: qbs.toolchain.includes("clang"); Android.ndk.appStl: "c++_shared" }
Android.ndk.appStl: "stlport_shared"
cpp.useRPaths: false
}
diff --git a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml
index 289969409..272fe55de 100644
--- a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml
+++ b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product1/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.qt.dummy1" android:versionCode="1" android:versionName="1.0">
- <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="19"/>
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<uses-feature android:glEsVersion="0x00020000"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:allowBackup="true" android:hasCode="true">
diff --git a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/product2.qbs b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/product2.qbs
index 9be70dcda..f880a576d 100644
--- a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/product2.qbs
+++ b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/product2.qbs
@@ -13,7 +13,7 @@ Project {
name: "p2lib2"
files: ["src/main/jni/lib2.cpp"]
qbs.targetPlatform: "android"
- Properties { condition: qbs.toolchain.contains("clang"); Android.ndk.appStl: "c++_shared" }
+ Properties { condition: qbs.toolchain.includes("clang"); Android.ndk.appStl: "c++_shared" }
Android.ndk.appStl: "stlport_shared"
}
diff --git a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml
index ef0fbe54f..871aadbe6 100644
--- a/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml
+++ b/tests/auto/blackbox/testdata-android/multiple-apks-per-project/product2/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.qt.dummy2" android:versionCode="1" android:versionName="1.0">
- <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="19"/>
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<uses-feature android:glEsVersion="0x00020000"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:allowBackup="true" android:hasCode="true">
diff --git a/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/multiple-libs-per-apk.qbs b/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/multiple-libs-per-apk.qbs
index 8b9ded211..a5e193905 100644
--- a/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/multiple-libs-per-apk.qbs
+++ b/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/multiple-libs-per-apk.qbs
@@ -5,7 +5,7 @@ Project {
name: "lib1"
files: ["src/main/jni/lib1.cpp"]
qbs.targetPlatform: "android"
- Properties { condition: qbs.toolchain.contains("clang"); Android.ndk.appStl: "c++_shared" }
+ Properties { condition: qbs.toolchain.includes("clang"); Android.ndk.appStl: "c++_shared" }
Android.ndk.appStl: "stlport_shared"
cpp.useRPaths: false
}
@@ -16,7 +16,7 @@ Project {
name: "lib2"
files: ["src/main/jni/lib2.cpp"]
qbs.targetPlatform: "android"
- Properties { condition: qbs.toolchain.contains("clang"); Android.ndk.appStl: "c++_shared" }
+ Properties { condition: qbs.toolchain.includes("clang"); Android.ndk.appStl: "c++_shared" }
Android.ndk.appStl: "stlport_shared"
cpp.useRPaths: false
}
diff --git a/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/src/main/AndroidManifest.xml
index 6694afc18..f184a8f1f 100644
--- a/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/src/main/AndroidManifest.xml
+++ b/tests/auto/blackbox/testdata-android/multiple-libs-per-apk/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="io.qt.dummy" android:versionCode="1" android:versionName="1.0">
- <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="19"/>
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<uses-feature android:glEsVersion="0x00020000"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:allowBackup="true" android:hasCode="true">
diff --git a/tests/auto/blackbox/testdata-android/qml-app/qml-app.qbs b/tests/auto/blackbox/testdata-android/qml-app/qml-app.qbs
index e91a14902..5760fa4f0 100644
--- a/tests/auto/blackbox/testdata-android/qml-app/qml-app.qbs
+++ b/tests/auto/blackbox/testdata-android/qml-app/qml-app.qbs
@@ -3,12 +3,12 @@ QtApplication {
Depends { name: "Qt.quick" }
Depends { name: "Qt.android_support" }
Properties {
- condition: qbs.targetOS.contains("android")
+ condition: qbs.targetOS.includes("android")
Qt.android_support.extraPrefixDirs: path
}
Android.sdk.packageName: "my.qmlapp"
Android.sdk.apkBaseName: name
- property stringList qmlImportPaths: path
+ Qt.android_support.qmlImportPaths: path
files: [
"main.cpp",
"qml.qrc",
diff --git a/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml b/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml
index 066ec0a63..c8237c639 100644
--- a/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml
+++ b/tests/auto/blackbox/testdata-android/qml-app/src/main/AndroidManifest.xml
@@ -16,7 +16,6 @@
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
- <meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
@@ -31,8 +30,6 @@
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
- <meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
- <meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
@@ -67,7 +64,7 @@
</application>
- <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16"/>
+ <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
diff --git a/tests/auto/blackbox/testdata-android/qt-app/MainWindow.cpp b/tests/auto/blackbox/testdata-android/qt-app/MainWindow.cpp
new file mode 100644
index 000000000..b2e08c83e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-android/qt-app/MainWindow.cpp
@@ -0,0 +1,11 @@
+#include "MainWindow.h"
+
+MainWindow::MainWindow(QWidget *parent)
+ : QMainWindow(parent)
+{
+}
+
+MainWindow::~MainWindow()
+{
+}
+
diff --git a/tests/auto/blackbox/testdata-android/qt-app/MainWindow.h b/tests/auto/blackbox/testdata-android/qt-app/MainWindow.h
new file mode 100644
index 000000000..ace53a4a0
--- /dev/null
+++ b/tests/auto/blackbox/testdata-android/qt-app/MainWindow.h
@@ -0,0 +1,15 @@
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+public:
+ MainWindow(QWidget *parent = nullptr);
+ ~MainWindow();
+};
+
+#endif // MAINWINDOW_H
diff --git a/tests/auto/blackbox/testdata-android/qt-app/Test.java b/tests/auto/blackbox/testdata-android/qt-app/Test.java
new file mode 100644
index 000000000..c57486d56
--- /dev/null
+++ b/tests/auto/blackbox/testdata-android/qt-app/Test.java
@@ -0,0 +1,54 @@
+package org.qbs.example;
+
+import org.qtproject.qt5.android.bindings.QtActivity;
+import android.os.*;
+import android.content.*;
+import android.app.*;
+import android.util.Log;
+
+import java.lang.String;
+import android.content.Intent;
+
+import org.qbs.example.*;
+
+
+public class Test extends QtActivity
+{
+ public static native void testFunc(String test);
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d("qbs", "onCreate Test");
+ Intent theIntent = getIntent();
+ if (theIntent != null) {
+ String theAction = theIntent.getAction();
+ if (theAction != null) {
+ Log.d("qbs onCreate ", theAction);
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d("qbs", "onDestroy");
+ System.exit(0);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d("qbs onActivityResult", "requestCode: "+requestCode);
+ if (resultCode == RESULT_OK) {
+ Log.d("qbs onActivityResult - resultCode: ", "SUCCESS");
+ } else {
+ Log.d("qbs onActivityResult - resultCode: ", "CANCEL");
+ }
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ Log.d("qbs", "onNewIntent");
+ super.onNewIntent(intent);
+ setIntent(intent);
+ }
+}
diff --git a/tests/auto/blackbox/testdata-android/qt-app/TestQt6.java b/tests/auto/blackbox/testdata-android/qt-app/TestQt6.java
new file mode 100644
index 000000000..239507122
--- /dev/null
+++ b/tests/auto/blackbox/testdata-android/qt-app/TestQt6.java
@@ -0,0 +1,54 @@
+package org.qbs.example;
+
+import org.qtproject.qt.android.bindings.QtActivity;
+import android.os.*;
+import android.content.*;
+import android.app.*;
+import android.util.Log;
+
+import java.lang.String;
+import android.content.Intent;
+
+import org.qbs.example.*;
+
+
+public class TestQt6 extends QtActivity
+{
+ public static native void testFunc(String test);
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d("qbs", "onCreate Test");
+ Intent theIntent = getIntent();
+ if (theIntent != null) {
+ String theAction = theIntent.getAction();
+ if (theAction != null) {
+ Log.d("qbs onCreate ", theAction);
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.d("qbs", "onDestroy");
+ System.exit(0);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d("qbs onActivityResult", "requestCode: "+requestCode);
+ if (resultCode == RESULT_OK) {
+ Log.d("qbs onActivityResult - resultCode: ", "SUCCESS");
+ } else {
+ Log.d("qbs onActivityResult - resultCode: ", "CANCEL");
+ }
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ Log.d("qbs", "onNewIntent");
+ super.onNewIntent(intent);
+ setIntent(intent);
+ }
+}
diff --git a/tests/auto/blackbox/testdata-android/qt-app/main.cpp b/tests/auto/blackbox/testdata-android/qt-app/main.cpp
new file mode 100644
index 000000000..0a0916fca
--- /dev/null
+++ b/tests/auto/blackbox/testdata-android/qt-app/main.cpp
@@ -0,0 +1,11 @@
+#include "MainWindow.h"
+
+#include <QApplication>
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+ MainWindow w;
+ w.show();
+ return a.exec();
+}
diff --git a/tests/auto/blackbox/testdata-android/qt-app/qt-app.qbs b/tests/auto/blackbox/testdata-android/qt-app/qt-app.qbs
new file mode 100644
index 000000000..04dcbf403
--- /dev/null
+++ b/tests/auto/blackbox/testdata-android/qt-app/qt-app.qbs
@@ -0,0 +1,27 @@
+Project {
+ QtGuiApplication {
+ Depends { name: "Lib" }
+ files: ["main.cpp", "MainWindow.cpp", "MainWindow.h" ]
+ Group {
+ condition: Qt.core.versionMajor == 5
+ files: ["Test.java"]
+ }
+ Group {
+ condition: Qt.core.versionMajor == 6
+ files: ["TestQt6.java"]
+ }
+
+ Android.sdk.packageName: "my.qtapp"
+ Android.sdk.apkBaseName: name
+ Depends { name: "Qt"; submodules: ["core", "widgets"] }
+ }
+
+ StaticLibrary {
+ name: "Lib"
+ Export {
+ Depends {
+ name: "Qt.android_support";
+ }
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-android/qt-app/test.keystore b/tests/auto/blackbox/testdata-android/qt-app/test.keystore
new file mode 100644
index 000000000..5713d10d2
--- /dev/null
+++ b/tests/auto/blackbox/testdata-android/qt-app/test.keystore
Binary files differ
diff --git a/tests/auto/blackbox/testdata-android/teapot/teapot.qbs b/tests/auto/blackbox/testdata-android/teapot/teapot.qbs
index 2b5cb8aba..0b1dead6e 100644
--- a/tests/auto/blackbox/testdata-android/teapot/teapot.qbs
+++ b/tests/auto/blackbox/testdata-android/teapot/teapot.qbs
@@ -51,7 +51,7 @@ Project {
files: ["*.cpp", "*.h"].concat(
!File.exists(ndkHelperProbe.dir + "/gl3stub.cpp") ? ["gl3stub.c"] : [])
}
- Properties { condition: qbs.toolchain.contains("clang"); Android.ndk.appStl: "c++_shared" }
+ Properties { condition: qbs.toolchain.includes("clang"); Android.ndk.appStl: "c++_shared" }
Android.ndk.appStl: "gnustl_shared"
cpp.cxxLanguageVersion: "c++11"
@@ -133,10 +133,11 @@ Project {
FileTagger { patterns: ["*.inl"]; fileTags: ["hpp"] }
+ version: "2.0"
Android.sdk.apkBaseName: name
Android.sdk.packageName: "com.sample.teapot"
Android.sdk.sourceSetDir: teapotProbe.dir
- Properties { condition: qbs.toolchain.contains("clang"); Android.ndk.appStl: "c++_shared" }
+ Properties { condition: qbs.toolchain.includes("clang"); Android.ndk.appStl: "c++_shared" }
Android.ndk.appStl: "gnustl_shared"
cpp.cxxLanguageVersion: "c++11"
cpp.dynamicLibraries: ["log", "android", "EGL", "GLESv2"]
diff --git a/tests/auto/blackbox/testdata-apple/aggregateDependencyLinking/aggregateDependencyLinking.qbs b/tests/auto/blackbox/testdata-apple/aggregateDependencyLinking/aggregateDependencyLinking.qbs
index e7c8867bd..6f57c1e44 100644
--- a/tests/auto/blackbox/testdata-apple/aggregateDependencyLinking/aggregateDependencyLinking.qbs
+++ b/tests/auto/blackbox/testdata-apple/aggregateDependencyLinking/aggregateDependencyLinking.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
minimumQbsVersion: "1.8"
@@ -9,11 +7,27 @@ Project {
Depends { name: "cpp" }
Depends { name: "bundle" }
+ property bool hasX86Mac: true // cannot use xcode.version in qbs.architectures
+ property bool hasArmMac: false
bundle.isBundle: false
// This will generate 2 multiplex configs and an aggregate.
- qbs.architectures: ["x86", "x86_64"]
+ qbs.architectures: {
+ if (qbs.targetPlatform === "macos") {
+ if (hasX86Mac)
+ return ["x86_64", "x86"];
+ else if (hasArmMac)
+ return ["arm64", "x86_64"];
+ } else if (qbs.targetPlatform === "ios") {
+ return ["arm64", "armv7a"];
+ }
+ console.info("Cannot build fat binaries for this target platform ("
+ + qbs.targetPlatform + ")");
+ return original;
+ }
+
qbs.buildVariant: "debug"
+ cpp.minimumMacosVersion: "10.8"
}
CppApplication {
@@ -21,15 +35,22 @@ Project {
files: ["app.c"]
// This should link only against the aggregate static library, and not against
- // the {debug, x86_64} variant, or worse - against both the single arch variant
+ // the {debug, arm64} variant, or worse - against both the single arch variant
// and the lipo-ed one.
Depends { name: "multi_arch_lib" }
Depends { name: "bundle" }
bundle.isBundle: false
- qbs.architecture: "x86_64"
+ qbs.architecture: {
+ if (qbs.targetPlatform === "macos")
+ return "x86_64";
+ else if (qbs.targetPlatform === "ios")
+ return "arm64";
+ return original;
+ }
qbs.buildVariant: "debug"
+ cpp.minimumMacosVersion: "10.8"
multiplexByQbsProperties: []
}
}
diff --git a/tests/auto/blackbox/testdata-apple/apple-multiconfig/apple-multiconfig.qbs b/tests/auto/blackbox/testdata-apple/apple-multiconfig/apple-multiconfig.qbs
index 438624f70..5615722d5 100644
--- a/tests/auto/blackbox/testdata-apple/apple-multiconfig/apple-multiconfig.qbs
+++ b/tests/auto/blackbox/testdata-apple/apple-multiconfig/apple-multiconfig.qbs
@@ -1,23 +1,32 @@
import qbs.Utilities
+import "../multiarch-helpers.js" as Helpers
+
Project {
minimumQbsVersion: "1.8"
- property bool enableX86
+ condition: xcodeVersion
+ property string xcodeVersion
CppApplication {
Depends { name: "singlelib" }
Depends { name: "bundle" }
+ property bool isShallow: {
+ console.info("isShallow: " + bundle.isShallow);
+ return bundle.isShallow;
+ }
name: "singleapp"
targetName: "singleapp"
files: ["app.c"]
cpp.rpaths: [cpp.rpathOrigin + "/../../../"]
cpp.minimumMacosVersion: "10.6"
+ cpp.minimumIosVersion: "8.0"
// Turn off multiplexing
aggregate: false
multiplexByQbsProperties: []
install: true
+ installDebugInformation: false
installDir: ""
}
@@ -29,13 +38,15 @@ Project {
files: ["app.c"]
cpp.rpaths: [cpp.rpathOrigin + "/../../../"]
cpp.minimumMacosVersion: "10.6"
+ cpp.minimumIosVersion: "8.0"
// Force aggregation when not needed
aggregate: true
- qbs.architectures: ["x86_64"]
+ qbs.architectures: [Helpers.getNewArch(qbs)]
qbs.buildVariants: ["release"]
install: true
+ installDebugInformation: false
installDir: ""
}
@@ -45,7 +56,7 @@ Project {
name: "singlelib"
targetName: "singlelib"
files: ["lib.c"]
- cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+ cpp.sonamePrefix: qbs.targetOS.includes("darwin") ? "@rpath" : undefined
cpp.defines: ["VARIANT=" + Utilities.cStringQuote(qbs.buildVariant)]
// Turn off multiplexing
@@ -53,6 +64,7 @@ Project {
multiplexByQbsProperties: []
install: true
+ installDebugInformation: false
installDir: ""
}
@@ -64,8 +76,10 @@ Project {
files: ["app.c"]
cpp.rpaths: [cpp.rpathOrigin + "/../../../"]
cpp.minimumMacosVersion: "10.6"
+ cpp.minimumIosVersion: "8.0"
install: true
+ installDebugInformation: false
installDir: ""
}
@@ -77,13 +91,16 @@ Project {
files: ["app.c"]
cpp.rpaths: [cpp.rpathOrigin + "/../../../"]
cpp.minimumMacosVersion: "10.6"
- qbs.architectures: project.enableX86 ? ["x86", "x86_64"] : ["x86_64"]
- qbs.architecture: "x86_64"
- multiplexByQbsProperties: project.enableX86 ? ["architectures", "buildVariants"]
- : ["buildVariants"]
+ cpp.minimumIosVersion: "8.0"
+ qbs.architectures: Helpers.getArchitectures(qbs, project.xcodeVersion)
+ qbs.architecture: Helpers.getNewArch(qbs)
+ multiplexByQbsProperties: Helpers.enableOldArch(qbs, project.xcodeVersion)
+ ? ["architectures", "buildVariants"]
+ : ["buildVariants"]
qbs.buildVariants: "debug"
install: true
+ installDebugInformation: false
installDir: ""
}
@@ -95,10 +112,12 @@ Project {
files: ["app.c"]
cpp.rpaths: [cpp.rpathOrigin + "/../../../"]
cpp.minimumMacosVersion: "10.6"
- qbs.architectures: project.enableX86 ? ["x86", "x86_64"] : ["x86_64"]
- qbs.buildVariants: ["debug", "profile"]
+ cpp.minimumIosVersion: "8.0"
+ qbs.architectures: Helpers.getArchitectures(qbs, project.xcodeVersion)
+ qbs.buildVariants: ["debug", "profiling"]
install: true
+ installDebugInformation: false
installDir: ""
}
@@ -108,12 +127,14 @@ Project {
name: "multilib"
targetName: "multilib"
files: ["lib.c"]
- cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+ cpp.minimumIosVersion: "8.0"
+ cpp.sonamePrefix: qbs.targetOS.includes("darwin") ? "@rpath" : undefined
cpp.defines: ["VARIANT=" + Utilities.cStringQuote(qbs.buildVariant)]
- qbs.architectures: project.enableX86 ? ["x86", "x86_64"] : ["x86_64"]
- qbs.buildVariants: ["release", "debug", "profile"]
+ qbs.architectures: Helpers.getArchitectures(qbs, project.xcodeVersion)
+ qbs.buildVariants: ["release", "debug", "profiling"]
install: true
+ installDebugInformation: false
installDir: ""
}
@@ -123,12 +144,14 @@ Project {
name: "multilib-no-release"
targetName: "multilib-no-release"
files: ["lib.c"]
- cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+ cpp.minimumIosVersion: "8.0"
+ cpp.sonamePrefix: qbs.targetOS.includes("darwin") ? "@rpath" : undefined
cpp.defines: ["VARIANT=" + Utilities.cStringQuote(qbs.buildVariant)]
- qbs.architectures: project.enableX86 ? ["x86", "x86_64"] : ["x86_64"]
- qbs.buildVariants: ["debug", "profile"]
+ qbs.architectures: Helpers.getArchitectures(qbs, project.xcodeVersion)
+ qbs.buildVariants: ["debug", "profiling"]
install: true
+ installDebugInformation: false
installDir: ""
}
@@ -138,11 +161,13 @@ Project {
Depends { name: "multilibB" }
name: "multilibA"
files: ["lib.c"]
+ cpp.minimumIosVersion: "8.0"
cpp.sonamePrefix: "@rpath"
cpp.defines: ["VARIANT=" + Utilities.cStringQuote(qbs.buildVariant)]
- qbs.architectures: project.enableX86 ? ["x86", "x86_64"] : ["x86_64"]
- qbs.buildVariants: ["debug", "profile"]
+ qbs.architectures: Helpers.getArchitectures(qbs, project.xcodeVersion)
+ qbs.buildVariants: ["debug", "profiling"]
install: true
+ installDebugInformation: false
installDir: ""
}
DynamicLibrary {
@@ -150,11 +175,13 @@ Project {
Depends { name: "bundle" }
name: "multilibB"
files: ["lib.c"]
+ cpp.minimumIosVersion: "8.0"
cpp.sonamePrefix: "@rpath"
cpp.defines: ["VARIANT=" + Utilities.cStringQuote(qbs.buildVariant)]
- qbs.architectures: project.enableX86 ? ["x86", "x86_64"] : ["x86_64"]
- qbs.buildVariants: ["debug", "profile"]
+ qbs.architectures: Helpers.getArchitectures(qbs, project.xcodeVersion)
+ qbs.buildVariants: ["debug", "profiling"]
install: true
+ installDebugInformation: false
installDir: ""
}
}
diff --git a/tests/auto/blackbox/testdata-apple/bundle-structure/bundle-structure.qbs b/tests/auto/blackbox/testdata-apple/bundle-structure/bundle-structure.qbs
index 8cf031e33..33ac58967 100644
--- a/tests/auto/blackbox/testdata-apple/bundle-structure/bundle-structure.qbs
+++ b/tests/auto/blackbox/testdata-apple/bundle-structure/bundle-structure.qbs
@@ -3,12 +3,21 @@ Project {
property stringList buildableProducts: ["A", "B", "C", "D", "E", "F", "G"]
+ Product {
+ Depends { name: "bundle" }
+ condition: {
+ console.info("bundle.isShallow: " + bundle.isShallow);
+ console.info("qbs.targetOS: " + qbs.targetOS);
+ return false;
+ }
+ }
+
Application {
Depends { name: "cpp" }
Depends { name: "B" }
Depends { name: "C" }
Depends { name: "D" }
- condition: buildableProducts.contains("A")
+ condition: buildableProducts.includes("A")
name: "A"
bundle.isBundle: true
bundle.publicHeaders: ["dummy.h"]
@@ -24,7 +33,7 @@ Project {
Depends { name: "B" }
Depends { name: "C" }
Depends { name: "D" }
- condition: buildableProducts.contains("ABadApple")
+ condition: buildableProducts.includes("ABadApple")
name: "ABadApple"
bundle._productTypeIdentifier: "com.apple.product-type.will.never.exist.ever.guaranteed"
bundle.isBundle: true
@@ -41,7 +50,7 @@ Project {
Depends { name: "B" }
Depends { name: "C" }
Depends { name: "D" }
- condition: buildableProducts.contains("ABadThirdParty")
+ condition: buildableProducts.includes("ABadThirdParty")
name: "ABadThirdParty"
bundle._productTypeIdentifier: "org.special.third.party.non.existent.product.type"
bundle.isBundle: true
@@ -94,7 +103,7 @@ Project {
ApplicationExtension {
Depends { name: "cpp" }
- condition: buildableProducts.contains("E")
+ condition: buildableProducts.includes("E")
name: "E"
bundle.isBundle: true
bundle.publicHeaders: ["dummy.h"]
@@ -107,7 +116,7 @@ Project {
XPCService {
Depends { name: "cpp" }
- condition: buildableProducts.contains("F")
+ condition: buildableProducts.includes("F")
name: "F"
bundle.isBundle: true
bundle.publicHeaders: ["dummy.h"]
@@ -120,11 +129,14 @@ Project {
Product {
Depends { name: "bundle" }
- condition: buildableProducts.contains("G")
+ condition: buildableProducts.includes("G")
type: ["inapppurchase"]
name: "G"
bundle.isBundle: true
bundle.resources: ["resource.txt"]
+ // XCode 12.5 does not support com.apple.product-type.in-app-purchase-content type anymore,
+ // so use older specs from Qbs
+ bundle._useXcodeBuildSpecs: false
Group {
fileTagsFilter: product.type.concat(project.bundleFileTags)
qbs.install: true
diff --git a/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/ByteArray-Info.plist b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/ByteArray-Info.plist
new file mode 100644
index 000000000..df0429f25
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/ByteArray-Info.plist
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>DataKey</key>
+ <!--The data value-->
+ <data>VGhlIGRhdGEgdmFsdWU=</data>
+ <key>StringKey</key>
+ <string>The string value</string>
+</dict>
+</plist>
diff --git a/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/byteArrayInfoPlist.qbs b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/byteArrayInfoPlist.qbs
new file mode 100644
index 000000000..4df0886ff
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/byteArrayInfoPlist.qbs
@@ -0,0 +1,37 @@
+import qbs.BundleTools
+import qbs.TextFile
+
+CppApplication {
+ Depends { name: "bundle" }
+ cpp.minimumMacosVersion: "10.7"
+ files: ["main.c", "ByteArray-Info.plist"]
+ type: base.concat(["txt_output"])
+
+ Properties {
+ condition: qbs.targetOS.includes("darwin")
+ bundle.isBundle: true
+ bundle.identifierPrefix: "com.test"
+ }
+
+ Rule {
+ inputs: ["aggregate_infoplist"]
+ Artifact {
+ filePath: input.fileName + ".out"
+ fileTags: ["txt_output"]
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating" + output.fileName + " from " + input.fileName;
+ cmd.highlight = "codegen";
+ cmd.sourceCode = function() {
+ var plist = new BundleTools.infoPlistContents(input.filePath);
+ var content = plist["DataKey"];
+ var int8view = new Uint8Array(content);
+ file = new TextFile(output.filePath, TextFile.WriteOnly);
+ file.write(String.fromCharCode.apply(null, int8view));
+ file.close();
+ }
+ return [cmd];
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/innosetupDependencies/main.c b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/main.c
index 76e819701..76e819701 100644
--- a/tests/auto/blackbox/testdata/innosetupDependencies/main.c
+++ b/tests/auto/blackbox/testdata-apple/byteArrayInfoPlist/main.c
diff --git a/tests/auto/blackbox/testdata/path-probe/main.cpp b/tests/auto/blackbox/testdata-apple/codesign/app.cpp
index 76e819701..76e819701 100644
--- a/tests/auto/blackbox/testdata/path-probe/main.cpp
+++ b/tests/auto/blackbox/testdata-apple/codesign/app.cpp
diff --git a/tests/auto/blackbox/testdata-apple/codesign/codesign.qbs b/tests/auto/blackbox/testdata-apple/codesign/codesign.qbs
new file mode 100644
index 000000000..c1fc0502a
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/codesign/codesign.qbs
@@ -0,0 +1,57 @@
+import "../multiarch-helpers.js" as Helpers
+
+Project {
+ name: "p"
+ // we do not have the access to xcode version in qbs.architectures so we need to pass it here
+ property string xcodeVersion
+
+ property bool isBundle: true
+ property bool enableSigning: true
+ property bool multiArch: false
+ property bool multiVariant: false
+
+ CppApplication {
+ name: "A"
+ version: "1.0.0"
+ bundle.isBundle: project.isBundle
+ files: "app.cpp"
+ codesign.enableCodeSigning: project.enableSigning
+ codesign.signingType: "ad-hoc"
+ install: true
+ installDir: ""
+
+ qbs.architectures:
+ multiArch ? Helpers.getArchitectures(qbs, project.xcodeVersion) : []
+ qbs.buildVariants: project.multiVariant ? ["debug", "release"] : []
+ }
+
+ DynamicLibrary {
+ Depends { name: "cpp" }
+ name: "B"
+ version: "1.0.0"
+ bundle.isBundle: project.isBundle
+ files: "app.cpp"
+ codesign.enableCodeSigning: project.enableSigning
+ codesign.signingType: "ad-hoc"
+ install: true
+ installDir: ""
+ qbs.architectures:
+ multiArch ? Helpers.getArchitectures(qbs, project.xcodeVersion) : []
+ qbs.buildVariants: project.multiVariant ? ["debug", "release"] : []
+ }
+
+ LoadableModule {
+ Depends { name: "cpp" }
+ name: "C"
+ version: "1.0.0"
+ bundle.isBundle: project.isBundle
+ files: "app.cpp"
+ codesign.enableCodeSigning: project.enableSigning
+ codesign.signingType: "ad-hoc"
+ install: true
+ installDir: ""
+ qbs.architectures:
+ multiArch ? Helpers.getArchitectures(qbs, project.xcodeVersion) : []
+ qbs.buildVariants: project.multiVariant ? ["debug", "release"] : []
+ }
+}
diff --git a/tests/auto/blackbox/testdata-apple/deploymentTarget/deployment.qbs b/tests/auto/blackbox/testdata-apple/deploymentTarget/deployment.qbs
index 9eff57b60..2179e9c84 100644
--- a/tests/auto/blackbox/testdata-apple/deploymentTarget/deployment.qbs
+++ b/tests/auto/blackbox/testdata-apple/deploymentTarget/deployment.qbs
@@ -5,7 +5,7 @@ CppApplication {
// - will actually link (as of Xcode 8.1)
// - exist for the given architecture(s)
cpp.minimumMacosVersion: qbs.architecture === "x86_64h" ? "10.12" : "10.6"
- cpp.minimumIosVersion: ["armv7s", "arm64", "x86_64"].contains(qbs.architecture) ? "7.0" : "6.0"
+ cpp.minimumIosVersion: ["armv7s", "arm64", "x86_64"].includes(qbs.architecture) ? "7.0" : "6.0"
cpp.minimumTvosVersion: "9.0"
cpp.minimumWatchosVersion: "2.0"
diff --git a/tests/auto/blackbox/testdata-apple/frameworkStructure/frameworkStructure.qbs b/tests/auto/blackbox/testdata-apple/frameworkStructure/frameworkStructure.qbs
index a812ae513..3c6d39332 100644
--- a/tests/auto/blackbox/testdata-apple/frameworkStructure/frameworkStructure.qbs
+++ b/tests/auto/blackbox/testdata-apple/frameworkStructure/frameworkStructure.qbs
@@ -2,6 +2,12 @@ Project {
property bool includeHeaders: true
Library {
Depends { name: "cpp" }
+ Depends { name: "bundle" }
+
+ property bool isShallow: {
+ console.info("isShallow: " + bundle.isShallow);
+ return bundle.isShallow;
+ }
name: "Widget"
bundle.isBundle: true
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/100.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/100.png
new file mode 100644
index 000000000..98f8eaeb7
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/100.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/114.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/114.png
new file mode 100644
index 000000000..a14be545e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/114.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/120.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/120.png
new file mode 100644
index 000000000..b910f96df
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/120.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/128.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/128.png
new file mode 100644
index 000000000..7cf0327dc
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/128.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/144.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/144.png
new file mode 100644
index 000000000..c7f18cda1
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/144.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/152.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/152.png
new file mode 100644
index 000000000..af411d7e3
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/152.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/16.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/16.png
new file mode 100644
index 000000000..be3743a22
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/16.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/167.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/167.png
new file mode 100644
index 000000000..855952774
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/167.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/172.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/172.png
new file mode 100644
index 000000000..a410f8bb2
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/172.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/180.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/180.png
new file mode 100644
index 000000000..fcef493f4
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/180.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/196.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/196.png
new file mode 100644
index 000000000..c974bf14a
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/196.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/20.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/20.png
new file mode 100644
index 000000000..25897fc42
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/20.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/216.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/216.png
new file mode 100644
index 000000000..ea0703112
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/216.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/256.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/256.png
new file mode 100644
index 000000000..f1a3ef352
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/256.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/29.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/29.png
new file mode 100644
index 000000000..1f9ef0678
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/29.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/32.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/32.png
new file mode 100644
index 000000000..d969ed1df
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/32.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/40.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/40.png
new file mode 100644
index 000000000..1f76c0b09
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/40.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/48.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/48.png
new file mode 100644
index 000000000..5279cb807
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/48.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/50.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/50.png
new file mode 100644
index 000000000..8045e514d
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/50.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/512.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/512.png
new file mode 100644
index 000000000..c8f3cb758
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/512.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/55.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/55.png
new file mode 100644
index 000000000..c3d8811f6
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/55.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/57.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/57.png
new file mode 100644
index 000000000..5abf4b377
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/57.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/58.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/58.png
new file mode 100644
index 000000000..fc8cf5df5
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/58.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/60.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/60.png
new file mode 100644
index 000000000..ddc66846c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/60.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/64.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/64.png
new file mode 100644
index 000000000..ad87d2af1
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/64.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/72.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/72.png
new file mode 100644
index 000000000..b78c47708
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/72.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/76.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/76.png
new file mode 100644
index 000000000..50bab502d
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/76.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/80.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/80.png
new file mode 100644
index 000000000..6a3a31fa6
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/80.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/87.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/87.png
new file mode 100644
index 000000000..13fb01fef
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/87.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/88.png b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/88.png
new file mode 100644
index 000000000..8eb955ca8
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/88.png
Binary files differ
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/Contents.json b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 000000000..87ae131b9
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/AppIconSet.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,299 @@
+{
+ "images" : [
+ {
+ "filename" : "40.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "60.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "29.png",
+ "idiom" : "iphone",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "58.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "87.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "80.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "120.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "57.png",
+ "idiom" : "iphone",
+ "scale" : "1x",
+ "size" : "57x57"
+ },
+ {
+ "filename" : "114.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "57x57"
+ },
+ {
+ "filename" : "120.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "filename" : "180.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "filename" : "20.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "40.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "29.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "58.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "40.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "80.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "50.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "50x50"
+ },
+ {
+ "filename" : "100.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "50x50"
+ },
+ {
+ "filename" : "72.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "72x72"
+ },
+ {
+ "filename" : "144.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "72x72"
+ },
+ {
+ "filename" : "76.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "filename" : "152.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "filename" : "167.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ },
+ {
+ "filename" : "48.png",
+ "idiom" : "watch",
+ "role" : "notificationCenter",
+ "scale" : "2x",
+ "size" : "24x24",
+ "subtype" : "38mm"
+ },
+ {
+ "filename" : "55.png",
+ "idiom" : "watch",
+ "role" : "notificationCenter",
+ "scale" : "2x",
+ "size" : "27.5x27.5",
+ "subtype" : "42mm"
+ },
+ {
+ "filename" : "58.png",
+ "idiom" : "watch",
+ "role" : "companionSettings",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "87.png",
+ "idiom" : "watch",
+ "role" : "companionSettings",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "80.png",
+ "idiom" : "watch",
+ "role" : "appLauncher",
+ "scale" : "2x",
+ "size" : "40x40",
+ "subtype" : "38mm"
+ },
+ {
+ "filename" : "88.png",
+ "idiom" : "watch",
+ "role" : "appLauncher",
+ "scale" : "2x",
+ "size" : "44x44",
+ "subtype" : "40mm"
+ },
+ {
+ "filename" : "100.png",
+ "idiom" : "watch",
+ "role" : "appLauncher",
+ "scale" : "2x",
+ "size" : "50x50",
+ "subtype" : "44mm"
+ },
+ {
+ "filename" : "172.png",
+ "idiom" : "watch",
+ "role" : "quickLook",
+ "scale" : "2x",
+ "size" : "86x86",
+ "subtype" : "38mm"
+ },
+ {
+ "filename" : "196.png",
+ "idiom" : "watch",
+ "role" : "quickLook",
+ "scale" : "2x",
+ "size" : "98x98",
+ "subtype" : "42mm"
+ },
+ {
+ "filename" : "216.png",
+ "idiom" : "watch",
+ "role" : "quickLook",
+ "scale" : "2x",
+ "size" : "108x108",
+ "subtype" : "44mm"
+ },
+ {
+ "idiom" : "watch-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ },
+ {
+ "filename" : "16.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "16x16"
+ },
+ {
+ "filename" : "32.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "16x16"
+ },
+ {
+ "filename" : "32.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "32x32"
+ },
+ {
+ "filename" : "64.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "32x32"
+ },
+ {
+ "filename" : "128.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "128x128"
+ },
+ {
+ "filename" : "256.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "128x128"
+ },
+ {
+ "filename" : "256.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "256x256"
+ },
+ {
+ "filename" : "512.png",
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "256x256"
+ },
+ {
+ "filename" : "512.png",
+ "idiom" : "mac",
+ "scale" : "1x",
+ "size" : "512x512"
+ },
+ {
+ "idiom" : "mac",
+ "scale" : "2x",
+ "size" : "512x512"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/tests/auto/blackbox/testdata-apple/ib/appiconset/appiconset.qbs b/tests/auto/blackbox/testdata-apple/ib/appiconset/appiconset.qbs
new file mode 100644
index 000000000..2015032ea
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/appiconset.qbs
@@ -0,0 +1,10 @@
+CppApplication {
+ Depends { name: "ib" }
+ Depends { name: "bundle" }
+ property bool isShallow: {
+ console.info("bundle.isShallow: " + bundle.isShallow);
+ return bundle.isShallow;
+ }
+ files: ["main.c", "AppIconSet.xcassets"]
+ ib.appIconName: "AppIcon"
+}
diff --git a/tests/auto/blackbox/testdata/wixDependencies/main.c b/tests/auto/blackbox/testdata-apple/ib/appiconset/main.c
index 76e819701..76e819701 100644
--- a/tests/auto/blackbox/testdata/wixDependencies/main.c
+++ b/tests/auto/blackbox/testdata-apple/ib/appiconset/main.c
diff --git a/tests/auto/blackbox/testdata-apple/ib/assetcatalog/assetcatalogempty.qbs b/tests/auto/blackbox/testdata-apple/ib/assetcatalog/assetcatalogempty.qbs
index 622fa46cb..d06e24eb3 100644
--- a/tests/auto/blackbox/testdata-apple/ib/assetcatalog/assetcatalogempty.qbs
+++ b/tests/auto/blackbox/testdata-apple/ib/assetcatalog/assetcatalogempty.qbs
@@ -1,6 +1,13 @@
+import qbs.Host
import qbs.Utilities
Project {
+ condition: {
+ var result = qbs.targetOS.includes("macos");
+ if (!result)
+ console.info("Skip this test");
+ return result;
+ }
property bool includeIconset
CppApplication {
@@ -11,7 +18,8 @@ Project {
filez.push("empty.xcassets/empty.iconset");
else if (Utilities.versionCompare(xcode.version, "5") >= 0)
filez.push("empty.xcassets");
- if (qbs.hostOSVersionMinor >= 10 // need macOS 10.10 to build SBs
+ if ((Host.osVersionMajor() >= 11
+ || Host.osVersionMinor() >= 10) // need macOS 10.10 or higher to build SBs
&& cpp.minimumMacosVersion !== undefined
&& Utilities.versionCompare(cpp.minimumMacosVersion, "10.10") >= 0)
filez.push("Storyboard.storyboard");
diff --git a/tests/auto/blackbox/testdata-apple/ib/iconsetapp/iconsetapp.qbs b/tests/auto/blackbox/testdata-apple/ib/iconsetapp/iconsetapp.qbs
index 04e6ce0ce..dd39d1639 100644
--- a/tests/auto/blackbox/testdata-apple/ib/iconsetapp/iconsetapp.qbs
+++ b/tests/auto/blackbox/testdata-apple/ib/iconsetapp/iconsetapp.qbs
@@ -1,4 +1,9 @@
CppApplication {
Depends { name: "ib" }
+ Depends { name: "bundle" }
+ property bool isShallow: {
+ console.info("isShallow: " + bundle.isShallow);
+ return bundle.isShallow;
+ }
files: ["main.c", "white.iconset"]
}
diff --git a/tests/auto/blackbox/testdata-apple/infoPlistVariables/Info.plist b/tests/auto/blackbox/testdata-apple/infoPlistVariables/Info.plist
new file mode 100644
index 000000000..cb879d70c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/infoPlistVariables/Info.plist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>Curly</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>Braces</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>At</key>
+ <string>@EXECUTABLE_NAME@</string>
+ <key>CurlyMult</key>
+ <string>${EXECUTABLE_NAME}_${PRODUCT_NAME}</string>
+ <key>BracesMult</key>
+ <string>$(EXECUTABLE_NAME)_$(PRODUCT_NAME)</string>
+ <key>AtMult</key>
+ <string>@EXECUTABLE_NAME@_@PRODUCT_NAME@</string>
+ <key>CurlyNested</key>
+ <string>${${EXE}_NAME}</string>
+ <key>BracesNested</key>
+ <string>${${EXE}_NAME}</string>
+ <key>WithDefault</key>
+ <string>${NON_EXISTING:default=DEFAULT}</string>
+</dict>
+</plist>
diff --git a/tests/auto/blackbox/testdata-apple/infoPlistVariables/infoPlistVariables.qbs b/tests/auto/blackbox/testdata-apple/infoPlistVariables/infoPlistVariables.qbs
new file mode 100644
index 000000000..47ca80f07
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/infoPlistVariables/infoPlistVariables.qbs
@@ -0,0 +1,16 @@
+CppApplication {
+ Depends { name: "bundle" }
+ cpp.minimumMacosVersion: "10.7"
+ files: ["main.c", "Info.plist"]
+
+ Properties {
+ condition: qbs.targetOS.includes("darwin")
+ bundle.isBundle: true
+ bundle.identifierPrefix: "com.test"
+ bundle.extraEnv: {
+ var result = original;
+ result["EXE"] = "EXECUTABLE";
+ return result;
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-apple/infoPlistVariables/main.c b/tests/auto/blackbox/testdata-apple/infoPlistVariables/main.c
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/infoPlistVariables/main.c
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/blackbox/testdata-apple/multiarch-helpers.js b/tests/auto/blackbox/testdata-apple/multiarch-helpers.js
new file mode 100644
index 000000000..a8054b63c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/multiarch-helpers.js
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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 Utilities = require("qbs.Utilities");
+
+// Typically, multiple architectures are used for migration from "old" arch to a "new" one
+// For example: x86 -> x86_64 on macOS, armv7 -> arm64 on iOS
+
+function enableOldArch(qbs, xcodeVersion) {
+ return qbs.targetOS.includes("macos")
+ && xcodeVersion
+ && (Utilities.versionCompare(xcodeVersion, "10") < 0
+ || Utilities.versionCompare(xcodeVersion, "12.2") >= 0)
+ || qbs.targetOS.includes("ios")
+}
+
+function getNewArch(qbs, xcodeVersion) {
+ if (qbs.targetOS.includes("macos"))
+ return xcodeVersion
+ && Utilities.versionCompare(xcodeVersion, "12.2") >= 0 ? "arm64" : "x86_64";
+ else if (qbs.targetOS.includes("ios-simulator"))
+ return "x86_64"
+ else if (qbs.targetOS.includes("ios"))
+ return "arm64"
+ else if (qbs.targetOS.includes("tvos"))
+ return "arm64"
+ else if (qbs.targetOS.includes("watchos"))
+ return "armv7k"
+ throw "unsupported targetOS: " + qbs.targetOS;
+}
+
+function getOldArch(qbs, xcodeVersion) {
+ if (qbs.targetOS.includes("macos"))
+ return xcodeVersion
+ && Utilities.versionCompare(xcodeVersion, "12.2") >= 0 ? "x86_64" : "x86";
+ else if (qbs.targetOS.includes("ios-simulator"))
+ return "x86"
+ else if (qbs.targetOS.includes("ios"))
+ return "armv7a"
+ throw "unsupported targetOS: " + qbs.targetOS;
+}
+
+function getArchitectures(qbs, xcodeVersion) {
+ return enableOldArch(qbs, xcodeVersion)
+ ? [getOldArch(qbs, xcodeVersion), getNewArch(qbs, xcodeVersion)]
+ : [getNewArch(qbs, xcodeVersion)];
+}
diff --git a/tests/auto/blackbox/testdata-apple/objc-arc/objc-arc.qbs b/tests/auto/blackbox/testdata-apple/objc-arc/objc-arc.qbs
index 545d5701c..ce128059f 100644
--- a/tests/auto/blackbox/testdata-apple/objc-arc/objc-arc.qbs
+++ b/tests/auto/blackbox/testdata-apple/objc-arc/objc-arc.qbs
@@ -2,7 +2,7 @@ Product {
Depends { name: "cpp" }
consoleApplication: true
type: ["application"]
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
Group {
cpp.automaticReferenceCounting: true
diff --git a/tests/auto/blackbox/testdata-apple/overrideInfoPlist/Override-Info.plist b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/Override-Info.plist
new file mode 100644
index 000000000..f2621e983
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/Override-Info.plist
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>DefaultValue</key>
+ <string>The default value</string>
+ <key>OverriddenValue</key>
+ <string>The default value</string>
+</dict>
+</plist>
diff --git a/tests/auto/blackbox/testdata-apple/overrideInfoPlist/main.c b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/main.c
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/main.c
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/blackbox/testdata-apple/overrideInfoPlist/overrideInfoPlist.qbs b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/overrideInfoPlist.qbs
new file mode 100644
index 000000000..270a0792c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-apple/overrideInfoPlist/overrideInfoPlist.qbs
@@ -0,0 +1,16 @@
+CppApplication {
+ Depends { name: "bundle" }
+ cpp.minimumMacosVersion: "10.7"
+ files: ["main.c", "Override-Info.plist"]
+
+ Properties {
+ condition: qbs.targetOS.includes("darwin")
+ bundle.isBundle: true
+ bundle.identifierPrefix: "com.test"
+
+ bundle.infoPlist: ({
+ "CFBundleName": "My Bundle",
+ "OverriddenValue": "The overridden value",
+ })
+ }
+}
diff --git a/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs b/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs
index fbab6d0b1..e7bf4739a 100644
--- a/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs
+++ b/tests/auto/blackbox/testdata-apple/xcode/xcode-project.qbs
@@ -16,19 +16,19 @@ Project {
console.info("Available SDK versions: " + xcode.availableSdkVersions.join(", "));
var targetOsToKey = function(targetOS) {
- if (targetOS.contains("ios"))
+ if (targetOS.includes("ios"))
return "iphoneos";
- if (targetOS.contains("ios-simulator"))
+ if (targetOS.includes("ios-simulator"))
return "iphonesimulator";
- if (targetOS.contains("macos"))
+ if (targetOS.includes("macos"))
return "macosx";
- if (targetOS.contains("tvos"))
+ if (targetOS.includes("tvos"))
return "appletvos";
- if (targetOS.contains("tvos-simulator"))
+ if (targetOS.includes("tvos-simulator"))
return "appletvsimulator";
- if (targetOS.contains("watchos"))
+ if (targetOS.includes("watchos"))
return "watchos";
- if (targetOS.contains("watchos-simulator"))
+ if (targetOS.includes("watchos-simulator"))
return "watchossimulator";
throw "Unsupported OS" + targetOS;
}
@@ -43,8 +43,11 @@ Project {
}
for (var i = 0; i < a.length; ++i) {
- if (a[i] !== b[i]) {
- throw msg;
+ var version1 = a[i].split('.');
+ var version2 = b[i].split('.');
+ for (var j = 0; j < version1.length; ++j) {
+ if (version1[j] !== version2[j])
+ throw msg;
}
}
}
diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs
new file mode 100644
index 000000000..7a35e4e13
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/BareMetalApplication.qbs
@@ -0,0 +1,29 @@
+import qbs.Host
+
+BareMetalProduct {
+ type: "application"
+ consoleApplication: true
+
+ property bool dummy: {
+ if (qbs.targetPlatform !== Host.platform()
+ || qbs.architecture !== Host.architecture()) {
+
+ function supportsCrossRun() {
+ // We can run 32 bit applications on 64 bit Windows.
+ if (Host.platform() === "windows" && Host.architecture() === "x86_64"
+ && qbs.targetPlatform === "windows" && qbs.architecture === "x86") {
+ return true;
+ }
+ }
+
+ if (!supportsCrossRun())
+ console.info("targetPlatform differs from hostPlatform")
+ }
+ }
+
+ Group {
+ condition: qbs.toolchain.includes("cosmic")
+ files: "cosmic.lkf"
+ fileTags: "linkerscript"
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalProduct.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalProduct.qbs
new file mode 100644
index 000000000..446cfe086
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/BareMetalProduct.qbs
@@ -0,0 +1,158 @@
+Product {
+ Depends { name: "cpp" }
+ cpp.positionIndependentCode: false
+ Properties {
+ condition: qbs.toolchain.includes("iar")
+ && qbs.architecture === "stm8"
+ cpp.driverLinkerFlags: [
+ "--config_def", "_CSTACK_SIZE=0x100",
+ "--config_def", "_HEAP_SIZE=0x100",
+ ]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("iar")
+ && qbs.architecture === "rl78"
+ cpp.driverLinkerFlags: [
+ "--config_def", "_NEAR_HEAP_SIZE=256",
+ "--config_def", "_FAR_HEAP_SIZE=4096",
+ "--config_def", "_HUGE_HEAP_SIZE=0",
+ "--config_def", "_STACK_SIZE=128",
+ "--config_def", "_NEAR_CONST_LOCATION_SIZE=0x6F00",
+ "--config_def", "_NEAR_CONST_LOCATION_START=0x3000",
+ "--define_symbol", "_NEAR_CONST_LOCATION=0",
+ "--config", cpp.toolchainInstallPath + "/../config/lnkrl78_s3.icf"
+ ]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("iar")
+ && qbs.architecture === "rh850"
+ cpp.driverLinkerFlags: [
+ "--config_def", "CSTACK_SIZE=0x1000",
+ "--config_def", "HEAP_SIZE=0x1000",
+ "--config", cpp.toolchainInstallPath + "/../config/lnkrh850_g3m.icf"
+ ]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("iar")
+ && qbs.architecture === "v850"
+ cpp.driverLinkerFlags: [
+ "-D_CSTACK_SIZE=1000",
+ "-D_HEAP_SIZE=1000",
+ "-f", cpp.toolchainInstallPath + "/../config/lnk85.xcl"
+ ]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("iar")
+ && qbs.architecture === "78k"
+ cpp.commonCompilerFlags: [
+ "--core", "78k0",
+ "--code_model", "standard"
+ ]
+ cpp.driverLinkerFlags: [
+ "-D_CSTACK_SIZE=80",
+ "-D_HEAP_SIZE=200",
+ "-D_CODEBANK_START=0",
+ "-D_CODEBANK_END=0",
+ "-D_CODEBANK_BANKS=0",
+ "-f", cpp.toolchainInstallPath + "/../config/lnk.xcl",
+ cpp.toolchainInstallPath + "/../lib/clib/cl78ks1.r26"
+ ]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("iar")
+ && qbs.architecture === "sh"
+ cpp.driverLinkerFlags: [
+ "--config_def", "_CSTACK_SIZE=0x800",
+ "--config_def", "_HEAP_SIZE=0x800",
+ "--config_def", "_INT_TABLE=0x10",
+ "--config", cpp.toolchainInstallPath + "/../config/generic.icf"
+ ]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("iar")
+ && qbs.architecture === "hcs8"
+ cpp.driverLinkerFlags: [
+ "-D_CSTACK_SIZE=200",
+ "-D_HEAP_SIZE=200",
+ "-f", cpp.toolchainInstallPath + "/../config/lnkunspecifieds08.xcl"
+ ]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("iar")
+ && qbs.architecture === "m32c"
+ cpp.driverLinkerFlags: [
+ "-D_CSTACK_SIZE=100",
+ "-D_NEAR_HEAP_SIZE=400",
+ "-D_FAR_HEAP_SIZE=400",
+ "-D_HUGE_HEAP_SIZE=400",
+ "-D_ISTACK_SIZE=40",
+ "-f", cpp.toolchainInstallPath + "/../config/lnkm32c.xcl",
+ cpp.toolchainInstallPath + (qbs.debugInformation ? "/../lib/dlib/dlm32cnf.r48" : "/../lib/clib/clm32cf.r48")
+ ]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("iar")
+ && qbs.architecture === "riscv"
+ cpp.driverLinkerFlags: [
+ "--config_def", "CSTACK_SIZE=0x1000",
+ "--config_def", "HEAP_SIZE=0x1000"
+ ]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("iar")
+ && qbs.architecture === "m68k"
+ cpp.cFlags: [
+ "--no_div"
+ ]
+ cpp.driverLinkerFlags: [
+ "-D__FLASHBEGIN=0",
+ "-D__FLASHEND=1FFFF",
+ "-D__RAMBEGIN=800000",
+ "-D__RAMEND=803FFF",
+ "-D_CSTACK_SIZE=200",
+ "-D_HEAP_SIZE=1000",
+ "-D_VBR_ADDRESS=0",
+ "-f", cpp.toolchainInstallPath + "/../config/lnkm51ac128.xcl",
+ cpp.toolchainInstallPath + "/../lib/dlcfcffdn.r68"
+ ]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("keil")
+ && qbs.architecture.startsWith("arm")
+ && cpp.compilerName.startsWith("armcc")
+ cpp.assemblerFlags: ["--cpu", "cortex-m0"]
+ cpp.commonCompilerFlags: ["--cpu", "cortex-m0"]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("keil")
+ && qbs.architecture.startsWith("arm")
+ && cpp.compilerName.startsWith("armclang")
+ cpp.assemblerFlags: ["--cpu", "cortex-m0"]
+ cpp.commonCompilerFlags: ["-mcpu=cortex-m0", "--target=arm-arm-none-eabi"]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("gcc")
+ && qbs.architecture.startsWith("arm")
+ cpp.driverFlags: ["-specs=nosys.specs"]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("gcc")
+ && qbs.architecture === "xtensa"
+ cpp.driverFlags: ["-nostdlib"]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("gcc")
+ && qbs.architecture === "msp430"
+ cpp.driverFlags: ["-mmcu=msp430f5529", "-nostdlib"]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("gcc")
+ && qbs.architecture === "m32r"
+ cpp.driverFlags: ["-nostdlib"]
+ }
+ Properties {
+ condition: qbs.toolchain.includes("gcc")
+ && qbs.architecture === "riscv"
+ cpp.driverFlags: ["-nostdlib"]
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs b/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs
new file mode 100644
index 000000000..7259b1446
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/BareMetalStaticLibrary.qbs
@@ -0,0 +1,3 @@
+BareMetalProduct {
+ type: "staticlibrary"
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/app.c b/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/app.c
new file mode 100644
index 000000000..58fe69254
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/app.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/compiler-defines-by-language.qbs b/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/compiler-defines-by-language.qbs
new file mode 100644
index 000000000..73365ada4
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/compiler-defines-by-language.qbs
@@ -0,0 +1,73 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+Project {
+ property bool supportsCpp: {
+ if (qbs.toolchain.includes("cosmic"))
+ return false;
+ if (qbs.toolchain.includes("sdcc"))
+ return false;
+ if (qbs.toolchain.includes("keil")) {
+ if (qbs.architecture === "mcs51"
+ || qbs.architecture === "mcs251"
+ || qbs.architecture === "c166") {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ BareMetalApplication {
+ name: "c_language"
+ files: ["app.c", "ctest.c"]
+ cpp.enableCompilerDefinesByLanguage: []
+ property var foo: {
+ if (!cpp.compilerDefinesByLanguage)
+ throw "ASSERT cpp.compilerDefinesByLanguage: "
+ + cpp.compilerDefinesByLanguage;
+ if (!cpp.compilerDefinesByLanguage["c"])
+ throw "ASSERT cpp.compilerDefinesByLanguage[\"c\"]: "
+ + cpp.compilerDefinesByLanguage["c"];
+ if (cpp.compilerDefinesByLanguage["cpp"])
+ throw "ASSERT !cpp.compilerDefinesByLanguage[\"cpp\"]: "
+ + cpp.compilerDefinesByLanguage["cpp"];
+ }
+ }
+
+ BareMetalApplication {
+ condition: supportsCpp
+ name: "cpp_language"
+ files: ["app.c", "cpptest.cpp"]
+ cpp.enableCompilerDefinesByLanguage: ["cpp"]
+ cpp.enableExceptions: false
+ property var foo: {
+ if (!cpp.compilerDefinesByLanguage)
+ throw "ASSERT cpp.compilerDefinesByLanguage: "
+ + cpp.compilerDefinesByLanguage;
+ if (cpp.compilerDefinesByLanguage["c"])
+ throw "ASSERT !cpp.compilerDefinesByLanguage[\"c\"]: "
+ + cpp.compilerDefinesByLanguage["c"];
+ if (!cpp.compilerDefinesByLanguage["cpp"])
+ throw "ASSERT cpp.compilerDefinesByLanguage[\"cpp\"]: "
+ + cpp.compilerDefinesByLanguage["cpp"];
+ }
+ }
+
+ BareMetalApplication {
+ condition: supportsCpp
+ name: "c_and_cpp_language"
+ files: ["app.c", "ctest.c", "cpptest.cpp"]
+ cpp.enableCompilerDefinesByLanguage: ["c", "cpp"]
+ cpp.enableExceptions: false
+ property var foo: {
+ if (!cpp.compilerDefinesByLanguage)
+ throw "ASSERT cpp.compilerDefinesByLanguage: "
+ + cpp.compilerDefinesByLanguage;
+ if (!cpp.compilerDefinesByLanguage["c"])
+ throw "ASSERT cpp.compilerDefinesByLanguage[\"c\"]: "
+ + cpp.compilerDefinesByLanguage["c"];
+ if (!cpp.compilerDefinesByLanguage["cpp"])
+ throw "ASSERT cpp.compilerDefinesByLanguage[\"cpp\"]: "
+ + cpp.compilerDefinesByLanguage["cpp"];
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/cpptest.cpp b/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/cpptest.cpp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/cpptest.cpp
diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/ctest.c b/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/ctest.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/compiler-defines-by-language/ctest.c
diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-include-paths/compiler-include-paths.qbs b/tests/auto/blackbox/testdata-baremetal/compiler-include-paths/compiler-include-paths.qbs
new file mode 100644
index 000000000..5c73302ad
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/compiler-include-paths/compiler-include-paths.qbs
@@ -0,0 +1,9 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ files: ["main.c"]
+ property bool dummy: {
+ console.info("compilerIncludePaths: %%" + cpp.compilerIncludePaths + "%%");
+ return true;
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-include-paths/main.c b/tests/auto/blackbox/testdata-baremetal/compiler-include-paths/main.c
new file mode 100644
index 000000000..58fe69254
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/compiler-include-paths/main.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs b/tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs
new file mode 100644
index 000000000..6bd51e3bd
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/compiler-listing/compiler-listing.qbs
@@ -0,0 +1,14 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ condition: {
+ if (!qbs.toolchain.includes("gcc")) {
+ console.info("compiler listing suffix: %%" + cpp.compilerListingSuffix + "%%");
+ return true;
+ }
+ console.info("unsupported toolset: %%"
+ + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%");
+ return false;
+ }
+ files: ["main.c", "fun.c"]
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-listing/fun.c b/tests/auto/blackbox/testdata-baremetal/compiler-listing/fun.c
new file mode 100644
index 000000000..3b8c8f2f4
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/compiler-listing/fun.c
@@ -0,0 +1,4 @@
+int f(void)
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/compiler-listing/main.c b/tests/auto/blackbox/testdata-baremetal/compiler-listing/main.c
new file mode 100644
index 000000000..2c3d7726c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/compiler-listing/main.c
@@ -0,0 +1,6 @@
+extern int f(void);
+
+int main(void)
+{
+ return f();
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/cosmic.lkf b/tests/auto/blackbox/testdata-baremetal/cosmic.lkf
new file mode 100644
index 000000000..90c254d1e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/cosmic.lkf
@@ -0,0 +1 @@
+@*
diff --git a/tests/auto/blackbox/testdata-baremetal/defines/defines.qbs b/tests/auto/blackbox/testdata-baremetal/defines/defines.qbs
new file mode 100644
index 000000000..b257a8a4b
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/defines/defines.qbs
@@ -0,0 +1,6 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ cpp.defines: ["FOO", "BAR"]
+ files: ["main.c"]
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/defines/main.c b/tests/auto/blackbox/testdata-baremetal/defines/main.c
new file mode 100644
index 000000000..d2d4769e4
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/defines/main.c
@@ -0,0 +1,11 @@
+#ifndef FOO
+#error FOO missing!
+#endif
+#ifndef BAR
+#error BAR missing!
+#endif
+
+int main(void)
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/bar/bar.h b/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/bar/bar.h
new file mode 100644
index 000000000..49ffa0b12
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/bar/bar.h
@@ -0,0 +1,6 @@
+#ifndef BAR_H
+#define BAR_H
+
+#define BAR_VALUE 1
+
+#endif // BAR_H
diff --git a/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/distribution-include-paths.qbs b/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/distribution-include-paths.qbs
new file mode 100644
index 000000000..0fded6a46
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/distribution-include-paths.qbs
@@ -0,0 +1,6 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ files: ["main.c"]
+ cpp.distributionIncludePaths: ["foo", "bar"]
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/foo/foo.h b/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/foo/foo.h
new file mode 100644
index 000000000..dc510379d
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/foo/foo.h
@@ -0,0 +1,6 @@
+#ifndef FOO_H
+#define FOO_H
+
+#define FOO_VALUE 1
+
+#endif // FOO_H
diff --git a/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/main.c b/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/main.c
new file mode 100644
index 000000000..aabc97a0c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/distribution-include-paths/main.c
@@ -0,0 +1,7 @@
+#include <foo.h>
+#include <bar.h>
+
+int main(void)
+{
+ return FOO_VALUE - BAR_VALUE;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/external-static-libraries/external-static-libraries.qbs b/tests/auto/blackbox/testdata-baremetal/external-static-libraries/external-static-libraries.qbs
new file mode 100644
index 000000000..7e7427561
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/external-static-libraries/external-static-libraries.qbs
@@ -0,0 +1,34 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+import "../BareMetalStaticLibrary.qbs" as BareMetalStaticLibrary
+
+Project {
+ property string outputLibrariesDirectory: sourceDirectory + "/libs"
+ BareMetalStaticLibrary {
+ name: "lib-a"
+ destinationDirectory: project.outputLibrariesDirectory
+ Depends { name: "cpp" }
+ Properties {
+ condition: qbs.targetOS.includes("darwin")
+ bundle.isBundle: false
+ }
+ files: ["lib-a.c"]
+ }
+ BareMetalStaticLibrary {
+ name: "lib-b"
+ destinationDirectory: project.outputLibrariesDirectory
+ Depends { name: "cpp" }
+ Depends { name: "lib-a" }
+ Properties {
+ condition: qbs.targetOS.includes("darwin")
+ bundle.isBundle: false
+ }
+ files: ["lib-b.c"]
+ }
+ BareMetalApplication {
+ Depends { name: "lib-a"; cpp.link: false }
+ Depends { name: "lib-b"; cpp.link: false }
+ files: ["main.c"]
+ cpp.libraryPaths: [project.outputLibrariesDirectory]
+ cpp.staticLibraries: ["lib-b", "lib-a"]
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/external-static-libraries/lib-a.c b/tests/auto/blackbox/testdata-baremetal/external-static-libraries/lib-a.c
new file mode 100644
index 000000000..13401861f
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/external-static-libraries/lib-a.c
@@ -0,0 +1,4 @@
+int a(void)
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/external-static-libraries/lib-b.c b/tests/auto/blackbox/testdata-baremetal/external-static-libraries/lib-b.c
new file mode 100644
index 000000000..5d45b8175
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/external-static-libraries/lib-b.c
@@ -0,0 +1,6 @@
+extern int a(void);
+
+int b(void)
+{
+ return a();
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/external-static-libraries/main.c b/tests/auto/blackbox/testdata-baremetal/external-static-libraries/main.c
new file mode 100644
index 000000000..84ef5e51e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/external-static-libraries/main.c
@@ -0,0 +1,6 @@
+extern int b();
+
+int main(void)
+{
+ return b();
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/linker-map/linker-map.qbs b/tests/auto/blackbox/testdata-baremetal/linker-map/linker-map.qbs
new file mode 100644
index 000000000..fe93ac144
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/linker-map/linker-map.qbs
@@ -0,0 +1,8 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ property bool dummy: {
+ console.info("linker map suffix: %%" + cpp.linkerMapSuffix + "%%");
+ }
+ files: ["main.c"]
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/linker-map/main.c b/tests/auto/blackbox/testdata-baremetal/linker-map/main.c
new file mode 100644
index 000000000..58fe69254
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/linker-map/main.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-application/main.c b/tests/auto/blackbox/testdata-baremetal/one-object-application/main.c
new file mode 100644
index 000000000..58fe69254
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-application/main.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-application/one-object-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-application/one-object-application.qbs
new file mode 100644
index 000000000..482425b5f
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-application/one-object-application.qbs
@@ -0,0 +1,5 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ files: ["main.c"]
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/78k-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/78k-iar.s
new file mode 100644
index 000000000..25f0e2bbd
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/78k-iar.s
@@ -0,0 +1,6 @@
+ PUBLIC main
+ RSEG CODE:CODE:NOROOT(0)
+main:
+ MOVW AX, #0
+ RET
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-cosmic.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-cosmic.s
new file mode 100644
index 000000000..4b45cc989
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-cosmic.s
@@ -0,0 +1,5 @@
+_main:
+ movs r0, #0
+ bx lr
+ xdef _main
+ end
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-gcc.s
new file mode 100644
index 000000000..c7b894230
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-gcc.s
@@ -0,0 +1,5 @@
+ .global main
+ .type main, %function
+main:
+ mov r0, #0
+ bx lr
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-iar.s
new file mode 100644
index 000000000..0a13a5dc2
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC main
+ SECTION `.text`:CODE:NOROOT(1)
+ THUMB
+main:
+ MOVS R0, #+0
+ BX LR
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-keil.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-keil.s
new file mode 100644
index 000000000..f3fcd50fe
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/arm-keil.s
@@ -0,0 +1,7 @@
+ THUMB
+ AREA ||.text||, CODE, READONLY, ALIGN = 1
+main PROC
+ MOVS r0, #0
+ BX lr
+ ENDP
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-gcc.s
new file mode 100644
index 000000000..4ba005a47
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-gcc.s
@@ -0,0 +1,6 @@
+ .global main
+ .type main, %function
+main:
+ ldi r24, 0
+ ldi r25, 0
+ ret
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-iar.s
new file mode 100644
index 000000000..49e9d476e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC main
+ RSEG CODE:CODE:NOROOT(1)
+main:
+ LDI R16, 0
+ LDI R17, 0
+ RET
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr32-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr32-gcc.s
new file mode 100644
index 000000000..879e54150
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr32-gcc.s
@@ -0,0 +1,8 @@
+ .global main
+ .type main, @function
+main:
+ stm --sp, r7, lr
+ mov r7, sp
+ mov r8, 0
+ mov r12, r8
+ ldm sp++, r7, pc
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr32-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr32-iar.s
new file mode 100644
index 000000000..c5e78896f
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/avr32-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC main
+ RSEG CODE32:CODE:REORDER:NOROOT(2)
+ CODE
+main:
+ MOV R12, 0x0
+ RET R12
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/c166-keil.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/c166-keil.s
new file mode 100644
index 000000000..394bc2ae4
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/c166-keil.s
@@ -0,0 +1,7 @@
+MAIN_SEG SECTION CODE WORD 'NCODE'
+main PROC NEAR
+ MOV R4, #0
+ RET
+main ENDP
+MAIN_SEG ENDS
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/cr16-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/cr16-iar.s
new file mode 100644
index 000000000..4a14de6a1
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/cr16-iar.s
@@ -0,0 +1,6 @@
+ PUBLIC main
+ RSEG CODE:CODE:NOROOT(0)
+main:
+ MOVW $0, R0
+ JUMP (RA)
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs12-cosmic.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs12-cosmic.s
new file mode 100644
index 000000000..c33c2b32d
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs12-cosmic.s
@@ -0,0 +1,5 @@
+_main:
+ .dcall "2,0,_main"
+ rts
+ xdef _main
+ end
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs12-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs12-iar.s
new file mode 100644
index 000000000..4344e757f
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs12-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC main
+ RSEG CODE:CODE:REORDER:NOROOT(0)
+main:
+ CLRB
+ CLRA
+ RTS
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-cosmic.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-cosmic.s
new file mode 100644
index 000000000..c33c2b32d
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-cosmic.s
@@ -0,0 +1,5 @@
+_main:
+ .dcall "2,0,_main"
+ rts
+ xdef _main
+ end
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-iar.s
new file mode 100644
index 000000000..883dfdcad
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-iar.s
@@ -0,0 +1,6 @@
+ PUBLIC main
+ RSEG CODE:CODE:REORDER:NOROOT(0)
+main:
+ LDHX #0x0000
+ RTS
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-sdcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-sdcc.s
new file mode 100644
index 000000000..f47fed1be
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/hcs8-sdcc.s
@@ -0,0 +1,6 @@
+ .globl main
+ .area DSEG (PAG)
+ .area HOME (CODE)
+main:
+ clra
+ tax
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m16c-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m16c-iar.s
new file mode 100644
index 000000000..4153f290e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m16c-iar.s
@@ -0,0 +1,6 @@
+ PUBLIC main
+ RSEG CODE:CODE:REORDER:NOROOT(0)
+main:
+ MOV.W #0x0, R0
+ RTS
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32c-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32c-gcc.s
new file mode 100644
index 000000000..173c04c39
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32c-gcc.s
@@ -0,0 +1,5 @@
+ .global _main
+ .type _main, @function
+_main:
+ mov.w #0, r0
+ rts
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32c-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32c-iar.s
new file mode 100644
index 000000000..4153f290e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32c-iar.s
@@ -0,0 +1,6 @@
+ PUBLIC main
+ RSEG CODE:CODE:REORDER:NOROOT(0)
+main:
+ MOV.W #0x0, R0
+ RTS
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32r-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32r-gcc.s
new file mode 100644
index 000000000..dfcf42ca1
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m32r-gcc.s
@@ -0,0 +1,9 @@
+ .global main
+ .type main, @function
+main:
+ push fp
+ mv fp, sp
+ ldi r4, #0
+ mv r0, r4
+ pop fp
+ jmp lr
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-cosmic.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-cosmic.s
new file mode 100644
index 000000000..811b1d79e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-cosmic.s
@@ -0,0 +1,5 @@
+_main:
+ .dcall "8,0,_main"
+ rts
+ xdef _main
+ end
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-gcc.s
new file mode 100644
index 000000000..fdde81dae
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-gcc.s
@@ -0,0 +1,7 @@
+ .global main
+ .type main, @function
+main:
+ link.w %fp, #0
+ clr.l %d0
+ unlk %fp
+ rts
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-iar.s
new file mode 100644
index 000000000..9811be134
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/m68k-iar.s
@@ -0,0 +1,6 @@
+ PUBLIC main
+ RSEG FCODE:CODE:NOROOT(1)
+main:
+ CLR.L D0
+ RTS
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs251-keil.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs251-keil.s
new file mode 100644
index 000000000..312cc9680
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs251-keil.s
@@ -0,0 +1,8 @@
+PUBLIC main
+MAIN_SEG SEGMENT CODE
+ RSEG MAIN_SEG
+main PROC
+ XRL WR6,WR6
+ RET
+ ENDP
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-iar.s
new file mode 100644
index 000000000..09cc64613
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC main
+ RSEG NEAR_CODE:CODE:NOROOT(0)
+main:
+ MOV R2, #0x0
+ MOV R3, #0x0
+ RET
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-keil.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-keil.s
new file mode 100644
index 000000000..28174d0e2
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-keil.s
@@ -0,0 +1,8 @@
+PUBLIC main
+MAIN_SEG SEGMENT CODE
+ RSEG MAIN_SEG
+main:
+ MOV R6, #0x0
+ MOV R7, #0x0
+ RET
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-sdcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-sdcc.s
new file mode 100644
index 000000000..eaa6467e3
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/mcs51-sdcc.s
@@ -0,0 +1,7 @@
+ .globl main
+ .area PSEG (PAG,XDATA)
+ .area XSEG (XDATA)
+ .area HOME (CODE)
+main:
+ mov dptr, #0x0000
+ ret
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-gcc.s
new file mode 100644
index 000000000..8e8a24980
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-gcc.s
@@ -0,0 +1,5 @@
+ .global main
+ .type main, %function
+main:
+ mov #0, r15
+ .LIRD0:
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-iar.s
new file mode 100644
index 000000000..fbabe3ba8
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/msp430-iar.s
@@ -0,0 +1,6 @@
+ PUBLIC main
+ RSEG `CODE`:CODE:REORDER:NOROOT(1)
+main:
+ MOV.W #0x0, R12
+ RET
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs
new file mode 100644
index 000000000..e8805225c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/one-object-asm-application.qbs
@@ -0,0 +1,122 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ condition: {
+ if (qbs.toolchainType === "cosmic") {
+ if (qbs.architecture.startsWith("arm"))
+ return true;
+ if (qbs.architecture === "stm8")
+ return true;
+ if (qbs.architecture === "hcs8")
+ return true;
+ if (qbs.architecture === "hcs12")
+ return true;
+ if (qbs.architecture === "m68k")
+ return true;
+ } else if (qbs.toolchainType === "keil") {
+ if (qbs.architecture.startsWith("arm"))
+ return true;
+ if (qbs.architecture === "mcs51")
+ return true;
+ if (qbs.architecture === "mcs251")
+ return true;
+ if (qbs.architecture === "c166")
+ return true;
+ } else if (qbs.toolchainType === "iar") {
+ if (qbs.architecture.startsWith("arm"))
+ return true;
+ if (qbs.architecture === "mcs51")
+ return true;
+ if (qbs.architecture === "stm8")
+ return true;
+ if (qbs.architecture === "avr")
+ return true;
+ if (qbs.architecture === "avr32")
+ return true;
+ if (qbs.architecture === "msp430")
+ return true;
+ if (qbs.architecture === "rl78")
+ return true;
+ if (qbs.architecture === "rh850")
+ return true;
+ if (qbs.architecture === "v850")
+ return true;
+ if (qbs.architecture === "78k")
+ return true;
+ if (qbs.architecture === "r32c")
+ return true;
+ if (qbs.architecture === "sh")
+ return true;
+ if (qbs.architecture === "cr16")
+ return true;
+ if (qbs.architecture === "m16c")
+ return true;
+ if (qbs.architecture === "hcs8")
+ return true;
+ if (qbs.architecture === "hcs12")
+ return true;
+ if (qbs.architecture === "rx")
+ return true;
+ if (qbs.architecture === "m32c")
+ return true;
+ if (qbs.architecture === "riscv")
+ return true;
+ if (qbs.architecture === "m68k")
+ return true;
+ } else if (qbs.toolchainType === "sdcc") {
+ if (qbs.architecture === "mcs51")
+ return true;
+ if (qbs.architecture === "stm8")
+ return true;
+ if (qbs.architecture === "hcs8")
+ return true;
+ } else if (qbs.toolchainType === "gcc") {
+ if (qbs.architecture.startsWith("arm"))
+ return true;
+ if (qbs.architecture === "avr")
+ return true;
+ if (qbs.architecture === "avr32")
+ return true;
+ if (qbs.architecture === "msp430")
+ return true;
+ if (qbs.architecture === "xtensa")
+ return true;
+ if (qbs.architecture === "rl78")
+ return true;
+ if (qbs.architecture === "m32c")
+ return true;
+ if (qbs.architecture === "m32r")
+ return true;
+ if (qbs.architecture === "m68k")
+ return true;
+ if (qbs.architecture === "v850")
+ return true;
+ if (qbs.architecture === "riscv")
+ return true;
+ if (qbs.architecture === "rx")
+ return true;
+ }
+ console.info("unsupported toolset: %%"
+ + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%");
+ return false;
+ }
+
+ Properties {
+ condition: qbs.toolchainType === "gcc"
+ && qbs.architecture === "msp430"
+ // We need to use this workaround to enable
+ // the cpp.driverFlags property.
+ cpp.linkerPath: cpp.compilerPathByLanguage["c"]
+ }
+
+ Properties {
+ condition: qbs.toolchainType === "iar"
+ && qbs.architecture.startsWith("arm")
+ cpp.entryPoint: "main"
+ }
+
+ cpp.linkerPath: original
+
+ files: [(qbs.architecture.startsWith("arm") ? "arm" : qbs.architecture)
+ + "-" + qbs.toolchainType + ".s"]
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/r32c-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/r32c-iar.s
new file mode 100644
index 000000000..844306811
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/r32c-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC main
+ RSEG CODE24:CODE:REORDER:NOROOT(0)
+main:
+ MOV.L:Z #0x0, R2R0
+ RTS
+ RSEG SBREF:DATA:NOROOT(0)
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rh850-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rh850-iar.s
new file mode 100644
index 000000000..8901027aa
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rh850-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC _main
+ SECTION `.text`:CODE:NOROOT(2)
+ CODE
+_main:
+ MOV r0, r10
+ JMP [lp]
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/riscv-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/riscv-gcc.s
new file mode 100644
index 000000000..d09097804
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/riscv-gcc.s
@@ -0,0 +1,11 @@
+ .globl main
+ .type main, @function
+main:
+ add sp, sp, -16
+ sd s0, 8(sp)
+ add s0, sp, 16
+ li a5, 0
+ mv a0, a5
+ ld s0, 8(sp)
+ add sp, sp, 16
+ jr ra
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/riscv-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/riscv-iar.s
new file mode 100644
index 000000000..e19fdfddb
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/riscv-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC main
+ SECTION `.text`:CODE:REORDER:NOROOT(2)
+ CODE
+main:
+ MV A0, ZERO
+ RET
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rl78-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rl78-gcc.s
new file mode 100644
index 000000000..59510bd01
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rl78-gcc.s
@@ -0,0 +1,11 @@
+r8 = 0xffef0
+.text
+ .global _main
+ .type _main, @function
+_main:
+ subw sp, #2
+ clrw ax
+ movw [sp], ax
+ movw r8, ax
+ addw sp, #2
+ ret
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rl78-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rl78-iar.s
new file mode 100644
index 000000000..1f00996cc
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rl78-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC _main
+ SECTION `.text`:CODE:NOROOT(0)
+ CODE
+_main:
+ CLRW AX
+ RET
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rx-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rx-gcc.s
new file mode 100644
index 000000000..501d4cd7e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rx-gcc.s
@@ -0,0 +1,8 @@
+ .global _main
+ .type _main, @function
+_main:
+ push.l r10
+ mov.L r0, r10
+ mov.L #0, r5
+ mov.L r5, r1
+ rtsd #4, r10-r10
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rx-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rx-iar.s
new file mode 100644
index 000000000..cc1573431
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/rx-iar.s
@@ -0,0 +1,5 @@
+ PUBLIC _main
+ SECTION CODE:CODE:ROOT(2)
+_main:
+ BRA _main
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/sh-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/sh-iar.s
new file mode 100644
index 000000000..d86780310
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/sh-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC _main
+ SECTION `.code32.text`:CODE:NOROOT(2)
+_main:
+ CODE
+ MOV #0, R0
+ RTS/N
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-cosmic.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-cosmic.s
new file mode 100644
index 000000000..818b0d680
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-cosmic.s
@@ -0,0 +1,7 @@
+ scross off
+_main:
+ .dcall "2,0,_main"
+ ret
+ .scheck _main
+ xdef _main
+ end
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-iar.s
new file mode 100644
index 000000000..674e20de6
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC main
+ SECTION `.near_func.text`:CODE:REORDER:NOROOT(0)
+ CODE
+main:
+ CLRW X
+ RET
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-sdcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-sdcc.s
new file mode 100644
index 000000000..1a552f4a4
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/stm8-sdcc.s
@@ -0,0 +1,7 @@
+ .globl main
+ .area DATA
+ .area SSEG
+ .area HOME
+main:
+ clrw x
+ ret
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/v850-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/v850-gcc.s
new file mode 100644
index 000000000..3599a1fb1
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/v850-gcc.s
@@ -0,0 +1,11 @@
+ .global _main
+ .type _main, @function
+_main:
+ add -4, sp
+ st.w r29, 0[sp]
+ mov sp, r29
+ mov 0, r10
+ mov r29, sp
+ ld.w 0[sp], r29
+ add 4, sp
+ jmp [r31]
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/v850-iar.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/v850-iar.s
new file mode 100644
index 000000000..4ccfacd64
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/v850-iar.s
@@ -0,0 +1,7 @@
+ PUBLIC _main
+ RSEG `CODE`:CODE:NOROOT(2)
+ CODE
+_main:
+ MOV zero, r1
+ JMP [lp]
+ END
diff --git a/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/xtensa-gcc.s b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/xtensa-gcc.s
new file mode 100644
index 000000000..c21000905
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/one-object-asm-application/xtensa-gcc.s
@@ -0,0 +1,11 @@
+ .global main
+ .type main, @function
+main:
+ addi sp, sp, -16
+ s32i.n a15, sp, 12
+ mov.n a15, sp
+ movi.n a2, 0
+ mov.n sp, a15
+ l32i.n a15, sp, 12
+ addi sp, sp, 16
+ ret.n
diff --git a/tests/auto/blackbox/testdata-baremetal/preinclude-headers/main.c b/tests/auto/blackbox/testdata-baremetal/preinclude-headers/main.c
new file mode 100644
index 000000000..755192287
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/preinclude-headers/main.c
@@ -0,0 +1,4 @@
+int main(void)
+{
+ return PREINCLUDE_VALUE;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs b/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs
new file mode 100644
index 000000000..0ded6ff15
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude-headers.qbs
@@ -0,0 +1,18 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ condition: {
+ if (qbs.toolchainType === "keil") {
+ if (qbs.architecture === "mcs51"
+ || qbs.architecture === "mcs251"
+ || qbs.architecture === "c166") {
+ console.info("unsupported toolset: %%"
+ + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%");
+ return false;
+ }
+ }
+ return true;
+ }
+ cpp.prefixHeaders: ["preinclude.h"]
+ files: ["main.c"]
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude.h b/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude.h
new file mode 100644
index 000000000..6b68e4826
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/preinclude-headers/preinclude.h
@@ -0,0 +1,6 @@
+#ifndef PREINCLUDE_H
+#define PREINCLUDE_H
+
+#define PREINCLUDE_VALUE 0
+
+#endif // PREINCLUDE_H
diff --git a/tests/auto/blackbox/testdata-baremetal/shared-libraries/app.c b/tests/auto/blackbox/testdata-baremetal/shared-libraries/app.c
new file mode 100644
index 000000000..f2ecb5f55
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/shared-libraries/app.c
@@ -0,0 +1,12 @@
+#include "../dllexport.h"
+
+#include <stdio.h>
+
+DLL_IMPORT void foo(void);
+
+int main(void)
+{
+ printf("Hello from app\n");
+ foo();
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/shared-libraries/shared-libraries.qbs b/tests/auto/blackbox/testdata-baremetal/shared-libraries/shared-libraries.qbs
new file mode 100644
index 000000000..fded553f6
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/shared-libraries/shared-libraries.qbs
@@ -0,0 +1,32 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+Project {
+ condition: {
+ if (qbs.targetPlatform === "windows" && qbs.architecture === "x86") {
+ if (qbs.toolchainType === "watcom")
+ return true;
+ if (qbs.toolchainType === "dmc")
+ return true;
+ }
+
+ if (qbs.toolchainType === "msvc")
+ return true;
+
+ console.info("unsupported toolset: %%"
+ + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%");
+ return false;
+ }
+
+ DynamicLibrary {
+ Depends { name: "cpp" }
+ destinationDirectory: "bin"
+ name: "shared"
+ files: ["shared.c"]
+ }
+ BareMetalApplication {
+ Depends { name: "shared" }
+ destinationDirectory: "bin"
+ name: "app"
+ files: ["app.c"]
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/shared-libraries/shared.c b/tests/auto/blackbox/testdata-baremetal/shared-libraries/shared.c
new file mode 100644
index 000000000..ab0c110fb
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/shared-libraries/shared.c
@@ -0,0 +1,19 @@
+#include "../dllexport.h"
+
+#include <stdio.h>
+
+#ifdef __DMC__
+#include <windows.h>
+#define EXPORT_FUN _export
+BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
+{
+ return TRUE;
+}
+#else
+#define EXPORT_FUN
+#endif // __DMC__
+
+DLL_EXPORT void EXPORT_FUN foo(void)
+{
+ printf("Hello from lib\n");
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/a1.c b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/a1.c
new file mode 100644
index 000000000..b593e95d8
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/a1.c
@@ -0,0 +1,4 @@
+int a1(void)
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/a2.c b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/a2.c
new file mode 100644
index 000000000..35ab7feb3
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/a2.c
@@ -0,0 +1,4 @@
+int a2(void)
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/app.c b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/app.c
new file mode 100644
index 000000000..9814bfd9c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/app.c
@@ -0,0 +1,6 @@
+extern int e(void);
+
+int main(void)
+{
+ return e();
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/b.c b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/b.c
new file mode 100644
index 000000000..92df418d1
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/b.c
@@ -0,0 +1,6 @@
+extern int a1(void);
+
+int b(void)
+{
+ return a1();
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/c.c b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/c.c
new file mode 100644
index 000000000..0c0e350f2
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/c.c
@@ -0,0 +1,6 @@
+extern int a2(void);
+
+int c(void)
+{
+ return a2();
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/d.c b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/d.c
new file mode 100644
index 000000000..a3fc084f5
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/d.c
@@ -0,0 +1,7 @@
+extern int b(void);
+extern int c(void);
+
+int d(void)
+{
+ return b() + c();
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/e.c b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/e.c
new file mode 100644
index 000000000..9381e845c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/e.c
@@ -0,0 +1,7 @@
+extern int d(void);
+
+int e(void)
+{
+ return d();
+}
+
diff --git a/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/static-library-dependencies.qbs b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/static-library-dependencies.qbs
new file mode 100644
index 000000000..7184f47ea
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/static-library-dependencies/static-library-dependencies.qbs
@@ -0,0 +1,40 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+import "../BareMetalStaticLibrary.qbs" as BareMetalStaticLibrary
+
+Project {
+ BareMetalStaticLibrary {
+ name: "lib-a"
+ Depends { name: "cpp" }
+ files: ["a1.c", "a2.c"]
+ }
+ BareMetalStaticLibrary {
+ name: "lib-b"
+ Depends { name: "cpp" }
+ Depends { name: "lib-a" }
+ files: ["b.c"]
+ }
+ BareMetalStaticLibrary {
+ name: "lib-c"
+ Depends { name: "cpp" }
+ Depends { name: "lib-a" }
+ files: ["c.c"]
+ }
+ BareMetalStaticLibrary {
+ name: "lib-d"
+ Depends { name: "cpp" }
+ Depends { name: "lib-b" }
+ Depends { name: "lib-c" }
+ files: ["d.c"]
+ }
+ BareMetalStaticLibrary {
+ name: "lib-e"
+ Depends { name: "cpp" }
+ Depends { name: "lib-d" }
+ files: ["e.c"]
+ }
+ BareMetalApplication {
+ name: "app"
+ Depends { name: "lib-e" }
+ files: ["app.c"]
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/system-include-paths/bar/bar.h b/tests/auto/blackbox/testdata-baremetal/system-include-paths/bar/bar.h
new file mode 100644
index 000000000..49ffa0b12
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/system-include-paths/bar/bar.h
@@ -0,0 +1,6 @@
+#ifndef BAR_H
+#define BAR_H
+
+#define BAR_VALUE 1
+
+#endif // BAR_H
diff --git a/tests/auto/blackbox/testdata-baremetal/system-include-paths/foo/foo.h b/tests/auto/blackbox/testdata-baremetal/system-include-paths/foo/foo.h
new file mode 100644
index 000000000..dc510379d
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/system-include-paths/foo/foo.h
@@ -0,0 +1,6 @@
+#ifndef FOO_H
+#define FOO_H
+
+#define FOO_VALUE 1
+
+#endif // FOO_H
diff --git a/tests/auto/blackbox/testdata-baremetal/system-include-paths/main.c b/tests/auto/blackbox/testdata-baremetal/system-include-paths/main.c
new file mode 100644
index 000000000..aabc97a0c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/system-include-paths/main.c
@@ -0,0 +1,7 @@
+#include <foo.h>
+#include <bar.h>
+
+int main(void)
+{
+ return FOO_VALUE - BAR_VALUE;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/system-include-paths/system-include-paths.qbs b/tests/auto/blackbox/testdata-baremetal/system-include-paths/system-include-paths.qbs
new file mode 100644
index 000000000..1f9fd1231
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/system-include-paths/system-include-paths.qbs
@@ -0,0 +1,6 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ files: ["main.c"]
+ cpp.systemIncludePaths: ["foo", "bar"]
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/target-platform/target-platform.qbs b/tests/auto/blackbox/testdata-baremetal/target-platform/target-platform.qbs
new file mode 100644
index 000000000..d29e7e619
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/target-platform/target-platform.qbs
@@ -0,0 +1,19 @@
+Product {
+ Depends { name: "cpp" }
+ condition: {
+ if (qbs.toolchainType === "keil"
+ || qbs.toolchainType === "iar"
+ || qbs.toolchainType === "sdcc"
+ || qbs.toolchainType === "cosmic") {
+ var hasNoPlatform = (qbs.targetPlatform === "none");
+ var hasNoOS = (qbs.targetOS.length === 1 && qbs.targetOS[0] === "none");
+ console.info("has no platform: " + hasNoPlatform);
+ console.info("has no os: " + hasNoOS);
+ } else {
+ console.info("unsupported toolset: %%"
+ + qbs.toolchainType + "%%, %%" + qbs.architecture + "%%");
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/dmc.qbs b/tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/dmc.qbs
new file mode 100644
index 000000000..13f751669
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/dmc.qbs
@@ -0,0 +1,31 @@
+import qbs.Probes
+
+Product {
+ id: product
+ condition: qbs.toolchainType === "dmc"
+
+ Depends { name: "cpp" }
+
+ Probes.DmcProbe {
+ id: probe
+ compilerFilePath: cpp.compilerPath
+ enableDefinesByLanguage: cpp.enableCompilerDefinesByLanguage
+ _targetPlatform: qbs.targetPlatform
+ _targetArchitecture: qbs.architecture
+ _targetExtender: cpp.extenderName
+ }
+
+ property bool dummy: {
+ if (!product.condition)
+ return;
+ if (!probe.found
+ || !probe.compilerDefinesByLanguage
+ || !probe.includePaths
+ || (probe.includePaths.length === 0)
+ || (qbs.architecture !== probe.architecture)
+ || (qbs.targetPlatform !== probe.targetPlatform)) {
+ console.info("broken probe: %%" + qbs.toolchainType + "%%, %%"
+ + qbs.architecture + "%%");
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/sdcc.qbs b/tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/sdcc.qbs
new file mode 100644
index 000000000..fa5f519a9
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/sdcc.qbs
@@ -0,0 +1,29 @@
+import qbs.Probes
+
+Product {
+ id: product
+ condition: qbs.toolchainType === "sdcc"
+
+ Depends { name: "cpp" }
+
+ Probes.SdccProbe {
+ id: probe
+ compilerFilePath: cpp.compilerPath
+ enableDefinesByLanguage: cpp.enableCompilerDefinesByLanguage
+ preferredArchitecture: qbs.architecture
+ }
+
+ property bool dummy: {
+ if (!product.condition)
+ return;
+ if (!probe.found
+ || !probe.endianness
+ || !probe.compilerDefinesByLanguage
+ || !probe.includePaths
+ || (probe.includePaths.length === 0)
+ || (qbs.architecture !== probe.architecture)) {
+ console.info("broken probe: %%" + qbs.toolchainType + "%%, %%"
+ + qbs.architecture + "%%");
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/watcom.qbs b/tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/watcom.qbs
new file mode 100644
index 000000000..b7c92dc21
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/toolchain-probe/probes/watcom.qbs
@@ -0,0 +1,34 @@
+import qbs.Probes
+
+Product {
+ id: product
+ condition: qbs.toolchainType === "watcom"
+
+ Depends { name: "cpp" }
+
+ Probes.WatcomProbe {
+ id: probe
+ compilerFilePath: cpp.compilerPath
+ enableDefinesByLanguage: cpp.enableCompilerDefinesByLanguage
+ _pathListSeparator: qbs.pathListSeparator
+ _toolchainInstallPath: cpp.toolchainInstallPath
+ _targetPlatform: qbs.targetPlatform
+ _targetArchitecture: qbs.architecture
+ }
+
+ property bool dummy: {
+ if (!product.condition)
+ return;
+ if (!probe.found
+ || !probe.endianness
+ || !probe.compilerDefinesByLanguage
+ || !probe.environment
+ || !probe.includePaths
+ || (probe.includePaths.length === 0)
+ || (qbs.architecture !== probe.architecture)
+ || (qbs.targetPlatform !== probe.targetPlatform)) {
+ console.info("broken probe: %%" + qbs.toolchainType + "%%, %%"
+ + qbs.architecture + "%%");
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/toolchain-probe/toolchain-probe.qbs b/tests/auto/blackbox/testdata-baremetal/toolchain-probe/toolchain-probe.qbs
new file mode 100644
index 000000000..36725cfde
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/toolchain-probe/toolchain-probe.qbs
@@ -0,0 +1,7 @@
+Project {
+ references: [
+ "probes/dmc.qbs",
+ "probes/sdcc.qbs",
+ "probes/watcom.qbs",
+ ]
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/two-object-application/fun.c b/tests/auto/blackbox/testdata-baremetal/two-object-application/fun.c
new file mode 100644
index 000000000..3b8c8f2f4
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/two-object-application/fun.c
@@ -0,0 +1,4 @@
+int f(void)
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/two-object-application/main.c b/tests/auto/blackbox/testdata-baremetal/two-object-application/main.c
new file mode 100644
index 000000000..2c3d7726c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/two-object-application/main.c
@@ -0,0 +1,6 @@
+extern int f(void);
+
+int main(void)
+{
+ return f();
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/two-object-application/two-object-application.qbs b/tests/auto/blackbox/testdata-baremetal/two-object-application/two-object-application.qbs
new file mode 100644
index 000000000..2947975ca
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/two-object-application/two-object-application.qbs
@@ -0,0 +1,5 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ files: ["main.c", "fun.c"]
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/user-include-paths/bar/bar.h b/tests/auto/blackbox/testdata-baremetal/user-include-paths/bar/bar.h
new file mode 100644
index 000000000..49ffa0b12
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/user-include-paths/bar/bar.h
@@ -0,0 +1,6 @@
+#ifndef BAR_H
+#define BAR_H
+
+#define BAR_VALUE 1
+
+#endif // BAR_H
diff --git a/tests/auto/blackbox/testdata-baremetal/user-include-paths/foo/foo.h b/tests/auto/blackbox/testdata-baremetal/user-include-paths/foo/foo.h
new file mode 100644
index 000000000..dc510379d
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/user-include-paths/foo/foo.h
@@ -0,0 +1,6 @@
+#ifndef FOO_H
+#define FOO_H
+
+#define FOO_VALUE 1
+
+#endif // FOO_H
diff --git a/tests/auto/blackbox/testdata-baremetal/user-include-paths/main.c b/tests/auto/blackbox/testdata-baremetal/user-include-paths/main.c
new file mode 100644
index 000000000..e76e08cbe
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/user-include-paths/main.c
@@ -0,0 +1,7 @@
+#include "foo.h"
+#include "bar.h"
+
+int main(void)
+{
+ return FOO_VALUE - BAR_VALUE;
+}
diff --git a/tests/auto/blackbox/testdata-baremetal/user-include-paths/user-include-paths.qbs b/tests/auto/blackbox/testdata-baremetal/user-include-paths/user-include-paths.qbs
new file mode 100644
index 000000000..23d5dbced
--- /dev/null
+++ b/tests/auto/blackbox/testdata-baremetal/user-include-paths/user-include-paths.qbs
@@ -0,0 +1,6 @@
+import "../BareMetalApplication.qbs" as BareMetalApplication
+
+BareMetalApplication {
+ files: ["main.c"]
+ cpp.includePaths: ["foo", "bar"]
+}
diff --git a/tests/auto/blackbox/testdata-clangdb/project1/project.qbs b/tests/auto/blackbox/testdata-clangdb/project1/project.qbs
index ecfc562b0..313f33ead 100644
--- a/tests/auto/blackbox/testdata-clangdb/project1/project.qbs
+++ b/tests/auto/blackbox/testdata-clangdb/project1/project.qbs
@@ -7,8 +7,8 @@ Project {
Application {
Probe {
id: dummy
- property bool isMingw: qbs.toolchain.contains("mingw")
- property bool isMsvc: qbs.toolchain.contains("msvc")
+ property bool isMingw: qbs.toolchain.includes("mingw")
+ property bool isMsvc: qbs.toolchain.includes("msvc")
property var buildEnv: cpp.buildEnv
configure: {
if (!buildEnv)
diff --git a/tests/auto/blackbox/testdata-java/java/vehicles.qbs b/tests/auto/blackbox/testdata-java/java/vehicles.qbs
index 8153efe00..8a4bb732f 100644
--- a/tests/auto/blackbox/testdata-java/java/vehicles.qbs
+++ b/tests/auto/blackbox/testdata-java/java/vehicles.qbs
@@ -1,4 +1,5 @@
import qbs.FileInfo
+import qbs.Host
import qbs.Utilities
Project {
@@ -6,10 +7,17 @@ Project {
Depends { name: "cpp" }
Depends { name: "car_jar" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
+ property bool _testPlatform: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
+
name: "native"
files: ["engine.c"]
@@ -34,7 +42,7 @@ Project {
Export {
Depends { name: "java" }
- java.manifestClassPath: [product.targetName + ".jar"]
+ java.manifestClassPath: [exportingProduct.targetName + ".jar"]
}
}
@@ -50,7 +58,7 @@ Project {
Export {
Depends { name: "java" }
- java.manifestClassPath: [product.targetName + ".jar"]
+ java.manifestClassPath: [exportingProduct.targetName + ".jar"]
}
}
@@ -68,13 +76,13 @@ Project {
cpp.systemIncludePaths: {
var paths = importingProduct.java.jdkIncludePaths;
if (Utilities.versionCompare(importingProduct.java.version, "1.8") >= 0) {
- paths.push(product.buildDirectory); // generated JNI headers
+ paths.push(exportingProduct.buildDirectory); // generated JNI headers
}
return paths;
}
Depends { name: "java" }
- java.manifestClassPath: [product.targetName + ".jar"]
+ java.manifestClassPath: [exportingProduct.targetName + ".jar"]
}
qbs.installPrefix: ""
diff --git a/tests/auto/blackbox/testdata-joblimits/job-limits-init/job-limits-init.qbs b/tests/auto/blackbox/testdata-joblimits/job-limits-init/job-limits-init.qbs
new file mode 100644
index 000000000..3a0075530
--- /dev/null
+++ b/tests/auto/blackbox/testdata-joblimits/job-limits-init/job-limits-init.qbs
@@ -0,0 +1,10 @@
+import qbs.Host
+
+Product {
+ property bool _testPlatform: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
+}
diff --git a/tests/auto/blackbox/testdata-joblimits/job-limits/job-limits.qbs b/tests/auto/blackbox/testdata-joblimits/job-limits/job-limits.qbs
index 221105a64..76f54807a 100644
--- a/tests/auto/blackbox/testdata-joblimits/job-limits/job-limits.qbs
+++ b/tests/auto/blackbox/testdata-joblimits/job-limits/job-limits.qbs
@@ -19,7 +19,7 @@ Project {
consoleApplication: true
cpp.cxxLanguageVersion: "c++14"
Properties {
- condition: qbs.targetOS.contains("macos")
+ condition: qbs.targetOS.includes("macos")
cpp.minimumMacosVersion: "10.9"
}
files: "main.cpp"
@@ -37,7 +37,7 @@ Project {
var cmd = new Command(explicitlyDependsOn.tool_tag[0].filePath,
[output.filePath]);
cmd.workingDirectory = product.buildDirectory;
- cmd.description = "Running tool";
+ cmd.description = "running tool";
cmd.jobPool = "singleton";
return cmd;
}
diff --git a/tests/auto/blackbox/testdata-joblimits/job-limits/main.cpp b/tests/auto/blackbox/testdata-joblimits/job-limits/main.cpp
index ec9acba80..5f436b0e9 100644
--- a/tests/auto/blackbox/testdata-joblimits/job-limits/main.cpp
+++ b/tests/auto/blackbox/testdata-joblimits/job-limits/main.cpp
@@ -63,7 +63,7 @@ int main(int argc, char *argv[])
const std::string lockFilePath = std::string(argv[0]) + ".lock";
std::FILE * const lockFile = std::fopen(lockFilePath.c_str(), "w");
if (!lockFile) {
- std::cerr << "cannot open lock file: " << strerror(errno) << std::endl;
+ std::cerr << "cannot open lock file: " << std::strerror(errno) << std::endl;
return 2;
}
if (!tryLock(lockFile)) {
@@ -71,17 +71,17 @@ int main(int argc, char *argv[])
std::cerr << "tool is exclusive" << std::endl;
return 3;
} else {
- std::cerr << "unexpected lock failure: " << strerror(errno) << std::endl;
- fclose(lockFile);
+ std::cerr << "unexpected lock failure: " << std::strerror(errno) << std::endl;
+ std::fclose(lockFile);
return 4;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
- fclose(lockFile);
+ std::fclose(lockFile);
std::FILE * const output = std::fopen(argv[1], "w");
if (!output) {
- std::cerr << "cannot create output file: " << strerror(errno) << std::endl;
+ std::cerr << "cannot create output file: " << std::strerror(errno) << std::endl;
return 5;
}
- fclose(output);
+ std::fclose(output);
}
diff --git a/tests/auto/blackbox/testdata-providers/allowed-values/allowed-values.qbs b/tests/auto/blackbox/testdata-providers/allowed-values/allowed-values.qbs
new file mode 100644
index 000000000..e6dbaddde
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/allowed-values/allowed-values.qbs
@@ -0,0 +1,4 @@
+Product {
+ Depends { name: "qbsmetatestmodule" }
+ qbsModuleProviders: "provider"
+}
diff --git a/tests/auto/blackbox/testdata-providers/allowed-values/module-providers/provider.qbs b/tests/auto/blackbox/testdata-providers/allowed-values/module-providers/provider.qbs
new file mode 100644
index 000000000..c0e426c96
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/allowed-values/module-providers/provider.qbs
@@ -0,0 +1,14 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ isEager: false
+ property stringList aProperty: "zero"
+ PropertyOptions {
+ name: "aProperty"
+ allowedValues: ["one", "two"]
+ }
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, moduleName, "from_provider");
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/broken-provider/broken-provider.qbs b/tests/auto/blackbox/testdata-providers/broken-provider/broken-provider.qbs
new file mode 100644
index 000000000..461c7e30e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/broken-provider/broken-provider.qbs
@@ -0,0 +1,13 @@
+Project {
+ qbsModuleProviders: "provider_a"
+ name: "project"
+ Project {
+ name: "innerProject"
+ Product {
+ name: "p1"
+ Depends { name: "qbsothermodule"; required: false }
+ Depends { name: "qbsmetatestmodule" }
+ }
+ }
+
+}
diff --git a/tests/auto/blackbox/testdata-providers/broken-provider/module-providers/provider_a.qbs b/tests/auto/blackbox/testdata-providers/broken-provider/module-providers/provider_a.qbs
new file mode 100644
index 000000000..f446d2d13
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/broken-provider/module-providers/provider_a.qbs
@@ -0,0 +1,5 @@
+ModuleProvider {
+ relativeSearchPaths: {
+ throw "This provider is broken";
+ }
+}
diff --git a/tests/auto/blackbox/testdata/fallback-module-provider/fallback-module-provider.qbs b/tests/auto/blackbox/testdata-providers/fallback-module-provider/fallback-module-provider.qbs
index a798e15b3..a798e15b3 100644
--- a/tests/auto/blackbox/testdata/fallback-module-provider/fallback-module-provider.qbs
+++ b/tests/auto/blackbox/testdata-providers/fallback-module-provider/fallback-module-provider.qbs
diff --git a/tests/auto/blackbox/testdata/fallback-module-provider/libdir/qbsmetatestmodule.pc b/tests/auto/blackbox/testdata-providers/fallback-module-provider/libdir/qbsmetatestmodule.pc
index ae4daba89..ae4daba89 100644
--- a/tests/auto/blackbox/testdata/fallback-module-provider/libdir/qbsmetatestmodule.pc
+++ b/tests/auto/blackbox/testdata-providers/fallback-module-provider/libdir/qbsmetatestmodule.pc
diff --git a/tests/auto/blackbox/testdata/fallback-module-provider/main.cpp b/tests/auto/blackbox/testdata-providers/fallback-module-provider/main.cpp
index 442b755bf..442b755bf 100644
--- a/tests/auto/blackbox/testdata/fallback-module-provider/main.cpp
+++ b/tests/auto/blackbox/testdata-providers/fallback-module-provider/main.cpp
diff --git a/tests/auto/blackbox/testdata-providers/module-providers-cache/module-providers-cache.qbs b/tests/auto/blackbox/testdata-providers/module-providers-cache/module-providers-cache.qbs
new file mode 100644
index 000000000..508ed84d2
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/module-providers-cache/module-providers-cache.qbs
@@ -0,0 +1,11 @@
+Project {
+ qbsModuleProviders: ["provider_a"]
+ name: "project"
+ property string dummyProp
+
+ Product {
+ name: "p1"
+ Depends { name: "qbsothermodule" }
+ Depends { name: "qbsmetatestmodule" }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/module-providers-cache/module-providers/provider_a.qbs b/tests/auto/blackbox/testdata-providers/module-providers-cache/module-providers/provider_a.qbs
new file mode 100644
index 000000000..782cf7d25
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/module-providers-cache/module-providers/provider_a.qbs
@@ -0,0 +1,9 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsmetatestmodule", "from_provider_a");
+ Helpers.writeModule(outputBaseDir, "qbsothermodule", "from_provider_a");
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/module-providers/main.cpp b/tests/auto/blackbox/testdata-providers/module-providers/main.cpp
new file mode 100644
index 000000000..85a4f551c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/module-providers/main.cpp
@@ -0,0 +1,8 @@
+#include <iostream>
+
+int main()
+{
+ std::cout << "The letters are " << LETTER1 << " and " << LETTER2 << std::endl;
+ std::cout << "The MY_DEFINE is " << MY_DEFINE << std::endl;
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-providers/module-providers/module-providers.qbs b/tests/auto/blackbox/testdata-providers/module-providers/module-providers.qbs
new file mode 100644
index 000000000..89bd1a11d
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/module-providers/module-providers.qbs
@@ -0,0 +1,36 @@
+import qbs.Host
+
+Project {
+ property bool enabled: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
+ readonly property string beginning: "beginning"
+ CppApplication {
+ name: "app1"
+ property string chooseLettersFrom: project.beginning // This indirection tests QBS-1747.
+ Depends { name: "mygenerator.module1" }
+ Depends { name: "mygenerator.module2" }
+ Depends { name: "othergenerator" }
+ moduleProviders.mygenerator.chooseLettersFrom: chooseLettersFrom
+ moduleProviders.othergenerator.someDefines: name
+ files: "main.cpp"
+ }
+ CppApplication {
+ readonly property string end: "end"
+ name: "app2"
+ Depends { name: "mygenerator.module1" }
+ Depends { name: "mygenerator.module2" }
+ Depends { name: "othergenerator" }
+ Profile {
+ name: "myProfile"
+ baseProfile: project.profile
+ moduleProviders.mygenerator.chooseLettersFrom: product.end
+ moduleProviders.othergenerator.someDefines: "app2"
+ }
+ qbs.profile: "myProfile"
+ files: "main.cpp"
+ }
+}
diff --git a/tests/auto/blackbox/testdata/module-providers/module-providers/mygenerator/provider.qbs b/tests/auto/blackbox/testdata-providers/module-providers/module-providers/mygenerator/provider.qbs
index dae02c03a..dae02c03a 100644
--- a/tests/auto/blackbox/testdata/module-providers/module-providers/mygenerator/provider.qbs
+++ b/tests/auto/blackbox/testdata-providers/module-providers/module-providers/mygenerator/provider.qbs
diff --git a/tests/auto/blackbox/testdata-providers/module-providers/module-providers/othergenerator/provider.qbs b/tests/auto/blackbox/testdata-providers/module-providers/module-providers/othergenerator/provider.qbs
new file mode 100644
index 000000000..66557037c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/module-providers/module-providers/othergenerator/provider.qbs
@@ -0,0 +1,19 @@
+import qbs.File;
+import qbs.FileInfo;
+import qbs.TextFile;
+
+ModuleProvider {
+ property string someDefines
+ relativeSearchPaths: {
+ console.info("Running setup script for " + name);
+ var moduleDir = FileInfo.joinPaths(outputBaseDir, "modules", "othergenerator");
+ File.makePath(moduleDir);
+ var module = new TextFile(FileInfo.joinPaths(moduleDir, "module.qbs"), TextFile.WriteOnly);
+ module.writeLine("Module {");
+ module.writeLine(" Depends { name: 'cpp' }");
+ module.writeLine(" cpp.defines: 'MY_DEFINE=\"" + someDefines + "\"'");
+ module.writeLine("}");
+ module.close();
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/non-eager-provider/module-providers/provider_a.qbs b/tests/auto/blackbox/testdata-providers/non-eager-provider/module-providers/provider_a.qbs
new file mode 100644
index 000000000..6cd9177db
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/non-eager-provider/module-providers/provider_a.qbs
@@ -0,0 +1,11 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ isEager: false
+ relativeSearchPaths: {
+ if (moduleName === "nonexistentmodule")
+ return undefined;
+ Helpers.writeModule(outputBaseDir, moduleName, "from_provider_a");
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/non-eager-provider/non-eager-provider.qbs b/tests/auto/blackbox/testdata-providers/non-eager-provider/non-eager-provider.qbs
new file mode 100644
index 000000000..bd3662de3
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/non-eager-provider/non-eager-provider.qbs
@@ -0,0 +1,13 @@
+Project {
+ Product {
+ name: "p1"
+ Depends { name: "qbsmetatestmodule" }
+ Depends { name: "qbsothermodule" }
+ Depends { name: "nonexistentmodule"; required: false }
+ property bool dummy: {
+ console.info("p1.qbsmetatestmodule.prop: " + qbsmetatestmodule.prop);
+ console.info("p1.qbsothermodule.prop: " + qbsothermodule.prop);
+ }
+ qbsModuleProviders: "provider_a"
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/probe-in-module-provider/module-providers/provider_a.qbs b/tests/auto/blackbox/testdata-providers/probe-in-module-provider/module-providers/provider_a.qbs
new file mode 100644
index 000000000..476a83143
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/probe-in-module-provider/module-providers/provider_a.qbs
@@ -0,0 +1,23 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ property string sysroot: qbs.sysroot
+ Probe {
+ id: theProbe
+ property string theValue: "value"
+ property string dummy: sysroot
+ configure: {
+ console.info("Running probe with irrelevant value '" + dummy + "'");
+ found = true;
+ }
+ }
+ isEager: false
+ property bool found: theProbe.found
+ property string theValue: theProbe.theValue
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsmetatestmodule", theValue, undefined, found);
+ if (sysroot !== qbs.sysroot)
+ throw "this is unexpected";
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/probe-in-module-provider/probe-in-module-provider.qbs b/tests/auto/blackbox/testdata-providers/probe-in-module-provider/probe-in-module-provider.qbs
new file mode 100644
index 000000000..1f2b3d387
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/probe-in-module-provider/probe-in-module-provider.qbs
@@ -0,0 +1,9 @@
+Product {
+ qbsModuleProviders: ["provider_a"]
+ name: "p"
+ Depends { name: "qbsmetatestmodule" }
+ property bool dummy: {
+ console.info("p.qbsmetatestmodule.boolProp: " + JSON.stringify(qbsmetatestmodule.boolProp));
+ console.info("p.qbsmetatestmodule.prop: " + JSON.stringify(qbsmetatestmodule.prop));
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/providers-properties/module-providers/provider_a.qbs b/tests/auto/blackbox/testdata-providers/providers-properties/module-providers/provider_a.qbs
new file mode 100644
index 000000000..ab9d475d8
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/providers-properties/module-providers/provider_a.qbs
@@ -0,0 +1,9 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ property stringList someProp: "provider_a"
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsmetatestmodule", undefined, someProp);
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/providers-properties/module-providers/provider_b.qbs b/tests/auto/blackbox/testdata-providers/providers-properties/module-providers/provider_b.qbs
new file mode 100644
index 000000000..1b2a79979
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/providers-properties/module-providers/provider_b.qbs
@@ -0,0 +1,9 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ property stringList someProp: "provider_b"
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsothermodule", undefined, someProp);
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/providers-properties/providers-properties.qbs b/tests/auto/blackbox/testdata-providers/providers-properties/providers-properties.qbs
new file mode 100644
index 000000000..258a973fa
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/providers-properties/providers-properties.qbs
@@ -0,0 +1,12 @@
+Product {
+ qbsModuleProviders: ["provider_a", "provider_b"]
+ name: "p"
+ Depends { name: "qbsmetatestmodule" }
+ Depends { name: "qbsothermodule" }
+ moduleProviders.provider_a.someProp: "someValue"
+ property bool dummy: {
+ console.info("p.qbsmetatestmodule.listProp: "
+ + JSON.stringify(qbsmetatestmodule.listProp));
+ console.info("p.qbsothermodule.listProp: " + JSON.stringify(qbsothermodule.listProp));
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-properties-in-providers/module-providers/provider_a.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-properties-in-providers/module-providers/provider_a.qbs
new file mode 100644
index 000000000..95c89cd1c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-properties-in-providers/module-providers/provider_a.qbs
@@ -0,0 +1,9 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ property string sysroot: qbs.sysroot
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsmetatestmodule", sysroot);
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-properties-in-providers/qbs-module-properties-in-providers.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-properties-in-providers/qbs-module-properties-in-providers.qbs
new file mode 100644
index 000000000..c2fc58299
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-properties-in-providers/qbs-module-properties-in-providers.qbs
@@ -0,0 +1,34 @@
+Project {
+ qbsModuleProviders: "provider_a"
+ name: "project"
+
+ Profile {
+ name: "profile1"
+ qbs.sysroot: "/sysroot1"
+ }
+
+ Profile {
+ name: "profile2"
+ qbs.sysroot: "/sysroot2"
+ }
+
+ Product {
+ name: "product1"
+ Depends { name: "qbsmetatestmodule" }
+ property bool dummy: {
+ console.info("product1.qbsmetatestmodule.prop: " + qbsmetatestmodule.prop);
+ }
+ // multiplex over profiles, sysroot should not be cached
+ qbs.profiles: ["profile1", "profile2"]
+ }
+
+ Product {
+ name: "product2"
+ Depends { name: "qbsmetatestmodule" }
+ property bool dummy: {
+ console.info("product2.qbsmetatestmodule.prop: " + qbsmetatestmodule.prop);
+ }
+ // multiplex over profiles, sysroot should not be cached
+ qbs.profiles: ["profile1", "profile2"]
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/module-providers/provider_a.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/module-providers/provider_a.qbs
new file mode 100644
index 000000000..d34d1cac5
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/module-providers/provider_a.qbs
@@ -0,0 +1,8 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsmetatestmodule", "from_provider_a");
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/module-providers/provider_b.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/module-providers/provider_b.qbs
new file mode 100644
index 000000000..767e30923
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/module-providers/provider_b.qbs
@@ -0,0 +1,9 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsmetatestmodule", "from_provider_b");
+ Helpers.writeModule(outputBaseDir, "qbsothermodule", "from_provider_b");
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/qbs-module-providers-cli-override.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/qbs-module-providers-cli-override.qbs
new file mode 100644
index 000000000..6f94ab207
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers-cli-override/qbs-module-providers-cli-override.qbs
@@ -0,0 +1,13 @@
+Project {
+ name: "project"
+ Project {
+ name: "innerProject"
+ Product {
+ name: "product"
+ Depends { name: "qbsmetatestmodule"; required: false }
+ property bool dummy: {
+ console.info("qbsmetatestmodule.prop: " + qbsmetatestmodule.prop);
+ }
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/module-providers/named_provider.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/module-providers/named_provider.qbs
new file mode 100644
index 000000000..07114b5ef
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/module-providers/named_provider.qbs
@@ -0,0 +1,8 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsmetatestmodule", "from_named_provider");
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/module-providers/qbsmetatestmodule/provider.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/module-providers/qbsmetatestmodule/provider.qbs
new file mode 100644
index 000000000..b04a52261
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/module-providers/qbsmetatestmodule/provider.qbs
@@ -0,0 +1,8 @@
+import "../../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsmetatestmodule", "from_scoped_provider");
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/qbs-module-providers-compatibility.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/qbs-module-providers-compatibility.qbs
new file mode 100644
index 000000000..7885b540a
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers-compatibility/qbs-module-providers-compatibility.qbs
@@ -0,0 +1,7 @@
+Product {
+ name: "p"
+ Depends { name: "qbsmetatestmodule" }
+ property bool dummy: {
+ console.info("qbsmetatestmodule.prop: " + qbsmetatestmodule.prop);
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers-helpers.js b/tests/auto/blackbox/testdata-providers/qbs-module-providers-helpers.js
new file mode 100644
index 000000000..8b6d9e275
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers-helpers.js
@@ -0,0 +1,23 @@
+var File = require("qbs.File");
+var FileInfo = require("qbs.FileInfo");
+var TextFile = require("qbs.TextFile");
+var ModUtils = require("qbs.ModUtils");
+
+function writeModule(outputBaseDir, name, prop, listProp, boolProp) {
+ console.info("Running setup script for " + name);
+ var moduleDir = FileInfo.joinPaths(outputBaseDir, "modules", name);
+ File.makePath(moduleDir);
+ var module = new TextFile(FileInfo.joinPaths(moduleDir, "module.qbs"), TextFile.WriteOnly);
+ module.writeLine("Module {");
+ module.writeLine(" property string prop: " + ModUtils.toJSLiteral(prop));
+ if (listProp) {
+ module.writeLine(" property stringList listProp: "
+ + ModUtils.toJSLiteral(listProp));
+ }
+ if (boolProp) {
+ module.writeLine(" property bool boolProp: "
+ + ModUtils.toJSLiteral(boolProp));
+ }
+ module.writeLine("}");
+ module.close();
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers/module-providers/provider_a.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-providers/module-providers/provider_a.qbs
new file mode 100644
index 000000000..d34d1cac5
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers/module-providers/provider_a.qbs
@@ -0,0 +1,8 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsmetatestmodule", "from_provider_a");
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers/module-providers/provider_b.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-providers/module-providers/provider_b.qbs
new file mode 100644
index 000000000..767e30923
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers/module-providers/provider_b.qbs
@@ -0,0 +1,9 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, "qbsmetatestmodule", "from_provider_b");
+ Helpers.writeModule(outputBaseDir, "qbsothermodule", "from_provider_b");
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers/qbs-module-providers.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-providers/qbs-module-providers.qbs
new file mode 100644
index 000000000..00776a62e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers/qbs-module-providers.qbs
@@ -0,0 +1,28 @@
+Project {
+ qbsModuleProviders: "provider_a"
+ property stringList wantedProviders: qbsModuleProviders
+ name: "project"
+ Project {
+ name: "innerProject"
+ qbsModuleProviders: project.wantedProviders
+ Product {
+ name: "p1"
+ Depends { name: "qbsmetatestmodule" }
+ Depends { name: "qbsothermodule"; required: false }
+ property bool dummy: {
+ console.info("p1.qbsmetatestmodule.prop: " + qbsmetatestmodule.prop);
+ console.info("p1.qbsothermodule.prop: " + qbsothermodule.prop);
+ }
+ }
+ }
+
+ Product {
+ name: "p2"
+ Depends { name: "qbsmetatestmodule" }
+ Depends { name: "qbsothermodule"; required: false }
+ property bool dummy: {
+ console.info("p2.qbsmetatestmodule.prop: " + qbsmetatestmodule.prop);
+ console.info("p2.qbsothermodule.prop: " + qbsothermodule.prop);
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libA.cpp b/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libA.cpp
new file mode 100644
index 000000000..0c5274415
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libA.cpp
@@ -0,0 +1,14 @@
+#include "libA.h"
+
+#include <iostream>
+
+void foo()
+{
+ std::cout << "hello from foo: ";
+#ifdef MYLIB_FRAMEWORK
+ std::cout << "bundled: yes";
+#else
+ std::cout << "bundled: no";
+#endif
+ std::cout << std::endl;
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libA.h b/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libA.h
new file mode 100644
index 000000000..ddaaf1609
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libA.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#if defined(_WIN32) || defined(WIN32)
+# define DECL_EXPORT __declspec(dllexport)
+# define DECL_IMPORT __declspec(dllimport)
+#else
+# define DECL_EXPORT __attribute__((visibility("default")))
+# define DECL_IMPORT __attribute__((visibility("default")))
+# endif
+
+#if defined(LIBA_STATIC_LIBRARY)
+# define LIBA_EXPORT
+#else
+# if defined(MYLIB_LIBRARY)
+# define LIBA_EXPORT DECL_EXPORT
+# else
+# define LIBA_EXPORT DECL_IMPORT
+# endif
+#endif
+
+LIBA_EXPORT void foo();
diff --git a/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libs.qbs b/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libs.qbs
new file mode 100644
index 000000000..b473083c6
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/libs/libs.qbs
@@ -0,0 +1,42 @@
+import qbs.FileInfo
+
+Project {
+ property bool isBundle: false
+
+ DynamicLibrary {
+ Depends { name: "cpp" }
+ Depends { name: "bundle" }
+ Depends { name: "Exporter.pkgconfig" }
+ Exporter.pkgconfig.versionEntry: "1.0"
+ name: "libA"
+ bundle.isBundle: project.isBundle
+ bundle.publicHeaders: ["libA.h"]
+ files: "libA.cpp"
+ cpp.defines: {
+ var result = [];
+ if (project.isBundle)
+ result.push("MYLIB_FRAMEWORK");
+ return result;
+ }
+ qbs.installPrefix: "/usr"
+ install: true
+ installImportLib: true
+ installDir: "lib"
+ Group {
+ files: ["libA.h"]
+ qbs.install: !project.isBundle
+ qbs.installDir: FileInfo.joinPaths("include", product.name)
+ }
+ Group {
+ fileTagsFilter: ["Exporter.pkgconfig.pc"]
+ qbs.install: !project.isBundle
+ qbs.installDir: FileInfo.joinPaths("share", "pkgconfig")
+ }
+ Export {
+ Depends { name: "cpp" }
+ cpp.defines: ["THE_MAGIC_DEFINE"]
+ cpp.includePaths: [FileInfo.joinPaths(exportingProduct.qbs.installPrefix, "include")]
+ cpp.libraryPaths: [FileInfo.joinPaths(exportingProduct.qbs.installPrefix, "lib")]
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/main.cpp b/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/main.cpp
new file mode 100644
index 000000000..5fa0f7eed
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/main.cpp
@@ -0,0 +1,11 @@
+#include <libA/libA.h>
+
+#ifndef THE_MAGIC_DEFINE
+#error "missing the magic define"
+#endif
+
+int main()
+{
+ foo();
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/qbspkgconfig-module-provider.qbs b/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/qbspkgconfig-module-provider.qbs
new file mode 100644
index 000000000..d2b3654ae
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/qbspkgconfig-module-provider/qbspkgconfig-module-provider.qbs
@@ -0,0 +1,6 @@
+CppApplication {
+ name: "p"
+ Depends { name: "libA" }
+ files: "main.cpp"
+ qbsModuleProviders: "qbspkgconfig"
+}
diff --git a/tests/auto/blackbox/testdata-providers/removal-version/module-providers/provider_a.qbs b/tests/auto/blackbox/testdata-providers/removal-version/module-providers/provider_a.qbs
new file mode 100644
index 000000000..7f358acbc
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/removal-version/module-providers/provider_a.qbs
@@ -0,0 +1,14 @@
+import "../../qbs-module-providers-helpers.js" as Helpers
+
+ModuleProvider {
+ isEager: false
+ property bool deprecated: false
+ PropertyOptions {
+ name: "deprecated"
+ removalVersion: "2.2.0"
+ }
+ relativeSearchPaths: {
+ Helpers.writeModule(outputBaseDir, moduleName, "from_provider_a");
+ return "";
+ }
+}
diff --git a/tests/auto/blackbox/testdata-providers/removal-version/removal-version.qbs b/tests/auto/blackbox/testdata-providers/removal-version/removal-version.qbs
new file mode 100644
index 000000000..1aa5e2ce9
--- /dev/null
+++ b/tests/auto/blackbox/testdata-providers/removal-version/removal-version.qbs
@@ -0,0 +1,12 @@
+Project {
+ qbsModuleProviders: "provider_a"
+ name: "project"
+ Project {
+ name: "innerProject"
+ Product {
+ name: "p1"
+ Depends { name: "qbsmetatestmodule" }
+ }
+ }
+
+}
diff --git a/tests/auto/blackbox/testdata-qt/auto-qrc/auto-qrc.qbs b/tests/auto/blackbox/testdata-qt/auto-qrc/auto-qrc.qbs
index e08f89032..bdbf8b2a1 100644
--- a/tests/auto/blackbox/testdata-qt/auto-qrc/auto-qrc.qbs
+++ b/tests/auto/blackbox/testdata-qt/auto-qrc/auto-qrc.qbs
@@ -1,5 +1,14 @@
+import qbs.Host
+
Project {
QtApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
+
name: "app"
files: ["main.cpp"]
diff --git a/tests/auto/blackbox/testdata-qt/dbus-adaptors/car.cpp b/tests/auto/blackbox/testdata-qt/dbus-adaptors/car.cpp
index 5e4f348d2..ce1d33ed6 100644
--- a/tests/auto/blackbox/testdata-qt/dbus-adaptors/car.cpp
+++ b/tests/auto/blackbox/testdata-qt/dbus-adaptors/car.cpp
@@ -49,7 +49,9 @@
****************************************************************************/
#include "car.h"
-#include <QtWidgets/QtWidgets>
+
+#include <QtGui/QPainter>
+
#include <math.h>
static const double Pi = 3.14159265358979323846264338327950288419717;
diff --git a/tests/auto/blackbox/testdata-qt/dbus-adaptors/main.cpp b/tests/auto/blackbox/testdata-qt/dbus-adaptors/main.cpp
index 0491719d7..197005215 100644
--- a/tests/auto/blackbox/testdata-qt/dbus-adaptors/main.cpp
+++ b/tests/auto/blackbox/testdata-qt/dbus-adaptors/main.cpp
@@ -50,9 +50,15 @@
#include "car.h"
#include "car_adaptor.h"
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
#include <QtWidgets/QApplication>
#include <QtWidgets/QGraphicsView>
#include <QtWidgets/QGraphicsScene>
+#else
+#include <QApplication>
+#include <QGraphicsView>
+#include <QGraphicsScene>
+#endif
#include <QtDBus/QDBusConnection>
int main(int argc, char *argv[])
diff --git a/tests/auto/blackbox/testdata-qt/dbus-interfaces/controller.cpp b/tests/auto/blackbox/testdata-qt/dbus-interfaces/controller.cpp
index eaff5c775..0f7a77efa 100644
--- a/tests/auto/blackbox/testdata-qt/dbus-interfaces/controller.cpp
+++ b/tests/auto/blackbox/testdata-qt/dbus-interfaces/controller.cpp
@@ -48,7 +48,8 @@
**
****************************************************************************/
-#include <QtWidgets>
+#include <QtGlobal>
+#include <QtDBus/QDBusConnection>
#include "controller.h"
#include "car_interface.h"
diff --git a/tests/auto/blackbox/testdata-qt/dbus-interfaces/main.cpp b/tests/auto/blackbox/testdata-qt/dbus-interfaces/main.cpp
index fdd9fc590..c4e6ee485 100644
--- a/tests/auto/blackbox/testdata-qt/dbus-interfaces/main.cpp
+++ b/tests/auto/blackbox/testdata-qt/dbus-interfaces/main.cpp
@@ -48,7 +48,12 @@
**
****************************************************************************/
-#include <QtWidgets>
+#include <QtGlobal>
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
+#include <QtWidgets/QApplication>
+#else
+#include <QtGui/QApplication>>
+#endif
#include <QtDBus>
#include "controller.h"
diff --git a/tests/auto/blackbox/testdata-qt/forced-moc/forced-moc.qbs b/tests/auto/blackbox/testdata-qt/forced-moc/forced-moc.qbs
index a59aaa28e..064470a79 100644
--- a/tests/auto/blackbox/testdata-qt/forced-moc/forced-moc.qbs
+++ b/tests/auto/blackbox/testdata-qt/forced-moc/forced-moc.qbs
@@ -1,4 +1,17 @@
+import qbs.Host
+import qbs.Utilities
+
QtApplication {
+ condition: {
+ if (Utilities.versionCompare(Qt.core.version, "5.0") < 0) {
+ console.info("using qt4");
+ return false;
+ }
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
files: "main.cpp"
Group {
name: "QObject service provider"
diff --git a/tests/auto/blackbox/testdata-qt/included-moc-cpp/included-moc-cpp.qbs b/tests/auto/blackbox/testdata-qt/included-moc-cpp/included-moc-cpp.qbs
index 1ed85ccdd..265b0c9e3 100644
--- a/tests/auto/blackbox/testdata-qt/included-moc-cpp/included-moc-cpp.qbs
+++ b/tests/auto/blackbox/testdata-qt/included-moc-cpp/included-moc-cpp.qbs
@@ -1,6 +1,13 @@
-import qbs
+import qbs.Utilities
QtApplication {
+ condition: {
+ if (Utilities.versionCompare(Qt.core.version, "5.0") < 0) {
+ console.info("using qt4");
+ return false;
+ }
+ return true;
+ }
files: [
"main.cpp",
"myobject.cpp",
diff --git a/tests/auto/blackbox/testdata-qt/linker-variant/qt-linker-variant.qbs b/tests/auto/blackbox/testdata-qt/linker-variant/qt-linker-variant.qbs
index ab5889007..c1a77d696 100644
--- a/tests/auto/blackbox/testdata-qt/linker-variant/qt-linker-variant.qbs
+++ b/tests/auto/blackbox/testdata-qt/linker-variant/qt-linker-variant.qbs
@@ -3,7 +3,7 @@ QtApplication {
id: qtConfigProbe
property stringList moduleConfig: Qt.core.moduleConfig
configure: {
- console.info("Qt requires gold: " + moduleConfig.contains("use_gold_linker"));
+ console.info("Qt requires gold: " + moduleConfig.includes("use_gold_linker"));
}
}
files: "main.cpp"
diff --git a/tests/auto/blackbox/testdata-qt/metatypes/metatypes.qbs b/tests/auto/blackbox/testdata-qt/metatypes/metatypes.qbs
new file mode 100644
index 000000000..bbc98c934
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/metatypes/metatypes.qbs
@@ -0,0 +1,28 @@
+import qbs.Utilities
+
+StaticLibrary {
+ name: "mylib"
+
+ Depends { name: "Qt.core" }
+
+ qbs.installPrefix: "some-prefix"
+
+ Probe {
+ id: capabilitiesChecker
+ property string version: Qt.core.version
+ configure: {
+ if (Utilities.versionCompare(version, "5.15") >= 0)
+ console.info("can generate");
+ else
+ console.info("cannot generate");
+ found = true;
+ }
+ }
+
+ files: [
+ "mocableclass1.cpp",
+ "mocableclass1.h",
+ "mocableclass2.cpp",
+ "unmocableclass.cpp",
+ ]
+}
diff --git a/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.cpp b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.cpp
new file mode 100644
index 000000000..06adc8ca5
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.cpp
@@ -0,0 +1,3 @@
+#include "mocableclass1.h"
+
+MocableClass1::MocableClass1(QObject *parent) : QObject(parent) {}
diff --git a/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.h b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.h
new file mode 100644
index 000000000..020c15179
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass1.h
@@ -0,0 +1,8 @@
+#include <QObject>
+
+class MocableClass1 : public QObject
+{
+ Q_OBJECT
+public:
+ MocableClass1(QObject *parent = nullptr);
+};
diff --git a/tests/auto/blackbox/testdata-qt/metatypes/mocableclass2.cpp b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass2.cpp
new file mode 100644
index 000000000..bf538913a
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/metatypes/mocableclass2.cpp
@@ -0,0 +1,10 @@
+#include <QObject>
+
+class MocableClass2 : public QObject
+{
+ Q_OBJECT
+public:
+ MocableClass2(QObject *parent) : QObject(parent) {}
+};
+
+#include <mocableclass2.moc>
diff --git a/tests/auto/blackbox/testdata-qt/metatypes/unmocableclass.cpp b/tests/auto/blackbox/testdata-qt/metatypes/unmocableclass.cpp
new file mode 100644
index 000000000..34330d189
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/metatypes/unmocableclass.cpp
@@ -0,0 +1,7 @@
+#include <QObject>
+
+class UnmocableClass : public QObject
+{
+public:
+ UnmocableClass(QObject *parent) : QObject(parent) {}
+};
diff --git a/tests/auto/blackbox/testdata-qt/mixed-build-variants/mixed-build-variants.qbs b/tests/auto/blackbox/testdata-qt/mixed-build-variants/mixed-build-variants.qbs
index 7d8ab1b90..dea30eef4 100644
--- a/tests/auto/blackbox/testdata-qt/mixed-build-variants/mixed-build-variants.qbs
+++ b/tests/auto/blackbox/testdata-qt/mixed-build-variants/mixed-build-variants.qbs
@@ -1,6 +1,6 @@
QtApplication {
Properties {
- condition: qbs.toolchain.contains("msvc")
+ condition: qbs.toolchain.includes("msvc")
Qt.core.qtBuildVariant: "release"
}
Qt.core.qtBuildVariant: "dummy"
diff --git a/tests/auto/blackbox/testdata-qt/moc-compiler-defines/main.cpp b/tests/auto/blackbox/testdata-qt/moc-compiler-defines/main.cpp
new file mode 100644
index 000000000..d3b8f310e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/moc-compiler-defines/main.cpp
@@ -0,0 +1,7 @@
+#include "object.h"
+
+int main()
+{
+ QObject o;
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata-qt/moc-compiler-defines/moc-compiler-defines.qbs b/tests/auto/blackbox/testdata-qt/moc-compiler-defines/moc-compiler-defines.qbs
new file mode 100644
index 000000000..e184f554e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/moc-compiler-defines/moc-compiler-defines.qbs
@@ -0,0 +1,3 @@
+QtApplication {
+ files: ["main.cpp", "object.h", "object.cpp"]
+}
diff --git a/tests/auto/blackbox/testdata-qt/moc-compiler-defines/object.cpp b/tests/auto/blackbox/testdata-qt/moc-compiler-defines/object.cpp
new file mode 100644
index 000000000..7ffcf419b
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/moc-compiler-defines/object.cpp
@@ -0,0 +1,6 @@
+#include "object.h"
+
+Object::Object(QObject *parent) : QObject(parent)
+{
+
+}
diff --git a/tests/auto/blackbox/testdata-qt/moc-compiler-defines/object.h b/tests/auto/blackbox/testdata-qt/moc-compiler-defines/object.h
new file mode 100644
index 000000000..3221d1173
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/moc-compiler-defines/object.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** 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 OBJECT_H
+#define OBJECT_H
+
+#include <QtCore/QObject>
+
+// These were not defined during the moc run (QBS-1592).
+// Do not use Q_OS_UNIX here as it is a fallback value which is defined when nothing else is.
+#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || defined(Q_OS_WIN)
+
+class Object : public QObject
+{
+ Q_OBJECT
+public:
+ explicit Object(QObject *parent = nullptr);
+};
+
+#endif
+
+#endif // OBJECT_H
diff --git a/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/lib.cpp b/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/lib.cpp
new file mode 100644
index 000000000..675960f2d
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/lib.cpp
@@ -0,0 +1,8 @@
+#include <QtDebug>
+
+#include "lib.h"
+
+SymbolsTest::SymbolsTest()
+{
+ // qDebug() << "hallo";
+}
diff --git a/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/lib.h b/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/lib.h
new file mode 100644
index 000000000..0cb4a23c4
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/lib.h
@@ -0,0 +1,18 @@
+#ifndef SYMBOLSTEST_H
+#define SYMBOLSTEST_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(SYMBOLSTEST_LIBRARY)
+# define SYMBOLSTEST_EXPORT Q_DECL_EXPORT
+#else
+# define SYMBOLSTEST_EXPORT Q_DECL_IMPORT
+#endif
+
+class SYMBOLSTEST_EXPORT SymbolsTest
+{
+ public:
+ SymbolsTest();
+};
+
+#endif // SYMBOLSTEST_H
diff --git a/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/main.cpp b/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/main.cpp
new file mode 100644
index 000000000..5047a34e3
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/main.cpp
@@ -0,0 +1,3 @@
+int main()
+{
+}
diff --git a/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/symbols-test.qbs b/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/symbols-test.qbs
new file mode 100644
index 000000000..a614d96c4
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/no-relink-on-qdebug/symbols-test.qbs
@@ -0,0 +1,27 @@
+Project {
+ CppApplication {
+ name: "app"
+ Depends { name: "lib" }
+ property bool dummy: {
+ console.info("is GCC: " + qbs.toolchain.includes("gcc"));
+ console.info("is MinGW: " + qbs.toolchain.includes("mingw"));
+ console.info("is Darwin: " + qbs.targetOS.includes("darwin"));
+ }
+ files: "main.cpp"
+ }
+
+ DynamicLibrary {
+ name: "lib"
+ Depends { name: "Qt.core" }
+
+ cpp.cxxLanguageVersion: "c++11"
+ cpp.defines: "SYMBOLSTEST_LIBRARY"
+
+ files: [
+ "lib.cpp",
+ "lib.h",
+ ]
+
+ Export { Depends { name: "Qt.core" } }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-qt/pkgconfig-qt/dump-libpath.qbs b/tests/auto/blackbox/testdata-qt/pkgconfig-qt/dump-libpath.qbs
new file mode 100644
index 000000000..9aa1c5d17
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/pkgconfig-qt/dump-libpath.qbs
@@ -0,0 +1,6 @@
+QtApplication {
+ files: "main.cpp"
+ property bool test: {
+ console.info("libPath="+Qt.core.libPath)
+ }
+}
diff --git a/tests/auto/blackbox/testdata-qt/pkgconfig-qt/main.cpp b/tests/auto/blackbox/testdata-qt/pkgconfig-qt/main.cpp
new file mode 100644
index 000000000..237c8ce18
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/pkgconfig-qt/main.cpp
@@ -0,0 +1 @@
+int main() {}
diff --git a/tests/auto/blackbox/testdata-qt/pkgconfig-qt/module-providers/dummyProvider.qbs b/tests/auto/blackbox/testdata-qt/pkgconfig-qt/module-providers/dummyProvider.qbs
new file mode 100644
index 000000000..6ed2ca82e
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/pkgconfig-qt/module-providers/dummyProvider.qbs
@@ -0,0 +1,3 @@
+ModuleProvider {
+ relativeSearchPaths: ""
+}
diff --git a/tests/auto/blackbox/testdata-qt/pkgconfig-qt/pkgconfig-qt.qbs b/tests/auto/blackbox/testdata-qt/pkgconfig-qt/pkgconfig-qt.qbs
new file mode 100644
index 000000000..a1d8d8974
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/pkgconfig-qt/pkgconfig-qt.qbs
@@ -0,0 +1,6 @@
+QtApplication {
+ name: "p"
+ files: "main.cpp"
+ qbsSearchPaths: "."
+ qbsModuleProviders: "qbspkgconfig"
+}
diff --git a/tests/auto/blackbox/testdata-qt/pkgconfig/pkgconfig.qbs b/tests/auto/blackbox/testdata-qt/pkgconfig/pkgconfig.qbs
index 04b0097ef..b2d411154 100644
--- a/tests/auto/blackbox/testdata-qt/pkgconfig/pkgconfig.qbs
+++ b/tests/auto/blackbox/testdata-qt/pkgconfig/pkgconfig.qbs
@@ -1,8 +1,15 @@
+import qbs.Host
import qbs.Probes
Project {
property string name: 'pkgconfig'
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: project.name
Probes.PkgConfigProbe {
id: pkgConfig
diff --git a/tests/auto/blackbox/testdata-qt/plugin-meta-data/plugin-meta-data.qbs b/tests/auto/blackbox/testdata-qt/plugin-meta-data/plugin-meta-data.qbs
index f018b34e0..dbe64d5ea 100644
--- a/tests/auto/blackbox/testdata-qt/plugin-meta-data/plugin-meta-data.qbs
+++ b/tests/auto/blackbox/testdata-qt/plugin-meta-data/plugin-meta-data.qbs
@@ -1,5 +1,20 @@
+import qbs.Host
+import qbs.Utilities
+
Project {
QtApplication {
+ condition: {
+ if (Utilities.versionCompare(Qt.core.version, "5.0") < 0) {
+ // qt4 moc can't be used with pluginMetaData
+ console.info("using qt4");
+ return false;
+ }
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
+
name: "app"
consoleApplication: true
@@ -8,7 +23,7 @@ Project {
cpp.cxxLanguageVersion: "c++11"
Properties {
- condition: qbs.targetOS.contains("unix")
+ condition: qbs.targetOS.includes("unix")
cpp.rpaths: [cpp.rpathOrigin]
}
@@ -28,12 +43,12 @@ Project {
Depends { name: "Qt.core" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
cpp.defines: [Qt.core.staticBuild ? "QT_STATICPLUGIN" : "QT_PLUGIN"]
cpp.cxxLanguageVersion: "c++11"
- cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+ cpp.sonamePrefix: qbs.targetOS.includes("darwin") ? "@rpath" : undefined
cpp.includePaths: ["."]
Qt.core.pluginMetaData: ["theKey=theValue"]
diff --git a/tests/auto/blackbox/testdata-qt/plugin-support/plugin-support.qbs b/tests/auto/blackbox/testdata-qt/plugin-support/plugin-support.qbs
index c554a7dc2..8e0923e94 100644
--- a/tests/auto/blackbox/testdata-qt/plugin-support/plugin-support.qbs
+++ b/tests/auto/blackbox/testdata-qt/plugin-support/plugin-support.qbs
@@ -1,4 +1,14 @@
+import qbs.Utilities
+
QtGuiApplication {
+ condition: {
+ // pluginTypes empty for Qt4
+ if (Utilities.versionCompare(Qt.core.version, "5.0") < 0) {
+ console.info("using qt4");
+ return false;
+ }
+ return true;
+ }
Probe {
id: staticProbe
property bool isStaticQt: Qt.gui.isStaticLibrary
diff --git a/tests/auto/blackbox/testdata-qt/qdoc/qdoc.qbs b/tests/auto/blackbox/testdata-qt/qdoc/qdoc.qbs
new file mode 100644
index 000000000..4b82bc405
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/qdoc/qdoc.qbs
@@ -0,0 +1,22 @@
+import qbs.Utilities
+
+Product {
+ condition: {
+ var ok = Utilities.versionCompare(Qt.core.version, "5.0.0") >= 0;
+ if (!ok)
+ console.info("Qt is too old");
+ return ok;
+ }
+ name: "QDoc Test"
+ type: ["qdoc-html", "qch"]
+
+ Depends { name: "Qt.core" }
+
+ files: ["qdoc.qdoc"]
+
+ Group {
+ name: "main qdocconf file"
+ files: "qdoc.qdocconf"
+ fileTags: "qdocconf-main"
+ }
+}
diff --git a/tests/auto/blackbox/testdata-qt/qdoc/qdoc.qdoc b/tests/auto/blackbox/testdata-qt/qdoc/qdoc.qdoc
new file mode 100644
index 000000000..9e44e0948
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/qdoc/qdoc.qdoc
@@ -0,0 +1,7 @@
+/*!
+ \page index.html
+
+ \title QDoc Test
+
+ QDoc Test is a test for QDoc.
+*/ \ No newline at end of file
diff --git a/tests/auto/blackbox/testdata-qt/qdoc/qdoc.qdocconf b/tests/auto/blackbox/testdata-qt/qdoc/qdoc.qdocconf
new file mode 100644
index 000000000..8c82bb575
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/qdoc/qdoc.qdocconf
@@ -0,0 +1,13 @@
+project = QDoc Test
+description = QDoc Test
+
+headerdirs = .
+sourcedirs = .
+exampledirs = .
+
+outputdir = doc/html
+
+qhp.projects = QDocTest
+qhp.QDocTest.file = qdoctest.qhp
+qhp.QDocTest.namespace = org.qt-project.QDocTest
+qhp.QDocTest.virtualFolder = doc
diff --git a/tests/auto/blackbox/testdata-qt/qml-debugging/qml-debugging.qbs b/tests/auto/blackbox/testdata-qt/qml-debugging/qml-debugging.qbs
index 8176a7c3e..0885e6b0b 100644
--- a/tests/auto/blackbox/testdata-qt/qml-debugging/qml-debugging.qbs
+++ b/tests/auto/blackbox/testdata-qt/qml-debugging/qml-debugging.qbs
@@ -4,4 +4,9 @@ QtApplication {
Depends { name: "Qt.quick" }
Qt.quick.qmlDebugging: true
files: "main.cpp"
+ Probe {
+ id: checker
+ property bool isGcc: qbs.toolchain.contains("gcc")
+ configure: { console.info("is gcc: " + isGcc); }
+ }
}
diff --git a/tests/auto/blackbox/testdata-qt/qmltyperegistrar/example.qml b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/example.qml
new file mode 100644
index 000000000..ef97df12d
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/example.qml
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $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$
+**
+****************************************************************************/
+
+// ![0]
+import People 1.0
+
+Person {
+ name: "Bob Jones"
+ shoeSize: 12
+}
+// ![0]
diff --git a/tests/auto/blackbox/testdata-qt/qmltyperegistrar/main.cpp b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/main.cpp
new file mode 100644
index 000000000..6c3920f04
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/main.cpp
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $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 <QCoreApplication>
+#include <QQmlEngine>
+#include <QQmlComponent>
+#include <QDebug>
+#include "person.h"
+
+int main(int argc, char ** argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QQmlEngine engine;
+ QQmlComponent component(&engine, QUrl("qrc:example.qml"));
+ auto *person = qobject_cast<Person *>(component.create());
+ if (person) {
+ qWarning() << "The person's name is" << person->name();
+ qWarning() << "They wear a" << person->shoeSize() << "sized shoe";
+ } else {
+ qWarning() << component.errors();
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.cpp b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.cpp
new file mode 100644
index 000000000..de4a33dd0
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.cpp
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $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 "person.h"
+
+// ![0]
+Person::Person(QObject *parent)
+: QObject(parent), m_shoeSize(0)
+{
+}
+
+QString Person::name() const
+{
+ return m_name;
+}
+
+void Person::setName(const QString &n)
+{
+ m_name = n;
+}
+
+int Person::shoeSize() const
+{
+ return m_shoeSize;
+}
+
+void Person::setShoeSize(int s)
+{
+ m_shoeSize = s;
+}
+
+// ![0]
diff --git a/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.h b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.h
new file mode 100644
index 000000000..530c335de
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/person.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $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 PERSON_H
+#define PERSON_H
+
+#include <QObject>
+#include <QtQml/qqml.h>
+
+//![0]
+class Person : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString name READ name WRITE setName)
+ Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize)
+ QML_ELEMENT
+public:
+ Person(QObject *parent = nullptr);
+
+ QString name() const;
+ void setName(const QString &);
+
+ int shoeSize() const;
+ void setShoeSize(int);
+
+private:
+ QString m_name;
+ int m_shoeSize;
+};
+//![0]
+
+#endif // PERSON_H
diff --git a/tests/auto/blackbox/testdata-qt/qmltyperegistrar/qmltyperegistrar.qbs b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/qmltyperegistrar.qbs
new file mode 100644
index 000000000..68dc83743
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/qmltyperegistrar/qmltyperegistrar.qbs
@@ -0,0 +1,33 @@
+import qbs.Utilities
+
+CppApplication {
+ name: "myapp"
+ Depends { name: "Qt.qml" }
+
+ Qt.qml.importVersion: "1"
+ cpp.includePaths: sourceDirectory
+ qbs.installPrefix: ""
+
+ files: [
+ "main.cpp",
+ "person.cpp",
+ "person.h",
+ ]
+
+ Group {
+ files: "example.qml"
+ fileTags: "qt.core.resource_data"
+ }
+
+ Probe {
+ id: versionProbe
+ property string version: Qt.core.version
+ configure: {
+ if (Utilities.versionCompare(version, "5.15") >= 0)
+ console.info("has registrar");
+ else
+ console.info("does not have registrar");
+ found = true;
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-qt/qrc/i.qbs b/tests/auto/blackbox/testdata-qt/qrc/i.qbs
index c005490c4..45275106d 100644
--- a/tests/auto/blackbox/testdata-qt/qrc/i.qbs
+++ b/tests/auto/blackbox/testdata-qt/qrc/i.qbs
@@ -1,5 +1,18 @@
+import qbs.Host
+import qbs.Utilities
+
Project {
Product {
+ condition: {
+ if (Utilities.versionCompare(Qt.core.version, "5.0") < 0) {
+ console.info("using qt4");
+ return false;
+ }
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
consoleApplication: true
type: "application"
name: "i"
diff --git a/tests/auto/blackbox/testdata-qt/qtscxml/qtscxml.qbs b/tests/auto/blackbox/testdata-qt/qtscxml/qtscxml.qbs
index 991e4ddcb..6fee91479 100644
--- a/tests/auto/blackbox/testdata-qt/qtscxml/qtscxml.qbs
+++ b/tests/auto/blackbox/testdata-qt/qtscxml/qtscxml.qbs
@@ -1,5 +1,6 @@
import qbs.Environment
import qbs.FileInfo
+import qbs.Host
import qbs.Utilities
Project {
@@ -24,6 +25,12 @@ Project {
}
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "runner"
type: ["runner"]
Depends { name: "app" }
@@ -33,18 +40,22 @@ Project {
prepare: {
var cmd = new Command(input.filePath);
cmd.description = "running " + input.filePath;
- var pathVar;
- var pathValue;
- if (product.qbs.hostOS.contains("windows")) {
- pathVar = "PATH";
- pathValue = FileInfo.toWindowsSeparators(input["Qt.core"].binPath);
+
+ var envVars = {};
+ if (Host.os().includes("windows")) {
+ envVars["PATH"] = FileInfo.toWindowsSeparators(input["Qt.core"].binPath);
+ } else if (Host.os().includes("macos")) {
+ envVars["DYLD_LIBRARY_PATH"] = input["Qt.core"].libPath;
+ envVars["DYLD_FRAMEWORK_PATH"] = input["Qt.core"].libPath;
} else {
- pathVar = "LD_LIBRARY_PATH";
- pathValue = input["Qt.core"].libPath;
+ envVars["LD_LIBRARY_PATH"] = input["Qt.core"].libPath;
}
- var oldValue = Environment.getEnv(pathVar) || "";
- var newValue = pathValue + product.qbs.pathListSeparator + oldValue;
- cmd.environment = [pathVar + '=' + newValue];
+ for (var varName in envVars) {
+ var oldValue = Environment.getEnv(varName) || "";
+ var newValue = envVars[varName] + FileInfo.pathListSeparator() + oldValue;
+ cmd.environment.push(varName + '=' + newValue);
+ }
+
return [cmd];
}
}
diff --git a/tests/auto/blackbox/testdata-qt/quick-compiler/quick-compiler.qbs b/tests/auto/blackbox/testdata-qt/quick-compiler/quick-compiler.qbs
index b141c8672..793d261e9 100644
--- a/tests/auto/blackbox/testdata-qt/quick-compiler/quick-compiler.qbs
+++ b/tests/auto/blackbox/testdata-qt/quick-compiler/quick-compiler.qbs
@@ -1,5 +1,9 @@
CppApplication {
- Depends { name: "Qt.quick" }
+ Depends {
+ name: "Qt.quick"
+ // Must fail when using Qt4
+ versionAtLeast: "5"
+ }
Qt.quick.useCompiler: Qt.quick.compilerAvailable
cpp.cxxLanguageVersion: "c++11"
diff --git a/tests/auto/blackbox/testdata-qt/static-qt-plugin-linking/lib.cpp b/tests/auto/blackbox/testdata-qt/static-qt-plugin-linking/lib.cpp
new file mode 100644
index 000000000..dd79cbe6c
--- /dev/null
+++ b/tests/auto/blackbox/testdata-qt/static-qt-plugin-linking/lib.cpp
@@ -0,0 +1 @@
+void function() {}
diff --git a/tests/auto/blackbox/testdata-qt/static-qt-plugin-linking/static-qt-plugin-linking.qbs b/tests/auto/blackbox/testdata-qt/static-qt-plugin-linking/static-qt-plugin-linking.qbs
index 745fe5527..e4e56bb4b 100644
--- a/tests/auto/blackbox/testdata-qt/static-qt-plugin-linking/static-qt-plugin-linking.qbs
+++ b/tests/auto/blackbox/testdata-qt/static-qt-plugin-linking/static-qt-plugin-linking.qbs
@@ -11,11 +11,16 @@ Product {
}
Group {
- condition: type.contains("application")
+ condition: type.includes("application")
files: "main.cpp"
}
+ Group {
+ condition: type.includes("staticlibrary")
+ files: "lib.cpp"
+ }
+
Depends { name: "Qt.core" }
Depends { name: "Qt.gui" }
- Depends { name: "Qt.qminimal"; condition: Qt.core.staticBuild; }
+ Depends { name: "Qt.qminimal"; condition: Qt.core.staticBuild }
}
diff --git a/tests/auto/blackbox/testdata-windows/codesign/app.cpp b/tests/auto/blackbox/testdata-windows/codesign/app.cpp
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/blackbox/testdata-windows/codesign/app.cpp
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/blackbox/testdata-windows/codesign/codesign.qbs b/tests/auto/blackbox/testdata-windows/codesign/codesign.qbs
new file mode 100644
index 000000000..1963f6926
--- /dev/null
+++ b/tests/auto/blackbox/testdata-windows/codesign/codesign.qbs
@@ -0,0 +1,43 @@
+Project {
+ name: "p"
+
+ property bool enableSigning: true
+ property string hashAlgorithm
+ property string subjectName
+ property string signingTimestamp
+
+ CppApplication {
+ name: "A"
+ files: "app.cpp"
+ condition: qbs.toolchain.includes("msvc")
+ codesign.enableCodeSigning: project.enableSigning
+ codesign.hashAlgorithm: project.hashAlgorithm
+ codesign.subjectName: project.subjectName
+ codesign.signingTimestamp: project.signingTimestamp
+ codesign.timestampAlgorithm: "sha256"
+ install: true
+ installDir: ""
+ property bool dummy: {
+ if (codesign.codesignPath)
+ console.info("signtool path: %%" + codesign.codesignPath + "%%");
+ }
+ }
+
+ DynamicLibrary {
+ Depends { name: "cpp" }
+ name: "B"
+ files: "app.cpp"
+ condition: qbs.toolchain.includes("msvc")
+ codesign.enableCodeSigning: project.enableSigning
+ codesign.hashAlgorithm: project.hashAlgorithm
+ codesign.subjectName: project.subjectName
+ codesign.signingTimestamp: project.signingTimestamp
+ codesign.timestampAlgorithm: "sha256"
+ install: true
+ installDir: ""
+ property bool dummy: {
+ if (codesign.codesignPath)
+ console.info("signtool path: %%" + codesign.codesignPath + "%%");
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata-windows/innosetup/inc/qbsinc.iss b/tests/auto/blackbox/testdata-windows/innosetup/inc/qbsinc.iss
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata-windows/innosetup/inc/qbsinc.iss
diff --git a/tests/auto/blackbox/testdata-windows/innosetup/innosetup.qbs b/tests/auto/blackbox/testdata-windows/innosetup/innosetup.qbs
new file mode 100644
index 000000000..fd8868900
--- /dev/null
+++ b/tests/auto/blackbox/testdata-windows/innosetup/innosetup.qbs
@@ -0,0 +1,25 @@
+import qbs.FileInfo
+
+Project {
+ InnoSetup {
+ property bool _test: {
+ var present = qbs.targetOS.includes("windows") && innosetup.present;
+ console.info("has innosetup: " + present);
+ }
+
+ 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-windows/innosetup/test.iss
index f9f9195a6..f9f9195a6 100644
--- a/tests/auto/blackbox/testdata/innosetup/test.iss
+++ b/tests/auto/blackbox/testdata-windows/innosetup/test.iss
diff --git a/tests/auto/blackbox/testdata-windows/innosetupDependencies/innosetupDependencies.qbs b/tests/auto/blackbox/testdata-windows/innosetupDependencies/innosetupDependencies.qbs
new file mode 100644
index 000000000..3ef8c30e7
--- /dev/null
+++ b/tests/auto/blackbox/testdata-windows/innosetupDependencies/innosetupDependencies.qbs
@@ -0,0 +1,76 @@
+import qbs.TextFile
+
+Project {
+ InnoSetup {
+ property bool _test: {
+ var present = qbs.targetOS.includes("windows") && innosetup.present;
+ console.info("has innosetup: " + present);
+ }
+ Depends { name: "app" }
+ Depends { name: "lib" }
+ name: "QbsSetup"
+ targetName: "qbs.setup.test"
+ version: "1.5"
+ files: [
+ "test.iss"
+ ]
+ innosetup.verboseOutput: true
+ innosetup.defines: [
+ "MyProgram=" + name,
+ "MyProgramVersion=" + version,
+ "buildDirectory=" + project.buildDirectory
+ ]
+ innosetup.compilerFlags: ["/V9"]
+ destinationDirectory: project.buildDirectory
+ }
+ Application {
+ Depends { name: "cpp" }
+ name: "app"
+ files: ["main.c"]
+ Group {
+ fileTagsFilter: product.type
+ qbs.install: true
+ }
+ destinationDirectory: project.buildDirectory
+ }
+ DynamicLibrary {
+ Depends { name: "cpp" }
+ name: "lib"
+ files: ["main.c"]
+ Group {
+ fileTagsFilter: product.type
+ qbs.install: true
+ }
+ Rule {
+ // This rule tries to provoke the installer into building too early (and the test
+ // verifies that it does not) by causing the build of the installables to take
+ // a lot longer.
+ multiplex: true
+ outputFileTags: ["c"]
+ outputArtifacts: {
+ var artifacts = [];
+ for (var i = 0; i < 96; ++i)
+ artifacts.push({ filePath: "c" + i + ".c", fileTags: ["c"] });
+ return artifacts;
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ for (var i = 0; i < outputs["c"].length; ++i) {
+ var tf;
+ try {
+ tf = new TextFile(outputs["c"][i].filePath, TextFile.WriteOnly);
+ tf.writeLine("int main" + i + "() { return 0; }");
+ } finally {
+ if (tf)
+ tf.close();
+ }
+ }
+ };
+ return [cmd];
+ }
+ }
+ destinationDirectory: project.buildDirectory
+ }
+}
diff --git a/tests/auto/blackbox/testdata-windows/innosetupDependencies/main.c b/tests/auto/blackbox/testdata-windows/innosetupDependencies/main.c
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/blackbox/testdata-windows/innosetupDependencies/main.c
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/blackbox/testdata/innosetupDependencies/test.iss b/tests/auto/blackbox/testdata-windows/innosetupDependencies/test.iss
index 430f9941b..430f9941b 100644
--- a/tests/auto/blackbox/testdata/innosetupDependencies/test.iss
+++ b/tests/auto/blackbox/testdata-windows/innosetupDependencies/test.iss
diff --git a/tests/auto/blackbox/testdata/wix/ExampleScript.bat b/tests/auto/blackbox/testdata-windows/wix/ExampleScript.bat
index 3af583cd8..3af583cd8 100644
--- a/tests/auto/blackbox/testdata/wix/ExampleScript.bat
+++ b/tests/auto/blackbox/testdata-windows/wix/ExampleScript.bat
diff --git a/tests/auto/blackbox/testdata/wix/QbsBootstrapper.wxs b/tests/auto/blackbox/testdata-windows/wix/QbsBootstrapper.wxs
index 272f6af5b..272f6af5b 100644
--- a/tests/auto/blackbox/testdata/wix/QbsBootstrapper.wxs
+++ b/tests/auto/blackbox/testdata-windows/wix/QbsBootstrapper.wxs
diff --git a/tests/auto/blackbox/testdata/wix/QbsSetup.wxs b/tests/auto/blackbox/testdata-windows/wix/QbsSetup.wxs
index 8f97ff667..8f97ff667 100644
--- a/tests/auto/blackbox/testdata/wix/QbsSetup.wxs
+++ b/tests/auto/blackbox/testdata-windows/wix/QbsSetup.wxs
diff --git a/tests/auto/blackbox/testdata/wix/Qt.wxs b/tests/auto/blackbox/testdata-windows/wix/Qt.wxs
index fbd992c43..fbd992c43 100644
--- a/tests/auto/blackbox/testdata/wix/Qt.wxs
+++ b/tests/auto/blackbox/testdata-windows/wix/Qt.wxs
diff --git a/tests/auto/blackbox/testdata-windows/wix/WiXInstallers.qbs b/tests/auto/blackbox/testdata-windows/wix/WiXInstallers.qbs
new file mode 100644
index 000000000..acc7cf7b3
--- /dev/null
+++ b/tests/auto/blackbox/testdata-windows/wix/WiXInstallers.qbs
@@ -0,0 +1,39 @@
+import qbs.FileInfo
+import qbs.Host
+
+Project {
+ WindowsInstallerPackage {
+ name: "QbsSetup"
+ targetName: "qbs"
+ files: ["QbsSetup.wxs", "ExampleScript.bat"]
+ wix.defines: ["scriptName=ExampleScript.bat"]
+ wix.extensions: ["WixBalExtension", "WixUIExtension"]
+ qbs.targetPlatform: "windows"
+
+ Export {
+ Depends { name: "wix" }
+ wix.defines: base.concat(["msiName=" +
+ FileInfo.joinPaths(exportingProduct.buildDirectory,
+ exportingProduct.targetName + wix.windowsInstallerSuffix)])
+ }
+ }
+
+ WindowsSetupPackage {
+ Depends { name: "QbsSetup" }
+ condition: Host.os().includes("windows") // currently does not work in Wine with WiX 3.9
+ name: "QbsBootstrapper"
+ targetName: "qbs-setup-" + qbs.architecture
+ files: ["QbsBootstrapper.wxs"]
+ qbs.architecture: original || "x86"
+ qbs.targetPlatform: "windows"
+ }
+
+ WindowsInstallerPackage {
+ name: "RegressionBuster9000"
+ files: ["QbsSetup.wxs", "Qt.wxs", "de.wxl"]
+ wix.defines: ["scriptName=ExampleScript.bat"]
+ wix.cultures: []
+ qbs.architecture: original || "x86"
+ qbs.targetPlatform: "windows"
+ }
+}
diff --git a/tests/auto/blackbox/testdata/wix/de.wxl b/tests/auto/blackbox/testdata-windows/wix/de.wxl
index 75394cfdd..75394cfdd 100644
--- a/tests/auto/blackbox/testdata/wix/de.wxl
+++ b/tests/auto/blackbox/testdata-windows/wix/de.wxl
diff --git a/tests/auto/blackbox/testdata/wixDependencies/QbsSetup.wxs b/tests/auto/blackbox/testdata-windows/wixDependencies/QbsSetup.wxs
index ec839a269..ec839a269 100644
--- a/tests/auto/blackbox/testdata/wixDependencies/QbsSetup.wxs
+++ b/tests/auto/blackbox/testdata-windows/wixDependencies/QbsSetup.wxs
diff --git a/tests/auto/blackbox/testdata-windows/wixDependencies/main.c b/tests/auto/blackbox/testdata-windows/wixDependencies/main.c
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/blackbox/testdata-windows/wixDependencies/main.c
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/blackbox/testdata/wixDependencies/wixDependencies.qbs b/tests/auto/blackbox/testdata-windows/wixDependencies/wixDependencies.qbs
index d42a18054..d42a18054 100644
--- a/tests/auto/blackbox/testdata/wixDependencies/wixDependencies.qbs
+++ b/tests/auto/blackbox/testdata-windows/wixDependencies/wixDependencies.qbs
diff --git a/tests/auto/blackbox/testdata/allowed-values/allowed-values.qbs b/tests/auto/blackbox/testdata/allowed-values/allowed-values.qbs
new file mode 100644
index 000000000..699713770
--- /dev/null
+++ b/tests/auto/blackbox/testdata/allowed-values/allowed-values.qbs
@@ -0,0 +1,19 @@
+Product {
+ Depends { name: "a" }
+ // tests VariantValue
+ property string prop
+ PropertyOptions {
+ name: "prop"
+ description: "Some prop"
+ allowedValues: "foo"
+ }
+ // tests JSValue
+ property string prop2 // setter for otherProp
+ property string otherProp: prop2
+ PropertyOptions {
+ name: "otherProp"
+ description: "Some other prop"
+ allowedValues: "foo"
+ }
+ name: "p"
+}
diff --git a/tests/auto/blackbox/testdata/allowed-values/modules/a/a.qbs b/tests/auto/blackbox/testdata/allowed-values/modules/a/a.qbs
new file mode 100644
index 000000000..2bbcde525
--- /dev/null
+++ b/tests/auto/blackbox/testdata/allowed-values/modules/a/a.qbs
@@ -0,0 +1,18 @@
+Module {
+ // tests VariantValue
+ property stringList prop
+ PropertyOptions {
+ name: "prop"
+ description: "Some prop"
+ allowedValues: ["foo", "bar"]
+ }
+ // tests JSValue
+ property stringList prop2 // setter for otherProp
+ property stringList otherProp: prop2
+ PropertyOptions {
+ name: "otherProp"
+ description: "Some other prop"
+ allowedValues: ["foo", "bar"]
+ }
+}
+
diff --git a/tests/auto/blackbox/testdata/assembly/assembly.qbs b/tests/auto/blackbox/testdata/assembly/assembly.qbs
index f7bd4ecad..9d5584af5 100644
--- a/tests/auto/blackbox/testdata/assembly/assembly.qbs
+++ b/tests/auto/blackbox/testdata/assembly/assembly.qbs
@@ -30,9 +30,9 @@ Project {
name : "testa"
files : [ "testa.s" ]
Depends { name: "cpp" }
- condition: qbs.toolchain.contains("gcc")
+ condition: qbs.toolchain.includes("gcc")
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -40,9 +40,9 @@ Project {
name : "testb"
files : [ "testb.S" ]
Depends { name: "cpp" }
- condition: qbs.toolchain.contains("gcc")
+ condition: qbs.toolchain.includes("gcc")
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -50,9 +50,9 @@ Project {
name : "testc"
files : [ "testc.sx" ]
Depends { name: "cpp" }
- condition: qbs.toolchain.contains("gcc")
+ condition: qbs.toolchain.includes("gcc")
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -63,10 +63,10 @@ Project {
files: ["testd_" + qbs.architecture + ".asm"]
}
Depends { name: "cpp" }
- condition: qbs.toolchain.contains("msvc")
+ condition: qbs.toolchain.includes("msvc")
&& (qbs.architecture === "x86" || qbs.architecture === "x86_64")
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
diff --git a/tests/auto/blackbox/testdata/autotest-timeout/autotests-timeout.qbs b/tests/auto/blackbox/testdata/autotest-timeout/autotests-timeout.qbs
index 49ee35d3a..b6094ed22 100644
--- a/tests/auto/blackbox/testdata/autotest-timeout/autotests-timeout.qbs
+++ b/tests/auto/blackbox/testdata/autotest-timeout/autotests-timeout.qbs
@@ -1,12 +1,20 @@
+import qbs.Host
+
Project {
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "testApp"
type: ["application", "autotest"]
Depends { name: "autotest" }
cpp.cxxLanguageVersion: "c++11"
cpp.minimumOsxVersion: "10.8" // For <chrono>
Properties {
- condition: qbs.toolchain.contains("gcc")
+ condition: qbs.toolchain.includes("gcc")
cpp.driverFlags: "-pthread"
}
files: "test-main.cpp"
@@ -14,7 +22,7 @@ Project {
AutotestRunner {
Depends {
name: "cpp" // Make sure build environment is set up properly.
- condition: qbs.hostOS.contains("windows") && qbs.toolchain.contains("gcc")
+ condition: Host.os().includes("windows") && qbs.toolchain.includes("gcc")
}
}
}
diff --git a/tests/auto/blackbox/testdata/autotest-with-dependencies/autotest-with-dependencies.qbs b/tests/auto/blackbox/testdata/autotest-with-dependencies/autotest-with-dependencies.qbs
index 7ae6cef73..bea74796a 100644
--- a/tests/auto/blackbox/testdata/autotest-with-dependencies/autotest-with-dependencies.qbs
+++ b/tests/auto/blackbox/testdata/autotest-with-dependencies/autotest-with-dependencies.qbs
@@ -1,7 +1,14 @@
import qbs.FileInfo
+import qbs.Host
Project {
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "helper-app"
type: ["application", "test-helper"]
consoleApplication: true
@@ -23,7 +30,7 @@ Project {
AutotestRunner {
Depends {
name: "cpp" // Make sure build environment is set up properly.
- condition: qbs.hostOS.contains("windows") && qbs.toolchain.contains("gcc")
+ condition: Host.os().includes("windows") && qbs.toolchain.includes("gcc")
}
arguments: FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix, "bin")
auxiliaryInputs: "test-helper"
diff --git a/tests/auto/blackbox/testdata/autotests/autotests.qbs b/tests/auto/blackbox/testdata/autotests/autotests.qbs
index 10334156e..cfb7fc560 100644
--- a/tests/auto/blackbox/testdata/autotests/autotests.qbs
+++ b/tests/auto/blackbox/testdata/autotests/autotests.qbs
@@ -1,9 +1,17 @@
+import qbs.Host
+
Project {
references: ["test1", "test2", "test3"]
AutotestRunner {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
Depends {
name: "cpp" // Make sure build environment is set up properly.
- condition: qbs.hostOS.contains("windows") && qbs.toolchain.contains("gcc")
+ condition: Host.os().includes("windows") && qbs.toolchain.includes("gcc")
}
}
}
diff --git a/tests/auto/blackbox/testdata/badInterpreter/badInterpreter.qbs b/tests/auto/blackbox/testdata/badInterpreter/badInterpreter.qbs
index bef82a003..3c8f64c6c 100644
--- a/tests/auto/blackbox/testdata/badInterpreter/badInterpreter.qbs
+++ b/tests/auto/blackbox/testdata/badInterpreter/badInterpreter.qbs
@@ -1,4 +1,13 @@
+import qbs.Host
+
Project {
+ property bool enabled: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
+
qbsSearchPaths: base.concat(["qbs"])
Product {
diff --git a/tests/auto/blackbox/testdata/build-variant-defaults/build-variant-defaults.qbs b/tests/auto/blackbox/testdata/build-variant-defaults/build-variant-defaults.qbs
new file mode 100644
index 000000000..4015817ca
--- /dev/null
+++ b/tests/auto/blackbox/testdata/build-variant-defaults/build-variant-defaults.qbs
@@ -0,0 +1,16 @@
+CppApplication {
+ property bool validate: {
+ var valid = true;
+ if (qbs.buildVariant === "release") {
+ valid = !qbs.enableDebugCode && !qbs.debugInformation && qbs.optimization === "fast";
+ } else if (qbs.buildVariant === "debug") {
+ valid = qbs.enableDebugCode && qbs.debugInformation && qbs.optimization === "none";
+ } else if (qbs.buildVariant === "profiling") {
+ valid = !qbs.enableDebugCode && qbs.debugInformation && qbs.optimization === "fast";
+ }
+
+ if (!valid)
+ throw "Invalid defaults";
+ return valid;
+ }
+}
diff --git a/tests/auto/blackbox/testdata/build-variant-defaults/main.cpp b/tests/auto/blackbox/testdata/build-variant-defaults/main.cpp
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/blackbox/testdata/build-variant-defaults/main.cpp
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/blackbox/testdata/buildenv-change/buildenv-change.qbs b/tests/auto/blackbox/testdata/buildenv-change/buildenv-change.qbs
index 6c0bcc73e..54005e76a 100644
--- a/tests/auto/blackbox/testdata/buildenv-change/buildenv-change.qbs
+++ b/tests/auto/blackbox/testdata/buildenv-change/buildenv-change.qbs
@@ -3,7 +3,7 @@ CppApplication {
id: dummy
property stringList toolchain: qbs.toolchain
configure: {
- if (toolchain.contains("msvc"))
+ if (toolchain.includes("msvc"))
console.info("msvc");
}
}
diff --git a/tests/auto/blackbox/testdata/capnproto/bar.capnp b/tests/auto/blackbox/testdata/capnproto/bar.capnp
new file mode 100644
index 000000000..a0e8a0f8c
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/bar.capnp
@@ -0,0 +1,8 @@
+@0xc967c84bcca70a1d;
+
+using Foo = import "foo.capnp";
+
+struct Bar {
+ foo @0 :Foo.Foo;
+ # Use type "Foo" defined in foo.capnp.
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/baz.capnp b/tests/auto/blackbox/testdata/capnproto/baz.capnp
new file mode 100644
index 000000000..8b2fe4faf
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/baz.capnp
@@ -0,0 +1,8 @@
+@0xc967c84bcca70a1d;
+
+using Foo = import "/imports/foo.capnp";
+
+struct Baz {
+ foo @0 :Foo.Foo;
+ # Use type "Foo" defined in foo.capnp.
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.cpp b/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.cpp
new file mode 100644
index 000000000..0e8979eec
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.cpp
@@ -0,0 +1,14 @@
+#include "baz.capnp.h"
+
+#include <capnp/message.h>
+
+int main()
+{
+ ::capnp::MallocMessageBuilder message;
+
+ auto baz = message.initRoot<Baz>();
+ auto foo = baz.initFoo();
+ foo.setStr("hello");
+
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs
new file mode 100644
index 000000000..0d7c8a1bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs
@@ -0,0 +1,20 @@
+import qbs.Host
+
+CppApplication {
+ Depends { name: "capnproto.cpp"; required: false }
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ if (!capnproto.cpp.present)
+ console.info("capnproto is not present");
+ return result && capnproto.cpp.present;
+ }
+ cpp.minimumMacosVersion: "10.8"
+ capnproto.cpp.importPaths: "."
+ files: [
+ "baz.capnp",
+ "capnproto_absolute_import.cpp",
+ "imports/foo.capnp",
+ ]
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.cpp b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.cpp
new file mode 100644
index 000000000..b9f729955
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.cpp
@@ -0,0 +1,13 @@
+#include "foo.capnp.h"
+
+#include <capnp/message.h>
+
+int main()
+{
+ ::capnp::MallocMessageBuilder message;
+
+ auto foo = message.initRoot<Foo>();
+ foo.setStr("hello");
+
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs
new file mode 100644
index 000000000..c31824bb0
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs
@@ -0,0 +1,18 @@
+import qbs.Host
+
+CppApplication {
+ Depends { name: "capnproto.cpp"; required: false }
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ if (!capnproto.cpp.present)
+ console.info("capnproto is not present");
+ return result && capnproto.cpp.present;
+ }
+ cpp.minimumMacosVersion: "10.8"
+ files: [
+ "capnproto_cpp.cpp",
+ "foo.capnp"
+ ]
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs
new file mode 100644
index 000000000..90201e440
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs
@@ -0,0 +1,19 @@
+import qbs.Host
+
+CppApplication {
+ Depends { name: "capnproto.cpp"; required: false }
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ if (!capnproto.cpp.present)
+ console.info("capnproto is not present");
+ return result && capnproto.cpp.present;
+ }
+ cpp.minimumMacosVersion: "10.8"
+ files: [
+ "capnproto_cpp.cpp",
+ "foo.capnp"
+ ]
+ qbsModuleProviders: "qbspkgconfig"
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.cpp b/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.cpp
new file mode 100644
index 000000000..5116bd3d6
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.cpp
@@ -0,0 +1,14 @@
+#include "bar.capnp.h"
+
+#include <capnp/message.h>
+
+int main()
+{
+ ::capnp::MallocMessageBuilder message;
+
+ auto bar = message.initRoot<Bar>();
+ auto foo = bar.initFoo();
+ foo.setStr("hello");
+
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.qbs
new file mode 100644
index 000000000..333a3cdb9
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/capnproto_relative_import.qbs
@@ -0,0 +1,19 @@
+import qbs.Host
+
+CppApplication {
+ Depends { name: "capnproto.cpp"; required: false }
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ if (!capnproto.cpp.present)
+ console.info("capnproto is not present");
+ return result && capnproto.cpp.present;
+ }
+ cpp.minimumMacosVersion: "10.8"
+ files: [
+ "bar.capnp",
+ "capnproto_relative_import.cpp",
+ "foo.capnp",
+ ]
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/foo.capnp b/tests/auto/blackbox/testdata/capnproto/foo.capnp
new file mode 100644
index 000000000..146a2969f
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/foo.capnp
@@ -0,0 +1,6 @@
+@0x8a2efe67220790be;
+
+struct Foo {
+ num @0 :UInt32;
+ str @1 :Text;
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/greeter-client.cpp b/tests/auto/blackbox/testdata/capnproto/greeter-client.cpp
new file mode 100644
index 000000000..d3fcdb4e3
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/greeter-client.cpp
@@ -0,0 +1,25 @@
+#include "greeter.capnp.h"
+
+#include <capnp/ez-rpc.h>
+
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+ const char address[] = "localhost:5050";
+ capnp::EzRpcClient client(address);
+ Greeter::Client greeter = client.getMain<Greeter>();
+
+ auto& waitScope = client.getWaitScope();
+
+ for (int i = 0; i < 2; ++i) {
+ auto request = greeter.sayHelloRequest();
+ request.initRequest().setName("hello workd");
+ auto promise = request.send();
+
+ auto response = promise.wait(waitScope);
+ std::cout << response.getResponse().getName().cStr() << std::endl;
+ }
+
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/greeter-server.cpp b/tests/auto/blackbox/testdata/capnproto/greeter-server.cpp
new file mode 100644
index 000000000..a7f482cc8
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/greeter-server.cpp
@@ -0,0 +1,27 @@
+#include "greeter.capnp.h"
+
+#include <capnp/ez-rpc.h>
+#include <capnp/message.h>
+
+#include <iostream>
+
+class GreeterImpl final: public Greeter::Server
+{
+public:
+ ::kj::Promise<void> sayHello(SayHelloContext context) override
+ {
+ auto response = context.getResults().initResponse();
+ response.setName(context.getParams().getRequest().getName());
+ return kj::READY_NOW;
+ };
+};
+
+int main(int argc, char *argv[])
+{
+ const char address[] = "localhost:5050";
+ capnp::EzRpcServer server(kj::heap<GreeterImpl>(), address);
+
+ auto& waitScope = server.getWaitScope();
+ // Run forever, accepting connections and handling requests.
+ kj::NEVER_DONE.wait(waitScope);
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/greeter.capnp b/tests/auto/blackbox/testdata/capnproto/greeter.capnp
new file mode 100644
index 000000000..b9188f634
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/greeter.capnp
@@ -0,0 +1,13 @@
+@0x85150b117366d14b;
+
+struct HelloRequest {
+ name @0 :Text;
+}
+
+struct HelloResponse {
+ name @0 :Text;
+}
+
+interface Greeter {
+ sayHello @0 (request: HelloRequest) -> (response: HelloResponse);
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs b/tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs
new file mode 100644
index 000000000..7266e9e15
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs
@@ -0,0 +1,34 @@
+import qbs.Host
+
+Project {
+ CppApplication {
+ Depends { name: "capnproto.cpp"; required: false }
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ if (!capnproto.cpp.present)
+ console.info("capnproto is not present");
+ return result && capnproto.cpp.present;
+ }
+ name: "server"
+ consoleApplication: true
+ cpp.minimumMacosVersion: "10.8"
+ capnproto.cpp.useRpc: true
+ files: [
+ "greeter.capnp",
+ "greeter-server.cpp"
+ ]
+ }
+ CppApplication {
+ Depends { name: "capnproto.cpp"; required: false }
+ name: "client"
+ consoleApplication: true
+ capnproto.cpp.useRpc: true
+ cpp.minimumMacosVersion: "10.8"
+ files: [
+ "greeter.capnp",
+ "greeter-client.cpp"
+ ]
+ }
+}
diff --git a/tests/auto/blackbox/testdata/capnproto/imports/foo.capnp b/tests/auto/blackbox/testdata/capnproto/imports/foo.capnp
new file mode 100644
index 000000000..146a2969f
--- /dev/null
+++ b/tests/auto/blackbox/testdata/capnproto/imports/foo.capnp
@@ -0,0 +1,6 @@
+@0x8a2efe67220790be;
+
+struct Foo {
+ num @0 :UInt32;
+ str @1 :Text;
+}
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
index cf5354268..011eedc91 100644
--- 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
@@ -15,7 +15,7 @@ Product {
prepare: {
var cmd = new JavaScriptCommand();
PrepareHelper.prepare(cmd);
- cmd.description = "Creating output";
+ cmd.description = "creating output";
return [cmd];
}
}
diff --git a/tests/auto/blackbox/testdata/change-tracking-and-multiplexing/change-tracking-and-multiplexing.qbs b/tests/auto/blackbox/testdata/change-tracking-and-multiplexing/change-tracking-and-multiplexing.qbs
index d1215355c..096778cd3 100644
--- a/tests/auto/blackbox/testdata/change-tracking-and-multiplexing/change-tracking-and-multiplexing.qbs
+++ b/tests/auto/blackbox/testdata/change-tracking-and-multiplexing/change-tracking-and-multiplexing.qbs
@@ -1,8 +1,8 @@
StaticLibrary {
name: "l"
- Depends { condition: qbs.targetOS.contains("darwin"); name: "bundle" }
- Properties { condition: qbs.targetOS.contains("darwin"); bundle.isBundle: false }
+ Depends { condition: qbs.targetOS.includes("darwin"); name: "bundle" }
+ Properties { condition: qbs.targetOS.includes("darwin"); bundle.isBundle: false }
multiplexByQbsProperties: ["buildVariants"]
qbs.buildVariants: ["debug", "release"]
diff --git a/tests/auto/blackbox/testdata/choose-module-instance/other-searchpath/modules/limerick/generic.qbs b/tests/auto/blackbox/testdata/choose-module-instance/other-searchpath/modules/limerick/generic.qbs
index 2ebaaac11..bd8e901c7 100644
--- a/tests/auto/blackbox/testdata/choose-module-instance/other-searchpath/modules/limerick/generic.qbs
+++ b/tests/auto/blackbox/testdata/choose-module-instance/other-searchpath/modules/limerick/generic.qbs
@@ -1,3 +1,3 @@
Module {
- condition: !qbs.targetOS.contains("Beatles")
+ condition: !qbs.targetOS.includes("Beatles")
}
diff --git a/tests/auto/blackbox/testdata/clean/clean.qbs b/tests/auto/blackbox/testdata/clean/clean.qbs
index ce3a8eb12..10eca2d41 100644
--- a/tests/auto/blackbox/testdata/clean/clean.qbs
+++ b/tests/auto/blackbox/testdata/clean/clean.qbs
@@ -5,7 +5,7 @@ Project {
name: "dep"
files: "dep.cpp"
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
diff --git a/tests/auto/blackbox/testdata/cli/dotnettest.qbs b/tests/auto/blackbox/testdata/cli/dotnettest.qbs
index 9a10b8068..5685be8c4 100644
--- a/tests/auto/blackbox/testdata/cli/dotnettest.qbs
+++ b/tests/auto/blackbox/testdata/cli/dotnettest.qbs
@@ -1,7 +1,7 @@
Project {
Application {
Depends { name: "cli" }
- Depends { name: "HelloWorldModule"; condition: !qbs.toolchain.contains("mono") }
+ Depends { name: "HelloWorldModule"; condition: !qbs.toolchain.includes("mono") }
Depends { name: "NetLib" }
type: "application"
@@ -16,7 +16,7 @@ Project {
// Mono's VB compiler doesn't support modules yet, and if we try with C#, it crashes anyways
NetModule {
- condition: !qbs.toolchain.contains("mono")
+ condition: !qbs.toolchain.includes("mono")
Depends { name: "cli" }
name: "HelloWorldModule"
@@ -37,7 +37,7 @@ Project {
// fill-in for missing NetModule
Group {
- condition: qbs.toolchain.contains("mono")
+ condition: qbs.toolchain.includes("mono")
files: ["Module.cs"]
}
diff --git a/tests/auto/blackbox/testdata/command-file/command-file.qbs b/tests/auto/blackbox/testdata/command-file/command-file.qbs
index 8e25221c5..87dd0e054 100644
--- a/tests/auto/blackbox/testdata/command-file/command-file.qbs
+++ b/tests/auto/blackbox/testdata/command-file/command-file.qbs
@@ -4,7 +4,7 @@ Project {
destinationDirectory: project.buildDirectory
Depends { name: "cpp" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
files: ["lib.cpp"]
@@ -14,6 +14,6 @@ Project {
cpp.libraryPaths: project.buildDirectory
files: ["main.cpp"]
cpp.staticLibraries: ['@' + sourceDirectory + '/'
- + (qbs.toolchain.contains("msvc") ? "list.msvc" : "list.gcc")]
+ + (qbs.toolchain.includes("msvc") ? "list.msvc" : "list.gcc")]
}
}
diff --git a/tests/auto/blackbox/testdata/compilerDefinesByLanguage/CppDefinesApp.qbs b/tests/auto/blackbox/testdata/compilerDefinesByLanguage/CppDefinesApp.qbs
index b205ef2a7..f2da212f6 100644
--- a/tests/auto/blackbox/testdata/compilerDefinesByLanguage/CppDefinesApp.qbs
+++ b/tests/auto/blackbox/testdata/compilerDefinesByLanguage/CppDefinesApp.qbs
@@ -1,7 +1,7 @@
CppApplication {
files: ["app.c"]
- property bool enableObjectiveC: qbs.targetOS.contains("darwin")
+ property bool enableObjectiveC: qbs.targetOS.includes("darwin")
Group {
name: "C/C++"
diff --git a/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile-probe-project.qbs b/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile-probe-project.qbs
new file mode 100644
index 000000000..adcb2bd9c
--- /dev/null
+++ b/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile-probe-project.qbs
@@ -0,0 +1,23 @@
+import qbs.Probes
+import qbs.TextFile
+
+Project {
+ readonly property bool forceFailure: false
+
+ Probes.ConanfileProbe {
+ id: conan
+ conanfilePath: path + "/conanfile.py"
+ options: ({opt: "True", forceFailure: (project.forceFailure ? "True" : "False")})
+ settings: ({os: "AIX"})
+ }
+
+ property var check: {
+ var tf = new TextFile(buildDirectory + "/results.json", TextFile.WriteOnly);
+ var o = {
+ json: conan.json.deps_env_info["ENV_VAR"],
+ dependencies: conan.dependencies["testlib"].libs,
+ generatedFilesPath: conan.generatedFilesPath
+ };
+ tf.write(JSON.stringify(o));
+ }
+}
diff --git a/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile.py b/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile.py
new file mode 100644
index 000000000..59e40cc80
--- /dev/null
+++ b/tests/auto/blackbox/testdata/conanfile-probe/testapp/conanfile.py
@@ -0,0 +1,26 @@
+from conans import ConanFile
+
+class TestApp(ConanFile):
+ name = "testapp"
+ description = "Our project package, to be inspected by the Qbs ConanfileProbe"
+ license = "none"
+ version = "6.6.6"
+
+ settings = "os"
+ options = {"opt": [True, False], "forceFailure": [True, False]}
+ default_options = {"opt": False, "forceFailure": False}
+
+ requires = "testlib/1.2.3@qbs/testing"
+
+ def configure(self):
+ assert(not self.options.forceFailure)
+ self.options["testlib"].opt = self.options.opt
+
+ def source(self):
+ pass
+
+ def build(self):
+ pass
+
+ def package(self):
+ pass
diff --git a/tests/auto/blackbox/testdata/conanfile-probe/testlib/conanfile.py b/tests/auto/blackbox/testdata/conanfile-probe/testlib/conanfile.py
new file mode 100644
index 000000000..983c22599
--- /dev/null
+++ b/tests/auto/blackbox/testdata/conanfile-probe/testlib/conanfile.py
@@ -0,0 +1,25 @@
+from conans import ConanFile
+
+class Testlib(ConanFile):
+ name = "testlib"
+ description = "Represents an arbitrary package, for instance on bintray"
+ license = "none"
+ version = "1.2.3"
+
+ settings = "os"
+ options = {"opt": [True, False]}
+ default_options = {"opt": False}
+
+ def source(self):
+ pass
+
+ def build(self):
+ pass
+
+ def package(self):
+ pass
+
+ def package_info(self):
+ self.cpp_info.libs = ["testlib1","testlib2"]
+ self.env_info.ENV_VAR = "TESTLIB_ENV_VAL"
+ self.user_info.user_var = "testlib_user_val"
diff --git a/tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs b/tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs
deleted file mode 100644
index 802aa1450..000000000
--- a/tests/auto/blackbox/testdata/concurrent-executor/concurrent-executor.qbs
+++ /dev/null
@@ -1,67 +0,0 @@
-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"]
- outputFileTags: "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/util.js b/tests/auto/blackbox/testdata/concurrent-executor/util.js
deleted file mode 100644
index a37a8cbb1..000000000
--- a/tests/auto/blackbox/testdata/concurrent-executor/util.js
+++ /dev/null
@@ -1,8 +0,0 @@
-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-filetagger/conditional-filetagger.qbs b/tests/auto/blackbox/testdata/conditional-filetagger/conditional-filetagger.qbs
index 5f0c93e46..f5d7ad676 100644
--- a/tests/auto/blackbox/testdata/conditional-filetagger/conditional-filetagger.qbs
+++ b/tests/auto/blackbox/testdata/conditional-filetagger/conditional-filetagger.qbs
@@ -1,7 +1,7 @@
CppApplication {
name: "theApp"
property bool enableTagger
- files: ["main.custom"];
+ files: ["main.custom"]
FileTagger {
condition: enableTagger
patterns: ["*.custom"]
diff --git a/tests/auto/blackbox/testdata/configure/configure.qbs b/tests/auto/blackbox/testdata/configure/configure.qbs
index af5638dde..be0e65247 100644
--- a/tests/auto/blackbox/testdata/configure/configure.qbs
+++ b/tests/auto/blackbox/testdata/configure/configure.qbs
@@ -1,6 +1,13 @@
import qbs.FileInfo
+import qbs.Host
Project {
+ property bool enabled: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
property string name: 'configure'
qbsSearchPaths: '.'
Product {
diff --git a/tests/auto/blackbox/testdata/configure/main.cpp b/tests/auto/blackbox/testdata/configure/main.cpp
index c7213c768..a91914f73 100644
--- a/tests/auto/blackbox/testdata/configure/main.cpp
+++ b/tests/auto/blackbox/testdata/configure/main.cpp
@@ -30,7 +30,7 @@
int main()
{
- printf("%s..\n", TEXT);
+ std::printf("%s..\n", TEXT);
return 0;
}
diff --git a/tests/auto/blackbox/testdata/configure/modules/definition/module.qbs b/tests/auto/blackbox/testdata/configure/modules/definition/module.qbs
index 1d647faa2..6a1dbe67d 100644
--- a/tests/auto/blackbox/testdata/configure/modules/definition/module.qbs
+++ b/tests/auto/blackbox/testdata/configure/modules/definition/module.qbs
@@ -12,7 +12,7 @@ Module {
var cmd;
var args;
var p = path;
- if (targetOS.contains("windows")) {
+ if (targetOS.includes("windows")) {
cmd = windowsShellPath;
args = ["/c", "date", "/t"];
} else {
diff --git a/tests/auto/blackbox/testdata/conflicting-property-values/conflicting-property-values.qbs b/tests/auto/blackbox/testdata/conflicting-property-values/conflicting-property-values.qbs
new file mode 100644
index 000000000..23b6ee5a3
--- /dev/null
+++ b/tests/auto/blackbox/testdata/conflicting-property-values/conflicting-property-values.qbs
@@ -0,0 +1,41 @@
+Project {
+ Product {
+ name: "low"
+ Export { property string prop: "low"; property string prop2: "low" }
+ }
+ Product {
+ name: "higher1"
+ Export { Depends { name: "low" } low.prop: "higher1" }
+ }
+ Product {
+ name: "higher2"
+ Export { Depends { name: "low" } low.prop: "higher2" }
+ }
+ Product {
+ name: "highest1"
+ Export {
+ Depends { name: "low" }
+ Depends { name: "higher1" }
+ Depends { name: "higher2" }
+ low.prop: "highest1"
+ low.prop2: "highest"
+ }
+ }
+ Product {
+ name: "highest2"
+ Export {
+ Depends { name: "low" }
+ Depends { name: "higher1" }
+ Depends { name: "higher2" }
+ low.prop: "highest2"
+ low.prop2: "highest"
+ }
+ }
+ Product {
+ name: "toplevel"
+ Depends { name: "highest1" }
+ Depends { name: "highest2" }
+ low.prop: name
+ property bool dummy: { console.info("final prop value: " + low.prop); }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/cpu-features/cpu-features.qbs b/tests/auto/blackbox/testdata/cpu-features/cpu-features.qbs
index 0bfdaceba..36e805516 100644
--- a/tests/auto/blackbox/testdata/cpu-features/cpu-features.qbs
+++ b/tests/auto/blackbox/testdata/cpu-features/cpu-features.qbs
@@ -8,7 +8,7 @@ CppApplication {
property bool dummy: {
console.info("is x86: " + (qbs.architecture === "x86"));
console.info("is x64: " + (qbs.architecture === "x86_64"));
- console.info("is gcc: " + qbs.toolchain.contains("gcc"));
- console.info("is msvc: " + qbs.toolchain.contains("msvc"));
+ console.info("is gcc: " + qbs.toolchain.includes("gcc"));
+ console.info("is msvc: " + qbs.toolchain.includes("msvc"));
}
}
diff --git a/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs b/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs
index 322ded85c..24c37951c 100644
--- a/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs
+++ b/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs
@@ -13,10 +13,10 @@ CppApplication {
var isEvenNewerMsvc;
var isOlderMsvc;
var isGcc;
- if (toolchain.contains("clang-cl")) {
+ if (toolchain.includes("clang-cl")) {
isEvenNewerMsvc = true;
isNewerMsvc = true;
- } else if (toolchain.contains("msvc")) {
+ } else if (toolchain.includes("msvc")) {
if (compilerVersion >= "19.12.25831")
isEvenNewerMsvc = true;
if (compilerVersion >= "18.00.30723")
diff --git a/tests/auto/blackbox/testdata/date-property/date-property.qbs b/tests/auto/blackbox/testdata/date-property/date-property.qbs
new file mode 100644
index 000000000..ffd584802
--- /dev/null
+++ b/tests/auto/blackbox/testdata/date-property/date-property.qbs
@@ -0,0 +1,18 @@
+Product {
+ type: "date"
+ property var theDate: new Date(1999, 11, 31);
+ Rule {
+ multiplex: true
+ Artifact { filePath: "dummy"; fileTags: "date" }
+ prepare: {
+ var cmd = new JavaScriptCommand;
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ var d = product.theDate;
+ console.info("The stored date was " + d.getFullYear() + '-' + (d.getMonth() + 1) + '-'
+ + d.getDate());
+ };
+ return cmd;
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs b/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs
index 735bfffbc..681ffeab0 100644
--- a/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs
+++ b/tests/auto/blackbox/testdata/dependenciesProperty/dependenciesProperty.qbs
@@ -18,7 +18,7 @@ Project {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = 'generate ' + FileInfo.fileName(output.filePath);
+ cmd.description = 'generating ' + FileInfo.fileName(output.filePath);
cmd.highlight = 'codegen';
cmd.sourceCode = function() {
file = new TextFile(output.filePath, TextFile.WriteOnly);
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
deleted file mode 100644
index 8a6f733c0..000000000
--- a/tests/auto/blackbox/testdata/dependency-profile-mismatch/dependency-profile-mismatch.qbs
+++ /dev/null
@@ -1,12 +0,0 @@
-Project {
- property string mainProfile
- property string depProfile
- Product {
- name: "dep"
- qbs.profiles: [project.depProfile]
- }
- Product {
- name: "main"
- Depends { name: "dep"; profiles: [project.mainProfile]; }
- }
-}
diff --git a/tests/auto/blackbox/testdata/dependency-scanning-loop/dependency-scanning-loop.qbs b/tests/auto/blackbox/testdata/dependency-scanning-loop/dependency-scanning-loop.qbs
new file mode 100644
index 000000000..ac8e7258c
--- /dev/null
+++ b/tests/auto/blackbox/testdata/dependency-scanning-loop/dependency-scanning-loop.qbs
@@ -0,0 +1,34 @@
+import qbs.FileInfo
+import qbs.TextFile
+
+CppApplication {
+ name: "app"
+ cpp.includePaths: buildDirectory
+ Group {
+ files: "main.cpp"
+ fileTags: ["cpp", "custom.in"]
+ }
+ Rule {
+ inputs: "custom.in"
+ Artifact {
+ filePath: FileInfo.completeBaseName(input.filePath) + ".h"
+ fileTags: "hpp"
+ }
+ Artifact {
+ filePath: "custom.txt"
+ fileTags: "whatever"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating " + outputs.hpp[0].fileName;
+ cmd.sourceCode = function() {
+ var f = new TextFile(outputs.hpp[0].filePath, TextFile.WriteOnly);
+ f.writeLine("int main() {}");
+ f.close();
+ f = new TextFile(outputs.whatever[0].filePath, TextFile.WriteOnly);
+ f.close();
+ }
+ return cmd;
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/dependency-scanning-loop/main.cpp b/tests/auto/blackbox/testdata/dependency-scanning-loop/main.cpp
new file mode 100644
index 000000000..5e8dda41b
--- /dev/null
+++ b/tests/auto/blackbox/testdata/dependency-scanning-loop/main.cpp
@@ -0,0 +1 @@
+#include <main.h>
diff --git a/tests/auto/blackbox/testdata/deprecated-property/deprecated-property.qbs b/tests/auto/blackbox/testdata/deprecated-property/deprecated-property.qbs
index a8efb97b5..7d1550312 100644
--- a/tests/auto/blackbox/testdata/deprecated-property/deprecated-property.qbs
+++ b/tests/auto/blackbox/testdata/deprecated-property/deprecated-property.qbs
@@ -1,8 +1,6 @@
-import qbs
-
Product {
Depends { name: "themodule" }
themodule.newProp: true
- themodule.oldProp: false
+ themodule.expiringProp: 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
index 106ed4135..58164e918 100644
--- a/tests/auto/blackbox/testdata/deprecated-property/modules/themodule/m.qbs
+++ b/tests/auto/blackbox/testdata/deprecated-property/modules/themodule/m.qbs
@@ -1,8 +1,8 @@
-import qbs
+import qbs.Environment
Module {
property bool newProp
- property bool oldProp
+ property bool expiringProp
property bool forgottenProp
PropertyOptions {
@@ -10,9 +10,9 @@ Module {
description: "Use this, it's good!"
}
PropertyOptions {
- name: "oldProp"
+ name: "expiringProp"
description: "Use newProp instead."
- removalVersion: "99.9"
+ removalVersion: Environment.getEnv("REMOVAL_VERSION")
}
PropertyOptions {
name: "veryOldProp"
diff --git a/tests/auto/blackbox/testdata/disappeared-profile/modules-dir/modules/m/m.qbs b/tests/auto/blackbox/testdata/disappeared-profile/modules-dir/modules/m/m.qbs
index 3e1747ade..fd755ce86 100644
--- a/tests/auto/blackbox/testdata/disappeared-profile/modules-dir/modules/m/m.qbs
+++ b/tests/auto/blackbox/testdata/disappeared-profile/modules-dir/modules/m/m.qbs
@@ -10,7 +10,7 @@ Module {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName + " with " + product.m.p1;
+ cmd.description = "creating " + output.fileName + " with " + product.m.p1;
cmd.sourceCode = function() {};
return [cmd];
}
@@ -24,7 +24,7 @@ Module {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName + " with " + product.m.p2;
+ cmd.description = "creating " + output.fileName + " with " + product.m.p2;
cmd.sourceCode = function() {};
return [cmd];
}
diff --git a/tests/auto/blackbox/testdata/discard-unused-data/discard-unused-data.qbs b/tests/auto/blackbox/testdata/discard-unused-data/discard-unused-data.qbs
index fdd3aa1f7..367bb665c 100644
--- a/tests/auto/blackbox/testdata/discard-unused-data/discard-unused-data.qbs
+++ b/tests/auto/blackbox/testdata/discard-unused-data/discard-unused-data.qbs
@@ -4,9 +4,9 @@ CppApplication {
files: "main.cpp"
- Depends { name: "bundle"; condition: qbs.targetOS.contains("darwin") }
+ Depends { name: "bundle"; condition: qbs.targetOS.includes("darwin") }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
@@ -17,7 +17,7 @@ CppApplication {
var cmd = new JavaScriptCommand();
cmd.silent = true;
cmd.sourceCode = function() {
- console.info("is Darwin: " + product.qbs.targetOS.contains("darwin"));
+ console.info("is Darwin: " + product.qbs.targetOS.includes("darwin"));
console.info("---" + product.cpp.nmPath + "---");
}
return [cmd];
diff --git a/tests/auto/blackbox/testdata/distribution-include-paths/distribution-include-paths.qbs b/tests/auto/blackbox/testdata/distribution-include-paths/distribution-include-paths.qbs
new file mode 100644
index 000000000..3e6fb9b00
--- /dev/null
+++ b/tests/auto/blackbox/testdata/distribution-include-paths/distribution-include-paths.qbs
@@ -0,0 +1,4 @@
+CppApplication {
+ files: ["main.cpp"]
+ cpp.distributionIncludePaths: ["subdir"]
+}
diff --git a/tests/auto/blackbox/testdata/distribution-include-paths/main.cpp b/tests/auto/blackbox/testdata/distribution-include-paths/main.cpp
new file mode 100644
index 000000000..10d376a31
--- /dev/null
+++ b/tests/auto/blackbox/testdata/distribution-include-paths/main.cpp
@@ -0,0 +1,8 @@
+#include <cstdio>
+#include <gagagugu.h>
+
+int main()
+{
+ printStuff();
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata/distribution-include-paths/subdir/gagagugu.h b/tests/auto/blackbox/testdata/distribution-include-paths/subdir/gagagugu.h
new file mode 100644
index 000000000..b951d8855
--- /dev/null
+++ b/tests/auto/blackbox/testdata/distribution-include-paths/subdir/gagagugu.h
@@ -0,0 +1,4 @@
+void printStuff()
+{
+ puts("alalalalonglonglilonglonglong");
+}
diff --git a/tests/auto/blackbox/testdata/dot-dot-pc-file/dot-dot-pc-file.qbs b/tests/auto/blackbox/testdata/dot-dot-pc-file/dot-dot-pc-file.qbs
new file mode 100644
index 000000000..60c4ea2bc
--- /dev/null
+++ b/tests/auto/blackbox/testdata/dot-dot-pc-file/dot-dot-pc-file.qbs
@@ -0,0 +1,7 @@
+CppApplication {
+ name: "p"
+ Depends { name: "qbs-metatest-module"; }
+ files: "main.cpp"
+ moduleProviders.qbspkgconfig.libDirs: "libdir"
+ qbsModuleProviders: "qbspkgconfig"
+}
diff --git a/tests/auto/blackbox/testdata/dot-dot-pc-file/libdir/qbs.metatest.module.pc b/tests/auto/blackbox/testdata/dot-dot-pc-file/libdir/qbs.metatest.module.pc
new file mode 100644
index 000000000..c00fd26d6
--- /dev/null
+++ b/tests/auto/blackbox/testdata/dot-dot-pc-file/libdir/qbs.metatest.module.pc
@@ -0,0 +1,5 @@
+Name: qbs.metatest.module
+Description: just a test
+Version: 0.0.1
+
+Cflags: -DTHE_MAGIC_DEFINE
diff --git a/tests/auto/blackbox/testdata/dot-dot-pc-file/main.cpp b/tests/auto/blackbox/testdata/dot-dot-pc-file/main.cpp
new file mode 100644
index 000000000..442b755bf
--- /dev/null
+++ b/tests/auto/blackbox/testdata/dot-dot-pc-file/main.cpp
@@ -0,0 +1,5 @@
+#ifndef THE_MAGIC_DEFINE
+#error "missing the magic define"
+#endif
+
+int main() {}
diff --git a/tests/auto/blackbox/testdata/driver-linker-flags/driver-linker-flags.qbs b/tests/auto/blackbox/testdata/driver-linker-flags/driver-linker-flags.qbs
index 5de0fe053..57f096494 100644
--- a/tests/auto/blackbox/testdata/driver-linker-flags/driver-linker-flags.qbs
+++ b/tests/auto/blackbox/testdata/driver-linker-flags/driver-linker-flags.qbs
@@ -8,7 +8,7 @@ CppApplication {
Probe {
id: toolchainProbe
- condition: qbs.toolchain.contains("gcc")
+ condition: qbs.toolchain.includes("gcc")
configure: {
console.info("toolchain is GCC-like");
found = true;
diff --git a/tests/auto/blackbox/testdata/dynamic-library-in-module/Dll.qbs b/tests/auto/blackbox/testdata/dynamic-library-in-module/Dll.qbs
index 1acf606dc..c95d88052 100644
--- a/tests/auto/blackbox/testdata/dynamic-library-in-module/Dll.qbs
+++ b/tests/auto/blackbox/testdata/dynamic-library-in-module/Dll.qbs
@@ -1,8 +1,8 @@
DynamicLibrary {
Depends { name: "cpp" }
- Depends { name: "bundle"; condition: qbs.targetOS.contains("darwin") }
+ Depends { name: "bundle"; condition: qbs.targetOS.includes("darwin") }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
cpp.minimumMacosVersion: "10.7" // For -rpath
}
diff --git a/tests/auto/blackbox/testdata/dynamic-library-in-module/modules/thelib/thelib.qbs b/tests/auto/blackbox/testdata/dynamic-library-in-module/modules/thelib/thelib.qbs
index 30f87337d..3059fa5e9 100644
--- a/tests/auto/blackbox/testdata/dynamic-library-in-module/modules/thelib/thelib.qbs
+++ b/tests/auto/blackbox/testdata/dynamic-library-in-module/modules/thelib/thelib.qbs
@@ -13,7 +13,7 @@ Module {
}
Group {
name: "thelib dll import"
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
files: FileInfo.joinPaths(product.thelib.baseDir, "thelib.lib")
fileTags: ["dynamiclibrary_import"]
filesAreTargets: true
diff --git a/tests/auto/blackbox/testdata/dynamic-library-in-module/modules/theotherlib/theotherlib.qbs b/tests/auto/blackbox/testdata/dynamic-library-in-module/modules/theotherlib/theotherlib.qbs
index 1eb1e01a5..f387feb80 100644
--- a/tests/auto/blackbox/testdata/dynamic-library-in-module/modules/theotherlib/theotherlib.qbs
+++ b/tests/auto/blackbox/testdata/dynamic-library-in-module/modules/theotherlib/theotherlib.qbs
@@ -13,7 +13,7 @@ Module {
}
Group {
name: "theotherlib dll import"
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
files: FileInfo.joinPaths(product.theotherlib.baseDir, "theotherlib.lib")
fileTags: ["dynamiclibrary_import"]
filesAreTargets: true
diff --git a/tests/auto/blackbox/testdata/dynamic-library-in-module/theapp.qbs b/tests/auto/blackbox/testdata/dynamic-library-in-module/theapp.qbs
index b9149d091..5ea8cef23 100644
--- a/tests/auto/blackbox/testdata/dynamic-library-in-module/theapp.qbs
+++ b/tests/auto/blackbox/testdata/dynamic-library-in-module/theapp.qbs
@@ -1,5 +1,13 @@
+import qbs.Host
+
Project {
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "theapp"
cpp.minimumMacosVersion: "10.7" // For -rpath
Depends { name: "theotherlib" }
diff --git a/tests/auto/blackbox/testdata/dynamic-project/dynamic-project.qbs b/tests/auto/blackbox/testdata/dynamic-project/dynamic-project.qbs
index 166648c80..5acdb8892 100644
--- a/tests/auto/blackbox/testdata/dynamic-project/dynamic-project.qbs
+++ b/tests/auto/blackbox/testdata/dynamic-project/dynamic-project.qbs
@@ -17,13 +17,12 @@ Project
var srcDir = FileInfo.joinPaths(sourceDir, "src");
var projectDirs = File.directoryEntries(srcDir, File.Dirs | File.NoDotAndDotDot);
var list = [];
- for (it in projectDirs) {
+ for (var it = 0; it < projectDirs.length; ++it) {
var name = projectDirs[it];
var productSrcDir = FileInfo.joinPaths(srcDir, name);
var productFilePath = FileInfo.joinPaths(tempDir, name + ".qbs");
var file = new TextFile(productFilePath, TextFile.WriteOnly);
try {
- file.writeLine("import qbs");
file.writeLine("CppApplication");
file.writeLine("{");
file.writeLine("\tfiles: [ \"" + productSrcDir + "/*.cpp\" ]");
diff --git a/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js
index bd596fbca..b925effcf 100644
--- a/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js
+++ b/tests/auto/blackbox/testdata/dynamicRuleOutputs/before/flexoptionsreader.js
@@ -35,7 +35,7 @@
**
****************************************************************************/
-// needs import qbs.TextFile
+var TextFile = require("qbs.TextFile");
function readFlexOptions(filePath)
{
@@ -82,7 +82,7 @@ function readFlexOptions(filePath)
}
}
- var tf = new TextFile(input.filePath);
+ var tf = new TextFile(filePath);
var line;
var optrex = /^%option\s+(.*$)/;
var res;
diff --git a/tests/auto/blackbox/testdata/empty-profile/empty-profile.qbs b/tests/auto/blackbox/testdata/empty-profile/empty-profile.qbs
new file mode 100644
index 000000000..da7536315
--- /dev/null
+++ b/tests/auto/blackbox/testdata/empty-profile/empty-profile.qbs
@@ -0,0 +1,3 @@
+CppApplication {
+ files: ["main.cpp"]
+}
diff --git a/tests/auto/blackbox/testdata/empty-profile/main.cpp b/tests/auto/blackbox/testdata/empty-profile/main.cpp
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/blackbox/testdata/empty-profile/main.cpp
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/blackbox/testdata/enableExceptions/exceptions.qbs b/tests/auto/blackbox/testdata/enableExceptions/exceptions.qbs
index 9a75043a8..e1d09f7e0 100644
--- a/tests/auto/blackbox/testdata/enableExceptions/exceptions.qbs
+++ b/tests/auto/blackbox/testdata/enableExceptions/exceptions.qbs
@@ -1,5 +1,5 @@
CppApplication {
files: ["main.cpp"]
cpp.treatWarningsAsErrors: true
- cpp.defines: qbs.toolchain.contains("msvc") && !cpp.enableExceptions ? ["FORCE_FAIL_VS"] : []
+ cpp.defines: qbs.toolchain.includes("msvc") && !cpp.enableExceptions ? ["FORCE_FAIL_VS"] : []
}
diff --git a/tests/auto/blackbox/testdata/enableExceptions/none.qbs b/tests/auto/blackbox/testdata/enableExceptions/none.qbs
index 8fb052476..439ea3037 100644
--- a/tests/auto/blackbox/testdata/enableExceptions/none.qbs
+++ b/tests/auto/blackbox/testdata/enableExceptions/none.qbs
@@ -2,7 +2,7 @@ CppApplication {
files: ["emptymain.cpp"]
Group {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
files: ["empty.m", "empty.mm"]
}
}
diff --git a/tests/auto/blackbox/testdata/env-merging/env-merging.qbs b/tests/auto/blackbox/testdata/env-merging/env-merging.qbs
index 6edeca444..eb681f36b 100644
--- a/tests/auto/blackbox/testdata/env-merging/env-merging.qbs
+++ b/tests/auto/blackbox/testdata/env-merging/env-merging.qbs
@@ -1,3 +1,5 @@
+import qbs.Host
+
Project {
CppApplication {
name: "tool"
@@ -5,6 +7,12 @@ Project {
}
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "p"
type: "custom"
Depends { name: "tool" }
diff --git a/tests/auto/blackbox/testdata/escaped-linker-flags/escaped-linker-flags.qbs b/tests/auto/blackbox/testdata/escaped-linker-flags/escaped-linker-flags.qbs
index f48bf8d1f..b4fb0df4d 100644
--- a/tests/auto/blackbox/testdata/escaped-linker-flags/escaped-linker-flags.qbs
+++ b/tests/auto/blackbox/testdata/escaped-linker-flags/escaped-linker-flags.qbs
@@ -10,4 +10,9 @@ CppApplication {
cpp.linkerFlags: ["-s"]
}
files: ["main.cpp"]
+ Probe {
+ id: checker
+ property bool isUnixGcc: qbs.toolchain.contains("gcc") && !qbs.targetOS.contains("macos")
+ configure: { console.info("is gcc: " + isUnixGcc); }
+ }
}
diff --git a/tests/auto/blackbox/testdata/export-rule/export-rule.qbs b/tests/auto/blackbox/testdata/export-rule/export-rule.qbs
index 29899e728..455ccba0d 100644
--- a/tests/auto/blackbox/testdata/export-rule/export-rule.qbs
+++ b/tests/auto/blackbox/testdata/export-rule/export-rule.qbs
@@ -13,7 +13,7 @@ Project {
Export {
Depends { name: "cpp" }
property bool enableTagger
- property string description: "Creating C++ source file.";
+ property string description: "creating C++ source file.";
FileTagger {
condition: enableTagger
patterns: ["*.blubb"]
diff --git a/tests/auto/blackbox/testdata/exports-cmake/Foo.cpp b/tests/auto/blackbox/testdata/exports-cmake/Foo.cpp
new file mode 100644
index 000000000..ea334f9af
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-cmake/Foo.cpp
@@ -0,0 +1,5 @@
+#include "Foo.h"
+int someFooWork()
+{
+ return 42;
+}
diff --git a/tests/auto/blackbox/testdata/exports-cmake/Foo.h b/tests/auto/blackbox/testdata/exports-cmake/Foo.h
new file mode 100644
index 000000000..2f279f577
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-cmake/Foo.h
@@ -0,0 +1,16 @@
+#ifndef FOO_H
+#define FOO_H
+#include <dllexport.h>
+
+#ifdef FOO_LIB_STATIC
+#define FOO_LIB_EXPORT
+#else
+#ifdef FOO_LIB
+#define FOO_LIB_EXPORT DLL_EXPORT
+#else
+#define FOO_LIB_EXPORT DLL_IMPORT
+#endif
+#endif
+
+FOO_LIB_EXPORT int someFooWork();
+#endif // FOO_H
diff --git a/tests/auto/blackbox/testdata/exports-cmake/cmake/CMakeLists.txt b/tests/auto/blackbox/testdata/exports-cmake/cmake/CMakeLists.txt
new file mode 100644
index 000000000..d874e0e92
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-cmake/cmake/CMakeLists.txt
@@ -0,0 +1,7 @@
+cmake_minimum_required(VERSION 3.10)
+
+project(qbs_import)
+
+find_package(Bar PATHS REQUIRED)
+add_executable(Consumer main.cpp)
+target_link_libraries(Consumer Bar)
diff --git a/tests/auto/blackbox/testdata/exports-cmake/cmake/main.cpp b/tests/auto/blackbox/testdata/exports-cmake/cmake/main.cpp
new file mode 100644
index 000000000..1a1fa90ec
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-cmake/cmake/main.cpp
@@ -0,0 +1,6 @@
+#include <Foo.h>
+
+int main()
+{
+ return someFooWork();
+}
diff --git a/tests/auto/blackbox/testdata/exports-cmake/exports-cmake.qbs b/tests/auto/blackbox/testdata/exports-cmake/exports-cmake.qbs
new file mode 100644
index 000000000..6464af705
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-cmake/exports-cmake.qbs
@@ -0,0 +1,70 @@
+import qbs.FileInfo
+
+Project {
+ property bool isStatic: false
+ property bool isBundle: false
+
+ property string headersInstallDir: "include"
+
+ Product {
+ name: "DllExport"
+ Depends { name: "Exporter.cmake" }
+ Group {
+ name: "API headers"
+ files: ["../dllexport.h"]
+ qbs.install: true
+ qbs.installDir: project.headersInstallDir
+ }
+ Group {
+ fileTagsFilter: ["Exporter.cmake.package"]
+ qbs.install: true
+ qbs.installDir: "/lib/cmake/DllExport"
+ }
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: FileInfo.joinPaths(
+ exportingProduct.qbs.installRoot,
+ exportingProduct.qbs.installPrefix,
+ project.headersInstallDir)
+ }
+ }
+
+ Library {
+ type: project.isStatic ? "staticlibrary" : "dynamiclibrary"
+ Depends { name: "cpp" }
+ Depends { name: "DllExport" }
+ Depends { name: "Exporter.cmake" }
+ Exporter.cmake.packageName: "Bar"
+ name: "Foo"
+ files: ["Foo.cpp"]
+ version: "1.2.3"
+ cpp.includePaths: "."
+ cpp.defines: "FOO_LIB"
+ Group {
+ name: "API headers"
+ files: ["Foo.h"]
+ qbs.install: true
+ qbs.installDir: project.headersInstallDir
+ }
+ install: true
+ installImportLib: true
+ Group {
+ fileTagsFilter: ["Exporter.cmake.package"]
+ qbs.install: true
+ qbs.installDir: "/lib/cmake/Bar"
+ }
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: FileInfo.joinPaths(
+ exportingProduct.qbs.installRoot,
+ exportingProduct.qbs.installPrefix,
+ project.headersInstallDir)
+ cpp.defines: ["FOO=1"].concat(project.isStatic ? ["FOO_LIB_STATIC"] : [])
+ cpp.commonCompilerFlags: "-DOTHER_DEF=1"
+ cpp.linkerFlags: exportingProduct.qbs.toolchain.contains("gcc") ? ["-s"] : []
+ }
+
+ Depends { name: 'bundle' }
+ bundle.isBundle: qbs.targetOS.includes("darwin") && project.isBundle
+ }
+}
diff --git a/tests/auto/blackbox/testdata/exports-cmake/find-cmake.qbs b/tests/auto/blackbox/testdata/exports-cmake/find-cmake.qbs
new file mode 100644
index 000000000..52f388966
--- /dev/null
+++ b/tests/auto/blackbox/testdata/exports-cmake/find-cmake.qbs
@@ -0,0 +1,46 @@
+import qbs.Probes
+
+Product {
+ Depends { name: "cpp" }
+
+ Probes.BinaryProbe {
+ id: cmakeProbe
+ names: "cmake"
+ }
+
+ Probes.BinaryProbe {
+ id: ninjaProbe
+ names: ["ninja"]
+ }
+
+ property bool test: {
+ var data = {
+ cmakeFound: cmakeProbe.found,
+ cmakeFilePath: cmakeProbe.filePath,
+ crossCompiling: qbs.targetPlatform !== qbs.hostPlatform,
+ installPrefix: qbs.installPrefix
+ };
+ data.buildEnv = {}
+ Object.assign(data.buildEnv, cpp.buildEnv); // deep copy buildEnv from a probe
+ if (qbs.toolchain.includes("gcc")) {
+ data.buildEnv["CC"] = cpp.cCompilerName;
+ data.buildEnv["CXX"] = cpp.cxxCompilerName;
+ } else {
+ data.buildEnv["CC"] = cpp.compilerName;
+ data.buildEnv["CXX"] = cpp.compilerName;
+ }
+
+ if (ninjaProbe.found) {
+ data.generator = "Ninja";
+ } else {
+ if (qbs.toolchain.includes("msvc")) {
+ data.generator = "NMake Makefiles"
+ } else if (qbs.toolchain.includes("mingw")) {
+ data.generator = "MinGW Makefiles";
+ } else if (qbs.toolchain.includes("gcc")) {
+ data.generator = "Unix Makefiles";
+ }
+ }
+ console.info("---" + JSON.stringify(data) + "---");
+ }
+}
diff --git a/tests/auto/blackbox/testdata/exports-pkgconfig/exports-pkgconfig.qbs b/tests/auto/blackbox/testdata/exports-pkgconfig/exports-pkgconfig.qbs
index 96d1cabb9..23331f93f 100644
--- a/tests/auto/blackbox/testdata/exports-pkgconfig/exports-pkgconfig.qbs
+++ b/tests/auto/blackbox/testdata/exports-pkgconfig/exports-pkgconfig.qbs
@@ -1,4 +1,5 @@
import qbs.FileInfo
+import qbs.Host
Project {
Product {
@@ -42,14 +43,14 @@ Project {
Depends { name: "Qt.core"; required: false }
Depends { name: "helper1" }
Depends { name: "helper3" }
- property bool someCondition: qbs.hostOS.contains("windows") // hostOS for easier testing
+ property bool someCondition: Host.os().includes("windows") // hostOS for easier testing
property bool someOtherCondition: someCondition
Properties {
condition: !someOtherCondition
cpp.driverFlags: ["-pthread"]
}
- cpp.defines: product.name
- cpp.includePaths: [FileInfo.joinPaths(product.qbs.installPrefix, "include")]
+ cpp.defines: exportingProduct.name
+ cpp.includePaths: [FileInfo.joinPaths(exportingProduct.qbs.installPrefix, "include")]
Qt.core.mocName: "muck"
}
@@ -94,7 +95,7 @@ Project {
Depends { name: "cpp" }
cpp.includePaths: [
"/opt/thesecondlib/include",
- product.sourceDirectory,
+ exportingProduct.sourceDirectory,
importingProduct.buildDirectory
]
property string hurz: importingProduct.name
diff --git a/tests/auto/blackbox/testdata/exports-qbs/consumer.qbs b/tests/auto/blackbox/testdata/exports-qbs/consumer.qbs
index 02affdfe8..073504400 100644
--- a/tests/auto/blackbox/testdata/exports-qbs/consumer.qbs
+++ b/tests/auto/blackbox/testdata/exports-qbs/consumer.qbs
@@ -1,4 +1,12 @@
+import qbs.Host
+
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "consumer"
qbsSearchPaths: "default/install-root/usr/qbs"
property string outTag: "cpp"
diff --git a/tests/auto/blackbox/testdata/exports-qbs/lib.qbs b/tests/auto/blackbox/testdata/exports-qbs/lib.qbs
index 53a472dbb..01f89a221 100644
--- a/tests/auto/blackbox/testdata/exports-qbs/lib.qbs
+++ b/tests/auto/blackbox/testdata/exports-qbs/lib.qbs
@@ -16,7 +16,7 @@ DynamicLibrary {
cpp.defines: ["MYLIB_BUILD"]
cpp.variantSuffix: qbs.buildVariant === "debug" ? "d" : ""
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
files: ["mylib.cpp"]
@@ -38,8 +38,8 @@ DynamicLibrary {
Export {
Depends { name: "cpp" }
- property string includeDir: product.sourceDirectory
- property var config: product.config
+ property string includeDir: exportingProduct.sourceDirectory
+ property var config: exportingProduct.config
Properties {
condition: true
cpp.includePaths: [includeDir]
@@ -52,7 +52,7 @@ DynamicLibrary {
condition: true
prefixMapping: [{
prefix: includeDir,
- replacement: FileInfo.joinPaths(qbs.installPrefix, product.headersInstallDir)
+ replacement: FileInfo.joinPaths(exportingProduct.qbs.installPrefix, exportingProduct.headersInstallDir)
}]
}
}
diff --git a/tests/auto/blackbox/testdata/exports-qbs/tool.qbs b/tests/auto/blackbox/testdata/exports-qbs/tool.qbs
index 655597cf7..d3626bead 100644
--- a/tests/auto/blackbox/testdata/exports-qbs/tool.qbs
+++ b/tests/auto/blackbox/testdata/exports-qbs/tool.qbs
@@ -33,7 +33,7 @@ CppApplication {
}
Export {
- property stringList toolTags: product.toolTags
+ property stringList toolTags: exportingProduct.toolTags
property stringList outTags: [importingProduct.outTag]
property var helper2Obj: Helper2
Rule {
@@ -49,7 +49,7 @@ CppApplication {
prepare: {
var cmd = new Command(explicitlyDependsOn["MyTool.tool"][0].filePath,
[input.filePath, output.filePath]);
- cmd.description = input.fileName + " -> " + output.fileName;
+ cmd.description = "compiling" + input.fileName + " to " + output.fileName;
return [cmd];
}
}
diff --git a/tests/auto/blackbox/testdata/external-libs/external-libs.qbs b/tests/auto/blackbox/testdata/external-libs/external-libs.qbs
index 619f40867..610df0507 100644
--- a/tests/auto/blackbox/testdata/external-libs/external-libs.qbs
+++ b/tests/auto/blackbox/testdata/external-libs/external-libs.qbs
@@ -7,7 +7,7 @@ Project {
destinationDirectory: project.libDir
Depends { name: "cpp" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
files: ["lib1.cpp"]
@@ -18,7 +18,7 @@ Project {
Depends { name: "cpp" }
Depends { name: "lib1" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
files: ["lib2.cpp"]
diff --git a/tests/auto/blackbox/testdata/fileDependencies/awesomelib/awesome.h b/tests/auto/blackbox/testdata/fileDependencies/awesomelib/awesome.h
index 4633f9746..8f1f25087 100644
--- a/tests/auto/blackbox/testdata/fileDependencies/awesomelib/awesome.h
+++ b/tests/auto/blackbox/testdata/fileDependencies/awesomelib/awesome.h
@@ -31,6 +31,6 @@
void doAwesomeStuff()
{
- printf(magnificentMessage);
+ std::printf(magnificentMessage);
}
diff --git a/tests/auto/blackbox/testdata/freedesktop/freedesktop.qbs b/tests/auto/blackbox/testdata/freedesktop/freedesktop.qbs
new file mode 100644
index 000000000..1efe5c91c
--- /dev/null
+++ b/tests/auto/blackbox/testdata/freedesktop/freedesktop.qbs
@@ -0,0 +1,23 @@
+Project {
+ CppApplication {
+ name: "main"
+ install: true
+ files: [
+ "main.cpp",
+ "myapp.desktop",
+ "myapp.appdata.xml",
+ ]
+
+ Depends { name: "freedesktop" }
+
+ freedesktop.appName: "My App"
+ freedesktop.desktopKeys: ({
+ 'Icon': "myapp.png"
+ })
+
+ Group {
+ files: "myapp.png"
+ fileTags: "freedesktop.appIcon"
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/freedesktop/main.cpp b/tests/auto/blackbox/testdata/freedesktop/main.cpp
new file mode 100644
index 000000000..905869dfa
--- /dev/null
+++ b/tests/auto/blackbox/testdata/freedesktop/main.cpp
@@ -0,0 +1,4 @@
+int main()
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata/freedesktop/myapp.appdata.xml b/tests/auto/blackbox/testdata/freedesktop/myapp.appdata.xml
new file mode 100644
index 000000000..3cf0a5641
--- /dev/null
+++ b/tests/auto/blackbox/testdata/freedesktop/myapp.appdata.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<component type="desktop">
+ <id>myapp.desktop</id>
+ <metadata_license>CC0</metadata_license>
+ <name>MyApp</name>
+ <summary>The coolest app ever</summary>
+
+ <description>
+ <p>This is a cool application.</p>
+ </description>
+
+ <url type="homepage">https://software.house/myapp</url>
+ <project_license>GPL-2.0+</project_license>
+ <developer_name>Coding Wizard</developer_name>
+</component>
diff --git a/tests/auto/blackbox/testdata/freedesktop/myapp.desktop b/tests/auto/blackbox/testdata/freedesktop/myapp.desktop
new file mode 100644
index 000000000..dac3014c3
--- /dev/null
+++ b/tests/auto/blackbox/testdata/freedesktop/myapp.desktop
@@ -0,0 +1,4 @@
+[Desktop Entry]
+GenericName=Image Editor
+Comment=Create images and edit photographs
+Icon=overridden.png
diff --git a/tests/auto/blackbox/testdata/freedesktop/myapp.png b/tests/auto/blackbox/testdata/freedesktop/myapp.png
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/freedesktop/myapp.png
diff --git a/tests/auto/blackbox/testdata/generate-linker-map-file/generate-linker-map-file.qbs b/tests/auto/blackbox/testdata/generate-linker-map-file/generate-linker-map-file.qbs
index 815e64853..0d67fa54c 100644
--- a/tests/auto/blackbox/testdata/generate-linker-map-file/generate-linker-map-file.qbs
+++ b/tests/auto/blackbox/testdata/generate-linker-map-file/generate-linker-map-file.qbs
@@ -2,11 +2,14 @@ Project {
CppApplication {
name: "app-map"
files: ["main.cpp"]
+ // lld-link has different flag for map files, test it by switching to "lld" linkerVariant
+ Properties { condition: qbs.toolchain.includes("clang-cl"); cpp.linkerVariant: "lld" }
cpp.generateLinkerMapFile: true
}
CppApplication {
name: "app-nomap"
files: ["main.cpp"]
+ Properties { condition: qbs.toolchain.includes("clang-cl"); cpp.linkerVariant: "lld" }
cpp.generateLinkerMapFile: false
}
CppApplication {
@@ -16,8 +19,8 @@ Project {
Probe {
id: toolchainProbe
- property bool isUsed: qbs.toolchain.contains("msvc")
- || qbs.toolchain.contains("gcc")
+ property bool isUsed: qbs.toolchain.includes("msvc")
+ || qbs.toolchain.includes("gcc")
configure: {
console.info("use test: " + isUsed);
}
diff --git a/tests/auto/blackbox/testdata/generator/generator.qbs b/tests/auto/blackbox/testdata/generator/generator.qbs
index d0857beb5..9f6d452e7 100644
--- a/tests/auto/blackbox/testdata/generator/generator.qbs
+++ b/tests/auto/blackbox/testdata/generator/generator.qbs
@@ -18,12 +18,12 @@ CppApplication {
var f = new TextFile(input.filePath, TextFile.ReadOnly);
var content = f.readAll();
f.close();
- if (content.contains("file1")) {
+ if (content.includes("file1")) {
f = new TextFile(outputs.file1[0].filePath, TextFile.WriteOnly);
f.writeLine("void f1() {}");
f.close();
}
- if (content.contains("file2")) {
+ if (content.includes("file2")) {
f = new TextFile(outputs.file2[0].filePath, TextFile.WriteOnly);
f.writeLine("void f2() {}");
f.close();
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
index 7347b1211..47275821f 100644
--- a/tests/auto/blackbox/testdata/groups-in-modules/groups-in-modules.qbs
+++ b/tests/auto/blackbox/testdata/groups-in-modules/groups-in-modules.qbs
@@ -1,11 +1,22 @@
+import qbs.Host
+
Project {
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
Depends { name: "dep" }
Depends { name: "helper" }
Depends {
name: "helper3"
required: false
}
+ Depends { name: "helper7" }
+ helper7.fileName: "helper7.c"
+
type: ["diamond"]
files: [
diff --git a/tests/auto/blackbox/testdata/groups-in-modules/imports/Helper7Base.qbs b/tests/auto/blackbox/testdata/groups-in-modules/imports/Helper7Base.qbs
new file mode 100644
index 000000000..cc55e6351
--- /dev/null
+++ b/tests/auto/blackbox/testdata/groups-in-modules/imports/Helper7Base.qbs
@@ -0,0 +1,8 @@
+Module {
+ property string directory
+ property string fileName
+ Group {
+ prefix: directory + "/"
+ files: fileName
+ }
+}
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
index cbd30a35d..9dc903411 100644
--- a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/helper.qbs
+++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper/helper.qbs
@@ -39,7 +39,7 @@ Module {
prepare: {
var cmd = new Command(FileInfo.joinPaths(product.buildDirectory, product.targetName), [input.filePath, output.filePath]);
- cmd.description = "compile " + input.fileName + " => " + output.fileName;
+ cmd.description = "compiling " + input.fileName + " to " + output.fileName;
return [cmd];
}
}
diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper7/helper7.c b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper7/helper7.c
new file mode 100644
index 000000000..a83f5476e
--- /dev/null
+++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper7/helper7.c
@@ -0,0 +1 @@
+void helper7(void) {}
diff --git a/tests/auto/blackbox/testdata/groups-in-modules/modules/helper7/helper7.qbs b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper7/helper7.qbs
new file mode 100644
index 000000000..90e1f11e2
--- /dev/null
+++ b/tests/auto/blackbox/testdata/groups-in-modules/modules/helper7/helper7.qbs
@@ -0,0 +1,3 @@
+Helper7Base {
+ directory: path
+}
diff --git a/tests/auto/blackbox/testdata/grpc/grpc.cpp b/tests/auto/blackbox/testdata/grpc/grpc.cpp
index 81995601d..5f85c5b40 100644
--- a/tests/auto/blackbox/testdata/grpc/grpc.cpp
+++ b/tests/auto/blackbox/testdata/grpc/grpc.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2019 Ivan Komissarov
-** Contact: abbapoh@gmail.com
+** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
diff --git a/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs b/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs
index 8ee3dd9c9..e36ae47ca 100644
--- a/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs
+++ b/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs
@@ -1,19 +1,27 @@
-import qbs
+import qbs.Host
CppApplication {
name: "grpc_cpp"
consoleApplication: true
- condition: hasDependencies
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result && hasDependencies;
+ }
Depends { name: "cpp" }
- cpp.cxxLanguageVersion: "c++11"
+ cpp.cxxLanguageVersion: "c++17"
+ cpp.minimumMacosVersion: "10.15"
cpp.warningLevel: "none"
Depends { name: "protobuf.cpp"; required: false }
+ Depends { name: "grpc++"; id: grpcpp; required: false }
protobuf.cpp.useGrpc: true
property bool hasDependencies: {
console.info("has grpc: " + protobuf.cpp.present);
+ console.info("has modules: " + grpcpp.present);
return protobuf.cpp.present;
}
diff --git a/tests/auto/blackbox/testdata/host-os-properties/host-os-properties.qbs b/tests/auto/blackbox/testdata/host-os-properties/host-os-properties.qbs
new file mode 100644
index 000000000..9d050e166
--- /dev/null
+++ b/tests/auto/blackbox/testdata/host-os-properties/host-os-properties.qbs
@@ -0,0 +1,16 @@
+import qbs.Host
+
+CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
+ consoleApplication: true
+ cpp.defines: [
+ 'HOST_ARCHITECTURE="' + Host.architecture() + '"',
+ 'HOST_PLATFORM="' + Host.platform() + '"'
+ ]
+ files: "main.cpp"
+}
diff --git a/tests/auto/blackbox/testdata/host-os-properties/main.cpp b/tests/auto/blackbox/testdata/host-os-properties/main.cpp
new file mode 100644
index 000000000..129c16379
--- /dev/null
+++ b/tests/auto/blackbox/testdata/host-os-properties/main.cpp
@@ -0,0 +1,7 @@
+#include <cstdio>
+
+int main() {
+ std::printf("HOST_ARCHITECTURE = %s\n", HOST_ARCHITECTURE);
+ std::printf("HOST_PLATFORM = %s\n", HOST_PLATFORM);
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata/importing-product/importing-product.qbs b/tests/auto/blackbox/testdata/importing-product/importing-product.qbs
index dfc4cd65e..54ad727b7 100644
--- a/tests/auto/blackbox/testdata/importing-product/importing-product.qbs
+++ b/tests/auto/blackbox/testdata/importing-product/importing-product.qbs
@@ -16,7 +16,7 @@ Project {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Copying file";
+ cmd.description = "copying file";
cmd.sourceCode = function() {
File.copy(input.filePath, output.filePath);
}
diff --git a/tests/auto/blackbox/testdata/includeLookup/includeLookup.qbs b/tests/auto/blackbox/testdata/includeLookup/includeLookup.qbs
index 182d1e232..903d170d0 100644
--- a/tests/auto/blackbox/testdata/includeLookup/includeLookup.qbs
+++ b/tests/auto/blackbox/testdata/includeLookup/includeLookup.qbs
@@ -1,9 +1,16 @@
import qbs.FileInfo
+import qbs.Host
Project {
property string name: 'includeLookup'
qbsSearchPaths: '.'
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
type: 'application'
consoleApplication: true
name: project.name
diff --git a/tests/auto/blackbox/testdata/includeLookup/main.cpp b/tests/auto/blackbox/testdata/includeLookup/main.cpp
index c7213c768..a91914f73 100644
--- a/tests/auto/blackbox/testdata/includeLookup/main.cpp
+++ b/tests/auto/blackbox/testdata/includeLookup/main.cpp
@@ -30,7 +30,7 @@
int main()
{
- printf("%s..\n", TEXT);
+ std::printf("%s..\n", TEXT);
return 0;
}
diff --git a/tests/auto/blackbox/testdata/innosetup/innosetup.qbs b/tests/auto/blackbox/testdata/innosetup/innosetup.qbs
deleted file mode 100644
index c9f6a22e8..000000000
--- a/tests/auto/blackbox/testdata/innosetup/innosetup.qbs
+++ /dev/null
@@ -1,22 +0,0 @@
-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"]
- qbs.targetPlatform: "windows"
- }
- InnoSetup {
- name: "Example1"
- files: [FileInfo.joinPaths(innosetup.toolchainInstallPath, "Examples", name + ".iss")]
- qbs.targetPlatform: "windows"
- }
-}
diff --git a/tests/auto/blackbox/testdata/innosetupDependencies/innosetupDependencies.qbs b/tests/auto/blackbox/testdata/innosetupDependencies/innosetupDependencies.qbs
deleted file mode 100644
index db65e127f..000000000
--- a/tests/auto/blackbox/testdata/innosetupDependencies/innosetupDependencies.qbs
+++ /dev/null
@@ -1,72 +0,0 @@
-import qbs.TextFile
-
-Project {
- InnoSetup {
- Depends { name: "app" }
- Depends { name: "lib" }
- name: "QbsSetup"
- targetName: "qbs.setup.test"
- version: "1.5"
- files: [
- "test.iss"
- ]
- innosetup.verboseOutput: true
- innosetup.defines: [
- "MyProgram=" + name,
- "MyProgramVersion=" + version,
- "buildDirectory=" + project.buildDirectory
- ]
- innosetup.compilerFlags: ["/V9"]
- destinationDirectory: project.buildDirectory
- }
- Application {
- Depends { name: "cpp" }
- name: "app"
- files: ["main.c"]
- Group {
- fileTagsFilter: product.type
- qbs.install: true
- }
- destinationDirectory: project.buildDirectory
- }
- DynamicLibrary {
- Depends { name: "cpp" }
- name: "lib"
- files: ["main.c"]
- Group {
- fileTagsFilter: product.type
- qbs.install: true
- }
- Rule {
- // This rule tries to provoke the installer into building too early (and the test
- // verifies that it does not) by causing the build of the installables to take
- // a lot longer.
- multiplex: true
- outputFileTags: ["c"]
- outputArtifacts: {
- var artifacts = [];
- for (var i = 0; i < 96; ++i)
- artifacts.push({ filePath: "c" + i + ".c", fileTags: ["c"] });
- return artifacts;
- }
- prepare: {
- var cmd = new JavaScriptCommand();
- cmd.silent = true;
- cmd.sourceCode = function() {
- for (var i = 0; i < outputs["c"].length; ++i) {
- var tf;
- try {
- tf = new TextFile(outputs["c"][i].filePath, TextFile.WriteOnly);
- tf.writeLine("int main" + i + "() { return 0; }");
- } finally {
- if (tf)
- tf.close();
- }
- }
- };
- return [cmd];
- }
- }
- destinationDirectory: project.buildDirectory
- }
-}
diff --git a/tests/auto/blackbox/testdata/input-tags-change-tracking/input-tags-change-tracking.qbs b/tests/auto/blackbox/testdata/input-tags-change-tracking/input-tags-change-tracking.qbs
index ef2c5c55b..1e4248009 100644
--- a/tests/auto/blackbox/testdata/input-tags-change-tracking/input-tags-change-tracking.qbs
+++ b/tests/auto/blackbox/testdata/input-tags-change-tracking/input-tags-change-tracking.qbs
@@ -43,10 +43,10 @@ Product {
inputs: "txt"
outputFileTags: "p_tag"
outputArtifacts: {
- if (input.fileTags.contains("empty"))
+ if (input.fileTags.includes("empty"))
return [];
return [{
- filePath: input.fileTags.contains("y") ? "y.out" : "x.out",
+ filePath: input.fileTags.includes("y") ? "y.out" : "x.out",
fileTags: "p_tag"
}]
}
diff --git a/tests/auto/blackbox/testdata/inputs-from-dependencies/inputs-from-dependencies.qbs b/tests/auto/blackbox/testdata/inputs-from-dependencies/inputs-from-dependencies.qbs
index 919060c73..f9993ecf3 100644
--- a/tests/auto/blackbox/testdata/inputs-from-dependencies/inputs-from-dependencies.qbs
+++ b/tests/auto/blackbox/testdata/inputs-from-dependencies/inputs-from-dependencies.qbs
@@ -37,7 +37,7 @@ Project {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Gathering text files";
+ cmd.description = "gathering text files";
cmd.sourceCode = function() {
for (i in inputs.txt)
console.info(inputs.txt[i].filePath);
diff --git a/tests/auto/blackbox/testdata/install-locations/install-locations.qbs b/tests/auto/blackbox/testdata/install-locations/install-locations.qbs
index 8a97f74a1..ba51c0dc1 100644
--- a/tests/auto/blackbox/testdata/install-locations/install-locations.qbs
+++ b/tests/auto/blackbox/testdata/install-locations/install-locations.qbs
@@ -1,16 +1,24 @@
Project {
property bool dummy: {
- if (qbs.targetOS.contains("windows"))
+ if (qbs.targetOS.includes("windows")) {
console.info("is windows");
- else if (qbs.targetOS.contains("macos"))
- console.info("is mac");
- else
+ } else if (qbs.targetOS.includes("darwin")) {
+ console.info("is darwin");
+ if (qbs.targetOS.includes("macos"))
+ console.info("is mac");
+ } else {
console.info("is unix");
+ }
+
+ if (qbs.toolchain.includes("mingw"))
+ console.info("is mingw");
}
CppApplication {
name: "theapp"
install: true
+ installDebugInformation: true
files: "main.cpp"
+ cpp.separateDebugInformation: true
Group {
fileTagsFilter: "application"
fileTags: "some-tag"
@@ -20,7 +28,17 @@ Project {
name: "thelib"
install: true
installImportLib: true
+ installDebugInformation: true
Depends { name: "cpp" }
+ cpp.separateDebugInformation: true
files: "thelib.cpp"
}
+ LoadableModule {
+ name: "theplugin"
+ install: true
+ installDebugInformation: true
+ Depends { name: "cpp" }
+ cpp.separateDebugInformation: true
+ files: "theplugin.cpp"
+ }
}
diff --git a/tests/auto/blackbox/testdata/install-locations/theplugin.cpp b/tests/auto/blackbox/testdata/install-locations/theplugin.cpp
new file mode 100644
index 000000000..ac1ede090
--- /dev/null
+++ b/tests/auto/blackbox/testdata/install-locations/theplugin.cpp
@@ -0,0 +1,3 @@
+#include "../dllexport.h"
+
+DLL_EXPORT void pluginFunc() {}
diff --git a/tests/auto/blackbox/testdata/installable-as-auxiliary-input/installable-as-auxiliary-input.qbs b/tests/auto/blackbox/testdata/installable-as-auxiliary-input/installable-as-auxiliary-input.qbs
index 2d37e85d6..a445fc35b 100644
--- a/tests/auto/blackbox/testdata/installable-as-auxiliary-input/installable-as-auxiliary-input.qbs
+++ b/tests/auto/blackbox/testdata/installable-as-auxiliary-input/installable-as-auxiliary-input.qbs
@@ -1,10 +1,17 @@
import qbs.File
import qbs.FileInfo
+import qbs.Host
import qbs.TextFile
Project {
name: "p"
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "app"
Depends { name: "installed-header" }
Rule {
@@ -42,7 +49,7 @@ Project {
Export {
Depends { name: "cpp" }
- cpp.includePaths: FileInfo.joinPaths(qbs.installRoot, product.installDir);
+ cpp.includePaths: FileInfo.joinPaths(qbs.installRoot, exportingProduct.installDir);
}
Rule {
@@ -50,7 +57,7 @@ Project {
Artifact { filePath: "theheader.h.in"; fileTags: "header.in" }
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.sourceCode = function() {
for (var i = 0; i < 1000; ++i) { // Artificial delay.
var file = new TextFile(output.filePath, TextFile.WriteOnly);
@@ -69,7 +76,7 @@ Project {
Artifact { filePath: "theheader.h"; fileTags: "header" }
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); };
return [cmd];
}
diff --git a/tests/auto/blackbox/testdata/installable/installable.qbs b/tests/auto/blackbox/testdata/installable/installable.qbs
index 56feb6ec5..de93cf61f 100644
--- a/tests/auto/blackbox/testdata/installable/installable.qbs
+++ b/tests/auto/blackbox/testdata/installable/installable.qbs
@@ -11,6 +11,7 @@ Project {
}
install: true
+ installDebugInformation: false
installDir: ""
}
@@ -27,7 +28,7 @@ Project {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName;
+ 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)
diff --git a/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs b/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs
index aa40b7691..6f84206be 100644
--- a/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs
+++ b/tests/auto/blackbox/testdata/installed-transformer-output/qbs668.qbs
@@ -18,7 +18,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating file:'" + output.fileName + "'";
+ cmd.description = "creating file:'" + output.fileName + "'";
cmd.highlight = "codegen";
cmd.sourceCode = function() {
var file = new TextFile(output.filePath, TextFile.WriteOnly);
diff --git a/tests/auto/blackbox/testdata/installpackage/installpackage.qbs b/tests/auto/blackbox/testdata/installpackage/installpackage.qbs
index a0649a578..3396b79a4 100644
--- a/tests/auto/blackbox/testdata/installpackage/installpackage.qbs
+++ b/tests/auto/blackbox/testdata/installpackage/installpackage.qbs
@@ -2,7 +2,7 @@ Project {
CppApplication {
name: "public_tool"
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
Depends { name: "mylib" }
@@ -20,7 +20,7 @@ Project {
}
DynamicLibrary {
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
Depends { name: "cpp" }
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
index b08fcd4a3..a07beda57 100644
--- a/tests/auto/blackbox/testdata/invalid-command-property/invalid-command-property.qbs
+++ b/tests/auto/blackbox/testdata/invalid-command-property/invalid-command-property.qbs
@@ -16,7 +16,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating output";
+ cmd.description = "creating output";
if (product.errorType === "qobject")
cmd.dummy = new TextFile(input.filePath, TextFile.ReadOnly);
else if (product.errorType === "input")
diff --git a/tests/auto/blackbox/testdata/jsextensions-binaryfile/binaryfile.qbs b/tests/auto/blackbox/testdata/jsextensions-binaryfile/binaryfile.qbs
index 1e7426744..5824fe518 100644
--- a/tests/auto/blackbox/testdata/jsextensions-binaryfile/binaryfile.qbs
+++ b/tests/auto/blackbox/testdata/jsextensions-binaryfile/binaryfile.qbs
@@ -36,6 +36,9 @@ Product {
destination.write(source.atEof() ? [ 0xFF ] : [ 0x00 ]);
source.close();
destination.close();
+ source = new BinaryFile("destination.dat", BinaryFile.ReadOnly);
+ destination = new BinaryFile("destination2.dat", BinaryFile.WriteOnly);
+ destination.write(source.read(8));
};
commands.push(cmd);
return commands;
diff --git a/tests/auto/blackbox/testdata/jsextensions-file/file.qbs b/tests/auto/blackbox/testdata/jsextensions-file/file.qbs
index 6adf714e2..7a47cf3cd 100644
--- a/tests/auto/blackbox/testdata/jsextensions-file/file.qbs
+++ b/tests/auto/blackbox/testdata/jsextensions-file/file.qbs
@@ -33,10 +33,10 @@ Product {
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"))
+ if (entries.length < 3 || !entries.includes("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"))
+ if (entries.length < 1 || !entries.includes("zePath"))
throw new Error("Directory did not contain only zePath");
var moveSource = FileInfo.joinPaths(product.sourceDirectory, "tomove.txt");
var moveTarget = FileInfo.joinPaths(product.sourceDirectory, "moved.txt");
diff --git a/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs b/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs
index d63ba2962..0e727d04b 100644
--- a/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs
+++ b/tests/auto/blackbox/testdata/jsextensions-fileinfo/fileinfo.qbs
@@ -39,6 +39,8 @@ Product {
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.writeLine(FileInfo.pathListSeparator());
+ output.writeLine(FileInfo.pathSeparator());
output.close();
};
return [cmd];
diff --git a/tests/auto/blackbox/testdata/jsextensions-host/host.qbs b/tests/auto/blackbox/testdata/jsextensions-host/host.qbs
new file mode 100644
index 000000000..08d13b25d
--- /dev/null
+++ b/tests/auto/blackbox/testdata/jsextensions-host/host.qbs
@@ -0,0 +1,31 @@
+import qbs.FileInfo
+import qbs.Host
+import qbs.TextFile
+
+Product {
+ type: ["dummy"]
+ Rule {
+ multiplex: true
+ outputFileTags: "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("architecture: " +Host.architecture());
+ output.writeLine("os: " + Host.os());
+ output.writeLine("platform: " + Host.platform());
+ output.writeLine("osVersion: " + Host.osVersion());
+ output.writeLine("osBuildVersion: " + Host.osBuildVersion());
+ output.writeLine("osVersionParts: " + Host.osVersionParts());
+ output.writeLine("osVersionMajor: " + Host.osVersionMajor());
+ output.writeLine("osVersionMinor: " + Host.osVersionMinor());
+ output.writeLine("osVersionPatch: " + Host.osVersionPatch());
+ output.writeLine("nullDevice: " + Host.nullDevice());
+ output.close();
+ };
+ return [cmd];
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/jsextensions-process/main.cpp b/tests/auto/blackbox/testdata/jsextensions-process/main.cpp
index df769de87..3f28fe091 100644
--- a/tests/auto/blackbox/testdata/jsextensions-process/main.cpp
+++ b/tests/auto/blackbox/testdata/jsextensions-process/main.cpp
@@ -4,17 +4,17 @@
int main(int argc, char *argv[])
{
- if (argc != 2 || strcmp(argv[1], "help") != 0) {
- fprintf(stderr, "First argument to this program must be 'help'.\n");
+ if (argc != 2 || std::strcmp(argv[1], "help") != 0) {
+ std::fprintf(stderr, "First argument to this program must be 'help'.\n");
return 1;
}
const char *env = std::getenv("SOME_ENV");
- if (!env || strcmp(env, "why, hello!") != 0) {
- fprintf(stderr, "The SOME_ENV environment variable must be 'why, hello!'.\n");
+ if (!env || std::strcmp(env, "why, hello!") != 0) {
+ std::fprintf(stderr, "The SOME_ENV environment variable must be 'why, hello!'.\n");
return 1;
}
- printf("qbs jsextensions-process test\n");
+ std::printf("qbs jsextensions-process test\n");
return 0;
}
diff --git a/tests/auto/blackbox/testdata/jsextensions-process/process.qbs b/tests/auto/blackbox/testdata/jsextensions-process/process.qbs
index eba95d005..50f22cdfc 100644
--- a/tests/auto/blackbox/testdata/jsextensions-process/process.qbs
+++ b/tests/auto/blackbox/testdata/jsextensions-process/process.qbs
@@ -1,10 +1,17 @@
import qbs.Environment
import qbs.FileInfo
+import qbs.Host
import qbs.Process
import qbs.TextFile
Project {
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
Depends { name: "cpp" }
type: ["dummy"]
name: "dummy"
@@ -48,7 +55,7 @@ Project {
// closeWriteChannel test
process = new Process();
- if (product.qbs.hostOS.contains("windows"))
+ if (Host.os().includes("windows"))
process.start(product.qbs.windowsShellPath,
["/C", product.qbs.windowsSystemRoot + "\\system32\\sort.exe"]);
else
@@ -69,7 +76,7 @@ Project {
testReadlineFile.close();
process = new Process();
- if (product.qbs.hostOS.contains("windows"))
+ if (Host.os().includes("windows"))
process.exec(product.qbs.windowsShellPath,
["/C", "type", "123.txt"],
true);
diff --git a/tests/auto/blackbox/testdata/last-module-candidate-broken/last-module-candidate-broken.qbs b/tests/auto/blackbox/testdata/last-module-candidate-broken/last-module-candidate-broken.qbs
new file mode 100644
index 000000000..db7dc2265
--- /dev/null
+++ b/tests/auto/blackbox/testdata/last-module-candidate-broken/last-module-candidate-broken.qbs
@@ -0,0 +1,5 @@
+CppApplication {
+ qbsSearchPaths: "qbs"
+ Depends { name: "Foo" }
+ files: "main.cpp"
+}
diff --git a/tests/auto/blackbox/testdata/last-module-candidate-broken/main.cpp b/tests/auto/blackbox/testdata/last-module-candidate-broken/main.cpp
new file mode 100644
index 000000000..76e819701
--- /dev/null
+++ b/tests/auto/blackbox/testdata/last-module-candidate-broken/main.cpp
@@ -0,0 +1 @@
+int main() { return 0; }
diff --git a/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo1.qbs b/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo1.qbs
new file mode 100644
index 000000000..ba08b862b
--- /dev/null
+++ b/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo1.qbs
@@ -0,0 +1,3 @@
+Module {
+ condition: false
+}
diff --git a/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo2.qbs b/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo2.qbs
new file mode 100644
index 000000000..0bc383b86
--- /dev/null
+++ b/tests/auto/blackbox/testdata/last-module-candidate-broken/qbs/modules/Foo/Foo2.qbs
@@ -0,0 +1,2 @@
+Group {
+}
diff --git a/tests/auto/blackbox/testdata/ld/ld.qbs b/tests/auto/blackbox/testdata/ld/ld.qbs
index 25fede1b0..cb04d3d9a 100644
--- a/tests/auto/blackbox/testdata/ld/ld.qbs
+++ b/tests/auto/blackbox/testdata/ld/ld.qbs
@@ -5,7 +5,7 @@ Project {
targetName: "qbs can handle any file paths, even the crazy ones! ;)"
files: ["coreutils.cpp", "coreutils.h"]
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
cpp.sonamePrefix: "@rpath"
}
diff --git a/tests/auto/blackbox/testdata/ld/main.cpp b/tests/auto/blackbox/testdata/ld/main.cpp
index fd6b72f3b..bcf293705 100644
--- a/tests/auto/blackbox/testdata/ld/main.cpp
+++ b/tests/auto/blackbox/testdata/ld/main.cpp
@@ -27,10 +27,11 @@
****************************************************************************/
#include "coreutils.h"
-#include <stdio.h>
+
+#include <cstdio>
int main(int argc, char *argv[])
{
- printf("%d\n", foo());
+ std::printf("%d\n", foo());
return 0;
}
diff --git a/tests/auto/blackbox/testdata/lexyacc/modules/bisonhelper/bisonhelper.qbs b/tests/auto/blackbox/testdata/lexyacc/modules/bisonhelper/bisonhelper.qbs
index 449b130e2..4cce9a1e3 100644
--- a/tests/auto/blackbox/testdata/lexyacc/modules/bisonhelper/bisonhelper.qbs
+++ b/tests/auto/blackbox/testdata/lexyacc/modules/bisonhelper/bisonhelper.qbs
@@ -1,4 +1,3 @@
-import qbs
import qbs.Process
Module {
@@ -8,7 +7,7 @@ Module {
property string yaccBinary: lex_yacc.yaccBinary
configure: {
var p = Process();
- found = p.exec(yaccBinary, ["-V"]) == 0 && p.readStdOut().contains("bison");
+ found = p.exec(yaccBinary, ["-V"]) == 0 && p.readStdOut().includes("bison");
p.close();
}
}
diff --git a/tests/auto/blackbox/testdata/lexyacc/one-grammar/one-grammar.qbs b/tests/auto/blackbox/testdata/lexyacc/one-grammar/one-grammar.qbs
index 6cd334247..faa5d1fac 100644
--- a/tests/auto/blackbox/testdata/lexyacc/one-grammar/one-grammar.qbs
+++ b/tests/auto/blackbox/testdata/lexyacc/one-grammar/one-grammar.qbs
@@ -1,4 +1,12 @@
+import qbs.Host
+
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
qbsSearchPaths: ".."
Depends { name: "bisonhelper" }
Depends { name: "lex_yacc" }
@@ -11,10 +19,10 @@ CppApplication {
Probe {
id: pathCheck
property string theDir: {
- if (qbs.targetOS.contains("windows")) {
- if (qbs.toolchain.contains("mingw"))
+ if (qbs.targetOS.includes("windows")) {
+ if (qbs.toolchain.includes("mingw"))
return cpp.toolchainInstallPath;
- if (qbs.toolchain.contains("clang") && qbs.sysroot)
+ if (qbs.toolchain.includes("clang") && qbs.sysroot)
return qbs.sysroot + "/bin";
}
}
diff --git a/tests/auto/blackbox/testdata/lexyacc/two-grammars/two-grammars.qbs b/tests/auto/blackbox/testdata/lexyacc/two-grammars/two-grammars.qbs
index 7b0c1e515..648860d9c 100644
--- a/tests/auto/blackbox/testdata/lexyacc/two-grammars/two-grammars.qbs
+++ b/tests/auto/blackbox/testdata/lexyacc/two-grammars/two-grammars.qbs
@@ -1,7 +1,13 @@
+import qbs.Host
+
CppApplication {
Depends { name: "lex_yacc" }
consoleApplication: true
cpp.includePaths: ".."
+ Properties {
+ condition: Host.os().includes("darwin") && qbs.toolchain.includes("clang")
+ cpp.cFlags: "-Wno-implicit-function-declaration"
+ }
files: [
"g1.l",
"g1.y",
diff --git a/tests/auto/blackbox/testdata/linker-library-duplicates/setup-run-environment.qbs b/tests/auto/blackbox/testdata/linker-library-duplicates/setup-run-environment.qbs
index 9723fd3f5..c41e8f1d7 100644
--- a/tests/auto/blackbox/testdata/linker-library-duplicates/setup-run-environment.qbs
+++ b/tests/auto/blackbox/testdata/linker-library-duplicates/setup-run-environment.qbs
@@ -1,5 +1,3 @@
-import qbs 1.0
-
Project {
DynamicLibrary {
id: idLib1
@@ -8,6 +6,11 @@ Project {
files: ["lib1.cpp"]
Depends { name: "bundle" }
bundle.isBundle: false
+ Probe {
+ id: checker
+ property bool isGcc: qbs.toolchain.contains("gcc")
+ configure: { console.info("is gcc: " + isGcc); }
+ }
}
DynamicLibrary {
diff --git a/tests/auto/blackbox/testdata/linker-module-definition/linker-module-definition.qbs b/tests/auto/blackbox/testdata/linker-module-definition/linker-module-definition.qbs
new file mode 100644
index 000000000..cae80e651
--- /dev/null
+++ b/tests/auto/blackbox/testdata/linker-module-definition/linker-module-definition.qbs
@@ -0,0 +1,20 @@
+import qbs.Host
+
+Project {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
+ DynamicLibrary {
+ name: "testlib"
+ Depends { name: "cpp"}
+ files: ["testlib.cpp", "testlib.def"]
+ }
+ CppApplication {
+ name: "testapp"
+ Depends { name: "testlib"}
+ files: ["testapp.cpp"]
+ }
+}
diff --git a/tests/auto/blackbox/testdata/linker-module-definition/testapp.cpp b/tests/auto/blackbox/testdata/linker-module-definition/testapp.cpp
new file mode 100644
index 000000000..7cb5ee901
--- /dev/null
+++ b/tests/auto/blackbox/testdata/linker-module-definition/testapp.cpp
@@ -0,0 +1,39 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** 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.
+**
+****************************************************************************/
+
+extern void foo();
+extern void bar();
+
+int main()
+{
+ foo();
+ bar();
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata/linker-module-definition/testlib.cpp b/tests/auto/blackbox/testdata/linker-module-definition/testlib.cpp
new file mode 100644
index 000000000..4af8dd9bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/linker-module-definition/testlib.cpp
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** 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 <cstdio>
+
+void foo()
+{
+ std::printf("foo\n");
+}
+
+void bar()
+{
+ std::printf("bar\n");
+}
diff --git a/tests/auto/blackbox/testdata/linker-module-definition/testlib.def b/tests/auto/blackbox/testdata/linker-module-definition/testlib.def
new file mode 100644
index 000000000..36967ddd5
--- /dev/null
+++ b/tests/auto/blackbox/testdata/linker-module-definition/testlib.def
@@ -0,0 +1,3 @@
+EXPORTS
+foo
+bar
diff --git a/tests/auto/blackbox/testdata/linker-variant/linker-variant.qbs b/tests/auto/blackbox/testdata/linker-variant/linker-variant.qbs
index 57bd4ccba..9256bf767 100644
--- a/tests/auto/blackbox/testdata/linker-variant/linker-variant.qbs
+++ b/tests/auto/blackbox/testdata/linker-variant/linker-variant.qbs
@@ -3,7 +3,7 @@ CppApplication {
property string linkerVariant
Probe {
id: gccProbe
- property bool isGcc: qbs.toolchain.contains("gcc")
+ property bool isGcc: qbs.toolchain.includes("gcc")
configure: {
console.info("is GCC: " + isGcc);
if (isGcc)
diff --git a/tests/auto/blackbox/testdata/linkerMode/linkerMode.qbs b/tests/auto/blackbox/testdata/linkerMode/linkerMode.qbs
index 1be50c0aa..9934efb04 100644
--- a/tests/auto/blackbox/testdata/linkerMode/linkerMode.qbs
+++ b/tests/auto/blackbox/testdata/linkerMode/linkerMode.qbs
@@ -24,7 +24,7 @@ Project {
}
CppApplication {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
consoleApplication: true
name: "LinkedProduct-Objective-C"
@@ -50,7 +50,7 @@ Project {
}
CppApplication {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
consoleApplication: true
name: "LinkedProduct-Objective-C++"
diff --git a/tests/auto/blackbox/testdata/linkerMode/main.s b/tests/auto/blackbox/testdata/linkerMode/main.s
index ef88f8c52..fb165114b 100644
--- a/tests/auto/blackbox/testdata/linkerMode/main.s
+++ b/tests/auto/blackbox/testdata/linkerMode/main.s
@@ -3,3 +3,4 @@
_main:
main:
+ ret
diff --git a/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs b/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs
index 0b4de0ab9..6d068b6a2 100644
--- a/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs
+++ b/tests/auto/blackbox/testdata/linkerscripts/linkerscripts.qbs
@@ -55,6 +55,13 @@ DynamicLibrary {
}
}
+ Probe {
+ id: checker
+ property bool isGcc: qbs.toolchain.contains("gcc")
+ property bool isLinux: qbs.targetOS.contains("linux")
+ configure: { console.info("is Linux gcc: " + (isGcc && isLinux)) }
+ }
+
qbs.installPrefix: ""
install: true
installDir: ""
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
index df9181641..c47a40aea 100644
--- a/tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs
+++ b/tests/auto/blackbox/testdata/list-property-order/modules/lower/lower.qbs
@@ -1,5 +1,5 @@
Module {
- property stringList listProp
+ property stringList listProp: [ "lower" ]
Rule {
inputs: ["intype"]
diff --git a/tests/auto/blackbox/testdata/list-property-order/product.qbs b/tests/auto/blackbox/testdata/list-property-order/product.qbs
index e92494693..bec122214 100644
--- a/tests/auto/blackbox/testdata/list-property-order/product.qbs
+++ b/tests/auto/blackbox/testdata/list-property-order/product.qbs
@@ -4,6 +4,7 @@ Product {
Depends { name: "higher1" }
Depends { name: "higher2" }
Depends { name: "higher3" }
+ lower.listProp: ["product"]
Group {
files: ["dummy.txt"]
fileTags: ["intype"]
diff --git a/tests/auto/blackbox/testdata/loadablemodule/loadablemodule.qbs b/tests/auto/blackbox/testdata/loadablemodule/loadablemodule.qbs
index ec5b0b358..8023eed36 100644
--- a/tests/auto/blackbox/testdata/loadablemodule/loadablemodule.qbs
+++ b/tests/auto/blackbox/testdata/loadablemodule/loadablemodule.qbs
@@ -1,8 +1,10 @@
+import qbs.Host
+
Project {
LoadableModule {
Depends { name: "cpp" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
name: "CoolPlugIn"
@@ -15,6 +17,12 @@ Project {
}
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
Depends { name: "cpp" }
Depends { name: "CoolPlugIn"; cpp.link: false }
consoleApplication: true
@@ -22,10 +30,10 @@ Project {
files: ["main.cpp"]
cpp.cxxLanguageVersion: "c++11"
- cpp.dynamicLibraries: [qbs.targetOS.contains("windows") ? "kernel32" : "dl"]
+ cpp.dynamicLibraries: [qbs.targetOS.includes("windows") ? "kernel32" : "dl"]
Properties {
- condition: qbs.targetOS.contains("unix") && !qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("unix") && !qbs.targetOS.includes("darwin")
cpp.rpaths: [cpp.rpathOrigin]
}
diff --git a/tests/auto/blackbox/testdata/localDeployment/localDeployment.qbs b/tests/auto/blackbox/testdata/localDeployment/localDeployment.qbs
index 650f07104..b3fa86d32 100644
--- a/tests/auto/blackbox/testdata/localDeployment/localDeployment.qbs
+++ b/tests/auto/blackbox/testdata/localDeployment/localDeployment.qbs
@@ -1,5 +1,13 @@
+import qbs.Host
+
Project {
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
type: ["application"]
consoleApplication: true
name: "HelloWorld"
diff --git a/tests/auto/blackbox/testdata/lsp/lsp.qbs b/tests/auto/blackbox/testdata/lsp/lsp.qbs
new file mode 100644
index 000000000..24479e0ec
--- /dev/null
+++ b/tests/auto/blackbox/testdata/lsp/lsp.qbs
@@ -0,0 +1,11 @@
+Project {
+ Product {
+ name: "dep"
+ Depends { name: "m" }
+ Depends { name: "Prefix"; submodules: ["m1", "m2", "m3"] }
+
+ }
+ Product {
+ Depends { name: "dep" }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/lsp/modules/Prefix/m1/m1.qbs b/tests/auto/blackbox/testdata/lsp/modules/Prefix/m1/m1.qbs
new file mode 100644
index 000000000..09bac2dc2
--- /dev/null
+++ b/tests/auto/blackbox/testdata/lsp/modules/Prefix/m1/m1.qbs
@@ -0,0 +1,5 @@
+Module {
+ property bool p1
+ property string p2
+ property bool x
+}
diff --git a/tests/auto/language/testdata/erroneous/modules/prefix1/prefix1.qbs b/tests/auto/blackbox/testdata/lsp/modules/Prefix/m2/m2.qbs
index 84957060c..84957060c 100644
--- a/tests/auto/language/testdata/erroneous/modules/prefix1/prefix1.qbs
+++ b/tests/auto/blackbox/testdata/lsp/modules/Prefix/m2/m2.qbs
diff --git a/tests/auto/blackbox/testdata/lsp/modules/Prefix/m3/m3.qbs b/tests/auto/blackbox/testdata/lsp/modules/Prefix/m3/m3.qbs
new file mode 100644
index 000000000..84957060c
--- /dev/null
+++ b/tests/auto/blackbox/testdata/lsp/modules/Prefix/m3/m3.qbs
@@ -0,0 +1,2 @@
+Module {
+}
diff --git a/tests/auto/blackbox/testdata/lsp/modules/m/m.qbs b/tests/auto/blackbox/testdata/lsp/modules/m/m.qbs
new file mode 100644
index 000000000..84957060c
--- /dev/null
+++ b/tests/auto/blackbox/testdata/lsp/modules/m/m.qbs
@@ -0,0 +1,2 @@
+Module {
+}
diff --git a/tests/auto/blackbox/testdata/makefile-generator/app.qbs b/tests/auto/blackbox/testdata/makefile-generator/app.qbs
index dfd67276e..fd3fbb91b 100644
--- a/tests/auto/blackbox/testdata/makefile-generator/app.qbs
+++ b/tests/auto/blackbox/testdata/makefile-generator/app.qbs
@@ -1,11 +1,19 @@
+import qbs.Host
+
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "the app"
consoleApplication: true
cpp.cxxLanguageVersion: "c++11"
cpp.separateDebugInformation: false
Properties {
- condition: qbs.targetOS.contains("macos")
+ condition: qbs.targetOS.includes("macos")
bundle.embedInfoPlist: false
cpp.minimumMacosVersion: "10.7"
}
diff --git a/tests/auto/blackbox/testdata/maximum-c-language-version/maximum-c-language-version.qbs b/tests/auto/blackbox/testdata/maximum-c-language-version/maximum-c-language-version.qbs
index 320494d00..9f41570f8 100644
--- a/tests/auto/blackbox/testdata/maximum-c-language-version/maximum-c-language-version.qbs
+++ b/tests/auto/blackbox/testdata/maximum-c-language-version/maximum-c-language-version.qbs
@@ -4,10 +4,13 @@ CppApplication {
Probe {
id: osProbe
- property stringList toolchain: qbs.toolchain
+ property string toolchainType: qbs.toolchainType
+ property string compilerVersion: cpp.compilerVersion
configure: {
- if (toolchain.contains("msvc"))
- console.info("is msvc");
+ console.info("is msvc: " + (toolchainType === "msvc" || toolchainType === "clang-cl"));
+ var isOld = (toolchainType === "msvc" && compilerVersion < "19.29.30138")
+ || (toolchainType === "clang-cl" && compilerVersion < "13");
+ console.info("is old msvc: " + isOld);
found = true;
}
}
diff --git a/tests/auto/blackbox/testdata/maximum-cxx-language-version/modules/newestmodule/newestmodule.qbs b/tests/auto/blackbox/testdata/maximum-cxx-language-version/modules/newestmodule/newestmodule.qbs
index f99932b10..305cb29f1 100644
--- a/tests/auto/blackbox/testdata/maximum-cxx-language-version/modules/newestmodule/newestmodule.qbs
+++ b/tests/auto/blackbox/testdata/maximum-cxx-language-version/modules/newestmodule/newestmodule.qbs
@@ -1,4 +1,4 @@
Module {
Depends { name: "cpp" }
- cpp.cxxLanguageVersion: "c++17"
+ cpp.cxxLanguageVersion: "c++23"
}
diff --git a/tests/auto/blackbox/testdata/minimumSystemVersion/fakewindows.qbs b/tests/auto/blackbox/testdata/minimumSystemVersion/fakewindows.qbs
index 1a56e0b7e..da836e609 100644
--- a/tests/auto/blackbox/testdata/minimumSystemVersion/fakewindows.qbs
+++ b/tests/auto/blackbox/testdata/minimumSystemVersion/fakewindows.qbs
@@ -4,7 +4,7 @@ import qbs.Utilities
// (but will still compile and link since we avoid passing a
// bad value to the linker)
CppApplication {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
files: ["main.cpp"]
consoleApplication: true
cpp.minimumWindowsVersion: "5.3"
diff --git a/tests/auto/blackbox/testdata/minimumSystemVersion/macappstore.qbs b/tests/auto/blackbox/testdata/minimumSystemVersion/macappstore.qbs
index 8440da779..26a94e132 100644
--- a/tests/auto/blackbox/testdata/minimumSystemVersion/macappstore.qbs
+++ b/tests/auto/blackbox/testdata/minimumSystemVersion/macappstore.qbs
@@ -1,8 +1,15 @@
// 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
+import qbs.Host
+
CppApplication {
- condition: qbs.targetOS.contains("macos")
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result && qbs.targetOS.includes("macos");
+ }
files: ["main.mm"]
consoleApplication: true
cpp.frameworks: "Foundation"
diff --git a/tests/auto/blackbox/testdata/minimumSystemVersion/specific.qbs b/tests/auto/blackbox/testdata/minimumSystemVersion/specific.qbs
index f6ecab418..b2b67642a 100644
--- a/tests/auto/blackbox/testdata/minimumSystemVersion/specific.qbs
+++ b/tests/auto/blackbox/testdata/minimumSystemVersion/specific.qbs
@@ -3,22 +3,29 @@ import qbs.Utilities
// a specific version of the operating systems is specified
// when the application is run its output should confirm
// that the given values took effect
+import qbs.Host
+
CppApplication {
- condition: qbs.targetOS.contains("windows") || qbs.targetOS.contains("macos")
- files: [qbs.targetOS.contains("darwin") ? "main.mm" : "main.cpp"]
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result && qbs.targetOS.includes("windows") || qbs.targetOS.includes("macos");
+ }
+ files: [qbs.targetOS.includes("darwin") ? "main.mm" : "main.cpp"]
consoleApplication: true
Properties {
- condition: qbs.targetOS.contains("windows")
- cpp.minimumWindowsVersion: "6.0"
+ condition: qbs.targetOS.includes("windows")
+ cpp.minimumWindowsVersion: "6.2"
cpp.defines: [
- "QBS_WINVER=0x600",
+ "QBS_WINVER=0x602",
"TOOLCHAIN_INSTALL_PATH=" + Utilities.cStringQuote(cpp.toolchainInstallPath)
]
}
Properties {
- condition: qbs.targetOS.contains("macos")
+ condition: qbs.targetOS.includes("macos")
cpp.frameworks: "Foundation"
cpp.minimumMacosVersion: "10.7"
}
diff --git a/tests/auto/blackbox/testdata/minimumSystemVersion/unspecified-forced.qbs b/tests/auto/blackbox/testdata/minimumSystemVersion/unspecified-forced.qbs
index a5ea620fd..adb61a6d8 100644
--- a/tests/auto/blackbox/testdata/minimumSystemVersion/unspecified-forced.qbs
+++ b/tests/auto/blackbox/testdata/minimumSystemVersion/unspecified-forced.qbs
@@ -2,8 +2,16 @@ import qbs.Utilities
// no minimum versions are specified, and explicitly set to undefined in
// case the profile has set it
+import qbs.Host
+
CppApplication {
- files: [qbs.targetOS.contains("darwin") ? "main.mm" : "main.cpp"]
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
+ files: [qbs.targetOS.includes("darwin") ? "main.mm" : "main.cpp"]
consoleApplication: true
cpp.minimumWindowsVersion: undefined
cpp.minimumMacosVersion: undefined
@@ -11,12 +19,12 @@ CppApplication {
cpp.minimumAndroidVersion: undefined
Properties {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
cpp.defines: ["TOOLCHAIN_INSTALL_PATH=" + Utilities.cStringQuote(cpp.toolchainInstallPath)]
}
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
cpp.frameworks: "Foundation"
}
}
diff --git a/tests/auto/blackbox/testdata/minimumSystemVersion/unspecified.qbs b/tests/auto/blackbox/testdata/minimumSystemVersion/unspecified.qbs
index 0eeb2d547..3c6559c39 100644
--- a/tests/auto/blackbox/testdata/minimumSystemVersion/unspecified.qbs
+++ b/tests/auto/blackbox/testdata/minimumSystemVersion/unspecified.qbs
@@ -1,17 +1,25 @@
import qbs.Utilities
// no minimum versions are specified so the profile defaults will be used
+import qbs.Host
+
CppApplication {
- files: [qbs.targetOS.contains("darwin") ? "main.mm" : "main.cpp"]
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
+ files: [qbs.targetOS.includes("darwin") ? "main.mm" : "main.cpp"]
consoleApplication: true
Properties {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
cpp.defines: ["TOOLCHAIN_INSTALL_PATH=" + Utilities.cStringQuote(cpp.toolchainInstallPath)]
}
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
cpp.frameworks: "Foundation"
}
}
diff --git a/tests/auto/blackbox/testdata/module-conditions/module-conditions.qbs b/tests/auto/blackbox/testdata/module-conditions/module-conditions.qbs
index dc3768203..207df5ded 100644
--- a/tests/auto/blackbox/testdata/module-conditions/module-conditions.qbs
+++ b/tests/auto/blackbox/testdata/module-conditions/module-conditions.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
Product {
name: "p1"
diff --git a/tests/auto/blackbox/testdata/module-providers/main.cpp b/tests/auto/blackbox/testdata/module-providers/main.cpp
deleted file mode 100644
index 9cd29b1fe..000000000
--- a/tests/auto/blackbox/testdata/module-providers/main.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#include <iostream>
-
-int main()
-{
- std::cout << "The letters are " << LETTER1 << " and " << LETTER2 << std::endl;
-}
diff --git a/tests/auto/blackbox/testdata/module-providers/module-providers.qbs b/tests/auto/blackbox/testdata/module-providers/module-providers.qbs
deleted file mode 100644
index d1ff79269..000000000
--- a/tests/auto/blackbox/testdata/module-providers/module-providers.qbs
+++ /dev/null
@@ -1,20 +0,0 @@
-Project {
- CppApplication {
- name: "app1"
- Depends { name: "mygenerator.module1" }
- Depends { name: "mygenerator.module2" }
- moduleProviders.mygenerator.chooseLettersFrom: "beginning"
- files: "main.cpp"
- }
- CppApplication {
- name: "app2"
- Depends { name: "mygenerator.module1" }
- Depends { name: "mygenerator.module2" }
- Profile {
- name: "myProfile"
- moduleProviders.mygenerator.chooseLettersFrom: "end"
- }
- qbs.profile: "myProfile"
- files: "main.cpp"
- }
-}
diff --git a/tests/auto/blackbox/testdata/msvc-asm-flags/include/header.inc b/tests/auto/blackbox/testdata/msvc-asm-flags/include/header.inc
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/msvc-asm-flags/include/header.inc
diff --git a/tests/auto/blackbox/testdata/msvc-asm-flags/msvc-asm-flags.asm b/tests/auto/blackbox/testdata/msvc-asm-flags/msvc-asm-flags.asm
new file mode 100644
index 000000000..eddc49131
--- /dev/null
+++ b/tests/auto/blackbox/testdata/msvc-asm-flags/msvc-asm-flags.asm
@@ -0,0 +1,8 @@
+include header.inc
+
+.code
+main proc
+ mov ecx, 16
+main endp
+
+end \ No newline at end of file
diff --git a/tests/auto/blackbox/testdata/msvc-asm-flags/msvc-asm-flags.qbs b/tests/auto/blackbox/testdata/msvc-asm-flags/msvc-asm-flags.qbs
new file mode 100644
index 000000000..d632098c9
--- /dev/null
+++ b/tests/auto/blackbox/testdata/msvc-asm-flags/msvc-asm-flags.qbs
@@ -0,0 +1,6 @@
+StaticLibrary {
+ condition: qbs.toolchain.includes("msvc")
+ Depends { name: "cpp" }
+ files: "msvc-asm-flags.asm"
+ cpp.assemblerFlags: ["/I", "include"]
+}
diff --git a/tests/auto/blackbox/testdata/multiplexed-tool/multiplexed-tool.qbs b/tests/auto/blackbox/testdata/multiplexed-tool/multiplexed-tool.qbs
index 3994bd95f..1ec51c354 100644
--- a/tests/auto/blackbox/testdata/multiplexed-tool/multiplexed-tool.qbs
+++ b/tests/auto/blackbox/testdata/multiplexed-tool/multiplexed-tool.qbs
@@ -1,13 +1,23 @@
+import qbs.Host
+
Project {
CppApplication {
name: "tool"
consoleApplication: true
+ property bool _testPlatform: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
Profile {
name: "debugProfile"
+ baseProfile: project.profile
qbs.buildVariant: "debug"
}
Profile {
name: "releaseProfile"
+ baseProfile: project.profile
qbs.buildVariant: "release"
}
multiplexByQbsProperties: "profiles"
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
index f8b6a7dc0..a5a6c7d4b 100644
--- a/tests/auto/blackbox/testdata/nested-properties/modules/lowerlevel/lower-level.qbs
+++ b/tests/auto/blackbox/testdata/nested-properties/modules/lowerlevel/lower-level.qbs
@@ -10,7 +10,7 @@ Module {
var cmd = new JavaScriptCommand();
cmd.sourceCode = function() { };
var prop = product.lowerlevel.prop;
- cmd.description = "lowerlevel.prop is '" + prop + "'.";
+ cmd.description = "lowerlevel.prop is '" + prop + "'";
return [cmd];
}
}
diff --git a/tests/auto/blackbox/testdata/no-exported-symbols/no-exported-symbols.qbs b/tests/auto/blackbox/testdata/no-exported-symbols/no-exported-symbols.qbs
index 346a94e21..9aad3d032 100644
--- a/tests/auto/blackbox/testdata/no-exported-symbols/no-exported-symbols.qbs
+++ b/tests/auto/blackbox/testdata/no-exported-symbols/no-exported-symbols.qbs
@@ -12,7 +12,7 @@ Project {
id: toolchainProbe
property stringList toolchain: qbs.toolchain
configure: {
- if (toolchain.contains("msvc") && !toolchain.contains("clang-cl"))
+ if (toolchain.includes("msvc") && !toolchain.includes("clang-cl"))
console.info("compiler is MSVC")
else
console.info("compiler is not MSVC")
diff --git a/tests/auto/blackbox/testdata/no-such-profile/no-such-profile.qbs b/tests/auto/blackbox/testdata/no-such-profile/no-such-profile.qbs
index 8e64ba478..1de3a4b10 100644
--- a/tests/auto/blackbox/testdata/no-such-profile/no-such-profile.qbs
+++ b/tests/auto/blackbox/testdata/no-such-profile/no-such-profile.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Product {
name: "theProduct"
property int p
diff --git a/tests/auto/blackbox/testdata/nodejs/hello.qbs b/tests/auto/blackbox/testdata/nodejs/hello.qbs
index c19e455db..849a8789f 100644
--- a/tests/auto/blackbox/testdata/nodejs/hello.qbs
+++ b/tests/auto/blackbox/testdata/nodejs/hello.qbs
@@ -1,4 +1,12 @@
+import qbs.Host
+
NodeJSApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
nodejs.applicationFile: "hello.js"
name: "hello"
}
diff --git a/tests/auto/blackbox/testdata/nsis/hello.qbs b/tests/auto/blackbox/testdata/nsis/hello.qbs
index a161a6998..f70f27e2b 100644
--- a/tests/auto/blackbox/testdata/nsis/hello.qbs
+++ b/tests/auto/blackbox/testdata/nsis/hello.qbs
@@ -1,5 +1,5 @@
NSISSetup {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
name: "Qbs Hello"
targetName: "qbs-hello-" + qbs.architecture
files: ["hello.nsi", "hello.bat"]
diff --git a/tests/auto/blackbox/testdata/nsisDependencies/nsisDependencies.qbs b/tests/auto/blackbox/testdata/nsisDependencies/nsisDependencies.qbs
index d8185aabf..a4ce92067 100644
--- a/tests/auto/blackbox/testdata/nsisDependencies/nsisDependencies.qbs
+++ b/tests/auto/blackbox/testdata/nsisDependencies/nsisDependencies.qbs
@@ -2,7 +2,7 @@ import qbs.FileInfo
import qbs.TextFile
Project {
- condition: qbs.targetOS.contains("windows")
+ condition: qbs.targetOS.includes("windows")
NSISSetup {
Depends { name: "app" }
diff --git a/tests/auto/blackbox/testdata/output-redirection/output-redirection.qbs b/tests/auto/blackbox/testdata/output-redirection/output-redirection.qbs
index 3ee443438..d2474cecf 100644
--- a/tests/auto/blackbox/testdata/output-redirection/output-redirection.qbs
+++ b/tests/auto/blackbox/testdata/output-redirection/output-redirection.qbs
@@ -1,4 +1,5 @@
import qbs.FileInfo
+import qbs.Host
Product {
name: "the-product"
@@ -21,7 +22,7 @@ Product {
prepare: {
var binary;
var prefixArgs;
- if (product.qbs.hostOS.contains("windows")) {
+ if (Host.os().includes("windows")) {
binary = product.qbs.windowsShellPath;
prefixArgs = ["/c", "type"];
} else {
diff --git a/tests/auto/blackbox/testdata/overrideProjectProperties/helper_lib.qbs b/tests/auto/blackbox/testdata/overrideProjectProperties/helper_lib.qbs
index b69dd0da8..c7eab99e2 100644
--- a/tests/auto/blackbox/testdata/overrideProjectProperties/helper_lib.qbs
+++ b/tests/auto/blackbox/testdata/overrideProjectProperties/helper_lib.qbs
@@ -3,7 +3,7 @@ DynamicLibrary {
files: "helperlib.cpp"
Depends { name: "cpp" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
diff --git a/tests/auto/blackbox/testdata/path-list-in-probe/main.cpp b/tests/auto/blackbox/testdata/path-list-in-probe/main.cpp
new file mode 100644
index 000000000..5bc549337
--- /dev/null
+++ b/tests/auto/blackbox/testdata/path-list-in-probe/main.cpp
@@ -0,0 +1,4 @@
+int main()
+{
+ return 0;
+} \ No newline at end of file
diff --git a/tests/auto/blackbox/testdata/path-list-in-probe/path-list-in-probe.qbs b/tests/auto/blackbox/testdata/path-list-in-probe/path-list-in-probe.qbs
new file mode 100644
index 000000000..3bf36367a
--- /dev/null
+++ b/tests/auto/blackbox/testdata/path-list-in-probe/path-list-in-probe.qbs
@@ -0,0 +1,18 @@
+Project {
+ CppApplication {
+ Probe {
+ id: theProbe
+ property pathList result
+ configure: {
+ result = ["main.cpp"]
+ found = true
+ }
+ }
+ property pathList res: theProbe.found ? theProbe.result : []
+
+ Group {
+ name: "files"
+ files: res
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs b/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs
index 84c00c240..62871698e 100644
--- a/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/BaseApp.qbs
@@ -28,9 +28,10 @@
**
****************************************************************************/
+import qbs.FileInfo
import qbs.Probes
-CppApplication {
+Product {
property varList inputSelectors
property varList inputNames
@@ -38,8 +39,10 @@ CppApplication {
property pathList inputSearchPaths
property var inputNameFilter
property var inputCandidateFilter
+ property stringList inputEnvironmentPaths
property stringList outputFilePaths
+ property var outputCandidatePaths
Probes.PathProbe {
id: probe
@@ -49,6 +52,8 @@ CppApplication {
nameFilter: inputNameFilter
candidateFilter: inputCandidateFilter
searchPaths: inputSearchPaths
+ platformSearchPaths: []
+ environmentPaths: inputEnvironmentPaths
}
property bool validate: {
@@ -56,21 +61,72 @@ CppApplication {
if (lhs.length !== rhs.length)
return false;
for (var i = 0; i < lhs.length; ++i) {
- if (lhs[i] !== rhs[i])
+ if ((lhs[i] instanceof Array) && (rhs[i] instanceof Array)) {
+ if (!compareArrays(lhs[i], rhs[i]))
+ return false;
+ } else if (FileInfo.resolvePath(path, lhs[i]) !== FileInfo.resolvePath(path, rhs[i])) {
return false;
+ }
}
return true;
};
- if (!probe.found)
+ if (outputCandidatePaths) {
+ var actual = probe.allResults.map(function(file) { return file.candidatePaths; });
+ if (!compareArrays(actual, outputCandidatePaths)) {
+ throw "Invalid canndidatePaths: actual = " + JSON.stringify(actual)
+ + ", expected = " + JSON.stringify(outputCandidatePaths);
+ }
+ }
+
+ if (!probe.found) {
+ if (probe.filePath) {
+ throw "Invalid filePath: actual = " + JSON.stringify(probe.filePath)
+ + ", expected = 'undefined'";
+ }
+ if (probe.fileName) {
+ throw "Invalid fileName: actual = " + JSON.stringify(probe.fileName)
+ + ", expected = 'undefined'";
+ }
+ if (probe.path) {
+ throw "Invalid path: actual = " + JSON.stringify(probe.path)
+ + ", expected = 'undefined'";
+ }
+
throw "Probe failed to find files";
+ }
if (outputFilePaths) {
var actual = probe.allResults.map(function(file) { return file.filePath; });
- if (!compareArrays(actual, outputFilePaths))
- throw "Invalid filePaths: actual = " + actual + ", expected = " + outputFilePaths;
+ if (!compareArrays(actual, outputFilePaths)) {
+ throw "Invalid filePaths: actual = " + JSON.stringify(actual)
+ + ", expected = " + JSON.stringify(outputFilePaths);
+ }
}
- }
- files: ["main.cpp"]
+ if (probe.allResults.length !== 1)
+ return;
+
+ // check that single-file interface matches the first value in allResults
+ var expectedFilePath = probe.allResults[0].filePath;
+ if (probe.filePath !== expectedFilePath) {
+ throw "Invalid filePath: actual = " + probe.filePath
+ + ", expected = " + expectedFilePath;
+ }
+ var expectedFileName = probe.allResults[0].fileName;
+ if (probe.fileName !== expectedFileName) {
+ throw "Invalid fileName: actual = " + probe.fileName
+ + ", expected = " + expectedFileName;
+ }
+ var expectedPath = probe.allResults[0].path;
+ if (FileInfo.resolvePath(path, probe.path) !== FileInfo.resolvePath(path, expectedPath)) {
+ throw "Invalid path: actual = " + probe.path
+ + ", expected = " + expectedPath;
+ }
+ var expectedCandidatePaths = probe.allResults[0].candidatePaths;
+ if (!compareArrays(probe.candidatePaths, expectedCandidatePaths)) {
+ throw "Invalid candidatePaths: actual = " + JSON.stringify(probe.candidatePaths)
+ + ", expected = " + JSON.stringify(expectedCandidatePaths);
+ }
+ }
}
diff --git a/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs b/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs
index a65256a68..5c259ae9b 100644
--- a/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/candidate-filter.qbs
@@ -3,10 +3,12 @@ import qbs.FileInfo
BaseApp {
inputNames: ["tool.1", "tool.2"]
inputSearchPaths: "bin"
- outputFilePaths: ["bin/tool.2"]
inputCandidateFilter: {
+ var fi = FileInfo;
return function(f) {
- return FileInfo.fileName(f) == "tool.2";
+ return fi.fileName(f) == "tool.2";
}
}
+ outputFilePaths: ["bin/tool.2"]
+ outputCandidatePaths: [["bin/tool.1", "bin/tool.2"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/environment-paths.qbs b/tests/auto/blackbox/testdata/path-probe/environment-paths.qbs
new file mode 100644
index 000000000..fca824bfb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/path-probe/environment-paths.qbs
@@ -0,0 +1,10 @@
+import qbs.FileInfo
+
+BaseApp {
+ inputNames: "tool"
+ inputSearchPaths: ["bin", "usr/bin"]
+ // env takes precedence
+ inputEnvironmentPaths: "SEARCH_PATH"
+ outputFilePaths: ["usr/bin/tool"]
+ outputCandidatePaths: [["usr/bin/tool"]]
+}
diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-common-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-common-suffixes.qbs
new file mode 100644
index 000000000..c4d53a715
--- /dev/null
+++ b/tests/auto/blackbox/testdata/path-probe/mult-files-common-suffixes.qbs
@@ -0,0 +1,10 @@
+BaseApp {
+ inputSelectors: [
+ {names : "tool"},
+ {names : "super-tool"},
+ ]
+ inputNameSuffixes: ".1"
+ inputSearchPaths: "bin"
+ outputFilePaths: ["bin/tool.1", "bin/super-tool.1"]
+ outputCandidatePaths: [["bin/tool.1"], ["bin/super-tool.1"]]
+}
diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs
index b112db44d..33656d4e6 100644
--- a/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-suffixes.qbs
@@ -1,8 +1,9 @@
BaseApp {
inputSelectors: [
- {names : "tool", nameSuffixes: [".1", ".2"]},
+ {names : "tool", nameSuffixes: [".0", ".1", ".2"]},
{names : "super-tool", nameSuffixes: [".1"]},
]
inputSearchPaths: "bin"
outputFilePaths: ["bin/tool.1", "bin/super-tool.1"]
+ outputCandidatePaths: [["bin/tool.0", "bin/tool.1"], ["bin/super-tool.1"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs
index 60c56e6b4..dd0b58aa2 100644
--- a/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/mult-files-mult-variants.qbs
@@ -1,9 +1,10 @@
BaseApp {
inputSelectors: [
"tool",
- ["tool.1", "tool.2"],
+ ["tool.0", "tool.1", "tool.2"],
{names : ["tool.3", "tool.4"]},
]
inputSearchPaths: "bin"
outputFilePaths: ["bin/tool", "bin/tool.1", "bin/tool.3"]
+ outputCandidatePaths: [["bin/tool"], ["bin/tool.0", "bin/tool.1"], ["bin/tool.3"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs
index 5e4fc27ca..7ae78de24 100644
--- a/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/mult-files-suffixes.qbs
@@ -5,4 +5,5 @@ BaseApp {
]
inputSearchPaths: "bin"
outputFilePaths: ["bin/tool.2", "bin/super-tool.1"]
+ outputCandidatePaths: [["bin/tool.2"], ["bin/super-tool.1"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/mult-files.qbs b/tests/auto/blackbox/testdata/path-probe/mult-files.qbs
index 08727ac01..aa08befc8 100644
--- a/tests/auto/blackbox/testdata/path-probe/mult-files.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/mult-files.qbs
@@ -7,4 +7,5 @@ BaseApp {
]
inputSearchPaths: "bin"
outputFilePaths: ["bin/tool.1", "bin/tool.2", "bin/tool.3", "bin/tool.4"]
+ outputCandidatePaths: [["bin/tool.1"], ["bin/tool.2"], ["bin/tool.3"], ["bin/tool.4"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/name-filter.qbs b/tests/auto/blackbox/testdata/path-probe/name-filter.qbs
index 406988fed..b2840443b 100644
--- a/tests/auto/blackbox/testdata/path-probe/name-filter.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/name-filter.qbs
@@ -7,4 +7,5 @@ BaseApp {
};
}
outputFilePaths: ["bin/tool.2"]
+ outputCandidatePaths: [["bin/tool.2"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs b/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs
index aaa27042c..aabb0fe7b 100644
--- a/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/non-existent-selector.qbs
@@ -5,4 +5,5 @@ BaseApp {
"tool.2",
]
inputSearchPaths: "bin"
+ outputCandidatePaths: [["bin/tool.1"], ["bin/nonexistent"], ["bin/tool.2"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/non-existent.qbs b/tests/auto/blackbox/testdata/path-probe/non-existent.qbs
index f0c58fa6c..aad01c31b 100644
--- a/tests/auto/blackbox/testdata/path-probe/non-existent.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/non-existent.qbs
@@ -1,4 +1,5 @@
BaseApp {
inputNames: "nonexistent"
inputSearchPaths: "bin"
+ outputCandidatePaths: [["bin/nonexistent"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs
index 992a0bea4..98f5b141a 100644
--- a/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/single-file-mult-variants.qbs
@@ -2,4 +2,5 @@ BaseApp {
inputNames: ["tool.1", "tool.2"]
inputSearchPaths: "bin"
outputFilePaths: ["bin/tool.1"]
+ outputCandidatePaths: [["bin/tool.1"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs
index 697665242..292df4add 100644
--- a/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/single-file-selector-array.qbs
@@ -2,4 +2,5 @@ BaseApp {
inputSelectors: ["tool"]
inputSearchPaths: "bin"
outputFilePaths: ["bin/tool"]
+ outputCandidatePaths: [["bin/tool"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs
index d57700baf..cf7cfe436 100644
--- a/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/single-file-selector.qbs
@@ -2,4 +2,5 @@ BaseApp {
inputSelectors: "tool"
inputSearchPaths: "bin"
outputFilePaths: ["bin/tool"]
+ outputCandidatePaths: [["bin/tool"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs b/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs
index 4442e719a..3436a49c3 100644
--- a/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/single-file-suffixes.qbs
@@ -1,6 +1,7 @@
BaseApp {
inputNames: "tool"
inputSearchPaths: "bin"
- inputNameSuffixes: [".1", ".2"]
+ inputNameSuffixes: [".0", ".1", ".2"]
outputFilePaths: ["bin/tool.1"]
+ outputCandidatePaths: [["bin/tool.0", "bin/tool.1"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/single-file.qbs b/tests/auto/blackbox/testdata/path-probe/single-file.qbs
index 3590e7664..e22d7ba0d 100644
--- a/tests/auto/blackbox/testdata/path-probe/single-file.qbs
+++ b/tests/auto/blackbox/testdata/path-probe/single-file.qbs
@@ -2,4 +2,5 @@ BaseApp {
inputNames: "tool"
inputSearchPaths: "bin"
outputFilePaths: ["bin/tool"]
+ outputCandidatePaths: [["bin/tool"]]
}
diff --git a/tests/auto/blackbox/testdata/path-probe/usr/bin/tool b/tests/auto/blackbox/testdata/path-probe/usr/bin/tool
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/path-probe/usr/bin/tool
diff --git a/tests/auto/blackbox/testdata/plugin-dependency/helper1.cpp b/tests/auto/blackbox/testdata/plugin-dependency/helper1.cpp
index 72331da80..15a3e0d86 100644
--- a/tests/auto/blackbox/testdata/plugin-dependency/helper1.cpp
+++ b/tests/auto/blackbox/testdata/plugin-dependency/helper1.cpp
@@ -1,4 +1,4 @@
-#include <stdio.h>
+#include <cstdio>
#if defined(_WIN32) || defined(WIN32)
# define EXPORT __declspec(dllexport)
@@ -12,5 +12,5 @@
EXPORT void helper1_hello()
{
- puts("helper1 says hello!");
+ std::puts("helper1 says hello!");
}
diff --git a/tests/auto/blackbox/testdata/plugin-dependency/helper2.cpp b/tests/auto/blackbox/testdata/plugin-dependency/helper2.cpp
index cdcdfc942..dccd72a04 100644
--- a/tests/auto/blackbox/testdata/plugin-dependency/helper2.cpp
+++ b/tests/auto/blackbox/testdata/plugin-dependency/helper2.cpp
@@ -1,7 +1,7 @@
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT void helper2_hello()
{
- puts("Hello from helper2!");
+ std::puts("Hello from helper2!");
}
diff --git a/tests/auto/blackbox/testdata/plugin-dependency/plugin-dependency.qbs b/tests/auto/blackbox/testdata/plugin-dependency/plugin-dependency.qbs
index c619b33ef..752673b78 100644
--- a/tests/auto/blackbox/testdata/plugin-dependency/plugin-dependency.qbs
+++ b/tests/auto/blackbox/testdata/plugin-dependency/plugin-dependency.qbs
@@ -1,16 +1,18 @@
+import qbs.Host
+
Project {
CppApplication {
name: "myapp"
files: ["main.cpp"]
Depends {
name: "plugin1" // not to be linked
- cpp.link: qbs.hostOS === undefined
+ cpp.link: Host.os() === undefined
}
Depends { name: "plugin2" } // not to be linked
Depends {
name: "plugin3" // supposed to be linked
- //property bool theCondition: true
- cpp.link: /*theCondition && */product.name === "myapp" // TODO: Make this work
+ property bool theCondition: true
+ cpp.link: theCondition && product.name === "myapp"
}
Depends { name: "plugin4" } // supposed to be linked
Depends { name: "helper1" } // supposed to be linked
diff --git a/tests/auto/blackbox/testdata/plugin-dependency/plugin1.cpp b/tests/auto/blackbox/testdata/plugin-dependency/plugin1.cpp
index 2535bd85e..8cf7e3851 100644
--- a/tests/auto/blackbox/testdata/plugin-dependency/plugin1.cpp
+++ b/tests/auto/blackbox/testdata/plugin-dependency/plugin1.cpp
@@ -1,7 +1,7 @@
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT void plugin1_hello()
{
- puts("plugin1 says hello!");
+ std::puts("plugin1 says hello!");
}
diff --git a/tests/auto/blackbox/testdata/plugin-dependency/plugin2.cpp b/tests/auto/blackbox/testdata/plugin-dependency/plugin2.cpp
index fb2030d60..9efc83e2b 100644
--- a/tests/auto/blackbox/testdata/plugin-dependency/plugin2.cpp
+++ b/tests/auto/blackbox/testdata/plugin-dependency/plugin2.cpp
@@ -1,7 +1,7 @@
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT void plugin2_hello()
{
- puts("plugin2 says hello!");
+ std::puts("plugin2 says hello!");
}
diff --git a/tests/auto/blackbox/testdata/plugin-dependency/plugin3.cpp b/tests/auto/blackbox/testdata/plugin-dependency/plugin3.cpp
index 8a9f5ee76..0e08ac85a 100644
--- a/tests/auto/blackbox/testdata/plugin-dependency/plugin3.cpp
+++ b/tests/auto/blackbox/testdata/plugin-dependency/plugin3.cpp
@@ -1,7 +1,7 @@
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT void plugin3_hello()
{
- puts("plugin3 says hello!");
+ std::puts("plugin3 says hello!");
}
diff --git a/tests/auto/blackbox/testdata/plugin-dependency/plugin4.cpp b/tests/auto/blackbox/testdata/plugin-dependency/plugin4.cpp
index 4663247fa..cee2362ec 100644
--- a/tests/auto/blackbox/testdata/plugin-dependency/plugin4.cpp
+++ b/tests/auto/blackbox/testdata/plugin-dependency/plugin4.cpp
@@ -1,7 +1,7 @@
#include "../dllexport.h"
-#include <stdio.h>
+#include <cstdio>
DLL_EXPORT void plugin4_hello()
{
- puts("plugin4 says hello!");
+ std::puts("plugin4 says hello!");
}
diff --git a/tests/auto/blackbox/testdata/precompiled-headers-and-redefine/precompiled-headers-and-redefine.qbs b/tests/auto/blackbox/testdata/precompiled-headers-and-redefine/precompiled-headers-and-redefine.qbs
index 55b53a7af..3974b514e 100644
--- a/tests/auto/blackbox/testdata/precompiled-headers-and-redefine/precompiled-headers-and-redefine.qbs
+++ b/tests/auto/blackbox/testdata/precompiled-headers-and-redefine/precompiled-headers-and-redefine.qbs
@@ -8,7 +8,7 @@ CppApplication {
Group {
files: ["file.cpp"]
cpp.defines: ["MYDEF=1"]
- cpp.cxxFlags: base.concat(qbs.toolchain.contains("clang-cl") ? ["-Wno-clang-cl-pch"] : [])
+ cpp.cxxFlags: base.concat(qbs.toolchain.includes("clang-cl") ? ["-Wno-clang-cl-pch"] : [])
}
cpp.treatWarningsAsErrors: true
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
index 41c6cfe50..45b8e157f 100644
--- 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
@@ -7,7 +7,7 @@ Module {
outputFileTags: "dep-out"
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating dep-out artifact";
+ cmd.description = "creating dep-out artifact";
cmd.sourceCode = function() {
console.info("prop: " + product.depmodule.prop);
console.info("listProp: " + product.depmodule.listProp);
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
index 89d544f7e..31275aa40 100644
--- 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
@@ -13,7 +13,7 @@ Module {
outputFileTags: "out"
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating out artifact";
+ cmd.description = "creating out artifact";
cmd.sourceCode = function() {
console.info("found: " + product.mymodule.found);
};
diff --git a/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs b/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs
index 9846eacef..ce89d11f4 100644
--- a/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs
+++ b/tests/auto/blackbox/testdata/probeProperties/probeProperties.qbs
@@ -1,29 +1,40 @@
import qbs.Probes
-CppApplication {
- Probes.PathProbe {
- id: probe1
- names: ["bin/tool"]
- platformSearchPaths: [product.sourceDirectory]
- }
+Project {
- Probes.PathProbe {
- id: probe2
- names: ["tool"]
- platformSearchPaths: [product.sourceDirectory + "/bin"]
- }
+ CppApplication {
+ Probes.PathProbe {
+ id: probe1
+ names: ["bin/tool"]
+ platformSearchPaths: [product.sourceDirectory]
+ }
- targetName: {
- console.info("probe1.fileName=" + probe1.fileName);
- console.info("probe1.path=" + probe1.path);
- console.info("probe1.filePath=" + probe1.filePath);
+ Probes.PathProbe {
+ id: probe2
+ names: ["tool"]
+ platformSearchPaths: [product.sourceDirectory + "/bin"]
+ }
- console.info("probe2.fileName=" + probe2.fileName);
- console.info("probe2.path=" + probe2.path);
- console.info("probe2.filePath=" + probe2.filePath);
+ targetName: {
+ console.info("probe1.fileName=" + probe1.fileName);
+ console.info("probe1.path=" + probe1.path);
+ console.info("probe1.filePath=" + probe1.filePath);
- return name;
+ console.info("probe2.fileName=" + probe2.fileName);
+ console.info("probe2.path=" + probe2.path);
+ console.info("probe2.filePath=" + probe2.filePath);
+
+ console.info("probe3.fileName=" + probe3.fileName);
+ console.info("probe3.path=" + probe3.path);
+ console.info("probe3.filePath=" + probe3.filePath);
+ return name;
+ }
+ }
+
+ Probes.PathProbe {
+ id: probe3
+ names: ["tool"]
+ platformSearchPaths: [project.sourceDirectory + "/bin"]
}
- 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
index e5c368a88..92ece5be7 100644
--- 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
@@ -3,8 +3,7 @@ Module {
id: propProbe
property stringList prop: []
configure: {
- prop = [];
- prop.push("probe");
+ prop = ["probe"];
found = true;
}
}
diff --git a/tests/auto/blackbox/testdata/product-dependencies-by-type/product-dependencies-by-type.qbs b/tests/auto/blackbox/testdata/product-dependencies-by-type/product-dependencies-by-type.qbs
index 8fa761a2d..8e1f291f3 100644
--- a/tests/auto/blackbox/testdata/product-dependencies-by-type/product-dependencies-by-type.qbs
+++ b/tests/auto/blackbox/testdata/product-dependencies-by-type/product-dependencies-by-type.qbs
@@ -91,7 +91,7 @@ Project {
name: "lib-product"
files: "main.cpp"
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -116,7 +116,7 @@ Project {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Collecting apps";
+ cmd.description = "collecting apps";
cmd.sourceCode = function() {
var file = new TextFile(output.filePath, TextFile.WriteOnly);
for (var i = 0; i < inputs["application"].length; ++i)
diff --git a/tests/auto/blackbox/testdata/product-in-exported-module/modules/m/m.qbs b/tests/auto/blackbox/testdata/product-in-exported-module/modules/m/m.qbs
new file mode 100644
index 000000000..0e79d0abe
--- /dev/null
+++ b/tests/auto/blackbox/testdata/product-in-exported-module/modules/m/m.qbs
@@ -0,0 +1,3 @@
+Module {
+ Depends { name: "dummy"; condition: { console.info("product: " + product.name); return false; } }
+}
diff --git a/tests/auto/blackbox/testdata/product-in-exported-module/product-in-exported-module.qbs b/tests/auto/blackbox/testdata/product-in-exported-module/product-in-exported-module.qbs
new file mode 100644
index 000000000..3ead0ca6c
--- /dev/null
+++ b/tests/auto/blackbox/testdata/product-in-exported-module/product-in-exported-module.qbs
@@ -0,0 +1,10 @@
+Project {
+ Product {
+ name: "importing"
+ Depends { name: "dep" }
+ }
+ Product {
+ name: "dep"
+ Export { Depends { name: "m" } }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/productproperties/header.qbs b/tests/auto/blackbox/testdata/productproperties/header.qbs
index 42f9c88d9..13a62035f 100644
--- a/tests/auto/blackbox/testdata/productproperties/header.qbs
+++ b/tests/auto/blackbox/testdata/productproperties/header.qbs
@@ -29,6 +29,6 @@ Product {
Export {
Depends { name: "cpp" }
- cpp.includePaths: product.buildDirectory
+ cpp.includePaths: exportingProduct.buildDirectory
}
}
diff --git a/tests/auto/blackbox/testdata/proper quoting/main.cpp b/tests/auto/blackbox/testdata/proper quoting/main.cpp
index 22cafeaa9..6e9be6df1 100644
--- a/tests/auto/blackbox/testdata/proper quoting/main.cpp
+++ b/tests/auto/blackbox/testdata/proper quoting/main.cpp
@@ -26,16 +26,16 @@
**
****************************************************************************/
-#include <stdio.h>
+#include <cstdio>
int bla();
int main()
{
- printf(DEFINE"\n");
- printf(DEFINEWITHSPACE"\n");
- printf(DEFINEWITHTAB"\n");
- printf(DEFINEWITHBACKSLASH"\n");
+ std::printf(DEFINE"\n");
+ std::printf(DEFINEWITHSPACE"\n");
+ std::printf(DEFINEWITHTAB"\n");
+ std::printf(DEFINEWITHBACKSLASH"\n");
return bla();
}
diff --git a/tests/auto/blackbox/testdata/proper quoting/my static lib.cpp b/tests/auto/blackbox/testdata/proper quoting/my static lib.cpp
index e7490e807..7c07fd4c9 100644
--- a/tests/auto/blackbox/testdata/proper quoting/my static lib.cpp
+++ b/tests/auto/blackbox/testdata/proper quoting/my static lib.cpp
@@ -26,12 +26,13 @@
**
****************************************************************************/
-#include <stdio.h>
#include <some helper.h>
+#include <cstdio>
+
int bla()
{
int n = getSomeNumber();
- printf("Hello World! The magic number is %d.", n);
+ std::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
index 184ee13e3..1587940a2 100644
--- a/tests/auto/blackbox/testdata/proper quoting/proper quoting.qbs
+++ b/tests/auto/blackbox/testdata/proper quoting/proper quoting.qbs
@@ -1,7 +1,13 @@
-import qbs 1.0
+import qbs.Host
Project {
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
type: "application"
consoleApplication: true
name: "Hello World"
@@ -34,7 +40,7 @@ Project {
Depends { name: "cpp" }
Export {
Depends { name: "cpp" }
- cpp.includePaths: [product.sourceDirectory + '/some helper']
+ cpp.includePaths: [exportingProduct.sourceDirectory + '/some helper']
}
}
}
diff --git a/tests/auto/blackbox/testdata/property-assignment-on-non-present-module/property-assignment-on-non-present-module.qbs b/tests/auto/blackbox/testdata/property-assignment-on-non-present-module/property-assignment-on-non-present-module.qbs
deleted file mode 100644
index a01d6c561..000000000
--- a/tests/auto/blackbox/testdata/property-assignment-on-non-present-module/property-assignment-on-non-present-module.qbs
+++ /dev/null
@@ -1,4 +0,0 @@
-Product {
- Depends { name: "nein"; required: false }
- nein.doch: "ohhh!"
-}
diff --git a/tests/auto/blackbox/testdata/property-evaluation-context/modules/base/base.qbs b/tests/auto/blackbox/testdata/property-evaluation-context/modules/base/base.qbs
new file mode 100644
index 000000000..a97538751
--- /dev/null
+++ b/tests/auto/blackbox/testdata/property-evaluation-context/modules/base/base.qbs
@@ -0,0 +1,4 @@
+Module {
+ property string productInBase: product.name
+ property string productInTop: ""
+}
diff --git a/tests/auto/blackbox/testdata/property-evaluation-context/modules/top/top.qbs b/tests/auto/blackbox/testdata/property-evaluation-context/modules/top/top.qbs
new file mode 100644
index 000000000..fa073ff78
--- /dev/null
+++ b/tests/auto/blackbox/testdata/property-evaluation-context/modules/top/top.qbs
@@ -0,0 +1,6 @@
+Module {
+ Depends { name: "base" }
+ base.productInTop: product.name
+ property string productInTop: product.name
+ property string productInExport: ""
+}
diff --git a/tests/auto/blackbox/testdata/property-evaluation-context/property-evaluation-context.qbs b/tests/auto/blackbox/testdata/property-evaluation-context/property-evaluation-context.qbs
new file mode 100644
index 000000000..ede32cf74
--- /dev/null
+++ b/tests/auto/blackbox/testdata/property-evaluation-context/property-evaluation-context.qbs
@@ -0,0 +1,34 @@
+Project {
+ qbsSearchPaths: [ path ]
+ Product {
+ name: "mylib"
+ Export {
+ Depends { name: "top" }
+ top.productInExport: exportingProduct.name
+ }
+ }
+
+ Product {
+ type: "rule-output"
+ name: "myapp"
+ Depends { name: "mylib" }
+
+ Rule {
+ alwaysRun: true
+ multiplex: true
+ requiresInputs: false
+ outputFileTags: "rule-output"
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ console.info("base.productInBase evaluated in: " + product.base.productInBase);
+ console.info("base.productInTop evaluated in: " + product.base.productInTop);
+ console.info("top.productInExport evaluated in: " + product.top.productInExport);
+ console.info("top.productInTop evaluated in: " + product.top.productInTop);
+ }
+ return [cmd];
+ }
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs b/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs
index b1e4a1fdc..590736f6a 100644
--- a/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs
+++ b/tests/auto/blackbox/testdata/propertyChanges/modules/TestModule/module.qbs
@@ -18,7 +18,7 @@ Module {
prepare: {
var cmd = new JavaScriptCommand();
cmd.highlight = "codegen";
- cmd.description = "Making output from input";
+ cmd.description = "making output from input";
cmd.sourceCode = function() {
// console.info('Change in source code');
console.info(input.TestModule.testProperty);
diff --git a/tests/auto/blackbox/testdata/propertyChanges/propertyChanges.qbs b/tests/auto/blackbox/testdata/propertyChanges/propertyChanges.qbs
index f13b1986e..29365a887 100644
--- a/tests/auto/blackbox/testdata/propertyChanges/propertyChanges.qbs
+++ b/tests/auto/blackbox/testdata/propertyChanges/propertyChanges.qbs
@@ -27,7 +27,7 @@ Project {
name: "library"
files: "lib.cpp"
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
}
@@ -80,7 +80,7 @@ Project {
prepare: {
var cmd = new JavaScriptCommand();
cmd.highlight = "codegen";
- cmd.description = "Making output from other output";
+ cmd.description = "making output from other output";
cmd.sourceCode = function() { File.copy(input.filePath, output.filePath); }
return cmd;
}
diff --git a/tests/auto/blackbox/testdata/protobuf-library-install/hello.proto b/tests/auto/blackbox/testdata/protobuf-library-install/hello.proto
new file mode 100644
index 000000000..946108ddc
--- /dev/null
+++ b/tests/auto/blackbox/testdata/protobuf-library-install/hello.proto
@@ -0,0 +1,8 @@
+syntax = "proto3";
+package protobuf.library;
+
+import "hello/world.proto";
+
+message Hello {
+ hello.World world = 1;
+};
diff --git a/tests/auto/blackbox/testdata/protobuf-library-install/hello/world.proto b/tests/auto/blackbox/testdata/protobuf-library-install/hello/world.proto
new file mode 100644
index 000000000..f718621b6
--- /dev/null
+++ b/tests/auto/blackbox/testdata/protobuf-library-install/hello/world.proto
@@ -0,0 +1,6 @@
+syntax = "proto3";
+package protobuf.library.hello;
+
+message World {
+ int32 value = 1;
+};
diff --git a/tests/auto/blackbox/testdata/protobuf-library-install/protobuf-library.qbs b/tests/auto/blackbox/testdata/protobuf-library-install/protobuf-library.qbs
new file mode 100644
index 000000000..501675c15
--- /dev/null
+++ b/tests/auto/blackbox/testdata/protobuf-library-install/protobuf-library.qbs
@@ -0,0 +1,34 @@
+import qbs.Host
+
+StaticLibrary {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result && hasProtobuf;
+ }
+
+ Depends { name: "cpp" }
+ cpp.cxxLanguageVersion: "c++11"
+ cpp.minimumMacosVersion: "10.8"
+
+ protobuf.cpp.importPaths: product.sourceDirectory
+
+ Depends { name: "protobuf.cpp"; required: false }
+ property bool hasProtobuf: {
+ console.info("has protobuf: " + protobuf.cpp.present);
+ return protobuf.cpp.present;
+ }
+
+ Group {
+ fileTagsFilter: "protobuf.hpp"
+ qbs.installDir: "include"
+ qbs.installSourceBase: protobuf.cpp.outputDir
+ qbs.install: true
+ }
+
+ files: [
+ "hello.proto",
+ "hello/world.proto",
+ ]
+}
diff --git a/tests/auto/blackbox/testdata/protobuf/addressbook_cpp.qbs b/tests/auto/blackbox/testdata/protobuf/addressbook_cpp.qbs
index a5a4caf75..959552ea5 100644
--- a/tests/auto/blackbox/testdata/protobuf/addressbook_cpp.qbs
+++ b/tests/auto/blackbox/testdata/protobuf/addressbook_cpp.qbs
@@ -1,16 +1,22 @@
-import qbs
+import qbs.Host
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result && hasProtobuf;
+ }
name: "addressbook_cpp"
consoleApplication: true
- condition: hasProtobuf
Depends { name: "cpp" }
- cpp.cxxLanguageVersion: "c++11"
+ cpp.minimumMacosVersion: "10.15"
Depends { name: "protobuf.cpp"; required: false }
property bool hasProtobuf: {
console.info("has protobuf: " + protobuf.cpp.present);
+ console.info("has modules: " + protobuflib.present);
return protobuf.cpp.present;
}
diff --git a/tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.options b/tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.options
new file mode 100644
index 000000000..60726de93
--- /dev/null
+++ b/tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.options
@@ -0,0 +1,3 @@
+tutorial.Person.name max_size:128
+tutorial.Person.email max_size:256
+tutorial.Person.phones max_count:16
diff --git a/tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.proto b/tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.proto
new file mode 100644
index 000000000..5db0bedbf
--- /dev/null
+++ b/tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.proto
@@ -0,0 +1,38 @@
+// See README.txt for information and build instructions.
+//
+// Note: START and END tags are used in comments to define sections used in
+// tutorials. They are not part of the syntax for Protocol Buffers.
+//
+// To get an in-depth walkthrough of this file and the related examples, see:
+// https://developers.google.com/protocol-buffers/docs/tutorials
+
+// [START declaration]
+syntax = "proto3";
+package tutorial;
+// [END declaration]
+
+// [START messages]
+message Person {
+ string name = 1;
+ int32 id = 2; // Unique ID number for this person.
+ string email = 3;
+
+ enum PhoneType {
+ MOBILE = 0;
+ HOME = 1;
+ WORK = 2;
+ }
+
+ message PhoneNumber {
+ string number = 1;
+ PhoneType type = 2;
+ }
+
+ repeated PhoneNumber phones = 4;
+}
+
+// Our address book file is just one of these.
+message AddressBook {
+ repeated Person people = 1;
+}
+// [END messages]
diff --git a/tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.qbs b/tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.qbs
new file mode 100644
index 000000000..3dfc911e1
--- /dev/null
+++ b/tests/auto/blackbox/testdata/protobuf/addressbook_nanopb.qbs
@@ -0,0 +1,29 @@
+import qbs.Host
+
+CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result && hasProtobuf;
+ }
+ name: "addressbook_nanopb"
+ consoleApplication: true
+
+ Depends { name: "cpp" }
+ cpp.minimumMacosVersion: "10.8"
+
+ Depends { name: "protobuf.nanopb"; required: false }
+ property bool hasProtobuf: {
+ console.info("has protobuf: " + protobuf.nanopb.present);
+ console.info("has modules: false");
+ return protobuf.nanopb.present;
+ }
+ protobuf.nanopb.importPaths: product.sourceDirectory
+
+ files: [
+ "addressbook_nanopb.proto",
+ "addressbook_nanopb.options",
+ "main_nanopb.cpp",
+ ]
+}
diff --git a/tests/auto/blackbox/testdata/protobuf/addressbook_objc.qbs b/tests/auto/blackbox/testdata/protobuf/addressbook_objc.qbs
index be68abfee..7f0b999aa 100644
--- a/tests/auto/blackbox/testdata/protobuf/addressbook_objc.qbs
+++ b/tests/auto/blackbox/testdata/protobuf/addressbook_objc.qbs
@@ -1,14 +1,20 @@
-import qbs
+import qbs.Host
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result && hasProtobuf;
+ }
name: "addressbook_objc"
consoleApplication: true
- condition: hasProtobuf
Depends { name: "cpp" }
Depends { name: "protobuf.objc"; required: false }
property bool hasProtobuf: {
console.info("has protobuf: " + protobuf.objc.present);
+ console.info("has modules: false");
return protobuf.objc.present;
}
diff --git a/tests/auto/blackbox/testdata/protobuf/create-proto-library.qbs b/tests/auto/blackbox/testdata/protobuf/create-proto-library.qbs
new file mode 100644
index 000000000..005752fd8
--- /dev/null
+++ b/tests/auto/blackbox/testdata/protobuf/create-proto-library.qbs
@@ -0,0 +1,53 @@
+import qbs.Host
+
+Project {
+ StaticLibrary {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result && hasProtobuf;
+ }
+ name: "proto_lib"
+
+ Depends { name: "cpp" }
+ cpp.minimumMacosVersion: "10.8"
+
+ protobuf.cpp.importPaths: product.sourceDirectory
+
+ Depends { name: "protobuf.cpp"; required: false }
+ property bool hasProtobuf: {
+ console.info("has protobuf: " + protobuf.cpp.present);
+ console.info("has modules: " + protobuflib.present);
+ return protobuf.cpp.present;
+ }
+
+ files: [
+ "import.proto",
+ "subdir/myenum.proto",
+ ]
+
+ Export {
+ Depends { name: "cpp" }
+ Depends { name: "protobuf.cpp"; required: false }
+ cpp.cxxLanguageVersion: "c++11"
+ cpp.minimumMacosVersion: "10.8"
+ cpp.includePaths: exportingProduct.protobuf.cpp.outputDir
+ }
+ }
+
+ CppApplication {
+ condition: proto_lib.present
+ name: "consumes_proto_lib"
+ consoleApplication: true
+
+ files: [
+ "import-main.cpp",
+ ]
+
+ Depends {
+ name: "proto_lib"
+ required: false
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/protobuf/import-main.cpp b/tests/auto/blackbox/testdata/protobuf/import-main.cpp
index 6d90cdf16..65048dd83 100644
--- a/tests/auto/blackbox/testdata/protobuf/import-main.cpp
+++ b/tests/auto/blackbox/testdata/protobuf/import-main.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2018 Ivan Komissarov
-** Contact: abbapoh@gmail.com
+** Copyright (C) 2018 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
diff --git a/tests/auto/blackbox/testdata/protobuf/import.qbs b/tests/auto/blackbox/testdata/protobuf/import.qbs
index 4c4de063f..56d489de8 100644
--- a/tests/auto/blackbox/testdata/protobuf/import.qbs
+++ b/tests/auto/blackbox/testdata/protobuf/import.qbs
@@ -1,17 +1,23 @@
-import qbs
+import qbs.Host
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result && hasProtobuf;
+ }
name: "app"
consoleApplication: true
- condition: hasProtobuf
protobuf.cpp.importPaths: [sourceDirectory]
- cpp.cxxLanguageVersion: "c++11"
+ cpp.minimumMacosVersion: "10.8"
Depends { name: "protobuf.cpp"; required: false }
property bool hasProtobuf: {
console.info("has protobuf: " + protobuf.cpp.present);
+ console.info("has modules: " + protobuflib.present);
return protobuf.cpp.present;
}
diff --git a/tests/auto/blackbox/testdata/protobuf/main.cpp b/tests/auto/blackbox/testdata/protobuf/main.cpp
index c93c46717..d4b1c431b 100644
--- a/tests/auto/blackbox/testdata/protobuf/main.cpp
+++ b/tests/auto/blackbox/testdata/protobuf/main.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2018 Ivan Komissarov
-** Contact: abbapoh@gmail.com
+** Copyright (C) 2018 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
diff --git a/tests/auto/blackbox/testdata/protobuf/main.m b/tests/auto/blackbox/testdata/protobuf/main.m
index e9d7ce66a..414d1aa2f 100644
--- a/tests/auto/blackbox/testdata/protobuf/main.m
+++ b/tests/auto/blackbox/testdata/protobuf/main.m
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2018 Ivan Komissarov
-** Contact: abbapoh@gmail.com
+** Copyright (C) 2018 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
diff --git a/tests/auto/blackbox/testdata/protobuf/main_nanopb.cpp b/tests/auto/blackbox/testdata/protobuf/main_nanopb.cpp
new file mode 100644
index 000000000..76fa03fca
--- /dev/null
+++ b/tests/auto/blackbox/testdata/protobuf/main_nanopb.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Kai Dohmen (psykai1993@gmail.com)
+** Contact: https://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 <array>
+#include <cassert>
+#include <pb_encode.h>
+
+#include "addressbook_nanopb.pb.h"
+
+static_assert(std::is_array<decltype(std::declval<tutorial_Person>().name)>::value, "");
+static_assert(std::is_array<decltype(std::declval<tutorial_Person>().email)>::value, "");
+static_assert(std::is_array<decltype(std::declval<tutorial_Person>().phones)>::value, "");
+
+bool writeString(pb_ostream_t *stream, const pb_field_t *field, void *const *)
+{
+ constexpr auto str = "0123456789";
+ if (!pb_encode_tag_for_field(stream, field))
+ return false;
+ return pb_encode_string(stream, reinterpret_cast<const uint8_t*>(str), strlen(str));
+}
+
+int main()
+{
+ std::array<uint8_t, 32> buffer = {};
+
+ tutorial_Person_PhoneNumber phoneNumber;
+ phoneNumber.number.funcs.encode = writeString;
+ phoneNumber.type = tutorial_Person_PhoneType_WORK;
+
+ auto ostream = pb_ostream_from_buffer(buffer.data(), buffer.size());
+ assert(pb_encode(&ostream, tutorial_Person_PhoneNumber_fields, &phoneNumber));
+
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata/protobuf/needs-import-dir-main.cpp b/tests/auto/blackbox/testdata/protobuf/needs-import-dir-main.cpp
index d6faf9e84..5d62a0d06 100644
--- a/tests/auto/blackbox/testdata/protobuf/needs-import-dir-main.cpp
+++ b/tests/auto/blackbox/testdata/protobuf/needs-import-dir-main.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2018 Ivan Komissarov
-** Contact: abbapoh@gmail.com
+** Copyright (C) 2018 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
**
** This file is part of Qbs.
**
diff --git a/tests/auto/blackbox/testdata/protobuf/needs-import-dir.qbs b/tests/auto/blackbox/testdata/protobuf/needs-import-dir.qbs
index 788bbc93c..07be566cb 100644
--- a/tests/auto/blackbox/testdata/protobuf/needs-import-dir.qbs
+++ b/tests/auto/blackbox/testdata/protobuf/needs-import-dir.qbs
@@ -1,18 +1,24 @@
-import qbs
+import qbs.Host
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result && hasProtobuf;
+ }
name: "app"
consoleApplication: true
- condition: hasProtobuf
property path theImportDir
protobuf.cpp.importPaths: (theImportDir ? [theImportDir] : []).concat([sourceDirectory])
- cpp.cxxLanguageVersion: "c++11"
+ cpp.minimumMacosVersion: "10.8"
Depends { name: "protobuf.cpp"; required: false }
property bool hasProtobuf: {
console.info("has protobuf: " + protobuf.cpp.present);
+ console.info("has modules: " + protobuflib.present);
return protobuf.cpp.present;
}
diff --git a/tests/auto/blackbox/testdata/qbs-config-import-export/config.json b/tests/auto/blackbox/testdata/qbs-config-import-export/config.json
new file mode 100644
index 000000000..edcaf238a
--- /dev/null
+++ b/tests/auto/blackbox/testdata/qbs-config-import-export/config.json
@@ -0,0 +1,11 @@
+{
+ "group": {
+ "key1": "value1",
+ "key2": "value2"
+ },
+ "key": "value",
+ "listKey": [
+ "valueOne",
+ "valueTwo"
+ ]
+}
diff --git a/tests/auto/blackbox/testdata/qbs-config-import-export/config.txt b/tests/auto/blackbox/testdata/qbs-config-import-export/config.txt
new file mode 100644
index 000000000..2bd19c422
--- /dev/null
+++ b/tests/auto/blackbox/testdata/qbs-config-import-export/config.txt
@@ -0,0 +1,4 @@
+group.key1: "value1"
+group.key2: "value2"
+key: "value"
+listKey: ["valueOne", "valueTwo"]
diff --git a/tests/auto/blackbox/testdata/recursive_wildcards/recursive_wildcards.qbs b/tests/auto/blackbox/testdata/recursive_wildcards/recursive_wildcards.qbs
index 4e9da01d3..4fc275877 100644
--- a/tests/auto/blackbox/testdata/recursive_wildcards/recursive_wildcards.qbs
+++ b/tests/auto/blackbox/testdata/recursive_wildcards/recursive_wildcards.qbs
@@ -23,7 +23,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating " + output.fileName;
+ cmd.description = "creating " + output.fileName;
cmd.sourceCode = function() {
var inputList = explicitlyDependsOn["txt.in"];
var fileNameList = [];
diff --git a/tests/auto/blackbox/testdata/remove-duplicate-libs/remove-duplicate-libs.qbs b/tests/auto/blackbox/testdata/remove-duplicate-libs/remove-duplicate-libs.qbs
index 4ffb8d0e2..d89e47414 100644
--- a/tests/auto/blackbox/testdata/remove-duplicate-libs/remove-duplicate-libs.qbs
+++ b/tests/auto/blackbox/testdata/remove-duplicate-libs/remove-duplicate-libs.qbs
@@ -1,11 +1,13 @@
import "MyStaticLib.qbs" as MyStaticLib
+import qbs.Host
Project {
property bool removeDuplicates
property string libDir: buildDirectory + "/lib"
property bool dummy: {
+ // most BSD systems (including macOS) use LLVM linker now
console.info("is bfd linker: "
- + (qbs.toolchain.contains("gcc") && !qbs.hostOS.contains("macos")))
+ + (qbs.toolchain.includes("gcc") && !Host.os().includes("bsd")))
}
qbsSearchPaths: "."
diff --git a/tests/auto/blackbox/testdata/reproducible-build/reproducible-build.qbs b/tests/auto/blackbox/testdata/reproducible-build/reproducible-build.qbs
index f7ed8e61a..fabdf48db 100644
--- a/tests/auto/blackbox/testdata/reproducible-build/reproducible-build.qbs
+++ b/tests/auto/blackbox/testdata/reproducible-build/reproducible-build.qbs
@@ -2,4 +2,9 @@ CppApplication {
name: "the product"
files: ["file1.cpp", "file2.cpp", "main.cpp"]
cpp.cxxFlags: ["-flto"]
+ Probe {
+ id: checker
+ property bool isGcc: qbs.toolchain.contains("gcc") && !qbs.toolchain.contains("clang")
+ configure: { console.info("is gcc: " + isGcc); }
+ }
}
diff --git a/tests/auto/blackbox/testdata/require-deprecated/blubb.js b/tests/auto/blackbox/testdata/require-deprecated/blubb.js
deleted file mode 100644
index 9acc13968..000000000
--- a/tests/auto/blackbox/testdata/require-deprecated/blubb.js
+++ /dev/null
@@ -1,13 +0,0 @@
-var TextFile = loadExtension("qbs.TextFile")
-var zort = loadFile("zort.js")
-
-function createCommands(filePaths) {
- var cmd = new JavaScriptCommand();
- cmd.description = "Write an empty file";
- cmd.filePath = filePaths[0];
- cmd.sourceCode = function() {
- var f = new TextFile(filePath, TextFile.WriteOnly);
- f.close();
- }
- return [cmd, zort.createCommand(filePaths)];
-}
diff --git a/tests/auto/blackbox/testdata/require-deprecated/require.qbs b/tests/auto/blackbox/testdata/require-deprecated/require.qbs
deleted file mode 100644
index 87d8b054b..000000000
--- a/tests/auto/blackbox/testdata/require-deprecated/require.qbs
+++ /dev/null
@@ -1,21 +0,0 @@
-import 'blubb.js' as blubb
-
-Product {
- type: ["text"]
- Rule {
- multiplex: true
- Artifact {
- fileTags: ["text"]
- filePath: "one.txt"
- }
- Artifact {
- fileTags: ["text"]
- filePath: "two.txt"
- }
- prepare: {
- var filePaths = outputs.text.map(function (artifact) {return artifact.filePath; });
- return blubb.createCommands(filePaths);
- }
- }
-}
-
diff --git a/tests/auto/blackbox/testdata/require-deprecated/zort.js b/tests/auto/blackbox/testdata/require-deprecated/zort.js
deleted file mode 100644
index 0dcffb767..000000000
--- a/tests/auto/blackbox/testdata/require-deprecated/zort.js
+++ /dev/null
@@ -1,11 +0,0 @@
-var File = loadExtension("qbs.File")
-
-function createCommand(filePaths) {
- var cmd = new JavaScriptCommand();
- cmd.description = "Create another empty file";
- cmd.filePaths = filePaths;
- cmd.sourceCode = function() {
- File.copy(filePaths[0], filePaths[1]);
- };
- return cmd;
-}
diff --git a/tests/auto/blackbox/testdata/response-files/response-files.qbs b/tests/auto/blackbox/testdata/response-files/response-files.qbs
index fbb6f0518..c18fcac43 100644
--- a/tests/auto/blackbox/testdata/response-files/response-files.qbs
+++ b/tests/auto/blackbox/testdata/response-files/response-files.qbs
@@ -1,4 +1,5 @@
import qbs.FileInfo
+import qbs.Host
import qbs.TextFile
Project {
@@ -8,6 +9,12 @@ Project {
cpp.enableExceptions: true
}
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "response-file-text"
type: ["text"]
Depends { name: "cpp" }
@@ -38,6 +45,9 @@ Project {
Product {
name: "lotsofobjects"
type: ["dynamiclibrary"]
+ // clang-cl does not use response file internally, thus linker complains that command is
+ // too long. This can be worked around by calling the linker directly
+ cpp.linkerMode: qbs.toolchain.includes("clang-cl") ? "manual" : original
Depends { name: "cpp" }
Rule {
multiplex: true
diff --git a/tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication-lib.cpp b/tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication-lib.cpp
new file mode 100644
index 000000000..6418df94d
--- /dev/null
+++ b/tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication-lib.cpp
@@ -0,0 +1,3 @@
+int dynamicFunc() {
+ return 1;
+} \ No newline at end of file
diff --git a/tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication-main.cpp b/tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication-main.cpp
new file mode 100644
index 000000000..60f8494f6
--- /dev/null
+++ b/tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication-main.cpp
@@ -0,0 +1,5 @@
+extern int dynamicFunc();
+
+int main() {
+ return dynamicFunc();
+} \ No newline at end of file
diff --git a/tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication.qbs b/tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication.qbs
new file mode 100644
index 000000000..adb63872a
--- /dev/null
+++ b/tests/auto/blackbox/testdata/rpathlink-deduplication/rpathlink-deduplication.qbs
@@ -0,0 +1,47 @@
+Project {
+ DynamicLibrary {
+ Depends { name: "bundle" }
+ Depends { name: "cpp" }
+
+ bundle.isBundle: false
+ name: "DynamicLibraryA"
+ files: ["rpathlink-deduplication-lib.cpp"]
+ }
+
+ DynamicLibrary {
+ Depends { name: "bundle" }
+ Depends { name: "cpp" }
+ Depends { name: "DynamicLibraryA" }
+
+ bundle.isBundle: false
+ name: "DynamicLibraryB"
+ files: ["rpathlink-deduplication-lib.cpp"]
+ }
+
+ DynamicLibrary {
+ Depends { name: "bundle" }
+ Depends { name: "cpp" }
+ Depends { name: "DynamicLibraryA" }
+
+ bundle.isBundle: false
+ name: "DynamicLibraryC"
+ files: ["rpathlink-deduplication-lib.cpp"]
+ }
+
+ CppApplication {
+ Depends { name: "bundle" }
+ Depends { name: "DynamicLibraryB" }
+ Depends { name: "DynamicLibraryC" }
+ consoleApplication: true
+ bundle.isBundle: false
+ cpp.removeDuplicateLibraries: false
+ files: "rpathlink-deduplication-main.cpp"
+ property bool test: {
+ if (cpp.useRPathLink)
+ console.info("useRPathLink: true");
+ else
+ console.info("useRPathLink: false");
+ console.info("===" + cpp.rpathLinkFlag + "===");
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/auto/blackbox/testdata/rule-with-non-required-inputs/rule-with-non-required-inputs.qbs b/tests/auto/blackbox/testdata/rule-with-non-required-inputs/rule-with-non-required-inputs.qbs
index 1bd9beebf..9d861b674 100644
--- a/tests/auto/blackbox/testdata/rule-with-non-required-inputs/rule-with-non-required-inputs.qbs
+++ b/tests/auto/blackbox/testdata/rule-with-non-required-inputs/rule-with-non-required-inputs.qbs
@@ -22,7 +22,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Generating " + output.fileName;
+ cmd.description = "generating " + output.fileName;
cmd.sourceCode = function() {
var f = new TextFile(output.filePath, TextFile.WriteOnly);
f.write('(');
diff --git a/tests/auto/blackbox/testdata/run-multiplexed/main.cpp b/tests/auto/blackbox/testdata/run-multiplexed/main.cpp
new file mode 100644
index 000000000..237c8ce18
--- /dev/null
+++ b/tests/auto/blackbox/testdata/run-multiplexed/main.cpp
@@ -0,0 +1 @@
+int main() {}
diff --git a/tests/auto/blackbox/testdata/run-multiplexed/run-multiplexed.qbs b/tests/auto/blackbox/testdata/run-multiplexed/run-multiplexed.qbs
new file mode 100644
index 000000000..11577b54a
--- /dev/null
+++ b/tests/auto/blackbox/testdata/run-multiplexed/run-multiplexed.qbs
@@ -0,0 +1,21 @@
+import qbs.Host
+
+CppApplication {
+ aggregate: false
+ consoleApplication: true
+ name: "app"
+ multiplexByQbsProperties: "buildVariants"
+
+ qbs.buildVariants: ["debug", "release"]
+
+ files: "main.cpp"
+
+ Probe {
+ id: checker
+ property string targetPlatform: qbs.targetPlatform
+ configure: {
+ if (targetPlatform !== Host.platform())
+ console.info("targetPlatform differs from hostPlatform");
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/sanitizer/sanitizer.cpp b/tests/auto/blackbox/testdata/sanitizer/sanitizer.cpp
new file mode 100644
index 000000000..4a7c3ee32
--- /dev/null
+++ b/tests/auto/blackbox/testdata/sanitizer/sanitizer.cpp
@@ -0,0 +1,4 @@
+int main(int argc, char *argv[])
+{
+ return 0;
+}
diff --git a/tests/auto/blackbox/testdata/sanitizer/sanitizer.qbs b/tests/auto/blackbox/testdata/sanitizer/sanitizer.qbs
new file mode 100644
index 000000000..438f3cc39
--- /dev/null
+++ b/tests/auto/blackbox/testdata/sanitizer/sanitizer.qbs
@@ -0,0 +1,44 @@
+CppApplication {
+ Depends { name: "Sanitizers.address" }
+ Sanitizers.address.enabled: sanitizer === "address"
+ property string sanitizer
+
+ property bool supportsSanitizer: {
+ if (qbs.toolchain.includes("mingw"))
+ return false;
+ if (sanitizer === "address")
+ return Sanitizers.address._supported;
+ if (qbs.toolchain.includes("clang-cl")) {
+ if (cpp.toolchainInstallPath.includes("Microsoft Visual Studio")
+ && qbs.architecture === "x86_64") {
+ // 32 bit sanitizer shipped with VS misses the x86_64 libraries
+ return false;
+ }
+ // only these are supported
+ return sanitizer === "address" || sanitizer === "undefined";
+ }
+ if (!qbs.toolchain.includes("gcc"))
+ return false;
+ if (qbs.targetOS.includes("ios")) {
+ // thread sanitizer is not supported
+ return sanitizer !== "thread";
+ }
+ return true;
+ }
+
+ condition: {
+ if (!sanitizer)
+ return true;
+ if (!supportsSanitizer)
+ console.info("Compiler does not support sanitizer");
+ return supportsSanitizer;
+ }
+ qbs.buildVariant: "release"
+ cpp.cxxLanguageVersion: "c++11"
+ cpp.minimumMacosVersion: "10.8"
+ consoleApplication: true
+ cpp.runtimeLibrary: "static"
+ cpp.driverFlags: sanitizer && sanitizer !== "address" ? ["-fsanitize=" + sanitizer] : []
+ cpp.debugInformation: true
+ files: "sanitizer.cpp"
+}
diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.h b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.h
new file mode 100644
index 000000000..a82b12fbd
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.h
@@ -0,0 +1 @@
+#include "lib.h"
diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.qbs b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.qbs
new file mode 100644
index 000000000..e931b853c
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/app.qbs
@@ -0,0 +1,4 @@
+CppApplication {
+ cpp.includePaths: project.sourceDirectory + "/lib"
+ files: "main.cpp"
+}
diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/main.cpp b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/main.cpp
new file mode 100644
index 000000000..2e7bedac8
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/app/main.cpp
@@ -0,0 +1,3 @@
+#include "app.h"
+
+int main() { }
diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/lib/lib.h b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/lib/lib.h
new file mode 100644
index 000000000..af6f627b7
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/lib/lib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void lib1_foo(); \ No newline at end of file
diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/other/other.qbs b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/other/other.qbs
new file mode 100644
index 000000000..29682da1c
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/other/other.qbs
@@ -0,0 +1,24 @@
+import qbs.TextFile
+
+Product {
+ type: "testproduct"
+ files: "../lib/lib.h"
+
+ Rule {
+ multiplex: true
+ Artifact {
+ fileTags: ["testproduct"]
+ filePath: "fubar"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating text file";
+ cmd.sourceCode = function() {
+ var tf = new TextFile(output.filePath, TextFile.WriteOnly);
+ tf.writeLine("blubb");
+ tf.close();
+ }
+ return cmd;
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/scan-result-in-non-dependency/p.qbs b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/p.qbs
new file mode 100644
index 000000000..bcbd5ebce
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-non-dependency/p.qbs
@@ -0,0 +1,6 @@
+Project {
+ references: [
+ "app/app.qbs",
+ "other/other.qbs",
+ ]
+}
diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.h b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.h
new file mode 100644
index 000000000..a82b12fbd
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.h
@@ -0,0 +1 @@
+#include "lib.h"
diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.qbs b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.qbs
new file mode 100644
index 000000000..984e9aca9
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/app.qbs
@@ -0,0 +1,4 @@
+CppApplication {
+ Depends { name: "lib" }
+ files: "main.cpp"
+}
diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/app/main.cpp b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/main.cpp
new file mode 100644
index 000000000..2e7bedac8
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/app/main.cpp
@@ -0,0 +1,3 @@
+#include "app.h"
+
+int main() { }
diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.h b/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.h
new file mode 100644
index 000000000..af6f627b7
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.h
@@ -0,0 +1,3 @@
+#pragma once
+
+void lib1_foo(); \ No newline at end of file
diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.qbs b/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.qbs
new file mode 100644
index 000000000..a6ed0c990
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/lib/lib.qbs
@@ -0,0 +1,7 @@
+Product {
+ files: "lib.h"
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: exportingProduct.sourceDirectory
+ }
+}
diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/other/other.qbs b/tests/auto/blackbox/testdata/scan-result-in-other-product/other/other.qbs
new file mode 100644
index 000000000..29682da1c
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/other/other.qbs
@@ -0,0 +1,24 @@
+import qbs.TextFile
+
+Product {
+ type: "testproduct"
+ files: "../lib/lib.h"
+
+ Rule {
+ multiplex: true
+ Artifact {
+ fileTags: ["testproduct"]
+ filePath: "fubar"
+ }
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating text file";
+ cmd.sourceCode = function() {
+ var tf = new TextFile(output.filePath, TextFile.WriteOnly);
+ tf.writeLine("blubb");
+ tf.close();
+ }
+ return cmd;
+ }
+ }
+}
diff --git a/tests/auto/blackbox/testdata/scan-result-in-other-product/p.qbs b/tests/auto/blackbox/testdata/scan-result-in-other-product/p.qbs
new file mode 100644
index 000000000..fedf84989
--- /dev/null
+++ b/tests/auto/blackbox/testdata/scan-result-in-other-product/p.qbs
@@ -0,0 +1,7 @@
+Project {
+ references: [
+ "app/app.qbs",
+ "lib/lib.qbs",
+ "other/other.qbs",
+ ]
+}
diff --git a/tests/auto/blackbox/testdata/separate-debug-info/separate-debug-info.qbs b/tests/auto/blackbox/testdata/separate-debug-info/separate-debug-info.qbs
index 0b16d1984..4198b863f 100644
--- a/tests/auto/blackbox/testdata/separate-debug-info/separate-debug-info.qbs
+++ b/tests/auto/blackbox/testdata/separate-debug-info/separate-debug-info.qbs
@@ -8,9 +8,13 @@ Project {
Probe {
id: osProbe
property stringList targetOS: qbs.targetOS
+ property stringList toolchain: qbs.toolchain
configure: {
- console.info("is windows: " + (targetOS.contains("windows") ? "yes" : "no"));
- console.info("is darwin: " + (targetOS.contains("darwin") ? "yes" : "no"));
+ console.info("is windows: " + (targetOS.includes("windows") ? "yes" : "no"));
+ console.info("is macos: " + (targetOS.includes("macos") ? "yes" : "no"));
+ console.info("is darwin: " + (targetOS.includes("darwin") ? "yes" : "no"));
+ console.info("is gcc: " + (toolchain.includes("gcc") ? "yes" : "no"));
+ console.info("is msvc: " + (toolchain.includes("msvc") ? "yes" : "no"));
}
}
}
@@ -54,7 +58,7 @@ Project {
files: ["main.cpp"]
cpp.separateDebugInformation: true
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
cpp.dsymutilFlags: ["--flat"]
}
}
@@ -65,7 +69,7 @@ Project {
files: ["foo.cpp"]
cpp.separateDebugInformation: true
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
cpp.dsymutilFlags: ["--flat"]
}
}
@@ -75,7 +79,7 @@ Project {
files: ["foo.cpp"]
cpp.separateDebugInformation: true
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
cpp.dsymutilFlags: ["--flat"]
}
}
@@ -93,7 +97,7 @@ Project {
type: ["dynamiclibrary"]
files: ["foo.cpp"]
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
cpp.separateDebugInformation: true
@@ -103,7 +107,7 @@ Project {
name: "bar4"
files: ["foo.cpp"]
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
cpp.separateDebugInformation: true
@@ -114,12 +118,12 @@ Project {
type: ["application"]
files: ["main.cpp"]
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
cpp.separateDebugInformation: true
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
cpp.dsymutilFlags: ["--flat"]
}
}
@@ -129,12 +133,12 @@ Project {
type: ["dynamiclibrary"]
files: ["foo.cpp"]
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
cpp.separateDebugInformation: true
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
cpp.dsymutilFlags: ["--flat"]
}
}
@@ -143,12 +147,12 @@ Project {
name: "bar5"
files: ["foo.cpp"]
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
cpp.separateDebugInformation: true
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
cpp.dsymutilFlags: ["--flat"]
}
}
diff --git a/tests/auto/blackbox/testdata/setup-run-environment/setup-run-environment.qbs b/tests/auto/blackbox/testdata/setup-run-environment/setup-run-environment.qbs
index d2d47b767..b1310c005 100644
--- a/tests/auto/blackbox/testdata/setup-run-environment/setup-run-environment.qbs
+++ b/tests/auto/blackbox/testdata/setup-run-environment/setup-run-environment.qbs
@@ -1,4 +1,5 @@
import qbs.FileInfo
+import qbs.Host
Project {
DynamicLibrary { // Product dependency, installed
@@ -7,13 +8,13 @@ Project {
files: ["lib1.cpp"]
- install: !qbs.targetOS.contains("darwin")
+ install: !qbs.targetOS.includes("darwin")
installImportLib: true
installDir: "lib1"
importLibInstallDir: installDir
Group {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
fileTagsFilter: ["bundle.content"]
qbs.install: true
qbs.installSourceBase: destinationDirectory
@@ -26,7 +27,7 @@ Project {
Depends { name: "lib5" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
@@ -40,7 +41,7 @@ Project {
files: ["lib3.cpp"]
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
@@ -57,14 +58,14 @@ Project {
files: ["lib4.cpp"]
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
// Testing shows that clang (8.0) does not find dynamic libraries via
// the -L<dir> and -l<libname> mechanism unless the name is "lib<libname>.a".
Properties {
- condition: qbs.hostOS.contains("windows") && qbs.toolchain.contains("clang")
+ condition: Host.os().includes("windows") && qbs.toolchain.includes("clang")
cpp.dynamicLibraryPrefix: "lib"
cpp.dynamicLibraryImportSuffix: ".a"
}
@@ -82,7 +83,7 @@ Project {
Depends { name: "cpp" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
@@ -90,6 +91,12 @@ Project {
}
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: "app"
consoleApplication: true
files: "main.cpp"
@@ -101,7 +108,7 @@ Project {
property string fullInstallPrefix: FileInfo.joinPaths(qbs.installRoot, qbs.installPrefix)
property string lib3FilePath: FileInfo.joinPaths(fullInstallPrefix, "lib3",
- cpp.dynamicLibraryPrefix + "lib3" + (qbs.targetOS.contains("windows")
+ cpp.dynamicLibraryPrefix + "lib3" + (qbs.targetOS.includes("windows")
? cpp.dynamicLibraryImportSuffix
: cpp.dynamicLibrarySuffix))
cpp.dynamicLibraries: [lib3FilePath, "lib4"]
@@ -110,7 +117,7 @@ Project {
Probe {
id: osPrinter
- property bool isWindows: qbs.targetOS.contains("windows")
+ property bool isWindows: qbs.targetOS.includes("windows")
configure: {
console.info("is windows");
found = true;
diff --git a/tests/auto/blackbox/testdata/smart-relinking/smart-relinking.qbs b/tests/auto/blackbox/testdata/smart-relinking/smart-relinking.qbs
index aac0692a8..29e6c2e17 100644
--- a/tests/auto/blackbox/testdata/smart-relinking/smart-relinking.qbs
+++ b/tests/auto/blackbox/testdata/smart-relinking/smart-relinking.qbs
@@ -5,7 +5,7 @@ Project {
property stringList toolchain: qbs.toolchain
property stringList targetOS: qbs.targetOS
configure: {
- found = toolchain.contains("gcc") && targetOS.contains("unix");
+ found = toolchain.includes("gcc") && targetOS.includes("unix");
if (!found)
console.info("project disabled");
}
diff --git a/tests/auto/blackbox/testdata/source-artifact-changes/source-artifact-changes.qbs b/tests/auto/blackbox/testdata/source-artifact-changes/source-artifact-changes.qbs
index de56376df..a53819753 100644
--- a/tests/auto/blackbox/testdata/source-artifact-changes/source-artifact-changes.qbs
+++ b/tests/auto/blackbox/testdata/source-artifact-changes/source-artifact-changes.qbs
@@ -4,7 +4,7 @@ CppApplication {
consoleApplication: true
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.embedInfoPlist: false
}
@@ -12,7 +12,7 @@ CppApplication {
id: toolchainProbe
property stringList toolchain: qbs.toolchain
configure: {
- console.info("is gcc: " + toolchain.contains("gcc"));
+ console.info("is gcc: " + toolchain.includes("gcc"));
found = true;
}
}
diff --git a/tests/auto/blackbox/testdata/static-lib-without-sources/static-lib-without-sources.qbs b/tests/auto/blackbox/testdata/static-lib-without-sources/static-lib-without-sources.qbs
index 32a58c94b..54ec5e230 100644
--- a/tests/auto/blackbox/testdata/static-lib-without-sources/static-lib-without-sources.qbs
+++ b/tests/auto/blackbox/testdata/static-lib-without-sources/static-lib-without-sources.qbs
@@ -6,7 +6,7 @@ StaticLibrary {
}
Product {
- type: qbs.targetOS.contains("darwin") ? undefined : ["staticlibrary"]
+ type: qbs.targetOS.includes("darwin") ? undefined : ["staticlibrary"]
name: "b"
Depends { name: "cpp" }
Depends { name: "a" }
diff --git a/tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs b/tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs
index 77df81e39..c33e24d34 100644
--- a/tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs
+++ b/tests/auto/blackbox/testdata/successive-changes/successive-changes.qbs
@@ -17,7 +17,7 @@ Project {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating output";
+ cmd.description = "creating output";
cmd.sourceCode = function() {
var f = new TextFile(output.filePath, TextFile.WriteOnly);
f.write(project.version);
diff --git a/tests/auto/blackbox/testdata/symbolLinkMode/lib.cpp b/tests/auto/blackbox/testdata/symbolLinkMode/lib.cpp
index 8d96f5094..9dcb48738 100644
--- a/tests/auto/blackbox/testdata/symbolLinkMode/lib.cpp
+++ b/tests/auto/blackbox/testdata/symbolLinkMode/lib.cpp
@@ -3,9 +3,9 @@ int somefunction()
return 42;
}
-#include <stdio.h>
+#include <cstdio>
static const auto func = []() {
- printf("Lib was loaded!\n");
+ std::printf("Lib was loaded!\n");
return 0;
}();
diff --git a/tests/auto/blackbox/testdata/symbolLinkMode/main.cpp b/tests/auto/blackbox/testdata/symbolLinkMode/main.cpp
index 801491634..4d847ba19 100644
--- a/tests/auto/blackbox/testdata/symbolLinkMode/main.cpp
+++ b/tests/auto/blackbox/testdata/symbolLinkMode/main.cpp
@@ -1,15 +1,15 @@
extern WEAK_IMPORT int somefunction();
extern void indirect();
-#include <stdio.h>
+#include <cstdio>
int main()
{
- printf("meow\n");
+ std::printf("meow\n");
if (&somefunction != nullptr)
- printf("somefunction existed and it returned %d\n", somefunction());
+ std::printf("somefunction existed and it returned %d\n", somefunction());
else
- printf("somefunction did not exist\n");
+ std::printf("somefunction did not exist\n");
#if SHOULD_INSTALL_LIB
indirect();
#endif
diff --git a/tests/auto/blackbox/testdata/symbolLinkMode/symbolLinkMode.qbs b/tests/auto/blackbox/testdata/symbolLinkMode/symbolLinkMode.qbs
index 2d3d64b00..49bcd9951 100644
--- a/tests/auto/blackbox/testdata/symbolLinkMode/symbolLinkMode.qbs
+++ b/tests/auto/blackbox/testdata/symbolLinkMode/symbolLinkMode.qbs
@@ -1,15 +1,22 @@
import qbs.FileInfo
+import qbs.Host
Project {
property bool shouldInstallLibrary: true
property bool lazy: false
Application {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
Depends { name: "cpp" }
Depends {
name: "functions";
cpp.symbolLinkMode: product.symbolLinkMode
- cpp.link: !(product.qbs.targetOS.contains("linux") && product.symbolLinkMode === "weak")
+ cpp.link: !(product.qbs.targetOS.includes("linux") && product.symbolLinkMode === "weak")
}
property string symbolLinkMode: project.lazy ? "lazy" : "weak"
@@ -20,7 +27,7 @@ Project {
property string installLib: "SHOULD_INSTALL_LIB=" + project.shouldInstallLibrary
cpp.defines: {
if (symbolLinkMode === "weak") {
- return qbs.targetOS.contains("darwin")
+ return qbs.targetOS.includes("darwin")
? ["WEAK_IMPORT=__attribute__((weak_import))", installLib]
: ["WEAK_IMPORT=__attribute__((weak))", installLib];
}
@@ -42,7 +49,7 @@ Project {
Depends { name: "indirect"; cpp.symbolLinkMode: "reexport" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
name: "functions"
@@ -52,7 +59,7 @@ Project {
cpp.rpaths: [cpp.rpathOrigin]
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
cpp.sonamePrefix: "@rpath"
}
@@ -65,7 +72,7 @@ Project {
Export {
// let the autotest pass on Linux where reexport is not supported
- Depends { name: "indirect"; condition: !qbs.targetOS.contains("darwin") }
+ Depends { name: "indirect"; condition: !qbs.targetOS.includes("darwin") }
// on Linux, there is no LC_WEAK_LOAD_DYLIB equivalent (the library is simply omitted
// from the list of load commands entirely), so use LD_PRELOAD to emulate
@@ -84,7 +91,7 @@ Project {
Depends { name: "cpp" }
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.isBundle: false
}
name: "indirect"
@@ -93,7 +100,7 @@ Project {
cpp.minimumMacosVersion: "10.7"
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
// reexport is incompatible with rpath,
// "ERROR: ld: file not found: @rpath/libindirect.dylib for architecture x86_64"
cpp.sonamePrefix: qbs.installRoot + "/lib"
diff --git a/tests/auto/blackbox/testdata/system-include-paths/main.cpp b/tests/auto/blackbox/testdata/system-include-paths/main.cpp
index e449173d3..10d376a31 100644
--- a/tests/auto/blackbox/testdata/system-include-paths/main.cpp
+++ b/tests/auto/blackbox/testdata/system-include-paths/main.cpp
@@ -1,4 +1,4 @@
-#include <stdio.h>
+#include <cstdio>
#include <gagagugu.h>
int main()
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
index 6e2137173..35dd7a00f 100644
--- a/tests/auto/blackbox/testdata/system-run-paths/system-run-paths.qbs
+++ b/tests/auto/blackbox/testdata/system-run-paths/system-run-paths.qbs
@@ -20,4 +20,9 @@ Project {
cpp.rpaths: qbs.installRoot + "/lib"
cpp.systemRunPaths: project.setRunPaths ? [qbs.installRoot + "/lib"] : []
}
+ Probe {
+ id: checker
+ property bool isUnix: qbs.targetOS.contains("unix")
+ configure: { console.info("is unix: " + isUnix); }
+ }
}
diff --git a/tests/auto/blackbox/testdata/trackAddFile/after/main.cpp b/tests/auto/blackbox/testdata/trackAddFile/after/main.cpp
index 0e474b221..d108825d6 100644
--- a/tests/auto/blackbox/testdata/trackAddFile/after/main.cpp
+++ b/tests/auto/blackbox/testdata/trackAddFile/after/main.cpp
@@ -31,7 +31,7 @@
int main(int argc, char **argv)
{
- printf("Hello World!\n");
+ std::printf("Hello World!\n");
Narf narf;
narf.shout();
Zort zort;
diff --git a/tests/auto/blackbox/testdata/trackAddFile/after/trackAddFile.qbs b/tests/auto/blackbox/testdata/trackAddFile/after/trackAddFile.qbs
index f4b9ba21f..4d454d4ff 100644
--- a/tests/auto/blackbox/testdata/trackAddFile/after/trackAddFile.qbs
+++ b/tests/auto/blackbox/testdata/trackAddFile/after/trackAddFile.qbs
@@ -1,5 +1,13 @@
+import qbs.Host
+
Project {
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: 'someapp'
type: 'application'
consoleApplication: true
diff --git a/tests/auto/blackbox/testdata/trackAddFile/after/zort.cpp b/tests/auto/blackbox/testdata/trackAddFile/after/zort.cpp
index 1915ea869..55a9dfa6d 100644
--- a/tests/auto/blackbox/testdata/trackAddFile/after/zort.cpp
+++ b/tests/auto/blackbox/testdata/trackAddFile/after/zort.cpp
@@ -30,6 +30,6 @@
void Zort::shout()
{
- printf("ZORT!\n");
+ std::printf("ZORT!\n");
}
diff --git a/tests/auto/blackbox/testdata/trackAddFile/before/main.cpp b/tests/auto/blackbox/testdata/trackAddFile/before/main.cpp
index f989e1d9b..e8915030e 100644
--- a/tests/auto/blackbox/testdata/trackAddFile/before/main.cpp
+++ b/tests/auto/blackbox/testdata/trackAddFile/before/main.cpp
@@ -30,7 +30,7 @@
int main(int argc, char **argv)
{
- printf("Hello World!\n");
+ std::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
index 280d5f3bd..c301b57df 100644
--- a/tests/auto/blackbox/testdata/trackAddFile/before/narf.cpp
+++ b/tests/auto/blackbox/testdata/trackAddFile/before/narf.cpp
@@ -30,6 +30,6 @@
void Narf::shout()
{
- printf("NARF!\n");
+ std::printf("NARF!\n");
}
diff --git a/tests/auto/blackbox/testdata/trackAddFile/before/trackAddFile.qbs b/tests/auto/blackbox/testdata/trackAddFile/before/trackAddFile.qbs
index bb0ab7d44..fd858b247 100644
--- a/tests/auto/blackbox/testdata/trackAddFile/before/trackAddFile.qbs
+++ b/tests/auto/blackbox/testdata/trackAddFile/before/trackAddFile.qbs
@@ -1,5 +1,13 @@
+import qbs.Host
+
Project {
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: 'someapp'
type: 'application'
consoleApplication: true
diff --git a/tests/auto/blackbox/testdata/trackExternalProductChanges/trackExternalProductChanges.qbs b/tests/auto/blackbox/testdata/trackExternalProductChanges/trackExternalProductChanges.qbs
index 534f49ff2..0e28e5687 100644
--- a/tests/auto/blackbox/testdata/trackExternalProductChanges/trackExternalProductChanges.qbs
+++ b/tests/auto/blackbox/testdata/trackExternalProductChanges/trackExternalProductChanges.qbs
@@ -11,4 +11,9 @@ CppApplication {
name: "file that needs help from the environment to find a header"
files: "including.cpp"
}
+ Probe {
+ id: checker
+ property bool isGcc: qbs.toolchain.contains("gcc")
+ configure: { console.info("is gcc: " + isGcc); }
+ }
}
diff --git a/tests/auto/blackbox/testdata/trackFileTags/after/main.cpp b/tests/auto/blackbox/testdata/trackFileTags/after/main.cpp
index 1d2c8ebb7..7520b5ed7 100644
--- a/tests/auto/blackbox/testdata/trackFileTags/after/main.cpp
+++ b/tests/auto/blackbox/testdata/trackFileTags/after/main.cpp
@@ -31,7 +31,7 @@ int foo();
int main(int argc, char **argv)
{
- printf("there's %d foo here\n", foo());
+ std::printf("there's %d foo here\n", foo());
return 0;
}
diff --git a/tests/auto/blackbox/testdata/trackFileTags/after/trackFileTags.qbs b/tests/auto/blackbox/testdata/trackFileTags/after/trackFileTags.qbs
index 869ce238b..1c5b6b56f 100644
--- a/tests/auto/blackbox/testdata/trackFileTags/after/trackFileTags.qbs
+++ b/tests/auto/blackbox/testdata/trackFileTags/after/trackFileTags.qbs
@@ -1,7 +1,15 @@
import qbs.TextFile
+import qbs.Host
+
Project {
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: 'someapp'
type: 'application'
consoleApplication: true
diff --git a/tests/auto/blackbox/testdata/trackFileTags/before/main.cpp b/tests/auto/blackbox/testdata/trackFileTags/before/main.cpp
index 3016f8bb1..b4d71f995 100644
--- a/tests/auto/blackbox/testdata/trackFileTags/before/main.cpp
+++ b/tests/auto/blackbox/testdata/trackFileTags/before/main.cpp
@@ -29,7 +29,7 @@
int main(int argc, char **argv)
{
- printf("there's no foo here\n");
+ std::printf("there's no foo here\n");
return 0;
}
diff --git a/tests/auto/blackbox/testdata/trackFileTags/before/trackFileTags.qbs b/tests/auto/blackbox/testdata/trackFileTags/before/trackFileTags.qbs
index ebe31bf26..86771ab8f 100644
--- a/tests/auto/blackbox/testdata/trackFileTags/before/trackFileTags.qbs
+++ b/tests/auto/blackbox/testdata/trackFileTags/before/trackFileTags.qbs
@@ -1,7 +1,15 @@
import qbs.TextFile
+import qbs.Host
+
Project {
Product {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
name: 'someapp'
type: 'application'
consoleApplication: true
diff --git a/tests/auto/blackbox/testdata/trackProducts/after/zoo.cpp b/tests/auto/blackbox/testdata/trackProducts/after/zoo.cpp
index b65fb3f9d..fc0d13e4b 100644
--- a/tests/auto/blackbox/testdata/trackProducts/after/zoo.cpp
+++ b/tests/auto/blackbox/testdata/trackProducts/after/zoo.cpp
@@ -30,5 +30,5 @@
int main()
{
- printf("zoo\n");
+ std::printf("zoo\n");
}
diff --git a/tests/auto/blackbox/testdata/trackProducts/before/bar.cpp b/tests/auto/blackbox/testdata/trackProducts/before/bar.cpp
index 7880c8669..3664897de 100644
--- a/tests/auto/blackbox/testdata/trackProducts/before/bar.cpp
+++ b/tests/auto/blackbox/testdata/trackProducts/before/bar.cpp
@@ -30,5 +30,5 @@
int main()
{
- printf("bar\n");
+ std::printf("bar\n");
}
diff --git a/tests/auto/blackbox/testdata/trackProducts/before/foo.cpp b/tests/auto/blackbox/testdata/trackProducts/before/foo.cpp
index 865fb1299..fb5c32769 100644
--- a/tests/auto/blackbox/testdata/trackProducts/before/foo.cpp
+++ b/tests/auto/blackbox/testdata/trackProducts/before/foo.cpp
@@ -30,5 +30,5 @@
int main()
{
- printf("foo\n");
+ std::printf("foo\n");
}
diff --git a/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/a/a.qbs b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/a/a.qbs
new file mode 100644
index 000000000..fd52488fb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/a/a.qbs
@@ -0,0 +1,5 @@
+Module {
+ validate: {
+ throw "Module cannot be loaded";
+ }
+}
diff --git a/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/b/b.qbs b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/b/b.qbs
new file mode 100644
index 000000000..605a2aaee
--- /dev/null
+++ b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/b/b.qbs
@@ -0,0 +1,3 @@
+Module {
+ Depends { name: "a" }
+}
diff --git a/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/c/c.qbs b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/c/c.qbs
new file mode 100644
index 000000000..ac7dbbec6
--- /dev/null
+++ b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/c/c.qbs
@@ -0,0 +1,3 @@
+Module {
+ Depends { name: "a"; required: false }
+}
diff --git a/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/d/d.qbs b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/d/d.qbs
new file mode 100644
index 000000000..0bdd8c3b7
--- /dev/null
+++ b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/modules/d/d.qbs
@@ -0,0 +1,4 @@
+Module {
+ Depends { name: "b"; }
+ Depends { name: "c"; }
+}
diff --git a/tests/auto/blackbox/testdata/transitive-invalid-dependencies/transitive-invalid-dependencies.qbs b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/transitive-invalid-dependencies.qbs
new file mode 100644
index 000000000..209b1e47d
--- /dev/null
+++ b/tests/auto/blackbox/testdata/transitive-invalid-dependencies/transitive-invalid-dependencies.qbs
@@ -0,0 +1,11 @@
+Product {
+ property bool modulePresent: {
+ console.info("b.present = " + b.present);
+ console.info("c.present = " + c.present);
+ console.info("d.present = " + d.present);
+ }
+
+ Depends { name: "b"; required: false }
+ Depends { name: "c"; required: false }
+ Depends { name: "d"; required: false }
+}
diff --git a/tests/auto/blackbox/testdata/undefined-target-platform/undefined-target-platform.qbs b/tests/auto/blackbox/testdata/undefined-target-platform/undefined-target-platform.qbs
new file mode 100644
index 000000000..4a738afcc
--- /dev/null
+++ b/tests/auto/blackbox/testdata/undefined-target-platform/undefined-target-platform.qbs
@@ -0,0 +1,13 @@
+import qbs.File
+import qbs.FileInfo
+
+Product {
+ name: "undefined-target-platform"
+ qbs.targetPlatform: undefined
+
+ readonly property bool _validate: {
+ if ((qbs.targetOS instanceof Array) && qbs.targetOS.length === 0)
+ return true;
+ throw "Invalid qbs.targetOS value: " + qbs.targetOS;
+ }
+}
diff --git a/tests/auto/blackbox/testdata/variant-suffix/variant-suffix.qbs b/tests/auto/blackbox/testdata/variant-suffix/variant-suffix.qbs
index b6e025e4c..1131e6a2f 100644
--- a/tests/auto/blackbox/testdata/variant-suffix/variant-suffix.qbs
+++ b/tests/auto/blackbox/testdata/variant-suffix/variant-suffix.qbs
@@ -1,8 +1,8 @@
StaticLibrary {
name: "l"
- Depends { condition: qbs.targetOS.contains("darwin"); name: "bundle" }
- Properties { condition: qbs.targetOS.contains("darwin"); bundle.isBundle: false }
+ Depends { condition: qbs.targetOS.includes("darwin"); name: "bundle" }
+ Properties { condition: qbs.targetOS.includes("darwin"); bundle.isBundle: false }
aggregate: false
property string variantSuffix
@@ -31,8 +31,8 @@ StaticLibrary {
id: targetOSProbe
property stringList targetOS: qbs.targetOS
configure: {
- console.info("is Windows: " + targetOS.contains("windows"));
- console.info("is Apple: " + targetOS.contains("darwin"));
+ console.info("is Windows: " + targetOS.includes("windows"));
+ console.info("is Apple: " + targetOS.includes("darwin"));
}
}
}
diff --git a/tests/auto/blackbox/testdata/vcs/vcstest.qbs b/tests/auto/blackbox/testdata/vcs/vcstest.qbs
index 5d359e2c7..4f861654b 100644
--- a/tests/auto/blackbox/testdata/vcs/vcstest.qbs
+++ b/tests/auto/blackbox/testdata/vcs/vcstest.qbs
@@ -1,4 +1,12 @@
+import qbs.Host
+
CppApplication {
+ condition: {
+ var result = qbs.targetPlatform === Host.platform();
+ if (!result)
+ console.info("targetPlatform differs from hostPlatform");
+ return result;
+ }
Depends { name: "vcs" }
vcs.headerFileName: "my-repo-state.h"
files: ["main.cpp"]
diff --git a/tests/auto/blackbox/testdata/versionscript/versionscript.qbs b/tests/auto/blackbox/testdata/versionscript/versionscript.qbs
index cc5c7b1cc..fcb4314a2 100644
--- a/tests/auto/blackbox/testdata/versionscript/versionscript.qbs
+++ b/tests/auto/blackbox/testdata/versionscript/versionscript.qbs
@@ -20,6 +20,12 @@ DynamicLibrary {
return [cmd];
}
}
+ Probe {
+ id: checker
+ property bool isLinux: qbs.targetOS.includes("linux")
+ property bool isGcc: qbs.toolchain.contains("gcc")
+ configure: { console.info("is gcc for Linux: " + (isLinux && isGcc)); }
+ }
qbs.installPrefix: ""
install: true
diff --git a/tests/auto/blackbox/testdata/whole-archive/whole-archive.qbs b/tests/auto/blackbox/testdata/whole-archive/whole-archive.qbs
index f3bcff2cd..c138e46c0 100644
--- a/tests/auto/blackbox/testdata/whole-archive/whole-archive.qbs
+++ b/tests/auto/blackbox/testdata/whole-archive/whole-archive.qbs
@@ -32,7 +32,8 @@ Project {
property string compilerVersion: cpp.compilerVersion
property string dummy: product.linkWholeArchive // To force probe re-execution
configure: {
- if (!toolchain.contains("msvc")
+ if (!toolchain.includes("msvc")
+ || toolchain.includes("clang-cl")
|| Utilities.versionCompare(compilerVersion, "19.0.24215.1") >= 0) {
console.info("can link whole archives");
} else {
diff --git a/tests/auto/blackbox/testdata/wildcards-and-change-tracking/nonrecursive/subdir1/file.txt b/tests/auto/blackbox/testdata/wildcards-and-change-tracking/nonrecursive/subdir1/file.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/wildcards-and-change-tracking/nonrecursive/subdir1/file.txt
diff --git a/tests/auto/blackbox/testdata/wildcards-and-change-tracking/nonrecursive/subdir2/file.txt b/tests/auto/blackbox/testdata/wildcards-and-change-tracking/nonrecursive/subdir2/file.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/wildcards-and-change-tracking/nonrecursive/subdir2/file.txt
diff --git a/tests/auto/blackbox/testdata/wildcards-and-change-tracking/recursive1/recursive.txt b/tests/auto/blackbox/testdata/wildcards-and-change-tracking/recursive1/recursive.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/wildcards-and-change-tracking/recursive1/recursive.txt
diff --git a/tests/auto/blackbox/testdata/wildcards-and-change-tracking/recursive2/recursive.txt b/tests/auto/blackbox/testdata/wildcards-and-change-tracking/recursive2/recursive.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/auto/blackbox/testdata/wildcards-and-change-tracking/recursive2/recursive.txt
diff --git a/tests/auto/blackbox/testdata/wildcards-and-change-tracking/wildcards-and-change-tracking.qbs b/tests/auto/blackbox/testdata/wildcards-and-change-tracking/wildcards-and-change-tracking.qbs
new file mode 100644
index 000000000..0a889bab3
--- /dev/null
+++ b/tests/auto/blackbox/testdata/wildcards-and-change-tracking/wildcards-and-change-tracking.qbs
@@ -0,0 +1,16 @@
+Product {
+ Group {
+ name: "recursive"
+ files: "**/file.txt"
+ }
+ Group {
+ name: "directories"
+ prefix: "nonrecursive/"
+ files: "subdi?/file.txt"
+ }
+ Group {
+ prefix: "nonrecursive/empty/"
+ name: "no files"
+ files: "*.txt"
+ }
+}
diff --git a/tests/auto/blackbox/testdata/wildcards-and-rules/wildcards-and-rules.qbs b/tests/auto/blackbox/testdata/wildcards-and-rules/wildcards-and-rules.qbs
index f6662529d..fe6088943 100644
--- a/tests/auto/blackbox/testdata/wildcards-and-rules/wildcards-and-rules.qbs
+++ b/tests/auto/blackbox/testdata/wildcards-and-rules/wildcards-and-rules.qbs
@@ -22,7 +22,7 @@ Product {
}
prepare: {
var cmd = new JavaScriptCommand();
- cmd.description = "Creating output artifact";
+ cmd.description = "creating output artifact";
cmd.highlight = "codegen";
cmd.sourceCode = function() {
var file = new TextFile(output.filePath, TextFile.WriteOnly);
diff --git a/tests/auto/blackbox/testdata/wix/WiXInstallers.qbs b/tests/auto/blackbox/testdata/wix/WiXInstallers.qbs
deleted file mode 100644
index 07f61ba2c..000000000
--- a/tests/auto/blackbox/testdata/wix/WiXInstallers.qbs
+++ /dev/null
@@ -1,38 +0,0 @@
-import qbs.FileInfo
-
-Project {
- WindowsInstallerPackage {
- name: "QbsSetup"
- targetName: "qbs"
- files: ["QbsSetup.wxs", "ExampleScript.bat"]
- wix.defines: ["scriptName=ExampleScript.bat"]
- wix.extensions: ["WixBalExtension", "WixUIExtension"]
- qbs.targetPlatform: "windows"
-
- 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"]
- qbs.architecture: original || "x86"
- qbs.targetPlatform: "windows"
- }
-
- WindowsInstallerPackage {
- name: "RegressionBuster9000"
- files: ["QbsSetup.wxs", "Qt.wxs", "de.wxl"]
- wix.defines: ["scriptName=ExampleScript.bat"]
- wix.cultures: []
- qbs.architecture: original || "x86"
- qbs.targetPlatform: "windows"
- }
-}
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index faada314f..c62fdbca0 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -49,10 +49,11 @@
#include <QtCore/qjsonobject.h>
#include <QtCore/qjsonvalue.h>
#include <QtCore/qlocale.h>
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qsettings.h>
#include <QtCore/qtemporarydir.h>
#include <QtCore/qtemporaryfile.h>
+#include <QtCore/qversionnumber.h>
#include <algorithm>
#include <functional>
@@ -62,7 +63,6 @@
#define WAIT_FOR_NEW_TIMESTAMP() waitForNewTimestamp(testDataDir)
using qbs::Internal::HostOsInfo;
-using qbs::Profile;
class MacosTarHealer {
public:
@@ -138,7 +138,7 @@ QString TestBlackbox::findArchiver(const QString &fileName, int *status)
QString binary = findExecutable(QStringList(fileName));
if (binary.isEmpty()) {
const SettingsPtr s = settings();
- Profile p(profileName(), s.get());
+ qbs::Profile p(profileName(), s.get());
binary = findExecutable(p.value("archiver.command").toStringList());
}
return binary;
@@ -253,6 +253,13 @@ void TestBlackbox::systemIncludePaths()
QCOMPARE(runQbs(), 0);
}
+void TestBlackbox::distributionIncludePaths()
+{
+ const QString projectDir = testDataDir + "/distribution-include-paths";
+ QDir::setCurrent(projectDir);
+ QCOMPARE(runQbs(), 0);
+}
+
void TestBlackbox::tar()
{
if (HostOsInfo::hostOs() == HostOsInfo::HostOsWindows)
@@ -291,7 +298,7 @@ void TestBlackbox::textTemplate()
static QStringList sortedFileList(const QByteArray &ba)
{
- auto list = QString::fromUtf8(ba).split(QRegExp("[\r\n]"), QString::SkipEmptyParts);
+ auto list = QString::fromUtf8(ba).split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts);
std::sort(list.begin(), list.end());
return list;
}
@@ -356,6 +363,57 @@ TestBlackbox::TestBlackbox() : TestBlackboxBase (SRCDIR "/testdata", "blackbox")
{
}
+void TestBlackbox::allowedValues()
+{
+ QFETCH(QString, property);
+ QFETCH(QString, value);
+ QFETCH(QString, invalidValue);
+
+ QDir::setCurrent(testDataDir + "/allowed-values");
+ rmDirR(relativeBuildDir());
+
+ QbsRunParameters params;
+ if (!property.isEmpty() && !value.isEmpty()) {
+ params.arguments << QStringLiteral("%1:%2").arg(property, value);
+ }
+
+ params.expectFailure = !invalidValue.isEmpty();
+ QCOMPARE(runQbs(params) == 0, !params.expectFailure);
+ if (params.expectFailure) {
+ const auto errorString =
+ QStringLiteral("Value '%1' is not allowed for property").arg(invalidValue);
+ QVERIFY2(m_qbsStderr.contains(errorString.toUtf8()), m_qbsStderr.constData());
+ }
+}
+
+void TestBlackbox::allowedValues_data()
+{
+ QTest::addColumn<QString>("property");
+ QTest::addColumn<QString>("value");
+ QTest::addColumn<QString>("invalidValue");
+
+ QTest::newRow("default") << QString() << QString() << QString();
+
+ QTest::newRow("allowed (product, CLI)") << "products.p.prop" << "foo" << QString();
+ QTest::newRow("not allowed (product, CLI)") << "products.p.prop" << "bar" << "bar";
+ QTest::newRow("allowed (product, JS)") << "products.p.prop2" << "foo" << QString();
+ QTest::newRow("not allowed (product, JS)") << "products.p.prop2" << "bar" << "bar";
+
+ QTest::newRow("allowed single (module, CLI)") << "modules.a.prop" << "foo" << QString();
+ QTest::newRow("not allowed single (module, CLI)") << "modules.a.prop" << "baz" << "baz";
+ QTest::newRow("allowed mult (module, CLI)") << "modules.a.prop" << "foo,bar" << QString();
+ QTest::newRow("not allowed mult (module, CLI)") << "modules.a.prop" << "foo,baz" << "baz";
+
+ QTest::newRow("allowed single (module, JS)") << "modules.a.prop2" << "foo" << QString();
+ QTest::newRow("not allowed single (module, JS)") << "modules.a.prop2" << "baz" << "baz";
+ QTest::newRow("allowed mult (module, JS)") << "modules.a.prop2" << "foo,bar" << QString();
+ QTest::newRow("not allowed mult (module, JS)") << "modules.a.prop2" << "foo,baz" << "baz";
+
+ // undefined should always be allowed
+ QTest::newRow("undefined (product)") << "products.p.prop" << "undefined" << QString();
+ QTest::newRow("undefined (module)") << "modules.a.prop" << "undefined" << QString();
+}
+
void TestBlackbox::addFileTagToGeneratedArtifact()
{
QDir::setCurrent(testDataDir + "/add-filetag-to-generated-artifact");
@@ -640,7 +698,7 @@ void TestBlackbox::buildDirectories()
QDir::setCurrent(projectDir);
QCOMPARE(runQbs(), 0);
const QStringList outputLines
- = QString::fromLocal8Bit(m_qbsStdout.trimmed()).split('\n', QString::SkipEmptyParts);
+ = QString::fromLocal8Bit(m_qbsStdout.trimmed()).split('\n', Qt::SkipEmptyParts);
QVERIFY2(outputLines.contains(projectDir + '/' + relativeProductBuildDir("p1")),
m_qbsStdout.constData());
QVERIFY2(outputLines.contains(projectDir + '/' + relativeProductBuildDir("p2")),
@@ -713,6 +771,52 @@ void TestBlackbox::buildGraphVersions()
QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
}
+void TestBlackbox::buildVariantDefaults_data()
+{
+ QTest::addColumn<QString>("buildVariant");
+ QTest::newRow("default") << QString();
+ QTest::newRow("debug") << QStringLiteral("debug");
+ QTest::newRow("release") << QStringLiteral("release");
+ QTest::newRow("profiling") << QStringLiteral("profiling");
+}
+
+void TestBlackbox::buildVariantDefaults()
+{
+ QFETCH(QString, buildVariant);
+ QDir::setCurrent(testDataDir + "/build-variant-defaults");
+ QbsRunParameters params{QStringLiteral("resolve")};
+ if (!buildVariant.isEmpty())
+ params.arguments << ("modules.qbs.buildVariant:" + buildVariant);
+ QCOMPARE(runQbs(params), 0);
+}
+
+void TestBlackbox::capnproto()
+{
+ QFETCH(QString, projectFile);
+ QDir::setCurrent(testDataDir + "/capnproto");
+ rmDirR(relativeBuildDir());
+
+ QbsRunParameters params{QStringLiteral("resolve"), {QStringLiteral("-f"), projectFile}};
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+ if (m_qbsStdout.contains("capnproto is not present"))
+ QSKIP("capnproto is not present");
+
+ params.command.clear();
+ QCOMPARE(runQbs(params), 0);
+}
+
+void TestBlackbox::capnproto_data()
+{
+ QTest::addColumn<QString>("projectFile");
+
+ QTest::newRow("cpp") << QStringLiteral("capnproto_cpp.qbs");
+ QTest::newRow("cpp-pkgconfig") << QStringLiteral("capnproto_cpp_pkgconfig.qbs");
+ QTest::newRow("greeter cpp (grpc)") << QStringLiteral("greeter_cpp.qbs");
+ QTest::newRow("relative import") << QStringLiteral("capnproto_relative_import.qbs");
+ QTest::newRow("absolute import") << QStringLiteral("capnproto_absolute_import.qbs");
+}
+
void TestBlackbox::changedFiles_data()
{
QTest::addColumn<bool>("useChangedFilesForInitialBuild");
@@ -885,7 +989,7 @@ void TestBlackbox::dependenciesProperty()
QJsonArray cpp_dependencies = product2_cpp.value("dependencies").toArray();
QVERIFY(!cpp_dependencies.isEmpty());
int qbsCount = 0;
- for (const auto &dep : cpp_dependencies) {
+ for (const auto dep : cpp_dependencies) {
if (dep.toObject().value("name").toString() == "qbs")
++qbsCount;
}
@@ -897,7 +1001,7 @@ void TestBlackbox::dependenciesProperty()
REPLACE_IN_FILE(projectFile, "// Depends { name: 'newDependency' }",
"Depends { name: 'newDependency' }");
QCOMPARE(runQbs(), 0);
- QVERIFY2(m_qbsStdout.contains("generate product1.deps"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating product1.deps"), m_qbsStdout.constData());
readDepsOutput(depsFile, jsondoc);
dependencies = jsondoc.array();
QCOMPARE(dependencies.size(), 3);
@@ -908,7 +1012,7 @@ void TestBlackbox::dependenciesProperty()
REPLACE_IN_FILE(projectFile, "// Depends { name: 'product2' }", "Depends { name: 'product2' }");
QCOMPARE(runQbs(), 0);
QVERIFY2(m_qbsStdout.contains("Resolving"), m_qbsStdout.constData());
- QVERIFY2(!m_qbsStdout.contains("generate product1.deps"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("generating product1.deps"), m_qbsStdout.constData());
readDepsOutput(depsFile, jsondoc);
dependencies = jsondoc.array();
QCOMPARE(dependencies.size(), 3);
@@ -918,7 +1022,7 @@ void TestBlackbox::dependenciesProperty()
0);
QCOMPARE(runQbs(), 0);
QVERIFY2(!m_qbsStdout.contains("compiling product2.cpp"), m_qbsStdout.constData());
- QVERIFY2(m_qbsStdout.contains("generate product1.deps"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating product1.deps"), m_qbsStdout.constData());
readDepsOutput(depsFile, jsondoc);
dependencies = jsondoc.array();
QCOMPARE(dependencies.size(), 3);
@@ -930,7 +1034,7 @@ void TestBlackbox::dependenciesProperty()
"products.product2.cpp.defines:DIGEDAG"})), 0);
QCOMPARE(runQbs(), 0);
QVERIFY2(m_qbsStdout.contains("compiling product2.cpp"), m_qbsStdout.constData());
- QVERIFY2(m_qbsStdout.contains("generate product1.deps"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating product1.deps"), m_qbsStdout.constData());
readDepsOutput(depsFile, jsondoc);
dependencies = jsondoc.array();
QCOMPARE(dependencies.size(), 3);
@@ -942,40 +1046,77 @@ void TestBlackbox::dependenciesProperty()
QCOMPARE(product2_cpp_defines.first().toString(), QLatin1String("DIGEDAG"));
}
-void TestBlackbox::dependencyProfileMismatch()
+void TestBlackbox::dependencyScanningLoop()
{
- QDir::setCurrent(testDataDir + "/dependency-profile-mismatch");
- const SettingsPtr s = settings();
- qbs::Internal::TemporaryProfile depProfile("qbs_autotests_profileMismatch", s.get());
- 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;
- QVERIFY2(runQbs(params) != 0, m_qbsStderr.constData());
- QVERIFY2(m_qbsStderr.contains(profileName().toLocal8Bit())
- && m_qbsStderr.contains("', which does not exist"),
- m_qbsStderr.constData());
+ QDir::setCurrent(testDataDir + "/dependency-scanning-loop");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
}
void TestBlackbox::deprecatedProperty()
{
+ QFETCH(QString, version);
+ QFETCH(QString, mode);
+ QFETCH(bool, expiringWarning);
+ QFETCH(bool, expiringError);
+
QDir::setCurrent(testDataDir + "/deprecated-property");
QbsRunParameters params(QStringList("-q"));
params.expectFailure = true;
+ params.environment.insert("REMOVAL_VERSION", version);
+ if (!mode.isEmpty())
+ params.arguments << "--deprecation-warnings" << mode;
QVERIFY(runQbs(params) != 0);
m_qbsStderr = QDir::fromNativeSeparators(QString::fromLocal8Bit(m_qbsStderr)).toLocal8Bit();
- 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());
+ const bool hasExpiringWarning = m_qbsStderr.contains(QByteArray(
+ "deprecated-property.qbs:4:29 The property 'expiringProp' is "
+ "deprecated and will be removed in Qbs ") + version.toLocal8Bit());
+ QVERIFY2(expiringWarning == hasExpiringWarning, m_qbsStderr.constData());
+ const bool hasRemovedOutput = m_qbsStderr.contains(
+ "deprecated-property.qbs:5:28 The property 'veryOldProp' can no "
+ "longer be used. It was removed in Qbs 1.3.0.");
+ QVERIFY2(hasRemovedOutput == !expiringError, m_qbsStderr.constData());
QVERIFY2(m_qbsStderr.contains("Property 'forgottenProp' was scheduled for removal in version "
"1.8.0, but is still present."), m_qbsStderr.constData());
QVERIFY2(m_qbsStderr.contains("themodule/m.qbs:22:5 Removal version for 'forgottenProp' "
"specified here."), 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());
+ QVERIFY2(m_qbsStderr.count("Use newProp instead.") == 1
+ + int(expiringWarning && !expiringError), m_qbsStderr.constData());
+ QVERIFY2(m_qbsStderr.count("is deprecated") == int(expiringWarning), m_qbsStderr.constData());
+ QVERIFY2(m_qbsStderr.count("was removed") == int(!expiringError), m_qbsStderr.constData());
+}
+
+void TestBlackbox::deprecatedProperty_data()
+{
+ QTest::addColumn<QString>("version");
+ QTest::addColumn<QString>("mode");
+ QTest::addColumn<bool>("expiringWarning");
+ QTest::addColumn<bool>("expiringError");
+
+ const auto current = QVersionNumber::fromString(QBS_VERSION);
+ const QString next = QVersionNumber(current.majorVersion(), current.minorVersion() + 1)
+ .toString();
+ const QString nextNext = QVersionNumber(current.majorVersion(), current.minorVersion() + 2)
+ .toString();
+ const QString nextMajor = QVersionNumber(current.majorVersion() + 1).toString();
+
+ QTest::newRow("default/next") << next << QString() << true << false;
+ QTest::newRow("default/nextnext") << nextNext << QString() << false << false;
+ QTest::newRow("default/nextmajor") << nextMajor << QString() << true << false;
+ QTest::newRow("error/next") << next << QString("error") << true << true;
+ QTest::newRow("error/nextnext") << nextNext << QString("error") << true << true;
+ QTest::newRow("error/nextmajor") << nextMajor << QString("error") << true << true;
+ QTest::newRow("on/next") << next << QString("on") << true << false;
+ QTest::newRow("on/nextnext") << nextNext << QString("on") << true << false;
+ QTest::newRow("on/nextmajor") << nextMajor << QString("on") << true << false;
+ QTest::newRow("before-removal/next") << next << QString("before-removal") << true << false;
+ QTest::newRow("before-removal/nextnext") << nextNext << QString("before-removal")
+ << false << false;
+ QTest::newRow("before-removal/nextmajor") << nextMajor << QString("before-removal")
+ << true << false;
+ QTest::newRow("off/next") << next << QString("off") << false << false;
+ QTest::newRow("off/nextnext") << nextNext << QString("off") << false << false;
+ QTest::newRow("off/nextmajor") << nextMajor << QString("off") << false << false;
}
void TestBlackbox::disappearedProfile()
@@ -1008,16 +1149,16 @@ void TestBlackbox::disappearedProfile()
QbsRunParameters buildParams;
buildParams.profile.clear();
QCOMPARE(runQbs(buildParams), 0);
- QVERIFY2(m_qbsStdout.contains("Creating dummy1.txt with p1 from profile"),
+ QVERIFY2(m_qbsStdout.contains("creating dummy1.txt with p1 from profile"),
m_qbsStdout.constData());
- QVERIFY2(m_qbsStdout.contains("Creating dummy2.txt with p2 from profile"),
+ QVERIFY2(m_qbsStdout.contains("creating dummy2.txt with p2 from profile"),
m_qbsStdout.constData());
// Now we do use the "resolve" command, so the new property value is taken into account.
QCOMPARE(runQbs(resolveParams), 0);
QCOMPARE(runQbs(buildParams), 0);
- QVERIFY2(!m_qbsStdout.contains("Creating dummy1.txt"), m_qbsStdout.constData());
- QVERIFY2(m_qbsStdout.contains("Creating dummy2.txt with p2 new from profile"),
+ QVERIFY2(!m_qbsStdout.contains("creating dummy1.txt"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("creating dummy2.txt with p2 new from profile"),
m_qbsStdout.constData());
// Now we change the profile again without a "resolve" command. However, this time we
@@ -1030,8 +1171,8 @@ void TestBlackbox::disappearedProfile()
"property string p1: 'p1 from module'");
QCOMPARE(runQbs(buildParams), 0);
QVERIFY2(m_qbsStdout.contains("Resolving"), m_qbsStdout.constData());
- QVERIFY2(!m_qbsStdout.contains("Creating dummy1.txt"), m_qbsStdout.constData());
- QVERIFY2(!m_qbsStdout.contains("Creating dummy2.txt"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("creating dummy1.txt"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("creating dummy2.txt"), m_qbsStdout.constData());
// Now we run the "resolve" command without giving the necessary settings path to find
// the profile.
@@ -1055,10 +1196,12 @@ void TestBlackbox::discardUnusedData()
QVERIFY2(m_qbsStdout.contains("is Darwin"), m_qbsStdout.constData());
const bool isDarwin = m_qbsStdout.contains("is Darwin: true");
const QString output = QString::fromLocal8Bit(m_qbsStdout);
- QRegExp pattern(".*---(.*)---.*");
- QVERIFY2(pattern.exactMatch(output), qPrintable(output));
- QCOMPARE(pattern.captureCount(), 1);
- const QString nmPath = pattern.capturedTexts().at(1);
+ const QRegularExpression pattern(QRegularExpression::anchoredPattern(".*---(.*)---.*"),
+ QRegularExpression::DotMatchesEverythingOption);
+ const QRegularExpressionMatch match = pattern.match(output);
+ QVERIFY2(match.hasMatch(), qPrintable(output));
+ QCOMPARE(match.lastCapturedIndex(), 1);
+ const QString nmPath = match.captured(1);
if (!QFile::exists(nmPath))
QSKIP("Cannot check for symbol presence: No nm found.");
QProcess nm;
@@ -1082,6 +1225,13 @@ void TestBlackbox::discardUnusedData_data()
QTest::newRow("default") << QString() << true;
}
+void TestBlackbox::dotDotPcFile()
+{
+ QDir::setCurrent(testDataDir + "/dot-dot-pc-file");
+
+ QCOMPARE(runQbs(), 0);
+}
+
void TestBlackbox::driverLinkerFlags()
{
QDir::setCurrent(testDataDir + QLatin1String("/driver-linker-flags"));
@@ -1124,9 +1274,13 @@ void TestBlackbox::dynamicLibraryInModule()
QbsRunParameters libParams(QStringList({"-f", "thelibs.qbs", installRootSpec}));
libParams.buildDirectory = "libbuild";
QCOMPARE(runQbs(libParams), 0);
- QbsRunParameters appParams("run", QStringList({"-f", "theapp.qbs", installRootSpec}));
+ QbsRunParameters appParams("build", QStringList({"-f", "theapp.qbs", installRootSpec}));
appParams.buildDirectory = "appbuild";
QCOMPARE(runQbs(appParams), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+ appParams.command = "run";
+ QCOMPARE(runQbs(appParams), 0);
QVERIFY2(m_qbsStdout.contains("Hello from thelib"), m_qbsStdout.constData());
QVERIFY2(m_qbsStdout.contains("Hello from theotherlib"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("thirdlib"), m_qbsStdout.constData());
@@ -1232,9 +1386,9 @@ void TestBlackbox::variantSuffix_data()
std::make_pair(QString("unix"), QStringList())});
}
-static bool waitForProcessSuccess(QProcess &p)
+static bool waitForProcessSuccess(QProcess &p, int msecs = 30000)
{
- if (!p.waitForStarted() || !p.waitForFinished()) {
+ if (!p.waitForStarted(msecs) || !p.waitForFinished(msecs)) {
qDebug() << p.errorString();
return false;
}
@@ -1275,6 +1429,9 @@ void TestBlackbox::vcsGit()
return m_qbsStdout.mid(startIndex + 2, endIndex - startIndex - 2);
};
+ QCOMPARE(runQbs({"resolve", {"-f", repoDir.path()}}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
// Run without git metadata.
QbsRunParameters params("run", QStringList{"-f", repoDir.path()});
params.workingDir = repoDir.path() + "/..";
@@ -1340,6 +1497,9 @@ void TestBlackbox::vcsSubversion()
if (svnFilePath.isEmpty())
QSKIP("svn not found");
+ if (HostOsInfo::isWindowsHost() && qEnvironmentVariableIsSet("GITHUB_ACTIONS"))
+ QSKIP("Skip this test when running on GitHub");
+
// Set up repo.
QTemporaryDir repoDir;
QVERIFY(repoDir.isValid());
@@ -1363,6 +1523,8 @@ void TestBlackbox::vcsSubversion()
failParams.command = "run";
failParams.expectFailure = true;
const int retval = runQbs(failParams);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
if (m_qbsStderr.contains("svn too old"))
QSKIP("svn too old");
QCOMPARE(retval, 0);
@@ -1431,19 +1593,21 @@ void TestBlackbox::versionCheck_data()
void TestBlackbox::versionScript()
{
- const SettingsPtr s = settings();
- Profile buildProfile(profileName(), s.get());
- 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);
+ QCOMPARE(runQbs(QbsRunParameters("resolve", {"qbs.installRoot:" + QDir::currentPath()})), 0);
+ const bool isLinuxGcc = m_qbsStdout.contains("is gcc for Linux: true");
+ const bool isNotLinuxGcc = m_qbsStdout.contains("is gcc for Linux: false");
+ if (isNotLinuxGcc)
+ QSKIP("version script test only applies to Linux");
+ QVERIFY(isLinuxGcc);
+ QCOMPARE(runQbs(QbsRunParameters(QStringList("-q"))), 0);
const QString output = QString::fromLocal8Bit(m_qbsStderr);
- QRegExp pattern(".*---(.*)---.*");
- QVERIFY2(pattern.exactMatch(output), qPrintable(output));
+ const QRegularExpression pattern(QRegularExpression::anchoredPattern(".*---(.*)---.*"),
+ QRegularExpression::DotMatchesEverythingOption);
+ const QRegularExpressionMatch match = pattern.match(output);
+ QVERIFY2(match.hasMatch(), qPrintable(output));
QCOMPARE(pattern.captureCount(), 1);
- const QString nmPath = pattern.capturedTexts().at(1);
+ const QString nmPath = match.captured(1);
if (!QFile::exists(nmPath))
QSKIP("Cannot check for symbol presence: No nm found.");
QProcess nm;
@@ -1554,7 +1718,7 @@ void TestBlackbox::clean()
QVERIFY(!QFile(appExeFilePath).exists());
QVERIFY(!QFile(depObjectFilePath).exists());
QVERIFY(!QFile(depLibFilePath).exists());
- for (const QString &symLink : qAsConst(symlinks))
+ for (const QString &symLink : std::as_const(symlinks))
QVERIFY2(!symlinkExists(symLink), qPrintable(symLink));
// Remove all, with a forced re-resolve in between.
@@ -1573,7 +1737,7 @@ void TestBlackbox::clean()
QVERIFY(!QFile(appExeFilePath).exists());
QVERIFY(!QFile(depObjectFilePath).exists());
QVERIFY(!QFile(depLibFilePath).exists());
- for (const QString &symLink : qAsConst(symlinks))
+ for (const QString &symLink : std::as_const(symlinks))
QVERIFY2(!symlinkExists(symLink), qPrintable(symLink));
// Dry run.
@@ -1585,7 +1749,7 @@ void TestBlackbox::clean()
QVERIFY(regularFileExists(appExeFilePath));
QVERIFY(regularFileExists(depObjectFilePath));
QVERIFY(regularFileExists(depLibFilePath));
- for (const QString &symLink : qAsConst(symlinks))
+ for (const QString &symLink : std::as_const(symlinks))
QVERIFY2(symlinkExists(symLink), qPrintable(symLink));
// Product-wise, dependency only.
@@ -1599,7 +1763,7 @@ void TestBlackbox::clean()
QVERIFY(regularFileExists(appExeFilePath));
QVERIFY(!QFile(depObjectFilePath).exists());
QVERIFY(!QFile(depLibFilePath).exists());
- for (const QString &symLink : qAsConst(symlinks))
+ for (const QString &symLink : std::as_const(symlinks))
QVERIFY2(!symlinkExists(symLink), qPrintable(symLink));
// Product-wise, dependent product only.
@@ -1613,17 +1777,10 @@ void TestBlackbox::clean()
QVERIFY(!QFile(appExeFilePath).exists());
QVERIFY(regularFileExists(depObjectFilePath));
QVERIFY(regularFileExists(depLibFilePath));
- for (const QString &symLink : qAsConst(symlinks))
+ for (const QString &symLink : std::as_const(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");
@@ -1657,6 +1814,9 @@ void TestBlackbox::conditionalFileTagger()
void TestBlackbox::configure()
{
QDir::setCurrent(testDataDir + "/configure");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters params;
params.command = "run";
QCOMPARE(runQbs(params), 0);
@@ -1753,6 +1913,89 @@ void TestBlackbox::cxxLanguageVersion_data()
std::make_pair(QString("msvc-new"), QString("/std:"))});
}
+void TestBlackbox::conanfileProbe_data()
+{
+ QTest::addColumn<bool>("forceFailure");
+
+ QTest::newRow("success") << false;
+ QTest::newRow("failure") << true;
+}
+
+void TestBlackbox::conanfileProbe()
+{
+ QFETCH(bool, forceFailure);
+
+ QString executable = findExecutable({"conan"});
+ if (executable.isEmpty())
+ QSKIP("conan is not installed or not available in PATH.");
+
+ // We first build a dummy package testlib and use that as dependency
+ // in the testapp package.
+ QDir::setCurrent(testDataDir + "/conanfile-probe/testlib");
+ QStringList arguments { "create", "-o", "opt=True", "-s", "os=AIX", ".",
+ "testlib/1.2.3@qbs/testing" };
+ QProcess conan;
+ conan.start(executable, arguments);
+ QVERIFY(waitForProcessSuccess(conan));
+
+ QDir::setCurrent(testDataDir + "/conanfile-probe/testapp");
+ QCOMPARE(runQbs(QbsRunParameters("resolve",
+ {"--force-probe-execution",
+ QStringLiteral("projects.conanfile-probe-project.forceFailure:") +
+ (forceFailure ? "true" : "false")})), forceFailure ? 1 : 0);
+
+ QFile file(relativeBuildDir() + "/results.json");
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QVariantMap actualResults = QJsonDocument::fromJson(file.readAll()).toVariant().toMap();
+ const auto generatedFilesPath = actualResults.take("generatedFilesPath").toString();
+ // We want to make sure that generatedFilesPath is under the project directory,
+ // but we don't care about the actual name.
+ QVERIFY(directoryExists(relativeBuildDir() + "/genconan/"
+ + QFileInfo(generatedFilesPath).baseName()));
+
+ const QVariantMap expectedResults = {
+ { "json", "TESTLIB_ENV_VAL" },
+ { "dependencies", QVariantList{"testlib1", "testlib2"} },
+ };
+ QCOMPARE(actualResults, expectedResults);
+}
+
+void TestBlackbox::conflictingPropertyValues_data()
+{
+ QTest::addColumn<bool>("overrideInProduct");
+ QTest::newRow("don't override in product") << false;
+ QTest::newRow("override in product") << true;
+}
+
+void TestBlackbox::conflictingPropertyValues()
+{
+ QFETCH(bool, overrideInProduct);
+
+ QDir::setCurrent(testDataDir + "/conflicting-property-values");
+ if (overrideInProduct)
+ REPLACE_IN_FILE("conflicting-property-values.qbs", "// low.prop: name", "low.prop: name");
+ else
+ REPLACE_IN_FILE("conflicting-property-values.qbs", "low.prop: name", "// low.prop: name");
+ WAIT_FOR_NEW_TIMESTAMP();
+ QCOMPARE(runQbs(QString("resolve")), 0);
+ if (overrideInProduct) {
+ // Binding in product itself overrides everything else, module-level conflicts
+ // are irrelevant.
+ QVERIFY2(m_qbsStdout.contains("final prop value: toplevel"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
+ } else {
+ // Only the conflicts in the highest-level modules are reported, lower-level conflicts
+ // are irrelevant.
+ // prop2 does not cause a conflict, because the values are the same.
+ QVERIFY2(m_qbsStdout.contains("final prop value: highest"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStderr.contains("Conflicting scalar values for property 'prop'"),
+ m_qbsStderr.constData());
+ QVERIFY2(m_qbsStderr.count("values.qbs") == 2, m_qbsStderr.constData());
+ QVERIFY2(m_qbsStderr.contains("values.qbs:20:23"), m_qbsStderr.constData());
+ QVERIFY2(m_qbsStderr.contains("values.qbs:30:23"), m_qbsStderr.constData());
+ }
+}
+
void TestBlackbox::cpuFeatures()
{
QDir::setCurrent(testDataDir + "/cpu-features");
@@ -1784,6 +2027,13 @@ void TestBlackbox::cpuFeatures()
}
}
+void TestBlackbox::dateProperty()
+{
+ QDir::setCurrent(testDataDir + "/date-property");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("The stored date was 1999-12-31"), m_qbsStdout.constData());
+}
+
void TestBlackbox::renameDependency()
{
QDir::setCurrent(testDataDir + "/renameDependency");
@@ -1811,13 +2061,19 @@ void TestBlackbox::separateDebugInfo()
const bool isWindows = m_qbsStdout.contains("is windows: yes");
const bool isNotWindows = m_qbsStdout.contains("is windows: no");
QVERIFY(isWindows != isNotWindows);
+ const bool isMacos = m_qbsStdout.contains("is macos: yes");
+ const bool isNotMacos = m_qbsStdout.contains("is macos: no");
+ QVERIFY(isMacos != isNotMacos);
const bool isDarwin = m_qbsStdout.contains("is darwin: yes");
const bool isNotDarwin = m_qbsStdout.contains("is darwin: no");
QVERIFY(isDarwin != isNotDarwin);
+ const bool isGcc = m_qbsStdout.contains("is gcc: yes");
+ const bool isNotGcc = m_qbsStdout.contains("is gcc: no");
+ QVERIFY(isGcc != isNotGcc);
+ const bool isMsvc = m_qbsStdout.contains("is msvc: yes");
+ const bool isNotMsvc = m_qbsStdout.contains("is msvc: no");
+ QVERIFY(isMsvc != isNotMsvc);
- const SettingsPtr s = settings();
- Profile buildProfile(profileName(), s.get());
- QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList();
if (isDarwin) {
QVERIFY(directoryExists(relativeProductBuildDir("app1") + "/app1.app.dSYM"));
QVERIFY(regularFileExists(relativeProductBuildDir("app1")
@@ -1829,8 +2085,13 @@ void TestBlackbox::separateDebugInfo()
.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"));
+ if (isMacos) {
+ QVERIFY(regularFileExists(relativeProductBuildDir("app3")
+ + "/app3.app/Contents/MacOS/app3.dwarf"));
+ } else {
+ QVERIFY(regularFileExists(relativeProductBuildDir("app3")
+ + "/app3.app/app3.dwarf"));
+ }
QVERIFY(directoryExists(relativeProductBuildDir("app4") + "/app4.dSYM"));
QVERIFY(regularFileExists(relativeProductBuildDir("app4")
+ "/app4.dSYM/Contents/Info.plist"));
@@ -1850,8 +2111,13 @@ void TestBlackbox::separateDebugInfo()
.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"));
+ if (isMacos) {
+ QVERIFY(regularFileExists(relativeProductBuildDir("foo3")
+ + "/foo3.framework/Versions/A/foo3.dwarf"));
+ } else {
+ QVERIFY(regularFileExists(relativeProductBuildDir("foo3")
+ + "/foo3.framework/foo3.dwarf"));
+ }
QVERIFY(directoryExists(relativeProductBuildDir("foo4") + "/libfoo4.dylib.dSYM"));
QVERIFY(regularFileExists(relativeProductBuildDir("foo4")
+ "/libfoo4.dylib.dSYM/Contents/Info.plist"));
@@ -1871,8 +2137,13 @@ void TestBlackbox::separateDebugInfo()
.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"));
+ if (isMacos) {
+ QVERIFY(regularFileExists(relativeProductBuildDir("bar3")
+ + "/bar3.bundle/Contents/MacOS/bar3.dwarf"));
+ } else {
+ QVERIFY(regularFileExists(relativeProductBuildDir("bar3")
+ + "/bar3.bundle/bar3.dwarf"));
+ }
QVERIFY(directoryExists(relativeProductBuildDir("bar4") + "/bar4.bundle.dSYM"));
QVERIFY(regularFileExists(relativeProductBuildDir("bar4")
+ "/bar4.bundle.dSYM/Contents/Info.plist"));
@@ -1882,7 +2153,7 @@ void TestBlackbox::separateDebugInfo()
+ "/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")) {
+ } else if (isGcc) {
const QString exeSuffix = isWindows ? ".exe" : "";
const QString dllPrefix = isWindows ? "" : "lib";
const QString dllSuffix = isWindows ? ".dll" : ".so";
@@ -1896,7 +2167,7 @@ void TestBlackbox::separateDebugInfo()
+ '/' + dllPrefix + "bar1" + dllSuffix + ".debug"));
QVERIFY(!QFile::exists(relativeProductBuildDir("bar2")
+ '/' + dllPrefix + "bar2" + dllSuffix + ".debug"));
- } else if (toolchain.contains("msvc")) {
+ } else if (isMsvc) {
QVERIFY(QFile::exists(relativeProductBuildDir("app1") + "/app1.pdb"));
QVERIFY(QFile::exists(relativeProductBuildDir("foo1") + "/foo1.pdb"));
QVERIFY(QFile::exists(relativeProductBuildDir("bar1") + "/bar1.pdb"));
@@ -1915,6 +2186,9 @@ void TestBlackbox::trackAddFile()
QDir().mkdir("work");
ccp("before", "work");
QDir::setCurrent(testDataDir + "/trackAddFile/work");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
const QbsRunParameters runParams("run", QStringList{"-qp", "someapp"});
QCOMPARE(runQbs(runParams), 0);
@@ -1950,6 +2224,9 @@ void TestBlackbox::trackExternalProductChanges()
QVERIFY(!m_qbsStdout.contains("compiling jsFileChange.cpp"));
QVERIFY(!m_qbsStdout.contains("compiling fileExists.cpp"));
+ const bool isGcc = m_qbsStdout.contains("is gcc: true");
+ const bool isNotGcc = m_qbsStdout.contains("is gcc: false");
+
QbsRunParameters params;
params.environment.insert("QBS_TEST_PULL_IN_FILE_VIA_ENV", "1");
QCOMPARE(runQbs(params), 0);
@@ -1999,13 +2276,12 @@ void TestBlackbox::trackExternalProductChanges()
QVERIFY(!m_qbsStdout.contains("compiling jsFileChange.cpp"));
QVERIFY(m_qbsStdout.contains("compiling fileExists.cpp"));
+ if (isNotGcc)
+ QSKIP("The remainder of this test requires a GCC-like toolchain");
+ QVERIFY(isGcc);
+
rmDirR(relativeBuildDir());
- const SettingsPtr s = settings();
- const Profile profile(profileName(), s.get());
- 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 = QbsRunParameters::defaultEnvironment();
params.environment.insert("INCLUDE_PATH_TEST", "1");
params.expectFailure = true;
QVERIFY(runQbs(params) != 0);
@@ -2044,6 +2320,9 @@ void TestBlackbox::trackRemoveFile()
ccp("before", "work");
ccp("after", "work");
QDir::setCurrent(testDataDir + "/trackAddFile/work");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
const QbsRunParameters runParams("run", QStringList{"-qp", "someapp"});
QCOMPARE(runQbs(runParams), 0);
output = m_qbsStdout.split('\n');
@@ -2088,6 +2367,9 @@ void TestBlackbox::trackAddFileTag()
QDir().mkdir("work");
ccp("before", "work");
QDir::setCurrent(testDataDir + "/trackFileTags/work");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
const QbsRunParameters runParams("run", QStringList{"-qp", "someapp"});
QCOMPARE(runQbs(runParams), 0);
output = m_qbsStdout.split('\n');
@@ -2112,6 +2394,9 @@ void TestBlackbox::trackRemoveFileTag()
QDir().mkdir("work");
ccp("after", "work");
QDir::setCurrent(testDataDir + "/trackFileTags/work");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
const QbsRunParameters runParams("run", QStringList{"-qp", "someapp"});
QCOMPARE(runQbs(runParams), 0);
@@ -2260,8 +2545,8 @@ void TestBlackbox::referenceErrorInExport()
QbsRunParameters params;
params.expectFailure = true;
QVERIFY(runQbs(params) != 0);
- QVERIFY(m_qbsStderr.contains(
- "referenceErrorInExport.qbs:15:12 ReferenceError: Can't find variable: includePaths"));
+ QVERIFY2(m_qbsStderr.contains("referenceErrorInExport.qbs:5:27 'includePaths' is not defined"),
+ m_qbsStderr.constData());
}
void TestBlackbox::removeDuplicateLibraries_data()
@@ -2288,20 +2573,21 @@ void TestBlackbox::removeDuplicateLibraries()
void TestBlackbox::reproducibleBuild()
{
- const SettingsPtr s = settings();
- const Profile profile(profileName(), s.get());
- 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;
+ QbsRunParameters params("resolve");
params.arguments << QString("modules.cpp.enableReproducibleBuilds:")
+ (reproducible ? "true" : "false");
rmDirR(relativeBuildDir());
QCOMPARE(runQbs(params), 0);
+ const bool isGcc = m_qbsStdout.contains("is gcc: true");
+ const bool isNotGcc = m_qbsStdout.contains("is gcc: false");
+ if (isNotGcc)
+ QSKIP("reproducible builds only supported for gcc");
+ QVERIFY(isGcc);
+
+ QCOMPARE(runQbs(), 0);
QFile object(relativeProductBuildDir("the product") + '/' + inputDirHash(".") + '/'
+ objectFileName("file1.cpp", profileName()));
QVERIFY2(object.open(QIODevice::ReadOnly), qPrintable(object.fileName()));
@@ -2309,6 +2595,7 @@ void TestBlackbox::reproducibleBuild()
object.close();
QCOMPARE(runQbs(QbsRunParameters("clean")), 0);
QVERIFY(!object.exists());
+ params.command = "build";
QCOMPARE(runQbs(params), 0);
if (reproducible) {
QVERIFY(object.open(QIODevice::ReadOnly));
@@ -2329,6 +2616,9 @@ void TestBlackbox::reproducibleBuild_data()
void TestBlackbox::responseFiles()
{
QDir::setCurrent(testDataDir + "/response-files");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters params;
params.command = "install";
params.arguments << "--install-root" << "installed";
@@ -2367,6 +2657,32 @@ void TestBlackbox::retaggedOutputArtifact()
QVERIFY2(!QFile::exists(a3), qPrintable(a3));
}
+void TestBlackbox::rpathlinkDeduplication()
+{
+ QDir::setCurrent(testDataDir + "/rpathlink-deduplication");
+ QbsRunParameters resolveParams{"resolve"};
+ QCOMPARE(runQbs(resolveParams), 0);
+ const bool useRPathLink = m_qbsStdout.contains("useRPathLink: true");
+ const bool dontUseRPathLink = m_qbsStdout.contains("useRPathLink: false");
+ QVERIFY2(useRPathLink || dontUseRPathLink, m_qbsStdout);
+ if (dontUseRPathLink)
+ QSKIP("Only applies to toolchains that support rPathLink");
+ const QString output = QString::fromLocal8Bit(m_qbsStdout);
+ const QRegularExpression pattern(QRegularExpression::anchoredPattern(".*===(.*)===.*"),
+ QRegularExpression::DotMatchesEverythingOption);
+ const QRegularExpressionMatch match = pattern.match(output);
+ QVERIFY2(match.hasMatch(), qPrintable(output));
+ QCOMPARE(pattern.captureCount(), 1);
+ const QString linkFlag = match.captured(1);
+
+ QbsRunParameters buildParams;
+ buildParams.arguments = QStringList({"--command-echo-mode", "command-line"});
+ QCOMPARE(runQbs(buildParams), 0);
+ // private DynamicLibraryA is a dependency for 2 other libs but should only appear once
+ const auto libDir = QFileInfo(relativeProductBuildDir("DynamicLibraryA")).absoluteFilePath();
+ QCOMPARE(m_qbsStdout.count((linkFlag + libDir).toUtf8()), 1);
+}
+
void TestBlackbox::ruleConditions()
{
QDir::setCurrent(testDataDir + "/ruleConditions");
@@ -2444,11 +2760,60 @@ void TestBlackbox::ruleWithNonRequiredInputs()
output = outFile.readAll();
QCOMPARE(output, QByteArray("(a.inp,b.inp,c.inp,)"));
QCOMPARE(runQbs(), 0);
- QVERIFY2(!m_qbsStdout.contains("Generating"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("generating"), m_qbsStdout.constData());
WAIT_FOR_NEW_TIMESTAMP();
touch("a.inp");
QCOMPARE(runQbs(), 0);
- QVERIFY2(m_qbsStdout.contains("Generating"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating"), m_qbsStdout.constData());
+}
+
+void TestBlackbox::runMultiplexed()
+{
+ QDir::setCurrent(testDataDir + "/run-multiplexed");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+
+ QbsRunParameters params("run");
+ params.expectFailure = true;
+ QVERIFY(runQbs(params) != 0);
+ params.arguments = QStringList{"-p", "app"};
+ QVERIFY(runQbs(params) != 0);
+ params.expectFailure = false;
+ params.arguments.last() = "app {\"buildVariant\":\"debug\"}";
+ QCOMPARE(runQbs(params), 0);
+ params.arguments.last() = "app {\"buildVariant\":\"release\"}";
+ QCOMPARE(runQbs(params), 0);
+}
+
+void TestBlackbox::sanitizer_data()
+{
+ QTest::addColumn<QString>("sanitizer");
+ QTest::newRow("none") << QString();
+ QTest::newRow("address") << QStringLiteral("address");
+ QTest::newRow("undefined") << QStringLiteral("undefined");
+ QTest::newRow("thread") << QStringLiteral("thread");
+}
+
+void TestBlackbox::sanitizer()
+{
+ QFETCH(QString, sanitizer);
+ QDir::setCurrent(testDataDir + "/sanitizer");
+ rmDirR(relativeBuildDir());
+ QbsRunParameters params("build", {"--command-echo-mode", "command-line"});
+ if (!sanitizer.isEmpty()) {
+ params.arguments.append(
+ {QStringLiteral("products.sanitizer.sanitizer:\"") + sanitizer + "\""});
+ }
+ QCOMPARE(runQbs(params), 0);
+ if (m_qbsStdout.contains(QByteArrayLiteral("Compiler does not support sanitizer")))
+ QSKIP("Compiler does not support the specified sanitizer");
+ if (!sanitizer.isEmpty()) {
+ QVERIFY2(m_qbsStdout.contains(QByteArrayLiteral("fsanitize=") + sanitizer.toLatin1()),
+ qPrintable(m_qbsStdout));
+ } else {
+ QVERIFY2(!m_qbsStdout.contains(QByteArrayLiteral("fsanitize=")), qPrintable(m_qbsStdout));
+ }
}
void TestBlackbox::scannerItem()
@@ -2469,6 +2834,47 @@ void TestBlackbox::scannerItem()
QVERIFY2(m_qbsStdout.contains("handling file2.in"), m_qbsStdout.constData());
}
+void TestBlackbox::scanResultInOtherProduct()
+{
+ QDir::setCurrent(testDataDir + "/scan-result-in-other-product");
+ QCOMPARE(runQbs(QStringList("-vv")), 0);
+ QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating text file"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStderr.contains("The file dependency might get lost during change tracking"),
+ m_qbsStderr.constData());
+ WAIT_FOR_NEW_TIMESTAMP();
+ REPLACE_IN_FILE("other/other.qbs", "blubb", "blubb2");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(!m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating text file"), m_qbsStdout.constData());
+ WAIT_FOR_NEW_TIMESTAMP();
+ touch("lib/lib.h");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("generating text file"), m_qbsStdout.constData());
+}
+
+void TestBlackbox::scanResultInNonDependency()
+{
+ QDir::setCurrent(testDataDir + "/scan-result-in-non-dependency");
+ QCOMPARE(runQbs(QStringList("-vv")), 0);
+ QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating text file"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStderr.contains("The file dependency might get lost during change tracking"),
+ m_qbsStderr.constData());
+ WAIT_FOR_NEW_TIMESTAMP();
+ REPLACE_IN_FILE("other/other.qbs", "blubb", "blubb2");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(!m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("generating text file"), m_qbsStdout.constData());
+ WAIT_FOR_NEW_TIMESTAMP();
+ touch("lib/lib.h");
+ QCOMPARE(runQbs(), 0);
+ QEXPECT_FAIL("", "QBS-1532", Continue);
+ QVERIFY2(m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("generating text file"), m_qbsStdout.constData());
+}
+
void TestBlackbox::setupBuildEnvironment()
{
QDir::setCurrent(testDataDir + "/setup-build-environment");
@@ -2486,6 +2892,8 @@ void TestBlackbox::setupRunEnvironment()
{
QDir::setCurrent(testDataDir + "/setup-run-environment");
QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters failParams("run", QStringList({"--setup-run-env-config",
"ignore-lib-dependencies"}));
failParams.expectFailure = true;
@@ -2760,9 +3168,11 @@ void TestBlackbox::pathProbe_data()
QTest::newRow("mult-files-mult-variants") << QString("mult-files-mult-variants.qbs") << true;
QTest::newRow("single-file-suffixes") << QString("single-file-suffixes.qbs") << true;
QTest::newRow("mult-files-suffixes") << QString("mult-files-suffixes.qbs") << true;
+ QTest::newRow("mult-files-common-suffixes") << QString("mult-files-common-suffixes.qbs") << true;
QTest::newRow("mult-files-mult-suffixes") << QString("mult-files-mult-suffixes.qbs") << true;
QTest::newRow("name-filter") << QString("name-filter.qbs") << true;
QTest::newRow("candidate-filter") << QString("candidate-filter.qbs") << true;
+ QTest::newRow("environment-paths") << QString("environment-paths.qbs") << true;
}
void TestBlackbox::pathProbe()
@@ -2774,23 +3184,41 @@ void TestBlackbox::pathProbe()
QbsRunParameters buildParams("build", QStringList{"-f", projectFile});
buildParams.expectFailure = !successExpected;
+ buildParams.environment.insert("SEARCH_PATH", "usr/bin");
QCOMPARE(runQbs(buildParams) == 0, successExpected);
+ if (!successExpected)
+ QVERIFY2(m_qbsStderr.contains("Probe failed to find files"), m_qbsStderr);
+}
+
+void TestBlackbox::pathListInProbe()
+{
+ QDir::setCurrent(testDataDir + "/path-list-in-probe");
+ QCOMPARE(runQbs(), 0);
}
void TestBlackbox::pchChangeTracking()
{
QDir::setCurrent(testDataDir + "/pch-change-tracking");
- QCOMPARE(runQbs(), 0);
+ bool success = runQbs() == 0;
+ if (!success && m_qbsStderr.contains("mingw32_gt_pch_use_address"))
+ QSKIP("https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91440");
+ QVERIFY(success);
QVERIFY(m_qbsStdout.contains("precompiling pch.h (cpp)"));
WAIT_FOR_NEW_TIMESTAMP();
touch("header1.h");
- QCOMPARE(runQbs(), 0);
+ success = runQbs() == 0;
+ if (!success && m_qbsStderr.contains("mingw32_gt_pch_use_address"))
+ QSKIP("https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91440");
+ QVERIFY(success);
QVERIFY(m_qbsStdout.contains("precompiling pch.h (cpp)"));
QVERIFY(m_qbsStdout.contains("compiling header2.cpp"));
QVERIFY(m_qbsStdout.contains("compiling main.cpp"));
WAIT_FOR_NEW_TIMESTAMP();
touch("header2.h");
- QCOMPARE(runQbs(), 0);
+ success = runQbs() == 0;
+ if (!success && m_qbsStderr.contains("mingw32_gt_pch_use_address"))
+ QSKIP("https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91440");
+ QVERIFY(success);
QVERIFY2(!m_qbsStdout.contains("precompiling pch.h (cpp)"), m_qbsStdout.constData());
}
@@ -2920,13 +3348,19 @@ void TestBlackbox::pluginDependency()
void TestBlackbox::precompiledAndPrefixHeaders()
{
QDir::setCurrent(testDataDir + "/precompiled-and-prefix-headers");
- QCOMPARE(runQbs(), 0);
+ const bool success = runQbs() == 0;
+ if (!success && m_qbsStderr.contains("mingw32_gt_pch_use_address"))
+ QSKIP("https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91440");
+ QVERIFY(success);
}
void TestBlackbox::precompiledHeaderAndRedefine()
{
QDir::setCurrent(testDataDir + "/precompiled-headers-and-redefine");
- QCOMPARE(runQbs(), 0);
+ const bool success = runQbs() == 0;
+ if (!success && m_qbsStderr.contains("mingw32_gt_pch_use_address"))
+ QSKIP("https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91440");
+ QVERIFY(success);
}
void TestBlackbox::preventFloatingPointValues()
@@ -3031,6 +3465,9 @@ void TestBlackbox::probeProperties()
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());
+ QVERIFY2(m_qbsStdout.contains("probe3.fileName=tool"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("probe3.path=" + dir + "/bin"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("probe3.filePath=" + dir + "/bin/tool"), m_qbsStdout.constData());
}
void TestBlackbox::probesAndShadowProducts()
@@ -3053,7 +3490,7 @@ void TestBlackbox::probeInExportedModule()
<< QStringLiteral("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());
+ QVERIFY2(m_qbsStdout.contains("listProp: myother,my"), m_qbsStdout.constData());
}
void TestBlackbox::probesAndArrayProperties()
@@ -3075,13 +3512,6 @@ void TestBlackbox::productProperties()
QVERIFY(regularFileExists(relativeExecutableFilePath("blubb_user")));
}
-void TestBlackbox::propertyAssignmentOnNonPresentModule()
-{
- QDir::setCurrent(testDataDir + "/property-assignment-on-non-present-module");
- QCOMPARE(runQbs(), 0);
- QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
-}
-
void TestBlackbox::propertyAssignmentInFailedModule()
{
QDir::setCurrent(testDataDir + "/property-assignment-in-failed-module");
@@ -3091,7 +3521,7 @@ void TestBlackbox::propertyAssignmentInFailedModule()
QVERIFY(runQbs(failParams) != 0);
QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("modules.m.doFail:true"))), 0);
QVERIFY2(m_qbsStdout.contains("Resolving"), m_qbsStdout.constData());
- QEXPECT_FAIL(0, "circular dependency between module merging and validation", Continue);
+ failParams.expectFailure = false;
QCOMPARE(runQbs(failParams), 0);
}
@@ -3109,9 +3539,9 @@ void TestBlackbox::propertyChanges()
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("making output from input"));
QVERIFY(m_qbsStdout.contains("default value"));
- QVERIFY(m_qbsStdout.contains("Making output from other output"));
+ 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"));
@@ -3125,8 +3555,8 @@ void TestBlackbox::propertyChanges()
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"));
+ 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();
@@ -3138,8 +3568,8 @@ void TestBlackbox::propertyChanges()
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"));
+ 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();
@@ -3151,8 +3581,8 @@ void TestBlackbox::propertyChanges()
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"));
+ 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();
@@ -3162,8 +3592,8 @@ void TestBlackbox::propertyChanges()
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"));
+ 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.command = "resolve";
@@ -3181,8 +3611,8 @@ void TestBlackbox::propertyChanges()
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"));
+ 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");
@@ -3197,8 +3627,8 @@ void TestBlackbox::propertyChanges()
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"));
+ QVERIFY(!m_qbsStdout.contains("making output from input"));
+ QVERIFY(!m_qbsStdout.contains("making output from other output"));
params.environment.insert("QBS_BLACKBOX_DEFINE", "newvalue");
QCOMPARE(runQbs(params), 0);
QCOMPARE(runQbs(), 0);
@@ -3213,8 +3643,8 @@ void TestBlackbox::propertyChanges()
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"));
+ 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";
@@ -3234,8 +3664,8 @@ void TestBlackbox::propertyChanges()
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"));
+ 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();
@@ -3247,8 +3677,8 @@ void TestBlackbox::propertyChanges()
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"));
+ 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();
@@ -3259,8 +3689,8 @@ void TestBlackbox::propertyChanges()
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("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();
@@ -3274,8 +3704,8 @@ void TestBlackbox::propertyChanges()
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("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();
@@ -3289,8 +3719,8 @@ void TestBlackbox::propertyChanges()
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("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();
@@ -3304,8 +3734,8 @@ void TestBlackbox::propertyChanges()
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("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.
@@ -3318,22 +3748,31 @@ void TestBlackbox::propertyChanges()
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("making output from input"));
+ QVERIFY(m_qbsStdout.contains("making output from other output"));
+}
+
+void TestBlackbox::propertyEvaluationContext()
+{
+ const QString testDir = testDataDir + "/property-evaluation-context";
+ QDir::setCurrent(testDir);
+ QCOMPARE(runQbs(), 0);
+ QCOMPARE(m_qbsStdout.count("base.productInBase evaluated in: myapp"), 1);
+ QCOMPARE(m_qbsStdout.count("base.productInTop evaluated in: myapp"), 1);
+ QCOMPARE(m_qbsStdout.count("top.productInExport evaluated in: mylib"), 1);
+ QCOMPARE(m_qbsStdout.count("top.productInTop evaluated in: myapp"), 1);
}
void TestBlackbox::qtBug51237()
{
- const QString profileName = "profile-qtBug51237";
- const QString propertyName = "mymodule.theProperty";
- {
- const SettingsPtr s = settings();
- Profile profile(profileName, s.get());
- profile.setValue(propertyName, QStringList());
- }
+ const SettingsPtr s = settings();
+ qbs::Internal::TemporaryProfile profile("qbs_autotests_qtBug51237", s.get());
+ profile.p.setValue("mymodule.theProperty", QStringList());
+ s->sync();
+
QDir::setCurrent(testDataDir + "/QTBUG-51237");
QbsRunParameters params;
- params.profile = profileName;
+ params.profile = profile.p.name();
QCOMPARE(runQbs(params), 0);
}
@@ -3406,6 +3845,42 @@ void TestBlackbox::dynamicRuleOutputs()
QVERIFY(!QFile::exists(sourceFile2));
}
+void TestBlackbox::emptyProfile()
+{
+ QDir::setCurrent(testDataDir + "/empty-profile");
+
+ const SettingsPtr s = settings();
+ const qbs::Profile buildProfile(profileName(), s.get());
+ bool isMsvc = false;
+ auto toolchainType = buildProfile.value(QStringLiteral("qbs.toolchainType")).toString();
+ QbsRunParameters params;
+ params.profile = "none";
+
+ if (toolchainType.isEmpty()) {
+ const auto toolchain = buildProfile.value(QStringLiteral("qbs.toolchain")).toStringList();
+ if (!toolchain.isEmpty())
+ toolchainType = toolchain.first();
+ }
+ if (!toolchainType.isEmpty()) {
+ params.arguments = QStringList{QStringLiteral("qbs.toolchainType:") + toolchainType};
+ isMsvc = toolchainType == "msvc" || toolchainType == "clang-cl";
+ }
+
+ if (!isMsvc) {
+ const auto tcPath =
+ QDir::toNativeSeparators(
+ buildProfile.value(QStringLiteral("cpp.toolchainInstallPath")).toString());
+ auto paths = params.environment.value(QStringLiteral("PATH"))
+ .split(HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts);
+ if (!tcPath.isEmpty() && !paths.contains(tcPath)) {
+ paths.prepend(tcPath);
+ params.environment.insert(
+ QStringLiteral("PATH"), paths.join(HostOsInfo::pathListSeparator()));
+ }
+ }
+ QCOMPARE(runQbs(params), 0);
+}
+
void TestBlackbox::erroneousFiles_data()
{
QTest::addColumn<QString>("errorMessage");
@@ -3433,7 +3908,7 @@ void TestBlackbox::erroneousFiles()
params.expectFailure = true;
QVERIFY(runQbs(params) != 0);
QString err = QString::fromLocal8Bit(m_qbsStderr);
- if (!err.contains(QRegExp(errorMessage))) {
+ if (!err.contains(QRegularExpression(errorMessage))) {
qDebug().noquote() << "Output: " << err;
qDebug().noquote() << "Expected: " << errorMessage;
QFAIL("Unexpected error message.");
@@ -3496,13 +3971,16 @@ void TestBlackbox::errorInfo()
void TestBlackbox::escapedLinkerFlags()
{
- const SettingsPtr s = settings();
- const Profile buildProfile(profileName(), s.get());
- const QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList();
- if (!toolchain.contains("gcc") || targetOs() == HostOsInfo::HostOsMacos)
- QSKIP("escaped linker flags test only applies with gcc and GNU ld");
QDir::setCurrent(testDataDir + "/escaped-linker-flags");
- QbsRunParameters params(QStringList("products.app.escapeLinkerFlags:false"));
+ QbsRunParameters params("resolve", QStringList("products.app.escapeLinkerFlags:false"));
+ QCOMPARE(runQbs(params), 0);
+ const bool isGcc = m_qbsStdout.contains("is gcc: true");
+ const bool isNotGcc = m_qbsStdout.contains("is gcc: false");
+ if (isNotGcc)
+ QSKIP("escaped linker flags test only applies on plain unix with gcc and GNU ld");
+ QVERIFY(isGcc);
+
+ params.command = "build";
QCOMPARE(runQbs(params), 0);
params.command = "resolve";
params.arguments = QStringList() << "products.app.escapeLinkerFlags:true";
@@ -3559,26 +4037,24 @@ void TestBlackbox::exportedPropertyInDisabledProduct_data()
void TestBlackbox::systemRunPaths()
{
- const SettingsPtr s = settings();
- const Profile buildProfile(profileName(), s.get());
- 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);
rmDirR(relativeBuildDir());
- QbsRunParameters params;
+ QbsRunParameters params("resolve");
params.arguments << QString("project.setRunPaths:") + (setRunPaths ? "true" : "false");
QCOMPARE(runQbs(params), 0);
+ const bool isUnix = m_qbsStdout.contains("is unix: true");
+ const bool isNotUnix = m_qbsStdout.contains("is unix: false");
+ if (isNotUnix)
+ QSKIP("only applies on Unix");
+ QVERIFY(isUnix);
+
+ params.command = "build";
+ QCOMPARE(runQbs(params), 0);
QProcess ldd;
ldd.start(lddFilePath, QStringList() << relativeExecutableFilePath("app"));
QVERIFY2(ldd.waitForStarted(), qPrintable(ldd.errorString()));
@@ -3615,7 +4091,7 @@ void TestBlackbox::exportRule()
params.expectFailure = false;
QCOMPARE(runQbs(params), 0);
QCOMPARE(runQbs(), 0);
- QVERIFY2(m_qbsStdout.contains("Creating C++ source file"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("creating C++ source file"), m_qbsStdout.constData());
QVERIFY2(m_qbsStdout.contains("compiling myapp.cpp"), m_qbsStdout.constData());
}
@@ -3629,12 +4105,88 @@ void TestBlackbox::exportToOutsideSearchPath()
m_qbsStderr.constData());
}
+void TestBlackbox::exportsCMake()
+{
+ QFETCH(QStringList, arguments);
+
+ QDir::setCurrent(testDataDir + "/exports-cmake");
+ rmDirR(relativeBuildDir());
+ QbsRunParameters findCMakeParams("resolve", {"-f", "find-cmake.qbs"});
+ QCOMPARE(runQbs(findCMakeParams), 0);
+ const QString output = QString::fromLocal8Bit(m_qbsStdout);
+ const QRegularExpression pattern(
+ QRegularExpression::anchoredPattern(".*---(.*)---.*"),
+ QRegularExpression::DotMatchesEverythingOption);
+ const QRegularExpressionMatch match = pattern.match(output);
+ QVERIFY2(match.hasMatch(), qPrintable(output));
+ QCOMPARE(pattern.captureCount(), 1);
+ const QString jsonString = match.captured(1);
+ const QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonString.toUtf8());
+ const QJsonObject jsonData = jsonDoc.object();
+
+ rmDirR(relativeBuildDir());
+ const QStringList exporterArgs{"-f", "exports-cmake.qbs"};
+ QbsRunParameters exporterRunParams("build", exporterArgs);
+ exporterRunParams.arguments << arguments;
+ QCOMPARE(runQbs(exporterRunParams), 0);
+
+ if (!jsonData.value(u"cmakeFound").toBool()) {
+ QSKIP("cmake is not installed");
+ return;
+ }
+
+ if (jsonData.value(u"crossCompiling").toBool()) {
+ QSKIP("test is not applicable with cross-compile toolchains");
+ return;
+ }
+
+ const auto cmakeFilePath = jsonData.value(u"cmakeFilePath").toString();
+ QVERIFY(!cmakeFilePath.isEmpty());
+
+ const auto generator = jsonData.value(u"generator").toString();
+ if (generator.isEmpty()) {
+ QSKIP("cannot detect cmake generator");
+ return;
+ }
+
+ QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+ const auto buildEnv = jsonData.value(u"buildEnv").toObject();
+ for (auto it = buildEnv.begin(), end = buildEnv.end(); it != end; ++it) {
+ env.insert(it.key(), it.value().toString());
+ }
+
+ const auto installPrefix = jsonData.value(u"installPrefix").toString();
+ const auto cmakePrefixPath = QFileInfo(relativeBuildDir()).absoluteFilePath() + "/install-root/"
+ + installPrefix + "/lib/cmake";
+ const auto sourceDirectory = testDataDir + "/exports-cmake/cmake";
+ QProcess configure;
+ configure.setProcessEnvironment(env);
+ configure.setWorkingDirectory(sourceDirectory);
+ configure.start(
+ cmakeFilePath, {".", "-DCMAKE_PREFIX_PATH=" + cmakePrefixPath, "-G" + generator});
+ QVERIFY(waitForProcessSuccess(configure, 120000));
+
+ QProcess build;
+ build.setProcessEnvironment(env);
+ build.setWorkingDirectory(sourceDirectory);
+ build.start(cmakeFilePath, QStringList{"--build", "."});
+ QVERIFY(waitForProcessSuccess(build));
+}
+
+void TestBlackbox::exportsCMake_data()
+{
+ QTest::addColumn<QStringList>("arguments");
+ QTest::newRow("dynamic lib") << QStringList("project.isStatic: false");
+ QTest::newRow("static lib") << QStringList("project.isStatic: true");
+ QTest::newRow("framework") << QStringList("project.isBundle: true");
+}
+
void TestBlackbox::exportsPkgconfig()
{
QDir::setCurrent(testDataDir + "/exports-pkgconfig");
QCOMPARE(runQbs(), 0);
- QVERIFY2(m_qbsStdout.contains("Creating TheFirstLib.pc"), m_qbsStdout.constData());
- QVERIFY2(m_qbsStdout.contains("Creating TheSecondLib.pc"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("creating TheFirstLib.pc"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("creating TheSecondLib.pc"), m_qbsStdout.constData());
QFile sourcePcFile(HostOsInfo::isWindowsHost() ? "TheFirstLib_windows.pc" : "TheFirstLib.pc");
QString generatedPcFilePath = relativeProductBuildDir("TheFirstLib") + "/TheFirstLib.pc";
QFile generatedPcFile(generatedPcFilePath);
@@ -3649,14 +4201,17 @@ void TestBlackbox::exportsPkgconfig()
touch("firstlib.cpp");
QCOMPARE(runQbs(), 0);
QVERIFY2(m_qbsStdout.contains("linking"), m_qbsStdout.constData());
- QVERIFY2(!m_qbsStdout.contains("Creating TheFirstLib.pc"), m_qbsStdout.constData());
- QVERIFY2(!m_qbsStdout.contains("Creating TheSecondLib.pc"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("creating TheFirstLib.pc"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("creating TheSecondLib.pc"), m_qbsStdout.constData());
}
void TestBlackbox::exportsQbs()
{
QDir::setCurrent(testDataDir + "/exports-qbs");
+ QCOMPARE(runQbs({"resolve", {"-f", "exports-qbs.qbs"}}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
// First we build exportable products and use them (as products) inside
// the original project.
QCOMPARE(runQbs(QStringList{"-f", "exports-qbs.qbs", "--command-echo-mode", "command-line"}),
@@ -3679,11 +4234,12 @@ void TestBlackbox::exportsQbs()
// Trying to build with an unsupported build variant must fail.
paramsExternalBuild.arguments = QStringList{"-f", "consumer.qbs",
- "modules.qbs.buildVariant:unknown"};
+ "modules.qbs.buildVariant:profiling"};
paramsExternalBuild.buildDirectory = QDir::currentPath() + "/external-consumer-profile";
paramsExternalBuild.expectFailure = true;
QVERIFY(runQbs(paramsExternalBuild) != 0);
- QVERIFY2(m_qbsStderr.contains("MyLib could not be loaded"), m_qbsStderr.constData());
+ QVERIFY2(m_qbsStderr.contains("Dependency 'MyLib' not found for product 'consumer'"),
+ m_qbsStderr.constData());
// Removing the condition from the generated module leaves us with two conflicting
// candidates.
@@ -3697,19 +4253,19 @@ void TestBlackbox::exportsQbs()
// Change tracking for accesses to product.exports (negative).
QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList{"-f", "exports-qbs.qbs"})), 0);
QCOMPARE(runQbs(), 0);
- QVERIFY2(m_qbsStdout.contains("Creating MyTool.qbs"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("creating MyTool.qbs"), m_qbsStdout.constData());
WAIT_FOR_NEW_TIMESTAMP();
touch("exports-qbs.qbs");
QCOMPARE(runQbs(QStringList({"-p", "MyTool"})), 0);
- QVERIFY2(!m_qbsStdout.contains("Creating MyTool.qbs"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("creating MyTool.qbs"), m_qbsStdout.constData());
// Rebuilding the target binary should not cause recreating the module file.
WAIT_FOR_NEW_TIMESTAMP();
touch("mylib.cpp");
QCOMPARE(runQbs(), 0);
QVERIFY2(m_qbsStdout.count("linking") >= 2, m_qbsStdout.constData());
- QVERIFY2(!m_qbsStdout.contains("Creating MyLib"), m_qbsStdout.constData());
- QVERIFY2(!m_qbsStdout.contains("Creating MyTool.qbs"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("creating MyLib"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("creating MyTool.qbs"), m_qbsStdout.constData());
// Changing a setting that influences the name of a target artifact should cause
// recreating the module file.
@@ -3718,14 +4274,14 @@ void TestBlackbox::exportsQbs()
QCOMPARE(runQbs(resolveParams), 0);
QCOMPARE(runQbs(), 0);
QVERIFY2(m_qbsStdout.count("linking") >= 2, m_qbsStdout.constData());
- QVERIFY2(m_qbsStdout.count("Creating MyLib") == 2, m_qbsStdout.constData());
- QVERIFY2(!m_qbsStdout.contains("Creating MyTool.qbs"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.count("creating MyLib") == 2, m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("creating MyTool.qbs"), m_qbsStdout.constData());
// Change tracking for accesses to product.exports (positive).
WAIT_FOR_NEW_TIMESTAMP();
- REPLACE_IN_FILE("tool.qbs", "product.toolTags", "[]");
+ REPLACE_IN_FILE("tool.qbs", "exportingProduct.toolTags", "[]");
QCOMPARE(runQbs(QStringList({"-p", "MyTool"})), 0);
- QVERIFY2(m_qbsStdout.contains("Creating MyTool.qbs"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("creating MyTool.qbs"), m_qbsStdout.constData());
}
void TestBlackbox::externalLibs()
@@ -3787,6 +4343,40 @@ void TestBlackbox::fileTagsFilterMerging()
QVERIFY2(QFile::exists(otherOutput), qPrintable(otherOutput));
}
+void TestBlackbox::freedesktop()
+{
+ if (!HostOsInfo::isAnyUnixHost())
+ QSKIP("only applies on Unix");
+ if (HostOsInfo::isMacosHost())
+ QSKIP("Does not apply on macOS");
+ QDir::setCurrent(testDataDir + "/freedesktop");
+ QCOMPARE(runQbs(), 0);
+
+ // Check desktop file
+ QString desktopFilePath =
+ defaultInstallRoot + "/usr/local/share/applications/myapp.desktop";
+ QVERIFY(QFile::exists(desktopFilePath));
+ QFile desktopFile(desktopFilePath);
+ QVERIFY2(desktopFile.open(QIODevice::ReadOnly), qPrintable(desktopFile.errorString()));
+ QByteArrayList lines = desktopFile.readAll().split('\n');
+ // Automatically filled line:
+ QVERIFY(lines.contains("Exec=main"));
+ // Name specified in `freedesktop.name` property
+ QVERIFY(lines.contains("Name=My App"));
+ // Overridden line:
+ QVERIFY(lines.contains("Icon=myapp.png"));
+ // Untouched line:
+ QVERIFY(lines.contains("Terminal=false"));
+
+ // Check AppStream file
+ QVERIFY(QFile::exists(defaultInstallRoot +
+ "/usr/local/share/metainfo/myapp.appdata.xml"));
+
+ // Check icon file
+ QVERIFY(QFile::exists(defaultInstallRoot +
+ "/usr/local/share/icons/hicolor/scalable/apps/myapp.png"));
+}
+
void TestBlackbox::installedTransformerOutput()
{
QDir::setCurrent(testDataDir + "/installed-transformer-output");
@@ -3800,8 +4390,16 @@ void TestBlackbox::installLocations_data()
QTest::addColumn<QString>("binDir");
QTest::addColumn<QString>("dllDir");
QTest::addColumn<QString>("libDir");
- QTest::newRow("explicit values") << QString("bindir") << QString("dlldir") << QString("libdir");
- QTest::newRow("default values") << QString() << QString() << QString();
+ QTest::addColumn<QString>("pluginDir");
+ QTest::addColumn<QString>("dsymDir");
+ QTest::newRow("explicit values")
+ << QString("bindir")
+ << QString("dlldir")
+ << QString("libdir")
+ << QString("pluginDir")
+ << QString("dsymDir");
+ QTest::newRow("default values")
+ << QString() << QString() << QString() << QString() << QString();
}
void TestBlackbox::installLocations()
@@ -3810,6 +4408,8 @@ void TestBlackbox::installLocations()
QFETCH(QString, binDir);
QFETCH(QString, dllDir);
QFETCH(QString, libDir);
+ QFETCH(QString, pluginDir);
+ QFETCH(QString, dsymDir);
QbsRunParameters params("resolve");
if (!binDir.isEmpty())
params.arguments.push_back("products.theapp.installDir:" + binDir);
@@ -3817,35 +4417,98 @@ void TestBlackbox::installLocations()
params.arguments.push_back("products.thelib.installDir:" + dllDir);
if (!libDir.isEmpty())
params.arguments.push_back("products.thelib.importLibInstallDir:" + libDir);
+ if (!pluginDir.isEmpty())
+ params.arguments.push_back("products.theplugin.installDir:" + pluginDir);
+ if (!dsymDir.isEmpty()) {
+ params.arguments.push_back("products.theapp.debugInformationInstallDir:" + dsymDir);
+ params.arguments.push_back("products.thelib.debugInformationInstallDir:" + dsymDir);
+ params.arguments.push_back("products.theplugin.debugInformationInstallDir:" + dsymDir);
+ }
QCOMPARE(runQbs(params), 0);
const bool isWindows = m_qbsStdout.contains("is windows");
+ const bool isDarwin = m_qbsStdout.contains("is darwin");
const bool isMac = m_qbsStdout.contains("is mac");
const bool isUnix = m_qbsStdout.contains("is unix");
- QVERIFY(isWindows || isMac || isUnix);
+ const bool isMingw = m_qbsStdout.contains("is mingw");
+ QVERIFY(isWindows || isDarwin || isUnix);
QCOMPARE(runQbs(QbsRunParameters(QStringList("--clean-install-root"))), 0);
- const QString dllFileName = isWindows ? "thelib.dll" : isMac ? "thelib" : "libthelib.so";
- const QString appFileName = isWindows ? "theapp.exe" : "theapp";
- if (binDir.isEmpty())
- binDir = isMac ? "/Applications" : "/bin";
- if (dllDir.isEmpty())
- dllDir = isMac ? "/Library/Frameworks" : isWindows ? "/bin" : "/lib";
- if (libDir.isEmpty())
- libDir = "/lib";
- if (isMac) {
- binDir += "/theapp.app/Contents/MacOS";
- dllDir += "/thelib.framework";
- }
+
+ struct BinaryInfo
+ {
+ QString fileName;
+ QString installDir;
+ QString subDir;
+
+ QString absolutePath(const QString &prefix) const
+ {
+ return QDir::cleanPath(prefix + '/' + installDir + '/' + subDir + '/' + fileName);
+ }
+ };
+
+ const BinaryInfo dll = {
+ isWindows ? "thelib.dll" : isDarwin ? "thelib" : "libthelib.so",
+ dllDir.isEmpty()
+ ? (isDarwin ? "/Library/Frameworks" : (isWindows ? "/bin" : "/lib"))
+ : dllDir,
+ isDarwin ? "thelib.framework" : ""
+ };
+ const BinaryInfo dllDsym = {
+ isWindows
+ ? (!isMingw ? "thelib.pdb" : "thelib.dll.debug")
+ : isDarwin ? "thelib.framework.dSYM" : "libthelib.so.debug",
+ dsymDir.isEmpty() ? dll.installDir : dsymDir,
+ {}
+ };
+ const BinaryInfo plugin = {
+ isWindows ? "theplugin.dll" : isDarwin ? "theplugin" : "libtheplugin.so",
+ pluginDir.isEmpty() ? dll.installDir : pluginDir,
+ isDarwin ? (isMac ? "theplugin.bundle/Contents/MacOS" : "theplugin.bundle") : ""
+ };
+ const BinaryInfo pluginDsym = {
+ isWindows
+ ? (!isMingw ? "theplugin.pdb" : "theplugin.dll.debug")
+ : isDarwin ? "theplugin.bundle.dSYM" : "libtheplugin.so.debug",
+ dsymDir.isEmpty() ? plugin.installDir : dsymDir,
+ {}
+ };
+ const BinaryInfo app = {
+ isWindows ? "theapp.exe" : "theapp",
+ binDir.isEmpty() ? (isDarwin ? "/Applications" : "/bin") : binDir,
+ isDarwin ? (isMac ? "theapp.app/Contents/MacOS" : "theapp.app") : ""
+ };
+ const BinaryInfo appDsym = {
+ isWindows
+ ? (!isMingw ? "theapp.pdb" : "theapp.exe.debug")
+ : isDarwin ? "theapp.app.dSYM" : "theapp.debug",
+ dsymDir.isEmpty() ? app.installDir : dsymDir,
+ {}
+ };
+
const QString installRoot = QDir::currentPath() + "/default/install-root";
const QString installPrefix = isWindows ? QString() : "/usr/local";
const QString fullInstallPrefix = installRoot + '/' + installPrefix + '/';
- const QString appFilePath = fullInstallPrefix + binDir + '/' + appFileName;
+ const QString appFilePath = app.absolutePath(fullInstallPrefix);
QVERIFY2(QFile::exists(appFilePath), qPrintable(appFilePath));
- const QString dllFilePath = fullInstallPrefix + dllDir + '/' + dllFileName;
+ const QString dllFilePath = dll.absolutePath(fullInstallPrefix);
QVERIFY2(QFile::exists(dllFilePath), qPrintable(dllFilePath));
if (isWindows) {
- const QString libFilePath = fullInstallPrefix + libDir + "/thelib.lib";
+ const BinaryInfo lib = {
+ "thelib.lib",
+ libDir.isEmpty() ? "/lib" : libDir,
+ ""
+ };
+ const QString libFilePath = lib.absolutePath(fullInstallPrefix);
QVERIFY2(QFile::exists(libFilePath), qPrintable(libFilePath));
}
+ const QString pluginFilePath = plugin.absolutePath(fullInstallPrefix);
+ QVERIFY2(QFile::exists(pluginFilePath), qPrintable(pluginFilePath));
+
+ const QString appDsymFilePath = appDsym.absolutePath(fullInstallPrefix);
+ QVERIFY2(QFileInfo(appDsymFilePath).exists(), qPrintable(appDsymFilePath));
+ const QString dllDsymFilePath = dllDsym.absolutePath(fullInstallPrefix);
+ QVERIFY2(QFileInfo(dllDsymFilePath).exists(), qPrintable(dllDsymFilePath));
+ const QString pluginDsymFilePath = pluginDsym.absolutePath(fullInstallPrefix);
+ QVERIFY2(QFile::exists(pluginDsymFilePath), qPrintable(pluginDsymFilePath));
}
void TestBlackbox::inputsFromDependencies()
@@ -3887,7 +4550,7 @@ void TestBlackbox::installPackage()
cleanOutputLines.push_back(trimmedLine);
}
QCOMPARE(cleanOutputLines.size(), 3);
- for (const QByteArray &line : qAsConst(cleanOutputLines)) {
+ for (const QByteArray &line : std::as_const(cleanOutputLines)) {
QVERIFY2(line.contains("public_tool") || line.contains("mylib") || line.contains("lib.h"),
line.constData());
}
@@ -3915,6 +4578,9 @@ void TestBlackbox::installable()
void TestBlackbox::installableAsAuxiliaryInput()
{
QDir::setCurrent(testDataDir + "/installable-as-auxiliary-input");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(runQbs(QbsRunParameters("run")), 0);
QVERIFY2(m_qbsStdout.contains("f-impl"), m_qbsStdout.constData());
}
@@ -3961,7 +4627,7 @@ void TestBlackbox::invalidLibraryNames()
QbsRunParameters params(QStringList("project.valueIndex:" + index));
params.expectFailure = !success;
QCOMPARE(runQbs(params) == 0, success);
- for (const QString &diag : qAsConst(diagnostics))
+ for (const QString &diag : std::as_const(diagnostics))
QVERIFY2(m_qbsStderr.contains(diag.toLocal8Bit()), m_qbsStderr.constData());
}
@@ -4022,8 +4688,8 @@ void TestBlackbox::cli()
QCOMPARE(status, 0);
const SettingsPtr s = settings();
- Profile p("qbs_autotests-cli", s.get());
- const QStringList toolchain = p.value("qbs.toolchain").toStringList();
+ qbs::Profile p("qbs_autotests-cli", s.get());
+ const QStringList toolchain = profileToolchain(p);
if (!p.exists() || !(toolchain.contains("dotnet") || toolchain.contains("mono")))
QSKIP("No suitable Common Language Infrastructure test profile");
@@ -4114,7 +4780,7 @@ void TestBlackbox::jsExtensionsFileInfo()
QVERIFY(output.exists());
QVERIFY(output.open(QIODevice::ReadOnly));
const QList<QByteArray> lines = output.readAll().trimmed().split('\n');
- QCOMPARE(lines.size(), 26);
+ QCOMPARE(lines.size(), 28);
int i = 0;
QCOMPARE(lines.at(i++).trimmed().constData(), "blubb");
QCOMPARE(lines.at(i++).trimmed().constData(), qUtf8Printable(
@@ -4143,11 +4809,52 @@ void TestBlackbox::jsExtensionsFileInfo()
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(), qUtf8Printable(HostOsInfo::pathListSeparator()));
+ QCOMPARE(lines.at(i++).trimmed().constData(), qUtf8Printable(HostOsInfo::pathSeparator()));
+}
+
+void TestBlackbox::jsExtensionsHost()
+{
+ QDir::setCurrent(testDataDir + "//jsextensions-host");
+ QbsRunParameters params(QStringList { "-f", "host.qbs" });
+ QCOMPARE(runQbs(params), 0);
+ QFile output("output.txt");
+ QVERIFY(output.exists());
+ QVERIFY(output.open(QIODevice::ReadOnly));
+ const QList<QByteArray> lines = output.readAll().trimmed().split('\n');
+ QCOMPARE(lines.size(), 10);
+ int i = 0;
+ QCOMPARE(lines.at(i++).trimmed().constData(), "architecture: " +
+ HostOsInfo::hostOSArchitecture());
+ QStringList list;
+ for (const auto &s : HostOsInfo::canonicalOSIdentifiers(HostOsInfo::hostOSIdentifier()))
+ list.push_back(s);
+ QCOMPARE(lines.at(i++).trimmed().constData(), "os: " + list.join(','));
+ QCOMPARE(lines.at(i++).trimmed().constData(), "platform: " + HostOsInfo::hostOSIdentifier());
+ QCOMPARE(lines.at(i++).trimmed().constData(), "osVersion: " +
+ HostOsInfo::hostOsVersion().toString());
+ QCOMPARE(lines.at(i++).trimmed().constData(), "osBuildVersion: " +
+ (HostOsInfo::hostOsBuildVersion().isNull() ? "undefined" :
+ HostOsInfo::hostOsBuildVersion()));
+ QCOMPARE(lines.at(i++).trimmed().constData(), "osVersionParts: " +
+ HostOsInfo::hostOsVersion().toString(','));
+ QCOMPARE(lines.at(i++).trimmed().constData(), "osVersionMajor: " + QString::number(
+ HostOsInfo::hostOsVersion().majorVersion()));
+ QCOMPARE(lines.at(i++).trimmed().constData(), "osVersionMinor: " + QString::number(
+ HostOsInfo::hostOsVersion().minorVersion()));
+ QCOMPARE(lines.at(i++).trimmed().constData(), "osVersionPatch: " + QString::number(
+ HostOsInfo::hostOsVersion().patchLevel()));
+ QString nullDevice = HostOsInfo::isWindowsHost() ? QStringLiteral("NUL") :
+ QStringLiteral("/dev/null");
+ QCOMPARE(lines.at(i++).trimmed().constData(), "nullDevice: " + nullDevice);
}
void TestBlackbox::jsExtensionsProcess()
{
QDir::setCurrent(testDataDir + "/jsextensions-process");
+ QCOMPARE(runQbs({"resolve", {"-f", "process.qbs"}}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters params(QStringList() << "-f" << "process.qbs");
QCOMPARE(runQbs(params), 0);
QFile output("output.txt");
@@ -4249,6 +4956,20 @@ void TestBlackbox::jsExtensionsBinaryFile()
QCOMPARE(data.at(5), char(0x05));
QCOMPARE(data.at(6), char(0x06));
QCOMPARE(data.at(7), char(0xFF));
+ QFile destination2("destination2.dat");
+ QVERIFY(destination2.exists());
+ QVERIFY(destination2.open(QIODevice::ReadOnly));
+ QCOMPARE(destination2.readAll(), data);
+}
+
+void TestBlackbox::lastModuleCandidateBroken()
+{
+ QDir::setCurrent(testDataDir + "/last-module-candidate-broken");
+ QbsRunParameters params;
+ params.expectFailure = true;
+ QVERIFY(runQbs(params) != 0);
+ QVERIFY2(m_qbsStderr.contains("Dependency 'Foo' not found for product "
+ "'last-module-candidate-broken'"), m_qbsStderr);
}
void TestBlackbox::ld()
@@ -4264,6 +4985,9 @@ void TestBlackbox::symbolLinkMode()
QDir::setCurrent(testDataDir + "/symbolLinkMode");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters params;
params.command = "run";
const QStringList commonArgs{"-p", "driver", "--setup-run-env-config",
@@ -4351,6 +5075,7 @@ void TestBlackbox::linkerVariant_data()
QTest::newRow("default") << QString();
QTest::newRow("bfd") << QString("bfd");
QTest::newRow("gold") << QString("gold");
+ QTest::newRow("mold") << QString("mold");
}
void TestBlackbox::linkerVariant()
@@ -4378,6 +5103,9 @@ void TestBlackbox::lexyacc()
if (!lexYaccExist())
QSKIP("lex or yacc not present");
QDir::setCurrent(testDataDir + "/lexyacc/one-grammar");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(runQbs(), 0);
const QString parserBinary = relativeExecutableFilePath("one-grammar");
QProcess p;
@@ -4393,7 +5121,7 @@ void TestBlackbox::lexyacc()
env.insert("PATH", dir);
p.setProcessEnvironment(env);
}
- p.start(parserBinary);
+ p.start(parserBinary, QStringList());
QVERIFY2(p.waitForStarted(), qPrintable(p.errorString()));
p.write("a && b || c && !d");
p.closeWriteChannel();
@@ -4481,22 +5209,20 @@ void TestBlackbox::lexyaccOutputs_data()
void TestBlackbox::linkerLibraryDuplicates()
{
- const SettingsPtr s = settings();
- Profile buildProfile(profileName(), s.get());
- QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList();
- if (!toolchain.contains("gcc"))
- QSKIP("linkerLibraryDuplicates test only applies to GCC toolchain");
-
QDir::setCurrent(testDataDir + "/linker-library-duplicates");
rmDirR(relativeBuildDir());
-
QFETCH(QString, removeDuplicateLibraries);
QStringList runParams;
- if (!removeDuplicateLibraries.isEmpty()) {
+ if (!removeDuplicateLibraries.isEmpty())
runParams.append(removeDuplicateLibraries);
- }
QCOMPARE(runQbs(QbsRunParameters("resolve", runParams)), 0);
+ const bool isGcc = m_qbsStdout.contains("is gcc: true");
+ const bool isNotGcc = m_qbsStdout.contains("is gcc: false");
+ if (isNotGcc)
+ QSKIP("linkerLibraryDuplicates test only applies to GCC toolchain");
+ QVERIFY(isGcc);
+
QCOMPARE(runQbs(QStringList { "--command-echo-mode", "command-line" }), 0);
const QByteArrayList output = m_qbsStdout.split('\n');
QByteArray linkLine;
@@ -4561,25 +5287,27 @@ void TestBlackbox::linkerLibraryDuplicates_data()
void TestBlackbox::linkerScripts()
{
- const SettingsPtr s = settings();
- Profile buildProfile(profileName(), s.get());
- QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList();
- if (!toolchain.contains("gcc") || targetOs() != HostOsInfo::HostOsLinux)
- QSKIP("linker script test only applies to Linux ");
-
- QbsRunParameters runParams(QStringList()
-// << "--log-level" << "debug"
- << ("qbs.installRoot:" + QDir::currentPath()));
const QString sourceDir = QDir::cleanPath(testDataDir + "/linkerscripts");
+ QbsRunParameters runParams("resolve", {"qbs.installRoot:" + QDir::currentPath()});
runParams.buildDirectory = sourceDir + "/build";
runParams.workingDir = sourceDir;
QCOMPARE(runQbs(runParams), 0);
+ const bool isGcc = m_qbsStdout.contains("is Linux gcc: true");
+ const bool isNotGcc = m_qbsStdout.contains("is Linux gcc: false");
+ if (isNotGcc)
+ QSKIP("linker script test only applies to Linux");
+ QVERIFY(isGcc);
+
+ runParams.command = "build";
+ QCOMPARE(runQbs(runParams), 0);
const QString output = QString::fromLocal8Bit(m_qbsStderr);
- QRegExp pattern(".*---(.*)---.*");
- QVERIFY2(pattern.exactMatch(output), qPrintable(output));
+ const QRegularExpression pattern(QRegularExpression::anchoredPattern(".*---(.*)---.*"),
+ QRegularExpression::DotMatchesEverythingOption);
+ const QRegularExpressionMatch match = pattern.match(output);
+ QVERIFY2(match.hasMatch(), qPrintable(output));
QCOMPARE(pattern.captureCount(), 1);
- const QString nmPath = pattern.capturedTexts().at(1);
+ const QString nmPath = match.captured(1);
if (!QFile::exists(nmPath))
QSKIP("Cannot check for symbol presence: No nm found.");
@@ -4652,6 +5380,26 @@ void TestBlackbox::linkerScripts()
"TEST_SYMBOL_FROM_RECURSIVE_MODIFIED"}));
}
+void TestBlackbox::linkerModuleDefinition()
+{
+ QDir::setCurrent(testDataDir + "/linker-module-definition");
+ QCOMPARE(runQbs({"build"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+ QCOMPARE(runQbs({"run"}), 0);
+ const auto verifyOutput = [this](const QByteArrayList &symbols) {
+ for (const QByteArray &symbol : symbols) {
+ if (!m_qbsStdout.contains(symbol)) {
+ qDebug() << "Expected symbol" << symbol
+ << "not found in" << m_qbsStdout;
+ return false;
+ }
+ }
+ return true;
+ };
+ QVERIFY(verifyOutput({"foo", "bar"}));
+}
+
void TestBlackbox::listProducts()
{
QDir::setCurrent(testDataDir + "/list-products");
@@ -4680,7 +5428,7 @@ void TestBlackbox::listPropertyOrder()
const QbsRunParameters params(QStringList() << "-q");
QCOMPARE(runQbs(params), 0);
const QByteArray firstOutput = m_qbsStderr;
- QVERIFY(firstOutput.contains("listProp = [\"higher1\",\"higher2\",\"higher3\"]"));
+ QVERIFY(firstOutput.contains("listProp = [\"product\",\"higher3\",\"higher2\",\"higher1\",\"lower\"]"));
for (int i = 0; i < 25; ++i) {
rmDirR(relativeBuildDir());
QCOMPARE(runQbs(params), 0);
@@ -4696,16 +5444,6 @@ void TestBlackbox::require()
QCOMPARE(runQbs(), 0);
}
-void TestBlackbox::requireDeprecated()
-{
- QDir::setCurrent(testDataDir + "/require-deprecated");
- QCOMPARE(runQbs(), 0);
- QVERIFY2(m_qbsStderr.contains("loadExtension() function is deprecated"),
- m_qbsStderr.constData());
- QVERIFY2(m_qbsStderr.contains("loadFile() function is deprecated"),
- m_qbsStderr.constData());
-}
-
void TestBlackbox::rescueTransformerData()
{
QDir::setCurrent(testDataDir + "/rescue-transformer-data");
@@ -4767,6 +5505,9 @@ void TestBlackbox::multipleConfigurations()
void TestBlackbox::multiplexedTool()
{
QDir::setCurrent(testDataDir + "/multiplexed-tool");
+ QCOMPARE(runQbs(QStringLiteral("resolve")), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(runQbs(), 0);
QCOMPARE(m_qbsStdout.count("creating tool.out"), 4);
}
@@ -4922,6 +5663,8 @@ void TestBlackbox::propertyPrecedence()
// Case 1: [cmdline=0,prod=0,export=0,nonleaf=0,profile=0]
QCOMPARE(runQbs(params), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
+
QVERIFY2(m_qbsStdout.contains("scalar prop: leaf\n")
&& m_qbsStdout.contains("list prop: [\"leaf\"]\n"),
m_qbsStdout.constData());
@@ -4930,6 +5673,8 @@ void TestBlackbox::propertyPrecedence()
// Case 2: [cmdline=0,prod=0,export=0,nonleaf=0,profile=1]
switchProfileContents(profile.p, s.get(), true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
+
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: profile\n")
&& m_qbsStdout.contains("list prop: [\"profile\"]\n"),
@@ -4941,6 +5686,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), false);
switchFileContents(nonleafFile, true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: nonleaf\n")
&& m_qbsStdout.contains("list prop: [\"nonleaf\",\"leaf\"]\n"),
@@ -4949,6 +5695,7 @@ void TestBlackbox::propertyPrecedence()
// Case 4: [cmdline=0,prod=0,export=0,nonleaf=1,profile=1]
switchProfileContents(profile.p, s.get(), true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: nonleaf\n")
&& m_qbsStdout.contains("list prop: [\"nonleaf\",\"profile\"]\n"),
@@ -4961,6 +5708,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(nonleafFile, false);
switchFileContents(depFile, true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: export\n")
&& m_qbsStdout.contains("list prop: [\"export\",\"leaf\"]\n"),
@@ -4969,16 +5717,18 @@ void TestBlackbox::propertyPrecedence()
// Case 6: [cmdline=0,prod=0,export=1,nonleaf=0,profile=1]
switchProfileContents(profile.p, s.get(), true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
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.get(), false);
switchFileContents(nonleafFile, true);
QCOMPARE(runQbs(resolveParams), 0);
- QCOMPARE(runQbs(params), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData()); QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: export\n")
&& m_qbsStdout.contains("list prop: [\"export\",\"nonleaf\",\"leaf\"]\n"),
m_qbsStdout.constData());
@@ -4986,6 +5736,7 @@ void TestBlackbox::propertyPrecedence()
// Case 8: [cmdline=0,prod=0,export=1,nonleaf=1,profile=1]
switchProfileContents(profile.p, s.get(), true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: export\n")
&& m_qbsStdout.contains("list prop: [\"export\",\"nonleaf\",\"profile\"]\n"),
@@ -4999,6 +5750,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(depFile, false);
switchFileContents(productFile, true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: product\n")
&& m_qbsStdout.contains("list prop: [\"product\",\"leaf\"]\n"),
@@ -5007,6 +5759,7 @@ void TestBlackbox::propertyPrecedence()
// Case 10: [cmdline=0,prod=1,export=0,nonleaf=0,profile=1]
switchProfileContents(profile.p, s.get(), true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: product\n")
&& m_qbsStdout.contains("list prop: [\"product\",\"profile\"]\n"),
@@ -5016,6 +5769,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), false);
switchFileContents(nonleafFile, true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: product\n")
&& m_qbsStdout.contains("list prop: [\"product\",\"nonleaf\",\"leaf\"]\n"),
@@ -5024,6 +5778,7 @@ void TestBlackbox::propertyPrecedence()
// Case 12: [cmdline=0,prod=1,export=0,nonleaf=1,profile=1]
switchProfileContents(profile.p, s.get(), true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: product\n")
&& m_qbsStdout.contains("list prop: [\"product\",\"nonleaf\",\"profile\"]\n"),
@@ -5034,6 +5789,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(nonleafFile, false);
switchFileContents(depFile, true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: product\n")
&& m_qbsStdout.contains("list prop: [\"product\",\"export\",\"leaf\"]\n"),
@@ -5042,6 +5798,7 @@ void TestBlackbox::propertyPrecedence()
// Case 14: [cmdline=0,prod=1,export=1,nonleaf=0,profile=1]
switchProfileContents(profile.p, s.get(), true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: product\n")
&& m_qbsStdout.contains("list prop: [\"product\",\"export\",\"profile\"]\n"),
@@ -5051,6 +5808,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), false);
switchFileContents(nonleafFile, true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: product\n")
&& m_qbsStdout.contains("list prop: [\"product\",\"export\",\"nonleaf\",\"leaf\"]\n"),
@@ -5059,6 +5817,7 @@ void TestBlackbox::propertyPrecedence()
// Case 16: [cmdline=0,prod=1,export=1,nonleaf=1,profile=1]
switchProfileContents(profile.p, s.get(), true);
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: product\n")
&& m_qbsStdout.contains("list prop: [\"product\",\"export\",\"nonleaf\",\"profile\"]\n"),
@@ -5072,6 +5831,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(productFile, false);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5081,6 +5841,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5091,6 +5852,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(nonleafFile, true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5100,6 +5862,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5111,6 +5874,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(depFile, true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5120,6 +5884,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5130,6 +5895,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(nonleafFile, true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5139,6 +5905,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5151,6 +5918,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(productFile, true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5160,6 +5928,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5170,6 +5939,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(nonleafFile, true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5179,6 +5949,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5190,6 +5961,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(depFile, true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5199,6 +5971,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5209,6 +5982,7 @@ void TestBlackbox::propertyPrecedence()
switchFileContents(nonleafFile, true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5218,6 +5992,7 @@ void TestBlackbox::propertyPrecedence()
switchProfileContents(profile.p, s.get(), true);
resolveParams.arguments << "modules.leaf.scalarProp:cmdline" << "modules.leaf.listProp:cmdline";
QCOMPARE(runQbs(resolveParams), 0);
+ QVERIFY2(m_qbsStderr.isEmpty(), m_qbsStderr.constData());
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("scalar prop: cmdline\n")
&& m_qbsStdout.contains("list prop: [\"cmdline\"]\n"),
@@ -5247,10 +6022,19 @@ void TestBlackbox::productDependenciesByType()
QVERIFY(apps.empty());
}
+void TestBlackbox::productInExportedModule()
+{
+ QDir::setCurrent(testDataDir + "/product-in-exported-module");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("product: importing"), m_qbsStdout.constData());
+}
+
void TestBlackbox::properQuoting()
{
QDir::setCurrent(testDataDir + "/proper quoting");
QCOMPARE(runQbs(), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters params(QStringLiteral("run"), QStringList() << "-q" << "-p" << "Hello World");
params.expectFailure = true; // Because the exit code is non-zero.
QCOMPARE(runQbs(params), 156);
@@ -5272,15 +6056,21 @@ void TestBlackbox::protobuf_data()
{
QTest::addColumn<QString>("projectFile");
QTest::addColumn<QStringList>("properties");
+ QTest::addColumn<bool>("hasModules");
QTest::addColumn<bool>("successExpected");
- QTest::newRow("cpp") << QString("addressbook_cpp.qbs") << QStringList() << true;
- QTest::newRow("objc") << QString("addressbook_objc.qbs") << QStringList() << true;
- QTest::newRow("import") << QString("import.qbs") << QStringList() << true;
+ QTest::newRow("cpp-pkgconfig")
+ << QString("addressbook_cpp.qbs")
+ << QStringList({"project.qbsModuleProviders:qbspkgconfig"}) << true << true;
+ QTest::newRow("objc") << QString("addressbook_objc.qbs") << QStringList() << false << true;
+ QTest::newRow("nanopb") << QString("addressbook_nanopb.qbs") << QStringList() << false << true;
+ QTest::newRow("import") << QString("import.qbs") << QStringList() << true << true;
QTest::newRow("missing import dir") << QString("needs-import-dir.qbs")
- << QStringList() << false;
+ << QStringList() << true << false;
QTest::newRow("provided import dir")
<< QString("needs-import-dir.qbs")
- << QStringList("products.app.theImportDir:subdir") << true;
+ << QStringList("products.app.theImportDir:subdir") << true << true;
+ QTest::newRow("create proto library")
+ << QString("create-proto-library.qbs") << QStringList() << true << true;
}
void TestBlackbox::protobuf()
@@ -5288,20 +6078,49 @@ void TestBlackbox::protobuf()
QDir::setCurrent(testDataDir + "/protobuf");
QFETCH(QString, projectFile);
QFETCH(QStringList, properties);
+ QFETCH(bool, hasModules);
QFETCH(bool, successExpected);
rmDirR(relativeBuildDir());
QbsRunParameters resolveParams("resolve", QStringList{"-f", projectFile} << properties);
QCOMPARE(runQbs(resolveParams), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
const bool withProtobuf = m_qbsStdout.contains("has protobuf: true");
const bool withoutProtobuf = m_qbsStdout.contains("has protobuf: false");
QVERIFY2(withProtobuf || withoutProtobuf, m_qbsStdout.constData());
if (withoutProtobuf)
QSKIP("protobuf module not present");
+ const bool hasMods = m_qbsStdout.contains("has modules: true");
+ const bool dontHaveMods = m_qbsStdout.contains("has modules: false");
+ QVERIFY2(hasMods == !dontHaveMods, m_qbsStdout.constData());
+ QCOMPARE(hasMods, hasModules);
QbsRunParameters runParams("run");
runParams.expectFailure = !successExpected;
QCOMPARE(runQbs(runParams) == 0, successExpected);
}
+void TestBlackbox::protobufLibraryInstall()
+{
+ QDir::setCurrent(testDataDir + "/protobuf-library-install");
+ rmDirR(relativeBuildDir());
+ QbsRunParameters resolveParams("resolve", QStringList{"qbs.installPrefix:/usr/local"});
+ QCOMPARE(runQbs(resolveParams), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+ const bool withProtobuf = m_qbsStdout.contains("has protobuf: true");
+ const bool withoutProtobuf = m_qbsStdout.contains("has protobuf: false");
+ QVERIFY2(withProtobuf || withoutProtobuf, m_qbsStdout.constData());
+ if (withoutProtobuf)
+ QSKIP("protobuf module not present");
+ QbsRunParameters buildParams("build");
+ buildParams.expectFailure = false;
+ QCOMPARE(runQbs(buildParams), 0);
+
+ const QString installRootInclude = relativeBuildDir() + "/install-root/usr/local/include";
+ QVERIFY(QFileInfo::exists(installRootInclude + "/hello.pb.h") &&
+ QFileInfo::exists(installRootInclude + "/hello/world.pb.h"));
+}
+
void TestBlackbox::pseudoMultiplexing()
{
// This is "pseudo-multiplexing" on all platforms that initialize qbs.architectures
@@ -5411,7 +6230,158 @@ void TestBlackbox::qbsConfig()
if (!canWriteToSystemSettings) {
QVERIFY2(m_qbsStderr.contains("You do not have permission to write to that location."),
m_qbsStderr.constData());
+ } else {
+ // cleanup system settings
+ params.arguments = QStringList{"--system", "--unset", "key.subkey.scalar"};
+ QCOMPARE(runQbs(params) == 0, canWriteToSystemSettings);
+ }
+}
+
+void TestBlackbox::qbsConfigAddProfile()
+{
+ QbsRunParameters params("config");
+ QTemporaryDir settingsDir1;
+ QTemporaryDir settingsDir2;
+ QVERIFY(settingsDir1.isValid());
+ QVERIFY(settingsDir2.isValid());
+ const QStringList settingsDir1Args = QStringList{"--settings-dir", settingsDir1.path()};
+ const QStringList settingsDir2Args = QStringList{"--settings-dir", settingsDir2.path()};
+
+ QFETCH(QStringList, args);
+ QFETCH(QString, errorMsg);
+
+ // Step 1: Run --add-profile.
+ params.arguments = settingsDir1Args;
+ params.arguments << "--add-profile";
+ params.arguments << args;
+ params.expectFailure = !errorMsg.isEmpty();
+ QCOMPARE(runQbs(params) == 0, !params.expectFailure);
+ if (params.expectFailure) {
+ QVERIFY(QString::fromLocal8Bit(m_qbsStderr).contains(errorMsg));
+ return;
+ }
+ params.expectFailure = false;
+ params.arguments = settingsDir1Args;
+ params.arguments << "--list";
+ QCOMPARE(runQbs(params), 0);
+ const QByteArray output1 = m_qbsStdout;
+
+ // Step 2: Set properties manually.
+ for (int i = 1; i < args.size(); i += 2) {
+ params.arguments = settingsDir2Args;
+ params.arguments << ("profiles." + args.first() + '.' + args.at(i)) << args.at(i + 1);
+ QCOMPARE(runQbs(params), 0);
+ }
+ params.arguments = settingsDir2Args;
+ params.arguments << "--list";
+ QCOMPARE(runQbs(params), 0);
+ const QByteArray output2 = m_qbsStdout;
+
+ // Step3: Compare results.
+ QCOMPARE(output1, output2);
+}
+
+void TestBlackbox::qbsConfigAddProfile_data()
+{
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<QString>("errorMsg");
+ QTest::newRow("no arguments") << QStringList() << QString("Profile name missing");
+ QTest::newRow("empty name") << QStringList{"", "p", "v"}
+ << QString("Profile name must not be empty");
+ QTest::newRow("no properties") << QStringList("p")
+ << QString("Profile properties must be provided");
+ QTest::newRow("one property") << QStringList{"p", "p", "v"} << QString();
+ QTest::newRow("two properties") << QStringList{"p", "p1", "v1", "p2", "v2"} << QString();
+ QTest::newRow("missing value") << QStringList{"p", "p"}
+ << QString("Profile properties must be key/value pairs");
+ QTest::newRow("missing values") << QStringList{"p", "p1", "v1", "p2"}
+ << QString("Profile properties must be key/value pairs");
+}
+
+void TestBlackbox::qbsConfigImport()
+{
+ QFETCH(QString, format);
+
+ QDir::setCurrent(testDataDir + "/qbs-config-import-export");
+
+ QbsRunParameters params("config");
+ QTemporaryDir settingsDir;
+ QVERIFY(settingsDir.isValid());
+ const QStringList settingsDirArgs = QStringList{"--settings-dir", settingsDir.path()};
+ params.arguments = settingsDirArgs;
+ params.arguments << "--import" << "config." + format;
+
+ QCOMPARE(runQbs(params), 0);
+
+ params.arguments = settingsDirArgs;
+ params.arguments << "--list";
+ QCOMPARE(runQbs(params), 0);
+ const QByteArray output = m_qbsStdout;
+ const auto lines = output.split('\n');
+ QCOMPARE(lines.count(), 5);
+ QCOMPARE(lines[0], "group.key1: \"value1\"");
+ QCOMPARE(lines[1], "group.key2: \"value2\"");
+ QCOMPARE(lines[2], "key: \"value\"");
+ QCOMPARE(lines[3], "listKey: [\"valueOne\", \"valueTwo\"]");
+ QCOMPARE(lines[4], "");
+}
+
+void TestBlackbox::qbsConfigImport_data()
+{
+ QTest::addColumn<QString>("format");
+
+ QTest::newRow("text") << QStringLiteral("txt");
+ QTest::newRow("json") << QStringLiteral("json");
+}
+
+void TestBlackbox::qbsConfigExport()
+{
+ QFETCH(QString, format);
+
+ QDir::setCurrent(testDataDir + "/qbs-config-import-export");
+
+ QbsRunParameters params("config");
+ QTemporaryDir settingsDir;
+ const QString fileName = "config." + format;
+ const QString filePath = settingsDir.path() + "/" + fileName;
+ QVERIFY(settingsDir.isValid());
+ const QStringList commonArgs = QStringList{"--settings-dir", settingsDir.path(), "--user"};
+
+ std::pair<QString, QString> values[] = {
+ {"key", "value"},
+ {"listKey", "[\"valueOne\",\"valueTwo\"]"},
+ {"group.key1", "value1"},
+ {"group.key2", "value2"}
+ };
+
+ for (const auto &value: values) {
+ params.arguments = commonArgs;
+ params.arguments << value.first << value.second;
+ QCOMPARE(runQbs(params), 0);
}
+
+ params.arguments = commonArgs;
+ params.arguments << "--export" << filePath;
+
+ QCOMPARE(runQbs(params), 0);
+
+ QVERIFY(QFileInfo(filePath).canonicalPath() != QFileInfo(fileName).canonicalPath());
+
+ QFile exported(filePath);
+ QFile expected(fileName);
+
+ QVERIFY(exported.open(QIODevice::ReadOnly | QIODevice::Text));
+ QVERIFY(expected.open(QIODevice::ReadOnly | QIODevice::Text));
+
+ QCOMPARE(exported.readAll(), expected.readAll());
+}
+
+void TestBlackbox::qbsConfigExport_data()
+{
+ QTest::addColumn<QString>("format");
+
+ QTest::newRow("text") << QStringLiteral("txt");
+ QTest::newRow("json") << QStringLiteral("json");
}
static QJsonObject getNextSessionPacket(QProcess &session, QByteArray &data)
@@ -5450,6 +6420,137 @@ static QJsonObject getNextSessionPacket(QProcess &session, QByteArray &data)
return QJsonDocument::fromJson(QByteArray::fromBase64(msg)).object();
}
+static void sendSessionPacket(QProcess &sessionProc, const QJsonObject &message)
+{
+ const QByteArray data = QJsonDocument(message).toJson().toBase64();
+ sessionProc.write("qbsmsg:");
+ sessionProc.write(QByteArray::number(data.length()));
+ sessionProc.write("\n");
+ sessionProc.write(data);
+}
+
+void TestBlackbox::qbsLanguageServer_data()
+{
+ QTest::addColumn<QString>("request");
+ QTest::addColumn<QString>("location");
+ QTest::addColumn<QString>("insertLocation");
+ QTest::addColumn<QString>("insertString");
+ QTest::addColumn<QString>("expectedReply");
+
+ QTest::addRow("follow to module") << "--goto-def"
+ << (testDataDir + "/lsp/lsp.qbs:4:9")
+ << QString() << QString()
+ << (testDataDir + "/lsp/modules/m/m.qbs:1:1");
+ QTest::addRow("follow to submodules")
+ << "--goto-def"
+ << (testDataDir + "/lsp/lsp.qbs:5:35")
+ << QString() << QString()
+ << ((testDataDir + "/lsp/modules/Prefix/m1/m1.qbs:1:1\n")
+ + (testDataDir + "/lsp/modules/Prefix/m2/m2.qbs:1:1\n")
+ + (testDataDir + "/lsp/modules/Prefix/m3/m3.qbs:1:1"));
+ QTest::addRow("follow to product")
+ << "--goto-def" << (testDataDir + "/lsp/lsp.qbs:9:19") << QString() << QString()
+ << (testDataDir + "/lsp/lsp.qbs:2:5");
+ QTest::addRow("follow to module, non-invalidating insert")
+ << "--goto-def" << (testDataDir + "/lsp/lsp.qbs:4:9") << "5:9"
+ << QString("property bool dummy\n") << (testDataDir + "/lsp/modules/m/m.qbs:1:1");
+ QTest::addRow("follow to module, invalidating insert")
+ << "--goto-def" << (testDataDir + "/lsp/lsp.qbs:4:9") << QString()
+ << QString("property bool dummy\n") << QString();
+ QTest::addRow("completion: LHS, module prefix")
+ << "--completion" << (testDataDir + "/lsp/lsp.qbs:7:1") << QString() << QString("P")
+ << QString("Prefix.m1\nPrefix.m2\nPrefix.m3");
+ QTest::addRow("completion: LHS, module name")
+ << "--completion" << (testDataDir + "/lsp/lsp.qbs:7:1") << QString() << QString("Prefix.m")
+ << QString("m1\nm2\nm3");
+ QTest::addRow("completion: LHS, module property right after dot")
+ << "--completion" << (testDataDir + "/lsp/lsp.qbs:7:1") << QString()
+ << QString("Prefix.m1.") << QString("p1 bool\np2 string\nx bool");
+ QTest::addRow("completion: LHS, module property with identifier prefix")
+ << "--completion" << (testDataDir + "/lsp/lsp.qbs:7:1") << QString()
+ << QString("Prefix.m1.p") << QString("p1 bool\np2 string");
+ QTest::addRow("completion: simple RHS, module property")
+ << "--completion" << (testDataDir + "/lsp/lsp.qbs:7:1") << QString()
+ << QString("property bool dummy: Prefix.m1.p") << QString("p1 bool\np2 string");
+ QTest::addRow("completion: complex RHS, module property")
+ << "--completion" << (testDataDir + "/lsp/lsp.qbs:7:1") << QString()
+ << QString("property bool dummy: { return Prefix.m1.p") << QString("p1 bool\np2 string");
+}
+
+void TestBlackbox::qbsLanguageServer()
+{
+ QFETCH(QString, request);
+ QFETCH(QString, location);
+ QFETCH(QString, insertLocation);
+ QFETCH(QString, insertString);
+ QFETCH(QString, expectedReply);
+
+ QDir::setCurrent(testDataDir + "/lsp");
+ QProcess sessionProc;
+ sessionProc.start(qbsExecutableFilePath, QStringList("session"));
+
+ QVERIFY(sessionProc.waitForStarted());
+
+ QByteArray incomingData;
+
+ // Wait for and verify hello packet.
+ QJsonObject receivedMessage = getNextSessionPacket(sessionProc, incomingData);
+ const QString socketPath = receivedMessage.value("lsp-socket").toString();
+ QVERIFY(!socketPath.isEmpty());
+
+ // Resolve project.
+ QJsonObject resolveMessage;
+ resolveMessage.insert("type", "resolve-project");
+ resolveMessage.insert("top-level-profile", profileName());
+ resolveMessage.insert("project-file-path", QDir::currentPath() + "/lsp.qbs");
+ resolveMessage.insert("build-root", QDir::currentPath());
+ resolveMessage.insert("settings-directory", settings()->baseDirectory());
+ sendSessionPacket(sessionProc, resolveMessage);
+ bool receivedReply = false;
+ while (!receivedReply) {
+ receivedMessage = getNextSessionPacket(sessionProc, incomingData);
+ QVERIFY(!receivedMessage.isEmpty());
+ const QString msgType = receivedMessage.value("type").toString();
+ if (msgType == "project-resolved") {
+ receivedReply = true;
+ const QJsonObject error = receivedMessage.value("error").toObject();
+ if (!error.isEmpty())
+ qDebug() << error;
+ QVERIFY(error.isEmpty());
+ }
+ }
+ QVERIFY(receivedReply);
+
+ // Employ client app to send request.
+ QProcess lspClient;
+ const QFileInfo qbsFileInfo(qbsExecutableFilePath);
+ const QString clientFilePath = HostOsInfo::appendExecutableSuffix(
+ qbsFileInfo.absolutePath() + "/qbs_lspclient");
+ QStringList args{"--socket", socketPath, request, location};
+ if (!insertString.isEmpty())
+ args << "--insert-code" << insertString;
+ if (!insertLocation.isEmpty())
+ args << "--insert-location" << insertLocation;
+ lspClient.start(clientFilePath, args);
+ QVERIFY2(lspClient.waitForStarted(), qPrintable(lspClient.errorString()));
+ QVERIFY2(lspClient.waitForFinished(), qPrintable(lspClient.errorString()));
+ QString errMsg;
+ if (lspClient.exitStatus() != QProcess::NormalExit)
+ errMsg = lspClient.errorString();
+ if (errMsg.isEmpty())
+ errMsg = QString::fromLocal8Bit(lspClient.readAllStandardError());
+ QVERIFY2(lspClient.exitCode() == 0, qPrintable(errMsg));
+ QVERIFY2(errMsg.isEmpty(), qPrintable(errMsg));
+ QString output = QString::fromUtf8(lspClient.readAllStandardOutput().trimmed());
+ output.replace("\r\n", "\n");
+ QCOMPARE(output, expectedReply);
+
+ QJsonObject quitRequest;
+ quitRequest.insert("type", "quit");
+ sendSessionPacket(sessionProc, quitRequest);
+ QVERIFY(sessionProc.waitForFinished(3000));
+}
+
void TestBlackbox::qbsSession()
{
QDir::setCurrent(testDataDir + "/qbs-session");
@@ -5466,11 +6567,7 @@ void TestBlackbox::qbsSession()
QVERIFY(sessionProc.waitForStarted());
const auto sendPacket = [&sessionProc](const QJsonObject &message) {
- const QByteArray data = QJsonDocument(message).toJson().toBase64();
- sessionProc.write("qbsmsg:");
- sessionProc.write(QByteArray::number(data.length()));
- sessionProc.write("\n");
- sessionProc.write(data);
+ sendSessionPacket(sessionProc, message);
};
static const auto envToJson = [](const QProcessEnvironment &env) {
@@ -5494,8 +6591,8 @@ void TestBlackbox::qbsSession()
// Wait for and verify hello packet.
QJsonObject receivedMessage = getNextSessionPacket(sessionProc, incomingData);
QCOMPARE(receivedMessage.value("type"), "hello");
- QCOMPARE(receivedMessage.value("api-level").toInt(), 1);
- QCOMPARE(receivedMessage.value("api-compat-level").toInt(), 1);
+ QCOMPARE(receivedMessage.value("api-level").toInt(), 5);
+ QCOMPARE(receivedMessage.value("api-compat-level").toInt(), 2);
// Resolve & verify structure
QJsonObject resolveMessage;
@@ -5508,8 +6605,9 @@ void TestBlackbox::qbsSession()
QJsonObject overriddenValues;
overriddenValues.insert("products.theLib.cpp.cxxLanguageVersion", "c++17");
resolveMessage.insert("overridden-properties", overriddenValues);
- resolveMessage.insert("environment", envToJson(QProcessEnvironment::systemEnvironment()));
+ resolveMessage.insert("environment", envToJson(QbsRunParameters::defaultEnvironment()));
resolveMessage.insert("data-mode", "only-if-changed");
+ resolveMessage.insert("max-job-count", 2);
resolveMessage.insert("log-time", true);
resolveMessage.insert("module-properties",
QJsonArray::fromStringList({"cpp.cxxLanguageVersion"}));
@@ -5553,7 +6651,7 @@ void TestBlackbox::qbsSession()
const QJsonObject group = v.toObject();
const QJsonArray sourceArtifacts
= group.value("source-artifacts").toArray();
- const auto findArtifact = [&sourceArtifacts](const QString fileName) {
+ const auto findArtifact = [&sourceArtifacts](const QString &fileName) {
for (const QJsonValue &v : sourceArtifacts) {
const QJsonObject artifact = v.toObject();
if (QFileInfo(artifact.value("file-path").toString()).fileName()
@@ -5815,7 +6913,7 @@ void TestBlackbox::qbsSession()
QCOMPARE(receivedMessage.value("type").toString(), QString("files-added"));
error = receivedMessage.value("error").toObject();
if (!error.isEmpty()) {
- for (const auto &item: error[QStringLiteral("items")].toArray()) {
+ for (const auto item: error[QStringLiteral("items")].toArray()) {
const auto description = QStringLiteral("Project file updates are not enabled");
if (item.toObject()[QStringLiteral("description")].toString().contains(description))
QSKIP("File updates are disabled");
@@ -5823,34 +6921,51 @@ void TestBlackbox::qbsSession()
qDebug() << error;
}
QVERIFY(error.isEmpty());
- QJsonObject projectData = receivedMessage.value("project-data").toObject();
- QJsonArray products = projectData.value("products").toArray();
- bool file1 = false;
- bool file2 = false;
- for (const QJsonValue &v : products) {
- const QJsonObject product = v.toObject();
- const QString productName = product.value("full-display-name").toString();
- const QJsonArray groups = product.value("groups").toArray();
- for (const QJsonValue &v : groups) {
- const QJsonObject group = v.toObject();
- const QString groupName = group.value("name").toString();
- const QJsonArray sourceArtifacts = group.value("source-artifacts").toArray();
- for (const QJsonValue &v : sourceArtifacts) {
- const QString filePath = v.toObject().value("file-path").toString();
- if (filePath.endsWith("file1.cpp")) {
- QCOMPARE(productName, QString("theLib"));
- QCOMPARE(groupName, QString("sources"));
- file1 = true;
- } else if (filePath.endsWith("file2.cpp")) {
- QCOMPARE(productName, QString("theLib"));
- QCOMPARE(groupName, QString("sources"));
- file2 = true;
+
+ receivedReply = false;
+ sendPacket(resolveMessage);
+ while (!receivedReply) {
+ receivedMessage = getNextSessionPacket(sessionProc, incomingData);
+ QVERIFY(!receivedMessage.isEmpty());
+ const QString msgType = receivedMessage.value("type").toString();
+ if (msgType == "project-resolved") {
+ receivedReply = true;
+ const QJsonObject error = receivedMessage.value("error").toObject();
+ if (!error.isEmpty())
+ qDebug() << error;
+ QVERIFY(error.isEmpty());
+ const QJsonObject projectData = receivedMessage.value("project-data").toObject();
+ QJsonArray products = projectData.value("products").toArray();
+ bool file1 = false;
+ bool file2 = false;
+ for (const QJsonValue v : products) {
+ const QJsonObject product = v.toObject();
+ const QString productName = product.value("full-display-name").toString();
+ const QJsonArray groups = product.value("groups").toArray();
+ for (const QJsonValue &v : groups) {
+ const QJsonObject group = v.toObject();
+ const QString groupName = group.value("name").toString();
+ const QJsonArray sourceArtifacts = group.value("source-artifacts").toArray();
+ for (const QJsonValue &v : sourceArtifacts) {
+ const QString filePath = v.toObject().value("file-path").toString();
+ if (filePath.endsWith("file1.cpp")) {
+ QCOMPARE(productName, QString("theLib"));
+ QCOMPARE(groupName, QString("sources"));
+ file1 = true;
+ } else if (filePath.endsWith("file2.cpp")) {
+ QCOMPARE(productName, QString("theLib"));
+ QCOMPARE(groupName, QString("sources"));
+ file2 = true;
+ }
+ }
}
}
+ QVERIFY(file1);
+ QVERIFY(file2);
}
}
- QVERIFY(file1);
- QVERIFY(file2);
+ QVERIFY(receivedReply);
+
receivedReply = false;
receivedProcessResult = false;
bool compiledFile1 = false;
@@ -5909,32 +7024,48 @@ void TestBlackbox::qbsSession()
if (!error.isEmpty())
qDebug() << error;
QVERIFY(error.isEmpty());
- projectData = receivedMessage.value("project-data").toObject();
- products = projectData.value("products").toArray();
- file1 = false;
- file2 = false;
- for (const QJsonValue &v : products) {
- const QJsonObject product = v.toObject();
- const QString productName = product.value("full-display-name").toString();
- const QJsonArray groups = product.value("groups").toArray();
- for (const QJsonValue &v : groups) {
- const QJsonObject group = v.toObject();
- const QString groupName = group.value("name").toString();
- const QJsonArray sourceArtifacts = group.value("source-artifacts").toArray();
- for (const QJsonValue &v : sourceArtifacts) {
- const QString filePath = v.toObject().value("file-path").toString();
- if (filePath.endsWith("file1.cpp")) {
- file1 = true;
- } else if (filePath.endsWith("file2.cpp")) {
- QCOMPARE(productName, QString("theLib"));
- QCOMPARE(groupName, QString("sources"));
- file2 = true;
+ receivedReply = false;
+ sendPacket(resolveMessage);
+ while (!receivedReply) {
+ receivedMessage = getNextSessionPacket(sessionProc, incomingData);
+ QVERIFY(!receivedMessage.isEmpty());
+ const QString msgType = receivedMessage.value("type").toString();
+ if (msgType == "project-resolved") {
+ receivedReply = true;
+ const QJsonObject error = receivedMessage.value("error").toObject();
+ if (!error.isEmpty())
+ qDebug() << error;
+ QVERIFY(error.isEmpty());
+ const QJsonObject projectData = receivedMessage.value("project-data").toObject();
+ QJsonArray products = projectData.value("products").toArray();
+ bool file1 = false;
+ bool file2 = false;
+ for (const QJsonValue v : products) {
+ const QJsonObject product = v.toObject();
+ const QString productName = product.value("full-display-name").toString();
+ const QJsonArray groups = product.value("groups").toArray();
+ for (const QJsonValue &v : groups) {
+ const QJsonObject group = v.toObject();
+ const QString groupName = group.value("name").toString();
+ const QJsonArray sourceArtifacts = group.value("source-artifacts").toArray();
+ for (const QJsonValue &v : sourceArtifacts) {
+ const QString filePath = v.toObject().value("file-path").toString();
+ if (filePath.endsWith("file1.cpp")) {
+ file1 = true;
+ } else if (filePath.endsWith("file2.cpp")) {
+ QCOMPARE(productName, QString("theLib"));
+ QCOMPARE(groupName, QString("sources"));
+ file2 = true;
+ }
+ }
}
}
+ QVERIFY(!file1);
+ QVERIFY(file2);
}
}
- QVERIFY(!file1);
- QVERIFY(file2);
+ QVERIFY(receivedReply);
+
receivedReply = false;
receivedProcessResult = false;
compiledFile1 = false;
@@ -6388,6 +7519,9 @@ void TestBlackbox::assembly()
void TestBlackbox::autotestWithDependencies()
{
QDir::setCurrent(testDataDir + "/autotest-with-dependencies");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(runQbs(QStringList({"-p", "autotest-runner"})), 0);
QVERIFY2(m_qbsStdout.contains("i am the test app")
&& m_qbsStdout.contains("i am the helper"), m_qbsStdout.constData());
@@ -6400,6 +7534,8 @@ void TestBlackbox::autotestTimeout()
QDir::setCurrent(testDataDir + "/autotest-timeout");
QbsRunParameters resolveParameters("resolve", resolveParams);
QCOMPARE(runQbs(resolveParameters), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters buildParameters(QStringList({"-p", "autotest-runner"}));
buildParameters.expectFailure = expectFailure;
if (expectFailure) {
@@ -6444,6 +7580,8 @@ void TestBlackbox::autotests()
if (!evilPropertySpec.isEmpty())
resolveParams.arguments << evilPropertySpec;
QCOMPARE(runQbs(resolveParams), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters testParams(QStringList{"-p", "autotest-runner"});
if (!evilPropertySpec.isEmpty())
testParams.expectFailure = true;
@@ -6452,9 +7590,9 @@ void TestBlackbox::autotests()
QVERIFY2(m_qbsStderr.contains(expectedErrorMessage), m_qbsStderr.constData());
return;
}
- QVERIFY2(m_qbsStdout.contains("Running test test1")
- && m_qbsStdout.contains("Running test test2")
- && m_qbsStdout.contains("Running test test3"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("running test test1")
+ && m_qbsStdout.contains("running test test2")
+ && m_qbsStdout.contains("running test test3"), m_qbsStdout.constData());
QCOMPARE(m_qbsStdout.count("PASS"), 2);
QCOMPARE(m_qbsStderr.count("FAIL"), 1);
}
@@ -6543,9 +7681,9 @@ static bool haveMakeNsis()
<< QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\NSIS");
QStringList paths = QProcessEnvironment::systemEnvironment().value("PATH")
- .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+ .split(HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts);
- for (const QString &key : qAsConst(regKeys)) {
+ for (const QString &key : std::as_const(regKeys)) {
QSettings settings(key, QSettings::NativeFormat);
QString str = settings.value(QStringLiteral(".")).toString();
if (!str.isEmpty())
@@ -6553,7 +7691,7 @@ static bool haveMakeNsis()
}
bool haveMakeNsis = false;
- for (const QString &path : qAsConst(paths)) {
+ for (const QString &path : std::as_const(paths)) {
if (regularFileExists(QDir::fromNativeSeparators(path) +
HostOsInfo::appendExecutableSuffix(QStringLiteral("/makensis")))) {
haveMakeNsis = true;
@@ -6678,12 +7816,16 @@ void TestBlackbox::enableRtti()
void TestBlackbox::envMerging()
{
QDir::setCurrent(testDataDir + "/env-merging");
- QbsRunParameters params;
+ QbsRunParameters params("resolve");
QString pathVal = params.environment.value("PATH");
pathVal.prepend(HostOsInfo::pathListSeparator()).prepend("/opt/blackbox/bin");
const QString keyName = HostOsInfo::isWindowsHost() ? "pATh" : "PATH";
params.environment.insert(keyName, pathVal);
QCOMPARE(runQbs(params), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+ params.command = "build";
+ QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains(QByteArray("PATH=/opt/tool/bin")
+ HostOsInfo::pathListSeparator().toLatin1())
&& m_qbsStdout.contains(HostOsInfo::pathListSeparator().toLatin1()
@@ -6774,98 +7916,10 @@ void TestBlackbox::generator_data()
QTest::newRow("no update") << QString() << QStringList();
}
-static bool haveWiX(const Profile &profile)
-{
- if (profile.value("wix.toolchainInstallPath").isValid() &&
- profile.value("wix.toolchainInstallRoot").isValid()) {
- return true;
- }
-
- QStringList regKeys;
- regKeys << QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows Installer XML\\")
- << QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Installer XML\\");
-
- QStringList paths = QProcessEnvironment::systemEnvironment().value("PATH")
- .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
-
- for (const QString &key : qAsConst(regKeys)) {
- const QStringList versions = QSettings(key, QSettings::NativeFormat).childGroups();
- for (const QString &version : versions) {
- QSettings settings(key + version, QSettings::NativeFormat);
- QString str = settings.value(QStringLiteral("InstallRoot")).toString();
- if (!str.isEmpty())
- paths.prepend(str);
- }
- }
-
- for (const QString &path : qAsConst(paths)) {
- if (regularFileExists(QDir::fromNativeSeparators(path) +
- HostOsInfo::appendExecutableSuffix(QStringLiteral("/candle"))) &&
- regularFileExists(QDir::fromNativeSeparators(path) +
- HostOsInfo::appendExecutableSuffix(QStringLiteral("/light")))) {
- return true;
- }
- }
-
- return false;
-}
-
-void TestBlackbox::wix()
-{
- const SettingsPtr s = settings();
- Profile profile(profileName(), s.get());
-
- if (!haveWiX(profile)) {
- QSKIP("WiX is not installed");
- return;
- }
-
- QByteArray arch = profile.value("qbs.architecture").toString().toLatin1();
- if (arch.isEmpty())
- arch = QByteArrayLiteral("x86");
-
- QDir::setCurrent(testDataDir + "/wix");
- QCOMPARE(runQbs(), 0);
- QVERIFY2(m_qbsStdout.contains("compiling QbsSetup.wxs"), m_qbsStdout);
- QVERIFY2(m_qbsStdout.contains("linking qbs.msi"), m_qbsStdout);
- QVERIFY(regularFileExists(relativeProductBuildDir("QbsSetup") + "/qbs.msi"));
-
- if (HostOsInfo::isWindowsHost()) {
- QVERIFY2(m_qbsStdout.contains("compiling QbsBootstrapper.wxs"), m_qbsStdout);
- QVERIFY2(m_qbsStdout.contains("linking qbs-setup-" + arch + ".exe"), m_qbsStdout);
- QVERIFY(regularFileExists(relativeProductBuildDir("QbsBootstrapper")
- + "/qbs-setup-" + arch + ".exe"));
- }
-}
-
-void TestBlackbox::wixDependencies()
-{
- const SettingsPtr s = settings();
- Profile profile(profileName(), s.get());
-
- if (!haveWiX(profile)) {
- QSKIP("WiX is not installed");
- return;
- }
-
- QByteArray arch = profile.value("qbs.architecture").toString().toLatin1();
- if (arch.isEmpty())
- arch = QByteArrayLiteral("x86");
-
- QDir::setCurrent(testDataDir + "/wixDependencies");
- QbsRunParameters params;
- if (!HostOsInfo::isWindowsHost())
- params.arguments << "qbs.targetOS:windows";
- QCOMPARE(runQbs(params), 0);
- QVERIFY2(m_qbsStdout.contains("compiling QbsSetup.wxs"), m_qbsStdout);
- QVERIFY2(m_qbsStdout.contains("linking qbs.msi"), m_qbsStdout);
- QVERIFY(regularFileExists(relativeBuildDir() + "/qbs.msi"));
-}
-
void TestBlackbox::nodejs()
{
const SettingsPtr s = settings();
- Profile p(profileName(), s.get());
+ qbs::Profile p(profileName(), s.get());
int status;
findNodejs(&status);
@@ -6884,6 +7938,13 @@ void TestBlackbox::nodejs()
QSKIP("nodejs.packageManagerFilePath not set and automatic detection failed");
}
+ if (p.value("nodejs.interpreterFilePath").toString().isEmpty()
+ && status != 0 && m_qbsStderr.contains("interpreterPath")) {
+ QSKIP("nodejs.interpreterFilePath not set and automatic detection failed");
+ }
+
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(status, 0);
QbsRunParameters params;
@@ -6895,8 +7956,11 @@ void TestBlackbox::nodejs()
void TestBlackbox::typescript()
{
+ if (qEnvironmentVariableIsSet("GITHUB_ACTIONS"))
+ QSKIP("Skip this test when running on GitHub");
+
const SettingsPtr s = settings();
- Profile p(profileName(), s.get());
+ qbs::Profile p(profileName(), s.get());
int status;
findTypeScript(&status);
@@ -6930,6 +7994,12 @@ void TestBlackbox::typescript()
QVERIFY(regularFileExists(relativeProductBuildDir("animals") + "/main.js"));
}
+void TestBlackbox::undefinedTargetPlatform()
+{
+ QDir::setCurrent(testDataDir + "/undefined-target-platform");
+ QCOMPARE(runQbs(), 0);
+}
+
void TestBlackbox::importInPropertiesCondition()
{
QDir::setCurrent(testDataDir + "/import-in-properties-condition");
@@ -6959,77 +8029,15 @@ void TestBlackbox::importsConflict()
void TestBlackbox::includeLookup()
{
QDir::setCurrent(testDataDir + "/includeLookup");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters params;
params.command = "run";
QCOMPARE(runQbs(params), 0);
QVERIFY2(m_qbsStdout.contains("definition.."), m_qbsStdout.constData());
}
-static bool haveInnoSetup(const Profile &profile)
-{
- if (profile.value("innosetup.toolchainInstallPath").isValid())
- return true;
-
- QStringList regKeys;
- regKeys << QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Inno Setup 5_is1")
- << QStringLiteral("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 : qAsConst(regKeys)) {
- QSettings settings(key, QSettings::NativeFormat);
- QString str = settings.value(QStringLiteral("InstallLocation")).toString();
- if (!str.isEmpty())
- paths.prepend(str);
- }
-
- for (const QString &path : qAsConst(paths)) {
- if (regularFileExists(QDir::fromNativeSeparators(path) +
- HostOsInfo::appendExecutableSuffix(QStringLiteral("/ISCC"))))
- return true;
- }
-
- return false;
-}
-
-void TestBlackbox::innoSetup()
-{
- const SettingsPtr s = settings();
- Profile profile(profileName(), s.get());
-
- if (!haveInnoSetup(profile)) {
- QSKIP("Inno Setup is not installed");
- return;
- }
-
- QDir::setCurrent(testDataDir + "/innosetup");
- QCOMPARE(runQbs(), 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::innoSetupDependencies()
-{
- const SettingsPtr s = settings();
- Profile profile(profileName(), s.get());
-
- if (!haveInnoSetup(profile)) {
- QSKIP("Inno Setup is not installed");
- return;
- }
-
- QDir::setCurrent(testDataDir + "/innosetupDependencies");
- QbsRunParameters params;
- if (!HostOsInfo::isWindowsHost())
- params.arguments << "qbs.targetOS:windows";
- QCOMPARE(runQbs(params), 0);
- QVERIFY(m_qbsStdout.contains("compiling test.iss"));
- QVERIFY(regularFileExists(relativeBuildDir() + "/qbs.setup.test.exe"));
-}
-
void TestBlackbox::inputTagsChangeTracking_data()
{
QTest::addColumn<QString>("generateInput");
@@ -7091,7 +8099,7 @@ void TestBlackbox::wildCardsAndRules()
{
QDir::setCurrent(testDataDir + "/wildcards-and-rules");
QCOMPARE(runQbs(), 0);
- QVERIFY(m_qbsStdout.contains("Creating output artifact"));
+ 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);
@@ -7103,7 +8111,7 @@ void TestBlackbox::wildCardsAndRules()
QbsRunParameters params;
params.expectFailure = true;
QCOMPARE(runQbs(params), 0);
- QVERIFY(m_qbsStdout.contains("Creating output artifact"));
+ QVERIFY(m_qbsStdout.contains("creating output artifact"));
QVERIFY2(output.open(QIODevice::ReadOnly), qPrintable(output.errorString()));
QCOMPARE(output.readAll().count('\n'), 2);
output.close();
@@ -7112,17 +8120,49 @@ void TestBlackbox::wildCardsAndRules()
WAIT_FOR_NEW_TIMESTAMP();
touch("dep.dep");
QCOMPARE(runQbs(), 0);
- QVERIFY(m_qbsStdout.contains("Creating output artifact"));
+ QVERIFY(m_qbsStdout.contains("creating output artifact"));
// Add nothing.
QCOMPARE(runQbs(), 0);
- QVERIFY(!m_qbsStdout.contains("Creating output artifact"));
+ QVERIFY(!m_qbsStdout.contains("creating output artifact"));
+}
+
+void TestBlackbox::wildCardsAndChangeTracking_data()
+{
+ QTest::addColumn<QString>("dirToModify");
+ QTest::addColumn<bool>("expectReResolve");
+
+ QTest::newRow("root path") << QString(".") << false;
+ QTest::newRow("dir with recursive match") << QString("recursive1") << false;
+ QTest::newRow("non-recursive base dir") << QString("nonrecursive") << true;
+ QTest::newRow("empty base dir with file patterns") << QString("nonrecursive/empty") << true;
+}
+
+void TestBlackbox::wildCardsAndChangeTracking()
+{
+ QFETCH(QString, dirToModify);
+ QFETCH(bool, expectReResolve);
+
+ const QString srcDir = testDataDir + "/wildcards-and-change-tracking";
+ QDir::setCurrent(srcDir);
+ rmDirR("default");
+ QDir::current().mkdir("nonrecursive/empty");
+
+ QCOMPARE(runQbs({"resolve"}), 0);
+ QVERIFY2(m_qbsStdout.contains("Resolving"), m_qbsStdout.constData());
+ WAIT_FOR_NEW_TIMESTAMP();
+ touch(dirToModify + "/blubb.txt");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ QCOMPARE(m_qbsStdout.contains("Resolving"), expectReResolve);
}
void TestBlackbox::loadableModule()
{
QDir::setCurrent(testDataDir + QLatin1String("/loadablemodule"));
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters params;
params.command = "run";
QCOMPARE(runQbs(params), 0);
@@ -7136,6 +8176,10 @@ void TestBlackbox::localDeployment()
QVERIFY(main.open(QIODevice::ReadOnly));
QByteArray content = main.readAll();
content.replace('\r', "");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+
QbsRunParameters params;
params.command = "run";
QCOMPARE(runQbs(params), 0);
@@ -7149,6 +8193,8 @@ void TestBlackbox::makefileGenerator()
QCOMPARE(runQbs(params), 0);
if (HostOsInfo::isWindowsHost())
return;
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QProcess make;
make.setWorkingDirectory(QDir::currentPath() + '/' + relativeBuildDir());
const QString customInstallRoot = QDir::currentPath() + "/my-install-root";
@@ -7157,7 +8203,7 @@ void TestBlackbox::makefileGenerator()
QVERIFY(QFile::exists(relativeExecutableFilePath("the app")));
QVERIFY(!QFile::exists(relativeBuildGraphFilePath()));
QProcess app;
- app.start('"' + customInstallRoot + "/usr/local/bin/the app\"");
+ app.start(customInstallRoot + "/usr/local/bin/the app", QStringList());
QVERIFY(waitForProcessSuccess(app));
const QByteArray appStdout = app.readAllStandardOutput();
QVERIFY2(appStdout.contains("Hello, World!"), appStdout.constData());
@@ -7171,14 +8217,18 @@ void TestBlackbox::maximumCLanguageVersion()
QDir::setCurrent(testDataDir + "/maximum-c-language-version");
QCOMPARE(runQbs(QbsRunParameters("resolve",
QStringList("products.app.enableNewestModule:true"))), 0);
- if (m_qbsStdout.contains("is msvc"))
- QSKIP("MSVC has no support for setting the C language version.");
+ const bool isMsvc = m_qbsStdout.contains("is msvc: true");
+ if (isMsvc && m_qbsStdout.contains("is old msvc: true"))
+ QSKIP("MSVC supports setting the C language version only from version 16.8, and Clang from version 13.");
QCOMPARE(runQbs(QStringList({"--command-echo-mode", "command-line", "-n"})), 0);
QVERIFY2(m_qbsStdout.contains("c11") || m_qbsStdout.contains("c1x"), m_qbsStdout.constData());
QCOMPARE(runQbs(QbsRunParameters("resolve",
QStringList("products.app.enableNewestModule:false"))), 0);
QCOMPARE(runQbs(QStringList({"--command-echo-mode", "command-line", "-n"})), 0);
- QVERIFY2(m_qbsStdout.contains("c99"), m_qbsStdout.constData());
+ if (isMsvc)
+ QVERIFY2(!m_qbsStdout.contains("c11"), m_qbsStdout.constData());
+ else
+ QVERIFY2(m_qbsStdout.contains("c99"), m_qbsStdout.constData());
}
void TestBlackbox::maximumCxxLanguageVersion()
@@ -7187,7 +8237,7 @@ void TestBlackbox::maximumCxxLanguageVersion()
QCOMPARE(runQbs(QbsRunParameters("resolve",
QStringList("products.app.enableNewestModule:true"))), 0);
QCOMPARE(runQbs(QStringList({"--command-echo-mode", "command-line", "-n"})), 0);
- QVERIFY2(m_qbsStdout.contains("c++17") || m_qbsStdout.contains("c++1z")
+ QVERIFY2(m_qbsStdout.contains("c++23") || m_qbsStdout.contains("c++2b")
|| m_qbsStdout.contains("c++latest"), m_qbsStdout.constData());
QCOMPARE(runQbs(QbsRunParameters("resolve",
QStringList("products.app.enableNewestModule:false"))), 0);
@@ -7196,107 +8246,6 @@ void TestBlackbox::maximumCxxLanguageVersion()
m_qbsStdout.constData());
}
-void TestBlackbox::moduleProviders()
-{
- QDir::setCurrent(testDataDir + "/module-providers");
-
- // Resolving in dry-run mode must not leave any data behind.
- QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0);
- QCOMPARE(m_qbsStdout.count("Running setup script for mygenerator"), 2);
- QVERIFY(!QFile::exists(relativeBuildDir()));
-
- // Initial build.
- QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app1"})), 0);
- QVERIFY(QFile::exists(relativeBuildDir()));
- QCOMPARE(m_qbsStdout.count("Running setup script for mygenerator"), 2);
- QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
- QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app2"})), 0);
- QVERIFY2(m_qbsStdout.contains("The letters are Z and Y"), m_qbsStdout.constData());
-
- // Rebuild with overridden module provider config. The output for product 2 must change,
- // but no setup script must be re-run, because both config values have already been
- // handled in the first run.
- const QStringList resolveArgs("moduleProviders.mygenerator.chooseLettersFrom:beginning");
- QCOMPARE(runQbs(QbsRunParameters("resolve", resolveArgs)), 0);
- QVERIFY2(!m_qbsStdout.contains("Running setup script"), m_qbsStdout.constData());
- QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app1"})), 0);
- QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
- QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app2"})), 0);
- QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
-
- // Forcing Probe execution triggers a re-run of the setup script. But only once,
- // because the module provider config is the same now.
- QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList(resolveArgs)
- << "--force-probe-execution")), 0);
- QCOMPARE(m_qbsStdout.count("Running setup script for mygenerator"), 1);
- QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app1"})), 0);
- QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
- QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app2"})), 0);
- QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
-
- // Now re-run without the module provider config override. Again, the setup script must
- // run once, for the config value that was not present in the last run.
- QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
- QCOMPARE(m_qbsStdout.count("Running setup script for mygenerator"), 1);
- QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app1"})), 0);
- QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
- QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app2"})), 0);
- QVERIFY2(m_qbsStdout.contains("The letters are Z and Y"), m_qbsStdout.constData());
-}
-
-void TestBlackbox::fallbackModuleProvider_data()
-{
- QTest::addColumn<bool>("fallbacksEnabledGlobally");
- QTest::addColumn<bool>("fallbacksEnabledInProduct");
- QTest::addColumn<QStringList>("pkgConfigLibDirs");
- QTest::addColumn<bool>("successExpected");
- QTest::newRow("without custom lib dir, fallbacks disabled globally and in product")
- << false << false << QStringList() << false;
- QTest::newRow("without custom lib dir, fallbacks disabled globally, enabled in product")
- << false << true << QStringList() << false;
- QTest::newRow("without custom lib dir, fallbacks enabled globally, disabled in product")
- << true << false << QStringList() << false;
- QTest::newRow("without custom lib dir, fallbacks enabled globally and in product")
- << true << true << QStringList() << false;
- QTest::newRow("with custom lib dir, fallbacks disabled globally and in product")
- << false << false << QStringList(testDataDir + "/fallback-module-provider/libdir")
- << false;
- QTest::newRow("with custom lib dir, fallbacks disabled globally, enabled in product")
- << false << true << QStringList(testDataDir + "/fallback-module-provider/libdir")
- << false;
- QTest::newRow("with custom lib dir, fallbacks enabled globally, disabled in product")
- << true << false << QStringList(testDataDir + "/fallback-module-provider/libdir")
- << false;
- QTest::newRow("with custom lib dir, fallbacks enabled globally and in product")
- << true << true << QStringList(testDataDir + "/fallback-module-provider/libdir")
- << true;
-}
-
-void TestBlackbox::fallbackModuleProvider()
-{
- QFETCH(bool, fallbacksEnabledInProduct);
- QFETCH(bool, fallbacksEnabledGlobally);
- QFETCH(QStringList, pkgConfigLibDirs);
- QFETCH(bool, successExpected);
- QDir::setCurrent(testDataDir + "/fallback-module-provider");
- static const auto b2s = [](bool b) { return QString(b ? "true" : "false"); };
- QbsRunParameters resolveParams("resolve",
- QStringList{"modules.pkgconfig.libDirs:" + pkgConfigLibDirs.join(','),
- "products.p.fallbacksEnabled:" + b2s(fallbacksEnabledInProduct),
- "--force-probe-execution"});
- if (!fallbacksEnabledGlobally)
- resolveParams.arguments << "--no-fallback-module-provider";
- QCOMPARE(runQbs(resolveParams), 0);
- const bool pkgConfigPresent = m_qbsStdout.contains("pkg-config present: true");
- const bool pkgConfigNotPresent = m_qbsStdout.contains("pkg-config present: false");
- QVERIFY(pkgConfigPresent != pkgConfigNotPresent);
- if (pkgConfigNotPresent)
- successExpected = false;
- QbsRunParameters buildParams;
- buildParams.expectFailure = !successExpected;
- QCOMPARE(runQbs(buildParams) == 0, successExpected);
-}
-
void TestBlackbox::minimumSystemVersion()
{
rmDirR(relativeBuildDir());
@@ -7304,6 +8253,10 @@ void TestBlackbox::minimumSystemVersion()
QFETCH(QString, file);
QFETCH(QString, output);
QbsRunParameters params({ "-f", file + ".qbs" });
+ params.command = "resolve";
+ QCOMPARE(runQbs(params), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
params.command = "run";
QCOMPARE(runQbs(params), 0);
if (m_qbsStdout.contains("Unsupported compiler"))
@@ -7385,7 +8338,7 @@ void TestBlackbox::minimumSystemVersion_data()
return "__MAC_OS_X_VERSION_MIN_REQUIRED=1070\nversion 10.7\n";
if (HostOsInfo::isWindowsHost())
- return "WINVER=1536\n6.00 operating system version\n6.00 subsystem version\n";
+ return "WINVER=1538\n6.02 operating system version\n6.02 subsystem version\n";
return "";
}();
@@ -7413,7 +8366,7 @@ void TestBlackbox::missingBuildGraph()
QbsRunParameters params;
params.expectFailure = true;
params.arguments << QLatin1String("config:") + actualConfigName;
- for (const QString &command : qAsConst(commands)) {
+ for (const QString &command : std::as_const(commands)) {
params.command = command;
QVERIFY2(runQbs(params) != 0, qPrintable(command));
const QString expectedErrorMessage = QString("Build graph not found for "
@@ -7523,6 +8476,12 @@ void TestBlackbox::movedFileDependency()
QVERIFY2(!m_qbsStdout.contains("compiling main.cpp"), m_qbsStdout.constData());
}
+void TestBlackbox::msvcAsmLinkerFlags()
+{
+ QDir::setCurrent(testDataDir + "/msvc-asm-flags");
+ QCOMPARE(runQbs(), 0);
+}
+
void TestBlackbox::badInterpreter()
{
if (!HostOsInfo::isAnyUnixHost())
@@ -7531,16 +8490,19 @@ void TestBlackbox::badInterpreter()
QDir::setCurrent(testDataDir + QLatin1String("/badInterpreter"));
QCOMPARE(runQbs(), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+
QbsRunParameters params("run");
params.expectFailure = true;
- const QRegExp reNoSuchFileOrDir("bad interpreter:.* No such file or directory");
- const QRegExp rePermissionDenied("bad interpreter:.* Permission denied");
+ const QRegularExpression reNoSuchFileOrDir("bad interpreter:.* No such file or directory");
+ const QRegularExpression 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));
+ QVERIFY2(strerr.contains(reNoSuchFileOrDir), m_qbsStderr);
params.arguments = QStringList() << "-p" << "script-interp-noexec";
QCOMPARE(runQbs(params), 1);
@@ -7609,6 +8571,16 @@ void TestBlackbox::qbsVersion()
QVERIFY(runQbs(params) != 0);
}
+void TestBlackbox::transitiveInvalidDependencies()
+{
+ QDir::setCurrent(testDataDir + "/transitive-invalid-dependencies");
+ QbsRunParameters params;
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY2(m_qbsStdout.contains("b.present = false"), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains("c.present = true"), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains("d.present = false"), m_qbsStdout);
+}
+
void TestBlackbox::transitiveOptionalDependencies()
{
QDir::setCurrent(testDataDir + "/transitive-optional-dependencies");
@@ -7619,19 +8591,23 @@ void TestBlackbox::transitiveOptionalDependencies()
void TestBlackbox::groupsInModules()
{
QDir::setCurrent(testDataDir + "/groups-in-modules");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
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 rock.coal to rock.diamond"));
+ QVERIFY(m_qbsStdout.contains("compiling chunk.coal to 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"));
+ QVERIFY(m_qbsStdout.contains("compiling helper7.c"));
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 rock.coal to rock.diamond"));
+ QVERIFY(!m_qbsStdout.contains("compiling chunk.coal to chunk.diamond"));
WAIT_FOR_NEW_TIMESTAMP();
touch("modules/helper/diamondc.c");
@@ -7639,8 +8615,8 @@ void TestBlackbox::groupsInModules()
waitForFileUnlock();
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(m_qbsStdout.contains("compiling rock.coal to rock.diamond"));
+ QVERIFY(m_qbsStdout.contains("compiling chunk.coal to chunk.diamond"));
QVERIFY(regularFileExists(relativeProductBuildDir("groups-in-modules") + "/rock.diamond"));
QFile output(relativeProductBuildDir("groups-in-modules") + "/rock.diamond");
QVERIFY(output.open(QIODevice::ReadOnly));
@@ -7650,15 +8626,35 @@ void TestBlackbox::groupsInModules()
void TestBlackbox::grpc_data()
{
QTest::addColumn<QString>("projectFile");
- QTest::newRow("cpp") << QString("grpc_cpp.qbs");
+ QTest::addColumn<QStringList>("arguments");
+ QTest::addColumn<bool>("hasModules");
+
+ QStringList pkgConfigArgs({"project.qbsModuleProviders:qbspkgconfig"});
+ // on macOS, openSSL is hidden from pkg-config by default
+ if (qbs::Internal::HostOsInfo::isMacosHost()) {
+ pkgConfigArgs
+ << "moduleProviders.qbspkgconfig.extraPaths:/usr/local/opt/openssl@1.1/lib/pkgconfig";
+ }
+ QTest::newRow("cpp-pkgconfig") << QString("grpc_cpp.qbs") << pkgConfigArgs << true;
}
void TestBlackbox::grpc()
{
QDir::setCurrent(testDataDir + "/grpc");
QFETCH(QString, projectFile);
+ QFETCH(QStringList, arguments);
+ QFETCH(bool, hasModules);
+
rmDirR(relativeBuildDir());
QbsRunParameters resolveParams("resolve", QStringList{"-f", projectFile});
+ if (QTest::currentDataTag() == QLatin1String("cpp")) {
+ if (const QString extraLibs = qEnvironmentVariable("QBS_EXTRA_GRPC_LIBS");
+ !extraLibs.isEmpty()) {
+ resolveParams.arguments << (QLatin1String("modules.protobuf.cpp._extraGrpcLibs:")
+ + extraLibs);
+ }
+ }
+ resolveParams.arguments << arguments;
QCOMPARE(runQbs(resolveParams), 0);
const bool withGrpc = m_qbsStdout.contains("has grpc: true");
const bool withoutGrpc = m_qbsStdout.contains("has grpc: false");
@@ -7666,10 +8662,33 @@ void TestBlackbox::grpc()
if (withoutGrpc)
QSKIP("grpc module not present");
+ const bool hasMods = m_qbsStdout.contains("has modules: true");
+ const bool dontHaveMods = m_qbsStdout.contains("has modules: false");
+ QVERIFY2(hasMods == !dontHaveMods, m_qbsStdout.constData());
+ QCOMPARE(hasMods, hasModules);
+
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+
QbsRunParameters runParams;
QCOMPARE(runQbs(runParams), 0);
}
+void TestBlackbox::hostOsProperties()
+{
+ QDir::setCurrent(testDataDir + "/host-os-properties");
+ QCOMPARE(runQbs(QStringLiteral("resolve")), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+ QCOMPARE(runQbs(QStringLiteral("run")), 0);
+ QVERIFY2(m_qbsStdout.contains(
+ ("HOST_ARCHITECTURE = " + HostOsInfo::hostOSArchitecture().toUtf8()).data()),
+ m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains(
+ ("HOST_PLATFORM = " + HostOsInfo::hostOSIdentifier().toUtf8()).data()),
+ m_qbsStdout.constData());
+}
+
void TestBlackbox::ico()
{
QDir::setCurrent(testDataDir + "/ico");
@@ -7747,8 +8766,8 @@ void TestBlackbox::ico()
QCOMPARE(b.at(12), '\0');
QCOMPARE(b.at(26), '\0');
QCOMPARE(b.at(28), '\0');
- QWARN("this version of icoutil does not support setting the hotspot "
- "for cursor files with multiple images");
+ qWarning() << "this version of icoutil does not support setting the hotspot "
+ "for cursor files with multiple images";
} else {
QCOMPARE(b.at(10), '\x8');
QCOMPARE(b.at(12), '\x9');
diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h
index 4e2755724..5ba53ef52 100644
--- a/tests/auto/blackbox/tst_blackbox.h
+++ b/tests/auto/blackbox/tst_blackbox.h
@@ -40,6 +40,8 @@ public:
TestBlackbox();
private slots:
+ void allowedValues();
+ void allowedValues_data();
void addFileTagToGeneratedArtifact();
void alwaysRun();
void alwaysRun_data();
@@ -60,6 +62,10 @@ private slots:
void buildDirectories();
void buildEnvChange();
void buildGraphVersions();
+ void buildVariantDefaults_data();
+ void buildVariantDefaults();
+ void capnproto();
+ void capnproto_data();
void changedFiles_data();
void changedFiles();
void changedInputsFromDependencies();
@@ -76,26 +82,33 @@ private slots:
void combinedSources();
void commandFile();
void compilerDefinesByLanguage();
- void concurrentExecutor();
void conditionalExport();
void conditionalFileTagger();
void configure();
void conflictingArtifacts();
void cxxLanguageVersion();
void cxxLanguageVersion_data();
+ void conanfileProbe_data();
+ void conanfileProbe();
+ void conflictingPropertyValues_data();
+ void conflictingPropertyValues();
void cpuFeatures();
+ void dateProperty();
void dependenciesProperty();
- void dependencyProfileMismatch();
+ void dependencyScanningLoop();
void deprecatedProperty();
+ void deprecatedProperty_data();
void disappearedProfile();
void discardUnusedData();
void discardUnusedData_data();
+ void dotDotPcFile();
void driverLinkerFlags();
void driverLinkerFlags_data();
void dynamicLibraryInModule();
void dynamicMultiplexRule();
void dynamicProject();
void dynamicRuleOutputs();
+ void emptyProfile();
void enableExceptions();
void enableExceptions_data();
void enableRtti();
@@ -113,11 +126,14 @@ private slots:
void exportedPropertyInDisabledProduct_data();
void exportRule();
void exportToOutsideSearchPath();
+ void exportsCMake();
+ void exportsCMake_data();
void exportsPkgconfig();
void exportsQbs();
void externalLibs();
void fileDependencies();
void fileTagsFilterMerging();
+ void freedesktop();
void generatedArtifactAsInputToDynamicRule();
void generateLinkerMapFile();
void generator();
@@ -125,6 +141,7 @@ private slots:
void groupsInModules();
void grpc_data();
void grpc();
+ void hostOsProperties();
void ico();
void importAssignment();
void importChangeTracking();
@@ -133,8 +150,6 @@ private slots:
void importingProduct();
void importsConflict();
void includeLookup();
- void innoSetup();
- void innoSetupDependencies();
void inputTagsChangeTracking_data();
void inputTagsChangeTracking();
void inputsFromDependencies();
@@ -159,11 +174,13 @@ private slots:
void invalidLibraryNames_data();
void jsExtensionsFile();
void jsExtensionsFileInfo();
+ void jsExtensionsHost();
void jsExtensionsProcess();
void jsExtensionsPropertyList();
void jsExtensionsTemporaryDir();
void jsExtensionsTextFile();
void jsExtensionsBinaryFile();
+ void lastModuleCandidateBroken();
void ld();
void linkerMode();
void linkerVariant_data();
@@ -174,6 +191,7 @@ private slots:
void linkerLibraryDuplicates();
void linkerLibraryDuplicates_data();
void linkerScripts();
+ void linkerModuleDefinition();
void listProducts();
void listPropertiesWithOuter();
void listPropertyOrder();
@@ -182,9 +200,6 @@ private slots:
void makefileGenerator();
void maximumCLanguageVersion();
void maximumCxxLanguageVersion();
- void moduleProviders();
- void fallbackModuleProvider_data();
- void fallbackModuleProvider();
void minimumSystemVersion();
void minimumSystemVersion_data();
void missingBuildGraph();
@@ -194,6 +209,7 @@ private slots:
void missingOverridePrefix();
void moduleConditions();
void movedFileDependency();
+ void msvcAsmLinkerFlags();
void multipleChanges();
void multipleConfigurations();
void multiplexedTool();
@@ -216,6 +232,7 @@ private slots:
void overrideProjectProperties();
void pathProbe_data();
void pathProbe();
+ void pathListInProbe();
void pchChangeTracking();
void perGroupDefineInExportItem();
void pkgConfigProbe();
@@ -232,17 +249,27 @@ private slots:
void probesAndArrayProperties();
void probesInNestedModules();
void productDependenciesByType();
+ void productInExportedModule();
void productProperties();
- void propertyAssignmentOnNonPresentModule();
void propertyAssignmentInFailedModule();
void propertyChanges();
+ void propertyEvaluationContext();
void propertyPrecedence();
void properQuoting();
void propertiesInExportItems();
void protobuf_data();
void protobuf();
+ void protobufLibraryInstall();
void pseudoMultiplexing();
void qbsConfig();
+ void qbsConfigAddProfile();
+ void qbsConfigAddProfile_data();
+ void qbsConfigImport();
+ void qbsConfigImport_data();
+ void qbsConfigExport();
+ void qbsConfigExport_data();
+ void qbsLanguageServer_data();
+ void qbsLanguageServer();
void qbsSession();
void qbsVersion();
void qtBug51237();
@@ -256,16 +283,21 @@ private slots:
void reproducibleBuild();
void reproducibleBuild_data();
void require();
- void requireDeprecated();
void rescueTransformerData();
void responseFiles();
void retaggedOutputArtifact();
+ void rpathlinkDeduplication();
void ruleConditions();
void ruleConnectionWithExcludedInputs();
void ruleCycle();
void ruleWithNoInputs();
void ruleWithNonRequiredInputs();
+ void runMultiplexed();
+ void sanitizer_data();
+ void sanitizer();
void scannerItem();
+ void scanResultInOtherProduct();
+ void scanResultInNonDependency();
void setupBuildEnvironment();
void setupRunEnvironment();
void smartRelinking();
@@ -285,6 +317,7 @@ private slots:
void suspiciousCalls();
void suspiciousCalls_data();
void systemIncludePaths();
+ void distributionIncludePaths();
void systemRunPaths();
void systemRunPaths_data();
void tar();
@@ -299,8 +332,10 @@ private slots:
void trackRemoveFile();
void trackRemoveFileTag();
void trackRemoveProduct();
+ void transitiveInvalidDependencies();
void transitiveOptionalDependencies();
void typescript();
+ void undefinedTargetPlatform();
void usingsAsSoleInputsNonMultiplexed();
void variantSuffix();
void variantSuffix_data();
@@ -312,9 +347,9 @@ private slots:
void wholeArchive();
void wholeArchive_data();
void wildCardsAndRules();
+ void wildCardsAndChangeTracking_data();
+ void wildCardsAndChangeTracking();
void wildcardRenaming();
- void wix();
- void wixDependencies();
void zip();
void zip_data();
void zipInvalid();
diff --git a/tests/auto/blackbox/tst_blackboxandroid.cpp b/tests/auto/blackbox/tst_blackboxandroid.cpp
index e312c4493..a74d9415d 100644
--- a/tests/auto/blackbox/tst_blackboxandroid.cpp
+++ b/tests/auto/blackbox/tst_blackboxandroid.cpp
@@ -58,6 +58,7 @@ QMap<QString, QString> TestBlackboxAndroid::findAndroid(int *status, const QStri
return {
{"sdk", QDir::fromNativeSeparators(tools["sdk"].toString())},
{"sdk-build-tools-dx", QDir::fromNativeSeparators(tools["sdk-build-tools-dx"].toString())},
+ {"sdk-build-tools-d8", QDir::fromNativeSeparators(tools["sdk-build-tools-d8"].toString())},
{"ndk", QDir::fromNativeSeparators(tools["ndk"].toString())},
{"ndk-samples", QDir::fromNativeSeparators(tools["ndk-samples"].toString())},
{"jar", QDir::fromNativeSeparators(tools["jar"].toString())},
@@ -80,11 +81,19 @@ void TestBlackboxAndroid::android()
QFETCH(QStringList, productNames);
QFETCH(QList<QByteArrayList>, expectedFilesLists);
QFETCH(QStringList, customProperties);
+ QFETCH(bool, enableAapt2);
+ QFETCH(bool, generateAab);
+ QFETCH(bool, isIncrementalBuild);
+ QFETCH(bool, enableD8);
+
+ // skip tests on github except when run in docker - this var is not propagated into the image
+ if (qEnvironmentVariableIsSet("GITHUB_ACTIONS"))
+ QSKIP("Skip Android tests when running on GitHub");
const SettingsPtr s = settings();
- Profile p(theProfileName(projectDir == "qml-app"), s.get());
+ Profile p(theProfileName(projectDir == "qml-app" || projectDir == "qt-app"), s.get());
if (!p.exists())
- p = Profile("none", s.get());
+ QSKIP("Qt is not installed");
int status;
const auto androidPaths = findAndroid(&status, p.name());
QCOMPARE(status, 0);
@@ -103,33 +112,39 @@ void TestBlackboxAndroid::android()
&& ndkSamplesDirs.contains(projectDir))
QSKIP("NDK samples directory not present");
+ const QString buildSubDir = enableAapt2 ? (generateAab ? "aab" : "aapt2") : "aapt";
QDir::setCurrent(testDataDir + "/" + projectDir);
+ if (!isIncrementalBuild)
+ rmDirR(relativeBuildDir(buildSubDir));
static const QStringList configNames { "debug", "release" };
for (const QString &configName : configNames) {
auto currentExpectedFilesLists = expectedFilesLists;
const QString configArgument = "config:" + configName;
QbsRunParameters resolveParams("resolve");
+ resolveParams.buildDirectory = buildSubDir;
resolveParams.arguments << configArgument << customProperties;
resolveParams.profile = p.name();
QCOMPARE(runQbs(resolveParams), 0);
QbsRunParameters buildParams(QStringList{"--command-echo-mode", "command-line",
configArgument});
+ buildParams.buildDirectory = buildSubDir;
buildParams.profile = p.name();
QCOMPARE(runQbs(buildParams), 0);
- for (const QString &productName : qAsConst(productNames)) {
+ for (const QString &productName : std::as_const(productNames)) {
const QByteArray tag(QTest::currentDataTag());
- const bool isIncrementalBuild = tag.startsWith("qml app") && tag != "qml app";
- QCOMPARE(m_qbsStdout.count("Generating BuildConfig.java"),
+ QCOMPARE(m_qbsStdout.count("generating BuildConfig.java"),
isIncrementalBuild ? 0 : productNames.size());
- QVERIFY(m_qbsStdout.contains(productName.toLocal8Bit() + ".apk"));
- const QString apkFilePath = relativeProductBuildDir(productName, configName)
- + '/' + productName + ".apk";
- QVERIFY2(regularFileExists(apkFilePath), qPrintable(apkFilePath));
+ const QString packageName = productName + (generateAab ? ".aab" : ".apk");
+ QVERIFY(m_qbsStdout.contains(packageName.toLocal8Bit()));
+ const QString packageFilePath = buildSubDir + "/" + relativeProductBuildDir(productName,
+ configName)
+ + '/' + packageName;
+ QVERIFY2(regularFileExists(packageFilePath), qPrintable(packageFilePath));
const QString jarFilePath = androidPaths["jar"];
QVERIFY(!jarFilePath.isEmpty());
QProcess jar;
- jar.start(jarFilePath, QStringList() << "-tf" << apkFilePath);
+ jar.start(jarFilePath, QStringList() << "-tf" << packageFilePath);
QVERIFY2(jar.waitForStarted(), qPrintable(jar.errorString()));
QVERIFY2(jar.waitForFinished(), qPrintable(jar.errorString()));
QVERIFY2(jar.exitCode() == 0, qPrintable(jar.readAllStandardError().constData()));
@@ -159,8 +174,8 @@ void TestBlackboxAndroid::android()
return f.contains("qmltooling");
};
if (none_of(actualFiles, isFileSharedObject)
- || std::all_of(actualFiles.cbegin(), actualFiles.cend(), isQmlToolingLib)) {
- QWARN(msg);
+ || qbs::Internal::all_of(actualFiles, isQmlToolingLib)) {
+ qWarning() << msg;
} else {
QFAIL(msg);
}
@@ -168,11 +183,12 @@ void TestBlackboxAndroid::android()
}
if (projectDir == "multiple-libs-per-apk") {
- const auto dxPath = androidPaths["sdk-build-tools-dx"];
- QVERIFY(!dxPath.isEmpty());
+ const auto dexCompilerPath = enableD8 ? androidPaths["sdk-build-tools-d8"]
+ : androidPaths["sdk-build-tools-dx"];
+ QVERIFY(!dexCompilerPath.isEmpty());
const auto lines = m_qbsStdout.split('\n');
const auto it = std::find_if(lines.cbegin(), lines.cend(), [&](const QByteArray &line) {
- return !line.isEmpty() && line.startsWith(dxPath.toUtf8());
+ return !line.isEmpty() && line.startsWith(dexCompilerPath.toUtf8());
});
QVERIFY2(it != lines.cend(), qPrintable(m_qbsStdout.constData()));
const auto line = *it;
@@ -204,71 +220,102 @@ void TestBlackboxAndroid::android_data()
const auto cxxLibPath = [&p, &pQt](const QByteArray &oldcxxLib, bool forQt) {
const bool usesClang = (forQt ? pQt : p).value(QStringLiteral("qbs.toolchainType"))
.toString() == "clang";
- return QByteArray("lib/${ARCH}/") + (usesClang ? "libc++_shared.so" : oldcxxLib);
+ const QByteArray path = "lib/${ARCH}/";
+ return path + (usesClang ? QByteArrayLiteral("libc++_shared.so") : oldcxxLib);
};
- const QByteArrayList archsForQt = { pQt.value("qbs.architecture").toString().toUtf8() };
- QByteArrayList ndkArchsForQt = archsForQt;
- if (ndkArchsForQt.first() == "armv7a")
- ndkArchsForQt.first() = "armeabi-v7a";
- else if (ndkArchsForQt.first() == "armv5te")
- ndkArchsForQt.first() = "armeabi";
- else if (ndkArchsForQt.first() == "arm64")
- ndkArchsForQt.first() = "arm64-v8a";
+ qbs::Version version(5, 13);
+ QStringList qmakeFilePaths = pQt.value(QStringLiteral("moduleProviders.Qt.qmakeFilePaths")).
+ toStringList();
+ if (qmakeFilePaths.size() >= 1)
+ version = TestBlackboxBase::qmakeVersion(qmakeFilePaths[0]);
+ bool singleArchQt = (version < qbs::Version(5, 14));
+ QByteArray qtVersionMajor((version >= qbs::Version(6, 0)) ? "6" : "5");
+ QByteArrayList archsForQt;
+ if (singleArchQt) {
+ archsForQt = { pQt.value("qbs.architecture").toString().toUtf8() };
+ if (archsStringList.empty())
+ archsStringList << QStringLiteral("armv7a"); // must match default in common.qbs
+ } else {
+ QStringList archsForQtStringList = pQt.value(QStringLiteral("qbs.architectures"))
+ .toStringList();
+ if (archsForQtStringList.empty())
+ archsForQtStringList << pQt.value("qbs.architecture").toString();
+ std::transform(archsForQtStringList.begin(),
+ archsForQtStringList.end(),
+ std::back_inserter(archsForQt),
+ [] (const QString &s) {
+ return s.toUtf8();
+ });
+ }
+
+ QByteArrayList ndkArchsForQt;
+ std::transform(archsForQt.begin(), archsForQt.end(), std::back_inserter(ndkArchsForQt),
+ [] (const QString &s) {
+ return s.toUtf8().replace("armv7a", "armeabi-v7a")
+ .replace("armv5te", "armeabi")
+ .replace("arm64", "arm64-v8a");
+ });
- auto expandArchs = [] (const QByteArrayList &archs, const QByteArrayList &lst) {
+ auto expandArchs = [] (const QByteArrayList &archs, const QByteArrayList &lst, bool aabPackage) {
const QByteArray &archPlaceHolder = "${ARCH}";
QByteArrayList result;
+ QByteArray base( aabPackage ? "base/" : "");
for (const QByteArray &entry : lst) {
if (entry.contains(archPlaceHolder)) {
- for (const QByteArray &arch : qAsConst(archs))
- result << QByteArray(entry).replace(archPlaceHolder, arch);
+ for (const QByteArray &arch : std::as_const(archs))
+ result << (base + QByteArray(entry).replace(archPlaceHolder, arch));
} else {
- result << entry;
+ result << (base + entry);
}
}
return result;
};
- const QByteArrayList commonFiles = expandArchs(archs, {
- "AndroidManifest.xml", "META-INF/ANDROIDD.RSA", "META-INF/ANDROIDD.SF",
- "META-INF/MANIFEST.MF", "classes.dex"
- });
+ auto commonFiles = [](bool generateAab, bool codeSign = true,
+ QString keyAlias="androiddebugkey") {
+ QByteArrayList files;
+ if (generateAab)
+ files << "base/manifest/AndroidManifest.xml" << "base/dex/classes.dex"
+ << "BundleConfig.pb";
+ else
+ files << "AndroidManifest.xml" << "classes.dex";
+ if (codeSign)
+ files << QByteArray("META-INF/" + keyAlias.toUpper().left(8).toUtf8() + ".RSA")
+ << QByteArray("META-INF/" + keyAlias.toUpper().left(8).toUtf8() + ".SF")
+ << "META-INF/MANIFEST.MF";
+ return files;
+ };
QTest::addColumn<QString>("projectDir");
QTest::addColumn<QStringList>("productNames");
QTest::addColumn<QList<QByteArrayList>>("expectedFilesLists");
QTest::addColumn<QStringList>("customProperties");
- QTest::newRow("teapot")
- << "teapot" << QStringList("TeapotNativeActivity")
- << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, {
- "resources.arsc",
- "assets/Shaders/ShaderPlain.fsh",
- "assets/Shaders/VS_ShaderPlain.vsh",
- "lib/${ARCH}/libgdbserver.so",
- cxxLibPath("libgnustl_shared.so", false),
- "lib/${ARCH}/libTeapotNativeActivity.so",
- "res/layout/widgets.xml"}))
- << QStringList();
- QTest::newRow("minimal-native")
- << "minimal-native" << QStringList("minimalnative")
- << (QList<QByteArrayList>() << commonFiles + expandArchs({archs.first()}, {
- "lib/${ARCH}/libminimalnative.so",
- cxxLibPath("libstlport_shared.so", false),
- "lib/${ARCH}/libdependency.so"}))
- << QStringList{"products.minimalnative.multiplexByQbsProperties:[]",
- "modules.qbs.architecture:" + archsStringList.first()};
- QTest::newRow("qml app")
- << "qml-app" << QStringList("qmlapp")
- << (QList<QByteArrayList>() << commonFiles + expandArchs(ndkArchsForQt, {
- "resources.arsc",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/qmldir",
- "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
- "lib/${ARCH}/libgdbserver.so",
+ QTest::addColumn<bool>("enableAapt2");
+ QTest::addColumn<bool>("generateAab");
+ QTest::addColumn<bool>("isIncrementalBuild");
+ QTest::addColumn<bool>("enableD8");
+
+ const auto aaptVersion = [](bool enableAapt2) {
+ return QString("modules.Android.sdk.aaptName:") + (enableAapt2 ? "aapt2" : "aapt");
+ };
+ bool enableAapt2 = false;
+ const auto packageType = [](bool generateAab) {
+ return QString("modules.Android.sdk.packageType:") + (generateAab ? "aab" : "apk");
+ };
+ bool generateAab = false;
+ bool isIncrementalBuild = false;
+
+ const auto dexCompilerVersion = [](bool enableD8) {
+ return QString("modules.Android.sdk.dexCompilerName:") + (enableD8 ? "d8" : "dx");
+ };
+ bool enableD8 = true;
+ auto qtAppExpectedFiles = [&](bool generateAab, bool enableAapt2, bool codeSign = true,
+ QString keyAlias="androiddebugkey") {
+ QByteArrayList expectedFile;
+ if (singleArchQt) {
+ expectedFile << commonFiles(generateAab, codeSign, keyAlias) + expandArchs(ndkArchsForQt, {
cxxLibPath("libgnustl_shared.so", true),
- "lib/${ARCH}/libplugins_bearer_libqandroidbearer.so",
+ "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
"lib/${ARCH}/libplugins_imageformats_libqgif.so",
"lib/${ARCH}/libplugins_imageformats_libqicns.so",
"lib/${ARCH}/libplugins_imageformats_libqico.so",
@@ -278,139 +325,791 @@ void TestBlackboxAndroid::android_data()
"lib/${ARCH}/libplugins_imageformats_libqwbmp.so",
"lib/${ARCH}/libplugins_imageformats_libqwebp.so",
"lib/${ARCH}/libplugins_platforms_android_libqtforandroid.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_debugger.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_inspector.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_local.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_messages.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_native.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_nativedebugger.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_profiler.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_preview.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_quickprofiler.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_server.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_tcp.so",
- "lib/${ARCH}/libqml_QtQuick.2_libqtquick2plugin.so",
- "lib/${ARCH}/libqml_QtQuick_Window.2_libwindowplugin.so",
+ "lib/${ARCH}/libplugins_styles_libqandroidstyle.so",
"lib/${ARCH}/libQt5Core.so",
"lib/${ARCH}/libQt5Gui.so",
- "lib/${ARCH}/libQt5Network.so",
- "lib/${ARCH}/libQt5Qml.so",
- "lib/${ARCH}/libQt5QuickParticles.so",
- "lib/${ARCH}/libQt5Quick.so",
- "lib/${ARCH}/libqmlapp.so",
- "res/layout/splash.xml"}))
- << QStringList{"modules.Android.sdk.automaticSources:false",
- "modules.qbs.architecture:" + archsForQt.first()};
- QTest::newRow("qml app using Ministro")
- << "qml-app" << QStringList("qmlapp")
- << (QList<QByteArrayList>() << commonFiles + expandArchs(ndkArchsForQt, {
- "resources.arsc",
- "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
- "lib/${ARCH}/libgdbserver.so",
+ "lib/${ARCH}/libQt5Widgets.so",
+ "lib/${ARCH}/libqt-app.so"}, generateAab);
+ } else {
+ expectedFile << commonFiles(generateAab, codeSign, keyAlias) + expandArchs(ndkArchsForQt, {
cxxLibPath("libgnustl_shared.so", true),
- "lib/${ARCH}/libqmlapp.so",
- "res/layout/splash.xml"}))
- << QStringList{"modules.Qt.android_support.useMinistro:true",
- "modules.Android.sdk.automaticSources:false"};
+ "lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qjpeg_${ARCH}.so",
+ "lib/${ARCH}/libplugins_platforms_qtforandroid_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Core_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Gui_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Widgets_${ARCH}.so",
+ "lib/${ARCH}/libqt-app_${ARCH}.so"}, generateAab);
+ }
+ if (generateAab)
+ expectedFile << "base/resources.pb" << "base/native.pb";
+ else
+ expectedFile << "resources.arsc";
+ if (version >= qbs::Version(5, 14))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libplugins_styles_qandroidstyle_${ARCH}.so"}, generateAab);
+ if (version < qbs::Version(6, 0) && version >= qbs::Version(5, 14))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libplugins_imageformats_qicns_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qtga_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(6, 5))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libQt6Svg_${ARCH}.so",
+ "lib/${ARCH}/libplugins_iconengines_qsvgicon_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qsvg_${ARCH}.so"}, generateAab);
+ if (!enableAapt2 && version < qbs::Version(6, 0))
+ expectedFile << "res/layout/splash.xml";
+ return expectedFile;
+ };
+ auto codeSignProperties = [&](bool codeSign, QString keyStorePath, QString keystorePassword,
+ QString keyPassword, QString keyAlias) {
+ if (!codeSign)
+ return QStringList{"modules.codesign.enableCodeSigning:false"};
+ return QStringList{
+ "modules.codesign.enableCodeSigning:true",
+ "modules.codesign.keystorePath:" + keyStorePath,
+ "modules.codesign.keystorePassword:" + keystorePassword,
+ "modules.codesign.keyPassword:" + keyPassword,
+ "modules.codesign.keyAlias:" + keyAlias,
+ };
+ };
+ bool codeSign = true;
+ QString keyStorePath(testDataDir + "/qt-app/test.keystore");
+ QString keystorePassword("qbsKeystoreTest");
+ QString keyPassword("qbsKeyTest");
+ QString keyAlias("qbsTest");
+ QTest::newRow("qt app")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;;
+ codeSign = false;
+ QTest::newRow("qt app no signing")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = true;
+ codeSign = true;
+ QTest::newRow("qt app aapt2")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ generateAab = true;
+ QTest::newRow("qt app aab")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ codeSign = false;
+ QTest::newRow("qt app aab no signing")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+
+ const QByteArrayList ndkArchsForQtSave = ndkArchsForQt;
+ ndkArchsForQt = {ndkArchsForQt.first()};
+ QTest::newRow("qt app (single arch)")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2)))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab),
+ "modules.qbs.architectures:" + archsForQt.first()}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ ndkArchsForQt = ndkArchsForQtSave;
+
+ auto teaPotAppExpectedFiles = [&](const QByteArrayList &archs, bool generateAab) {
+ QByteArrayList expectedFile;
+ expectedFile << commonFiles(generateAab) + expandArchs(archs, {
+ "assets/Shaders/ShaderPlain.fsh",
+ "assets/Shaders/VS_ShaderPlain.vsh",
+ cxxLibPath("libgnustl_shared.so", false),
+ "lib/${ARCH}/libTeapotNativeActivity.so",
+ "res/layout/widgets.xml",
+ "res/mipmap-hdpi-v4/ic_launcher.png",
+ "res/mipmap-mdpi-v4/ic_launcher.png",
+ "res/mipmap-xhdpi-v4/ic_launcher.png",
+ "res/mipmap-xxhdpi-v4/ic_launcher.png"}, generateAab);
+ if (generateAab)
+ expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb";
+ else
+ expectedFile << "resources.arsc";
+ return expectedFile;
+ };
+ generateAab = false;
+ enableAapt2 = false;
+ QTest::newRow("teapot")
+ << "teapot" << QStringList("TeapotNativeActivity")
+ << (QList<QByteArrayList>() << teaPotAppExpectedFiles(archs, generateAab))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = true;
+ QTest::newRow("teapot aapt2")
+ << "teapot" << QStringList("TeapotNativeActivity")
+ << (QList<QByteArrayList>() << teaPotAppExpectedFiles(archs, generateAab))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ generateAab = true;
+ QTest::newRow("teapot aapt2 aab")
+ << "teapot" << QStringList("TeapotNativeActivity")
+ << (QList<QByteArrayList>() << teaPotAppExpectedFiles(archs, generateAab))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = false;
+ generateAab = false;
+ QTest::newRow("minimal-native")
+ << "minimal-native" << QStringList("minimalnative")
+ << (QList<QByteArrayList>() << commonFiles(generateAab) + expandArchs({archs.first()}, {
+ "lib/${ARCH}/libminimalnative.so",
+ cxxLibPath("libstlport_shared.so", false),
+ "lib/${ARCH}/libdependency.so"}, generateAab))
+ << QStringList{"products.minimalnative.multiplexByQbsProperties:[]",
+ "modules.qbs.architecture:" + archsStringList.first(),
+ aaptVersion(enableAapt2)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = true;
+ QTest::newRow("minimal-native aapt2")
+ << "minimal-native" << QStringList("minimalnative")
+ << (QList<QByteArrayList>() << commonFiles(generateAab) +
+ (QByteArrayList() << "resources.arsc") + expandArchs({archs.first()}, {
+ "lib/${ARCH}/libminimalnative.so",
+ cxxLibPath("libstlport_shared.so", false),
+ "lib/${ARCH}/libdependency.so"}, generateAab))
+ << QStringList{"products.minimalnative.multiplexByQbsProperties:[]",
+ "modules.qbs.architecture:" + archsStringList.first(),
+ aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ generateAab = true;
+ QTest::newRow("minimal-native aapt2 aab")
+ << "minimal-native" << QStringList("minimalnative")
+ << (QList<QByteArrayList>() << commonFiles(generateAab) +
+ (QByteArrayList() << "base/resources.pb" << "base/native.pb") +
+ expandArchs({archs.first()}, {
+ "lib/${ARCH}/libminimalnative.so",
+ cxxLibPath("libstlport_shared.so", false),
+ "lib/${ARCH}/libdependency.so"}, generateAab))
+ << QStringList{"products.minimalnative.multiplexByQbsProperties:[]",
+ "modules.qbs.architecture:" + archsStringList.first(),
+ aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ auto qmlAppExpectedFiles = [&](bool generateAab, bool enableAapt2) {
+ QByteArrayList expectedFile;
+ if (singleArchQt) {
+ expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, {
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes",
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir",
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes",
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/qmldir",
+ "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
+ cxxLibPath("libgnustl_shared.so", true),
+ "lib/${ARCH}/libplugins_bearer_libqandroidbearer.so",
+ "lib/${ARCH}/libplugins_imageformats_libqgif.so",
+ "lib/${ARCH}/libplugins_imageformats_libqicns.so",
+ "lib/${ARCH}/libplugins_imageformats_libqico.so",
+ "lib/${ARCH}/libplugins_imageformats_libqjpeg.so",
+ "lib/${ARCH}/libplugins_imageformats_libqtga.so",
+ "lib/${ARCH}/libplugins_imageformats_libqtiff.so",
+ "lib/${ARCH}/libplugins_imageformats_libqwbmp.so",
+ "lib/${ARCH}/libplugins_imageformats_libqwebp.so",
+ "lib/${ARCH}/libplugins_platforms_android_libqtforandroid.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_debugger.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_inspector.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_local.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_messages.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_native.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_nativedebugger.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_profiler.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_preview.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_quickprofiler.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_server.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_tcp.so",
+ "lib/${ARCH}/libqml_QtQuick.2_libqtquick2plugin.so",
+ "lib/${ARCH}/libqml_QtQuick_Window.2_libwindowplugin.so",
+ "lib/${ARCH}/libQt5Core.so",
+ "lib/${ARCH}/libQt5Gui.so",
+ "lib/${ARCH}/libQt5Network.so",
+ "lib/${ARCH}/libQt5Qml.so",
+ "lib/${ARCH}/libQt5QuickParticles.so",
+ "lib/${ARCH}/libQt5Quick.so",
+ "lib/${ARCH}/libqmlapp.so"}, generateAab);
+ } else {
+ expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, {
+ "assets/android_rcc_bundle.rcc",
+ cxxLibPath("libgnustl_shared.so", true),
+ "lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qjpeg_${ARCH}.so",
+ "lib/${ARCH}/libplugins_platforms_qtforandroid_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_debugger_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_inspector_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_local_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_messages_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_native_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_nativedebugger_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_profiler_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_preview_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_quickprofiler_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_server_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_tcp_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Core_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Gui_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Network_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Qml_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Quick_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "QmlModels_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "QmlWorkerScript_${ARCH}.so",
+ "lib/${ARCH}/libqmlapp_${ARCH}.so"}, generateAab);
+ if (version < qbs::Version(5, 15))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libQt5QuickParticles_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(5, 15) && version < qbs::Version(6, 0))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQml_StateMachine_qtqmlstatemachine_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_WorkerScript.2_workerscriptplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_Models.2_modelsplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_qmlplugin_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(5, 14) && version < qbs::Version(6, 0))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQuick.2_qtquick2plugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Window.2_windowplugin_${ARCH}.so"},
+ generateAab);
+ if (version < qbs::Version(6, 0))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libplugins_bearer_qandroidbearer_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qicns_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qtga_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(6, 5))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libQt6Svg_${ARCH}.so",
+ "lib/${ARCH}/libplugins_iconengines_qsvgicon_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qsvg_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(6, 0)) {
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libQt6OpenGL_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_Models_modelsplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_WorkerScript_workerscriptplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_qtquick2plugin_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(6, 5))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQml_Base_qmlplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_qmlmetaplugin_${ARCH}.so"}, generateAab);
+ else
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQml_qmlplugin_${ARCH}.so"}, generateAab);
+ }
+ if (version >= qbs::Version(6, 2))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libplugins_networkinformation_qandroidnetworkinformation_${ARCH}.so",
+ "lib/${ARCH}/libplugins_tls_qcertonlybackend_${ARCH}.so",
+ "lib/${ARCH}/libplugins_tls_qopensslbackend_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Window_quickwindowplugin_${ARCH}.so",
+ }, generateAab);
+
+ if (version >= qbs::Version(6, 0) && version < qbs::Version(6, 3)) {
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libQt6QuickControls2Impl_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickControls2_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickParticles_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickShapes_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickTemplates2_${ARCH}.so",
+ "lib/${ARCH}/libQt6Sql_${ARCH}.so",
+ "lib/${ARCH}/libplugins_sqldrivers_qsqlite_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Basic_impl_qtquickcontrols2basicstyleimplplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Basic_qtquickcontrols2basicstyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Fusion_impl_qtquickcontrols2fusionstyleimplplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Fusion_qtquickcontrols2fusionstyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Imagine_impl_qtquickcontrols2imaginestyleimplplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Imagine_qtquickcontrols2imaginestyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Material_impl_qtquickcontrols2materialstyleimplplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Material_qtquickcontrols2materialstyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Universal_impl_qtquickcontrols2universalstyleimplplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Universal_qtquickcontrols2universalstyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_impl_qtquickcontrols2implplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_qtquickcontrols2plugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_LocalStorage_qmllocalstorageplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_NativeStyle_qtquickcontrols2nativestyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Particles_particlesplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Shapes_qmlshapesplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Templates_qtquicktemplates2plugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Timeline_qtquicktimelineplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Layouts_qquicklayoutsplugin_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(6, 2))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQuick_tooling_quicktoolingplugin_${ARCH}.so",
+ "lib/${ARCH}/libQt6QmlLocalStorage_${ARCH}.so",
+ "lib/${ARCH}/libQt6QmlXmlListModel_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickDialogs2QuickImpl_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickDialogs2Utils_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickDialogs2_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickLayouts_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickTimeline_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_XmlListModel_qmlxmllistmodelplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Dialogs_qtquickdialogsplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Dialogs_quickimpl_qtquickdialogs2quickimplplugin_${ARCH}.so"},
+ generateAab);
+ else
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQuick_Window_quickwindow_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_tooling_quicktooling_${ARCH}.so"}, generateAab);
+ }
+ }
+ if (generateAab)
+ expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb";
+ else
+ expectedFile << "resources.arsc";
+ if (!enableAapt2 && version < qbs::Version(6, 0))
+ expectedFile << "res/layout/splash.xml";
+ return expectedFile;
+ };
+ auto qmlAppCustomMetaDataExpectedFiles = [&](bool generateAab, bool enableAapt2) {
+ QByteArrayList expectedFile;
+ if (singleArchQt) {
+ expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, {
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes",
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir",
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes",
+ "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/qmldir",
+ "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
+ "assets/dummyasset.txt",
+ cxxLibPath("libgnustl_shared.so", true),
+ "lib/${ARCH}/libplugins_bearer_libqandroidbearer.so",
+ "lib/${ARCH}/libplugins_imageformats_libqgif.so",
+ "lib/${ARCH}/libplugins_imageformats_libqicns.so",
+ "lib/${ARCH}/libplugins_imageformats_libqico.so",
+ "lib/${ARCH}/libplugins_imageformats_libqjpeg.so",
+ "lib/${ARCH}/libplugins_imageformats_libqtga.so",
+ "lib/${ARCH}/libplugins_imageformats_libqtiff.so",
+ "lib/${ARCH}/libplugins_imageformats_libqwbmp.so",
+ "lib/${ARCH}/libplugins_imageformats_libqwebp.so",
+ "lib/${ARCH}/libplugins_platforms_android_libqtforandroid.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_debugger.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_inspector.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_local.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_messages.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_native.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_nativedebugger.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_profiler.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_preview.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_quickprofiler.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_server.so",
+ "lib/${ARCH}/libplugins_qmltooling_libqmldbg_tcp.so",
+ "lib/${ARCH}/libqml_QtQuick.2_libqtquick2plugin.so",
+ "lib/${ARCH}/libqml_QtQuick_Window.2_libwindowplugin.so",
+ "lib/${ARCH}/libQt5Core.so",
+ "lib/${ARCH}/libQt5Gui.so",
+ "lib/${ARCH}/libQt5Network.so",
+ "lib/${ARCH}/libQt5Qml.so",
+ "lib/${ARCH}/libQt5QuickParticles.so",
+ "lib/${ARCH}/libQt5Quick.so",
+ "lib/${ARCH}/libqmlapp.so"}, generateAab);
+ } else {
+ expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, {
+ "assets/android_rcc_bundle.rcc",
+ "assets/dummyasset.txt",
+ cxxLibPath("libgnustl_shared.so", true),
+ "lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qjpeg_${ARCH}.so",
+ "lib/${ARCH}/libplugins_platforms_qtforandroid_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_debugger_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_inspector_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_local_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_messages_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_native_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_nativedebugger_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_profiler_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_preview_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_quickprofiler_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_server_${ARCH}.so",
+ "lib/${ARCH}/libplugins_qmltooling_qmldbg_tcp_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Core_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Gui_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Network_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Qml_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "Quick_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "QmlModels_${ARCH}.so",
+ "lib/${ARCH}/libQt" + qtVersionMajor + "QmlWorkerScript_${ARCH}.so",
+ "lib/${ARCH}/libqmlapp_${ARCH}.so"}, generateAab);
+ if (version < qbs::Version(5, 15))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libQt5QuickParticles_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(5, 15) && version < qbs::Version(6, 0))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQml_StateMachine_qtqmlstatemachine_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_WorkerScript.2_workerscriptplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_Models.2_modelsplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_qmlplugin_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(5, 14) && version < qbs::Version(6, 0))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQuick.2_qtquick2plugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Window.2_windowplugin_${ARCH}.so"},
+ generateAab);
+ if (version < qbs::Version(6, 0))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libplugins_bearer_qandroidbearer_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qicns_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qtga_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qtiff_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qwbmp_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qwebp_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(6, 5))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libQt6Svg_${ARCH}.so",
+ "lib/${ARCH}/libplugins_iconengines_qsvgicon_${ARCH}.so",
+ "lib/${ARCH}/libplugins_imageformats_qsvg_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(6, 0)) {
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libQt6OpenGL_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_Models_modelsplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_WorkerScript_workerscriptplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_qtquick2plugin_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(6, 5))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQml_Base_qmlplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_qmlmetaplugin_${ARCH}.so"}, generateAab);
+ else
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQml_qmlplugin_${ARCH}.so"}, generateAab);
+ }
+ if (version >= qbs::Version(6, 2))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libplugins_networkinformation_qandroidnetworkinformation_${ARCH}.so",
+ "lib/${ARCH}/libplugins_tls_qcertonlybackend_${ARCH}.so",
+ "lib/${ARCH}/libplugins_tls_qopensslbackend_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Window_quickwindowplugin_${ARCH}.so",
+ }, generateAab);
+ if (version >= qbs::Version(6, 0) && version < qbs::Version(6, 3)) {
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libQt6QuickControls2Impl_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickControls2_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickParticles_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickShapes_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickTemplates2_${ARCH}.so",
+ "lib/${ARCH}/libQt6Sql_${ARCH}.so",
+ "lib/${ARCH}/libplugins_sqldrivers_qsqlite_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Basic_impl_qtquickcontrols2basicstyleimplplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Basic_qtquickcontrols2basicstyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Fusion_impl_qtquickcontrols2fusionstyleimplplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Fusion_qtquickcontrols2fusionstyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Imagine_impl_qtquickcontrols2imaginestyleimplplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Imagine_qtquickcontrols2imaginestyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Material_impl_qtquickcontrols2materialstyleimplplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Material_qtquickcontrols2materialstyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Universal_impl_qtquickcontrols2universalstyleimplplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_Universal_qtquickcontrols2universalstyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_impl_qtquickcontrols2implplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Controls_qtquickcontrols2plugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_LocalStorage_qmllocalstorageplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_NativeStyle_qtquickcontrols2nativestyleplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Particles_particlesplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Shapes_qmlshapesplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Templates_qtquicktemplates2plugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Timeline_qtquicktimelineplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Layouts_qquicklayoutsplugin_${ARCH}.so"}, generateAab);
+ if (version >= qbs::Version(6, 2))
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQuick_tooling_quicktoolingplugin_${ARCH}.so",
+ "lib/${ARCH}/libQt6QmlLocalStorage_${ARCH}.so",
+ "lib/${ARCH}/libQt6QmlXmlListModel_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickDialogs2QuickImpl_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickDialogs2Utils_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickDialogs2_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickLayouts_${ARCH}.so",
+ "lib/${ARCH}/libQt6QuickTimeline_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQml_XmlListModel_qmlxmllistmodelplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Dialogs_qtquickdialogsplugin_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_Dialogs_quickimpl_qtquickdialogs2quickimplplugin_${ARCH}.so"},
+ generateAab);
+ else
+ expectedFile << expandArchs(ndkArchsForQt, {
+ "lib/${ARCH}/libqml_QtQuick_Window_quickwindow_${ARCH}.so",
+ "lib/${ARCH}/libqml_QtQuick_tooling_quicktooling_${ARCH}.so"}, generateAab);
+ }
+ }
+ if (generateAab)
+ expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb";
+ else
+ expectedFile << "resources.arsc";
+ if (!enableAapt2 && version < qbs::Version(6, 0))
+ expectedFile << "res/layout/splash.xml";
+ return expectedFile;
+ };
+ QStringList qmlAppCustomProperties;
+ if (singleArchQt) {
+ qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false",
+ "modules.qbs.architecture:" + archsForQt.first()};
+ } else {
+ qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false"};
+ }
+ // aapt tool for the resources works with a directory option pointing to the parent directory
+ // of the resources (res).
+ // The Qt.android_support module adds res/values/libs.xml (from Qt install dir). So the res from
+ // Qt install res directory is added to aapt. This results in adding res/layout/splash.xml to
+ // the package eventhough the file is not needed.
+ // On the other hand aapt2 requires giving all the resources files.
+ // Also when enabling aapt2 the resources.arsc is always created, eventhough no resources are
+ // declared.
+ enableAapt2 = false;
+ generateAab = false;
+ QTest::newRow("qml app")
+ << "qml-app" << QStringList("qmlapp")
+ << (QList<QByteArrayList>() << qmlAppExpectedFiles(generateAab, enableAapt2))
+ << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = true;
+ QTest::newRow("qml app aapt2")
+ << "qml-app" << QStringList("qmlapp")
+ << (QList<QByteArrayList>() << qmlAppExpectedFiles(generateAab, enableAapt2))
+ << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ generateAab = true;
+ QTest::newRow("qml app aab")
+ << "qml-app" << QStringList("qmlapp")
+ << (QList<QByteArrayList>() << qmlAppExpectedFiles(generateAab, enableAapt2))
+ << (QStringList() << qmlAppCustomProperties << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = false;
+ generateAab = false;
QTest::newRow("qml app with custom metadata")
<< "qml-app" << QStringList("qmlapp")
- << (QList<QByteArrayList>() << commonFiles + expandArchs(ndkArchsForQt, {
- "resources.arsc",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/plugins.qmltypes",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick.2/qmldir",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/plugins.qmltypes",
- "assets/--Added-by-androiddeployqt--/qml/QtQuick/Window.2/qmldir",
- "assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
- "assets/dummyasset.txt",
- "lib/${ARCH}/libgdbserver.so",
- cxxLibPath("libgnustl_shared.so", true),
- "lib/${ARCH}/libplugins_bearer_libqandroidbearer.so",
- "lib/${ARCH}/libplugins_imageformats_libqgif.so",
- "lib/${ARCH}/libplugins_imageformats_libqicns.so",
- "lib/${ARCH}/libplugins_imageformats_libqico.so",
- "lib/${ARCH}/libplugins_imageformats_libqjpeg.so",
- "lib/${ARCH}/libplugins_imageformats_libqtga.so",
- "lib/${ARCH}/libplugins_imageformats_libqtiff.so",
- "lib/${ARCH}/libplugins_imageformats_libqwbmp.so",
- "lib/${ARCH}/libplugins_imageformats_libqwebp.so",
- "lib/${ARCH}/libplugins_platforms_android_libqtforandroid.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_debugger.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_inspector.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_local.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_messages.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_native.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_nativedebugger.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_profiler.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_preview.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_quickprofiler.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_server.so",
- "lib/${ARCH}/libplugins_qmltooling_libqmldbg_tcp.so",
- "lib/${ARCH}/libqml_QtQuick.2_libqtquick2plugin.so",
- "lib/${ARCH}/libqml_QtQuick_Window.2_libwindowplugin.so",
- "lib/${ARCH}/libQt5Core.so",
- "lib/${ARCH}/libQt5Gui.so",
- "lib/${ARCH}/libQt5Network.so",
- "lib/${ARCH}/libQt5Qml.so",
- "lib/${ARCH}/libQt5QuickParticles.so",
- "lib/${ARCH}/libQt5Quick.so",
- "lib/${ARCH}/libqmlapp.so",
- "res/layout/splash.xml"}))
- << QStringList("modules.Android.sdk.automaticSources:true");
+ << (QList<QByteArrayList>() << (QByteArrayList()
+ << qmlAppCustomMetaDataExpectedFiles(generateAab,
+ enableAapt2)))
+ << QStringList{"modules.Android.sdk.automaticSources:true",
+ aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = true;
+ QTest::newRow("qml app with custom metadata aapt2")
+ << "qml-app" << QStringList("qmlapp")
+ << (QList<QByteArrayList>() << (QByteArrayList()
+ << qmlAppCustomMetaDataExpectedFiles(generateAab,
+ enableAapt2)))
+ << QStringList{"modules.Android.sdk.automaticSources:true", aaptVersion(enableAapt2),
+ packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ generateAab = true;
+ if (!singleArchQt) {
+ QTest::newRow("qml app with custom metadata aab")
+ << "qml-app" << QStringList("qmlapp")
+ << (QList<QByteArrayList>() << (QByteArrayList()
+ << qmlAppCustomMetaDataExpectedFiles(generateAab,
+ enableAapt2)))
+ << QStringList{"modules.Android.sdk.automaticSources:true", aaptVersion(enableAapt2),
+ packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ }
+ isIncrementalBuild = false;
+ enableAapt2 = false;
+ generateAab = false;
+ auto noNativeExpectedFiles = [&](bool generateAab) {
+ QByteArrayList expectedFile;
+ expectedFile << commonFiles(generateAab) + expandArchs(archs, {
+ "res/drawable-hdpi-v4/ic_action_play_disabled.png",
+ "res/drawable-hdpi-v4/ic_action_play.png",
+ "res/drawable-hdpi-v4/ic_launcher.png",
+ "res/drawable-hdpi-v4/tile.9.png",
+ "res/drawable-mdpi-v4/ic_action_play_disabled.png",
+ "res/drawable-mdpi-v4/ic_action_play.png",
+ "res/drawable-mdpi-v4/ic_launcher.png",
+ "res/drawable/selector_play.xml",
+ "res/drawable-xhdpi-v4/ic_action_play_disabled.png",
+ "res/drawable-xhdpi-v4/ic_action_play.png",
+ "res/drawable-xhdpi-v4/ic_launcher.png",
+ "res/drawable-xxhdpi-v4/ic_launcher.png",
+ "res/layout/sample_main.xml",
+ "res/menu/action_menu.xml",
+ "res/raw/vid_bigbuckbunny.mp4"}, generateAab);
+ if (generateAab)
+ expectedFile << "base/resources.pb";
+ else
+ expectedFile << "resources.arsc";
+ return expectedFile;
+ };
+
QTest::newRow("no native")
<< "no-native"
<< QStringList("com.example.android.basicmediadecoder")
- << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, {
- "resources.arsc",
- "res/drawable-hdpi-v4/ic_action_play_disabled.png",
- "res/drawable-hdpi-v4/ic_action_play.png",
- "res/drawable-hdpi-v4/ic_launcher.png",
- "res/drawable-hdpi-v4/tile.9.png",
- "res/drawable-mdpi-v4/ic_action_play_disabled.png",
- "res/drawable-mdpi-v4/ic_action_play.png",
- "res/drawable-mdpi-v4/ic_launcher.png",
- "res/drawable/selector_play.xml",
- "res/drawable-xhdpi-v4/ic_action_play_disabled.png",
- "res/drawable-xhdpi-v4/ic_action_play.png",
- "res/drawable-xhdpi-v4/ic_launcher.png",
- "res/drawable-xxhdpi-v4/ic_launcher.png",
- "res/layout/sample_main.xml",
- "res/menu/action_menu.xml",
- "res/menu-v11/action_menu.xml",
- "res/raw/vid_bigbuckbunny.mp4"}))
- << QStringList();
+ << (QList<QByteArrayList>() << noNativeExpectedFiles(generateAab))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = true;
+ QTest::newRow("no native aapt2")
+ << "no-native"
+ << QStringList("com.example.android.basicmediadecoder")
+ << (QList<QByteArrayList>() << noNativeExpectedFiles(generateAab))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ generateAab = true;
+ QTest::newRow("no native aab")
+ << "no-native"
+ << QStringList("com.example.android.basicmediadecoder")
+ << (QList<QByteArrayList>() << noNativeExpectedFiles(generateAab))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = false;
+ generateAab = false;
QTest::newRow("aidl") << "aidl" << QStringList("io.qbs.aidltest")
- << QList<QByteArrayList>{commonFiles} << QStringList();
+ << (QList<QByteArrayList>() << (QByteArrayList()
+ << commonFiles(generateAab)))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = true;
+ QTest::newRow("aidl aapt2") << "aidl" << QStringList("io.qbs.aidltest")
+ << (QList<QByteArrayList>() << (QByteArrayList()
+ << commonFiles(generateAab)
+ << "resources.arsc"))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ generateAab = true;
+ QTest::newRow("aidl aab") << "aidl" << QStringList("io.qbs.aidltest")
+ << (QList<QByteArrayList>() << (QByteArrayList()
+ << commonFiles(generateAab)
+ << "base/resources.pb"))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = false;
+ generateAab = false;
+ enableD8 = false;
QTest::newRow("multiple libs")
<< "multiple-libs-per-apk"
<< QStringList("twolibs")
- << (QList<QByteArrayList>() << commonFiles + expandArchs(archs, {
+ << (QList<QByteArrayList>() << commonFiles(generateAab) + expandArchs(archs, {
+ "resources.arsc",
+ "lib/${ARCH}/liblib1.so",
+ "lib/${ARCH}/liblib2.so",
+ cxxLibPath("libstlport_shared.so", false)}, generateAab))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab),
+ dexCompilerVersion(enableD8)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableD8 = true;
+ QTest::newRow("multiple libs with d8")
+ << "multiple-libs-per-apk"
+ << QStringList("twolibs")
+ << (QList<QByteArrayList>() << commonFiles(generateAab) + expandArchs(archs, {
+ "resources.arsc",
+ "lib/${ARCH}/liblib1.so",
+ "lib/${ARCH}/liblib2.so",
+ cxxLibPath("libstlport_shared.so", false)}, generateAab))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab),
+ dexCompilerVersion(enableD8)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableD8 = false;
+ enableAapt2 = true;
+ QTest::newRow("multiple libs aapt2")
+ << "multiple-libs-per-apk"
+ << QStringList("twolibs")
+ << (QList<QByteArrayList>() << commonFiles(generateAab) + expandArchs(archs, {
"resources.arsc",
- "lib/${ARCH}/libgdbserver.so",
"lib/${ARCH}/liblib1.so",
"lib/${ARCH}/liblib2.so",
- cxxLibPath("libstlport_shared.so", false)}))
- << QStringList();
- QByteArrayList expectedFiles1 = (commonFiles
- + expandArchs(QByteArrayList{"armeabi-v7a", "x86"}, {
- "resources.arsc",
- "lib/${ARCH}/libgdbserver.so",
- "lib/${ARCH}/libp1lib1.so",
- cxxLibPath("libstlport_shared.so", false)})
- + expandArchs(QByteArrayList{archs}, {
- "resources.arsc",
- "lib/${ARCH}/libgdbserver.so",
- "lib/${ARCH}/libp1lib2.so",
- cxxLibPath("libstlport_shared.so", false)})).toSet().toList();
- QByteArrayList expectedFiles2 = commonFiles + expandArchs(archs, {
- "lib/${ARCH}/libgdbserver.so",
- "lib/${ARCH}/libp2lib1.so",
- "lib/${ARCH}/libp2lib2.so",
- cxxLibPath("libstlport_shared.so", false)});
+ cxxLibPath("libstlport_shared.so", false)}, generateAab))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab),
+ dexCompilerVersion(enableD8)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ generateAab = true;
+ QTest::newRow("multiple libs aab")
+ << "multiple-libs-per-apk"
+ << QStringList("twolibs")
+ << (QList<QByteArrayList>() << commonFiles(generateAab) + expandArchs(archs, {
+ "resources.pb", "native.pb",
+ "lib/${ARCH}/liblib1.so",
+ "lib/${ARCH}/liblib2.so",
+ cxxLibPath("libstlport_shared.so", false)}, generateAab))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab),
+ dexCompilerVersion(enableD8)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+
+ enableAapt2 = false;
+ generateAab = false;
+ auto expectedFiles1 = [&](bool generateAab) {
+ QByteArrayList expectedFile = qbs::toList(qbs::toSet(commonFiles(generateAab)
+ + expandArchs(QByteArrayList{"armeabi-v7a", "x86"}, {
+ "lib/${ARCH}/libp1lib1.so",
+ cxxLibPath("libstlport_shared.so", false)}, generateAab)
+ + expandArchs(QByteArrayList{archs}, {
+ "lib/${ARCH}/libp1lib2.so",
+ cxxLibPath("libstlport_shared.so", false)}, generateAab)));
+ if (generateAab)
+ expectedFile << "base/resources.pb" << "base/native.pb";
+ else
+ expectedFile << "resources.arsc";
+ return expectedFile;
+ };
+ auto expectedFiles2 = [&](bool generateAab) {
+ QByteArrayList expectedFile = commonFiles(generateAab) + expandArchs(archs, {
+ "lib/${ARCH}/libp2lib1.so",
+ "lib/${ARCH}/libp2lib2.so",
+ cxxLibPath("libstlport_shared.so", false)}, generateAab);
+ return expectedFile;
+ };
QTest::newRow("multiple apks")
<< "multiple-apks-per-project"
<< (QStringList() << "twolibs1" << "twolibs2")
- << QList<QByteArrayList>{expectedFiles1, expectedFiles2}
- << QStringList();
+ << QList<QByteArrayList>{expectedFiles1(generateAab), expectedFiles2(generateAab)}
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab),
+ dexCompilerVersion(enableD8)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ enableAapt2 = true;
+ QTest::newRow("multiple apks aapt2")
+ << "multiple-apks-per-project"
+ << (QStringList() << "twolibs1" << "twolibs2")
+ << (QList<QByteArrayList>() << expectedFiles1(generateAab)
+ << (QByteArrayList() << expectedFiles2(generateAab) << "resources.arsc"))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab),
+ dexCompilerVersion(enableD8)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
+ generateAab = true;
+ QTest::newRow("multiple apks aab")
+ << "multiple-apks-per-project"
+ << (QStringList() << "twolibs1" << "twolibs2")
+ << (QList<QByteArrayList>() << expectedFiles1(generateAab)
+ << (QByteArrayList() << expectedFiles2(generateAab) << "base/resources.pb"
+ << "base/native.pb"))
+ << QStringList{aaptVersion(enableAapt2), packageType(generateAab),
+ dexCompilerVersion(enableD8)}
+ << enableAapt2 << generateAab << isIncrementalBuild << enableD8;
}
QTEST_MAIN(TestBlackboxAndroid)
diff --git a/tests/auto/blackbox/tst_blackboxapple.cpp b/tests/auto/blackbox/tst_blackboxapple.cpp
index 96cd70b58..30e20a1c9 100644
--- a/tests/auto/blackbox/tst_blackboxapple.cpp
+++ b/tests/auto/blackbox/tst_blackboxapple.cpp
@@ -31,7 +31,9 @@
#include "../shared.h"
#include <tools/hostosinfo.h>
#include <tools/profile.h>
+#include <tools/qttools.h>
+#include <QtCore/qdiriterator.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
#include <QtXml/qdom.h>
@@ -60,6 +62,117 @@ static QString getEmbeddedBinaryPlist(const QString &file)
return QString::fromUtf8(p.readAllStandardOutput()).trimmed();
}
+static QVariantMap readInfoPlistFile(const QString &infoPlistPath)
+{
+ if (!QFile::exists(infoPlistPath)) {
+ qWarning() << infoPlistPath << "doesn't exist";
+ return {};
+ }
+
+ QProcess plutil;
+ plutil.start("plutil", {
+ QStringLiteral("-convert"),
+ QStringLiteral("json"),
+ infoPlistPath
+ });
+ if (!plutil.waitForStarted()) {
+ qWarning() << plutil.errorString();
+ return {};
+ }
+ if (!plutil.waitForFinished()) {
+ qWarning() << plutil.errorString();
+ return {};
+ }
+ if (plutil.exitCode() != 0) {
+ qWarning() << plutil.readAllStandardError().constData();
+ return {};
+ }
+
+ QFile infoPlist(infoPlistPath);
+ if (!infoPlist.open(QIODevice::ReadOnly)) {
+ qWarning() << infoPlist.errorString();
+ return {};
+ }
+ QJsonParseError error;
+ const auto json = QJsonDocument::fromJson(infoPlist.readAll(), &error);
+ if (error.error != QJsonParseError::NoError) {
+ qWarning() << error.errorString();
+ return {};
+ }
+ return json.object().toVariantMap();
+}
+
+static QString getInfoPlistPath(const QString &bundlePath)
+{
+ QFileInfo contents(bundlePath + "/Contents");
+ if (contents.exists() && contents.isDir())
+ return contents.filePath() + "/Info.plist"; // macOS bundle
+ return bundlePath + "/Info.plist";
+}
+
+static bool testVariantListType(const QVariant &variant, QMetaType::Type type)
+{
+ if (variant.userType() != QMetaType::QVariantList)
+ return false;
+ for (const auto &value : variant.toList()) {
+ if (value.userType() != type)
+ return false;
+ }
+ return true;
+}
+
+static QString findFatLibrary(const QString &dir, const QString &libraryName)
+{
+ QDirIterator it(dir, {}, QDir::AllEntries, QDirIterator::Subdirectories);
+ while (it.hasNext()) {
+ it.next();
+ if (it.fileInfo().fileName() == libraryName) {
+ QProcess lipo;
+ lipo.start("lipo", { QStringLiteral("-info"), it.filePath() });
+ if (!lipo.waitForStarted() || !lipo.waitForFinished() || lipo.exitCode() != 0)
+ return {};
+ auto output = lipo.readAllStandardOutput();
+ if (output.contains(QByteArrayLiteral("Architectures in the fat file")))
+ return QDir::cleanPath(it.filePath());
+ }
+ }
+
+ return {};
+}
+
+enum class CodeSignResult { Failed = 0, Signed, Unsigned };
+using CodeSignData = QMap<QByteArray, QByteArray>;
+static std::pair<CodeSignResult, CodeSignData> parseCodeSignOutput(const QByteArray &output)
+{
+ CodeSignData data;
+ if (output.contains("code object is not signed at all"))
+ return {CodeSignResult::Unsigned, data};
+ const auto lines = output.split('\n');
+ for (const auto &line: lines) {
+ if (line.isEmpty()
+ || line.startsWith("CodeDirectory")
+ || line.startsWith("Sealed Resources")
+ || line.startsWith("Internal requirements")) {
+ continue;
+ }
+ const int index = line.indexOf('=');
+ if (index == -1)
+ return {CodeSignResult::Failed, {}};
+ data[line.mid(0, index)] = line.mid(index + 1);
+ }
+ return {CodeSignResult::Signed, data};
+}
+
+static std::pair<CodeSignResult, CodeSignData> getCodeSignInfo(const QString &path)
+{
+ QProcess codesign;
+ codesign.start("codesign", { QStringLiteral("-dv"), path });
+ if (!codesign.waitForStarted() || !codesign.waitForFinished())
+ return {CodeSignResult::Failed, {}};
+ const auto output = codesign.readAllStandardError();
+ return parseCodeSignOutput(output);
+}
+
TestBlackboxApple::TestBlackboxApple()
: TestBlackboxBase (SRCDIR "/testdata-apple", "blackbox-apple")
{
@@ -78,106 +191,190 @@ void TestBlackboxApple::initTestCase()
void TestBlackboxApple::appleMultiConfig()
{
const auto xcodeVersion = findXcodeVersion();
- const bool supportsX86 = xcodeVersion < qbs::Version(10);
-
+ if (!xcodeVersion)
+ QSKIP("requires Xcode profile");
QDir::setCurrent(testDataDir + "/apple-multiconfig");
QCOMPARE(runQbs(QbsRunParameters(QStringList{
"qbs.installPrefix:''",
- QString("project.enableX86:") + (supportsX86 ? "true" : "false")})), 0);
-
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/Contents/MacOS/singleapp").isExecutable());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/Contents/Info.plist").isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/Contents/PkgInfo").isRegularFile());
-
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/Contents/MacOS/singleapp_agg").isExecutable());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/Contents/Info.plist").isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/Contents/PkgInfo").isRegularFile());
-
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/singlelib").isFileSymLink());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Resources").isDirSymLink());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions").isRegularDir());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A").isRegularDir());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A/singlelib").isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A/Resources").isRegularDir());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A/Resources/Info.plist").isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/Current").isDirSymLink());
-
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/Contents/MacOS/multiapp").isExecutable());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/Contents/Info.plist").isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/Contents/PkgInfo").isRegularFile());
-
- QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Contents/MacOS/fatmultiapp").isExecutable());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Contents/Info.plist").isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Contents/PkgInfo").isRegularFile());
-
- QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Contents/MacOS/"
- "fatmultiappmultivariant").isFileSymLink());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Contents/MacOS/"
- "fatmultiappmultivariant_debug").isExecutable());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Contents/MacOS/"
- "fatmultiappmultivariant_profile").isExecutable());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Contents/Info.plist")
- .isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Contents/PkgInfo")
- .isRegularFile());
-
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/multilib").isFileSymLink());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Resources").isDirSymLink());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions").isRegularDir());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A").isRegularDir());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/multilib").isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/multilib_debug").isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/multilib_profile").isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/Resources").isRegularDir());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/Resources/Info.plist").isRegularFile());
- QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/Current").isDirSymLink());
-
- for (const QString &variant : { "release", "debug", "profile" }) {
- for (const QString &arch : { "x86_64" }) {
- QProcess process;
- process.setProgram("/usr/bin/arch");
- process.setArguments({
- "-arch", arch,
- "-e", "DYLD_IMAGE_SUFFIX=_" + variant,
- defaultInstallRoot + "/multiapp.app/Contents/MacOS/multiapp"
- });
- process.start();
- process.waitForFinished();
- QCOMPARE(process.exitCode(), 0);
- const auto processStdout = process.readAllStandardOutput();
- QVERIFY2(processStdout.contains("Hello from " + variant.toUtf8() + " " + arch.toUtf8()),
- processStdout.constData());
+ QStringLiteral("project.xcodeVersion:") + xcodeVersion->toString()})), 0);
+
+ if (m_qbsStdout.contains("isShallow: false")) {
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/Contents/MacOS/singleapp").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/Contents/Info.plist").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/Contents/PkgInfo").isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/Contents/MacOS/singleapp_agg").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/Contents/Info.plist").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/Contents/PkgInfo").isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/singlelib").isFileSymLink());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Resources").isDirSymLink());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions").isRegularDir());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A").isRegularDir());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A/singlelib").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A/Resources").isRegularDir());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/A/Resources/Info.plist").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Versions/Current").isDirSymLink());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/Contents/MacOS/multiapp").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/Contents/Info.plist").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/Contents/PkgInfo").isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Contents/MacOS/fatmultiapp").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Contents/Info.plist").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Contents/PkgInfo").isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Contents/MacOS/"
+ "fatmultiappmultivariant").isFileSymLink());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Contents/MacOS/"
+ "fatmultiappmultivariant_debug").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Contents/MacOS/"
+ "fatmultiappmultivariant_profiling").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Contents/Info.plist")
+ .isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Contents/PkgInfo")
+ .isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/multilib").isFileSymLink());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Resources").isDirSymLink());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions").isRegularDir());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A").isRegularDir());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/multilib").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/multilib_debug").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/multilib_profiling").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/Resources").isRegularDir());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/A/Resources/Info.plist").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Versions/Current").isDirSymLink());
+
+ for (const QString variant : { "release", "debug", "profiling" }) {
+ for (const QString arch : { "x86_64" }) {
+ QProcess process;
+ process.setProgram("/usr/bin/arch");
+ process.setArguments({
+ "-arch", arch,
+ "-e", "DYLD_IMAGE_SUFFIX=_" + variant,
+ defaultInstallRoot + "/multiapp.app/Contents/MacOS/multiapp"
+ });
+ process.start();
+ process.waitForFinished();
+ QCOMPARE(process.exitCode(), 0);
+ const auto processStdout = process.readAllStandardOutput();
+ QVERIFY2(processStdout.contains("Hello from " + variant.toUtf8() + " " + arch.toUtf8()),
+ processStdout.constData());
+ }
}
+ } else if (m_qbsStdout.contains("isShallow: true")) {
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/singleapp").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/Info.plist").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp.app/PkgInfo").isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/singleapp_agg").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/Info.plist").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singleapp_agg.app/PkgInfo").isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/singlelib").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/singlelib.framework/Info.plist").isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/multiapp").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/Info.plist").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multiapp.app/PkgInfo").isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/fatmultiapp").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/Info.plist").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiapp.app/PkgInfo").isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/"
+ "fatmultiappmultivariant").isFileSymLink());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/"
+ "fatmultiappmultivariant_debug").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/"
+ "fatmultiappmultivariant_profiling").isExecutable());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/Info.plist")
+ .isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/fatmultiappmultivariant.app/PkgInfo")
+ .isRegularFile());
+
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/multilib").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/multilib_debug").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/multilib_profiling").isRegularFile());
+ QVERIFY(QFileInfo2(defaultInstallRoot + "/multilib.framework/Info.plist").isRegularFile());
+ } else {
+ QVERIFY2(false, qPrintable(m_qbsStdout));
}
}
void TestBlackboxApple::aggregateDependencyLinking()
{
- if (HostOsInfo::hostOsVersion() > qbs::Version(10, 13, 4))
- QSKIP("32-bit arch build is no longer supported on macOS versions higher than 10.13.4.");
+ const auto xcodeVersion = findXcodeVersion();
+
+ if (!xcodeVersion)
+ QSKIP("requires Xcode profile");
+
+ // XCode 11 produces warning about deprecation of 32-bit apps, but still works
+ const bool hasX86Mac = xcodeVersion < qbs::Version(12);
+ const bool hasArmMac = xcodeVersion >= qbs::Version(12, 2);
QDir::setCurrent(testDataDir + "/aggregateDependencyLinking");
- QCOMPARE(runQbs(QStringList{"-p", "multi_arch_lib"}), 0);
+ QbsRunParameters params{QStringList{"-p", "multi_arch_lib"}};
+ params.arguments << QStringLiteral("products.multi_arch_lib.hasX86Mac:%1").arg(hasX86Mac);
+ params.arguments << QStringLiteral("products.multi_arch_lib.hasArmMac:%1").arg(hasArmMac);
+ QCOMPARE(runQbs(params), 0);
+ if (m_qbsStdout.contains("Cannot build fat binaries"))
+ QSKIP("Building fat binaries is not supported for this profile");
QCOMPARE(runQbs(QStringList{"-p", "just_app", "--command-echo-mode", "command-line"}), 0);
int linkedInLibrariesCount =
- QString::fromUtf8(m_qbsStdout).count(QStringLiteral("multi_arch_lib.a"));
+ QString::fromUtf8(m_qbsStdout).count(QStringLiteral("libmulti_arch_lib.a"));
QCOMPARE(linkedInLibrariesCount, 1);
+ const auto fatLibPath = findFatLibrary(testDataDir, QStringLiteral("libmulti_arch_lib.a"));
+ QVERIFY(!fatLibPath.isEmpty());
+ QVERIFY2(QString::fromUtf8(m_qbsStdout).contains(fatLibPath), m_qbsStdout);
+}
+
+void TestBlackboxApple::appiconset()
+{
+ QDir::setCurrent(testDataDir + QLatin1String("/ib/appiconset"));
+
+ QbsRunParameters params;
+ params.arguments = QStringList() << "-f" << "appiconset.qbs";
+ QCOMPARE(runQbs(params), 0);
+
+ const auto infoPlistPath = getInfoPlistPath(
+ relativeProductBuildDir("appiconset") + "/appiconset.app");
+ QVERIFY(QFile::exists(infoPlistPath));
+ const auto content = readInfoPlistFile(infoPlistPath);
+ QVERIFY(!content.isEmpty());
+
+ if (m_qbsStdout.contains("bundle.isShallow: false")) {
+ QCOMPARE(content.value(QStringLiteral("CFBundleIconFile")), QStringLiteral("AppIcon"));
+ QCOMPARE(content.value(QStringLiteral("CFBundleIconName")), QStringLiteral("AppIcon"));
+ QVERIFY(regularFileExists(relativeProductBuildDir("appiconset")
+ + "/appiconset.app/Contents/Resources/AppIcon.icns"));
+ } else if (m_qbsStdout.contains("bundle.isShallow: true")) {
+ const auto icons = content.value(QStringLiteral("CFBundleIcons")).toMap();
+ QVERIFY2(!icons.isEmpty(), "Info.plist doesn't contain CFBundleIcons key");
+ const auto primaryIcon = icons.value(QStringLiteral("CFBundlePrimaryIcon")).toMap();
+ QVERIFY2(!primaryIcon.isEmpty(), "Info.plist doesn't contain CFBundlePrimaryIcon key");
+ QCOMPARE(primaryIcon.value(QStringLiteral("CFBundleIconName")), QStringLiteral("AppIcon"));
+ } else {
+ QVERIFY2(false, "Cannot determine bundle type");
+ }
}
void TestBlackboxApple::assetCatalog()
{
QFETCH(bool, flatten);
- const auto xcodeVersion = findXcodeVersion();
QDir::setCurrent(testDataDir + QLatin1String("/ib/assetcatalog"));
rmDirR(relativeBuildDir());
+ if (!findXcode())
+ QSKIP("requires Xcode profile");
+
QbsRunParameters params;
- const auto v = HostOsInfo::hostOsVersion();
const QString flattens = "modules.ib.flatten:" + QString(flatten ? "true" : "false");
- const QString macosTarget = "modules.cpp.minimumMacosVersion:'" + v.toString() + "'";
+ const QString macosTarget = "modules.cpp.minimumMacosVersion:'10.15'";
// Make sure a dry run does not write anything
params.arguments = QStringList() << "-f" << "assetcatalogempty.qbs" << "--dry-run"
@@ -185,44 +382,20 @@ void TestBlackboxApple::assetCatalog()
QCOMPARE(runQbs(params), 0);
QVERIFY(!directoryExists(relativeBuildDir()));
+ if (m_qbsStdout.contains("Skip this test"))
+ QSKIP("Skip this test");
+
params.arguments = QStringList() << "-f" << "assetcatalogempty.qbs"
<< flattens << macosTarget;
QCOMPARE(runQbs(params), 0);
// empty asset catalogs must still produce output
- if (xcodeVersion >= qbs::Version(5))
- QVERIFY((bool)m_qbsStdout.contains("compiling empty.xcassets"));
-
- // should additionally produce raw assets since deployment target will be < 10.9
- // older versions of ibtool generated either raw assets OR .car files;
- // newer versions always generate the .car file regardless of the deployment target
- if (v < qbs::Version(10, 9)) {
- QVERIFY(regularFileExists(relativeProductBuildDir("assetcatalogempty")
- + "/assetcatalogempty.app/Contents/Resources/other.png"));
- QVERIFY(regularFileExists(relativeProductBuildDir("assetcatalogempty")
- + "/assetcatalogempty.app/Contents/Resources/other@2x.png"));
- }
-
- rmDirR(relativeBuildDir());
- params.arguments.push_back("modules.cpp.minimumMacosVersion:'10.10'"); // force CAR generation
- QCOMPARE(runQbs(params), 0);
+ QVERIFY((bool)m_qbsStdout.contains("compiling empty.xcassets"));
// empty asset catalogs must still produce output
- if (xcodeVersion >= qbs::Version(5)) {
- QVERIFY((bool)m_qbsStdout.contains("compiling empty.xcassets"));
- // No matter what, we need a 10.9 host to build CAR files
- if (HostOsInfo::hostOsVersion() >= qbs::Version(10, 9)) {
- QVERIFY(regularFileExists(relativeProductBuildDir("assetcatalogempty")
- + "/assetcatalogempty.app/Contents/Resources/Assets.car"));
- } else {
- QVERIFY(regularFileExists(relativeProductBuildDir("assetcatalogempty")
- + "/assetcatalogempty.app/Contents/Resources/empty.icns"));
- QVERIFY(regularFileExists(relativeProductBuildDir("assetcatalogempty")
- + "/assetcatalogempty.app/Contents/Resources/other.png"));
- QVERIFY(regularFileExists(relativeProductBuildDir("assetcatalogempty")
- + "/assetcatalogempty.app/Contents/Resources/other@2x.png"));
- }
- }
+ 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
@@ -301,19 +474,10 @@ void TestBlackboxApple::bundleStructure()
{
QFETCH(QString, productName);
QFETCH(QString, productTypeIdentifier);
- QFETCH(bool, isShallow);
QDir::setCurrent(testDataDir + "/bundle-structure");
QbsRunParameters params(QStringList{"qbs.installPrefix:''"});
params.arguments << "project.buildableProducts:" + productName;
- if (isShallow) {
- // Coerce shallow bundles - don't set bundle.isShallow directly because we want to test the
- // automatic detection
- const auto xcode5 = findXcodeVersion() >= qbs::Version(5);
- params.arguments
- << "modules.qbs.targetPlatform:ios"
- << (xcode5 ? "qbs.architectures:arm64" : "qbs.architectures:armv7a");
- }
if (productName == "ABadApple" || productName == "ABadThirdParty")
params.expectFailure = true;
@@ -330,7 +494,10 @@ void TestBlackboxApple::bundleStructure()
QCOMPARE(status, 0);
- if (!isShallow) {
+ if (m_qbsStdout.contains("bundle.isShallow: false")) {
+ // Test shallow bundles detection - bundles are not shallow only on macOS, so also check
+ // the qbs.targetOS property
+ QVERIFY2(m_qbsStdout.contains("qbs.targetOS: macos"), m_qbsStdout);
if (productName == "A") {
QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app").isRegularDir());
QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/Contents").isRegularDir());
@@ -424,7 +591,9 @@ void TestBlackboxApple::bundleStructure()
QVERIFY(QFileInfo2(defaultInstallRoot + "/G/ContentInfo.plist").isRegularFile());
QVERIFY(QFileInfo2(defaultInstallRoot + "/G/Contents/resource.txt").isRegularFile());
}
- } else {
+ } else if (m_qbsStdout.contains("bundle.isShallow: true")) {
+ QVERIFY2(m_qbsStdout.contains("qbs.targetOS:"), m_qbsStdout);
+ QVERIFY2(!m_qbsStdout.contains("qbs.targetOS: macos"), m_qbsStdout);
if (productName == "A") {
QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app").isRegularDir());
QVERIFY(QFileInfo2(defaultInstallRoot + "/A.app/A").isRegularFile());
@@ -502,6 +671,8 @@ void TestBlackboxApple::bundleStructure()
QVERIFY(QFileInfo2(defaultInstallRoot + "/G/ContentInfo.plist").isRegularFile());
QVERIFY(QFileInfo2(defaultInstallRoot + "/G/Contents/resource.txt").isRegularFile());
}
+ } else {
+ QVERIFY2(false, qPrintable(m_qbsStdout));
}
}
@@ -511,21 +682,127 @@ void TestBlackboxApple::bundleStructure_data()
QTest::addColumn<QString>("productTypeIdentifier");
QTest::addColumn<bool>("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;
- };
+ QTest::newRow("A") << "A" << "com.apple.product-type.application";
+ QTest::newRow("ABadApple") << "ABadApple" << "com.apple.product-type.will.never.exist.ever.guaranteed";
+ QTest::newRow("ABadThirdParty") << "ABadThirdParty" << "org.special.third.party.non.existent.product.type";
+ QTest::newRow("B") << "B" << "com.apple.product-type.framework";
+ QTest::newRow("C") << "C" << "com.apple.product-type.framework.static";
+ QTest::newRow("D") << "D" << "com.apple.product-type.bundle";
+ QTest::newRow("E") << "E" << "com.apple.product-type.app-extension";
+ QTest::newRow("F") << "F" << "com.apple.product-type.xpc-service";
+ QTest::newRow("G") << "G" << "com.apple.product-type.in-app-purchase-content";
+}
+
+void TestBlackboxApple::byteArrayInfoPlist()
+{
+ QDir::setCurrent(testDataDir + "/byteArrayInfoPlist");
+
+ QCOMPARE(runQbs(), 0);
+
+ const auto infoPlistPath = getInfoPlistPath(
+ relativeProductBuildDir("byteArrayInfoPlist") + "/byteArrayInfoPlist.app");
+ QVERIFY(QFile::exists(infoPlistPath));
+ const auto outFilePath =
+ relativeProductBuildDir("byteArrayInfoPlist") + "/bytearrayInfoPlist-Info.plist.out";
+ QFile file(outFilePath);
+ QVERIFY(file.exists());
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QCOMPARE(file.readAll(), "The data value");
+}
+
+void TestBlackboxApple::codesign()
+{
+ QFETCH(int, expectedCount);
+ QFETCH(bool, isBundle);
+ QFETCH(bool, enableSigning);
+ QFETCH(bool, multiArch);
+ QFETCH(bool, multiVariant);
+
+ const auto xcodeVersion = findXcodeVersion();
+
+ if (!xcodeVersion)
+ QSKIP("requires Xcode profile");
+
+ QDir::setCurrent(testDataDir + "/codesign");
+ QbsRunParameters params(QStringList{"qbs.installPrefix:''"});
+ // the test can't use xcode module to determine version itself
+ params.arguments << QStringLiteral("project.xcodeVersion:") + xcodeVersion->toString();
+ params.arguments << QStringLiteral("project.isBundle:%1").arg(isBundle ? "true" : "false");
+ params.arguments << QStringLiteral("project.enableSigning:%1")
+ .arg(enableSigning ? "true" : "false");
+ params.arguments << QStringLiteral("project.multiArch:%1").arg(multiArch ? "true" : "false");
+ params.arguments << QStringLiteral("project.multiVariant:%1")
+ .arg(multiVariant ? "true" : "false");
+
+ rmDirR(relativeBuildDir());
+ QCOMPARE(runQbs(params), 0);
+
+ const int codeSignCount =
+ QString::fromUtf8(m_qbsStdout).count(QStringLiteral("codesign"));
+ QCOMPARE(codeSignCount, expectedCount);
+
+ const auto appName = isBundle ? QStringLiteral("A.app") : QStringLiteral("A");
+ const auto appPath = defaultInstallRoot + "/" + appName;
+ QVERIFY(QFileInfo(appPath).exists());
+ auto codeSignInfo = getCodeSignInfo(appPath);
+ QVERIFY(codeSignInfo.first != CodeSignResult::Failed);
+ QCOMPARE(codeSignInfo.first == CodeSignResult::Signed, enableSigning);
+ QCOMPARE(codeSignInfo.second.isEmpty(), !enableSigning);
+ if (!codeSignInfo.second.isEmpty()) {
+ QVERIFY(codeSignInfo.second.contains(QByteArrayLiteral("Executable")));
+ QVERIFY(codeSignInfo.second.contains(QByteArrayLiteral("Identifier")));
+ QCOMPARE(codeSignInfo.second.value(QByteArrayLiteral("Signature")), "adhoc");
+ }
+
+ const auto libName =
+ isBundle ? QStringLiteral("B.framework") : QStringLiteral("libB.1.0.0.dylib");
+ const auto libPath = defaultInstallRoot + "/" + libName;
+ QVERIFY(QFileInfo(libPath).exists());
+ codeSignInfo = getCodeSignInfo(libPath);
+ QVERIFY(codeSignInfo.first != CodeSignResult::Failed);
+ QCOMPARE(codeSignInfo.first == CodeSignResult::Signed, enableSigning);
+ QCOMPARE(codeSignInfo.second.isEmpty(), !enableSigning);
+ if (!codeSignInfo.second.isEmpty()) {
+ QVERIFY(codeSignInfo.second.contains(QByteArrayLiteral("Executable")));
+ QVERIFY(codeSignInfo.second.contains(QByteArrayLiteral("Identifier")));
+ QCOMPARE(codeSignInfo.second.value(QByteArrayLiteral("Signature")), "adhoc");
+ }
+
+ const auto pluginPath = defaultInstallRoot + "/" + QStringLiteral("C.bundle");
+ QVERIFY(QFileInfo(pluginPath).exists());
+ QVERIFY(QFileInfo(pluginPath).isDir() == isBundle);
+ codeSignInfo = getCodeSignInfo(pluginPath);
+ QVERIFY(codeSignInfo.first != CodeSignResult::Failed);
+ QCOMPARE(codeSignInfo.first == CodeSignResult::Signed, enableSigning);
+ QCOMPARE(codeSignInfo.second.isEmpty(), !enableSigning);
+ if (!codeSignInfo.second.isEmpty()) {
+ QVERIFY(codeSignInfo.second.contains(QByteArrayLiteral("Executable")));
+ QVERIFY(codeSignInfo.second.contains(QByteArrayLiteral("Identifier")));
+ QCOMPARE(codeSignInfo.second.value(QByteArrayLiteral("Signature")), "adhoc");
+ }
+}
- addRows(true);
- addRows(false);
+void TestBlackboxApple::codesign_data()
+{
+ QTest::addColumn<int>("expectedCount");
+ QTest::addColumn<bool>("isBundle");
+ QTest::addColumn<bool>("enableSigning");
+ QTest::addColumn<bool>("multiArch");
+ QTest::addColumn<bool>("multiVariant");
+
+ QTest::newRow("standalone, unsigned") << 0 << false << false << false << false;
+ QTest::newRow("bundle, unsigned") << 0 << true << false << false << false;
+ QTest::newRow("standalone, signed") << 3 << false << true << false << false;
+ QTest::newRow("bundle, signed") << 3 << true << true << false << false;
+ // here we only sign the resulting lipo artifact
+ QTest::newRow("standalone, signed, multiarch") << 3 << false << true << true << false;
+ QTest::newRow("bundle, signed, multiarch") << 3 << true << true << true << false;
+ // here we sign all artifacts
+ QTest::newRow("standalone, signed, multivariant") << 15 << false << true << false << true;
+ QTest::newRow("bundle, signed, multivariant") << 15 << true << true << false << true;
+ QTest::newRow("standalone, signed, multiarch, multivariant")
+ << 15 << false << true << true << true;
+ QTest::newRow("bundle, signed, multiarch, multivariant") << 15 << true << true << true << true;
}
void TestBlackboxApple::deploymentTarget()
@@ -538,6 +815,9 @@ void TestBlackboxApple::deploymentTarget()
QDir::setCurrent(testDataDir + "/deploymentTarget");
+ if (!findXcode())
+ QSKIP("requires Xcode profile");
+
QbsRunParameters params;
params.arguments = QStringList()
<< "--command-echo-mode"
@@ -591,52 +871,54 @@ void TestBlackboxApple::deploymentTarget_data()
}
QTest::newRow("macos x86_64") << "macosx" << macos << "x86_64"
<< "-triple x86_64-apple-macosx10.6"
- << "-macosx_version_min 10.6";
+ << "10.6";
if (xcodeVersion >= qbs::Version(6))
QTest::newRow("macos x86_64h") << "macosx" << macos << "x86_64h"
<< "-triple x86_64h-apple-macosx10.12"
- << "-macosx_version_min 10.12";
+ << "10.12";
QTest::newRow("ios armv7a") << "iphoneos" << ios << "armv7a"
<< "-triple thumbv7-apple-ios6.0"
- << "-iphoneos_version_min 6.0";
+ << "6.0";
QTest::newRow("ios armv7s") << "iphoneos" <<ios << "armv7s"
<< "-triple thumbv7s-apple-ios7.0"
- << "-iphoneos_version_min 7.0";
+ << "7.0";
if (xcodeVersion >= qbs::Version(5))
QTest::newRow("ios arm64") << "iphoneos" <<ios << "arm64"
<< "-triple arm64-apple-ios7.0"
- << "-iphoneos_version_min 7.0";
+ << "7.0";
QTest::newRow("ios-simulator x86") << "iphonesimulator" << ios_sim << "x86"
<< "-triple i386-apple-ios6.0"
- << "-ios_simulator_version_min 6.0";
+ << "6.0";
if (xcodeVersion >= qbs::Version(5))
QTest::newRow("ios-simulator x86_64") << "iphonesimulator" << ios_sim << "x86_64"
<< "-triple x86_64-apple-ios7.0"
- << "-ios_simulator_version_min 7.0";
+ << "7.0";
if (xcodeVersion >= qbs::Version(7)) {
if (xcodeVersion >= qbs::Version(7, 1)) {
QTest::newRow("tvos arm64") << "appletvos" << tvos << "arm64"
<< "-triple arm64-apple-tvos9.0"
- << "-tvos_version_min 9.0";
+ << "9.0";
QTest::newRow("tvos-simulator x86_64") << "appletvsimulator" << tvos_sim << "x86_64"
<< "-triple x86_64-apple-tvos9.0"
- << "-tvos_simulator_version_min 9.0";
+ << "9.0";
}
QTest::newRow("watchos armv7k") << "watchos" << watchos << "armv7k"
<< "-triple thumbv7k-apple-watchos2.0"
- << "-watchos_version_min 2.0";
+ << "2.0";
QTest::newRow("watchos-simulator x86") << "watchsimulator" << watchos_sim << "x86"
<< "-triple i386-apple-watchos2.0"
- << "-watchos_simulator_version_min 2.0";
+ << "2.0";
}
}
void TestBlackboxApple::dmg()
{
+ if (qEnvironmentVariableIsSet("GITHUB_ACTIONS"))
+ QSKIP("Skip this test when running on GitHub");
QDir::setCurrent(testDataDir + "/apple-dmg");
QCOMPARE(runQbs(), 0);
}
@@ -669,16 +951,29 @@ void TestBlackboxApple::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"));
+ if (m_qbsStdout.contains("isShallow: false")) {
+ 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"));
+ } else if (m_qbsStdout.contains("isShallow: true")) {
+ QVERIFY(directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/Headers"));
+ QVERIFY(directoryExists(relativeProductBuildDir("Widget") + "/Widget.framework/PrivateHeaders"));
+ QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Widget"));
+ QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Headers/Widget.h"));
+ QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/PrivateHeaders/WidgetPrivate.h"));
+ QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/BaseResource"));
+ QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/en.lproj/EnglishResource"));
+ QVERIFY(regularFileExists(relativeProductBuildDir("Widget") + "/Widget.framework/Widget"));
+ } else {
+ QVERIFY2(false, qPrintable(m_qbsStdout));
+ }
params.command = "resolve";
params.arguments = QStringList() << "project.includeHeaders:false";
@@ -708,7 +1003,15 @@ void TestBlackboxApple::iconsetApp()
params.arguments = QStringList() << "-f" << "iconsetapp.qbs";
QCOMPARE(runQbs(params), 0);
- QVERIFY(regularFileExists(relativeProductBuildDir("iconsetapp") + "/iconsetapp.app/Contents/Resources/white.icns"));
+ if (m_qbsStdout.contains("isShallow: false")) {
+ QVERIFY(regularFileExists(relativeProductBuildDir("iconsetapp")
+ + "/iconsetapp.app/Contents/Resources/white.icns"));
+ } else if (m_qbsStdout.contains("isShallow: true")) {
+ QVERIFY(regularFileExists(relativeProductBuildDir("iconsetapp")
+ + "/iconsetapp.app/white.icns"));
+ } else {
+ QVERIFY2(false, qPrintable(m_qbsStdout));
+ }
}
void TestBlackboxApple::infoPlist()
@@ -719,40 +1022,74 @@ void TestBlackboxApple::infoPlist()
params.arguments = QStringList() << "-f" << "infoplist.qbs";
QCOMPARE(runQbs(params), 0);
- auto infoplistPath = relativeProductBuildDir("infoplist")
- + "/infoplist.app/Contents/Info.plist";
- if (!QFile::exists(infoplistPath))
- infoplistPath = relativeProductBuildDir("infoplist") + "/infoplist.app/Info.plist";
- QVERIFY(QFile::exists(infoplistPath));
- QProcess plutil;
- plutil.start("plutil", {
- QStringLiteral("-convert"),
- QStringLiteral("json"),
- infoplistPath
- });
- QVERIFY2(plutil.waitForStarted(), qPrintable(plutil.errorString()));
- QVERIFY2(plutil.waitForFinished(), qPrintable(plutil.errorString()));
- QVERIFY2(plutil.exitCode() == 0, qPrintable(plutil.readAllStandardError().constData()));
+ const auto infoPlistPath = getInfoPlistPath(
+ relativeProductBuildDir("infoplist") + "/infoplist.app");
+ QVERIFY(QFile::exists(infoPlistPath));
+ const auto content = readInfoPlistFile(infoPlistPath);
+ QVERIFY(!content.isEmpty());
- QFile infoplist(infoplistPath);
- QVERIFY(infoplist.open(QIODevice::ReadOnly));
- QJsonParseError error;
- const auto json = QJsonDocument::fromJson(infoplist.readAll(), &error);
- QCOMPARE(error.error, QJsonParseError::NoError);
- QVERIFY(json.isObject());
// common values
- QCOMPARE(json.object().value(QStringLiteral("CFBundleIdentifier")),
+ QCOMPARE(content.value(QStringLiteral("CFBundleIdentifier")),
QStringLiteral("org.example.infoplist"));
- QCOMPARE(json.object().value(QStringLiteral("CFBundleName")), QStringLiteral("infoplist"));
- QCOMPARE(json.object().value(QStringLiteral("CFBundleExecutable")),
+ QCOMPARE(content.value(QStringLiteral("CFBundleName")), QStringLiteral("infoplist"));
+ QCOMPARE(content.value(QStringLiteral("CFBundleExecutable")),
QStringLiteral("infoplist"));
- if (!json.object().contains(QStringLiteral("SDKROOT"))) { // macOS-specific values
- QCOMPARE(json.object().value("LSMinimumSystemVersion"), QStringLiteral("10.7"));
- QVERIFY(json.object().contains("NSPrincipalClass"));
+ if (!content.contains(QStringLiteral("SDKROOT"))) { // macOS-specific values
+ QCOMPARE(content.value("LSMinimumSystemVersion"), QStringLiteral("10.7"));
+ QCOMPARE(content.value("NSPrincipalClass"), QStringLiteral("NSApplication"));
+ QCOMPARE(content.value(QStringLiteral("NSSupportsAutomaticGraphicsSwitching")), true);
+ } else {
+ // QBS-1447: UIDeviceFamily was set to a string instead of an array
+ const auto family = content.value(QStringLiteral("UIDeviceFamily"));
+ if (family.isValid()) {
+ // Prior to Qt 5.15, int gets converted to a double when exporting plist as JSON
+ QVERIFY(testVariantListType(family, QMetaType::LongLong)
+ || testVariantListType(family, QMetaType::Double));
+ }
+ const auto caps = content.value(QStringLiteral("UIRequiredDeviceCapabilities"));
+ if (caps.isValid())
+ QVERIFY(testVariantListType(caps, QMetaType::QString));
+ const auto orientations = content.value(QStringLiteral("UIRequiredDeviceCapabilities"));
+ if (orientations.isValid())
+ QVERIFY(testVariantListType(orientations, QMetaType::QString));
}
}
+void TestBlackboxApple::infoPlistVariables()
+{
+ QDir::setCurrent(testDataDir + "/infoPlistVariables");
+
+ QbsRunParameters params;
+ params.arguments = QStringList() << "-f" << "infoPlistVariables.qbs";
+ QCOMPARE(runQbs(params), 0);
+
+ const auto infoPlistPath = getInfoPlistPath(
+ relativeProductBuildDir("infoPlistVariables") + "/infoPlistVariables.app");
+ QVERIFY(QFile::exists(infoPlistPath));
+ const auto content = readInfoPlistFile(infoPlistPath);
+ QVERIFY(!content.isEmpty());
+
+ QCOMPARE(content.value(QStringLiteral("Curly")),
+ QStringLiteral("infoPlistVariables"));
+ QCOMPARE(content.value(QStringLiteral("Braces")),
+ QStringLiteral("infoPlistVariables"));
+ QCOMPARE(content.value(QStringLiteral("At")),
+ QStringLiteral("infoPlistVariables"));
+ QCOMPARE(content.value(QStringLiteral("CurlyMult")),
+ QStringLiteral("infoPlistVariables_infoPlistVariables"));
+ QCOMPARE(content.value(QStringLiteral("BracesMult")),
+ QStringLiteral("infoPlistVariables_infoPlistVariables"));
+ QCOMPARE(content.value(QStringLiteral("AtMult")),
+ QStringLiteral("infoPlistVariables_infoPlistVariables"));
+ QCOMPARE(content.value(QStringLiteral("CurlyNested")),
+ QStringLiteral("infoPlistVariables"));
+ QCOMPARE(content.value(QStringLiteral("BracesNested")),
+ QStringLiteral("infoPlistVariables"));
+ QCOMPARE(content.value(QStringLiteral("WithDefault")),
+ QStringLiteral("DEFAULT"));
+}
+
void TestBlackboxApple::objcArc()
{
QDir::setCurrent(testDataDir + QLatin1String("/objc-arc"));
@@ -760,8 +1097,36 @@ void TestBlackboxApple::objcArc()
QCOMPARE(runQbs(), 0);
}
+void TestBlackboxApple::overrideInfoPlist()
+{
+ QDir::setCurrent(testDataDir + "/overrideInfoPlist");
+
+ QCOMPARE(runQbs(), 0);
+
+ const auto infoPlistPath = getInfoPlistPath(
+ relativeProductBuildDir("overrideInfoPlist") + "/overrideInfoPlist.app");
+ QVERIFY(QFile::exists(infoPlistPath));
+ const auto content = readInfoPlistFile(infoPlistPath);
+ QVERIFY(!content.isEmpty());
+
+ // test we do not override custom values by default
+ QCOMPARE(content.value(QStringLiteral("DefaultValue")),
+ QStringLiteral("The default value"));
+ // test we can override custom values
+ QCOMPARE(content.value(QStringLiteral("OverriddenValue")),
+ QStringLiteral("The overridden value"));
+ // test we do not override special values set by Qbs by default
+ QCOMPARE(content.value(QStringLiteral("CFBundleExecutable")),
+ QStringLiteral("overrideInfoPlist"));
+ // test we can override special values set by Qbs
+ QCOMPARE(content.value(QStringLiteral("CFBundleName")), QStringLiteral("My Bundle"));
+}
+
void TestBlackboxApple::xcode()
{
+ if (!findXcode())
+ QSKIP("requires Xcode profile");
+
QProcess xcodeSelect;
xcodeSelect.start("xcode-select", QStringList() << "--print-path");
QVERIFY2(xcodeSelect.waitForStarted(), qPrintable(xcodeSelect.errorString()));
@@ -775,8 +1140,10 @@ void TestBlackboxApple::xcode()
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()));
- const auto lines = QString::fromLocal8Bit(xcodebuildShowSdks.readAllStandardOutput().trimmed()).split('\n', QString::SkipEmptyParts);
+ QVERIFY2(xcodebuildShowSdks.exitCode() == 0,
+ qPrintable(xcodebuildShowSdks.readAllStandardError().constData()));
+ const auto lines = QString::fromLocal8Bit(xcodebuildShowSdks.readAllStandardOutput().trimmed())
+ .split('\n', Qt::SkipEmptyParts);
for (const QString &line : lines) {
static const std::regex regexp("^.+\\s+\\-sdk\\s+([a-z]+)([0-9]+\\.[0-9]+)$");
const auto ln = line.toStdString();
@@ -812,11 +1179,10 @@ void TestBlackboxApple::xcode()
QTEST_MAIN(TestBlackboxApple)
-QVariantMap TestBlackboxApple::findXcode(int *status)
+std::optional<QVariantMap> TestBlackboxApple::findXcode(int *status)
{
QTemporaryDir temp;
QbsRunParameters params = QStringList({"-f", testDataDir + "/find/find-xcode.qbs"});
- params.profile = "none";
params.buildDirectory = temp.path();
const int res = runQbs(params);
if (status)
@@ -825,10 +1191,16 @@ QVariantMap TestBlackboxApple::findXcode(int *status)
+ "/xcode.json");
if (!file.open(QIODevice::ReadOnly))
return {};
- return QJsonDocument::fromJson(file.readAll()).toVariant().toMap();
+ auto result = QJsonDocument::fromJson(file.readAll()).toVariant().toMap();
+ if (!result["present"].toBool())
+ return {};
+ return result;
}
-qbs::Version TestBlackboxApple::findXcodeVersion()
+std::optional<qbs::Version> TestBlackboxApple::findXcodeVersion()
{
- return qbs::Version::fromString(findXcode().value("version").toString());
+ const auto xcode = findXcode();
+ if (!xcode)
+ return {};
+ return qbs::Version::fromString(xcode->value("version").toString());
}
diff --git a/tests/auto/blackbox/tst_blackboxapple.h b/tests/auto/blackbox/tst_blackboxapple.h
index 76711ddf5..a51414917 100644
--- a/tests/auto/blackbox/tst_blackboxapple.h
+++ b/tests/auto/blackbox/tst_blackboxapple.h
@@ -31,6 +31,8 @@
#include "tst_blackboxbase.h"
+#include <optional>
+
namespace qbs {
class Version;
} // namespace qbs
@@ -48,12 +50,16 @@ public slots:
private slots:
void appleMultiConfig();
void aggregateDependencyLinking();
+ void appiconset();
void assetCatalog();
void assetCatalog_data();
void assetCatalogsEmpty();
void assetCatalogsMultiple();
void bundleStructure();
void bundleStructure_data();
+ void byteArrayInfoPlist();
+ void codesign();
+ void codesign_data();
void deploymentTarget();
void deploymentTarget_data();
void dmg();
@@ -62,12 +68,14 @@ private slots:
void iconset();
void iconsetApp();
void infoPlist();
+ void infoPlistVariables();
void objcArc();
+ void overrideInfoPlist();
void xcode();
private:
- QVariantMap findXcode(int *status = nullptr);
- qbs::Version findXcodeVersion();
+ std::optional<QVariantMap> findXcode(int *status = nullptr);
+ std::optional<qbs::Version> findXcodeVersion();
};
#endif // TST_BLACKBOXAPPLE_H
diff --git a/tests/auto/blackbox/tst_blackboxbaremetal.cpp b/tests/auto/blackbox/tst_blackboxbaremetal.cpp
new file mode 100644
index 000000000..ec6ffcdc6
--- /dev/null
+++ b/tests/auto/blackbox/tst_blackboxbaremetal.cpp
@@ -0,0 +1,311 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://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 "tst_blackboxbaremetal.h"
+
+#include "../shared.h"
+
+#include <QtCore/qdir.h>
+#include <QtCore/qregularexpression.h>
+
+static bool extractToolset(const QByteArray &output,
+ QByteArray &toolchain, QByteArray &architecture)
+{
+ const QRegularExpression re("%%([\\w\\-]+)%%, %%(\\w+)%%");
+ QRegularExpressionMatchIterator it = re.globalMatch(output);
+ if (!it.hasNext())
+ return false;
+ const QRegularExpressionMatch match = it.next();
+ toolchain = match.captured(1).toLocal8Bit();
+ architecture = match.captured(2).toLocal8Bit();
+ return true;
+}
+
+static bool extractCompilerIncludePaths(const QByteArray &output, QStringList &compilerIncludePaths)
+{
+ const QRegularExpression re("%%([^%%]+)%%");
+ QRegularExpressionMatchIterator it = re.globalMatch(output);
+ if (!it.hasNext())
+ return false;
+ const QRegularExpressionMatch match = it.next();
+ compilerIncludePaths = match.captured(1).split(",");
+ return true;
+}
+
+static bool extractQuitedValue(const QByteArray &output, QString &pattern)
+{
+ const QRegularExpression re("%%(.+)%%");
+ const QRegularExpressionMatch match = re.match(output);
+ if (!match.hasMatch())
+ return false;
+ pattern = match.captured(1);
+ return true;
+}
+
+static QByteArray unsupportedToolsetMessage(const QByteArray &output)
+{
+ QByteArray toolchain;
+ QByteArray architecture;
+ extractToolset(output, toolchain, architecture);
+ return "Unsupported toolchain '" + toolchain
+ + "' for architecture '" + architecture + "'";
+}
+
+static QByteArray brokenProbeMessage(const QByteArray &output)
+{
+ QByteArray toolchain;
+ QByteArray architecture;
+ extractToolset(output, toolchain, architecture);
+ return "Broken probe for toolchain '" + toolchain
+ + "' for architecture '" + architecture + "'";
+}
+
+TestBlackboxBareMetal::TestBlackboxBareMetal()
+ : TestBlackboxBase (SRCDIR "/testdata-baremetal", "blackbox-baremetal")
+{
+}
+
+void TestBlackboxBareMetal::targetPlatform()
+{
+ QDir::setCurrent(testDataDir + "/target-platform");
+ QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0);
+ if (m_qbsStdout.contains("unsupported toolset:"))
+ QSKIP(unsupportedToolsetMessage(m_qbsStdout));
+ const bool hasNoPlatform = m_qbsStdout.contains("has no platform: true");
+ QCOMPARE(hasNoPlatform, true);
+ const bool hasNoOS = m_qbsStdout.contains("has no os: true");
+ QCOMPARE(hasNoOS, true);
+}
+
+void TestBlackboxBareMetal::application_data()
+{
+ QTest::addColumn<QString>("testPath");
+ QTest::newRow("one-object-application") << "/one-object-application";
+ QTest::newRow("two-object-application") << "/two-object-application";
+ QTest::newRow("one-object-asm-application") << "/one-object-asm-application";
+}
+
+void TestBlackboxBareMetal::application()
+{
+ QFETCH(QString, testPath);
+ QDir::setCurrent(testDataDir + testPath);
+ QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0);
+ if (m_qbsStdout.contains("unsupported toolset:"))
+ QSKIP(unsupportedToolsetMessage(m_qbsStdout));
+ QCOMPARE(runQbs(QbsRunParameters("build")), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+ QCOMPARE(runQbs(QbsRunParameters("run")), 0);
+}
+
+void TestBlackboxBareMetal::staticLibraryDependencies()
+{
+ QDir::setCurrent(testDataDir + "/static-library-dependencies");
+ QCOMPARE(runQbs(QStringList{"-p", "lib-a,lib-b,lib-c,lib-d,lib-e"}), 0);
+ QCOMPARE(runQbs(QStringList{"--command-echo-mode", "command-line"}), 0);
+ const QByteArray output = m_qbsStdout + '\n' + m_qbsStderr;
+ QVERIFY(output.contains("lib-a"));
+ QVERIFY(output.contains("lib-b"));
+ QVERIFY(output.contains("lib-c"));
+ QVERIFY(output.contains("lib-d"));
+ QVERIFY(output.contains("lib-e"));
+}
+
+void TestBlackboxBareMetal::externalStaticLibraries()
+{
+ QDir::setCurrent(testDataDir + "/external-static-libraries");
+ QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0);
+ if (m_qbsStdout.contains("unsupported toolset:"))
+ QSKIP(unsupportedToolsetMessage(m_qbsStdout));
+ QCOMPARE(runQbs(), 0);
+}
+
+void TestBlackboxBareMetal::sharedLibraries()
+{
+ QDir::setCurrent(testDataDir + "/shared-libraries");
+ QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0);
+ if (m_qbsStdout.contains("unsupported toolset:"))
+ QSKIP(unsupportedToolsetMessage(m_qbsStdout));
+ QCOMPARE(runQbs(QbsRunParameters("build")), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+ QCOMPARE(runQbs(QbsRunParameters("run")), 0);
+ QVERIFY2(m_qbsStdout.contains("Hello from app"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("Hello from lib"), m_qbsStdout.constData());
+}
+
+void TestBlackboxBareMetal::userIncludePaths()
+{
+ QDir::setCurrent(testDataDir + "/user-include-paths");
+ QCOMPARE(runQbs(), 0);
+}
+
+void TestBlackboxBareMetal::systemIncludePaths()
+{
+ QDir::setCurrent(testDataDir + "/system-include-paths");
+ QCOMPARE(runQbs(), 0);
+}
+
+void TestBlackboxBareMetal::distributionIncludePaths()
+{
+ QDir::setCurrent(testDataDir + "/distribution-include-paths");
+ QCOMPARE(runQbs(), 0);
+}
+
+void TestBlackboxBareMetal::compilerIncludePaths()
+{
+ QDir::setCurrent(testDataDir + "/compiler-include-paths");
+ QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0);
+ if (!m_qbsStdout.contains("compilerIncludePaths:"))
+ QFAIL("No compiler include paths exists");
+
+ QStringList includePaths;
+ QVERIFY(extractCompilerIncludePaths(m_qbsStdout, includePaths));
+ QVERIFY(includePaths.count() > 0);
+ for (const auto &includePath : includePaths) {
+ const QDir dir(includePath);
+ QVERIFY(dir.exists());
+ }
+}
+
+void TestBlackboxBareMetal::preincludeHeaders()
+{
+ QDir::setCurrent(testDataDir + "/preinclude-headers");
+ QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0);
+ if (m_qbsStdout.contains("unsupported toolset:"))
+ QSKIP(unsupportedToolsetMessage(m_qbsStdout));
+ QCOMPARE(runQbs(), 0);
+}
+
+void TestBlackboxBareMetal::defines()
+{
+ QDir::setCurrent(testDataDir + "/defines");
+ QCOMPARE(runQbs(), 0);
+}
+
+void TestBlackboxBareMetal::compilerListingFiles_data()
+{
+ QTest::addColumn<bool>("generateListing");
+ QTest::addColumn<QString>("customListingSuffix");
+ QTest::newRow("do-not-generate-compiler-listing") << false << "";
+ QTest::newRow("generate-default-compiler-listing") << true << "";
+ QTest::newRow("generate-custom-compiler-listing") << true << ".lll";
+}
+
+void TestBlackboxBareMetal::compilerListingFiles()
+{
+ QFETCH(bool, generateListing);
+ QFETCH(QString, customListingSuffix);
+ QDir::setCurrent(testDataDir + "/compiler-listing");
+
+ rmDirR(relativeBuildDir());
+ QStringList args = {QStringLiteral("modules.cpp.generateCompilerListingFiles:%1")
+ .arg(generateListing ? "true" : "false")};
+ if (!customListingSuffix.isEmpty())
+ args << QStringLiteral("modules.cpp.compilerListingSuffix:%1").arg(customListingSuffix);
+
+ QCOMPARE(runQbs(QbsRunParameters("resolve", args)), 0);
+ if (m_qbsStdout.contains("unsupported toolset:"))
+ QSKIP(unsupportedToolsetMessage(m_qbsStdout));
+ if (!m_qbsStdout.contains("compiler listing suffix:"))
+ QFAIL("No current compiler listing suffix pattern exists");
+
+ QString compilerListingSuffix;
+ if (!extractQuitedValue(m_qbsStdout, compilerListingSuffix))
+ QFAIL("Unable to extract current compiler listing suffix");
+
+ if (!customListingSuffix.isEmpty())
+ QCOMPARE(compilerListingSuffix, customListingSuffix);
+
+ QCOMPARE(runQbs(QbsRunParameters(args)), 0);
+ const QString productBuildDir = relativeProductBuildDir("compiler-listing");
+ const QString hash = inputDirHash(".");
+ const QString mainListing = productBuildDir + "/" + hash
+ + "/main.c" + compilerListingSuffix;
+ QCOMPARE(regularFileExists(mainListing), generateListing);
+ const QString funListing = productBuildDir + "/" + hash
+ + "/fun.c" + compilerListingSuffix;
+ QCOMPARE(regularFileExists(funListing), generateListing);
+}
+
+void TestBlackboxBareMetal::linkerMapFile_data()
+{
+ QTest::addColumn<bool>("generateMap");
+ QTest::addColumn<QString>("customMapSuffix");
+ QTest::newRow("do-not-generate-linker-map") << false << "";
+ QTest::newRow("generate-default-linker-map") << true << "";
+ QTest::newRow("generate-custom-linker-map") << true << ".mmm";
+}
+
+void TestBlackboxBareMetal::linkerMapFile()
+{
+ QFETCH(bool, generateMap);
+ QFETCH(QString, customMapSuffix);
+ QDir::setCurrent(testDataDir + "/linker-map");
+
+ rmDirR(relativeBuildDir());
+ QStringList args = {QStringLiteral("modules.cpp.generateLinkerMapFile:%1")
+ .arg(generateMap ? "true" : "false")};
+ if (!customMapSuffix.isEmpty())
+ args << QStringLiteral("modules.cpp.linkerMapSuffix:%1").arg(customMapSuffix);
+
+ QCOMPARE(runQbs(QbsRunParameters("resolve", args)), 0);
+ if (!m_qbsStdout.contains("linker map suffix:"))
+ QFAIL("No current linker map suffix pattern exists");
+
+ QString linkerMapSuffix;
+ if (!extractQuitedValue(m_qbsStdout, linkerMapSuffix))
+ QFAIL("Unable to extract current linker map suffix");
+
+ if (!customMapSuffix.isEmpty())
+ QCOMPARE(linkerMapSuffix, customMapSuffix);
+
+ QCOMPARE(runQbs(QbsRunParameters(args)), 0);
+ const QString productBuildDir = relativeProductBuildDir("linker-map");
+ const QString linkerMap = productBuildDir + "/linker-map" + linkerMapSuffix;
+ QCOMPARE(regularFileExists(linkerMap), generateMap);
+}
+
+void TestBlackboxBareMetal::compilerDefinesByLanguage()
+{
+ QDir::setCurrent(testDataDir + "/compiler-defines-by-language");
+ QbsRunParameters params(QStringList{ "-f", "compiler-defines-by-language.qbs" });
+ QCOMPARE(runQbs(params), 0);
+}
+
+void TestBlackboxBareMetal::toolchainProbe()
+{
+ QDir::setCurrent(testDataDir + "/toolchain-probe");
+ QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0);
+ if (m_qbsStdout.contains("broken probe:"))
+ QFAIL(brokenProbeMessage(m_qbsStdout));
+}
+
+QTEST_MAIN(TestBlackboxBareMetal)
diff --git a/tests/auto/blackbox/tst_blackboxbaremetal.h b/tests/auto/blackbox/tst_blackboxbaremetal.h
new file mode 100644
index 000000000..9c45fa63c
--- /dev/null
+++ b/tests/auto/blackbox/tst_blackboxbaremetal.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://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 TST_BLACKBOXBAREMETAL_H
+#define TST_BLACKBOXBAREMETAL_H
+
+#include "tst_blackboxbase.h"
+
+class TestBlackboxBareMetal : public TestBlackboxBase
+{
+ Q_OBJECT
+
+public:
+ TestBlackboxBareMetal();
+
+private slots:
+ void targetPlatform();
+
+ void application_data();
+ void application();
+
+ void staticLibraryDependencies();
+ void externalStaticLibraries();
+
+ void sharedLibraries();
+
+ void userIncludePaths();
+ void systemIncludePaths();
+ void distributionIncludePaths();
+ void compilerIncludePaths();
+
+ void preincludeHeaders();
+
+ void defines();
+
+ void compilerListingFiles_data();
+ void compilerListingFiles();
+
+ void linkerMapFile_data();
+ void linkerMapFile();
+
+ void compilerDefinesByLanguage();
+
+ void toolchainProbe();
+
+private:
+
+};
+
+#endif // TST_BLACKBOXBAREMETAL_H
diff --git a/tests/auto/blackbox/tst_blackboxbase.cpp b/tests/auto/blackbox/tst_blackboxbase.cpp
index 61b0271f6..17652e779 100644
--- a/tests/auto/blackbox/tst_blackboxbase.cpp
+++ b/tests/auto/blackbox/tst_blackboxbase.cpp
@@ -64,7 +64,7 @@ static bool supportsSettingsDirOption(const QString &command) {
TestBlackboxBase::TestBlackboxBase(const QString &testDataSrcDir, const QString &testName)
: testDataDir(testWorkDir(testName)),
- testSourceDir(QDir::cleanPath(testDataSrcDir)),
+ testSourceDir(testDataSourceDir(testDataSrcDir)),
qbsExecutableFilePath(initQbsExecutableFilePath()),
defaultInstallRoot(relativeBuildDir() + QLatin1Char('/') + InstallOptions::defaultInstallRoot())
{
@@ -96,14 +96,18 @@ int TestBlackboxBase::runQbs(const QbsRunParameters &params)
process.setWorkingDirectory(params.workingDir);
process.setProcessEnvironment(params.environment);
process.start(qbsExecutableFilePath, args);
+ int exitCode = 0;
if (!process.waitForStarted() || !process.waitForFinished(testTimeoutInMsecs())
|| process.exitStatus() != QProcess::NormalExit) {
- m_qbsStderr = process.readAllStandardError();
if (!params.expectCrash) {
QTest::qFail("qbs did not run correctly", __FILE__, __LINE__);
qDebug("%s", qPrintable(process.errorString()));
}
- return -1;
+ exitCode = -1;
+ } else if (m_qbsStdout.contains("Memory leak:")) {
+ exitCode = 27;
+ } else {
+ exitCode = process.exitCode();
}
m_qbsStderr = process.readAllStandardError();
@@ -111,14 +115,14 @@ int TestBlackboxBase::runQbs(const QbsRunParameters &params)
sanitizeOutput(&m_qbsStderr);
sanitizeOutput(&m_qbsStdout);
const bool shouldLog = (process.exitStatus() != QProcess::NormalExit
- || process.exitCode() != 0) && !params.expectFailure;
+ || exitCode != 0) && !params.expectFailure;
if (!m_qbsStderr.isEmpty()
&& (shouldLog || qEnvironmentVariableIsSet("QBS_AUTOTEST_ALWAYS_LOG_STDERR")))
qDebug("%s", m_qbsStderr.constData());
if (!m_qbsStdout.isEmpty()
&& (shouldLog || qEnvironmentVariableIsSet("QBS_AUTOTEST_ALWAYS_LOG_STDOUT")))
qDebug("%s", m_qbsStdout.constData());
- return process.exitCode();
+ return exitCode;
}
/*!
@@ -194,10 +198,30 @@ void TestBlackboxBase::initTestCase()
QVERIFY(copyDllExportHeader(testSourceDir, testDataDir));
}
+void TestBlackboxBase::validateTestProfile()
+{
+ const SettingsPtr s = settings();
+ if (profileName() != "none" && !s->profiles().contains(profileName()))
+ QFAIL(QByteArray("The build profile '" + profileName().toLocal8Bit() +
+ "' could not be found. Please set it up on your machine."));
+ if (!m_needsQt)
+ return;
+ const QStringList qmakeFilePaths = Profile(profileName(), s.get())
+ .value("moduleProviders.Qt.qmakeFilePaths").toStringList();
+ if (!qmakeFilePaths.empty())
+ return;
+ if (!findExecutable(QStringList{"qmake"}).isEmpty())
+ return;
+ QSKIP(QByteArray("The build profile '" + profileName().toLocal8Bit() +
+ "' is not a valid Qt profile and Qt was not found "
+ "in the global search paths."));
+
+}
+
QString TestBlackboxBase::findExecutable(const QStringList &fileNames)
{
const QStringList path = QString::fromLocal8Bit(qgetenv("PATH"))
- .split(HostOsInfo::pathListSeparator(), QString::SkipEmptyParts);
+ .split(HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts);
for (const QString &fileName : fileNames) {
QFileInfo fi(fileName);
@@ -232,3 +256,21 @@ QMap<QString, QString> TestBlackboxBase::findJdkTools(int *status)
{"jar", QDir::fromNativeSeparators(tools["jar"].toString())}
};
}
+
+qbs::Version TestBlackboxBase::qmakeVersion(const QString &qmakeFilePath)
+{
+ QStringList arguments;
+ arguments << "-query" << "QT_VERSION";
+ QProcess qmakeProcess;
+ qmakeProcess.start(qmakeFilePath, arguments);
+ if (!qmakeProcess.waitForStarted() || !qmakeProcess.waitForFinished()
+ || qmakeProcess.exitStatus() != QProcess::NormalExit) {
+ qDebug() << "qmake '" << qmakeFilePath << "' could not be run.";
+ return qbs::Version();
+ }
+ QByteArray result = qmakeProcess.readAll().simplified();
+ qbs::Version version = qbs::Version::fromString(result);
+ if (!version.isValid())
+ qDebug() << "qmake '" << qmakeFilePath << "' version is not valid.";
+ return version;
+}
diff --git a/tests/auto/blackbox/tst_blackboxbase.h b/tests/auto/blackbox/tst_blackboxbase.h
index 251f3752d..d020b7cd9 100644
--- a/tests/auto/blackbox/tst_blackboxbase.h
+++ b/tests/auto/blackbox/tst_blackboxbase.h
@@ -42,14 +42,14 @@ public:
init();
}
- QbsRunParameters(const QString &cmd, const QStringList &args = QStringList())
- : command(cmd), arguments(args)
+ QbsRunParameters(QString cmd, QStringList args = QStringList())
+ : command(std::move(cmd)), arguments(std::move(args))
{
init();
}
- QbsRunParameters(const QStringList &args)
- : arguments(args)
+ QbsRunParameters(QStringList args)
+ : arguments(std::move(args))
{
init();
}
@@ -60,7 +60,14 @@ public:
expectCrash = false;
profile = profileName();
settingsDir = settings()->baseDirectory();
- environment = QProcessEnvironment::systemEnvironment();
+ environment = defaultEnvironment();
+ }
+
+ static QProcessEnvironment defaultEnvironment()
+ {
+ auto result = QProcessEnvironment::systemEnvironment();
+ result.insert(QStringLiteral("QBS_AUTOTEST_CODE_SIGNING_REQUIRED"), QStringLiteral("0"));
+ return result;
}
QString command;
@@ -84,7 +91,9 @@ public slots:
virtual void initTestCase();
protected:
- virtual void validateTestProfile() { }
+ virtual void validateTestProfile();
+
+ void setNeedsQt() { m_needsQt = true; }
int runQbs(const QbsRunParameters &params = QbsRunParameters());
void rmDirR(const QString &dir);
static QByteArray unifiedLineEndings(const QByteArray &ba);
@@ -92,6 +101,7 @@ protected:
static void ccp(const QString &sourceDirPath, const QString &targetDirPath);
static QString findExecutable(const QStringList &fileNames);
QMap<QString, QString> findJdkTools(int *status);
+ static qbs::Version qmakeVersion(const QString &qmakeFilePath);
const QString testDataDir;
const QString testSourceDir;
@@ -100,6 +110,7 @@ protected:
QByteArray m_qbsStderr;
QByteArray m_qbsStdout;
+ int m_needsQt = false;
};
#endif // TST_BLACKBOXBASE_H
diff --git a/tests/auto/blackbox/tst_blackboxexamples.cpp b/tests/auto/blackbox/tst_blackboxexamples.cpp
new file mode 100644
index 000000000..b6a26b7f3
--- /dev/null
+++ b/tests/auto/blackbox/tst_blackboxexamples.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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 "tst_blackboxexamples.h"
+
+#include <QtCore/qdir.h>
+#include <QtCore/qdiriterator.h>
+
+QStringList TestBlackboxExamples::collectExamples(const QString &dirPath)
+{
+ QStringList result;
+ QDir dir(dirPath);
+ const auto subDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
+ for (const auto &subDir : subDirs) {
+ const auto path = dir.filePath(subDir);
+ if (!QFileInfo::exists(path + "/" + subDir + ".qbs"))
+ continue;
+ result.append(QDir(testDataDir).relativeFilePath(path));
+ }
+ return result;
+}
+
+TestBlackboxExamples::TestBlackboxExamples()
+ : TestBlackboxBase(SRCDIR "/../../../examples/", "blackbox-examples")
+{
+ setNeedsQt();
+}
+
+void TestBlackboxExamples::baremetal_data()
+{
+ QTest::addColumn<QString>("example");
+
+ QDir baremetal(testDataDir + "/baremetal/");
+ const auto subDirs = baremetal.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
+ for (const auto &subDir : subDirs) {
+ const auto examples = collectExamples(baremetal.filePath(subDir));
+ for (const auto &example: examples) {
+ const auto relativePath = baremetal.relativeFilePath(example);
+ QTest::newRow(relativePath.toUtf8().data()) << relativePath;
+ }
+ }
+}
+
+void TestBlackboxExamples::baremetal()
+{
+ QFETCH(QString, example);
+
+ QVERIFY(QDir::setCurrent(testDataDir + "/" + example));
+ QCOMPARE(runQbs(), 0);
+}
+
+void TestBlackboxExamples::examples_data()
+{
+ QTest::addColumn<QString>("example");
+
+ auto examples = collectExamples(testDataDir);
+ examples.append(collectExamples(testDataDir + "/protobuf"));
+ std::sort(examples.begin(), examples.end());
+
+ for (const auto &example: examples) {
+ if (example == u"baremetal")
+ continue;
+ QTest::newRow(example.toUtf8().data()) << example;
+ }
+}
+
+void TestBlackboxExamples::examples()
+{
+ QFETCH(QString, example);
+
+ QVERIFY(QDir::setCurrent(testDataDir + "/" + example));
+ QbsRunParameters params(
+ {QStringLiteral("-f"), QFileInfo(example).fileName() + QStringLiteral(".qbs")});
+ QCOMPARE(runQbs(params), 0);
+}
+
+QTEST_MAIN(TestBlackboxExamples)
diff --git a/tests/auto/blackbox/tst_blackboxexamples.h b/tests/auto/blackbox/tst_blackboxexamples.h
new file mode 100644
index 000000000..bea8be768
--- /dev/null
+++ b/tests/auto/blackbox/tst_blackboxexamples.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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 TST_BLACKBOXEXAMPLES_H
+#define TST_BLACKBOXEXAMPLES_H
+
+#include "tst_blackboxbase.h"
+
+class TestBlackboxExamples : public TestBlackboxBase
+{
+ Q_OBJECT
+
+private:
+ QStringList collectExamples(const QString &dirPath);
+
+public:
+ TestBlackboxExamples();
+
+private slots:
+ void baremetal_data();
+ void baremetal();
+ void examples_data();
+ void examples();
+};
+
+#endif // TST_BLACKBOXEXAMPLES_H
diff --git a/tests/auto/blackbox/tst_blackboxjava.cpp b/tests/auto/blackbox/tst_blackboxjava.cpp
index f7feb0612..4ddd6cdda 100644
--- a/tests/auto/blackbox/tst_blackboxjava.cpp
+++ b/tests/auto/blackbox/tst_blackboxjava.cpp
@@ -39,7 +39,9 @@
using qbs::Internal::HostOsInfo;
using qbs::Profile;
-TestBlackboxJava::TestBlackboxJava() : TestBlackboxBase (SRCDIR "/testdata-java", "blackbox-java")
+TestBlackboxJava::TestBlackboxJava()
+ : TestBlackboxBase (SRCDIR "/testdata-java", "blackbox-java"),
+ m_blacklistedJdks(qgetenv("QBS_AUTOTEST_JDK_BLACKLIST"))
{
}
@@ -73,6 +75,9 @@ void TestBlackboxJava::java()
QSKIP("java.jdkPath not set and automatic detection failed");
}
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Skip test in cross-compiled build");
+
QCOMPARE(status, 0);
const QStringList classFiles =
@@ -93,7 +98,7 @@ void TestBlackboxJava::java()
// Now check whether we correctly predicted the class file output paths.
QCOMPARE(runQbs(QbsRunParameters("clean")), 0);
- for (const QString &classFile : qAsConst(classFiles1)) {
+ for (const QString &classFile : std::as_const(classFiles1)) {
QVERIFY2(!regularFileExists(classFile), qPrintable(classFile));
}
@@ -119,7 +124,7 @@ void TestBlackboxJava::java()
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("Class-Path: random_stuff.jar car_jar.jar"), stdOut.constData());
QVERIFY2(stdOut.contains("Main-Class: Vehicles"), stdOut.constData());
QVERIFY2(stdOut.contains("Some-Property: Some-Value"), stdOut.constData());
QVERIFY2(stdOut.contains("Additional-Property: Additional-Value"), stdOut.constData());
@@ -145,8 +150,11 @@ void TestBlackboxJava::javaDependencyTracking()
QDir::setCurrent(testDataDir + "/java");
QbsRunParameters rp;
rp.arguments.push_back("--check-outputs");
- if (!jdkPath.isEmpty())
+ if (!jdkPath.isEmpty()) {
+ if (m_blacklistedJdks.contains(jdkPath))
+ QSKIP("skipping blacklisted JDK");
rp.arguments << ("modules.java.jdkPath:" + jdkPath);
+ }
if (!javaVersion.isEmpty())
rp.arguments << ("modules.java.languageVersion:'" + javaVersion + "'");
rmDirR(relativeBuildDir());
@@ -167,7 +175,7 @@ void TestBlackboxJava::javaDependencyTracking_data()
auto getSpecificJdkVersion = [](const QString &jdkVersion) -> QString {
if (HostOsInfo::isMacosHost()) {
QProcess java_home;
- java_home.start("/usr/libexec/java_home", QStringList() << "--version" << jdkVersion);
+ java_home.start("/usr/libexec/java_home", {"--version", jdkVersion, "--failfast"});
java_home.waitForFinished();
if (java_home.exitStatus() == QProcess::NormalExit && java_home.exitCode() == 0)
return QString::fromLocal8Bit(java_home.readAllStandardOutput().trimmed());
@@ -184,6 +192,7 @@ void TestBlackboxJava::javaDependencyTracking_data()
"/usr/lib/jvm/java-" + minorVersion + "-openjdk" + dpkgArch("-"), // Debian
"/usr/lib/jvm/java-" + minorVersion + "-openjdk", // Arch
"/usr/lib/jvm/jre-1." + minorVersion + ".0-openjdk", // Fedora
+ "/usr/lib64/jvm/java-1." + minorVersion + ".0-openjdk", // OpenSuSE
};
for (const QString &searchPath : searchPaths) {
if (QFile::exists(searchPath + "/bin/javac"))
@@ -194,7 +203,7 @@ void TestBlackboxJava::javaDependencyTracking_data()
return {};
};
- static const auto knownJdkVersions = QStringList() << "1.6" << "1.7" << "1.8" << "1.9"
+ static const auto knownJdkVersions = QStringList() << "1.7" << "1.8" << "1.9"
<< QString(); // default JDK;
QStringList seenJdkVersions;
for (const auto &jdkVersion : knownJdkVersions) {
diff --git a/tests/auto/blackbox/tst_blackboxjava.h b/tests/auto/blackbox/tst_blackboxjava.h
index 68d8a7f80..e770306df 100644
--- a/tests/auto/blackbox/tst_blackboxjava.h
+++ b/tests/auto/blackbox/tst_blackboxjava.h
@@ -43,6 +43,9 @@ private slots:
void javaDependencyTracking();
void javaDependencyTracking_data();
void javaDependencyTrackingInnerClass();
+
+private:
+ const QStringList m_blacklistedJdks;
};
#endif // TST_BLACKBOX_H
diff --git a/tests/auto/blackbox/tst_blackboxjoblimits.cpp b/tests/auto/blackbox/tst_blackboxjoblimits.cpp
index 89b5f638e..acaf69a5b 100644
--- a/tests/auto/blackbox/tst_blackboxjoblimits.cpp
+++ b/tests/auto/blackbox/tst_blackboxjoblimits.cpp
@@ -39,6 +39,7 @@ public:
TestBlackboxJobLimits();
private slots:
+ void initTestCase();
void jobLimits_data();
void jobLimits();
};
@@ -48,6 +49,16 @@ TestBlackboxJobLimits::TestBlackboxJobLimits()
{
}
+void TestBlackboxJobLimits::initTestCase()
+{
+ TestBlackboxBase::initTestCase();
+
+ QDir::setCurrent(testDataDir + "/job-limits-init");
+ QCOMPARE(runQbs({"resolve"}), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Skip test in cross-compiled build");
+}
+
void TestBlackboxJobLimits::jobLimits_data()
{
QTest::addColumn<int>("projectJobCount");
@@ -143,6 +154,7 @@ void TestBlackboxJobLimits::jobLimits()
SettingsPtr theSettings = settings();
qbs::Internal::TemporaryProfile profile("jobLimitsProfile", theSettings.get());
profile.p.setValue("preferences.jobLimit.singleton", prefsJobCount);
+ profile.p.setValue("baseProfile", profileName());
theSettings->sync();
QbsRunParameters resolveParams("resolve");
resolveParams.profile = profile.p.name();
@@ -150,6 +162,7 @@ void TestBlackboxJobLimits::jobLimits()
<< ("project.productJobCount:" + QString::number(productJobCount))
<< ("project.moduleJobCount:" + QString::number(moduleJobCount));
QCOMPARE(runQbs(resolveParams), 0);
+
QbsRunParameters buildParams;
buildParams.expectFailure = !expectSuccess;
if (cliJobCount != -1)
@@ -165,7 +178,7 @@ void TestBlackboxJobLimits::jobLimits()
else
QVERIFY2(m_qbsStderr.contains("exclusive"), m_qbsStderr.constData());
if (exitCode == 0)
- QCOMPARE(m_qbsStdout.count("Running tool"), 5);
+ QCOMPARE(m_qbsStdout.count("running tool"), 5);
}
QTEST_MAIN(TestBlackboxJobLimits)
diff --git a/tests/auto/blackbox/tst_blackboxproviders.cpp b/tests/auto/blackbox/tst_blackboxproviders.cpp
new file mode 100644
index 000000000..3d64d32d5
--- /dev/null
+++ b/tests/auto/blackbox/tst_blackboxproviders.cpp
@@ -0,0 +1,377 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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 "tst_blackboxproviders.h"
+
+#include "../shared.h"
+
+// #include <tools/hostosinfo.h>
+// #include <tools/profile.h>
+// #include <tools/qttools.h>
+
+// #include <QtCore/qdir.h>
+// #include <QtCore/qregularexpression.h>
+
+// using qbs::Internal::HostOsInfo;
+// using qbs::Profile;
+
+#define WAIT_FOR_NEW_TIMESTAMP() waitForNewTimestamp(testDataDir)
+
+TestBlackboxProviders::TestBlackboxProviders()
+ : TestBlackboxBase(SRCDIR "/testdata-providers", "blackbox-providers")
+{
+}
+
+void TestBlackboxProviders::allowedValues()
+{
+ QFETCH(QStringList, arguments);
+ QFETCH(bool, expectFailure);
+
+ QDir::setCurrent(testDataDir + "/allowed-values");
+ rmDirR(relativeBuildDir());
+ QbsRunParameters params;
+ params.arguments = arguments;
+ params.expectFailure = expectFailure;
+
+ QVERIFY2(runQbs(params) == int(expectFailure), m_qbsStderr);
+}
+
+void TestBlackboxProviders::allowedValues_data()
+{
+ QTest::addColumn<QStringList>("arguments");
+ QTest::addColumn<bool>("expectFailure");
+
+ QTest::newRow("invalid js value") << QStringList{} << true;
+ QTest::newRow("invalid variant value")
+ << QStringList{"moduleProviders.provider.aProperty:three"} << true;
+ QTest::newRow("valid variant value")
+ << QStringList{"moduleProviders.provider.aProperty:one"} << false;
+}
+
+void TestBlackboxProviders::brokenProvider()
+{
+ QDir::setCurrent(testDataDir + "/broken-provider");
+ QbsRunParameters params;
+ params.expectFailure = true;
+ QVERIFY(runQbs(params) != 0);
+
+ QVERIFY(m_qbsStderr.contains("Error executing provider for module 'qbsothermodule'"));
+ QVERIFY(m_qbsStderr.contains("Error executing provider for module 'qbsmetatestmodule'"));
+ QCOMPARE(m_qbsStderr.count("This provider is broken"), 2);
+}
+
+void TestBlackboxProviders::moduleProviders()
+{
+ QDir::setCurrent(testDataDir + "/module-providers");
+
+ // Resolving in dry-run mode must not leave any data behind.
+ QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("-n"))), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+ QCOMPARE(m_qbsStdout.count("Running setup script for mygenerator"), 2);
+ QVERIFY(!QFile::exists(relativeBuildDir()));
+
+ // Initial build.
+ QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app1"})), 0);
+ QVERIFY(QFile::exists(relativeBuildDir()));
+ QCOMPARE(m_qbsStdout.count("Running setup script for mygenerator"), 2);
+ QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("The MY_DEFINE is app1"), m_qbsStdout.constData());
+ QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app2"})), 0);
+ QVERIFY2(m_qbsStdout.contains("The letters are Z and Y"), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("The MY_DEFINE is app2"), m_qbsStdout.constData());
+
+ // Rebuild with overridden module provider config. The output for product 2 must change,
+ // but no setup script must be re-run, because both config values have already been
+ // handled in the first run.
+ const QStringList resolveArgs("moduleProviders.mygenerator.chooseLettersFrom:beginning");
+ QCOMPARE(runQbs(QbsRunParameters("resolve", resolveArgs)), 0);
+ QVERIFY2(!m_qbsStdout.contains("Running setup script"), m_qbsStdout.constData());
+ QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app1"})), 0);
+ QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
+ QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app2"})), 0);
+ QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
+
+ // Forcing Probe execution triggers a re-run of the setup script. But only once,
+ // because the module provider config is the same now.
+ QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList(resolveArgs)
+ << "--force-probe-execution")), 0);
+ QCOMPARE(m_qbsStdout.count("Running setup script for mygenerator"), 1);
+ QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app1"})), 0);
+ QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
+ QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app2"})), 0);
+ QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
+
+ // Now re-run without the module provider config override. Again, the setup script must
+ // run once, for the config value that was not present in the last run.
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+ QCOMPARE(m_qbsStdout.count("Running setup script for mygenerator"), 1);
+ QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app1"})), 0);
+ QVERIFY2(m_qbsStdout.contains("The letters are A and B"), m_qbsStdout.constData());
+ QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app2"})), 0);
+ QVERIFY2(m_qbsStdout.contains("The letters are Z and Y"), m_qbsStdout.constData());
+}
+
+// Checks regression - when loading 2 modules from the same provider, the second module should
+// come from provider cache
+void TestBlackboxProviders::moduleProvidersCache()
+{
+ QDir::setCurrent(testDataDir + "/module-providers-cache");
+
+ QbsRunParameters params("resolve", {"-v"});
+ QCOMPARE(runQbs(params), 0);
+ const auto qbsmetatestmoduleMessage = "Re-checking for module \"qbsmetatestmodule\" with "
+ "newly added search paths from module provider";
+ const auto qbsothermoduleMessage = "Re-checking for module \"qbsothermodule\" with "
+ "newly added search paths from module provider";
+ QCOMPARE(m_qbsStderr.count(qbsmetatestmoduleMessage), 1);
+ QCOMPARE(m_qbsStderr.count(qbsothermoduleMessage), 1);
+ QCOMPARE(m_qbsStderr.count("Re-using provider \"provider_a\" from cache"), 1);
+
+ // We didn't change providers, so both modules should come from cache.
+ params.arguments << "project.dummyProp:value";
+ QCOMPARE(runQbs(params), 0);
+ QCOMPARE(m_qbsStderr.count(qbsmetatestmoduleMessage), 1);
+ QCOMPARE(m_qbsStderr.count(qbsothermoduleMessage), 1);
+ QCOMPARE(m_qbsStderr.count("Re-using provider \"provider_a\" from cache"), 2);
+}
+
+void TestBlackboxProviders::nonEagerModuleProvider()
+{
+ QDir::setCurrent(testDataDir + "/non-eager-provider");
+
+ QbsRunParameters params("resolve");
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY2(m_qbsStdout.contains(("Running setup script for qbsmetatestmodule")), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains(("Running setup script for qbsothermodule")), m_qbsStdout);
+ QVERIFY2(!m_qbsStdout.contains(("Running setup script for nonexistentmodule")), m_qbsStdout);
+
+ QVERIFY2(m_qbsStdout.contains(("p1.qbsmetatestmodule.prop: from_provider_a")),
+ m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains(("p1.qbsothermodule.prop: from_provider_a")),
+ m_qbsStdout);
+}
+
+void TestBlackboxProviders::probeInModuleProvider()
+{
+ QDir::setCurrent(testDataDir + "/probe-in-module-provider");
+
+ QbsRunParameters params;
+ params.command = "build";
+ params.arguments << "--force-probe-execution";
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY2(m_qbsStdout.contains("Running probe"), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains("p.qbsmetatestmodule.boolProp: true"), m_qbsStdout);
+ WAIT_FOR_NEW_TIMESTAMP();
+ touch("probe-in-module-provider.qbs");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("p.qbsmetatestmodule.boolProp: true"), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains("p.qbsmetatestmodule.prop: \"value\""), m_qbsStdout);
+ QVERIFY2(!m_qbsStdout.contains("Running probe"), m_qbsStdout);
+}
+
+// Tests whether it is possible to set providers properties in a Product or from command-line
+void TestBlackboxProviders::providersProperties()
+{
+ QDir::setCurrent(testDataDir + "/providers-properties");
+
+ QbsRunParameters params("build");
+ params.arguments = QStringList("moduleProviders.provider_b.someProp: \"first,second\"");
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY2(m_qbsStdout.contains("p.qbsmetatestmodule.listProp: [\"someValue\"]"), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains(
+ "p.qbsothermodule.listProp: [\"first\",\"second\"]"), m_qbsStdout);
+}
+
+// checks that we can set qbs module properties in providers and provider cache works corectly
+void TestBlackboxProviders::qbsModulePropertiesInProviders()
+{
+ QDir::setCurrent(testDataDir + "/qbs-module-properties-in-providers");
+
+ QbsRunParameters params("resolve");
+
+ QCOMPARE(runQbs(params), 0);
+
+ // We have 2 products in 2 configurations, but second product should use the cached value
+ // so we should have only 2 copies of the module, not 4.
+ QCOMPARE(m_qbsStdout.count("Running setup script for qbsmetatestmodule"), 2);
+
+ // Check that products get correct values from modules
+ QVERIFY2(m_qbsStdout.contains(("product1.qbsmetatestmodule.prop: /sysroot1")), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains(("product1.qbsmetatestmodule.prop: /sysroot2")), m_qbsStdout);
+
+ QVERIFY2(m_qbsStdout.contains(("product2.qbsmetatestmodule.prop: /sysroot1")), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains(("product2.qbsmetatestmodule.prop: /sysroot2")), m_qbsStdout);
+}
+
+void TestBlackboxProviders::qbsModuleProviders_data()
+{
+ QTest::addColumn<QStringList>("arguments");
+ QTest::addColumn<QString>("firstProp");
+ QTest::addColumn<QString>("secondProp");
+
+ QTest::newRow("default") << QStringList() << "from_provider_a" << "undefined";
+ QTest::newRow("override")
+ << QStringList("projects.project.qbsModuleProviders:provider_b")
+ << "from_provider_b"
+ << "from_provider_b";
+ QTest::newRow("override list a")
+ << QStringList("projects.project.qbsModuleProviders:provider_a,provider_b")
+ << "from_provider_a"
+ << "from_provider_b";
+ QTest::newRow("override list b")
+ << QStringList("projects.project.qbsModuleProviders:provider_b,provider_a")
+ << "from_provider_b"
+ << "from_provider_b";
+}
+
+// Tests whether it is possible to set qbsModuleProviders in Product and Project items
+// and that the order of providers results in correct priority
+void TestBlackboxProviders::qbsModuleProviders()
+{
+ QFETCH(QStringList, arguments);
+ QFETCH(QString, firstProp);
+ QFETCH(QString, secondProp);
+
+ QDir::setCurrent(testDataDir + "/qbs-module-providers");
+
+ QbsRunParameters params("resolve");
+ params.arguments = arguments;
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY2(m_qbsStdout.contains(("p1.qbsmetatestmodule.prop: " + firstProp).toUtf8()),
+ m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains(("p1.qbsothermodule.prop: " + secondProp).toUtf8()),
+ m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains(("p2.qbsmetatestmodule.prop: " + firstProp).toUtf8()),
+ m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains(("p2.qbsothermodule.prop: " + secondProp).toUtf8()),
+ m_qbsStdout);
+}
+
+void TestBlackboxProviders::qbsModuleProvidersCliOverride_data()
+{
+ QTest::addColumn<QStringList>("arguments");
+ QTest::addColumn<QString>("propertyValue");
+
+ QTest::newRow("default") << QStringList() << "undefined";
+ QTest::newRow("project-wide")
+ << QStringList("project.qbsModuleProviders:provider_a")
+ << "from_provider_a";
+ QTest::newRow("concrete project")
+ << QStringList("projects.innerProject.qbsModuleProviders:provider_a")
+ << "from_provider_a";
+ QTest::newRow("concrete product")
+ << QStringList("products.product.qbsModuleProviders:provider_a")
+ << "from_provider_a";
+ QTest::newRow("concrete project override project-wide")
+ << QStringList({
+ "project.qbsModuleProviders:provider_a",
+ "projects.innerProject.qbsModuleProviders:provider_b"})
+ << "from_provider_b";
+ QTest::newRow("concrete product override project-wide")
+ << QStringList({
+ "project.qbsModuleProviders:provider_a",
+ "products.product.qbsModuleProviders:provider_b"})
+ << "from_provider_b";
+}
+
+// Tests possible use-cases how to override providers from command-line
+void TestBlackboxProviders::qbsModuleProvidersCliOverride()
+{
+ QFETCH(QStringList, arguments);
+ QFETCH(QString, propertyValue);
+
+ QDir::setCurrent(testDataDir + "/qbs-module-providers-cli-override");
+
+ QbsRunParameters params("resolve");
+ params.arguments = arguments;
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY2(m_qbsStdout.contains(("qbsmetatestmodule.prop: " + propertyValue).toUtf8()),
+ m_qbsStdout);
+}
+
+void TestBlackboxProviders::qbsModuleProvidersCompatibility_data()
+{
+ QTest::addColumn<QStringList>("arguments");
+ QTest::addColumn<QString>("propertyValue");
+
+ QTest::newRow("default") << QStringList() << "from_scoped_provider";
+ QTest::newRow("scoped by name") << QStringList("project.qbsModuleProviders:qbsmetatestmodule") << "from_scoped_provider";
+ QTest::newRow("named") << QStringList("project.qbsModuleProviders:named_provider") << "from_named_provider";
+}
+
+// Tests whether scoped providers can be used as named, i.e. new provider machinery
+// is compatible with the old one
+void TestBlackboxProviders::qbsModuleProvidersCompatibility()
+{
+ QFETCH(QStringList, arguments);
+ QFETCH(QString, propertyValue);
+
+ QDir::setCurrent(testDataDir + "/qbs-module-providers-compatibility");
+
+ QbsRunParameters params("resolve");
+ params.arguments = arguments;
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY2(m_qbsStdout.contains(("qbsmetatestmodule.prop: " + propertyValue).toUtf8()),
+ m_qbsStdout);
+}
+
+void TestBlackboxProviders::qbspkgconfigModuleProvider()
+{
+ QDir::setCurrent(testDataDir + "/qbspkgconfig-module-provider/libs");
+ rmDirR(relativeBuildDir());
+
+ const auto commonParams = QbsRunParameters(QStringLiteral("install"), {
+ QStringLiteral("--install-root"),
+ QStringLiteral("install-root")
+ });
+ auto dynamicParams = commonParams;
+ dynamicParams.arguments << "config:library" << "projects.libs.isBundle:false";
+ QCOMPARE(runQbs(dynamicParams), 0);
+
+ QDir::setCurrent(testDataDir + "/qbspkgconfig-module-provider");
+ rmDirR(relativeBuildDir());
+
+ const auto sysroot = testDataDir + "/qbspkgconfig-module-provider/libs/install-root";
+
+ QbsRunParameters params;
+ params.arguments << "moduleProviders.qbspkgconfig.sysroot:" + sysroot;
+ QCOMPARE(runQbs(params), 0);
+}
+
+void TestBlackboxProviders::removalVersion()
+{
+ QDir::setCurrent(testDataDir + "/removal-version");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY(m_qbsStderr.contains(
+ "Property 'deprecated' was scheduled for removal in version 2.2.0, but is still present"));
+}
+
+QTEST_MAIN(TestBlackboxProviders)
diff --git a/tests/auto/blackbox/tst_blackboxproviders.h b/tests/auto/blackbox/tst_blackboxproviders.h
new file mode 100644
index 000000000..dd0039013
--- /dev/null
+++ b/tests/auto/blackbox/tst_blackboxproviders.h
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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 TST_BLACKBOXPROVIDERS_H
+#define TST_BLACKBOXPROVIDERS_H
+
+#include "tst_blackboxbase.h"
+
+class TestBlackboxProviders : public TestBlackboxBase
+{
+ Q_OBJECT
+
+public:
+ TestBlackboxProviders();
+
+private slots:
+ void allowedValues();
+ void allowedValues_data();
+ void brokenProvider();
+ void moduleProviders();
+ void moduleProvidersCache();
+ void nonEagerModuleProvider();
+ void probeInModuleProvider();
+ void providersProperties();
+ void qbsModulePropertiesInProviders();
+ void qbsModuleProviders_data();
+ void qbsModuleProviders();
+ void qbsModuleProvidersCliOverride();
+ void qbsModuleProvidersCliOverride_data();
+ void qbsModuleProvidersCompatibility();
+ void qbsModuleProvidersCompatibility_data();
+ void qbspkgconfigModuleProvider();
+ void removalVersion();
+};
+
+#endif // TST_BLACKBOXPROVIDERS_H
diff --git a/tests/auto/blackbox/tst_blackboxqt.cpp b/tests/auto/blackbox/tst_blackboxqt.cpp
index 474fb95f3..b083a97e8 100644
--- a/tests/auto/blackbox/tst_blackboxqt.cpp
+++ b/tests/auto/blackbox/tst_blackboxqt.cpp
@@ -38,27 +38,10 @@
#define WAIT_FOR_NEW_TIMESTAMP() waitForNewTimestamp(testDataDir)
using qbs::Internal::HostOsInfo;
-using qbs::Profile;
TestBlackboxQt::TestBlackboxQt() : TestBlackboxBase (SRCDIR "/testdata-qt", "blackbox-qt")
{
-}
-
-void TestBlackboxQt::validateTestProfile()
-{
- const SettingsPtr s = settings();
- if (profileName() != "none" && !s->profiles().contains(profileName()))
- QFAIL(QByteArray("The build profile '" + profileName().toLocal8Bit() +
- "' could not be found. Please set it up on your machine."));
- const QStringList qmakeFilePaths = Profile(profileName(), s.get())
- .value("moduleProviders.Qt.qmakeFilePaths").toStringList();
- if (!qmakeFilePaths.empty())
- return;
- if (!findExecutable(QStringList{"qmake"}).isEmpty())
- return;
- QSKIP(QByteArray("The build profile '" + profileName().toLocal8Bit() +
- "' is not a valid Qt profile and Qt was not found "
- "in the global search paths."));
+ setNeedsQt();
}
void TestBlackboxQt::addQObjectMacroToGeneratedCppFile()
@@ -75,6 +58,9 @@ void TestBlackboxQt::addQObjectMacroToGeneratedCppFile()
void TestBlackboxQt::autoQrc()
{
QDir::setCurrent(testDataDir + "/auto-qrc");
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(runQbs(QbsRunParameters("run", QStringList{"-p", "app"})), 0);
QVERIFY2(m_qbsStdout.simplified().contains("resource data: resource1 resource2"),
m_qbsStdout.constData());
@@ -83,7 +69,9 @@ void TestBlackboxQt::autoQrc()
void TestBlackboxQt::cachedQml()
{
QDir::setCurrent(testDataDir + "/cached-qml");
- QCOMPARE(runQbs(), 0);
+ if ((runQbs() != 0) && m_qbsStderr.contains("Dependency 'Qt.qml' not found for product 'app'"))
+ QSKIP("Qt version too old");
+
QString dataDir = relativeBuildDir() + "/install-root/data";
QVERIFY2(m_qbsStdout.contains("qmlcachegen must work: true")
|| m_qbsStdout.contains("qmlcachegen must work: false"),
@@ -118,7 +106,7 @@ void TestBlackboxQt::combinedMoc()
void TestBlackboxQt::createProject()
{
QDir::setCurrent(testDataDir + "/create-project");
- QVERIFY(QFile::copy(SRCDIR "/../../../examples/helloworld-qt/main.cpp",
+ QVERIFY(QFile::copy(testSourceDir + "/../../../../examples/helloworld-qt/main.cpp",
QDir::currentPath() + "/main.cpp"));
QbsRunParameters createParams("create-project");
createParams.profile.clear();
@@ -145,6 +133,11 @@ void TestBlackboxQt::dbusInterfaces()
void TestBlackboxQt::forcedMoc()
{
QDir::setCurrent(testDataDir + "/forced-moc");
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+ if (m_qbsStdout.contains("using qt4"))
+ QSKIP("Qt version too old");
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(runQbs(QbsRunParameters("run")), 0);
QVERIFY2(m_qbsStderr.contains("Hello from slot"), m_qbsStderr.constData());
}
@@ -153,6 +146,8 @@ void TestBlackboxQt::includedMocCpp()
{
QDir::setCurrent(testDataDir + "/included-moc-cpp");
QCOMPARE(runQbs(), 0);
+ if (m_qbsStdout.contains("using qt4"))
+ QSKIP("Qt version too old");
QVERIFY2(!m_qbsStdout.contains("compiling moc_myobject.cpp"), m_qbsStdout.constData());
WAIT_FOR_NEW_TIMESTAMP();
REPLACE_IN_FILE("myobject.cpp", "#include <moc_myobject.cpp", "// #include <moc_myobject.cpp");
@@ -204,12 +199,48 @@ void TestBlackboxQt::lrelease()
QVERIFY(!regularFileExists(relativeProductBuildDir("lrelease-test") + "/hu.qm"));
}
+void TestBlackboxQt::metaTypes_data()
+{
+ QTest::addColumn<bool>("generate");
+ QTest::addColumn<QString>("installDir");
+ QTest::newRow("don't generate") << false << QString();
+ QTest::newRow("don't generate with install info") << false << QString("blubb");
+ QTest::newRow("generate only") << true << QString();
+ QTest::newRow("generate and install") << true << QString("blubb");
+}
+
+void TestBlackboxQt::metaTypes()
+{
+ QDir::setCurrent(testDataDir + "/metatypes");
+ QFETCH(bool, generate);
+ QFETCH(QString, installDir);
+ const QStringList args{"modules.Qt.core.generateMetaTypesFile:"
+ + QString(generate ? "true" : "false"),
+ "modules.Qt.core.metaTypesInstallDir:" + installDir,
+ "-v", "--force-probe-execution"};
+ QCOMPARE(runQbs(QbsRunParameters("resolve", args)), 0);
+ const bool canGenerate = m_qbsStdout.contains("can generate");
+ const bool cannotGenerate = m_qbsStdout.contains("cannot generate");
+ QVERIFY(canGenerate != cannotGenerate);
+ const bool expectFiles = generate && canGenerate;
+ const bool expectInstalledFiles = expectFiles && !installDir.isEmpty();
+ QCOMPARE(runQbs(QStringList("--clean-install-root")), 0);
+ const QString productDir = relativeProductBuildDir("mylib");
+ const QString outputDir = productDir + "/qt.headers";
+ QVERIFY(!regularFileExists(outputDir + "/moc_unmocableclass.cpp.json"));
+ QCOMPARE(regularFileExists(outputDir + "/moc_mocableclass1.cpp.json"), expectFiles);
+ QCOMPARE(regularFileExists(outputDir + "/mocableclass2.moc.json"), expectFiles);
+ QCOMPARE(regularFileExists(productDir + "/mylib_metatypes.json"), expectFiles);
+ QCOMPARE(regularFileExists(relativeBuildDir() + "/install-root/some-prefix/" + installDir
+ + "/mylib_metatypes.json"), expectInstalledFiles);
+}
+
void TestBlackboxQt::mixedBuildVariants()
{
QDir::setCurrent(testDataDir + "/mixed-build-variants");
const SettingsPtr s = settings();
- Profile profile(profileName(), s.get());
- if (profile.value("qbs.toolchain").toStringList().contains("msvc")) {
+ qbs::Profile profile(profileName(), s.get());
+ if (profileToolchain(profile).contains("msvc")) {
QbsRunParameters params;
params.arguments << "qbs.buildVariant:debug";
params.expectFailure = true;
@@ -240,6 +271,12 @@ void TestBlackboxQt::mocFlags()
QVERIFY(runQbs(params) != 0);
}
+void TestBlackboxQt::mocCompilerDefines()
+{
+ QDir::setCurrent(testDataDir + "/moc-compiler-defines");
+ QCOMPARE(runQbs(), 0);
+}
+
void TestBlackboxQt::mocSameFileName()
{
QDir::setCurrent(testDataDir + "/moc-same-file-name");
@@ -247,19 +284,141 @@ void TestBlackboxQt::mocSameFileName()
QCOMPARE(m_qbsStdout.count("compiling moc_someclass.cpp"), 2);
}
+void TestBlackboxQt::noRelinkOnQDebug()
+{
+ QFETCH(QString, checkMode);
+ QFETCH(bool, expectRelink);
+
+ QVERIFY(QDir::setCurrent(testDataDir + "/no-relink-on-qdebug"));
+ rmDirR("default");
+
+ // Target check.
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+ QVERIFY2(m_qbsStdout.contains("is GCC: "), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("is MinGW: "), m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("is Darwin: "), m_qbsStdout.constData());
+ const bool isGCCLike = m_qbsStdout.contains("is GCC: true");
+ const bool isMingw = m_qbsStdout.contains("is MinGW: true");
+ const bool isDarwin = m_qbsStdout.contains("is Darwin: true");
+ if (!isGCCLike)
+ expectRelink = false;
+ else if (isMingw || isDarwin)
+ expectRelink = true;
+
+ // Initial build.
+ QbsRunParameters params("resolve");
+ if (isGCCLike && !checkMode.isEmpty())
+ params.arguments << ("modules.cpp.exportedSymbolsCheckMode:" + checkMode);
+ QCOMPARE(runQbs(params), 0);
+ QCOMPARE(runQbs(), 0);
+ QCOMPARE(m_qbsStdout.count("linking"), 2);
+
+ // Inserting the qDebug() statement will pull in weak symbols.
+ WAIT_FOR_NEW_TIMESTAMP();
+ REPLACE_IN_FILE("lib.cpp", "// qDebug", "qDebug");
+ QCOMPARE(runQbs(), 0);
+ QCOMPARE(m_qbsStdout.count("linking"), expectRelink ? 2 : 1);
+
+ // Also check the opposite case.
+ WAIT_FOR_NEW_TIMESTAMP();
+ REPLACE_IN_FILE("lib.cpp", "qDebug", "// qDebug");
+ QCOMPARE(runQbs(), 0);
+ QCOMPARE(m_qbsStdout.count("linking"), expectRelink ? 2 : 1);
+}
+
+void TestBlackboxQt::noRelinkOnQDebug_data()
+{
+ QTest::addColumn<QString>("checkMode");
+ QTest::addColumn<bool>("expectRelink");
+ QTest::newRow("default") << QString() << false;
+ QTest::newRow("relaxed") << QString("ignore-undefined") << false;
+ QTest::newRow("strict") << QString("strict") << true;
+}
+
void TestBlackboxQt::pkgconfig()
{
QDir::setCurrent(testDataDir + "/pkgconfig");
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QbsRunParameters params;
params.command = "run";
QCOMPARE(runQbs(params), 0);
- if (m_qbsStdout.contains("Skip this test"))
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
QSKIP("pkgconfig or Qt not found");
}
+void TestBlackboxQt::pkgconfigQt()
+{
+ QFETCH(QStringList, arguments);
+ QFETCH(bool, success);
+
+ QDir::setCurrent(testDataDir + "/pkgconfig-qt");
+ rmDirR(relativeBuildDir());
+
+ QbsRunParameters dumpParams("resolve", {"-f", "dump-libpath.qbs"});
+ QCOMPARE(runQbs(dumpParams), 0);
+ auto lines = QString::fromUtf8(m_qbsStdout).split('\n');
+ const QString needle = "libPath=";
+ qbs::Internal::removeIf(
+ lines, [&needle](const auto &line) { return !line.startsWith(needle); });
+ QCOMPARE(lines.size(), 1);
+ const auto libPath = lines[0].mid(needle.size());
+ auto prefix = QFileInfo(libPath).path();
+ if (prefix.endsWith("/lib") && !prefix.startsWith("/lib"))
+ prefix = QFileInfo(prefix).path();
+ const auto pkgConfigPath = libPath + "/pkgconfig/";
+ if (!QFileInfo(pkgConfigPath).exists())
+ QSKIP("No *.pc files found");
+
+ rmDirR(relativeBuildDir());
+ QbsRunParameters params("build", {"-f", "pkgconfig-qt.qbs"});
+ // need to override prefix for the downloaded Qt
+ params.environment.insert("PKG_CONFIG_QT5CORE_PREFIX", prefix);
+ params.environment.insert("PKG_CONFIG_QT6CORE_PREFIX", prefix);
+ params.arguments << "moduleProviders.qbspkgconfig.extraPaths:" + pkgConfigPath;
+ params.arguments << arguments;
+
+ QCOMPARE(runQbs(params) == 0, success);
+
+ if (!success)
+ QVERIFY(m_qbsStderr.contains("Dependency 'Qt.core' not found for product 'p'"));
+}
+
+void TestBlackboxQt::pkgconfigQt_data()
+{
+ QTest::addColumn<QStringList>("arguments");
+ QTest::addColumn<bool>("success");
+ QTest::newRow("pkgconfig") << QStringList() << true;
+ QTest::newRow("dummy")
+ << QStringList({"products.p.qbsModuleProviders:dummyProvider"}) << false;
+ QTest::newRow("cross-compiling")
+ << QStringList({"moduleProviders.qbspkgconfig.sysroot:/some/fake/sysroot"}) << false;
+}
+
+void TestBlackboxQt::pkgconfigNoQt()
+{
+ QDir::setCurrent(testDataDir + "/pkgconfig-qt");
+ rmDirR(relativeBuildDir());
+ QbsRunParameters params("build", {"-f", "pkgconfig-qt.qbs"});
+ params.arguments << "moduleProviders.qbspkgconfig.libDirs:nonexistent";
+ params.expectFailure = true;
+
+ QCOMPARE(runQbs(params) == 0, false);
+ QVERIFY2(m_qbsStderr.contains("Dependency 'Qt.core' not found for product 'p'"), m_qbsStderr);
+ // QBS-1777: basic check for JS exceptions in case of missing Qt
+ QVERIFY2(!m_qbsStderr.contains("Error executing provider for module 'Qt.core'"), m_qbsStderr);
+}
+
void TestBlackboxQt::pluginMetaData()
{
QDir::setCurrent(testDataDir + "/plugin-meta-data");
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+ if (m_qbsStdout.contains("using qt4"))
+ QSKIP("Qt version too old");
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
+
QVERIFY2(runQbs(QbsRunParameters("run", QStringList{"-p", "app"})) == 0,
m_qbsStderr.constData());
QVERIFY2(m_qbsStderr.contains("all ok!"), m_qbsStderr.constData());
@@ -286,7 +445,11 @@ void TestBlackboxQt::pluginSupport()
resolveParams.arguments << "modules.m1.useDummy:true";
resolveParams.expectFailure = true;
}
- QCOMPARE(runQbs(resolveParams) == 0, !invalidPlugin);
+ bool resolveResult = runQbs(resolveParams) == 0;
+ if (m_qbsStdout.contains("using qt4"))
+ QSKIP("Qt version too old");
+ QCOMPARE(resolveResult, !invalidPlugin);
+
if (invalidPlugin) {
QVERIFY2(m_qbsStderr.contains("Plugin 'dummy' of type 'imageformats' was requested, "
"but is not available"), m_qbsStderr.constData());
@@ -320,22 +483,36 @@ void TestBlackboxQt::pluginSupport()
}
}
+void TestBlackboxQt::qdoc()
+{
+ QDir::setCurrent(testDataDir + "/qdoc");
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+ if (m_qbsStdout.contains("Qt is too old"))
+ QSKIP("Skip test since qdoc3 does not work properly");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY(QFileInfo(relativeProductBuildDir("QDoc Test") + "/qdoctest.qch").exists());
+}
+
void TestBlackboxQt::qmlDebugging()
{
QDir::setCurrent(testDataDir + "/qml-debugging");
QCOMPARE(runQbs(), 0);
- const SettingsPtr s = settings();
- Profile profile(profileName(), s.get());
- if (!profile.value("qbs.toolchain").toStringList().contains("gcc"))
- return;
+
+ const bool isGcc = m_qbsStdout.contains("is gcc: true");
+ const bool isNotGcc = m_qbsStdout.contains("is gcc: false");
+ if (isNotGcc)
+ QSKIP("The remainder of this test only applies to gcc");
+ QVERIFY(isGcc);
+
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());
- }
+ if (!nm.waitForStarted())
+ QSKIP("The remainder of this test requires 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 TestBlackboxQt::qobjectInObjectiveCpp()
@@ -347,6 +524,42 @@ void TestBlackboxQt::qobjectInObjectiveCpp()
QCOMPARE(runQbs(), 0);
}
+void TestBlackboxQt::qmlTypeRegistrar_data()
+{
+ QTest::addColumn<QString>("importName");
+ QTest::addColumn<QString>("installDir");
+ QTest::newRow("don't generate") << QString() << QString();
+ QTest::newRow("don't generate with install info") << QString() << QString("blubb");
+ QTest::newRow("generate only") << QString("People") << QString();
+ QTest::newRow("generate and install") << QString("People") << QString("blubb");
+}
+
+void TestBlackboxQt::qmlTypeRegistrar()
+{
+ QDir::setCurrent(testDataDir + "/qmltyperegistrar");
+ QFETCH(QString, importName);
+ QFETCH(QString, installDir);
+ rmDirR(relativeBuildDir());
+ const QStringList args{"modules.Qt.qml.importName:" + importName,
+ "modules.Qt.qml.typesInstallDir:" + installDir};
+ if ((runQbs(QbsRunParameters("resolve", args)) != 0) &&
+ m_qbsStderr.contains("Dependency 'Qt.qml' not found for product 'myapp'"))
+ QSKIP("Qt version too old");
+ const bool hasRegistrar = m_qbsStdout.contains("has registrar");
+ const bool doesNotHaveRegistrar = m_qbsStdout.contains("does not have registrar");
+ QVERIFY(hasRegistrar != doesNotHaveRegistrar);
+ if (doesNotHaveRegistrar)
+ QSKIP("Qt version too old");
+ QCOMPARE(runQbs(), 0);
+ const bool enabled = !importName.isEmpty();
+ QCOMPARE(m_qbsStdout.contains("running qmltyperegistrar"), enabled);
+ QCOMPARE(m_qbsStdout.contains("compiling myapp_qmltyperegistrations.cpp"), enabled);
+ const QString buildDir = relativeProductBuildDir("myapp");
+ QCOMPARE(regularFileExists(buildDir + "/myapp.qmltypes"), enabled);
+ QCOMPARE(regularFileExists(relativeBuildDir() + "/install-root/" + installDir
+ + "/myapp.qmltypes"), enabled && !installDir.isEmpty());
+}
+
void TestBlackboxQt::qtKeywords()
{
QDir::setCurrent(testDataDir + "/qt-keywords");
@@ -364,7 +577,9 @@ void TestBlackboxQt::qtKeywords()
void TestBlackboxQt::quickCompiler()
{
QDir::setCurrent(testDataDir + "/quick-compiler");
- QCOMPARE(runQbs(), 0);
+ if ((runQbs() != 0) &&
+ m_qbsStderr.contains("'Qt.quick' has version 4.8.7, but it needs to be at least 5.0.0."))
+ QSKIP("Qt version too old");
const bool hasCompiler = m_qbsStdout.contains("compiler available");
const bool doesNotHaveCompiler = m_qbsStdout.contains("compiler not available");
QVERIFY2(hasCompiler || doesNotHaveCompiler, m_qbsStdout.constData());
@@ -390,6 +605,9 @@ void TestBlackboxQt::quickCompiler()
void TestBlackboxQt::qtScxml()
{
QDir::setCurrent(testDataDir + "/qtscxml");
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(runQbs(), 0);
if (m_qbsStdout.contains("QtScxml not present"))
QSKIP("QtScxml module not present");
@@ -423,10 +641,10 @@ void TestBlackboxQt::staticQtPluginLinking()
QDir::setCurrent(testDataDir + "/static-qt-plugin-linking");
QCOMPARE(runQbs(QStringList("products.p.type:application")), 0);
const bool isStaticQt = m_qbsStdout.contains("Qt is static");
- QVERIFY2(m_qbsStdout.contains("Creating static import") == isStaticQt, m_qbsStdout.constData());
+ QVERIFY2(m_qbsStdout.contains("creating static import") == isStaticQt, m_qbsStdout.constData());
QCOMPARE(runQbs(QbsRunParameters("resolve", QStringList("products.p.type:staticlibrary"))), 0);
QCOMPARE(runQbs(), 0);
- QVERIFY2(!m_qbsStdout.contains("Creating static import"), m_qbsStdout.constData());
+ QVERIFY2(!m_qbsStdout.contains("creating static import"), m_qbsStdout.constData());
}
void TestBlackboxQt::trackAddMocInclude()
@@ -469,6 +687,11 @@ void TestBlackboxQt::track_qobject_change()
void TestBlackboxQt::track_qrc()
{
QDir::setCurrent(testDataDir + "/qrc");
+ QCOMPARE(runQbs(QbsRunParameters("resolve")), 0);
+ if (m_qbsStdout.contains("using qt4"))
+ QSKIP("Qt version too old");
+ if (m_qbsStdout.contains("targetPlatform differs from hostPlatform"))
+ QSKIP("Cannot run binaries in cross-compiled build");
QCOMPARE(runQbs(QbsRunParameters("run")), 0);
QVERIFY2(m_qbsStdout.contains("rcc"), m_qbsStdout.constData());
QVERIFY2(!m_qbsStdout.contains("compiling test.cpp"), m_qbsStdout.constData());
diff --git a/tests/auto/blackbox/tst_blackboxqt.h b/tests/auto/blackbox/tst_blackboxqt.h
index 180f9e0c0..da395b7d4 100644
--- a/tests/auto/blackbox/tst_blackboxqt.h
+++ b/tests/auto/blackbox/tst_blackboxqt.h
@@ -38,9 +38,6 @@ class TestBlackboxQt : public TestBlackboxBase
public:
TestBlackboxQt();
-protected:
- void validateTestProfile() override;
-
private slots:
void addQObjectMacroToGeneratedCppFile();
void autoQrc();
@@ -53,16 +50,27 @@ private slots:
void includedMocCpp();
void linkerVariant();
void lrelease();
+ void metaTypes_data();
+ void metaTypes();
void mixedBuildVariants();
void mocAndCppCombining();
void mocFlags();
+ void mocCompilerDefines();
void mocSameFileName();
+ void noRelinkOnQDebug();
+ void noRelinkOnQDebug_data();
void pkgconfig();
+ void pkgconfigQt();
+ void pkgconfigQt_data();
+ void pkgconfigNoQt();
void pluginMetaData();
void pluginSupport_data();
void pluginSupport();
+ void qdoc();
void qmlDebugging();
void qobjectInObjectiveCpp();
+ void qmlTypeRegistrar_data();
+ void qmlTypeRegistrar();
void qtKeywords();
void quickCompiler();
void qtScxml();
diff --git a/tests/auto/blackbox/tst_blackboxtutorial.cpp b/tests/auto/blackbox/tst_blackboxtutorial.cpp
new file mode 100644
index 000000000..49de448a1
--- /dev/null
+++ b/tests/auto/blackbox/tst_blackboxtutorial.cpp
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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 "tst_blackboxtutorial.h"
+
+#include <QtCore/qdir.h>
+#include <QtCore/qdiriterator.h>
+
+static QStringList collectProjects(const QString &dirPath)
+{
+ QStringList result;
+ QDir dir(dirPath);
+ const auto subDirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);
+ for (const auto &subDir : subDirs) {
+ const auto path = dir.filePath(subDir);
+ if (!QFileInfo::exists(path + "/myproject.qbs"))
+ continue;
+ result.append(dir.relativeFilePath(path));
+ }
+ return result;
+}
+
+TestBlackboxTutorial::TestBlackboxTutorial()
+ : TestBlackboxBase(SRCDIR "/../../../tutorial/", "blackbox-tutorial")
+{}
+
+void TestBlackboxTutorial::tutorial_data()
+{
+ QTest::addColumn<QString>("project");
+
+ const auto projects = collectProjects(testDataDir);
+ for (const auto &project : projects) {
+ QTest::newRow(project.toUtf8().data()) << project;
+ }
+}
+
+void TestBlackboxTutorial::tutorial()
+{
+ QFETCH(QString, project);
+
+ QVERIFY(QDir::setCurrent(testDataDir + "/" + project));
+ QCOMPARE(runQbs(), 0);
+}
+
+QTEST_MAIN(TestBlackboxTutorial)
diff --git a/tests/auto/blackbox/tst_blackboxtutorial.h b/tests/auto/blackbox/tst_blackboxtutorial.h
new file mode 100644
index 000000000..2e84d6a96
--- /dev/null
+++ b/tests/auto/blackbox/tst_blackboxtutorial.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://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 TST_BLACKBOXTUTORIAL_H
+#define TST_BLACKBOXTUTORIAL_H
+
+#include "tst_blackboxbase.h"
+
+class TestBlackboxTutorial : public TestBlackboxBase
+{
+ Q_OBJECT
+
+public:
+ TestBlackboxTutorial();
+
+private slots:
+ void tutorial_data();
+ void tutorial();
+};
+
+#endif // TST_BLACKBOXTUTORIAL_H
diff --git a/tests/auto/blackbox/tst_blackboxwindows.cpp b/tests/auto/blackbox/tst_blackboxwindows.cpp
new file mode 100644
index 000000000..57bd7f947
--- /dev/null
+++ b/tests/auto/blackbox/tst_blackboxwindows.cpp
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://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 "tst_blackboxwindows.h"
+
+#include "../shared.h"
+
+#include <tools/hostosinfo.h>
+#include <tools/profile.h>
+#include <tools/qttools.h>
+
+#include <QtCore/qdir.h>
+#include <QtCore/qregularexpression.h>
+
+using qbs::Internal::HostOsInfo;
+using qbs::Profile;
+
+struct SigntoolInfo
+{
+ enum class CodeSignResult { Failed = 0, Signed, Unsigned };
+ CodeSignResult result = CodeSignResult::Failed;
+ bool timestamped = false;
+ QString hashAlgorithm;
+ QString subjectName;
+};
+
+Q_DECLARE_METATYPE(SigntoolInfo::CodeSignResult)
+
+static SigntoolInfo extractSigntoolInfo(const QString &signtoolPath, const QString &appPath)
+{
+ QProcess signtool;
+ signtool.start(signtoolPath, { QStringLiteral("verify"), QStringLiteral("/v"), appPath });
+ if (!signtool.waitForStarted() || !signtool.waitForFinished())
+ return {};
+ const auto output = signtool.readAllStandardError();
+ SigntoolInfo signtoolInfo;
+ if (output.contains("No signature found")) {
+ signtoolInfo.result = SigntoolInfo::CodeSignResult::Unsigned;
+ } else {
+ signtoolInfo.result = SigntoolInfo::CodeSignResult::Signed;
+ const auto output = signtool.readAllStandardOutput();
+ const auto lines = output.split('\n');
+ for (const auto &line: lines) {
+ {
+ const QRegularExpression re("^Hash of file \\((.+)\\):.+$");
+ const QRegularExpressionMatch match = re.match(line);
+ if (match.hasMatch()) {
+ signtoolInfo.hashAlgorithm = match.captured(1).toLocal8Bit();
+ continue;
+ }
+ }
+ {
+ const QRegularExpression re("Issued to: (.+)");
+ const QRegularExpressionMatch match = re.match(line);
+ if (match.hasMatch()) {
+ signtoolInfo.subjectName = match.captured(1).toLocal8Bit().trimmed();
+ continue;
+ }
+ }
+ if (line.startsWith("The signature is timestamped:")) {
+ signtoolInfo.timestamped = true;
+ break;
+ } else if (line.startsWith("File is not timestamped.")) {
+ break;
+ }
+ }
+ }
+ return signtoolInfo;
+}
+
+static QString extractSigntoolPath(const QByteArray &output)
+{
+ const QRegularExpression re("%%(.+)%%");
+ QRegularExpressionMatchIterator it = re.globalMatch(output);
+ if (!it.hasNext())
+ return {};
+ const QRegularExpressionMatch match = it.next();
+ return match.captured(1).toUtf8();
+}
+
+TestBlackboxWindows::TestBlackboxWindows()
+ : TestBlackboxBase (SRCDIR "/testdata-windows", "blackbox-windows")
+{
+}
+
+void TestBlackboxWindows::initTestCase()
+{
+ if (!HostOsInfo::isWindowsHost()) {
+ QSKIP("only applies on Windows");
+ return;
+ }
+
+ TestBlackboxBase::initTestCase();
+}
+
+void TestBlackboxWindows::innoSetup()
+{
+ const SettingsPtr s = settings();
+ Profile profile(profileName(), s.get());
+
+ QDir::setCurrent(testDataDir + "/innosetup");
+
+ QCOMPARE(runQbs({"resolve"}), 0);
+ const bool withInnosetup = m_qbsStdout.contains("has innosetup: true");
+ const bool withoutInnosetup = m_qbsStdout.contains("has innosetup: false");
+ QVERIFY2(withInnosetup || withoutInnosetup, m_qbsStdout.constData());
+ if (withoutInnosetup)
+ QSKIP("innosetup module not present");
+
+ QCOMPARE(runQbs(), 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 TestBlackboxWindows::innoSetupDependencies()
+{
+ const SettingsPtr s = settings();
+ Profile profile(profileName(), s.get());
+
+ QDir::setCurrent(testDataDir + "/innosetupDependencies");
+
+ QCOMPARE(runQbs({"resolve"}), 0);
+ const bool withInnosetup = m_qbsStdout.contains("has innosetup: true");
+ const bool withoutInnosetup = m_qbsStdout.contains("has innosetup: false");
+ QVERIFY2(withInnosetup || withoutInnosetup, m_qbsStdout.constData());
+ if (withoutInnosetup)
+ QSKIP("innosetup module not present");
+
+ QbsRunParameters params;
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY(m_qbsStdout.contains("compiling test.iss"));
+ QVERIFY(regularFileExists(relativeBuildDir() + "/qbs.setup.test.exe"));
+}
+
+void TestBlackboxWindows::standaloneCodesign()
+{
+ QFETCH(SigntoolInfo::CodeSignResult, result);
+ QFETCH(QString, hashAlgorithm);
+ QFETCH(QString, subjectName);
+ QFETCH(QString, signingTimestamp);
+
+ QDir::setCurrent(testDataDir + "/codesign");
+ QbsRunParameters params(QStringList{"qbs.installPrefix:''"});
+ params.arguments << QStringLiteral("project.enableSigning:%1").arg(
+ (result == SigntoolInfo::CodeSignResult::Signed) ? "true" : "false")
+ << QStringLiteral("project.hashAlgorithm:%1").arg(hashAlgorithm)
+ << QStringLiteral("project.subjectName:%1").arg(subjectName)
+ << QStringLiteral("project.signingTimestamp:%1").arg(signingTimestamp);
+
+ rmDirR(relativeBuildDir());
+ QCOMPARE(runQbs(params), 0);
+
+ if (!m_qbsStdout.contains("signtool path:"))
+ QSKIP("No current signtool path pattern exists");
+
+ const auto signtoolPath = extractSigntoolPath(m_qbsStdout);
+ QVERIFY(QFileInfo(signtoolPath).exists());
+
+ const QStringList outputBinaries = {"A.exe", "B.dll"};
+ for (const auto &outputBinary : outputBinaries) {
+ const auto outputBinaryPath = defaultInstallRoot + "/" + outputBinary;
+ QVERIFY(QFileInfo(outputBinaryPath).exists());
+
+ const SigntoolInfo signtoolInfo = extractSigntoolInfo(signtoolPath, outputBinaryPath);
+ QVERIFY(signtoolInfo.result != SigntoolInfo::CodeSignResult::Failed);
+ QCOMPARE(signtoolInfo.result, result);
+ QCOMPARE(signtoolInfo.hashAlgorithm, hashAlgorithm);
+ QCOMPARE(signtoolInfo.subjectName, subjectName);
+ QCOMPARE(signtoolInfo.timestamped, !signingTimestamp.isEmpty());
+ }
+}
+
+void TestBlackboxWindows::standaloneCodesign_data()
+{
+ QTest::addColumn<SigntoolInfo::CodeSignResult>("result");
+ QTest::addColumn<QString>("hashAlgorithm");
+ QTest::addColumn<QString>("subjectName");
+ QTest::addColumn<QString>("signingTimestamp");
+
+ QTest::newRow("standalone, unsigned")
+ << SigntoolInfo::CodeSignResult::Unsigned << "" << "" << "";
+ QTest::newRow("standalone, signed, sha1, qbs@community.test, no timestamp")
+ << SigntoolInfo::CodeSignResult::Signed << "sha1" << "qbs@community.test" << "";
+ QTest::newRow("standalone, signed, sha256, qbs@community.test, RFC3061 timestamp")
+ << SigntoolInfo::CodeSignResult::Signed << "sha256" << "qbs@community.test"
+ << "http://timestamp.digicert.com";
+}
+
+
+static bool haveWiX(const Profile &profile)
+{
+ if (profile.value("wix.toolchainInstallPath").isValid() &&
+ profile.value("wix.toolchainInstallRoot").isValid()) {
+ return true;
+ }
+
+ QStringList regKeys;
+ regKeys << QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Microsoft\\Windows Installer XML\\")
+ << QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Installer XML\\");
+
+ QStringList paths = QProcessEnvironment::systemEnvironment().value("PATH")
+ .split(HostOsInfo::pathListSeparator(), Qt::SkipEmptyParts);
+
+ for (const QString &key : std::as_const(regKeys)) {
+ const QStringList versions = QSettings(key, QSettings::NativeFormat).childGroups();
+ for (const QString &version : versions) {
+ QSettings settings(key + version, QSettings::NativeFormat);
+ QString str = settings.value(QStringLiteral("InstallRoot")).toString();
+ if (!str.isEmpty())
+ paths.prepend(str);
+ }
+ }
+
+ for (const QString &path : std::as_const(paths)) {
+ if (regularFileExists(QDir::fromNativeSeparators(path) +
+ HostOsInfo::appendExecutableSuffix(QStringLiteral("/candle"))) &&
+ regularFileExists(QDir::fromNativeSeparators(path) +
+ HostOsInfo::appendExecutableSuffix(QStringLiteral("/light")))) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void TestBlackboxWindows::wix()
+{
+ const SettingsPtr s = settings();
+ Profile profile(profileName(), s.get());
+
+ if (!haveWiX(profile)) {
+ QSKIP("WiX is not installed");
+ return;
+ }
+
+ QByteArray arch = profile.value("qbs.architecture").toString().toLatin1();
+ if (arch.isEmpty())
+ arch = QByteArrayLiteral("x86");
+
+ QDir::setCurrent(testDataDir + "/wix");
+ QCOMPARE(runQbs(), 0);
+ QVERIFY2(m_qbsStdout.contains("compiling QbsSetup.wxs"), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains("linking qbs.msi"), m_qbsStdout);
+ QVERIFY(regularFileExists(relativeProductBuildDir("QbsSetup") + "/qbs.msi"));
+
+ if (HostOsInfo::isWindowsHost()) {
+ QVERIFY2(m_qbsStdout.contains("compiling QbsBootstrapper.wxs"), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains("linking qbs-setup-" + arch + ".exe"), m_qbsStdout);
+ QVERIFY(regularFileExists(relativeProductBuildDir("QbsBootstrapper")
+ + "/qbs-setup-" + arch + ".exe"));
+ }
+}
+
+void TestBlackboxWindows::wixDependencies()
+{
+ const SettingsPtr s = settings();
+ Profile profile(profileName(), s.get());
+
+ if (!haveWiX(profile)) {
+ QSKIP("WiX is not installed");
+ return;
+ }
+
+ QByteArray arch = profile.value("qbs.architecture").toString().toLatin1();
+ if (arch.isEmpty())
+ arch = QByteArrayLiteral("x86");
+
+ QDir::setCurrent(testDataDir + "/wixDependencies");
+ QbsRunParameters params;
+ if (!HostOsInfo::isWindowsHost())
+ params.arguments << "qbs.targetOS:windows";
+ QCOMPARE(runQbs(params), 0);
+ QVERIFY2(m_qbsStdout.contains("compiling QbsSetup.wxs"), m_qbsStdout);
+ QVERIFY2(m_qbsStdout.contains("linking qbs.msi"), m_qbsStdout);
+ QVERIFY(regularFileExists(relativeBuildDir() + "/qbs.msi"));
+}
+
+QTEST_MAIN(TestBlackboxWindows)
diff --git a/tests/auto/blackbox/tst_blackboxwindows.h b/tests/auto/blackbox/tst_blackboxwindows.h
new file mode 100644
index 000000000..ad8d60ca3
--- /dev/null
+++ b/tests/auto/blackbox/tst_blackboxwindows.h
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Denis Shienkov <denis.shienkov@gmail.com>
+** Contact: https://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 TST_BLACKBOXWINDOWS_H
+#define TST_BLACKBOXWINDOWS_H
+
+#include "tst_blackboxbase.h"
+
+class TestBlackboxWindows : public TestBlackboxBase
+{
+ Q_OBJECT
+
+public:
+ TestBlackboxWindows();
+
+public slots:
+ void initTestCase() override;
+
+private slots:
+ void innoSetup();
+ void innoSetupDependencies();
+ void standaloneCodesign();
+ void standaloneCodesign_data();
+ void wix();
+ void wixDependencies();
+};
+
+#endif // TST_BLACKBOXWINDOWS_H
diff --git a/tests/auto/blackbox/tst_clangdb.cpp b/tests/auto/blackbox/tst_clangdb.cpp
index 3a6dd2d92..c4216ac13 100644
--- a/tests/auto/blackbox/tst_clangdb.cpp
+++ b/tests/auto/blackbox/tst_clangdb.cpp
@@ -36,7 +36,7 @@
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
-#include <QtCore/qregexp.h>
+#include <QtCore/qregularexpression.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qjsondocument.h>
@@ -202,17 +202,19 @@ void TestClangDb::checkClangDetectsSourceCodeProblems()
// clang-check.exe does not understand MSVC command-line syntax
const SettingsPtr s = settings();
qbs::Profile profile(profileName(), s.get());
- if (profile.value("qbs.toolchain").toStringList().contains("msvc")) {
+ if (profileToolchain(profile).contains("msvc")) {
arguments << "-extra-arg-before=--driver-mode=cl";
- } else if (profile.value("qbs.toolchain").toStringList().contains("mingw")) {
+ } else if (profileToolchain(profile).contains("mingw")) {
arguments << "-extra-arg-before=--driver-mode=g++";
}
arguments << "-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)));
+ QVERIFY(output.contains(QRegularExpression(QStringLiteral("warning.*undefined"),
+ QRegularExpression::CaseInsensitiveOption)));
+ QVERIFY(output.contains(QRegularExpression(QStringLiteral("warning.*never read"),
+ QRegularExpression::CaseInsensitiveOption)));
}
QTEST_MAIN(TestClangDb)
diff --git a/tests/auto/buildgraph/CMakeLists.txt b/tests/auto/buildgraph/CMakeLists.txt
new file mode 100644
index 000000000..d8a9a4bbe
--- /dev/null
+++ b/tests/auto/buildgraph/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_qbs_test(buildgraph
+ SOURCES
+ tst_buildgraph.cpp
+ tst_buildgraph.h
+ DEPENDS
+ qbsquickjsheaders
+ )
diff --git a/tests/auto/buildgraph/buildgraph.pro b/tests/auto/buildgraph/buildgraph.pro
deleted file mode 100644
index 9230bb748..000000000
--- a/tests/auto/buildgraph/buildgraph.pro
+++ /dev/null
@@ -1,13 +0,0 @@
-TARGET = tst_buildgraph
-
-SOURCES = tst_buildgraph.cpp
-HEADERS = tst_buildgraph.h
-
-include(../auto.pri)
-include(../../../src/app/shared/logging/logging.pri)
-include(../../../src/lib/bundledlibs.pri)
-
-qbs_use_bundled_qtscript {
- CONFIG += qbs_do_not_link_bundled_qtscript
- include(../../../src/lib/scriptengine/use_scriptengine.pri)
-}
diff --git a/tests/auto/buildgraph/buildgraph.qbs b/tests/auto/buildgraph/buildgraph.qbs
index aa3cdc3f0..694d59578 100644
--- a/tests/auto/buildgraph/buildgraph.qbs
+++ b/tests/auto/buildgraph/buildgraph.qbs
@@ -1,6 +1,5 @@
-import qbs
-
-QbsAutotest {
+QbsUnittest {
+ Depends { name: "qbsconsolelogger" }
testName: "buildgraph"
condition: qbsbuildconfig.enableUnitTests
files: [
diff --git a/tests/auto/buildgraph/tst_buildgraph.cpp b/tests/auto/buildgraph/tst_buildgraph.cpp
index 20f2cc6a5..84a52c19b 100644
--- a/tests/auto/buildgraph/tst_buildgraph.cpp
+++ b/tests/auto/buildgraph/tst_buildgraph.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "tst_buildgraph.h"
+#include <app/shared/logging/consolelogger.h>
#include <buildgraph/artifact.h>
#include <buildgraph/buildgraph.h>
#include <buildgraph/cycledetector.h>
@@ -47,8 +48,6 @@
#include <logging/logger.h>
#include <tools/error.h>
-#include "../shared/logging/consolelogger.h"
-
#include <QtTest/qtest.h>
#include <memory>
diff --git a/tests/auto/cmdlineparser/CMakeLists.txt b/tests/auto/cmdlineparser/CMakeLists.txt
new file mode 100644
index 000000000..bf072e24d
--- /dev/null
+++ b/tests/auto/cmdlineparser/CMakeLists.txt
@@ -0,0 +1,23 @@
+set(PARSER_SOURCES
+ commandlineoption.cpp
+ commandlineoption.h
+ commandlineoptionpool.cpp
+ commandlineoptionpool.h
+ commandlineparser.cpp
+ commandlineparser.h
+ commandpool.cpp
+ commandpool.h
+ commandtype.h
+ parsercommand.cpp
+ parsercommand.h
+ )
+list_transform_prepend(PARSER_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/../../../src/app/qbs/parser/")
+
+add_qbs_test(cmdlineparser
+ DEFINES
+ "QBS_VERSION=\"${QBS_VERSION}\""
+ INCLUDES
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../../src"
+ SOURCES
+ tst_cmdlineparser.cpp ../../../src/app/qbs/qbstool.cpp ${PARSER_SOURCES}
+ )
diff --git a/tests/auto/cmdlineparser/cmdlineparser.pro b/tests/auto/cmdlineparser/cmdlineparser.pro
deleted file mode 100644
index a95676be5..000000000
--- a/tests/auto/cmdlineparser/cmdlineparser.pro
+++ /dev/null
@@ -1,7 +0,0 @@
-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
index a45e61f5d..340a0c42e 100644
--- a/tests/auto/cmdlineparser/cmdlineparser.qbs
+++ b/tests/auto/cmdlineparser/cmdlineparser.qbs
@@ -1,8 +1,8 @@
-import qbs
import qbs.Utilities
QbsAutotest {
Depends { name: "qbsversion" }
+ Depends { name: "qbsconsolelogger" }
testName: "cmdlineparser"
files: ["tst_cmdlineparser.cpp", "../../../src/app/qbs/qbstool.cpp"]
cpp.defines: base.concat([
diff --git a/tests/auto/cmdlineparser/tst_cmdlineparser.cpp b/tests/auto/cmdlineparser/tst_cmdlineparser.cpp
index 15b9ec382..6a8b44f89 100644
--- a/tests/auto/cmdlineparser/tst_cmdlineparser.cpp
+++ b/tests/auto/cmdlineparser/tst_cmdlineparser.cpp
@@ -26,6 +26,8 @@
**
****************************************************************************/
+#include "../shared.h"
+
#include <app/qbs/parser/commandlineparser.h>
#include <app/shared/logging/consolelogger.h>
#include <tools/buildoptions.h>
@@ -33,7 +35,6 @@
#include <tools/hostosinfo.h>
#include <QtCore/qdir.h>
-#include <QtCore/qregexp.h>
#include <QtCore/qtemporaryfile.h>
#include <QtTest/qtest.h>
@@ -56,6 +57,25 @@ private slots:
m_fileArgs = QStringList() << "-f" << m_projectFile.fileName();
}
+ void testResolve_data()
+ {
+ QTest::addColumn<QStringList>("args");
+ QTest::addColumn<int>("expectedJobCount");
+
+ QTest::newRow("default job count") << QStringList() << BuildOptions::defaultMaxJobCount();
+ QTest::newRow("explicit job count") << QStringList("-j5") << 5;
+ }
+ void testResolve()
+ {
+ QFETCH(QStringList, args);
+ QFETCH(int, expectedJobCount);
+
+ CommandLineParser parser;
+ QVERIFY(parser.parseCommandLine(QStringList("resolve") << args << m_fileArgs));
+ QCOMPARE(parser.command(), ResolveCommandType);
+ QCOMPARE(parser.jobCount(profileName()), expectedJobCount);
+ }
+
void testValidCommandLine()
{
QStringList args;
diff --git a/tests/auto/language/CMakeLists.txt b/tests/auto/language/CMakeLists.txt
new file mode 100644
index 000000000..9c04b6c8d
--- /dev/null
+++ b/tests/auto/language/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_qbs_test(language
+ DEFINES
+ "QBS_VERSION=\"${QBS_VERSION}\""
+ DEPENDS
+ qbsquickjsheaders
+ Qt6Core5Compat
+ SOURCES
+ tst_language.cpp
+ tst_language.h
+ )
diff --git a/tests/auto/language/language.pro b/tests/auto/language/language.pro
deleted file mode 100644
index 86248d926..000000000
--- a/tests/auto/language/language.pro
+++ /dev/null
@@ -1,25 +0,0 @@
-TARGET = tst_language
-
-SOURCES = tst_language.cpp
-HEADERS = tst_language.h
-
-include(../auto.pri)
-include(../../../src/app/shared/logging/logging.pri)
-include(../../../src/lib/bundledlibs.pri)
-
-!qbs_use_bundled_qtscript: QT += script
-
-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
-
-qbs_use_bundled_qtscript {
- CONFIG += qbs_do_not_link_bundled_qtscript
- include(../../../src/lib/scriptengine/use_scriptengine.pri)
-}
diff --git a/tests/auto/language/language.qbs b/tests/auto/language/language.qbs
index f43ca0e44..3767477db 100644
--- a/tests/auto/language/language.qbs
+++ b/tests/auto/language/language.qbs
@@ -1,13 +1,8 @@
-import qbs
import qbs.Utilities
-QbsAutotest {
+QbsUnittest {
Depends { name: "qbsversion" }
- Depends {
- name: "Qt.script"
- condition: !qbsbuildconfig.useBundledQtScript
- required: false
- }
+ Depends { name: "qbsconsolelogger" }
testName: "language"
condition: qbsbuildconfig.enableUnitTests
diff --git a/tests/auto/language/testdata/ParentWithExport.qbs b/tests/auto/language/testdata/ParentWithExport.qbs
index 16f9a2cd1..b16097e69 100644
--- a/tests/auto/language/testdata/ParentWithExport.qbs
+++ b/tests/auto/language/testdata/ParentWithExport.qbs
@@ -1,6 +1,6 @@
Product {
Export {
Depends { name: "dummy" }
- dummy.defines: [product.name.toUpperCase()]
+ dummy.defines: [exportingProduct.name.toUpperCase()]
}
}
diff --git a/tests/auto/language/testdata/additional-product-types.qbs b/tests/auto/language/testdata/additional-product-types.qbs
index 686650f42..f84397da3 100644
--- a/tests/auto/language/testdata/additional-product-types.qbs
+++ b/tests/auto/language/testdata/additional-product-types.qbs
@@ -5,8 +5,8 @@ Product {
Depends { name: "dummy" }
Depends { name: "dummy2" }
- property bool hasTag1: type.contains("tag1")
- property bool hasTag2: type.contains("tag2")
- property bool hasTag3: type.contains("tag3")
- property bool hasTag4: type.contains("tag4")
+ property bool hasTag1: type.includes("tag1")
+ property bool hasTag2: type.includes("tag2")
+ property bool hasTag3: type.includes("tag3")
+ property bool hasTag4: type.includes("tag4")
}
diff --git a/tests/auto/language/testdata/chained-probes/modules/m/m.qbs b/tests/auto/language/testdata/chained-probes/modules/m/m.qbs
index 3c6bcd1c9..bf68e56b6 100644
--- a/tests/auto/language/testdata/chained-probes/modules/m/m.qbs
+++ b/tests/auto/language/testdata/chained-probes/modules/m/m.qbs
@@ -2,13 +2,13 @@ Module {
Probe {
id: probe1
property string probe1Prop
- configure: { probe1Prop = "probe1Val"; found = true; }
+ configure: { probe1Prop = "probe1Val"; found = true }
}
Probe {
id: probe2
property string inputProp: prop1
property string probe2Prop
- configure: { probe2Prop = inputProp + "probe2Val"; found = true; }
+ configure: { probe2Prop = inputProp + "probe2Val"; found = true }
}
property string prop1: probe1.probe1Prop
property string prop2: probe2.probe2Prop
diff --git a/tests/auto/language/testdata/conditionaldepends.qbs b/tests/auto/language/testdata/conditionaldepends.qbs
index 9a499da92..e4f9fae7d 100644
--- a/tests/auto/language/testdata/conditionaldepends.qbs
+++ b/tests/auto/language/testdata/conditionaldepends.qbs
@@ -14,25 +14,25 @@ Project {
Product {
name: "product_props_true"
property bool someTrueProp: true
- Depends { condition: someTrueProp; name: "dummy"}
+ Depends { condition: someTrueProp; name: "dummy" }
}
Product {
name: "product_props_false"
property bool someFalseProp: false
- Depends { condition: someFalseProp; name: "dummy"}
+ Depends { condition: someFalseProp; name: "dummy" }
}
property bool someTruePrjProp: true
Product {
name: "project_props_true"
- Depends { condition: project.someTruePrjProp; name: "dummy"}
+ Depends { condition: project.someTruePrjProp; name: "dummy" }
}
property bool someFalsePrjProp: false
Product {
name: "project_props_false"
- Depends { condition: project.someFalsePrjProp; name: "dummy"}
+ Depends { condition: project.someFalsePrjProp; name: "dummy" }
}
Product {
diff --git a/tests/auto/language/testdata/conditionaldepends_base.qbs b/tests/auto/language/testdata/conditionaldepends_base.qbs
index 5ab5b973a..74eb958d4 100644
--- a/tests/auto/language/testdata/conditionaldepends_base.qbs
+++ b/tests/auto/language/testdata/conditionaldepends_base.qbs
@@ -1,4 +1,4 @@
-Application {
+Product {
name: 'conditionaldepends_base'
property bool someProp: false
Depends {
diff --git a/tests/auto/language/testdata/dotted-names/dotted-names.qbs b/tests/auto/language/testdata/dotted-names/dotted-names.qbs
index cf5658384..714988f0b 100644
--- a/tests/auto/language/testdata/dotted-names/dotted-names.qbs
+++ b/tests/auto/language/testdata/dotted-names/dotted-names.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
name: "theProject"
property bool includeDottedProduct
diff --git a/tests/auto/language/testdata/dotted-names/modules/x/y/xy.qbs b/tests/auto/language/testdata/dotted-names/modules/x/y/xy.qbs
index 71cfac9cb..eee46458d 100644
--- a/tests/auto/language/testdata/dotted-names/modules/x/y/xy.qbs
+++ b/tests/auto/language/testdata/dotted-names/modules/x/y/xy.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Module {
property string z: "default"
}
diff --git a/tests/auto/language/testdata/duplicate-multiplex-value.qbs b/tests/auto/language/testdata/duplicate-multiplex-value.qbs
new file mode 100644
index 000000000..24b246604
--- /dev/null
+++ b/tests/auto/language/testdata/duplicate-multiplex-value.qbs
@@ -0,0 +1,6 @@
+Product {
+ name: "p"
+ multiplexByQbsProperties: "architectures"
+ aggregate: false
+ qbs.architectures: ["x86", "arm", "x86"]
+}
diff --git a/tests/auto/language/testdata/duplicate-multiplex-value2.qbs b/tests/auto/language/testdata/duplicate-multiplex-value2.qbs
new file mode 100644
index 000000000..d6c057a9e
--- /dev/null
+++ b/tests/auto/language/testdata/duplicate-multiplex-value2.qbs
@@ -0,0 +1,6 @@
+Product {
+ name: "p"
+ multiplexByQbsProperties: ["architectures", "buildVariants", "architectures"]
+ aggregate: false
+ qbs.architectures: ["x86", "arm"]
+}
diff --git a/tests/auto/language/testdata/erroneous/ambiguous-multiplex-dependency.qbs b/tests/auto/language/testdata/erroneous/ambiguous-multiplex-dependency.qbs
new file mode 100644
index 000000000..6f60b1faf
--- /dev/null
+++ b/tests/auto/language/testdata/erroneous/ambiguous-multiplex-dependency.qbs
@@ -0,0 +1,14 @@
+Project {
+ Product {
+ name: "a"
+ multiplexByQbsProperties: ["architectures", "buildVariants"]
+ qbs.architectures: ["x86", "arm"]
+ qbs.buildVariants: ["debug", "release"]
+ }
+ Product {
+ name: "b"
+ Depends { name: "a" }
+ multiplexByQbsProperties: ["architectures"]
+ qbs.architectures: ["x86", "arm"]
+ }
+}
diff --git a/tests/auto/language/testdata/erroneous/conflicting_fileTagsFilter.qbs b/tests/auto/language/testdata/erroneous/conflicting_fileTagsFilter.qbs
index 97e11bb9b..3738e0cca 100644
--- a/tests/auto/language/testdata/erroneous/conflicting_fileTagsFilter.qbs
+++ b/tests/auto/language/testdata/erroneous/conflicting_fileTagsFilter.qbs
@@ -1,10 +1,11 @@
-Application {
+Product {
+ type: "app"
Group {
- fileTagsFilter: "application"
+ fileTagsFilter: "app"
qbs.install: true
}
Group {
- fileTagsFilter: "application"
+ fileTagsFilter: "app"
qbs.install: false
}
}
diff --git a/tests/auto/language/testdata/erroneous/dependency-profile-mismatch-2.qbs b/tests/auto/language/testdata/erroneous/dependency-profile-mismatch-2.qbs
new file mode 100644
index 000000000..d76907a35
--- /dev/null
+++ b/tests/auto/language/testdata/erroneous/dependency-profile-mismatch-2.qbs
@@ -0,0 +1,17 @@
+Project {
+ Profile {
+ name: "profile1"
+ }
+ Profile {
+ name: "profile2"
+ }
+
+ Product {
+ name: "dep"
+ qbs.profiles: ["profile1", "profile2"]
+ }
+ Product {
+ name: "main"
+ Depends { name: "dep"; profiles: ["profile47"]; }
+ }
+}
diff --git a/tests/auto/language/testdata/erroneous/dependency-profile-mismatch.qbs b/tests/auto/language/testdata/erroneous/dependency-profile-mismatch.qbs
new file mode 100644
index 000000000..e014fa9b1
--- /dev/null
+++ b/tests/auto/language/testdata/erroneous/dependency-profile-mismatch.qbs
@@ -0,0 +1,14 @@
+Project {
+ Profile {
+ name: "profile1"
+ }
+
+ Product {
+ name: "dep"
+ qbs.profiles: ["profile1"]
+ }
+ Product {
+ name: "main"
+ Depends { name: "dep"; profiles: ["profile47"]; }
+ }
+}
diff --git a/tests/auto/language/testdata/erroneous/dependency_cycle.qbs b/tests/auto/language/testdata/erroneous/dependency_cycle.qbs
index 83a6e35f6..80c678f89 100644
--- a/tests/auto/language/testdata/erroneous/dependency_cycle.qbs
+++ b/tests/auto/language/testdata/erroneous/dependency_cycle.qbs
@@ -1,15 +1,15 @@
Project {
- CppApplication {
+ Product {
name: "A"
Depends { name: "B" }
files: ["main.cpp"]
}
- CppApplication {
+ Product {
name: "B"
Depends { name: "C" }
files: ["main.cpp"]
}
- CppApplication {
+ Product {
name: "C"
Depends { name: "A" }
files: ["main.cpp"]
diff --git a/tests/auto/language/testdata/erroneous/dependency_cycle2.qbs b/tests/auto/language/testdata/erroneous/dependency_cycle2.qbs
index 335355480..4aff0b75c 100644
--- a/tests/auto/language/testdata/erroneous/dependency_cycle2.qbs
+++ b/tests/auto/language/testdata/erroneous/dependency_cycle2.qbs
@@ -1,20 +1,20 @@
Project {
- CppApplication {
+ Product {
name: "A"
Depends { name: "B" }
files: ["main.cpp"]
}
- CppApplication {
+ Product {
name: "B"
Depends { name: "C" }
files: ["main.cpp"]
}
- CppApplication {
+ Product {
name: "C"
Depends { name: "A" }
files: ["main.cpp"]
}
- CppApplication {
+ Product {
name: "D"
files: ["main.cpp"]
}
diff --git a/tests/auto/language/testdata/erroneous/dependency_cycle3a.qbs b/tests/auto/language/testdata/erroneous/dependency_cycle3a.qbs
new file mode 100644
index 000000000..a0660c074
--- /dev/null
+++ b/tests/auto/language/testdata/erroneous/dependency_cycle3a.qbs
@@ -0,0 +1,11 @@
+Project {
+ Product {
+ name: "B"
+ Depends { productTypes: ["a"] }
+ }
+ Product {
+ type: ["a"]
+ name: "A"
+ Depends { name: "B" }
+ }
+}
diff --git a/tests/auto/language/testdata/erroneous/duplicate-multiplex-value.qbs b/tests/auto/language/testdata/erroneous/duplicate-multiplex-value.qbs
deleted file mode 100644
index 56da41af1..000000000
--- a/tests/auto/language/testdata/erroneous/duplicate-multiplex-value.qbs
+++ /dev/null
@@ -1,8 +0,0 @@
-import qbs
-
-Product {
- name: "p"
- multiplexByQbsProperties: "architectures"
- aggregate: false
- qbs.architectures: ["x86", "arm", "x86"]
-}
diff --git a/tests/auto/language/testdata/erroneous/duplicate-multiplex-value2.qbs b/tests/auto/language/testdata/erroneous/duplicate-multiplex-value2.qbs
deleted file mode 100644
index e412e5210..000000000
--- a/tests/auto/language/testdata/erroneous/duplicate-multiplex-value2.qbs
+++ /dev/null
@@ -1,8 +0,0 @@
-import qbs
-
-Product {
- name: "p"
- multiplexByQbsProperties: ["architectures", "buildVariants", "architectures"]
- aggregate: false
- qbs.architectures: ["x86", "arm"]
-}
diff --git a/tests/auto/language/testdata/erroneous/frozen-object-list.qbs b/tests/auto/language/testdata/erroneous/frozen-object-list.qbs
new file mode 100644
index 000000000..8bbd2b413
--- /dev/null
+++ b/tests/auto/language/testdata/erroneous/frozen-object-list.qbs
@@ -0,0 +1,17 @@
+
+Product {
+ Probe {
+ id: probe
+ property varList output
+ configure: {
+ output = [{"key": "value"}];
+ found = true;
+ }
+ }
+
+ property var test: {
+ var result = probe.output;
+ result.push({});
+ return result;
+ }
+}
diff --git a/tests/auto/language/testdata/erroneous/frozen-object.qbs b/tests/auto/language/testdata/erroneous/frozen-object.qbs
new file mode 100644
index 000000000..0f891d04b
--- /dev/null
+++ b/tests/auto/language/testdata/erroneous/frozen-object.qbs
@@ -0,0 +1,18 @@
+
+Product {
+ Probe {
+ id: probe
+ property var output
+ configure: {
+ output = {"key": "value"}
+ found = true
+ }
+ }
+
+ property var test: {
+ "use strict"
+ var result = probe.output;
+ result.key = "newValue";
+ return result;
+ }
+}
diff --git a/tests/auto/language/testdata/erroneous/invalid_file.qbs b/tests/auto/language/testdata/erroneous/invalid_file.qbs
index c461b87ef..54ec27410 100644
--- a/tests/auto/language/testdata/erroneous/invalid_file.qbs
+++ b/tests/auto/language/testdata/erroneous/invalid_file.qbs
@@ -1,3 +1,3 @@
-Application {
+Product {
files: ["main.cpp", "other.h"]
}
diff --git a/tests/auto/language/testdata/erroneous/missing-colon.qbs b/tests/auto/language/testdata/erroneous/missing-colon.qbs
index e62eb7cfd..6e136bb56 100644
--- a/tests/auto/language/testdata/erroneous/missing-colon.qbs
+++ b/tests/auto/language/testdata/erroneous/missing-colon.qbs
@@ -1,3 +1,5 @@
-CppApplication {
- cpp.dynamicLibraries { }
+Product {
+ Depends { name: "dummy" }
+ qbsSearchPaths: ".."
+ dummy.cxxFlags { }
}
diff --git a/tests/auto/language/testdata/erroneous/missing-js-file.qbs b/tests/auto/language/testdata/erroneous/missing-js-file.qbs
new file mode 100644
index 000000000..a3274a35b
--- /dev/null
+++ b/tests/auto/language/testdata/erroneous/missing-js-file.qbs
@@ -0,0 +1,3 @@
+Product {
+ Depends { name: "missing-js-file-module" }
+}
diff --git a/tests/auto/language/testdata/erroneous/module-property-binding-in-project.qbs b/tests/auto/language/testdata/erroneous/module-property-binding-in-project.qbs
new file mode 100644
index 000000000..667088c17
--- /dev/null
+++ b/tests/auto/language/testdata/erroneous/module-property-binding-in-project.qbs
@@ -0,0 +1,3 @@
+Project {
+ qbs.sysroot: "/"
+}
diff --git a/tests/auto/language/testdata/erroneous/modules/missing-js-file-module/missing-js-file-module.qbs b/tests/auto/language/testdata/erroneous/modules/missing-js-file-module/missing-js-file-module.qbs
new file mode 100644
index 000000000..31302b5c2
--- /dev/null
+++ b/tests/auto/language/testdata/erroneous/modules/missing-js-file-module/missing-js-file-module.qbs
@@ -0,0 +1,3 @@
+import "missing-js-file.js" as MissingJsFile
+
+Module { }
diff --git a/tests/auto/language/testdata/erroneous/modules/missing-js-file-module/missing-js-file.js b/tests/auto/language/testdata/erroneous/modules/missing-js-file-module/missing-js-file.js
new file mode 100644
index 000000000..b66048a8f
--- /dev/null
+++ b/tests/auto/language/testdata/erroneous/modules/missing-js-file-module/missing-js-file.js
@@ -0,0 +1 @@
+var userfile = require("javascriptfile.js")
diff --git a/tests/auto/language/testdata/erroneous/original-in-export-item.qbs b/tests/auto/language/testdata/erroneous/original-in-export-item.qbs
index 51fedb9a2..c83601cfb 100644
--- a/tests/auto/language/testdata/erroneous/original-in-export-item.qbs
+++ b/tests/auto/language/testdata/erroneous/original-in-export-item.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
Product {
name: "a"
diff --git a/tests/auto/language/testdata/erroneous/original-in-export-item2.qbs b/tests/auto/language/testdata/erroneous/original-in-export-item2.qbs
index d932d4aee..1c9f3de4b 100644
--- a/tests/auto/language/testdata/erroneous/original-in-export-item2.qbs
+++ b/tests/auto/language/testdata/erroneous/original-in-export-item2.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
Product {
name: "a"
diff --git a/tests/auto/language/testdata/erroneous/original-in-export-item3.qbs b/tests/auto/language/testdata/erroneous/original-in-export-item3.qbs
index d7bcb322e..d23ad0a08 100644
--- a/tests/auto/language/testdata/erroneous/original-in-export-item3.qbs
+++ b/tests/auto/language/testdata/erroneous/original-in-export-item3.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
Product {
name: "a"
diff --git a/tests/auto/language/testdata/erroneous/original-in-module-prototype.qbs b/tests/auto/language/testdata/erroneous/original-in-module-prototype.qbs
index e7128092a..00661fb66 100644
--- a/tests/auto/language/testdata/erroneous/original-in-module-prototype.qbs
+++ b/tests/auto/language/testdata/erroneous/original-in-module-prototype.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Product {
Depends { name: "module-with-invalid-original" }
}
diff --git a/tests/auto/language/testdata/erroneous/properties-item-with-invalid-condition.qbs b/tests/auto/language/testdata/erroneous/properties-item-with-invalid-condition.qbs
index 5b1a41f1b..0b48731ae 100644
--- a/tests/auto/language/testdata/erroneous/properties-item-with-invalid-condition.qbs
+++ b/tests/auto/language/testdata/erroneous/properties-item-with-invalid-condition.qbs
@@ -1,7 +1,7 @@
Product {
Depends { name: "cpp" }
Properties {
- condition: cpp.nonexistingproperty.contains("somevalue")
+ condition: cpp.nonexistingproperty.includes("somevalue")
cpp.defines: ["ABC"]
}
}
diff --git a/tests/auto/language/testdata/eval-error-in-non-present-module.qbs b/tests/auto/language/testdata/eval-error-in-non-present-module.qbs
index c112bc6d4..6882a2216 100644
--- a/tests/auto/language/testdata/eval-error-in-non-present-module.qbs
+++ b/tests/auto/language/testdata/eval-error-in-non-present-module.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Product {
name: "p"
property bool moduleRequired
diff --git a/tests/auto/language/testdata/exports.qbs b/tests/auto/language/testdata/exports.qbs
index cc86b1e4a..0071aadce 100644
--- a/tests/auto/language/testdata/exports.qbs
+++ b/tests/auto/language/testdata/exports.qbs
@@ -1,7 +1,8 @@
import "exports_product.qbs" as ProductWithInheritedExportItem
Project {
- Application {
+ Product {
+ type: "app"
name: "myapp"
Depends { name: "mylib" }
Depends { name: "dummy" }
@@ -15,30 +16,35 @@ Project {
"subdir2/exports-mylib2.qbs"
]
- Application {
+ Product {
+ type: "app"
name: "A"
Depends { name: "qbs" }
Depends { name: "B" }
}
- StaticLibrary {
+ Product {
+ type: "lib"
name: "B"
Export {
Depends { name: "C" }
Depends { name: "qbs" }
}
}
- StaticLibrary {
+ Product {
+ type: "lib"
name: "C"
Export {
Depends { name: "D" }
Depends { name: "qbs" }
}
}
- StaticLibrary {
+ Product {
+ type: "lib"
name: "D"
}
- Application {
+ Product {
+ type: "app"
name: "myapp2"
Depends { name: "productWithInheritedExportItem" }
Depends { name: "qbs" }
@@ -46,7 +52,7 @@ Project {
ProductWithInheritedExportItem {
name: "productWithInheritedExportItem"
Export {
- dummy.cFlags: base.concat("PRODUCT_" + product.name.toUpperCase())
+ dummy.cFlags: base.concat("PRODUCT_" + exportingProduct.name.toUpperCase())
dummy.cxxFlags: ["-bar"]
Properties {
condition: true
@@ -54,7 +60,8 @@ Project {
}
}
}
- Application {
+ Product {
+ type: "app"
name: "myapp3"
Depends { name: "productWithInheritedExportItem"; versionAtLeast: "2.0" }
}
@@ -125,7 +132,7 @@ Project {
Export {
property bool depend: false
- property var config: product.config
+ property var config: exportingProduct.config
Depends { condition: depend; name: "cpp" }
Properties { condition: depend; cpp.includePaths: ["."] }
}
@@ -142,8 +149,9 @@ Project {
name: "broken_cycle1"
Export {
property bool depend: true
- Depends { name: "broken_cycle3"; condition: depend } }
+ Depends { name: "broken_cycle3"; condition: depend }
}
+ }
Product {
name: "broken_cycle2"
Export {
diff --git a/tests/auto/language/testdata/exports_product.qbs b/tests/auto/language/testdata/exports_product.qbs
index dc4c9f462..394d388d9 100644
--- a/tests/auto/language/testdata/exports_product.qbs
+++ b/tests/auto/language/testdata/exports_product.qbs
@@ -2,7 +2,7 @@ Product {
Export {
version: "2.0"
Depends { name: "dummy" }
- dummy.cFlags: ["BASE_" + product.name.toUpperCase()]
+ dummy.cFlags: ["BASE_" + exportingProduct.name.toUpperCase()]
dummy.cxxFlags: ["-foo"]
dummy.defines: ["ABC"]
}
diff --git a/tests/auto/language/testdata/getNativeSetting.qbs b/tests/auto/language/testdata/getNativeSetting.qbs
index 975aefebc..73af01316 100644
--- a/tests/auto/language/testdata/getNativeSetting.qbs
+++ b/tests/auto/language/testdata/getNativeSetting.qbs
@@ -1,15 +1,17 @@
import qbs.FileInfo
import qbs.Utilities
+import qbs.Host
+
Project {
Product {
name: "p1"
targetName: {
- if (qbs.hostOS.contains("macos")) {
+ if (Host.os().includes("macos")) {
return Utilities.getNativeSetting("/System/Library/CoreServices/SystemVersion.plist", "ProductName");
- } else if (qbs.hostOS.contains("windows")) {
+ } else if (Host.os().includes("windows")) {
var productName = Utilities.getNativeSetting("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductName");
- if (productName.contains("Windows")) {
+ if (productName.includes("Windows")) {
return "Windows";
}
return undefined;
diff --git a/tests/auto/language/testdata/groupconditions.qbs b/tests/auto/language/testdata/groupconditions.qbs
index b7f383d45..4f55db9cd 100644
--- a/tests/auto/language/testdata/groupconditions.qbs
+++ b/tests/auto/language/testdata/groupconditions.qbs
@@ -43,7 +43,7 @@ Project {
Product {
name: "condition_accessing_module_property"
Group {
- condition: qbs.targetOS.contains("narf")
+ condition: qbs.targetOS.includes("narf")
files: ["main.cpp"]
qbs.install: false
}
diff --git a/tests/auto/language/testdata/inherited-properties-items/imports/DebugName.qbs b/tests/auto/language/testdata/inherited-properties-items/imports/DebugName.qbs
index 5ac15658c..d129dc9db 100644
--- a/tests/auto/language/testdata/inherited-properties-items/imports/DebugName.qbs
+++ b/tests/auto/language/testdata/inherited-properties-items/imports/DebugName.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Properties {
condition: qbs.buildVariant === "debug"
name: "product_debug"
diff --git a/tests/auto/language/testdata/inherited-properties-items/imports/ReleaseName.qbs b/tests/auto/language/testdata/inherited-properties-items/imports/ReleaseName.qbs
index e33c5652b..57ab96a7e 100644
--- a/tests/auto/language/testdata/inherited-properties-items/imports/ReleaseName.qbs
+++ b/tests/auto/language/testdata/inherited-properties-items/imports/ReleaseName.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Properties {
condition: qbs.buildVariant === "release"
name: "product_release"
diff --git a/tests/auto/language/testdata/inherited-properties-items/inherited-properties-items-product.qbs b/tests/auto/language/testdata/inherited-properties-items/inherited-properties-items-product.qbs
index c20f045c8..6d148ba53 100644
--- a/tests/auto/language/testdata/inherited-properties-items/inherited-properties-items-product.qbs
+++ b/tests/auto/language/testdata/inherited-properties-items/inherited-properties-items-product.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Product {
name: "product_default"
DebugName {}
diff --git a/tests/auto/language/testdata/inherited-properties-items/inherited-properties-items.qbs b/tests/auto/language/testdata/inherited-properties-items/inherited-properties-items.qbs
index 40d41c2fb..52f7ffc00 100644
--- a/tests/auto/language/testdata/inherited-properties-items/inherited-properties-items.qbs
+++ b/tests/auto/language/testdata/inherited-properties-items/inherited-properties-items.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Project {
qbsSearchPaths: sourceDirectory
references: "inherited-properties-items-product.qbs"
diff --git a/tests/auto/language/testdata/invalid-prop-on-non-required-module/invalid-prop-on-non-required-module.qbs b/tests/auto/language/testdata/invalid-prop-on-non-required-module/invalid-prop-on-non-required-module.qbs
new file mode 100644
index 000000000..80ae6ad93
--- /dev/null
+++ b/tests/auto/language/testdata/invalid-prop-on-non-required-module/invalid-prop-on-non-required-module.qbs
@@ -0,0 +1,19 @@
+Project {
+ property bool useExistingModule
+
+ Product {
+ name: "a"
+ condition: project.useExistingModule
+ Depends { name: "deploader" }
+ Depends { name: "dep" }
+ dep.nosuchprop: true
+ }
+
+ Product {
+ name: "b"
+ condition: !project.useExistingModule
+ Depends { name: "deploader" }
+ Depends { name: "random"; required: false }
+ random.nosuchprop: true
+ }
+}
diff --git a/tests/auto/language/testdata/invalid-prop-on-non-required-module/modules/dep/dep.qbs b/tests/auto/language/testdata/invalid-prop-on-non-required-module/modules/dep/dep.qbs
new file mode 100644
index 000000000..5e45122de
--- /dev/null
+++ b/tests/auto/language/testdata/invalid-prop-on-non-required-module/modules/dep/dep.qbs
@@ -0,0 +1 @@
+Module {}
diff --git a/tests/auto/language/testdata/invalid-prop-on-non-required-module/modules/deploader/deploader.qbs b/tests/auto/language/testdata/invalid-prop-on-non-required-module/modules/deploader/deploader.qbs
new file mode 100644
index 000000000..15a1b5309
--- /dev/null
+++ b/tests/auto/language/testdata/invalid-prop-on-non-required-module/modules/deploader/deploader.qbs
@@ -0,0 +1,7 @@
+Module {
+ // This indirection exists to properly model QBS-1776.
+ // "deploader" corresponds to "bundle", and "dep" corresponds to "codesign"
+ Depends { condition: project.useExistingModule; name: "dep"; required: false }
+
+ Depends { condition: !project.useExistingModule; name: "random"; required: false }
+}
diff --git a/tests/auto/language/testdata/jsextensions.js b/tests/auto/language/testdata/jsextensions.js
index df74a263b..5c5821e86 100644
--- a/tests/auto/language/testdata/jsextensions.js
+++ b/tests/auto/language/testdata/jsextensions.js
@@ -57,9 +57,9 @@ 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"));
+verify(a.includes("one"));
+verify(a.includes("two"));
+verify(a.includes("three"));
+verify(!a.includes("four"));
})() // END function wrapper
diff --git a/tests/auto/language/testdata/local-profile-as-top-level-profile.qbs b/tests/auto/language/testdata/local-profile-as-top-level-profile.qbs
new file mode 100644
index 000000000..9bca3c599
--- /dev/null
+++ b/tests/auto/language/testdata/local-profile-as-top-level-profile.qbs
@@ -0,0 +1,7 @@
+Product {
+ Profile {
+ name: "test-profile"
+ qbs.architecture: "arm"
+ qbs.targetPlatform: "macos"
+ }
+}
diff --git a/tests/auto/language/testdata/erroneous/module-depends-on-product.qbs b/tests/auto/language/testdata/module-depends-on-product.qbs
index a7db9e036..a7db9e036 100644
--- a/tests/auto/language/testdata/erroneous/module-depends-on-product.qbs
+++ b/tests/auto/language/testdata/module-depends-on-product.qbs
diff --git a/tests/auto/language/testdata/module-name-collisions/complex-collision.qbs b/tests/auto/language/testdata/module-name-collisions/complex-collision.qbs
new file mode 100644
index 000000000..cd55aa946
--- /dev/null
+++ b/tests/auto/language/testdata/module-name-collisions/complex-collision.qbs
@@ -0,0 +1,4 @@
+Product {
+ Depends { name: "prefix1.middle1" }
+ Depends { name: "prefix1.middle1.suffix1" }
+}
diff --git a/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/middle1.qbs b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/middle1.qbs
new file mode 100644
index 000000000..5e45122de
--- /dev/null
+++ b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/middle1.qbs
@@ -0,0 +1 @@
+Module {}
diff --git a/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/suffix1/suffix1.qbs b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/suffix1/suffix1.qbs
new file mode 100644
index 000000000..5e45122de
--- /dev/null
+++ b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/suffix1/suffix1.qbs
@@ -0,0 +1 @@
+Module {}
diff --git a/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/suffix2/suffix2.qbs b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/suffix2/suffix2.qbs
new file mode 100644
index 000000000..5e45122de
--- /dev/null
+++ b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle1/suffix2/suffix2.qbs
@@ -0,0 +1 @@
+Module {}
diff --git a/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle2/suffix/suffix.qbs b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle2/suffix/suffix.qbs
new file mode 100644
index 000000000..5e45122de
--- /dev/null
+++ b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/middle2/suffix/suffix.qbs
@@ -0,0 +1 @@
+Module {}
diff --git a/tests/auto/language/testdata/module-name-collisions/modules/prefix1/prefix1.qbs b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/prefix1.qbs
new file mode 100644
index 000000000..84957060c
--- /dev/null
+++ b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/prefix1.qbs
@@ -0,0 +1,2 @@
+Module {
+}
diff --git a/tests/auto/language/testdata/erroneous/modules/prefix1/suffix/suffix.qbs b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/suffix/suffix.qbs
index 218a4feb7..218a4feb7 100644
--- a/tests/auto/language/testdata/erroneous/modules/prefix1/suffix/suffix.qbs
+++ b/tests/auto/language/testdata/module-name-collisions/modules/prefix1/suffix/suffix.qbs
diff --git a/tests/auto/language/testdata/module-name-collisions/modules/prefix2/prefix2.qbs b/tests/auto/language/testdata/module-name-collisions/modules/prefix2/prefix2.qbs
new file mode 100644
index 000000000..a5aaa6f8b
--- /dev/null
+++ b/tests/auto/language/testdata/module-name-collisions/modules/prefix2/prefix2.qbs
@@ -0,0 +1,3 @@
+Module {
+ Depends { name: "prefix2.suffix" }
+}
diff --git a/tests/auto/language/testdata/module-name-collisions/modules/prefix2/suffix/suffix.qbs b/tests/auto/language/testdata/module-name-collisions/modules/prefix2/suffix/suffix.qbs
new file mode 100644
index 000000000..84957060c
--- /dev/null
+++ b/tests/auto/language/testdata/module-name-collisions/modules/prefix2/suffix/suffix.qbs
@@ -0,0 +1,2 @@
+Module {
+}
diff --git a/tests/auto/language/testdata/module-name-collisions/no-collision1.qbs b/tests/auto/language/testdata/module-name-collisions/no-collision1.qbs
new file mode 100644
index 000000000..e5a94ad66
--- /dev/null
+++ b/tests/auto/language/testdata/module-name-collisions/no-collision1.qbs
@@ -0,0 +1,4 @@
+Product {
+ Depends { name: "prefix1.middle1.suffix1" }
+ Depends { name: "prefix1.middle1.suffix2" }
+}
diff --git a/tests/auto/language/testdata/module-name-collisions/no-collision2.qbs b/tests/auto/language/testdata/module-name-collisions/no-collision2.qbs
new file mode 100644
index 000000000..664ec1729
--- /dev/null
+++ b/tests/auto/language/testdata/module-name-collisions/no-collision2.qbs
@@ -0,0 +1,4 @@
+Product {
+ Depends { name: "prefix1.middle1" }
+ Depends { name: "prefix1.middle2.suffix" }
+}
diff --git a/tests/auto/language/testdata/erroneous/same-module-prefix1.qbs b/tests/auto/language/testdata/module-name-collisions/simple-collision1.qbs
index 8aba31c2a..8aba31c2a 100644
--- a/tests/auto/language/testdata/erroneous/same-module-prefix1.qbs
+++ b/tests/auto/language/testdata/module-name-collisions/simple-collision1.qbs
diff --git a/tests/auto/language/testdata/erroneous/same-module-prefix2.qbs b/tests/auto/language/testdata/module-name-collisions/simple-collision2.qbs
index 6679091c2..6679091c2 100644
--- a/tests/auto/language/testdata/erroneous/same-module-prefix2.qbs
+++ b/tests/auto/language/testdata/module-name-collisions/simple-collision2.qbs
diff --git a/tests/auto/language/testdata/module-parameters/module-parameters.qbs b/tests/auto/language/testdata/module-parameters/module-parameters.qbs
new file mode 100644
index 000000000..48169d601
--- /dev/null
+++ b/tests/auto/language/testdata/module-parameters/module-parameters.qbs
@@ -0,0 +1,35 @@
+Project {
+ property bool overrideFromModule
+ property bool overrideFromExport
+ property bool overrideFromProduct
+
+ Product {
+ name: "dep"
+ Export {
+ Depends {
+ name: "higher";
+ condition: project.overrideFromExport
+ lower.param: "fromExportDepends"
+ }
+ Parameters { lower.param: "fromParameters" }
+ }
+ }
+ Product {
+ name: "main"
+
+ Depends {
+ name: "dep"
+ condition: project.overrideFromProduct
+ lower.param: "fromProductDepends"
+ }
+ Depends {
+ name: "higher"
+ condition: project.overrideFromProduct
+ lower.param: "fromProductDepends"
+ }
+ Depends { name: "dep"; condition: !project.overrideFromProduct }
+ Depends { name: "higher"; condition: !project.overrideFromProduct }
+ Depends { name: "highest" }
+ Depends { name: "broken"; required: false }
+ }
+}
diff --git a/tests/auto/language/testdata/module-parameters/modules/broken/broken.qbs b/tests/auto/language/testdata/module-parameters/modules/broken/broken.qbs
new file mode 100644
index 000000000..ae7b4c4ef
--- /dev/null
+++ b/tests/auto/language/testdata/module-parameters/modules/broken/broken.qbs
@@ -0,0 +1,4 @@
+Module {
+ Depends { name: "higher"; lower.param: "shouldNeverAppear" }
+ validate: { throw "As the name indicates, this module is broken."; }
+}
diff --git a/tests/auto/language/testdata/module-parameters/modules/higher/higher.qbs b/tests/auto/language/testdata/module-parameters/modules/higher/higher.qbs
new file mode 100644
index 000000000..006e05a93
--- /dev/null
+++ b/tests/auto/language/testdata/module-parameters/modules/higher/higher.qbs
@@ -0,0 +1,4 @@
+Module {
+ Depends { name: "lower" }
+ Parameters { lower.param: "fromParameters" }
+}
diff --git a/tests/auto/language/testdata/module-parameters/modules/highest/highest.qbs b/tests/auto/language/testdata/module-parameters/modules/highest/highest.qbs
new file mode 100644
index 000000000..83b0a0a03
--- /dev/null
+++ b/tests/auto/language/testdata/module-parameters/modules/highest/highest.qbs
@@ -0,0 +1,7 @@
+Module {
+ Depends {
+ name: "higher"
+ condition: project.overrideFromModule
+ lower.param: "fromModuleDepends"
+ }
+}
diff --git a/tests/auto/language/testdata/module-parameters/modules/lower/lower.qbs b/tests/auto/language/testdata/module-parameters/modules/lower/lower.qbs
new file mode 100644
index 000000000..11436ecd8
--- /dev/null
+++ b/tests/auto/language/testdata/module-parameters/modules/lower/lower.qbs
@@ -0,0 +1,3 @@
+Module {
+ Parameter { property string param: "origin" }
+}
diff --git a/tests/auto/language/testdata/modulepropertiesingroups.qbs b/tests/auto/language/testdata/modulepropertiesingroups.qbs
index e3857bdf4..49f24c0ca 100644
--- a/tests/auto/language/testdata/modulepropertiesingroups.qbs
+++ b/tests/auto/language/testdata/modulepropertiesingroups.qbs
@@ -80,4 +80,13 @@ Project {
}
}
}
+
+ Product {
+ name: "module-property-in-group-condition"
+ Depends { name: "cpp" }
+ Group {
+ condition: qbs.architecture === "x86_64"
+ cpp.includePaths: "."
+ }
+ }
}
diff --git a/tests/auto/language/testdata/modules/broken/broken.qbs b/tests/auto/language/testdata/modules/broken/broken.qbs
index b960117cf..a80547340 100644
--- a/tests/auto/language/testdata/modules/broken/broken.qbs
+++ b/tests/auto/language/testdata/modules/broken/broken.qbs
@@ -1,5 +1,3 @@
-import qbs
-
Module {
Probe {
id: theProbe
diff --git a/tests/auto/language/testdata/modules/dummy/dummy.qbs b/tests/auto/language/testdata/modules/dummy/dummy.qbs
index b1791ac96..623abc413 100644
--- a/tests/auto/language/testdata/modules/dummy/dummy.qbs
+++ b/tests/auto/language/testdata/modules/dummy/dummy.qbs
@@ -16,7 +16,7 @@ DummyBase {
property string zort: "zort in dummy"
property pathList includePaths
property path somePath
- property stringList listProp: product.type.contains("blubb") ? ["123"] : ["456"]
+ property stringList listProp: product.type.includes("blubb") ? ["123"] : ["456"]
property bool controllingProp: false
property stringList listProp2: controllingProp
diff --git a/tests/auto/language/testdata/erroneous/modules/module-with-product-dependency/module-with-product-dependency.qbs b/tests/auto/language/testdata/modules/module-with-product-dependency/module-with-product-dependency.qbs
index 5781bd6de..5781bd6de 100644
--- a/tests/auto/language/testdata/erroneous/modules/module-with-product-dependency/module-with-product-dependency.qbs
+++ b/tests/auto/language/testdata/modules/module-with-product-dependency/module-with-product-dependency.qbs
diff --git a/tests/auto/language/testdata/modules/multiple_backends/backend1.qbs b/tests/auto/language/testdata/modules/multiple_backends/backend1.qbs
index 794c67827..4f78a89ae 100644
--- a/tests/auto/language/testdata/modules/multiple_backends/backend1.qbs
+++ b/tests/auto/language/testdata/modules/multiple_backends/backend1.qbs
@@ -1,4 +1,4 @@
Module {
- condition: qbs.targetOS.contains("os1")
+ condition: qbs.targetOS.includes("os1")
property string prop: "backend 1"
}
diff --git a/tests/auto/language/testdata/modules/multiple_backends/backend2.qbs b/tests/auto/language/testdata/modules/multiple_backends/backend2.qbs
index 2073c4d0b..e7a44d4c7 100644
--- a/tests/auto/language/testdata/modules/multiple_backends/backend2.qbs
+++ b/tests/auto/language/testdata/modules/multiple_backends/backend2.qbs
@@ -1,5 +1,5 @@
Module {
- condition: qbs.targetOS.contains("os2")
+ condition: qbs.targetOS.includes("os2")
property string prop: "backend 2"
property string backend2Prop
}
diff --git a/tests/auto/language/testdata/modules/multiple_backends/backend3.qbs b/tests/auto/language/testdata/modules/multiple_backends/backend3.qbs
index 16228108b..762c12f20 100644
--- a/tests/auto/language/testdata/modules/multiple_backends/backend3.qbs
+++ b/tests/auto/language/testdata/modules/multiple_backends/backend3.qbs
@@ -1,5 +1,5 @@
Module {
- condition: qbs.targetOS.contains("os2") && qbs.toolchain.contains("tc")
+ condition: qbs.targetOS.includes("os2") && qbs.toolchain.includes("tc")
priority: 1
property string backend3Prop
}
diff --git a/tests/auto/language/testdata/multiplexed-exports.qbs b/tests/auto/language/testdata/multiplexed-exports.qbs
index f98ceff95..3252145f1 100644
--- a/tests/auto/language/testdata/multiplexed-exports.qbs
+++ b/tests/auto/language/testdata/multiplexed-exports.qbs
@@ -6,7 +6,7 @@ Project {
property string includeDir: qbs.buildVariant === "debug" ? "/d" : "/r"
Export {
Depends { name: "cpp" }
- cpp.includePaths: product.includeDir
+ cpp.includePaths: exportingProduct.includeDir
}
}
Product {
diff --git a/tests/auto/language/testdata/probes-and-multiplexing.qbs b/tests/auto/language/testdata/probes-and-multiplexing.qbs
new file mode 100644
index 000000000..38de08aee
--- /dev/null
+++ b/tests/auto/language/testdata/probes-and-multiplexing.qbs
@@ -0,0 +1,15 @@
+Product {
+ multiplexByQbsProperties: "architectures"
+ qbs.architectures: ["x86", "x86_64", "arm"]
+ property string archFromProbe: theProbe.archOut
+ Probe {
+ id: theProbe
+ property string archIn: qbs.architecture
+ property string archOut
+ configure: { archOut = archIn; }
+ }
+ Group {
+ name: "theGroup"
+ qbs.sysroot: "/" + theProbe.archOut
+ }
+}
diff --git a/tests/auto/language/testdata/profilevaluesandoverriddenvalues.qbs b/tests/auto/language/testdata/profilevaluesandoverriddenvalues.qbs
index 3aaa0cfa9..a0f189403 100644
--- a/tests/auto/language/testdata/profilevaluesandoverriddenvalues.qbs
+++ b/tests/auto/language/testdata/profilevaluesandoverriddenvalues.qbs
@@ -1,5 +1,5 @@
Project {
- Application {
+ Product {
name: "product1"
property bool dummyProp: {
if (!(dummy.cFlags instanceof Array))
diff --git a/tests/auto/language/testdata/qbs-properties-in-project-condition.qbs b/tests/auto/language/testdata/qbs-properties-in-project-condition.qbs
index 31f648233..9d27df254 100644
--- a/tests/auto/language/testdata/qbs-properties-in-project-condition.qbs
+++ b/tests/auto/language/testdata/qbs-properties-in-project-condition.qbs
@@ -1,5 +1,5 @@
Project {
- condition: qbs.targetOS.contains("whatever")
+ condition: qbs.targetOS.includes("whatever")
Product {
name: "never reached"
diff --git a/tests/auto/language/testdata/rfc1034identifier.qbs b/tests/auto/language/testdata/rfc1034identifier.qbs
index 4d320f899..e52dcc6b2 100644
--- a/tests/auto/language/testdata/rfc1034identifier.qbs
+++ b/tests/auto/language/testdata/rfc1034identifier.qbs
@@ -3,7 +3,7 @@ import qbs.Utilities
CppApplication {
name: Utilities.rfc1034Identifier("this!has@special#characters$uh-oh,Undersc0r3s_Are.Bad")
Properties {
- condition: qbs.targetOS.contains("darwin")
+ condition: qbs.targetOS.includes("darwin")
bundle.infoPlist: { return {"CFBundleIdentifier": "$(PRODUCT_NAME:rfc1034identifier)"}; }
}
}
diff --git a/tests/auto/language/testdata/subdir/exports-mylib.qbs b/tests/auto/language/testdata/subdir/exports-mylib.qbs
index 92f39483a..68ea30384 100644
--- a/tests/auto/language/testdata/subdir/exports-mylib.qbs
+++ b/tests/auto/language/testdata/subdir/exports-mylib.qbs
@@ -8,8 +8,8 @@ StaticLibrary {
Export {
Depends { name: "dummy" }
Depends { name: "mylib2" }
- dummy.defines: [product.definePrefix + product.name.toUpperCase()]
+ dummy.defines: [exportingProduct.definePrefix + exportingProduct.name.toUpperCase()]
dummy.includePaths: ["./lib"]
- dummy.somePath: product.aPath
+ dummy.somePath: exportingProduct.aPath
}
}
diff --git a/tests/auto/language/testdata/subdir2/exports-mylib2.qbs b/tests/auto/language/testdata/subdir2/exports-mylib2.qbs
index ac8b9ebe4..b16b47821 100644
--- a/tests/auto/language/testdata/subdir2/exports-mylib2.qbs
+++ b/tests/auto/language/testdata/subdir2/exports-mylib2.qbs
@@ -5,7 +5,7 @@ StaticLibrary {
property string definePrefix: "USE_"
Export {
Depends { name: "dummy" }
- dummy.defines: [product.definePrefix + product.name.toUpperCase()]
+ dummy.defines: [exportingProduct.definePrefix + exportingProduct.name.toUpperCase()]
dummy.includePaths: ["./lib"]
}
}
diff --git a/tests/auto/language/testdata/suppressed-and-non-suppressed-errors.qbs b/tests/auto/language/testdata/suppressed-and-non-suppressed-errors.qbs
index 4d5219157..684cc67b3 100644
--- a/tests/auto/language/testdata/suppressed-and-non-suppressed-errors.qbs
+++ b/tests/auto/language/testdata/suppressed-and-non-suppressed-errors.qbs
@@ -1,5 +1,5 @@
Project {
- CppApplication {
+ Product {
name: "mysterious creature"
files: ["easter bunny"]
}
diff --git a/tests/auto/language/testdata/throw.qbs b/tests/auto/language/testdata/throw.qbs
new file mode 100644
index 000000000..e9a97efb5
--- /dev/null
+++ b/tests/auto/language/testdata/throw.qbs
@@ -0,0 +1,16 @@
+Project {
+ property string throwType
+ property bool dummy: {
+ if (throwType === "bool")
+ throw true;
+ if (throwType === "int")
+ throw 43;
+ if (throwType === "string")
+ throw "an error";
+ if (throwType === "list")
+ throw ["an", "error"];
+ if (throwType === "object")
+ throw { result: "crash", reason: "overheating" };
+ throw "type missing";
+ }
+}
diff --git a/tests/auto/language/testdata/use-internal-profile.qbs b/tests/auto/language/testdata/use-internal-profile.qbs
new file mode 100644
index 000000000..c2e20ff19
--- /dev/null
+++ b/tests/auto/language/testdata/use-internal-profile.qbs
@@ -0,0 +1,13 @@
+Project {
+ name: "theproject"
+
+ Profile {
+ name: "theprofile"
+ dummy.defines: name
+ }
+
+ Product {
+ name: "theproduct"
+ Depends { name: "dummy" }
+ }
+} \ No newline at end of file
diff --git a/tests/auto/language/tst_language.cpp b/tests/auto/language/tst_language.cpp
index 31aebfa3a..456e8b9d0 100644
--- a/tests/auto/language/tst_language.cpp
+++ b/tests/auto/language/tst_language.cpp
@@ -43,6 +43,7 @@
#include "../shared.h"
+#include <app/shared/logging/consolelogger.h>
#include <language/evaluator.h>
#include <language/filecontext.h>
#include <language/identifiersearch.h>
@@ -52,6 +53,7 @@
#include <language/propertymapinternal.h>
#include <language/scriptengine.h>
#include <language/value.h>
+#include <loader/projectresolver.h>
#include <parser/qmljslexer_p.h>
#include <parser/qmljsparser_p.h>
#include <tools/scripttools.h>
@@ -64,8 +66,7 @@
#include <tools/settings.h>
#include <tools/stlutils.h>
-#include "../shared/logging/consolelogger.h"
-
+#include <QtCore/qjsonobject.h>
#include <QtCore/qprocess.h>
#include <algorithm>
@@ -79,28 +80,43 @@ using namespace qbs;
using namespace qbs::Internal;
static QString testDataDir() {
- return FileInfo::resolvePath(QStringLiteral(SRCDIR),
- QStringLiteral("../../../tests/auto/language/testdata"));
+ return testDataSourceDir(SRCDIR "/testdata");
}
static QString testProject(const char *fileName) {
return testDataDir() + QLatin1Char('/') + QLatin1String(fileName);
}
+class JSSourceValueCreator
+{
+ FileContextPtr m_fileContext;
+ std::vector<std::unique_ptr<QString>> m_strings;
+public:
+ JSSourceValueCreator(const FileContextPtr &fileContext)
+ : m_fileContext(fileContext)
+ {
+ }
+
+ JSSourceValuePtr create(const QString &sourceCode)
+ {
+ JSSourceValuePtr value = JSSourceValue::create();
+ value->setFile(m_fileContext);
+ auto str = std::make_unique<QString>(sourceCode);
+ value->setSourceCode(*str.get());
+ m_strings.push_back(std::move(str));
+ return value;
+ }
+};
+
TestLanguage::TestLanguage(ILogSink *logSink, Settings *settings)
: m_logSink(logSink)
, m_settings(settings)
, m_wildcardsTestDirPath(QDir::tempPath() + QLatin1String("/_wildcards_test_dir_"))
{
- qsrand(QTime::currentTime().msec());
+ m_rand.seed(QTime::currentTime().msec());
qRegisterMetaType<QList<bool> >("QList<bool>");
- defaultParameters.setBuildRoot(m_tempDir.path() + "/buildroot");
- defaultParameters.setPropertyCheckingMode(ErrorHandlingMode::Strict);
- defaultParameters.setSettingsDirectory(m_settings->baseDirectory());
}
-TestLanguage::~TestLanguage()
-{
-}
+TestLanguage::~TestLanguage() = default;
QHash<QString, ResolvedProductPtr> TestLanguage::productsFromProject(ResolvedProjectPtr project)
{
@@ -144,8 +160,7 @@ void TestLanguage::handleInitCleanupDataTags(const char *projectFileName, bool *
*handled = true;
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject(projectFileName));
- project = loader->loadProject(defaultParameters);
+ resolveProject(projectFileName);
QVERIFY(!!project);
} catch (const ErrorInfo &e) {
exceptionCaught = true;
@@ -160,9 +175,31 @@ void TestLanguage::handleInitCleanupDataTags(const char *projectFileName, bool *
}
}
+TopLevelProjectPtr TestLanguage::resolveProject(const char *relProjectFilePath)
+{
+ if (relProjectFilePath)
+ defaultParameters.setProjectFilePath(testProject(relProjectFilePath));
+ defaultParameters.expandBuildConfiguration();
+ ProjectResolver resolver(defaultParameters, m_engine.get(), m_logger);
+ return project = resolver.resolve();
+}
+
void TestLanguage::init()
{
+ // clear caches, otherwise StoredVariantValues may end up being at the same address
+ // as the destroyed value
+ m_engine->reset();
m_logSink->setLogLevel(LoggerInfo);
+ defaultParameters = {};
+ defaultParameters.setBuildRoot(m_tempDir.path() + "/buildroot");
+ defaultParameters.setPropertyCheckingMode(ErrorHandlingMode::Strict);
+ defaultParameters.setSettingsDirectory(m_settings->baseDirectory());
+ defaultParameters.setTopLevelProfile(profileName());
+ defaultParameters.setMaxJobCount(1);
+ defaultParameters.setConfigurationName("default");
+ defaultParameters.setEnvironment(QProcessEnvironment::systemEnvironment());
+ defaultParameters.setSearchPaths({SRCDIR "/../../../share/qbs"});
+
QVERIFY(m_tempDir.isValid());
}
@@ -177,28 +214,16 @@ void TestLanguage::init()
void TestLanguage::initTestCase()
{
m_logger = Logger(m_logSink);
- m_engine = ScriptEngine::create(m_logger, EvalContext::PropertyEvaluation, this);
- loader = new Loader(m_engine, m_logger);
- loader->setSearchPaths(QStringList()
- << QStringLiteral(SRCDIR "/../../../share/qbs"));
- defaultParameters.setTopLevelProfile(profileName());
- defaultParameters.setConfigurationName("default");
- defaultParameters.expandBuildConfiguration();
- defaultParameters.setEnvironment(QProcessEnvironment::systemEnvironment());
- QVERIFY(QFileInfo(m_wildcardsTestDirPath).isAbsolute());
-}
+ m_engine = ScriptEngine::create(m_logger, EvalContext::PropertyEvaluation);
-void TestLanguage::cleanupTestCase()
-{
- delete loader;
+ QVERIFY(QFileInfo(m_wildcardsTestDirPath).isAbsolute());
}
void TestLanguage::additionalProductTypes()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("additional-product-types.qbs"));
- project = loader->loadProject(defaultParameters);
+ resolveProject("additional-product-types.qbs");
QVERIFY(!!project);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
const ResolvedProductConstPtr product = products.value("p");
@@ -219,8 +244,7 @@ void TestLanguage::baseProperty()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("baseproperty.qbs"));
- project = loader->loadProject(defaultParameters);
+ resolveProject("baseproperty.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
ResolvedProductPtr product = products.value("product1");
@@ -237,10 +261,8 @@ void TestLanguage::baseProperty()
void TestLanguage::baseValidation()
{
- qbs::SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("base-validate/base-validate.qbs"));
try {
- project = loader->loadProject(params);
+ resolveProject("base-validate/base-validate.qbs");
QVERIFY2(false, "exception expected");
} catch (const qbs::ErrorInfo &e) {
QVERIFY2(e.toString().contains("Parent succeeded, child failed."),
@@ -250,11 +272,9 @@ void TestLanguage::baseValidation()
void TestLanguage::brokenDependencyCycle()
{
- qbs::SetupProjectParameters params = defaultParameters;
QFETCH(QString, projectFileName);
- params.setProjectFilePath(testProject(qPrintable(projectFileName)));
try {
- project = loader->loadProject(params);
+ resolveProject(qPrintable(projectFileName));
} catch (const qbs::ErrorInfo &e) {
QVERIFY2(false, qPrintable(e.toString()));
}
@@ -271,12 +291,10 @@ 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);
+ defaultParameters.setOverriddenValues(overriddenValues);
+ resolveProject("buildconfigstringlistsyntax.qbs");
QVERIFY(!!project);
QCOMPARE(project->projectProperties().value("someStrings").toStringList(),
QStringList() << "foo" << "bar" << "baz");
@@ -291,9 +309,7 @@ void TestLanguage::builtinFunctionInSearchPathsProperty()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters parameters = defaultParameters;
- parameters.setProjectFilePath(testProject("builtinFunctionInSearchPathsProperty.qbs"));
- QVERIFY(!!loader->loadProject(parameters));
+ QVERIFY(resolveProject("builtinFunctionInSearchPathsProperty.qbs"));
} catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
@@ -305,11 +321,12 @@ void TestLanguage::chainedProbes()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters parameters = defaultParameters;
- parameters.setProjectFilePath(testProject("chained-probes/chained-probes.qbs"));
- const TopLevelProjectConstPtr project = loader->loadProject(parameters);
+ resolveProject("chained-probes/chained-probes.qbs");
QVERIFY(!!project);
QCOMPARE(project->products.size(), size_t(1));
+ const QString prop1Val = project->products.front()->moduleProperties
+ ->moduleProperty("m", "prop1").toString();
+ QCOMPARE(prop1Val, QLatin1String("probe1Val"));
const QString prop2Val = project->products.front()->moduleProperties
->moduleProperty("m", "prop2").toString();
QCOMPARE(prop2Val, QLatin1String("probe1Valprobe2Val"));
@@ -325,9 +342,7 @@ void TestLanguage::versionCompare()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters parameters = defaultParameters;
- parameters.setProjectFilePath(testProject("versionCompare.qbs"));
- QVERIFY(!!loader->loadProject(parameters));
+ QVERIFY(resolveProject("versionCompare.qbs"));
} catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
@@ -339,8 +354,7 @@ void TestLanguage::canonicalArchitecture()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("canonicalArchitecture.qbs"));
- project = loader->loadProject(defaultParameters);
+ resolveProject("canonicalArchitecture.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
ResolvedProductPtr product = products.value(QStringLiteral("x86"));
@@ -356,8 +370,7 @@ void TestLanguage::rfc1034Identifier()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("rfc1034identifier.qbs"));
- project = loader->loadProject(defaultParameters);
+ resolveProject("rfc1034identifier.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
ResolvedProductPtr product = products.value(QStringLiteral("this-has-special-characters-"
@@ -370,17 +383,47 @@ void TestLanguage::rfc1034Identifier()
QCOMPARE(exceptionCaught, false);
}
+void TestLanguage::throwThings_data()
+{
+ QTest::addColumn<QString>("type");
+ QTest::addColumn<QString>("result");
+ QTest::addRow("bool") << "bool" << "true";
+ QTest::addRow("int") << "int" << "43";
+ QTest::addRow("string") << "string" << "an error";
+ QTest::addRow("list") << "list" << R"([
+ "an",
+ "error"
+])";
+ QTest::addRow("object") << "object" << R"({
+ "reason": "overheating",
+ "result": "crash"
+})";
+}
+
+void TestLanguage::throwThings()
+{
+ QFETCH(QString, type);
+ QFETCH(QString, result);
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setOverriddenValues({{"project.throwType", type}});
+ resolveProject("throw.qbs");
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ QVERIFY2(e.toString().contains(result), qPrintable(e.toString()));
+ }
+ QVERIFY(exceptionCaught);
+}
+
void TestLanguage::conditionalDepends()
{
bool exceptionCaught = false;
ResolvedProductPtr product;
ResolvedModuleConstPtr dependency;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("conditionaldepends.qbs"));
- params.setOverriddenValues({std::make_pair(QString("products."
+ defaultParameters.setOverriddenValues({std::make_pair(QString("products."
"multilevel_module_props_overridden.dummy3.loadDummy"), true)});
- project = loader->loadProject(params);
+ resolveProject("conditionaldepends.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
@@ -479,17 +522,38 @@ void TestLanguage::conditionalDepends()
QCOMPARE(exceptionCaught, false);
}
+void TestLanguage::convertStringList()
+{
+ FileContextPtr fileContext = FileContext::create();
+ fileContext->setFilePath("/dev/null");
+ JSSourceValueCreator sourceValueCreator(fileContext);
+ ItemPool pool;
+ Item *scope = Item::create(&pool, ItemType::Scope);
+ scope->setProperty("x", sourceValueCreator.create("[\"a\", \"b\"]"));
+
+ Evaluator evaluator(m_engine.get());
+ auto variantValue = evaluator.variantValue(scope, "x");
+ // despite we have a stringList prop, we evaluate it as a QVariantList
+ QCOMPARE(variantValue.userType(), QMetaType::Type::QVariantList);
+ // and we have to convert it explicitly
+#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
+ variantValue.convert(QMetaType(QMetaType::QStringList));
+#else
+ variantValue.convert(QMetaType::QStringList);
+#endif
+ QCOMPARE(variantValue.userType(), QMetaType::Type::QStringList);
+ QCOMPARE(variantValue, QStringList({"a", "b"}));
+}
+
void TestLanguage::delayedError()
{
QFETCH(bool, productEnabled);
try {
QFETCH(QString, projectFileName);
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject(projectFileName.toLatin1()));
QVariantMap overriddenValues;
overriddenValues.insert("project.enableProduct", productEnabled);
- params.setOverriddenValues(overriddenValues);
- project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues(overriddenValues);
+ resolveProject(projectFileName.toLatin1());
QCOMPARE(productEnabled, false);
QVERIFY(!!project);
QCOMPARE(project->products.size(), size_t(1));
@@ -521,8 +585,6 @@ void TestLanguage::dependencyOnAllProfiles()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("dependencyOnAllProfiles.qbs"));
TemporaryProfile p1("p1", m_settings);
p1.p.setValue("qbs.architecture", "arch1");
TemporaryProfile p2("p2", m_settings);
@@ -530,14 +592,14 @@ void TestLanguage::dependencyOnAllProfiles()
QVariantMap overriddenValues;
overriddenValues.insert("project.profile1", "p1");
overriddenValues.insert("project.profile2", "p2");
- params.setOverriddenValues(overriddenValues);
- project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues(overriddenValues);
+ resolveProject("dependencyOnAllProfiles.qbs");
QVERIFY(!!project);
QCOMPARE(project->products.size(), size_t(3));
const ResolvedProductConstPtr mainProduct = productsFromProject(project).value("main");
QVERIFY(!!mainProduct);
QCOMPARE(mainProduct->dependencies.size(), size_t { 2 });
- for (const ResolvedProductConstPtr &p : mainProduct->dependencies) {
+ for (const ResolvedProductPtr &p : mainProduct->dependencies) {
QCOMPARE(p->name, QLatin1String("dep"));
QVERIFY(p->profile() == "p1" || p->profile() == "p2");
}
@@ -552,9 +614,7 @@ void TestLanguage::derivedSubProject()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("derived-sub-project/project.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(params);
+ resolveProject("derived-sub-project/project.qbs");
QVERIFY(!!project);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
@@ -569,9 +629,7 @@ void TestLanguage::disabledSubProject()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("disabled-subproject.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(params);
+ resolveProject("disabled-subproject.qbs");
QVERIFY(!!project);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 0);
@@ -600,16 +658,14 @@ void TestLanguage::dottedNames()
{
QFETCH(bool, expectSuccess);
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("dotted-names/dotted-names.qbs"));
QFETCH(bool, useProduct);
QFETCH(bool, useModule);
const QVariantMap overridden{
std::make_pair("projects.theProject.includeDottedProduct", useProduct),
std::make_pair("projects.theProject.includeDottedModule", useModule)
};
- params.setOverriddenValues(overridden);
- TopLevelProjectPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues(overridden);
+ resolveProject("dotted-names/dotted-names.qbs");
QVERIFY(expectSuccess);
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
@@ -626,13 +682,44 @@ void TestLanguage::dottedNames()
}
}
+void TestLanguage::duplicateMultiplexValues_data()
+{
+ QTest::addColumn<bool>("dummy");
+ QTest::newRow("duplicate-multiplex-value") << true;
+ QTest::newRow("duplicate-multiplex-value2") << true;
+}
+
+void TestLanguage::duplicateMultiplexValues()
+{
+ bool exceptionCaught = false;
+ try {
+ resolveProject(qPrintable(QString::fromLocal8Bit(QTest::currentDataTag())
+ + QLatin1String(".qbs")));
+ QVERIFY(project);
+ const std::vector<ResolvedProductPtr> products = project->allProducts();
+ QCOMPARE(products.size(), 2);
+ bool x86 = false;
+ bool arm = false;
+ for (const ResolvedProductPtr &p : products) {
+ if (p->moduleProperties->moduleProperty("qbs", "architecture").toString() == "x86")
+ x86 = true;
+ else if (p->moduleProperties->moduleProperty("qbs", "architecture").toString() == "arm")
+ arm = true;
+ }
+ QVERIFY(x86);
+ QVERIFY(arm);
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QVERIFY(!exceptionCaught);
+}
+
void TestLanguage::emptyJsFile()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("empty-js-file.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(params);
+ resolveProject("empty-js-file.qbs");
QVERIFY(!!project);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
@@ -647,14 +734,12 @@ void TestLanguage::enumerateProjectProperties()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("enum-project-props.qbs"));
- auto project = loader->loadProject(params);
+ resolveProject("enum-project-props.qbs");
QVERIFY(!!project);
auto products = productsFromProject(project);
QCOMPARE(products.size(), 1);
auto product = products.values().front();
- auto files = product->groups.front()->allFiles();
+ auto files = product->groups.front()->files;
QCOMPARE(product->groups.size(), size_t(1));
QCOMPARE(files.size(), size_t(1));
auto fileName = FileInfo::fileName(files.front()->absoluteFilePath);
@@ -672,7 +757,7 @@ void TestLanguage::evalErrorInNonPresentModule_data()
QTest::addColumn<QString>("errorMessage");
QTest::newRow("module required")
- << true << "broken.qbs:4:5 Element at index 0 of list property 'broken' "
+ << true << "broken.qbs:2:5 Element at index 0 of list property 'broken' "
"does not have string type";
QTest::newRow("module not required") << false << QString();
}
@@ -682,19 +767,16 @@ void TestLanguage::evalErrorInNonPresentModule()
QFETCH(bool, moduleRequired);
QFETCH(QString, errorMessage);
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("eval-error-in-non-present-module.qbs"));
QVariantMap overridden{std::make_pair("products.p.moduleRequired", moduleRequired)};
- params.setOverriddenValues(overridden);
- TopLevelProjectPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues(overridden);
+ resolveProject("eval-error-in-non-present-module.qbs");
QVERIFY(errorMessage.isEmpty());
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
const ResolvedProductPtr product = products.value("p");
QVERIFY(!!product);
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
QVERIFY(!errorMessage.isEmpty());
QVERIFY2(e.toString().contains(errorMessage), qPrintable(e.toString()));
}
@@ -704,14 +786,12 @@ 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("modules.lower.prop1", prop1Value);
- params.setOverriddenValues(overridden);
- TopLevelProjectPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues(overridden);
+ resolveProject("defaultvalue/egon.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 2);
@@ -725,8 +805,7 @@ void TestLanguage::defaultValue()
propertyValue = product->moduleProperties->property(propertyName);
QFETCH(QVariant, expectedListPropValue);
QCOMPARE(propertyValue.toStringList(), expectedListPropValue.toStringList());
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -754,17 +833,14 @@ void TestLanguage::environmentVariable()
try {
// Create new environment:
const QString varName = QStringLiteral("PRODUCT_NAME");
- const QString productName = QLatin1String("MyApp") + QString::number(qrand());
+ const QString productName = QLatin1String("MyApp") + QString::number(m_rand.generate());
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
+ resolveProject("environmentvariable.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
@@ -781,9 +857,7 @@ void TestLanguage::errorInDisabledProduct()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("error-in-disabled-product.qbs"));
- auto project = loader->loadProject(params);
+ resolveProject("error-in-disabled-product.qbs");
QVERIFY(!!project);
auto products = productsFromProject(project);
QCOMPARE(products.size(), 5);
@@ -807,11 +881,11 @@ void TestLanguage::erroneousFiles_data()
QTest::newRow("importloop1")
<< "Loop detected when importing";
QTest::newRow("nonexistentouter")
- << "Can't find variable: outer";
+ << "'outer' is not defined";
QTest::newRow("invalid_file")
<< "does not exist";
QTest::newRow("invalid-parameter-rhs")
- << "ReferenceError: Can't find variable: access";
+ << "'access' is not defined";
QTest::newRow("invalid-parameter-type")
<< "Value assigned to property 'stringParameter' does not have type 'string'.";
QTest::newRow("invalid_property_type")
@@ -828,6 +902,8 @@ void TestLanguage::erroneousFiles_data()
<< "Cyclic dependencies detected.";
QTest::newRow("dependency_cycle3")
<< "Cyclic dependencies detected.";
+ QTest::newRow("dependency_cycle3a")
+ << "Cyclic dependencies detected.";
QTest::newRow("dependency_cycle4")
<< "Cyclic dependencies detected.";
QTest::newRow("references_cycle")
@@ -876,30 +952,24 @@ void TestLanguage::erroneousFiles_data()
QTest::newRow("wrongQbsVersionFormat")
<< "The value '.*' of Project.minimumQbsVersion is not a valid version string.";
QTest::newRow("properties-item-with-invalid-condition")
- << "properties-item-with-invalid-condition.qbs:4:19.*TypeError: Result of expression "
- "'cpp.nonexistingproperty'";
+ << "properties-item-with-invalid-condition.qbs:4:19.*"
+ "cannot read property 'includes' of undefined";
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'";
QTest::newRow("missing-colon")
- << "Invalid item 'cpp.dynamicLibraries'. Did you mean to set a module property?";
+ << "Invalid item 'dummy.cxxFlags'. Did you mean to set a module property?";
QTest::newRow("syntax-error-in-probe")
- << "syntax-error-in-probe.qbs:4:20.*ReferenceError";
+ << "syntax-error-in-probe.qbs:4:20.*'fngkgsdjfgklkf' is not defined";
QTest::newRow("wrong-toplevel-item")
<< "wrong-toplevel-item.qbs:1:1.*The top-level item must be of type 'Project' or "
"'Product', but it is of type 'Artifact'.";
QTest::newRow("conflicting-module-instances")
<< "There is more than one equally prioritized candidate for module "
"'conflicting-instances'.";
- QTest::newRow("module-depends-on-product")
- << "module-with-product-dependency.qbs:2:5.*Modules cannot depend on products.";
QTest::newRow("overwrite-inherited-readonly-property")
<< "overwrite-inherited-readonly-property.qbs"
":2:21.*Cannot set read-only property 'readOnlyString'.";
@@ -916,25 +986,37 @@ void TestLanguage::erroneousFiles_data()
<< "module-with-invalid-original.qbs:2:24.*The special value 'original' cannot be used "
"on the right-hand side of a property declaration.";
QTest::newRow("original-in-export-item")
- << "original-in-export-item.qbs:7:32.*The special value 'original' cannot be used "
+ << "original-in-export-item.qbs:5:32.*The special value 'original' cannot be used "
"on the right-hand side of a property declaration.";
QTest::newRow("original-in-export-item2")
- << "original-in-export-item2.qbs:6:9.*Item 'x.y' is not declared. Did you forget "
+ << "original-in-export-item2.qbs:4:9.*Item 'x.y' is not declared. Did you forget "
"to add a Depends item";
QTest::newRow("original-in-export-item3")
<< "original-in-export-item3.qbs:6:9.*Item 'x.y' is not declared. Did you forget "
"to add a Depends item";
QTest::newRow("mismatching-multiplex-dependency")
- << "mismatching-multiplex-dependency.qbs:7:5.*Dependency from product "
- "'b \\{\"architecture\":\"mips\"\\}' to product 'a \\{\"architecture\":\"mips\"\\}'"
- " not fulfilled.";
- QTest::newRow("duplicate-multiplex-value")
- << "duplicate-multiplex-value.qbs:3:1.*Duplicate entry 'x86' in qbs.architectures.";
- QTest::newRow("duplicate-multiplex-value2")
- << "duplicate-multiplex-value2.qbs:3:1.*Duplicate entry 'architecture' in "
- "Product.multiplexByQbsProperties.";
+ << "mismatching-multiplex-dependency.qbs:9:9.*Dependency from product "
+ "'b \\{\"architecture\":\"mips\"\\}' to product 'a'"
+ " not fulfilled. There are no eligible multiplex candidates.";
+ QTest::newRow("ambiguous-multiplex-dependency")
+ << "ambiguous-multiplex-dependency.qbs:10:9.*Dependency from product 'b "
+ "\\{\"architecture\":\"x86\"\\}' to product 'a' is ambiguous. Eligible multiplex "
+ "candidates: a \\{\"architecture\":\"x86\",\"buildVariant\":\"debug\"\\}, "
+ "a \\{\"architecture\":\"x86\",\"buildVariant\":\"release\"\\}.";
+ QTest::newRow("dependency-profile-mismatch")
+ << "dependency-profile-mismatch.qbs:10:5.*Product 'main' depends on 'dep', "
+ "which does not exist for the requested profile 'profile47'.";
+ QTest::newRow("dependency-profile-mismatch-2")
+ << "dependency-profile-mismatch-2.qbs:15:9 Dependency from product 'main' to "
+ "product 'dep' not fulfilled. There are no eligible multiplex candidates.";
QTest::newRow("invalid-references")
<< "invalid-references.qbs:2:17.*Cannot open '.*nosuchproject.qbs'";
+ QTest::newRow("missing-js-file")
+ << "missing-js-file-module.qbs.*Cannot open '.*javascriptfile.js'";
+ QTest::newRow("frozen-object") << "'key' is read-only";
+ QTest::newRow("frozen-object-list") << "object is not extensible";
+ QTest::newRow("module-property-binding-in-project")
+ << "Module properties cannot be set in Project items";
}
void TestLanguage::erroneousFiles()
@@ -942,10 +1024,10 @@ void TestLanguage::erroneousFiles()
QFETCH(QString, errorMessage);
QString fileName = QString::fromLocal8Bit(QTest::currentDataTag()) + QLatin1String(".qbs");
try {
- defaultParameters.setProjectFilePath(testProject("/erroneous/") + fileName);
- loader->loadProject(defaultParameters);
+ resolveProject(qPrintable("/erroneous/" + fileName));
} catch (const ErrorInfo &e) {
- if (!e.toString().contains(QRegExp(errorMessage))) {
+ const QRegularExpression reg(errorMessage, QRegularExpression::DotMatchesEverythingOption);
+ if (!e.toString().contains(reg)) {
qDebug() << "Message: " << e.toString();
qDebug() << "Expected: " << errorMessage;
QFAIL("Unexpected error message.");
@@ -961,8 +1043,7 @@ void TestLanguage::exports()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("exports.qbs"));
- TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("exports.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 22);
@@ -1007,6 +1088,7 @@ void TestLanguage::exports()
product = products.value("B");
QVERIFY(!!product);
QVERIFY(product->dependencies.empty());
+ QCOMPARE(product->exportedModule.productDependencies, std::vector<QString>{"C"});
product = products.value("C");
QVERIFY(!!product);
QVERIFY(product->dependencies.empty());
@@ -1051,7 +1133,7 @@ void TestLanguage::exports()
propertyName = QStringList() << "dummy" << "defines";
propertyValue = product->moduleProperties->property(propertyName);
QCOMPARE(propertyValue.toStringList(),
- QStringList() << "LIBA" << "LIBB" << "LIBC" << "LIBD");
+ QStringList() << "LIBD" << "LIBC" << "LIBA" << "LIBB");
propertyName = QStringList() << "dummy" << "productName";
propertyValue = product->moduleProperties->property(propertyName);
QCOMPARE(propertyValue.toString(), QString("libE"));
@@ -1059,7 +1141,7 @@ void TestLanguage::exports()
product = products.value("depender");
QVERIFY(!!product);
QCOMPARE(product->modules.size(), size_t(2));
- for (const ResolvedModuleConstPtr &m : product->modules) {
+ for (const ResolvedModulePtr &m : product->modules) {
QVERIFY2(m->name == QString("qbs") || m->name == QString("dependency"),
qPrintable(m->name));
}
@@ -1070,13 +1152,12 @@ void TestLanguage::exports()
product = products.value("broken_cycle3");
QVERIFY(!!product);
QCOMPARE(product->modules.size(), size_t(3));
- for (const ResolvedModuleConstPtr &m : product->modules) {
+ for (const ResolvedModulePtr &m : product->modules) {
QVERIFY2(m->name == QString("qbs") || m->name == QString("broken_cycle1")
|| m->name == QString("broken_cycle2"),
qPrintable(m->name));
}
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -1087,8 +1168,7 @@ void TestLanguage::fileContextProperties()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("filecontextproperties.qbs"));
- project = loader->loadProject(defaultParameters);
+ resolveProject("filecontextproperties.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
ResolvedProductPtr product = products.value("product1");
@@ -1134,14 +1214,12 @@ void TestLanguage::fileInProductAndModule()
QFETCH(bool, addFileToProduct);
QFETCH(bool, successExpected);
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("file-in-product-and-module.qbs"));
- params.setOverriddenValues(QVariantMap{
+ defaultParameters.setOverriddenValues(QVariantMap{
std::make_pair("modules.module_with_file.file1IsTarget", file1IsTarget),
std::make_pair("modules.module_with_file.file2IsTarget", file2IsTarget),
std::make_pair("products.p.addFileToProduct", addFileToProduct),
});
- project = loader->loadProject(params);
+ resolveProject("file-in-product-and-module.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
@@ -1156,16 +1234,19 @@ void TestLanguage::getNativeSetting()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("getNativeSetting.qbs"));
- project = loader->loadProject(defaultParameters);
+ resolveProject("getNativeSetting.qbs");
QString expectedTargetName;
- if (HostOsInfo::isMacosHost())
- expectedTargetName = QStringLiteral("Mac OS X");
- else if (HostOsInfo::isWindowsHost())
+ if (HostOsInfo::isMacosHost()) {
+ if (HostOsInfo::hostOsVersion() >= qbs::Version(11))
+ expectedTargetName = QStringLiteral("macOS");
+ else
+ expectedTargetName = QStringLiteral("Mac OS X");
+ } else if (HostOsInfo::isWindowsHost()) {
expectedTargetName = QStringLiteral("Windows");
- else
+ } else {
expectedTargetName = QStringLiteral("Unix");
+ }
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products;
@@ -1228,8 +1309,7 @@ void TestLanguage::groupName()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("groupname.qbs"));
- TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("groupname.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 2);
@@ -1256,8 +1336,7 @@ void TestLanguage::groupName()
group = product->groups.at(2);
QVERIFY(!!group);
QCOMPARE(group->name, QString("Group 2"));
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -1267,8 +1346,7 @@ void TestLanguage::groupName()
void TestLanguage::homeDirectory()
{
try {
- defaultParameters.setProjectFilePath(testProject("homeDirectory.qbs"));
- ResolvedProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("homeDirectory.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
@@ -1293,8 +1371,7 @@ void TestLanguage::homeDirectory()
FileInfo::resolvePath(product->sourceDirectory, QStringLiteral("a/~/bb")));
QCOMPARE(product->productProperties.value("user").toString(),
FileInfo::resolvePath(product->sourceDirectory, QStringLiteral("~foo/bar")));
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
qDebug() << e.toString();
}
}
@@ -1373,8 +1450,7 @@ void TestLanguage::idUsage()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("idusage.qbs"));
- TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("idusage.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 5);
@@ -1398,8 +1474,7 @@ void TestLanguage::idUsage()
QVERIFY(!!product5);
QCOMPARE(product5->moduleProperties->moduleProperty("deepdummy.deep.moat", "zort")
.toString(), QString("zort in dummy"));
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -1410,10 +1485,8 @@ void TestLanguage::idUniqueness()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("id-uniqueness.qbs"));
- loader->loadProject(defaultParameters);
- }
- catch (const ErrorInfo &e) {
+ resolveProject("id-uniqueness.qbs");
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
const QList<ErrorItem> items = e.items();
QCOMPARE(items.size(), 3);
@@ -1428,15 +1501,13 @@ void TestLanguage::importCollection()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("import-collection/project.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("import-collection/project.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
const ResolvedProductConstPtr product = products.value("da product");
QCOMPARE(product->productProperties.value("targetName").toString(),
QLatin1String("C1f1C1f2C2f1C2f2"));
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -1455,19 +1526,15 @@ void TestLanguage::inheritedPropertiesItems()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
QFETCH(QString, buildVariant);
QFETCH(QString, productName);
- params.setProjectFilePath
- (testProject("inherited-properties-items/inherited-properties-items.qbs"));
- params.setOverriddenValues(QVariantMap{std::make_pair("qbs.buildVariant", buildVariant)});
- TopLevelProjectPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues(QVariantMap{std::make_pair("qbs.buildVariant", buildVariant)});
+ resolveProject("inherited-properties-items/inherited-properties-items.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
QVERIFY(!!products.value(productName));
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -1478,13 +1545,11 @@ void TestLanguage::invalidBindingInDisabledItem()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("invalidBindingInDisabledItem.qbs"));
- TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("invalidBindingInDisabledItem.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 2);
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -1498,13 +1563,10 @@ void TestLanguage::invalidOverrides()
const bool successExpected = expectedErrorMessage.isEmpty();
bool exceptionCaught = false;
try {
- qbs::SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("invalid-overrides.qbs"));
- params.setOverriddenValues(QVariantMap{std::make_pair(key, true)});
- const TopLevelProjectPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues(QVariantMap{std::make_pair(key, true)});
+ resolveProject("invalid-overrides.qbs");
QVERIFY(!!project);
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
if (successExpected)
qDebug() << e.toString();
@@ -1548,31 +1610,32 @@ void TestLanguage::invalidOverrides_data()
<< "products.MyOtherProduct.cpp.useRPaths" << QString();
}
-class JSSourceValueCreator
+void TestLanguage::invalidPropOnNonRequiredModule_data()
{
- FileContextPtr m_fileContext;
- QList<QString *> m_strings;
-public:
- JSSourceValueCreator(const FileContextPtr &fileContext)
- : m_fileContext(fileContext)
- {
- }
+ QTest::addColumn<bool>("useExistingModule");
+ QTest::addColumn<bool>("errorExpected");
- ~JSSourceValueCreator()
- {
- qDeleteAll(m_strings);
- }
+ QTest::newRow("existing module") << true << true;
+ QTest::newRow("non-existing module") << false << false;
+}
- JSSourceValuePtr create(const QString &sourceCode)
- {
- JSSourceValuePtr value = JSSourceValue::create();
- value->setFile(m_fileContext);
- const auto str = new QString(sourceCode);
- m_strings.push_back(str);
- value->setSourceCode(QStringRef(str));
- return value;
+void TestLanguage::invalidPropOnNonRequiredModule()
+{
+ QFETCH(bool, useExistingModule);
+ QFETCH(bool, errorExpected);
+
+ try {
+ defaultParameters.setOverriddenValues(
+ {std::make_pair("project.useExistingModule", useExistingModule)});
+ resolveProject("invalid-prop-on-non-required-module");
+ QVERIFY(!errorExpected);
+ } catch (const ErrorInfo &e) {
+ const QString errorString = e.toString();
+ QVERIFY2(errorExpected, qPrintable(errorString));
+ QVERIFY2(errorString.contains("Property 'nosuchprop' is not declared"),
+ qPrintable(errorString));
}
-};
+}
void TestLanguage::itemPrototype()
{
@@ -1588,13 +1651,14 @@ void TestLanguage::itemPrototype()
item->setProperty("y", sourceValueCreator.create("x + 1"));
item->setProperty("z", sourceValueCreator.create("2"));
- Evaluator evaluator(m_engine);
- 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);
+ Evaluator evaluator(m_engine.get());
+ JSContext * const ctx = m_engine->context();
+ QCOMPARE(getJsVariant(ctx, evaluator.property(proto, "x")).toInt(), 1);
+ QCOMPARE(getJsVariant(ctx, evaluator.property(proto, "y")).toInt(), 1);
+ QVERIFY(JS_IsUndefined(evaluator.property(proto, "z")));
+ QCOMPARE(getJsVariant(ctx, evaluator.property(item, "x")).toInt(), 1);
+ QCOMPARE(getJsVariant(ctx, evaluator.property(item, "y")).toInt(), 2);
+ QCOMPARE(getJsVariant(ctx, evaluator.property(item, "z")).toInt(), 2);
}
void TestLanguage::itemScope()
@@ -1612,11 +1676,12 @@ void TestLanguage::itemScope()
item->setScope(scope2);
item->setProperty("z", sourceValueCreator.create("x + y"));
- Evaluator evaluator(m_engine);
- 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);
+ Evaluator evaluator(m_engine.get());
+ JSContext * const ctx = m_engine->context();
+ QCOMPARE(getJsVariant(ctx, evaluator.property(scope1, "x")).toInt(), 1);
+ QCOMPARE(getJsVariant(ctx, evaluator.property(scope2, "y")).toInt(), 2);
+ QVERIFY(JS_IsUndefined(evaluator.property(scope2, "x")));
+ QCOMPARE(getJsVariant(ctx, evaluator.property(item, "z")).toInt(), 3);
}
void TestLanguage::jsExtensions()
@@ -1626,10 +1691,10 @@ void TestLanguage::jsExtensions()
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)));
+ m_engine->evaluate(JsValueOwner::Caller, code, file.fileName(), 1);
+ if (JsException ex = m_engine->checkAndClearException({})) {
+ qDebug() << ex.stackTrace();
+ QFAIL(qPrintable(ex.message()));
}
}
@@ -1648,35 +1713,45 @@ void TestLanguage::jsImportUsedInMultipleScopes()
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("jsimportsinmultiplescopes.qbs"));
- params.setOverriddenValues({std::make_pair(QStringLiteral("qbs.buildVariant"),
- buildVariant)});
- params.expandBuildConfiguration();
- TopLevelProjectPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues({std::make_pair(QStringLiteral("qbs.buildVariant"),
+ buildVariant)});
+ resolveProject("jsimportsinmultiplescopes.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
ResolvedProductPtr product = products.values().front();
QVERIFY(!!product);
QCOMPARE(product->name, expectedProductName);
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
QVERIFY(!exceptionCaught);
}
+void TestLanguage::localProfileAsTopLevelProfile()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setTopLevelProfile("test-profile");
+ resolveProject("local-profile-as-top-level-profile.qbs");
+ QVERIFY(!!project);
+ QCOMPARE(int(project->products.size()), 1);
+ const PropertyMapConstPtr &props = project->products.front()->moduleProperties;
+ QCOMPARE(props->qbsPropertyValue("architecture"), "arm");
+ QCOMPARE(props->qbsPropertyValue("targetPlatform"), "macos");
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
void TestLanguage::moduleMergingVariantValues()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath
- (testProject("module-merging-variant-values/module-merging-variant-values.qbs"));
- params.expandBuildConfiguration();
- const TopLevelProjectPtr project = loader->loadProject(params);
+ resolveProject("module-merging-variant-values/module-merging-variant-values.qbs");
QVERIFY(!!project);
QCOMPARE(int(project->products.size()), 2);
} catch (const ErrorInfo &e) {
@@ -1686,6 +1761,185 @@ void TestLanguage::moduleMergingVariantValues()
QCOMPARE(exceptionCaught, false);
}
+void TestLanguage::moduleNameCollisions_data()
+{
+ QTest::addColumn<QString>("projectFile");
+ QTest::addColumn<bool>("collisionExpected");
+
+ QTest::newRow("simple collision (one order)") << "simple-collision1.qbs" << true;
+ QTest::newRow("simple collision (other order)") << "simple-collision2.qbs" << true;
+ QTest::newRow("collision with more components") << "complex-collision.qbs" << true;
+ QTest::newRow("no collision (same length)") << "no-collision1.qbs" << false;
+ QTest::newRow("no collision (different length)") << "no-collision2.qbs" << false;
+}
+
+void TestLanguage::moduleNameCollisions()
+{
+ QFETCH(QString, projectFile);
+ QFETCH(bool, collisionExpected);
+
+ try {
+ const QString compositeProjectFilePath = QString("module-name-collisions/") + projectFile;
+ QVERIFY(resolveProject(qPrintable(compositeProjectFilePath)));
+ QVERIFY(!collisionExpected);
+ } catch (const ErrorInfo &e) {
+ const QString errorString = e.toString();
+ QVERIFY2(collisionExpected, qPrintable(errorString));
+ QVERIFY2(errorString.contains("not allowed"), qPrintable(errorString));
+ }
+}
+
+void TestLanguage::moduleParameters_data()
+{
+ QTest::addColumn<QVariantMap>("inputProperties");
+ QTest::addColumn<QVariantMap>("expectedModuleParameters");
+ QTest::addColumn<bool>("errorExpected");
+
+ QTest::newRow("no overrides")
+ << QVariantMap{
+ std::make_pair("project.overrideFromModule", "false"),
+ std::make_pair("project.overrideFromExport", "false"),
+ std::make_pair("project.overrideFromProduct", "false")}
+ << QVariantMap{
+ std::make_pair("higher",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromParameters")})}),
+ std::make_pair("dep",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromParameters")})})}
+ << false;
+ QTest::newRow("override from product")
+ << QVariantMap{
+ std::make_pair("project.overrideFromModule", "false"),
+ std::make_pair("project.overrideFromExport", "false"),
+ std::make_pair("project.overrideFromProduct", "true")}
+ << QVariantMap{
+ std::make_pair("higher",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromProductDepends")})}),
+ std::make_pair("dep",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromProductDepends")})})}
+ << false;
+ QTest::newRow("override from export")
+ << QVariantMap{
+ std::make_pair("project.overrideFromModule", "false"),
+ std::make_pair("project.overrideFromExport", "true"),
+ std::make_pair("project.overrideFromProduct", "false")}
+ << QVariantMap{
+ std::make_pair("higher",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromExportDepends")})}),
+ std::make_pair("dep",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromParameters")})})}
+ << false;
+ QTest::newRow("override from export and product")
+ << QVariantMap{
+ std::make_pair("project.overrideFromModule", "false"),
+ std::make_pair("project.overrideFromExport", "true"),
+ std::make_pair("project.overrideFromProduct", "true")}
+ << QVariantMap{
+ std::make_pair("higher",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromProductDepends")})}),
+ std::make_pair("dep",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromProductDepends")})})}
+ << false;
+ QTest::newRow("override from module")
+ << QVariantMap{
+ std::make_pair("project.overrideFromModule", "true"),
+ std::make_pair("project.overrideFromExport", "false"),
+ std::make_pair("project.overrideFromProduct", "false")}
+ << QVariantMap{
+ std::make_pair("higher",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromModuleDepends")})}),
+ std::make_pair("dep",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromParameters")})})}
+ << false;
+ QTest::newRow("override from module and product")
+ << QVariantMap{
+ std::make_pair("project.overrideFromModule", "true"),
+ std::make_pair("project.overrideFromExport", "false"),
+ std::make_pair("project.overrideFromProduct", "true")}
+ << QVariantMap{
+ std::make_pair("higher",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromProductDepends")})}),
+ std::make_pair("dep",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromProductDepends")})})}
+ << false;
+ QTest::newRow("override from module and export")
+ << QVariantMap{
+ std::make_pair("project.overrideFromModule", "true"),
+ std::make_pair("project.overrideFromExport", "true"),
+ std::make_pair("project.overrideFromProduct", "false")}
+ << QVariantMap{
+ std::make_pair("higher",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromExportDepends")})}),
+ std::make_pair("dep",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromParameters")})})}
+ << true;
+ QTest::newRow("override from module, export and product")
+ << QVariantMap{
+ std::make_pair("project.overrideFromModule", "true"),
+ std::make_pair("project.overrideFromExport", "true"),
+ std::make_pair("project.overrideFromProduct", "true")}
+ << QVariantMap{
+ std::make_pair("higher",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromProductDepends")})}),
+ std::make_pair("dep",
+ QVariantMap{std::make_pair("lower",
+ QVariantMap{std::make_pair("param", "fromProductDepends")})})}
+ << false;
+}
+
+void TestLanguage::moduleParameters()
+{
+ QFETCH(QVariantMap, inputProperties);
+ QFETCH(QVariantMap, expectedModuleParameters);
+ QFETCH(bool, errorExpected);
+
+ try {
+ defaultParameters.setOverriddenValues(inputProperties);
+ resolveProject("module-parameters/module-parameters.qbs");
+ QVERIFY(!errorExpected);
+ QVERIFY(project);
+ QCOMPARE(int(project->products.size()), 2);
+ const ResolvedProductPtr mainProduct = productsFromProject(project).value("main");
+ QVERIFY(mainProduct);
+ QCOMPARE(int(mainProduct->moduleParameters.size()), 2);
+ for (auto it = expectedModuleParameters.cbegin(); it != expectedModuleParameters.cend();
+ ++it) {
+ const auto findInProduct = [&](const QString &moduleName) {
+ for (auto it = mainProduct->moduleParameters.cbegin();
+ it != mainProduct->moduleParameters.cend(); ++it) {
+ if (it.key()->name == moduleName)
+ return it.value();
+ }
+ return QVariantMap();
+ };
+ const QVariantMap actual = findInProduct(it.key());
+ const QVariantMap expected = it.value().toMap();
+ const bool same = qVariantMapsEqual(actual, expected);
+ if (!same) {
+ qDebug().noquote() << "---" << expected;
+ qDebug().noquote() << "+++" << actual;
+ }
+ QVERIFY(same);
+ }
+ } catch (const ErrorInfo &e) {
+ QVERIFY2(errorExpected, qPrintable(e.toString()));
+ }
+}
+
void TestLanguage::modulePrioritizationBySearchPath_data()
{
QTest::addColumn<QStringList>("searchPaths");
@@ -1701,12 +1955,10 @@ void TestLanguage::modulePrioritizationBySearchPath()
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("module-prioritization-by-search-path/project.qbs"));
- params.setOverriddenValues({std::make_pair(QStringLiteral("project.qbsSearchPaths"),
- searchPaths)});
- params.expandBuildConfiguration();
- TopLevelProjectPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues(
+ {std::make_pair(QStringLiteral("project.qbsSearchPaths"),
+ searchPaths)});
+ resolveProject("module-prioritization-by-search-path/project.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
@@ -1715,8 +1967,7 @@ void TestLanguage::modulePrioritizationBySearchPath()
const QString actualVariant = product->moduleProperties->moduleProperty
("conflicting-instances", "moduleVariant").toString();
QCOMPARE(actualVariant, expectedVariant);
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -1730,10 +1981,10 @@ void TestLanguage::moduleProperties_data()
QTest::newRow("init") << QString() << QVariant();
QTest::newRow("merge_lists")
<< "defines"
- << QVariant(QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK");
+ << QVariant(QStringList() << "THE_PRODUCT" << "QT_NETWORK" << "QT_GUI" << "QT_CORE");
QTest::newRow("merge_lists_and_values")
<< "defines"
- << QVariant(QStringList() << "THE_PRODUCT" << "QT_CORE" << "QT_GUI" << "QT_NETWORK");
+ << QVariant(QStringList() << "THE_PRODUCT" << "QT_NETWORK" << "QT_GUI" << "QT_CORE");
QTest::newRow("merge_lists_with_duplicates")
<< "cxxFlags"
<< QVariant(QStringList() << "-foo" << "BAR" << "-foo" << "BAZ");
@@ -1782,7 +2033,7 @@ void TestLanguage::modulePropertiesInGroups()
defaultParameters.setProjectFilePath(testProject("modulepropertiesingroups.qbs"));
bool exceptionCaught = false;
try {
- TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject();
QVERIFY(!!project);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
ResolvedProductPtr product = products.value("grouptest");
@@ -1793,7 +2044,7 @@ void TestLanguage::modulePropertiesInGroups()
GroupConstPtr g2;
GroupConstPtr g21;
GroupConstPtr g211;
- for (const GroupConstPtr &g : product->groups) {
+ for (const GroupPtr &g : product->groups) {
if (g->name == "g1")
g1= g;
else if (g->name == "g2")
@@ -1910,7 +2161,7 @@ void TestLanguage::modulePropertiesInGroups()
QCOMPARE(g2Gmod1List1, QStringList() << "gmod1_list1_proto" << "g2");
const auto &g2Gmod1List2 = moduleProperty(g2Props, "gmod.gmod1", "gmod1_list2")
.toStringList();
- QCOMPARE(g2Gmod1List2, QStringList() << "grouptest" << "g2" << "gmod1_list2_proto");
+ QCOMPARE(g2Gmod1List2, QStringList() << "grouptest" << "gmod1_string_proto" << "gmod1_list2_proto");
const int g2P0 = moduleProperty(g2Props, "gmod.gmod1", "p0").toInt();
QCOMPARE(g2P0, 6);
const int g2DepProp = moduleProperty(g2Props, "gmod.gmod1", "depProp").toInt();
@@ -1928,7 +2179,7 @@ void TestLanguage::modulePropertiesInGroups()
QCOMPARE(g21Gmod1List1, QStringList() << "gmod1_list1_proto" << "g2");
const auto &g21Gmod1List2 = moduleProperty(g21Props, "gmod.gmod1", "gmod1_list2")
.toStringList();
- QEXPECT_FAIL(0, "no re-eval when no module props set", Continue);
+ QEXPECT_FAIL(nullptr, "no re-eval when no module props set", Continue);
QCOMPARE(g21Gmod1List2, QStringList() << "grouptest" << "g2.1" << "gmod1_list2_proto");
const int g21P0 = moduleProperty(g21Props, "gmod.gmod1", "p0").toInt();
QCOMPARE(g21P0, 6);
@@ -1938,7 +2189,7 @@ void TestLanguage::modulePropertiesInGroups()
QCOMPARE(g21Gmod2String, QString("g2"));
const auto &g21Gmod2List = moduleProperty(g21Props, "gmod2", "gmod2_list")
.toStringList();
- QEXPECT_FAIL(0, "no re-eval when no module props set", Continue);
+ QEXPECT_FAIL(nullptr, "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");
@@ -1955,11 +2206,11 @@ void TestLanguage::modulePropertiesInGroups()
QCOMPARE(g211DepProp, 2);
const auto &g211Gmod2String
= moduleProperty(g211Props, "gmod2", "gmod2_string").toString();
- QEXPECT_FAIL(0, "re-eval not triggered", Continue);
+ QEXPECT_FAIL(nullptr, "re-eval not triggered", Continue);
QCOMPARE(g211Gmod2String, QString("g2.1.1"));
const auto &g211Gmod2List = moduleProperty(g211Props, "gmod2", "gmod2_list")
.toStringList();
- QEXPECT_FAIL(0, "re-eval not triggered", Continue);
+ QEXPECT_FAIL(nullptr, "re-eval not triggered", Continue);
QCOMPARE(g211Gmod2List, QStringList() << "g2.1.1" << "commonName_in_gmod1"
<< "g2.1.1_gmod4_g2.1.1_gmod3" << "g2.1.1_gmod3" << "gmod2_list_proto");
@@ -1967,7 +2218,7 @@ void TestLanguage::modulePropertiesInGroups()
QVERIFY(!!product);
g1.reset();
g11.reset();
- for (const GroupConstPtr &g : product->groups) {
+ for (const GroupPtr &g : product->groups) {
if (g->name == "g1")
g1= g;
else if (g->name == "g1.1")
@@ -1994,17 +2245,14 @@ void TestLanguage::modulePropertyOverridesPerProduct()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setOverriddenValues({
+ defaultParameters.setOverriddenValues({
std::make_pair("modules.dummy.rpaths", QStringList({"/usr/lib"})),
std::make_pair("modules.dummy.someString", "m"),
std::make_pair("products.b.dummy.someString", "b"),
std::make_pair("products.c.dummy.someString", "c"),
std::make_pair("products.c.dummy.rpaths", QStringList({"/home", "/tmp"}))
});
- params.setProjectFilePath(
- testProject("module-property-overrides-per-product.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(params);
+ resolveProject("module-property-overrides-per-product.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 3);
@@ -2037,8 +2285,7 @@ void TestLanguage::modulePropertyOverridesPerProduct()
QCOMPARE(listPropertyValue(a), productPropertyValue(a));
QCOMPARE(listPropertyValue(b), productPropertyValue(b));
QCOMPARE(listPropertyValue(c), productPropertyValue(c));
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -2050,7 +2297,7 @@ void TestLanguage::moduleScope()
bool exceptionCaught = false;
try {
defaultParameters.setProjectFilePath(testProject("modulescope.qbs"));
- TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject();
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
@@ -2070,13 +2317,30 @@ void TestLanguage::moduleScope()
QCOMPARE(intModuleValue("f"), 2); // overridden
QCOMPARE(intModuleValue("g"), 156); // overridden, dependent on product properties
QCOMPARE(intModuleValue("h"), 158); // overridden, base dependent on product properties
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
QCOMPARE(exceptionCaught, false);
+}
+void TestLanguage::moduleWithProductDependency()
+{
+ bool exceptionCaught = false;
+ try {
+ defaultParameters.setProjectFilePath(testProject("module-depends-on-product.qbs"));
+ resolveProject();
+ QVERIFY(project);
+ QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
+ QCOMPARE(products.size(), 2);
+ ResolvedProductPtr product = products.value("p1");
+ QVERIFY(product);
+ QCOMPARE(int(product->dependencies.size()), 1);
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
}
void TestLanguage::modules_data()
@@ -2135,9 +2399,7 @@ void TestLanguage::multiplexedExports()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("multiplexed-exports.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(params);
+ resolveProject("multiplexed-exports.qbs");
QVERIFY(!!project);
const auto products = project->allProducts();
QCOMPARE(products.size(), size_t(4));
@@ -2171,11 +2433,9 @@ void TestLanguage::multiplexingByProfile()
{
QFETCH(QString, projectFileName);
QFETCH(bool, successExpected);
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testDataDir() + "/multiplexing-by-profile/" + projectFileName);
try {
- params.setDryRun(true);
- const TopLevelProjectPtr project = loader->loadProject(params);
+ defaultParameters.setDryRun(true);
+ resolveProject(qPrintable("/multiplexing-by-profile/" + projectFileName));
QVERIFY(successExpected);
QVERIFY(!!project);
} catch (const ErrorInfo &e) {
@@ -2199,11 +2459,9 @@ void TestLanguage::nonApplicableModulePropertyInProfile()
QFETCH(QString, toolchain);
QFETCH(bool, successExpected);
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("non-applicable-module-property-in-profile.qbs"));
- params.setOverriddenValues(QVariantMap{std::make_pair("project.targetOS", targetOS),
+ defaultParameters.setOverriddenValues({std::make_pair("project.targetOS", targetOS),
std::make_pair("project.toolchain", toolchain)});
- const TopLevelProjectPtr project = loader->loadProject(params);
+ resolveProject("non-applicable-module-property-in-profile.qbs");
QVERIFY(!!project);
QVERIFY(successExpected);
} catch (const ErrorInfo &e) {
@@ -2233,8 +2491,6 @@ 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;
@@ -2242,8 +2498,8 @@ void TestLanguage::nonRequiredProducts()
overriddenValues.insert("projects.subproject.condition", false);
else if (!dependeeEnabled)
overriddenValues.insert("products.dependee.condition", false);
- params.setOverriddenValues(overriddenValues);
- const TopLevelProjectPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues(overriddenValues);
+ resolveProject("non-required-products.qbs");
QVERIFY(!!project);
const auto products = productsFromProject(project);
QCOMPARE(products.size(), 4 + !!subProjectEnabled);
@@ -2262,8 +2518,7 @@ void TestLanguage::nonRequiredProducts()
QVERIFY2(product, name);
QVERIFY2(!product->enabled, name);
}
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -2284,7 +2539,7 @@ void TestLanguage::outerInGroup()
bool exceptionCaught = false;
try {
defaultParameters.setProjectFilePath(testProject("outerInGroup.qbs"));
- TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject();
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
@@ -2305,8 +2560,7 @@ void TestLanguage::outerInGroup()
artifact = group->files.front();
installDir = artifact->properties->qbsPropertyValue("installDir");
QCOMPARE(installDir.toString(), QString("/somewhere/else"));
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -2319,16 +2573,14 @@ void TestLanguage::overriddenPropertiesAndPrototypes()
try {
QFETCH(QString, osName);
QFETCH(QString, backendName);
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("overridden-properties-and-prototypes.qbs"));
- params.setOverriddenValues({std::make_pair("modules.qbs.targetPlatform", osName)});
- TopLevelProjectConstPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues({std::make_pair("modules.qbs.targetPlatform",
+ osName)});
+ resolveProject("overridden-properties-and-prototypes.qbs");
QVERIFY(!!project);
QCOMPARE(project->products.size(), size_t(1));
QCOMPARE(project->products.front()->moduleProperties->moduleProperty(
"multiple_backends", "prop").toString(), backendName);
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -2347,11 +2599,9 @@ void TestLanguage::overriddenVariantProperty()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
const QVariantMap objectValue{std::make_pair("x", 1), std::make_pair("y", 2)};
- params.setOverriddenValues({std::make_pair("products.p.myObject", objectValue)});
- params.setProjectFilePath(testProject("overridden-variant-property.qbs"));
- TopLevelProjectConstPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues({std::make_pair("products.p.myObject", objectValue)});
+ resolveProject("overridden-variant-property.qbs");
QVERIFY(!!project);
QCOMPARE(project->products.size(), size_t(1));
QCOMPARE(project->products.front()->productProperties.value("myObject").toMap(),
@@ -2368,9 +2618,8 @@ void TestLanguage::parameterTypes()
bool exceptionCaught = false;
try {
defaultParameters.setProjectFilePath(testProject("parameter-types.qbs"));
- loader->loadProject(defaultParameters);
- }
- catch (const ErrorInfo &e) {
+ resolveProject();
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -2381,8 +2630,7 @@ void TestLanguage::pathProperties()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("pathproperties.qbs"));
- project = loader->loadProject(defaultParameters);
+ resolveProject("pathproperties.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
ResolvedProductPtr product = products.value("product1");
@@ -2408,6 +2656,35 @@ void TestLanguage::pathProperties()
QCOMPARE(exceptionCaught, false);
}
+void TestLanguage::probesAndMultiplexing()
+{
+ bool exceptionCaught = false;
+ try {
+ resolveProject("probes-and-multiplexing.qbs");
+ QVERIFY(project);
+ QCOMPARE(int(project->products.size()), 3);
+ QStringList architectures{"x86", "x86_64", "arm"};
+ for (const ResolvedProductPtr &product : project->products) {
+ const QString arch = product->moduleProperties->moduleProperty("qbs", "architecture")
+ .toString();
+ QVERIFY2(architectures.removeOne(arch), qPrintable(arch));
+ QCOMPARE(product->productProperties.value("archFromProbe").toString(), arch);
+ bool foundGroup = false;
+ for (const GroupPtr &group : product->groups) {
+ if (group->name == "theGroup") {
+ foundGroup = true;
+ QCOMPARE(group->properties->moduleProperty("qbs", "sysroot"), "/" + arch);
+ }
+ }
+ QVERIFY(foundGroup);
+ }
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
+ qDebug() << e.toString();
+ }
+ QCOMPARE(exceptionCaught, false);
+}
+
void TestLanguage::profileValuesAndOverriddenValues()
{
bool exceptionCaught = false;
@@ -2418,14 +2695,11 @@ void TestLanguage::profileValuesAndOverriddenValues()
profile.setValue("dummy.cFlags", "IN_PROFILE");
profile.setValue("dummy.cxxFlags", "IN_PROFILE");
profile.setValue("qbs.architecture", "x86");
- SetupProjectParameters parameters = defaultParameters;
- parameters.setTopLevelProfile(profile.name());
+ defaultParameters.setTopLevelProfile(profile.name());
QVariantMap overriddenValues;
overriddenValues.insert("modules.dummy.cFlags", "OVERRIDDEN");
- parameters.setOverriddenValues(overriddenValues);
- parameters.setProjectFilePath(testProject("profilevaluesandoverriddenvalues.qbs"));
- parameters.expandBuildConfiguration();
- project = loader->loadProject(parameters);
+ defaultParameters.setOverriddenValues(overriddenValues);
+ resolveProject("profilevaluesandoverriddenvalues.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
ResolvedProductPtr product = products.value("product1");
@@ -2455,7 +2729,7 @@ void TestLanguage::projectFileLookup()
try {
SetupProjectParameters params;
params.setProjectFilePath(projectFileInput);
- Loader::setupProjectFilePath(params);
+ params.finalizeProjectFilePath();
QVERIFY(!failureExpected);
QCOMPARE(params.projectFilePath(), projectFileOutput);
} catch (const ErrorInfo &) {
@@ -2469,7 +2743,7 @@ void TestLanguage::projectFileLookup_data()
QTest::addColumn<QString>("projectFileOutput");
QTest::addColumn<bool>("failureExpected");
- const QString baseDir = QLatin1String(SRCDIR) + "/testdata";
+ const QString baseDir = testDataDir();
const QString multiProjectsDir = baseDir + "/dirwithmultipleprojects";
const QString noProjectsDir = baseDir + "/dirwithnoprojects";
const QString oneProjectDir = baseDir + "/dirwithoneproject";
@@ -2486,8 +2760,7 @@ void TestLanguage::productConditions()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("productconditions.qbs"));
- TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("productconditions.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 6);
@@ -2515,8 +2788,7 @@ void TestLanguage::productConditions()
product = products.value("product_probe_condition_true");
QVERIFY(!!product);
QVERIFY(product->enabled);
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -2527,8 +2799,7 @@ void TestLanguage::productDirectories()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("productdirectories.qbs"));
- ResolvedProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("productdirectories.qbs");
QVERIFY(!!project);
QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 1);
@@ -2539,8 +2810,7 @@ void TestLanguage::productDirectories()
QCOMPARE(config.value(QStringLiteral("buildDirectory")).toString(),
product->buildDirectory());
QCOMPARE(config.value(QStringLiteral("sourceDirectory")).toString(), testDataDir());
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
exceptionCaught = true;
qDebug() << e.toString();
}
@@ -2686,8 +2956,7 @@ void TestLanguage::propertiesBlockInGroup()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("properties-block-in-group.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("properties-block-in-group.qbs");
QVERIFY(!!project);
QCOMPARE(project->allProducts().size(), size_t(1));
const ResolvedProductConstPtr product = project->allProducts().front();
@@ -2711,13 +2980,11 @@ void TestLanguage::propertiesItemInModule()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(
- testProject("properties-item-in-module.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("properties-item-in-module.qbs");
QVERIFY(!!project);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 2);
- for (const ResolvedProductConstPtr &p : products) {
+ for (const ResolvedProductPtr &p : products) {
QCOMPARE(p->moduleProperties->moduleProperty("dummy", "productName").toString(),
p->name);
}
@@ -2732,16 +2999,14 @@ void TestLanguage::propertyAssignmentInExportedGroup()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(
- testProject("property-assignment-in-exported-group.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("property-assignment-in-exported-group.qbs");
QVERIFY(!!project);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 2);
- for (const ResolvedProductConstPtr &p : products) {
+ for (const ResolvedProductPtr &p : products) {
QCOMPARE(p->moduleProperties->moduleProperty("dummy", "someString").toString(),
QString());
- for (const GroupConstPtr &g : p->groups) {
+ for (const GroupPtr &g : p->groups) {
const QString propValue
= g->properties->moduleProperty("dummy", "someString").toString();
if (g->name == "exported_group")
@@ -2761,8 +3026,7 @@ void TestLanguage::qbs1275()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(testProject("qbs1275.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("qbs1275.qbs");
QVERIFY(!!project);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.count(), 5);
@@ -2777,10 +3041,9 @@ void TestLanguage::qbsPropertiesInProjectCondition()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(
- testProject("qbs-properties-in-project-condition.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("qbs-properties-in-project-condition.qbs");
QVERIFY(!!project);
+ QVERIFY(!project->enabled);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 0);
} catch (const ErrorInfo &e) {
@@ -2794,16 +3057,14 @@ void TestLanguage::qbsPropertyConvenienceOverride()
{
bool exceptionCaught = false;
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("qbs-property-convenience-override.qbs"));
- params.setOverriddenValues({std::make_pair("qbs.installPrefix", "/opt")});
- TopLevelProjectConstPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues({std::make_pair("qbs.installPrefix", "/opt")});
+ resolveProject("qbs-property-convenience-override.qbs");
QVERIFY(!!project);
QCOMPARE(project->products.size(), size_t(1));
QCOMPARE(project->products.front()->moduleProperties->qbsPropertyValue("installPrefix")
.toString(), QString("/opt"));
- }
- catch (const ErrorInfo &e) {
+ } catch (const ErrorInfo &e) {
+ exceptionCaught = true;
qDebug() << e.toString();
}
QCOMPARE(exceptionCaught, false);
@@ -2814,11 +3075,9 @@ void TestLanguage::relaxedErrorMode()
m_logSink->setLogLevel(LoggerMinLevel);
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);
+ defaultParameters.setProductErrorMode(strictMode ? ErrorHandlingMode::Strict
+ : ErrorHandlingMode::Relaxed);
+ resolveProject("relaxed-error-mode/relaxed-error-mode.qbs");
QVERIFY(!strictMode);
const auto productMap = productsFromProject(project);
const ResolvedProductConstPtr brokenProduct = productMap.value("broken");
@@ -2841,7 +3100,7 @@ void TestLanguage::relaxedErrorMode()
QVERIFY(missingFile->enabled);
QCOMPARE(missingFile->groups.size(), size_t(1));
QVERIFY(missingFile->groups.front()->enabled);
- QCOMPARE(missingFile->groups.front()->allFiles().size(), size_t(2));
+ QCOMPARE(missingFile->groups.front()->files.size(), size_t(2));
const ResolvedProductConstPtr fine = productMap.value("fine");
QVERIFY(fine->enabled);
QCOMPARE(fine->allFiles().size(), size_t(1));
@@ -2863,10 +3122,7 @@ 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);
+ resolveProject(qPrintable("required-and-nonrequired-dependencies/" + projectFile));
QVERIFY(!!project);
QVERIFY(!exceptionExpected);
} catch (const ErrorInfo &e) {
@@ -2893,10 +3149,7 @@ void TestLanguage::requiredAndNonRequiredDependencies_data()
void TestLanguage::suppressedAndNonSuppressedErrors()
{
try {
- SetupProjectParameters params = defaultParameters;
- const QString projectFilePath = "suppressed-and-non-suppressed-errors.qbs";
- params.setProjectFilePath(testProject(projectFilePath.toLocal8Bit()));
- const TopLevelProjectConstPtr project = loader->loadProject(params);
+ resolveProject("suppressed-and-non-suppressed-errors.qbs");
QFAIL("failure expected");
} catch (const ErrorInfo &e) {
QVERIFY2(e.toString().contains("easter bunny"), qPrintable(e.toString()));
@@ -2908,12 +3161,10 @@ void TestLanguage::throwingProbe()
{
QFETCH(bool, enableProbe);
try {
- SetupProjectParameters params = defaultParameters;
- params.setProjectFilePath(testProject("throwing-probe.qbs"));
QVariantMap properties;
properties.insert(QStringLiteral("products.theProduct.enableProbe"), enableProbe);
- params.setOverriddenValues(properties);
- const TopLevelProjectPtr project = loader->loadProject(params);
+ defaultParameters.setOverriddenValues(properties);
+ resolveProject("throwing-probe.qbs");
QVERIFY(!!project);
QVERIFY(!enableProbe);
} catch (const ErrorInfo &e) {
@@ -2955,9 +3206,7 @@ void TestLanguage::recursiveProductDependencies()
{
bool exceptionCaught = false;
try {
- defaultParameters.setProjectFilePath(
- testProject("recursive-dependencies/recursive-dependencies.qbs"));
- const TopLevelProjectPtr project = loader->loadProject(defaultParameters);
+ resolveProject("recursive-dependencies/recursive-dependencies.qbs");
QVERIFY(!!project);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
QCOMPARE(products.size(), 4);
@@ -3014,6 +3263,19 @@ void TestLanguage::fileTags()
QCOMPARE(fileTags, expectedFileTags);
}
+void TestLanguage::useInternalProfile()
+{
+ const QString profile(QStringLiteral("theprofile"));
+ defaultParameters.setTopLevelProfile(profile);
+ resolveProject("use-internal-profile.qbs");
+ QVERIFY(!!project);
+ QCOMPARE(project->profile(), profile);
+ QCOMPARE(project->products.size(), size_t(1));
+ const ResolvedProductConstPtr product = project->products[0];
+ QCOMPARE(product->profile(), profile);
+ QCOMPARE(product->moduleProperties->moduleProperty("dummy", "defines").toString(), profile);
+}
+
void TestLanguage::wildcards_data()
{
QTest::addColumn<bool>("useGroup");
@@ -3182,8 +3444,8 @@ void TestLanguage::wildcards()
QFile projectFile(projectFilePath);
QVERIFY(projectFile.open(QIODevice::WriteOnly));
QTextStream s(&projectFile);
- s << "import qbs.base 1.0" << endl << endl
- << "Application {" << endl
+ using Qt::endl;
+ s << "Application {" << endl
<< " name: \"MyProduct\"" << endl;
if (useGroup) {
s << " Group {" << endl
@@ -3201,7 +3463,7 @@ void TestLanguage::wildcards()
}
// create files
- for (QString filePath : qAsConst(filesToCreate)) {
+ for (QString filePath : std::as_const(filesToCreate)) {
filePath.prepend(m_wildcardsTestDirPath + '/');
QFileInfo fi(filePath);
if (!QDir(fi.path()).exists())
@@ -3215,7 +3477,7 @@ void TestLanguage::wildcards()
ResolvedProductPtr product;
try {
defaultParameters.setProjectFilePath(projectFilePath);
- project = loader->loadProject(defaultParameters);
+ resolveProject();
QVERIFY(!!project);
const QHash<QString, ResolvedProductPtr> products = productsFromProject(project);
product = products.value("MyProduct");
@@ -3234,10 +3496,10 @@ void TestLanguage::wildcards()
group = product->groups.front();
}
QVERIFY(!!group);
- QCOMPARE(group->files.size(), size_t(0));
+ QCOMPARE(group->files.size(), expected.size()); // we assume all files are wildcards
QVERIFY(!!group->wildcards);
QStringList actualFilePaths;
- for (const SourceArtifactConstPtr &artifact : group->wildcards->files) {
+ for (const SourceArtifactPtr &artifact : group->files) {
QString str = artifact->absoluteFilePath;
int idx = str.indexOf(m_wildcardsTestDirPath);
if (idx != -1)
@@ -3261,4 +3523,3 @@ int main(int argc, char *argv[])
TestLanguage tl(ConsoleLogger::instance().logSink(), s.get());
return QTest::qExec(&tl, argc, argv);
}
-
diff --git a/tests/auto/language/tst_language.h b/tests/auto/language/tst_language.h
index 3fe6d8f2a..870b7d1f8 100644
--- a/tests/auto/language/tst_language.h
+++ b/tests/auto/language/tst_language.h
@@ -41,10 +41,12 @@
#define TST_LANGUAGE_H
#include <language/forward_decls.h>
-#include <language/loader.h>
+#include <language/scriptengine.h>
#include <logging/ilogsink.h>
+#include <logging/logger.h>
#include <tools/setupprojectparameters.h>
+#include <QtCore/qrandom.h>
#include <QtCore/qtemporarydir.h>
#include <QtTest/qtest.h>
@@ -55,27 +57,9 @@ public:
TestLanguage(qbs::ILogSink *logSink, qbs::Settings *settings);
~TestLanguage();
-private:
- qbs::ILogSink *m_logSink;
- qbs::Settings * const m_settings;
- qbs::Internal::Logger m_logger;
- qbs::Internal::ScriptEngine *m_engine;
- qbs::Internal::Loader *loader;
- qbs::Internal::TopLevelProjectPtr project;
- qbs::SetupProjectParameters defaultParameters;
- const QString m_wildcardsTestDirPath;
-
- QHash<QString, qbs::Internal::ResolvedProductPtr> productsFromProject(
- qbs::Internal::ResolvedProjectPtr project);
- qbs::Internal::ResolvedModuleConstPtr findModuleByName(
- qbs::Internal::ResolvedProductPtr product, const QString &name);
- QVariant productPropertyValue(qbs::Internal::ResolvedProductPtr product, QString propertyName);
- void handleInitCleanupDataTags(const char *projectFileName, bool *handled);
-
private slots:
void init();
void initTestCase();
- void cleanupTestCase();
void additionalProductTypes();
void baseProperty();
@@ -87,6 +71,7 @@ private slots:
void chainedProbes();
void canonicalArchitecture();
void conditionalDepends();
+ void convertStringList();
void delayedError();
void delayedError_data();
void dependencyOnAllProfiles();
@@ -94,6 +79,8 @@ private slots:
void disabledSubProject();
void dottedNames_data();
void dottedNames();
+ void duplicateMultiplexValues_data();
+ void duplicateMultiplexValues();
void emptyJsFile();
void enumerateProjectProperties();
void evalErrorInNonPresentModule_data();
@@ -123,12 +110,19 @@ private slots:
void invalidBindingInDisabledItem();
void invalidOverrides();
void invalidOverrides_data();
+ void invalidPropOnNonRequiredModule_data();
+ void invalidPropOnNonRequiredModule();
void itemPrototype();
void itemScope();
void jsExtensions();
void jsImportUsedInMultipleScopes_data();
void jsImportUsedInMultipleScopes();
+ void localProfileAsTopLevelProfile();
void moduleMergingVariantValues();
+ void moduleNameCollisions_data();
+ void moduleNameCollisions();
+ void moduleParameters_data();
+ void moduleParameters();
void modulePrioritizationBySearchPath_data();
void modulePrioritizationBySearchPath();
void moduleProperties_data();
@@ -136,6 +130,7 @@ private slots:
void modulePropertiesInGroups();
void modulePropertyOverridesPerProduct();
void moduleScope();
+ void moduleWithProductDependency();
void modules_data();
void modules();
void multiplexedExports();
@@ -151,6 +146,7 @@ private slots:
void overriddenVariantProperty();
void parameterTypes();
void pathProperties();
+ void probesAndMultiplexing();
void productConditions();
void productDirectories();
void profileValuesAndOverriddenValues();
@@ -176,12 +172,31 @@ private slots:
void qualifiedId();
void recursiveProductDependencies();
void rfc1034Identifier();
+ void throwThings_data();
+ void throwThings();
+ void useInternalProfile();
void versionCompare();
void wildcards_data();
void wildcards();
private:
+ QHash<QString, qbs::Internal::ResolvedProductPtr> productsFromProject(
+ qbs::Internal::ResolvedProjectPtr project);
+ qbs::Internal::ResolvedModuleConstPtr findModuleByName(
+ qbs::Internal::ResolvedProductPtr product, const QString &name);
+ QVariant productPropertyValue(qbs::Internal::ResolvedProductPtr product, QString propertyName);
+ void handleInitCleanupDataTags(const char *projectFileName, bool *handled);
+ qbs::Internal::TopLevelProjectPtr resolveProject(const char *relProjectFilePath = nullptr);
+
+ qbs::ILogSink * const m_logSink;
+ qbs::Settings * const m_settings;
+ qbs::Internal::Logger m_logger;
+ std::unique_ptr<qbs::Internal::ScriptEngine> m_engine;
+ qbs::Internal::TopLevelProjectPtr project;
+ qbs::SetupProjectParameters defaultParameters;
+ const QString m_wildcardsTestDirPath;
QTemporaryDir m_tempDir;
+ QRandomGenerator m_rand;
};
#endif // TST_LANGUAGE_H
diff --git a/tests/auto/pkgconfig/CMakeLists.txt b/tests/auto/pkgconfig/CMakeLists.txt
new file mode 100644
index 000000000..74a13a8ab
--- /dev/null
+++ b/tests/auto/pkgconfig/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_qbs_test(pkgconfig
+ SOURCES
+ tst_pkgconfig.cpp
+ tst_pkgconfig.h
+ DEPENDS
+ qbspkgconfig
+ qbsquickjsheaders
+ )
diff --git a/tests/auto/pkgconfig/pkgconfig.qbs b/tests/auto/pkgconfig/pkgconfig.qbs
new file mode 100644
index 000000000..d42a5233b
--- /dev/null
+++ b/tests/auto/pkgconfig/pkgconfig.qbs
@@ -0,0 +1,19 @@
+import qbs
+import qbs.Utilities
+
+QbsUnittest {
+ Depends { name: "qbspkgconfig" }
+ condition: qbsbuildconfig.enableUnitTests
+ testName: "pkgconfig"
+ files: ["../shared.h", "tst_pkgconfig.h", "tst_pkgconfig.cpp"]
+ cpp.defines: base.concat([
+ "SRCDIR=" + Utilities.cStringQuote(path),
+ ])
+
+ Group {
+ name: "testdata"
+ prefix: "testdata/"
+ files: ["**/*"]
+ fileTags: []
+ }
+}
diff --git a/tests/auto/pkgconfig/testdata/base.name.json b/tests/auto/pkgconfig/testdata/base.name.json
new file mode 100644
index 000000000..a10e905ac
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/base.name.json
@@ -0,0 +1,20 @@
+{
+ "Name": "Base Name test",
+ "Description": "Checks correct baseName detection",
+ "Version": "1.0.0",
+ "Vars": {
+ "prefix": "/usr",
+ "exec_prefix": "/usr",
+ "libdir": "/usr/lib",
+ "includedir": "/usr/include"
+ },
+ "Libs": [
+ {"Type": "LibraryName", "Value": "simple"}
+ ],
+ "LibsPrivate": [
+ {"Type": "LibraryName", "Value": "m"}
+ ],
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "/usr/include"}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/base.name.pc b/tests/auto/pkgconfig/testdata/base.name.pc
new file mode 100644
index 000000000..2bb3e275e
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/base.name.pc
@@ -0,0 +1,12 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: Base Name test
+Description: Checks correct baseName detection
+Version: 1.0.0
+Requires:
+Libs: -lsimple
+Libs.private: -lm
+Cflags: -I${includedir}
diff --git a/tests/auto/pkgconfig/testdata/empty-variable.json b/tests/auto/pkgconfig/testdata/empty-variable.json
new file mode 100644
index 000000000..b96689979
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/empty-variable.json
@@ -0,0 +1,21 @@
+{
+ "Name": "Empty Variable test",
+ "Description": "Checks that empty variables are handled correcty",
+ "Version": "1.0.0",
+ "Vars": {
+ "rootprefix": "",
+ "prefix": "/usr",
+ "exec_prefix": "//usr",
+ "libdir": "//usr/lib",
+ "includedir": "//usr/include"
+ },
+ "Libs": [
+ {"Type": "LibraryName", "Value": "simple"}
+ ],
+ "LibsPrivate": [
+ {"Type": "LibraryName", "Value": "m"}
+ ],
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "//usr/include"}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/empty-variable.pc b/tests/auto/pkgconfig/testdata/empty-variable.pc
new file mode 100644
index 000000000..581382b7e
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/empty-variable.pc
@@ -0,0 +1,13 @@
+rootprefix=
+prefix=/usr
+exec_prefix=${rootprefix}/${prefix}
+libdir=${exec_prefix}/lib
+includedir=${rootprefix}/${prefix}/include
+
+Name: Empty Variable test
+Description: Checks that empty variables are handled correcty
+Version: 1.0.0
+Requires:
+Libs: -lsimple
+Libs.private: -lm
+Cflags: -I${includedir}
diff --git a/tests/auto/pkgconfig/testdata/lib/pkgconfig/prefix.pc b/tests/auto/pkgconfig/testdata/lib/pkgconfig/prefix.pc
new file mode 100644
index 000000000..64b980803
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/lib/pkgconfig/prefix.pc
@@ -0,0 +1,13 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=/usr/include
+usrdir=/usrdir
+
+Name: Prefix test
+Description: This tests prefix auto detection
+Version: 1.0.0
+Requires:
+Libs: -lprefix
+Libs.private: -lm
+Cflags: -I${includedir}
diff --git a/tests/auto/pkgconfig/testdata/non-l-required.json b/tests/auto/pkgconfig/testdata/non-l-required.json
new file mode 100644
index 000000000..d2dd90f06
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/non-l-required.json
@@ -0,0 +1,12 @@
+{
+ "Name": "Non-l flags required test package",
+ "Description": "Test package for checking order of non-L Libs & Cflags",
+ "Version": "1.0.0",
+ "Libs": [
+ {"Type": "StaticLibraryName", "Value": "/non-l-required.a"},
+ {"Type": "LinkerFlag", "Value": "-pthread"}
+ ],
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "/non-l-required/include"}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/non-l-required.pc b/tests/auto/pkgconfig/testdata/non-l-required.pc
new file mode 100644
index 000000000..7e398e2e1
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/non-l-required.pc
@@ -0,0 +1,5 @@
+Name: Non-l flags required test package
+Description: Test package for checking order of non-L Libs & Cflags
+Version: 1.0.0
+Libs: /non-l-required.a -pthread
+Cflags: -I/non-l-required/include
diff --git a/tests/auto/pkgconfig/testdata/private-dep.pc b/tests/auto/pkgconfig/testdata/private-dep.pc
new file mode 100644
index 000000000..cb401391d
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/private-dep.pc
@@ -0,0 +1,6 @@
+Name: Requires test package
+Description: Dummy pkgconfig test package for testing Requires/Requires.private
+Version: 1.0.0
+Libs: -L/private-dep/lib -lprivate-dep
+Cflags: -I/private-dep/include
+
diff --git a/tests/auto/pkgconfig/testdata/public-dep.pc b/tests/auto/pkgconfig/testdata/public-dep.pc
new file mode 100644
index 000000000..e450e46f1
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/public-dep.pc
@@ -0,0 +1,6 @@
+Name: Requires test package
+Description: Dummy pkgconfig test package for testing Requires/Requires.private
+Version: 1.0.0
+Requires.private:
+Libs: -L/public-dep/lib -lpublic-dep
+Cflags: -I/public-dep/include
diff --git a/tests/auto/pkgconfig/testdata/requires-test.json b/tests/auto/pkgconfig/testdata/requires-test.json
new file mode 100644
index 000000000..32acf4b91
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/requires-test.json
@@ -0,0 +1,18 @@
+{
+ "Name": "Requires test package",
+ "Description": "Dummy pkgconfig test package for testing Requires/Requires.private",
+ "Version": "1.0.0",
+ "Libs": [
+ {"Type": "LibraryPath", "Value": "/requires-test/lib"},
+ {"Type": "LibraryName", "Value": "requires-test"}
+ ],
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "/requires-test/include"}
+ ],
+ "Requires": [
+ {"Comparison": "GreaterThanEqual", "Name": "public-dep", "Version": "1"}
+ ],
+ "RequiresPrivate": [
+ {"Comparison": "GreaterThanEqual", "Name": "private-dep", "Version": "1"}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/requires-test.pc b/tests/auto/pkgconfig/testdata/requires-test.pc
new file mode 100644
index 000000000..e483db2db
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/requires-test.pc
@@ -0,0 +1,8 @@
+Name: Requires test package
+Description: Dummy pkgconfig test package for testing Requires/Requires.private
+Version: 1.0.0
+Requires: public-dep >= 1
+Requires.private: private-dep >= 1
+Libs: -L/requires-test/lib -lrequires-test
+Cflags: -I/requires-test/include
+
diff --git a/tests/auto/pkgconfig/testdata/simple.json b/tests/auto/pkgconfig/testdata/simple.json
new file mode 100644
index 000000000..d58556e74
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/simple.json
@@ -0,0 +1,20 @@
+{
+ "Name": "Simple test",
+ "Description": "Dummy pkgconfig test package for testing pkgconfig",
+ "Version": "1.0.0",
+ "Vars": {
+ "prefix": "/usr",
+ "exec_prefix": "/usr",
+ "libdir": "/usr/lib",
+ "includedir": "/usr/include"
+ },
+ "Libs": [
+ {"Type": "LibraryName", "Value": "simple"}
+ ],
+ "LibsPrivate": [
+ {"Type": "LibraryName", "Value": "m"}
+ ],
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "/usr/include"}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/simple.pc b/tests/auto/pkgconfig/testdata/simple.pc
new file mode 100644
index 000000000..2daa0350f
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/simple.pc
@@ -0,0 +1,12 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: Simple test
+Description: Dummy pkgconfig test package for testing pkgconfig
+Version: 1.0.0
+Requires:
+Libs: -lsimple
+Libs.private: -lm
+Cflags: -I${includedir}
diff --git a/tests/auto/pkgconfig/testdata/special-flags.json b/tests/auto/pkgconfig/testdata/special-flags.json
new file mode 100644
index 000000000..1949820a6
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/special-flags.json
@@ -0,0 +1,30 @@
+{
+ "Name": "Special flags test",
+ "Description": "Dummy pkgconfig test package for testing pkgconfig",
+ "Version": "1.0.0",
+ "Vars": {
+ "prefix": "/usr",
+ "exec_prefix": "/usr",
+ "libdir": "/usr/lib",
+ "includedir": "/usr/include"
+ },
+ "Libs": [
+ {"Type": "LibraryPath", "Value": "/foo"},
+ {"Type": "Framework", "Value": "Foo"},
+ {"Type": "LibraryName", "Value": "simple"},
+ {"Type": "LibraryPath", "Value": "/bar"},
+ {"Type": "Framework", "Value": "Bar"},
+ {"Type": "LinkerFlag", "Value": "-Wl,-framework"},
+ {"Type": "LinkerFlag", "Value": "-Wl,Baz"}
+ ],
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "/foo"},
+ {"Type": "CompilerFlag", "Value": "-g"},
+ {"Type": "SystemIncludePath", "Value": "/system1"},
+ {"Type": "DirAfterIncludePath", "Value": "/after1"},
+ {"Type": "CompilerFlag", "Value": "-ffoo"},
+ {"Type": "IncludePath", "Value": "/bar"},
+ {"Type": "DirAfterIncludePath", "Value": "/after2"},
+ {"Type": "SystemIncludePath", "Value": "/system2"}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/special-flags.pc b/tests/auto/pkgconfig/testdata/special-flags.pc
new file mode 100644
index 000000000..0bdaeb1b0
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/special-flags.pc
@@ -0,0 +1,11 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: Special flags test
+Description: Dummy pkgconfig test package for testing pkgconfig
+Version: 1.0.0
+Requires:
+Libs: -L/foo -framework Foo -lsimple -L/bar -framework Bar -Wl,-framework -Wl,Baz
+Cflags: -I/foo -g -isystem /system1 -idirafter /after1 -ffoo -I/bar -idirafter /after2 -isystem /system2
diff --git a/tests/auto/pkgconfig/testdata/sysroot.json b/tests/auto/pkgconfig/testdata/sysroot.json
new file mode 100644
index 000000000..7e8b3f6bb
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/sysroot.json
@@ -0,0 +1,21 @@
+{
+ "Name": "Test for sysroot",
+ "Description": "Test package for testing sysroot",
+ "Version": "1.0.0",
+ "Vars": {
+ "prefix": "/opt",
+ "exec_prefix": "/opt",
+ "libdir": "/opt/lib",
+ "includedir": "/opt/include",
+ "sysroot": "/newroot"
+ },
+ "Libs": [
+ {"Type": "LibraryPath", "Value": "/newroot/opt/lib"},
+ {"Type": "LibraryName", "Value": "system"}
+ ],
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "/newroot/opt/include"},
+ {"Type": "DirAfterIncludePath", "Value": "/newroot/opt/include/after"},
+ {"Type": "SystemIncludePath", "Value": "/newroot/opt/include/system"}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/sysroot.pc b/tests/auto/pkgconfig/testdata/sysroot.pc
new file mode 100644
index 000000000..d2f78987f
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/sysroot.pc
@@ -0,0 +1,12 @@
+prefix=/opt
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+sysroot=${pc_sysrootdir}
+
+Name: Test for sysroot
+Description: Test package for testing sysroot
+Version: 1.0.0
+Requires:
+Libs: -L${libdir} -lsystem
+Cflags: -I${includedir} -idirafter ${includedir}/after -isystem ${includedir}/system
diff --git a/tests/auto/pkgconfig/testdata/system.json b/tests/auto/pkgconfig/testdata/system.json
new file mode 100644
index 000000000..89007e7ec
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/system.json
@@ -0,0 +1,17 @@
+{
+ "Name": "System library",
+ "Description": "Test package",
+ "Version": "1.0.0",
+ "Vars": {
+ "prefix": "/usr",
+ "exec_prefix": "/usr",
+ "libdir": "/usr/lib",
+ "includedir": "/usr/include"
+ },
+ "Libs": [
+ {"Type": "LibraryName", "Value": "system"}
+ ],
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "/usr/include"}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/system.pc b/tests/auto/pkgconfig/testdata/system.pc
new file mode 100644
index 000000000..2cef2ed9b
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/system.pc
@@ -0,0 +1,10 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: System library
+Description: Test package
+Version: 1.0.0
+Libs: -L${libdir} -lsystem
+Cflags: -I${includedir}
diff --git a/tests/auto/pkgconfig/testdata/tilde.json b/tests/auto/pkgconfig/testdata/tilde.json
new file mode 100644
index 000000000..01ea5d050
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/tilde.json
@@ -0,0 +1,11 @@
+{
+ "Name": "tilde",
+ "Description": "tilde test module",
+ "Version": "1.0",
+ "Libs": [
+ {"Type": "LibraryPath", "Value": "~"}
+ ],
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "~"}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/tilde.pc b/tests/auto/pkgconfig/testdata/tilde.pc
new file mode 100644
index 000000000..c3babc120
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/tilde.pc
@@ -0,0 +1,5 @@
+Name: tilde
+Description: tilde test module
+Version: 1.0
+Libs: -L~
+Cflags: -I~
diff --git a/tests/auto/pkgconfig/testdata/variables.json b/tests/auto/pkgconfig/testdata/variables.json
new file mode 100644
index 000000000..7565b6804
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/variables.json
@@ -0,0 +1,17 @@
+{
+ "Name": "Complex variables",
+ "Description": "Test complex variable output",
+ "Version": "1.0",
+ "Vars": {
+ "prefix": "/local",
+ "exec_prefix": "/local",
+ "libdir": "/local/lib",
+ "includedir": "\"/local/include\"",
+ "cppflags": "-I\"/local/include\"/foo -DFOO=\\\"/bar\\\""
+ },
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "/local/include"},
+ {"Type": "IncludePath", "Value": "/local/include/foo"},
+ {"Type": "Define", "Value": "FOO=\"/bar\""}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/variables.pc b/tests/auto/pkgconfig/testdata/variables.pc
new file mode 100644
index 000000000..b27ab78e1
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/variables.pc
@@ -0,0 +1,11 @@
+prefix=/local
+exec_prefix=${prefix}
+libdir=${exec_prefix}/lib
+includedir="${prefix}/include"
+cppflags=-I${includedir}/foo \
+ -DFOO=\"/bar\"
+
+Name: Complex variables
+Description: Test complex variable output
+Version: 1.0
+Cflags: -I${includedir} ${cppflags}
diff --git a/tests/auto/pkgconfig/testdata/whitespace.json b/tests/auto/pkgconfig/testdata/whitespace.json
new file mode 100644
index 000000000..dcfa3ece3
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/whitespace.json
@@ -0,0 +1,24 @@
+{
+ "Name": "Whitespace test",
+ "Description": "Dummy pkgconfig test package for testing pkgconfig",
+ "Version": "1.0.0",
+ "Vars": {
+ "prefix": "/usr",
+ "exec_prefix": "/usr",
+ "libdir": "\"/usr/white space/lib\"",
+ "includedir": "\"/usr/white space/include\""
+ },
+ "Libs": [
+ {"Type": "LibraryPath", "Value": "/usr/white space/lib"},
+ {"Type": "LibraryName", "Value": "foo bar"},
+ {"Type": "LibraryName", "Value": "bar baz"},
+ {"Type": "LinkerFlag", "Value": "-r:foo"}
+ ],
+ "Cflags": [
+ {"Type": "IncludePath", "Value": "/usr/white space/include"},
+ {"Type": "IncludePath", "Value": "$(top_builddir)"},
+ {"Type": "IncludePath", "Value": "include dir"},
+ {"Type": "IncludePath", "Value": "other include dir"},
+ {"Type": "Define", "Value": "lala=misc"}
+ ]
+}
diff --git a/tests/auto/pkgconfig/testdata/whitespace.pc b/tests/auto/pkgconfig/testdata/whitespace.pc
new file mode 100644
index 000000000..693bbc4d0
--- /dev/null
+++ b/tests/auto/pkgconfig/testdata/whitespace.pc
@@ -0,0 +1,11 @@
+prefix=/usr
+exec_prefix=${prefix}
+libdir="${exec_prefix}/white space/lib"
+includedir="${prefix}/white space/include"
+
+Name: Whitespace test
+Description: Dummy pkgconfig test package for testing pkgconfig
+Version: 1.0.0
+Requires:
+Libs: -L${libdir} -lfoo\ bar "-lbar baz" -r:foo
+Cflags: -I${includedir} -I$(top_builddir) -Iinclude\ dir "-Iother include dir" -Dlala=misc
diff --git a/tests/auto/pkgconfig/tst_pkgconfig.cpp b/tests/auto/pkgconfig/tst_pkgconfig.cpp
new file mode 100644
index 000000000..86d954259
--- /dev/null
+++ b/tests/auto/pkgconfig/tst_pkgconfig.cpp
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** 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_pkgconfig.h"
+
+#include "../shared.h"
+
+#include <tools/fileinfo.h>
+#include <tools/hostosinfo.h>
+#include <pkgconfig.h>
+#include <pcparser.h>
+#include <jsextensions/pkgconfigjs.h>
+
+#include <QJsonArray>
+#include <QJsonDocument>
+
+using HostOsInfo = qbs::Internal::HostOsInfo;
+using PcPackage = qbs::PcPackage;
+using PkgConfig = qbs::PkgConfig;
+using Options = qbs::PkgConfig::Options;
+
+TestPkgConfig::TestPkgConfig()
+ : m_sourceDataDir(testDataSourceDir(SRCDIR "/testdata"))
+ , m_workingDataDir(testWorkDir(QStringLiteral("pkgconfig")))
+{
+}
+
+void TestPkgConfig::initTestCase()
+{
+ QString errorMessage;
+ qbs::Internal::removeDirectoryWithContents(m_workingDataDir, &errorMessage);
+ QVERIFY2(qbs::Internal::copyFileRecursion(m_sourceDataDir,
+ m_workingDataDir, false, true, &errorMessage),
+ qPrintable(errorMessage));
+}
+
+void TestPkgConfig::fileName()
+{
+ QCOMPARE(qbs::Internal::fileName(""), "");
+ QCOMPARE(qbs::Internal::fileName("file.txt"), "file.txt");
+ QCOMPARE(qbs::Internal::fileName("/home/user/file.txt"), "file.txt");
+ QCOMPARE(qbs::Internal::fileName("/"), "");
+#if defined(Q_OS_WIN)
+ QCOMPARE(qbs::Internal::fileName("c:file.txt"), "file.txt");
+ QCOMPARE(qbs::Internal::fileName("c:"), "");
+#endif
+}
+
+void TestPkgConfig::completeBaseName()
+{
+ QCOMPARE(qbs::Internal::completeBaseName(""), "");
+ QCOMPARE(qbs::Internal::completeBaseName("file.txt"), "file");
+ QCOMPARE(qbs::Internal::completeBaseName("archive.tar.gz"), "archive.tar");
+ QCOMPARE(qbs::Internal::completeBaseName("/home/user/file.txt"), "file");
+#if defined(Q_OS_WIN)
+ QCOMPARE(qbs::Internal::completeBaseName("c:file.txt"), "file");
+ QCOMPARE(qbs::Internal::completeBaseName("c:archive.tar.gz"), "archive.tar");
+ QCOMPARE(qbs::Internal::completeBaseName("c:"), "");
+#endif
+}
+
+void TestPkgConfig::parentPath()
+{
+ QCOMPARE(qbs::Internal::parentPath(""), "");
+ QCOMPARE(qbs::Internal::parentPath("file.txt"), ".");
+ QCOMPARE(qbs::Internal::parentPath("/home/user/file.txt"), "/home/user");
+ QCOMPARE(qbs::Internal::parentPath("/home/user/"), "/home/user");
+ QCOMPARE(qbs::Internal::parentPath("/home"), "/");
+ QCOMPARE(qbs::Internal::parentPath("/"), "/");
+#if defined(Q_OS_WIN)
+ QCOMPARE(qbs::Internal::parentPath("c:/folder/file.txt"), "c:/folder");
+ QCOMPARE(qbs::Internal::parentPath("c:/folder/"), "c:/folder");
+ QCOMPARE(qbs::Internal::parentPath("c:/folder"), "c:/");
+ QCOMPARE(qbs::Internal::parentPath("c:/"), "c:/");
+ QCOMPARE(qbs::Internal::parentPath("c:"), "c:");
+#endif
+}
+
+void TestPkgConfig::pkgConfig()
+{
+ QFETCH(QString, pcFileName);
+ QFETCH(QString, jsonFileName);
+ QFETCH(QVariantMap, optionsMap);
+
+ if (jsonFileName.isEmpty())
+ jsonFileName = pcFileName;
+
+ Options options = qbs::Internal::PkgConfigJs::convertOptions(
+ QProcessEnvironment::systemEnvironment(), optionsMap);
+ options.libDirs.push_back(m_workingDataDir.toStdString());
+
+ PkgConfig pkgConfig(std::move(options));
+
+ QFile jsonFile(m_workingDataDir + "/" + jsonFileName + ".json");
+ QVERIFY(jsonFile.open(QIODevice::ReadOnly));
+ QJsonParseError error{};
+ const auto json = QJsonDocument::fromJson(jsonFile.readAll(), &error).toVariant().toMap();
+ QCOMPARE(error.error, QJsonParseError::NoError);
+
+ const auto &packageOr = pkgConfig.getPackage(pcFileName.toStdString());
+ QVERIFY(packageOr.isValid());
+ const auto &package = packageOr.asPackage();
+ QCOMPARE(QString::fromStdString(package.baseFileName), pcFileName);
+ QCOMPARE(QString::fromStdString(package.name), json.value("Name").toString());
+ QCOMPARE(QString::fromStdString(package.description), json.value("Description").toString());
+ QCOMPARE(QString::fromStdString(package.version), json.value("Version").toString());
+
+ auto variables = json["Vars"].toMap();
+ variables["pcfiledir"] = QFileInfo(m_workingDataDir).absoluteFilePath();
+
+ QCOMPARE(size_t(variables.size()), package.variables.size());
+ for (const auto &[key, value]: package.variables) {
+ QCOMPARE(QString::fromStdString(value),
+ variables.value(QString::fromStdString(key)).toString());
+ }
+
+ const auto jsonLibs = json.value("Libs").toJsonArray().toVariantList();
+ QCOMPARE(package.libs.size(), size_t(jsonLibs.size()));
+ for (size_t i = 0; i < package.libs.size(); ++i) {
+ const auto &item = package.libs[i];
+ const auto jsonItem = jsonLibs.at(i).toMap();
+
+ QCOMPARE(item.type,
+ *PcPackage::Flag::typeFromString(jsonItem.value("Type").toString().toStdString()));
+ QCOMPARE(QString::fromStdString(item.value), jsonItem.value("Value").toString());
+ }
+
+ const auto jsonLibsPrivate = json.value("LibsPrivate").toJsonArray().toVariantList();
+ QCOMPARE(package.libsPrivate.size(), size_t(jsonLibsPrivate.size()));
+ for (size_t i = 0; i < package.libsPrivate.size(); ++i) {
+ const auto &item = package.libsPrivate[i];
+ const auto jsonItem = jsonLibsPrivate.at(i).toMap();
+
+ QCOMPARE(item.type,
+ *PcPackage::Flag::typeFromString(jsonItem.value("Type").toString().toStdString()));
+ QCOMPARE(QString::fromStdString(item.value), jsonItem.value("Value").toString());
+ }
+
+ const auto jsonCFlags = json.value("Cflags").toJsonArray().toVariantList();
+ QCOMPARE(package.cflags.size(), size_t(jsonCFlags.size()));
+ for (size_t i = 0; i < package.cflags.size(); ++i) {
+ const auto &item = package.cflags[i];
+ const auto jsonItem = jsonCFlags.at(i).toMap();
+
+ QCOMPARE(item.type,
+ *PcPackage::Flag::typeFromString(jsonItem.value("Type").toString().toStdString()));
+ QCOMPARE(QString::fromStdString(item.value), jsonItem.value("Value").toString());
+ }
+
+ for (const auto &item: package.requiresPublic)
+ qInfo() << "requires" << item.name.c_str() << item.version.c_str();
+
+ const auto jsonRequires = json.value("Requires").toJsonArray().toVariantList();
+ QCOMPARE(package.requiresPublic.size(), size_t(jsonRequires.size()));
+ for (size_t i = 0; i < package.requiresPublic.size(); ++i) {
+ const auto &item = package.requiresPublic[i];
+ const auto jsonItem = jsonRequires.at(i).toMap();
+
+ QCOMPARE(item.comparison,
+ *PcPackage::RequiredVersion::comparisonFromString(
+ jsonItem.value("Comparison").toString().toStdString()));
+ QCOMPARE(QString::fromStdString(item.name), jsonItem.value("Name").toString());
+ QCOMPARE(QString::fromStdString(item.version), jsonItem.value("Version").toString());
+ }
+
+ const auto jsonRequiresPrivate = json.value("RequiresPrivate").toJsonArray().toVariantList();
+ QCOMPARE(package.requiresPrivate.size(), size_t(jsonRequiresPrivate.size()));
+ for (size_t i = 0; i < package.requiresPrivate.size(); ++i) {
+ const auto &item = package.requiresPrivate[i];
+ const auto jsonItem = jsonRequiresPrivate.at(i).toMap();
+
+ QCOMPARE(item.comparison,
+ *PcPackage::RequiredVersion::comparisonFromString(
+ jsonItem.value("Comparison").toString().toStdString()));
+ QCOMPARE(QString::fromStdString(item.name), jsonItem.value("Name").toString());
+ QCOMPARE(QString::fromStdString(item.version), jsonItem.value("Version").toString());
+ }
+}
+
+void TestPkgConfig::pkgConfig_data()
+{
+ QTest::addColumn<QString>("pcFileName");
+ QTest::addColumn<QString>("jsonFileName");
+ QTest::addColumn<QVariantMap>("optionsMap");
+
+ QTest::newRow("empty-variable")
+ << QStringLiteral("empty-variable") << QString() << QVariantMap();
+ QTest::newRow("non-l-required")
+ << QStringLiteral("non-l-required") << QString() << QVariantMap();
+ QTest::newRow("simple")
+ << QStringLiteral("simple") << QString() << QVariantMap();
+ QTest::newRow("requires-test")
+ << QStringLiteral("requires-test") << QString() << QVariantMap();
+ QTest::newRow("special-flags")
+ << QStringLiteral("special-flags") << QString() << QVariantMap();
+ QTest::newRow("system")
+ << QStringLiteral("system") << QString() << QVariantMap();
+ QTest::newRow("sysroot")
+ << QStringLiteral("sysroot") << QString() << QVariantMap({{"sysroot", "/newroot"}});
+ QTest::newRow("tilde")
+ << QStringLiteral("tilde") << QString() << QVariantMap();
+ QTest::newRow("variables")
+ << QStringLiteral("variables") << QString() << QVariantMap();
+ QTest::newRow("whitespace")
+ << QStringLiteral("whitespace") << QString() << QVariantMap();
+ QTest::newRow("base.name")
+ << QStringLiteral("base.name") << QString() << QVariantMap();
+}
+
+void TestPkgConfig::benchSystem()
+{
+ if (HostOsInfo::hostOs() == HostOsInfo::HostOsWindows)
+ QSKIP("Not available on Windows");
+ QBENCHMARK {
+ PkgConfig pkgConfig;
+ QVERIFY(!pkgConfig.packages().empty());
+ }
+}
+
+void TestPkgConfig::prefix()
+{
+ const auto prefixDir = m_workingDataDir;
+ const auto libDir = m_workingDataDir + "/lib";
+ const auto includeDir = m_workingDataDir + "/include";
+ const auto pkgconfigDir = libDir + "/pkgconfig";
+ Options options = qbs::Internal::PkgConfigJs::convertOptions(
+ QProcessEnvironment::systemEnvironment(), {});
+ options.definePrefix = true;
+ options.libDirs.push_back(pkgconfigDir.toStdString());
+ PkgConfig pkgConfig(std::move(options));
+ const auto &packageOr = pkgConfig.getPackage("prefix");
+ QVERIFY(packageOr.isValid());
+ const auto &package = packageOr.asPackage();
+ QCOMPARE(package.variables.at("prefix"), prefixDir.toStdString());
+ QCOMPARE(package.variables.at("exec_prefix"), prefixDir.toStdString());
+ QCOMPARE(package.variables.at("libdir"), libDir.toStdString());
+ QCOMPARE(package.variables.at("includedir"), includeDir.toStdString());
+ QCOMPARE(package.variables.at("usrdir"), "/usrdir");
+}
+
+QTEST_MAIN(TestPkgConfig)
diff --git a/tests/auto/pkgconfig/tst_pkgconfig.h b/tests/auto/pkgconfig/tst_pkgconfig.h
new file mode 100644
index 000000000..47654e1ec
--- /dev/null
+++ b/tests/auto/pkgconfig/tst_pkgconfig.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** 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 <QtCore/qobject.h>
+#include <QtCore/qvariant.h>
+
+class TestPkgConfig : public QObject
+{
+ Q_OBJECT
+
+public:
+ TestPkgConfig();
+
+private slots:
+ void initTestCase();
+ void fileName();
+ void completeBaseName();
+ void parentPath();
+ void pkgConfig();
+ void pkgConfig_data();
+ void benchSystem();
+ void prefix();
+
+private:
+ const QString m_sourceDataDir;
+ const QString m_workingDataDir;
+};
+
+#endif // Include guard.
diff --git a/tests/auto/shared.h b/tests/auto/shared.h
index e251d506c..408594be0 100644
--- a/tests/auto/shared.h
+++ b/tests/auto/shared.h
@@ -31,6 +31,7 @@
#include <tools/hostosinfo.h>
#include <tools/profile.h>
#include <tools/settings.h>
+#include <tools/toolchains.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qcryptographichash.h>
@@ -46,6 +47,8 @@
#include <memory>
+
+
#define REPLACE_IN_FILE(filePath, oldContent, newContent) \
do { \
QFile f((filePath)); \
@@ -155,8 +158,11 @@ inline QByteArray diffText(const QByteArray &actual, const QByteArray &expected)
n++;
}
auto addLines = [&result, &n] (const QList<QByteArray> &lines) {
- for (const QByteArray &line : qAsConst(lines)) {
- result += QStringLiteral("%1: %2\n").arg(n).arg(QString::fromUtf8(line));
+ for (const QByteArray &line : std::as_const(lines)) {
+ result += QStringLiteral("%1: %2\n")
+ .arg(n)
+ .arg(QString::fromUtf8(line))
+ .toUtf8();
n++;
}
};
@@ -268,14 +274,22 @@ inline void copyFileAndUpdateTimestamp(const QString &source, const QString &tar
touch(target);
}
+inline QStringList profileToolchain(const qbs::Profile &profile)
+{
+ const auto toolchainType = profile.value(QStringLiteral("qbs.toolchainType")).toString();
+ if (!toolchainType.isEmpty())
+ return qbs::canonicalToolchain(toolchainType);
+ return profile.value(QStringLiteral("qbs.toolchain")).toStringList();
+}
+
inline QString objectFileName(const QString &baseName, const QString &profileName)
{
const SettingsPtr s = settings();
qbs::Profile profile(profileName, s.get());
- const auto tc = profile.value("qbs.toolchainType").toString();
- const auto tcList = profile.value("qbs.toolchain").toStringList();
- const bool isMsvc = tc == "msvc" || tcList.contains("msvc")
- || (tc.isEmpty() && tcList.isEmpty() && qbs::Internal::HostOsInfo::isWindowsHost());
+
+ const auto tcList = profileToolchain(profile);
+ const bool isMsvc = tcList.contains("msvc")
+ || (tcList.isEmpty() && qbs::Internal::HostOsInfo::isWindowsHost());
const QString suffix = isMsvc ? "obj" : "o";
return baseName + '.' + suffix;
}
@@ -285,6 +299,30 @@ inline QString inputDirHash(const QString &dir)
return QCryptographicHash::hash(dir.toLatin1(), QCryptographicHash::Sha1).toHex().left(16);
}
+inline QString testDataSourceDir(const QString &dir)
+{
+ QDir result;
+ QString testSourceRootDirFromEnv = QDir::fromNativeSeparators(qEnvironmentVariable("QBS_TEST_SOURCE_ROOT"));
+ if (testSourceRootDirFromEnv.isEmpty()) {
+ result.setPath(dir);
+ } else {
+ QDir testSourceRootDir(dir);
+ while (testSourceRootDir.dirName() != "tests")
+ testSourceRootDir = QFileInfo(testSourceRootDir, "").dir();
+
+ QString relativeDataPath = testSourceRootDir.relativeFilePath(dir);
+ QString absoluteDataPath = QDir(testSourceRootDirFromEnv).absoluteFilePath(relativeDataPath);
+ result.setPath(absoluteDataPath);
+ }
+
+ if (!result.exists())
+ qFatal("Expected data folder '%s' to be present, but it does not exist. You may set "
+ "QBS_TEST_SOURCE_ROOT to the 'tests' folder in your qbs repository to configure "
+ "a custom location.", qPrintable(result.absolutePath()));
+
+ return result.absolutePath();
+}
+
inline QString testWorkDir(const QString &testName)
{
QString dir = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv("QBS_TEST_WORK_ROOT")));
@@ -294,12 +332,14 @@ inline QString testWorkDir(const QString &testName)
if (!dir.endsWith(QLatin1Char('/')))
dir += QLatin1Char('/');
}
- return dir + testName + "/testWorkDir";
+ return QDir::cleanPath(dir + testName + "/testWorkDir");
}
inline bool copyDllExportHeader(const QString &srcDataDir, const QString &targetDataDir)
{
QFile sourceFile(srcDataDir + "/../../dllexport.h");
+ if (!sourceFile.exists())
+ return true;
const QString targetPath = targetDataDir + "/dllexport.h";
QFile::remove(targetPath);
return sourceFile.copy(targetPath);
@@ -311,8 +351,7 @@ inline qbs::Internal::HostOsInfo::HostOs targetOs()
const qbs::Profile buildProfile(profileName(), s.get());
const QString targetPlatform = buildProfile.value("qbs.targetPlatform").toString();
if (!targetPlatform.isEmpty()) {
- const std::vector<std::string> targetOS = qbs::Internal::HostOsInfo::canonicalOSIdentifiers(
- targetPlatform.toStdString());
+ const auto targetOS = qbs::Internal::HostOsInfo::canonicalOSIdentifiers(targetPlatform);
if (qbs::Internal::contains(targetOS, "windows"))
return qbs::Internal::HostOsInfo::HostOsWindows;
if (qbs::Internal::contains(targetOS, "linux"))
diff --git a/tests/auto/tools/CMakeLists.txt b/tests/auto/tools/CMakeLists.txt
new file mode 100644
index 000000000..a75d998a3
--- /dev/null
+++ b/tests/auto/tools/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_qbs_test(tools
+ DEFINES
+ "QBS_VERSION=\"${QBS_VERSION}\""
+ SOURCES
+ tst_tools.cpp
+ tst_tools.h
+ )
diff --git a/tests/auto/tools/tools.pro b/tests/auto/tools/tools.pro
deleted file mode 100644
index ba293f417..000000000
--- a/tests/auto/tools/tools.pro
+++ /dev/null
@@ -1,6 +0,0 @@
-TARGET = tst_tools
-
-SOURCES = tst_tools.cpp ../../../src/app/qbs/qbstool.cpp
-HEADERS = tst_tools.h
-
-include(../auto.pri)
diff --git a/tests/auto/tools/tools.qbs b/tests/auto/tools/tools.qbs
index 64cced80e..781d6edae 100644
--- a/tests/auto/tools/tools.qbs
+++ b/tests/auto/tools/tools.qbs
@@ -1,7 +1,6 @@
-import qbs
import qbs.Utilities
-QbsAutotest {
+QbsUnittest {
Depends { name: "qbsversion" }
testName: "tools"
diff --git a/tests/auto/tools/tst_tools.cpp b/tests/auto/tools/tst_tools.cpp
index edf5a1308..a872bdd0d 100644
--- a/tests/auto/tools/tst_tools.cpp
+++ b/tests/auto/tools/tst_tools.cpp
@@ -68,6 +68,16 @@
using namespace qbs;
using namespace qbs::Internal;
+namespace std {
+template<typename T> struct hash<std::vector<T>>
+{
+ std::size_t operator()(const std::vector<T> &v) const noexcept
+ {
+ return hashRange(v);
+ }
+};
+} // namespace std
+
TestTools::TestTools(Settings *settings)
: m_settings(settings), testDataDir(testWorkDir("tools"))
{
@@ -298,10 +308,10 @@ void TestTools::testSettingsMigration()
QFETCH(bool, hasOldSettings);
Settings settings(baseDir);
if (hasOldSettings) {
- QVERIFY(QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles/right.txt")
+ // checks that we do not copy old "profiles/" dir anymore
+ QVERIFY(!QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles/right.txt")
.exists());
- QCOMPARE(settings.value("key", Settings::UserScope).toString(),
- settings.baseDirectory() + "/qbs/" QBS_VERSION "/profilesright");
+ QVERIFY(!settings.value("key", Settings::UserScope).toString().isEmpty());
} else {
QVERIFY(!QFileInfo(settings.baseDirectory() + "/qbs/" QBS_VERSION "/profiles").exists());
QVERIFY(settings.allKeys(Settings::UserScope).empty());
@@ -673,6 +683,30 @@ void TestTools::set_containsSet()
QVERIFY(set3.contains(set4));
}
+void TestTools::set_find()
+{
+ Set<QString> set1;
+
+ for (int i = 0; i < 500; ++i) {
+ QVERIFY(set1.find(QString::number(i)) == set1.end());
+ set1.insert(QString::number(i));
+ const auto it = set1.find(QString::number(i));
+ QVERIFY(it != set1.end());
+ QVERIFY(*it == QString::number(i));
+ }
+
+ QCOMPARE(set1.size(), size_t { 500 });
+
+ for (int j = 0; j < 500; ++j) {
+ int i = (j * 17) % 500;
+ const auto it = set1.find(QString::number(i));
+ QVERIFY(it != set1.end());
+ QVERIFY(*it == QString::number(i));
+ set1.remove(QString::number(i));
+ QVERIFY(set1.find(QString::number(i)) == set1.end());
+ }
+}
+
void TestTools::set_begin()
{
Set<int> set1;
@@ -1275,6 +1309,40 @@ void TestTools::stringutils_trimmed()
QCOMPARE(trimmed(std::move(a)), std::string("a"));
}
+void TestTools::hash_tuple()
+{
+ using Key = std::tuple<int, int>;
+
+ Key key0{0, 5};
+ Key key1{10, 20};
+ Key key2{30, 40};
+
+ std::unordered_map<Key, int> map;
+ map.insert({key1, 1});
+ map.insert({key2, 2});
+
+ QCOMPARE(map[key0], 0);
+ QCOMPARE(map[key1], 1);
+ QCOMPARE(map[key2], 2);
+}
+
+void TestTools::hash_range()
+{
+ using Key = std::vector<int>;
+
+ Key key0;
+ Key key1{1};
+ Key key2{1, 2};
+
+ std::unordered_map<Key, int> map;
+ map.insert({key1, 1});
+ map.insert({key2, 2});
+
+ QCOMPARE(map[key0], 0);
+ QCOMPARE(map[key1], 1);
+ QCOMPARE(map[key2], 2);
+}
+
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
diff --git a/tests/auto/tools/tst_tools.h b/tests/auto/tools/tst_tools.h
index bd8538be2..619174976 100644
--- a/tests/auto/tools/tst_tools.h
+++ b/tests/auto/tools/tst_tools.h
@@ -79,6 +79,7 @@ private slots:
void set_remove();
void set_contains();
void set_containsSet();
+ void set_find();
void set_begin();
void set_end();
void set_insert();
@@ -99,6 +100,9 @@ private slots:
void stringutils_endsWith();
void stringutils_trimmed();
+ void hash_tuple();
+ void hash_range();
+
private:
QString setupSettingsDir1();
QString setupSettingsDir2();
diff --git a/tests/benchmarker/CMakeLists.txt b/tests/benchmarker/CMakeLists.txt
new file mode 100644
index 000000000..12a6b3076
--- /dev/null
+++ b/tests/benchmarker/CMakeLists.txt
@@ -0,0 +1,18 @@
+set(SOURCES
+ activities.h
+ benchmarker-main.cpp
+ benchmarker.cpp
+ benchmarker.h
+ commandlineparser.cpp
+ commandlineparser.h
+ exception.h
+ runsupport.cpp
+ runsupport.h
+ valgrindrunner.cpp
+ valgrindrunner.h
+ )
+
+add_qbs_app(qbs_benchmarker
+ DEPENDS Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Concurrent
+ SOURCES ${SOURCES}
+ )
diff --git a/tests/benchmarker/benchmarker.cpp b/tests/benchmarker/benchmarker.cpp
index 5ecdbf08b..c6de412a3 100644
--- a/tests/benchmarker/benchmarker.cpp
+++ b/tests/benchmarker/benchmarker.cpp
@@ -32,18 +32,20 @@
#include "valgrindrunner.h"
#include <QtConcurrent/qtconcurrentrun.h>
+#include <QtCore/QCoreApplication>
#include <iostream>
+#include <utility>
namespace qbsBenchmarker {
-Benchmarker::Benchmarker(Activities activities, const QString &oldCommit, const QString &newCommit,
- const QString &testProject, const QString &qbsRepo)
+Benchmarker::Benchmarker(Activities activities, QString oldCommit, QString newCommit,
+ QString testProject, QString qbsRepo)
: m_activities(activities)
- , m_oldCommit(oldCommit)
- , m_newCommit(newCommit)
- , m_testProject(testProject)
- , m_qbsRepo(qbsRepo)
+ , m_oldCommit(std::move(oldCommit))
+ , m_newCommit(std::move(newCommit))
+ , m_testProject(std::move(testProject))
+ , m_qbsRepo(std::move(qbsRepo))
{
}
@@ -76,8 +78,8 @@ void Benchmarker::benchmark()
m_baseOutputDir.path() + "/benchmark-data." + m_oldCommit);
ValgrindRunner newDataRetriever(m_activities, m_testProject, newQbsBuildDir,
m_baseOutputDir.path() + "/benchmark-data." + m_newCommit);
- QFuture<void> oldFuture = QtConcurrent::run(&oldDataRetriever, &ValgrindRunner::run);
- QFuture<void> newFuture = QtConcurrent::run(&newDataRetriever, &ValgrindRunner::run);
+ QFuture<void> oldFuture = QtConcurrent::run([&oldDataRetriever]{ oldDataRetriever.run(); });
+ QFuture<void> newFuture = QtConcurrent::run([&newDataRetriever]{ newDataRetriever.run(); });
oldFuture.waitForFinished();
const auto oldValgrindResults = oldDataRetriever.results();
for (const ValgrindResult &valgrindResult : oldValgrindResults) {
@@ -117,9 +119,15 @@ void Benchmarker::buildQbs(const QString &buildDir) const
{
if (!QDir::root().mkpath(buildDir))
throw Exception(QStringLiteral("Failed to create directory '%1'.").arg(buildDir));
- runProcess(QStringList() << "qmake" << "CONFIG+=force_debug_info"
- << (m_qbsRepo + "/qbs.pro"), buildDir);
- runProcess(QStringList() << "make" << "-s", buildDir);
+ runProcess(QStringList() << QCoreApplication::applicationDirPath() + "/qbs"
+ << "resolve"
+ << "config:benchmarker"
+ << "qbs.buildVariant:profiling"
+ << "qbs.installPrefix:''"
+ << "-f" << m_qbsRepo + "/qbs.qbs", buildDir);
+ runProcess(QStringList() << QCoreApplication::applicationDirPath() + "/qbs"
+ << "build"
+ << "config:benchmarker", buildDir);
}
} // namespace qbsBenchmarker
diff --git a/tests/benchmarker/benchmarker.h b/tests/benchmarker/benchmarker.h
index 9b6d5a157..8c94e7f74 100644
--- a/tests/benchmarker/benchmarker.h
+++ b/tests/benchmarker/benchmarker.h
@@ -32,12 +32,9 @@
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
#include <QtCore/qtemporarydir.h>
-QT_BEGIN_NAMESPACE
-class QStringList;
-QT_END_NAMESPACE
-
namespace qbsBenchmarker {
class BenchmarkResult
@@ -53,8 +50,8 @@ using BenchmarkResults = QHash<Activity, BenchmarkResult>;
class Benchmarker
{
public:
- Benchmarker(Activities activities, const QString &oldCommit, const QString &newCommit,
- const QString &testProject, const QString &qbsRepo);
+ Benchmarker(Activities activities, QString oldCommit, QString newCommit,
+ QString testProject, QString qbsRepo);
~Benchmarker();
void benchmark();
diff --git a/tests/benchmarker/benchmarker.pro b/tests/benchmarker/benchmarker.pro
deleted file mode 100644
index 5342e650c..000000000
--- a/tests/benchmarker/benchmarker.pro
+++ /dev/null
@@ -1,20 +0,0 @@
-TARGET = qbs_benchmarker
-DESTDIR = ../../bin
-CONFIG += console
-CONFIG -= app_bundle
-CONFIG += c++14
-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
index 72b535b42..753359c59 100644
--- a/tests/benchmarker/benchmarker.qbs
+++ b/tests/benchmarker/benchmarker.qbs
@@ -1,10 +1,8 @@
-import qbs
-
QtApplication {
name: "qbs_benchmarker"
type: "application"
consoleApplication: true
- cpp.cxxLanguageVersion: "c++14"
+ cpp.cxxLanguageVersion: "c++17"
condition: Qt.concurrent.present
Depends { name: "qbsbuildconfig" }
Depends {
diff --git a/tests/benchmarker/commandlineparser.cpp b/tests/benchmarker/commandlineparser.cpp
index e725ad0a0..80241b445 100644
--- a/tests/benchmarker/commandlineparser.cpp
+++ b/tests/benchmarker/commandlineparser.cpp
@@ -82,10 +82,14 @@ void CommandLineParser::parse()
}
m_oldCommit = parser.value(oldCommitOption);
m_newCommit = parser.value(newCommitOption);
+ if (m_oldCommit == m_newCommit) {
+ throw Exception(QStringLiteral("Error parsing command line: "
+ "'new commit' and 'old commit' must be different commits.\n%1").arg(parser.helpText()));
+ }
m_testProjectFilePath = parser.value(testProjectOption);
m_qbsRepoDirPath = parser.value(qbsRepoOption);
const QStringList activitiesList = parser.value(activitiesOption).split(',');
- m_activities = 0;
+ m_activities = Activities();
for (const QString &activityString : activitiesList) {
if (activityString == allActivities()) {
m_activities = ActivityResolving | ActivityRuleExecution | ActivityNullBuild;
diff --git a/tests/benchmarker/exception.h b/tests/benchmarker/exception.h
index 216189b70..9fab479ea 100644
--- a/tests/benchmarker/exception.h
+++ b/tests/benchmarker/exception.h
@@ -35,7 +35,7 @@ namespace qbsBenchmarker {
class Exception : public QException {
public:
- explicit Exception(const QString &description) : m_description(description) {}
+ explicit Exception(QString description) : m_description(std::move(description)) {}
~Exception() throw() override = default;
QString description() const { return m_description; }
diff --git a/tests/benchmarker/runsupport.h b/tests/benchmarker/runsupport.h
index 6ee831007..39f485a02 100644
--- a/tests/benchmarker/runsupport.h
+++ b/tests/benchmarker/runsupport.h
@@ -30,10 +30,10 @@
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
QT_BEGIN_NAMESPACE
class QByteArray;
-class QStringList;
QT_END_NAMESPACE
namespace qbsBenchmarker {
diff --git a/tests/benchmarker/valgrindrunner.cpp b/tests/benchmarker/valgrindrunner.cpp
index 344a23516..7509dbb52 100644
--- a/tests/benchmarker/valgrindrunner.cpp
+++ b/tests/benchmarker/valgrindrunner.cpp
@@ -44,11 +44,11 @@
namespace qbsBenchmarker {
-ValgrindRunner::ValgrindRunner(Activities activities, const QString &testProject,
+ValgrindRunner::ValgrindRunner(Activities activities, QString testProject,
const QString &qbsBuildDir, const QString &baseOutputDir)
: m_activities(activities)
- , m_testProject(testProject)
- , m_qbsBinary(qbsBuildDir + "/bin/qbs")
+ , m_testProject(std::move(testProject))
+ , m_qbsBinary(qbsBuildDir + "/benchmarker/install-root/bin/qbs")
, m_baseOutputDir(baseOutputDir)
{
if (!QDir::root().mkpath(m_baseOutputDir))
@@ -59,11 +59,11 @@ void ValgrindRunner::run()
{
std::deque<QFuture<void>> futures;
if (m_activities & ActivityResolving)
- futures.push_back(QtConcurrent::run(this, &ValgrindRunner::traceResolving));
+ futures.push_back(QtConcurrent::run([this]{ traceResolving(); }));
if (m_activities & ActivityRuleExecution)
- futures.push_back(QtConcurrent::run(this, &ValgrindRunner::traceRuleExecution));
+ futures.push_back(QtConcurrent::run([this]{ traceRuleExecution(); }));
if (m_activities & ActivityNullBuild)
- futures.push_back(QtConcurrent::run(this, &ValgrindRunner::traceNullBuild));
+ futures.push_back(QtConcurrent::run([this]{ traceNullBuild(); }));
while (!futures.empty()) {
futures.front().waitForFinished();
futures.pop_front();
@@ -100,12 +100,11 @@ void ValgrindRunner::traceActivity(Activity activity, const QString &buildDirCal
{
QString activityString;
QString qbsCommand;
- bool dryRun;
+ bool dryRun = false;
switch (activity) {
case ActivityResolving:
activityString = "resolving";
qbsCommand = "resolve";
- dryRun = false;
break;
case ActivityRuleExecution:
activityString = "rule-execution";
@@ -115,16 +114,21 @@ void ValgrindRunner::traceActivity(Activity activity, const QString &buildDirCal
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<qint64> callGrindFuture = QtConcurrent::run(this, &ValgrindRunner::runCallgrind,
- qbsCommand, buildDirCallgrind, dryRun, outFileCallgrind);
- QFuture<qint64> massifFuture = QtConcurrent::run(this, &ValgrindRunner::runMassif, qbsCommand,
- buildDirMassif, dryRun, outFileMassif);
+ QFuture<qint64> callGrindFuture = QtConcurrent::run(
+ [this, qbsCommand, buildDirCallgrind, dryRun, outFileCallgrind]
+ {
+ return runCallgrind(qbsCommand, buildDirCallgrind, dryRun, outFileCallgrind);
+ });
+ QFuture<qint64> massifFuture = QtConcurrent::run(
+ [this, qbsCommand, buildDirMassif, dryRun, outFileMassif]
+ {
+ return runMassif(qbsCommand, buildDirMassif, dryRun, outFileMassif);
+ });
callGrindFuture.waitForFinished();
massifFuture.waitForFinished();
addToResults(ValgrindResult(activity, callGrindFuture.result(), massifFuture.result()));
diff --git a/tests/benchmarker/valgrindrunner.h b/tests/benchmarker/valgrindrunner.h
index 3ec82a6a2..c83fea468 100644
--- a/tests/benchmarker/valgrindrunner.h
+++ b/tests/benchmarker/valgrindrunner.h
@@ -30,15 +30,10 @@
#include "activities.h"
-#include <QtCore/qlist.h>
-#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
#include <mutex>
-QT_BEGIN_NAMESPACE
-class QStringList;
-QT_END_NAMESPACE
-
namespace qbsBenchmarker {
class ValgrindResult
@@ -55,7 +50,7 @@ public:
class ValgrindRunner
{
public:
- ValgrindRunner(Activities activities, const QString &testProject, const QString &qbsBuildDir,
+ ValgrindRunner(Activities activities, QString testProject, const QString &qbsBuildDir,
const QString &baseOutputDir);
void run();
diff --git a/tests/clang-format-test/CMakeLists.txt b/tests/clang-format-test/CMakeLists.txt
new file mode 100644
index 000000000..e84b539a7
--- /dev/null
+++ b/tests/clang-format-test/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_qbs_app(clang-format-test
+ DEPENDS Qt${QT_VERSION_MAJOR}::Core
+ SOURCES clang-format.cpp
+)
diff --git a/tests/clang-format-test/clang-format-test.qbs b/tests/clang-format-test/clang-format-test.qbs
new file mode 100644
index 000000000..3c34b5962
--- /dev/null
+++ b/tests/clang-format-test/clang-format-test.qbs
@@ -0,0 +1,5 @@
+QtApplication {
+ Depends { name: "Qt.core" }
+ files: "clang-format.cpp"
+ cpp.warningLevel: "none"
+}
diff --git a/tests/clang-format-test/clang-format.cpp b/tests/clang-format-test/clang-format.cpp
new file mode 100644
index 000000000..3d952f11c
--- /dev/null
+++ b/tests/clang-format-test/clang-format.cpp
@@ -0,0 +1,681 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+//
+// KEEP THIS FILE COMPILABLE TO ENSURE THAT WE TEST AGAINST VALID CODE.
+//
+
+// NOTE: Nice, includes are sorted
+#include <QCoreApplication>
+#include <QFile>
+#include <QLoggingCategory>
+#include <QObject>
+#include <QVector>
+
+#include <functional>
+#include <iostream>
+#include <vector>
+
+static int aGlobalInt = 3;
+
+// -------------------------------------------------------------------------------------------------
+// Preprocessor
+// -------------------------------------------------------------------------------------------------
+
+// NOTE: Ops, preprocessor branches are not indented ("#define" vs "# define")
+#if defined(TEXTEDITOR_LIBRARY)
+#define TEXTEDITOR_EXPORT Q_DECL_EXPORT
+#else
+#define TEXTEDITOR_EXPORT Q_DECL_IMPORT
+#endif
+
+// -------------------------------------------------------------------------------------------------
+// Macros
+// -------------------------------------------------------------------------------------------------
+
+#define QTCREATOR_UTILS_EXPORT
+
+// qtcassert.h:
+namespace Utils {
+void writeAssertLocation(const char *msg)
+{
+ std::printf("%s", msg);
+}
+} // namespace Utils
+
+#define QTC_ASSERT_STRINGIFY_HELPER(x) #x
+#define QTC_ASSERT_STRINGIFY(x) QTC_ASSERT_STRINGIFY_HELPER(x)
+#define QTC_ASSERT_STRING(cond) \
+ ::Utils::writeAssertLocation("\"" cond "\" in file " __FILE__ \
+ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
+
+// NOTE: Ops, macro definitions: are more verbose
+#define QTC_ASSERT(cond, action) \
+ if (cond) { \
+ } else { \
+ QTC_ASSERT_STRING(#cond); \
+ action; \
+ } \
+ do { \
+ } while (0)
+#define QTC_CHECK(cond) \
+ if (cond) { \
+ } else { \
+ QTC_ASSERT_STRING(#cond); \
+ } \
+ do { \
+ } while (0)
+#define QTC_GUARD(cond) ((cond) ? true : (QTC_ASSERT_STRING(#cond), false))
+
+void lala(int foo)
+{
+ Q_UNUSED(foo)
+ Q_UNUSED(foo);
+ QTC_ASSERT(true, return); // NOTE: Ops, extra space with QTC_ASSERT macro and return keyword
+ QTC_ASSERT(true, return;);
+ while (true)
+ QTC_ASSERT(true, break); // ...but this is fine
+}
+
+// -------------------------------------------------------------------------------------------------
+// Namespaces
+// -------------------------------------------------------------------------------------------------
+
+namespace N {
+namespace C {
+
+struct Foo
+{};
+
+struct Bar
+{};
+
+namespace {
+class ClassInUnnamedNamespace
+{};
+} // namespace
+
+// NOTE: Nice, namespace end comments are added/updated automatically
+} // namespace C
+
+struct Baz
+{};
+
+} // namespace N
+namespace N2 {}
+
+// -------------------------------------------------------------------------------------------------
+// Forward declarations
+// -------------------------------------------------------------------------------------------------
+
+// NOTE: Ops, no one-liner anymore: forward declarations within namespace
+namespace N {
+struct Baz;
+}
+
+class SourceLocation;
+class SourceRange;
+class ClangString;
+
+QT_BEGIN_NAMESPACE
+class QString;
+QT_END_NAMESPACE
+
+QT_FORWARD_DECLARE_CLASS(QTextDocument)
+QT_FORWARD_DECLARE_CLASS(QTextBlock);
+
+// -------------------------------------------------------------------------------------------------
+// Using declarations, using directives, using alias, typedefs
+// -------------------------------------------------------------------------------------------------
+
+// NOTE: OK, using directives are not sorted
+using namespace std;
+using namespace N2;
+using namespace N;
+
+// NOTE: Nice, using declarations are sorted
+using N::C::Bar;
+using N::C::Foo;
+
+// NOTE: OK, typedefs are not sorted
+typedef N::C::Foo TypedefedFoor;
+typedef N::C::Bar TypedefedBar;
+
+// NOTE: OK, using aliases are not sorted
+using AliasedFoor = N::C::Foo;
+using AliasedBar = N::C::Bar;
+
+// -------------------------------------------------------------------------------------------------
+// Comments
+// -------------------------------------------------------------------------------------------------
+
+// NOTE: OK, reflowing/wrapping of comments is turned off for now.
+// This is a fancy comment that might be reflowed or not or not or not or not or not or not or not or not or not.
+
+// NOTE: Nice, comments after declarations are aligned
+int foo; // fancy comment describing foo
+int superDuperFoo; // fancy comment describing superDuperFoo
+int lalaFoo; // fancy comment describing lalaFoo
+
+// -------------------------------------------------------------------------------------------------
+// Function declarations
+// -------------------------------------------------------------------------------------------------
+
+void f();
+
+void f2() {}
+
+void f3(int parameter1, int parameter2, int parameter3);
+
+// NOTE: Ops, awkward placement of parameter list, there is no equivalent of
+// PenaltyBreakBeforeFirstCallParameter for parameter list
+void f3(
+ int parameter1, int parameter2, int parameter3, int parameter4, int parameter5, int parameter6);
+
+void f3(
+ int parameter1,
+ int parameter2,
+ int parameter3,
+ int parameter4,
+ int parameter5,
+ int parameter6,
+ int parrameter7,
+ int p = aGlobalInt);
+
+bool operator==(N::Baz, N::Baz);
+
+template<class T>
+void fancyTemplateFunction();
+
+template<typename... Type>
+void variadicTemplateFunction(Type *...arg);
+
+// -------------------------------------------------------------------------------------------------
+// Inside functions
+// -------------------------------------------------------------------------------------------------
+
+int myfunction(int parameter1, int parameter2)
+{
+ // Function calls
+ myfunction(parameter1, parameter2);
+
+ // Casts
+ int value = 3;
+ int z = (int) value;
+ int a = static_cast<int>(value + z);
+
+ // Pointer/references alignment
+ int *p = nullptr;
+ int &r = *p;
+
+ // Operators
+ int result = parameter1 + parameter1 + parameter1 + parameter1 + parameter1 + parameter1
+ + parameter1 + parameter1;
+
+ // Long expressions
+ int someVeryLooooooooooooooooooooooooooooooooooooooooooooooooooongInt = -1;
+ bool condition1 = false;
+ bool condition2 = false;
+ // NOTE: Ops, alignment of readable, boolean and complex expressions will be reverted
+ if (condition1 || condition2
+ || someVeryLooooooooooooooooooooooooooooooooooooooooooooooooooongInt) {
+ return value;
+ }
+
+ // Initializer lists
+ vector<int> x{1, 2, 3, 4};
+ vector<Foo> y{{}, {}, {}, {}};
+ new int[3]{1, 2, 3};
+
+ // Streams
+ // NOTE: OK, there is a heuristic (endl, '\n') to wrap statements with stream operators.
+ cout << "Hello" << parameter1 << endl
+ << parameter2 << endl
+ << result << endl
+ << condition1 << someVeryLooooooooooooooooooooooooooooooooooooooooooooooooooongInt;
+ cout << "Hello" << parameter1 << '\n'
+ << parameter2 << '\n'
+ << a << '\n'
+ << condition1 << someVeryLooooooooooooooooooooooooooooooooooooooooooooooooooongInt;
+ // ...but here not:
+ cout << "Hello" << parameter1 << '\t' << parameter2 << '\t' << z << '\t' << condition1
+ << someVeryLooooooooooooooooooooooooooooooooooooooooooooooooooongInt << r;
+
+ return 1;
+}
+
+// -------------------------------------------------------------------------------------------------
+// Ternary Operator
+// -------------------------------------------------------------------------------------------------
+
+bool ternary()
+{
+ bool someWhatLongerName;
+ bool someWhatLongerName2;
+ bool isThatReallyReallyReallyReallyReallyReallyReallyReallyTrue = false;
+ bool isThatReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyTrue = false;
+
+ bool yesno = true ? true : false;
+ bool sino = isThatReallyReallyReallyReallyReallyReallyReallyReallyTrue ? someWhatLongerName
+ : someWhatLongerName2;
+ bool danet = isThatReallyReallyReallyReallyReallyReallyReallyReallyReallyReallyTrue
+ ? someWhatLongerName
+ : someWhatLongerName2;
+
+ return yesno && sino && danet;
+}
+
+// -------------------------------------------------------------------------------------------------
+// Penalty Test
+// -------------------------------------------------------------------------------------------------
+
+int functionToCall(int parameter1)
+{
+ return 1;
+}
+
+int functionToCall(int paramter1, int parameter2)
+{
+ return 1;
+}
+
+int functionToCall(int paramter1, int parameter2, int parameter3)
+{
+ return 1;
+}
+
+int functionToCall(int paramter1, int parameter2, int parameter3, int parameter4, int parameter5)
+{
+ return 1;
+}
+
+int functionToCall(
+ int paramter1, int parameter2, int parameter3, int parameter4, int parameter5, int paramete6)
+{
+ return 1;
+}
+
+int functionToCall(
+ int paramter1,
+ int parameter2,
+ int parameter3,
+ int parameter4,
+ int parameter5,
+ int paramete6,
+ int parameter6)
+{
+ return 1;
+}
+
+bool functionToCallSt(const QString &paramter1, const QStringList &list)
+{
+ return 1;
+}
+
+void emitAddOutput(const QString &text, int priority) {}
+void emitAddOutput(const QString &text, int priority, int category) {}
+
+void penaltyTests(bool isThatTrue)
+{
+ int valueX = 3;
+ int valueY = 1;
+ int valueZ = 1;
+ int valueXTimesY = valueX * valueY;
+ int unbelievableBigValue = 1000000;
+ int unlimitedValueunbelievableBigValue = 1000000;
+ QString arguments;
+ QString argumentsVeryLong;
+
+ const QFile::OpenMode openMode = isThatTrue ? QIODevice::ReadOnly
+ : (QIODevice::ReadOnly | QIODevice::Text);
+
+ const auto someValue10 = functionToCall(valueX, valueY, valueXTimesY);
+ const auto someValue11 = functionToCall(
+ valueX, valueY, valueXTimesY, unbelievableBigValue, unbelievableBigValue);
+ const auto someValue12 = functionToCall(
+ valueX,
+ valueY,
+ valueXTimesY,
+ unbelievableBigValue,
+ unbelievableBigValue * unbelievableBigValue,
+ unbelievableBigValue);
+
+ const auto someValue13 = functionToCall(
+ valueX,
+ valueY,
+ valueXTimesY,
+ unbelievableBigValue,
+ functionToCall(functionToCall(valueX), functionToCall(valueY)),
+ unbelievableBigValue);
+
+ const auto someValue14WithAnOutstandingLongName = functionToCall(
+ valueX,
+ valueY,
+ valueXTimesY,
+ unbelievableBigValue,
+ functionToCall(functionToCall(valueX), functionToCall(valueY)),
+ unbelievableBigValue);
+
+ const bool someValue20 = functionToCall(valueX, valueY, valueXTimesY) || functionToCall(3);
+ const bool someValue21 = functionToCall(valueX, valueY, valueXTimesY)
+ || functionToCall(valueX, valueY);
+
+ emitAddOutput(
+ QCoreApplication::tr("Starting: \"%1\" %2")
+ .arg("/some/very/very/very/very/long/path/to/an/executable", arguments),
+ functionToCall(3),
+ functionToCall(3) | valueX);
+
+ emitAddOutput(
+ QCoreApplication::tr("Starting: \"%1\" %2")
+ .arg("/some/very/very/very/very/long/path/to/an/executable", argumentsVeryLong),
+ functionToCall(3),
+ functionToCall(3) | unlimitedValueunbelievableBigValue
+ | unlimitedValueunbelievableBigValue);
+
+ const QString path;
+ const bool someLongerNameNNNNNNNNNN = functionToCallSt(
+ path, QStringList(QLatin1String("-print-env")));
+}
+
+// -------------------------------------------------------------------------------------------------
+// Classes and member functions
+// -------------------------------------------------------------------------------------------------
+
+class Base1
+{};
+class Base2
+{};
+
+class MyClass : public QObject, public Base1, public Base2
+{
+ Q_OBJECT
+
+public:
+ friend class FriendX; // for X::foo()
+ friend class OtherFriendY; // for Y::bar()
+
+ MyClass() {}
+
+ MyClass(int d1)
+ : data1(d1)
+ {}
+
+ MyClass(int d1, int d2)
+ : data1(d1)
+ , data2(d2)
+ , data3(d2)
+ {}
+
+ MyClass(
+ int initialData1,
+ int initialData2,
+ int initialData3,
+ int initialData4,
+ int initialData5,
+ int initialData6)
+ : data1(initialData1)
+ , data2(initialData2)
+ , data3(initialData3)
+ , data4(initialData4)
+ , data5(initialData5)
+ , data6(initialData6)
+ {
+ // NOTE: Ops, manual alignment of QObject::connect() arguments is reverted
+ // (we tend to write emitter/receiver on separate lines)
+ connect(this, &MyClass::inlineZeroStatements, this, &MyClass::nonInlineSingleStatement);
+ }
+
+ void inlineZeroStatements() {}
+ void inlineSingleStatement() { aGlobalInt = 2; }
+ void inlineMultipleStatements()
+ {
+ aGlobalInt = 2;
+ aGlobalInt = 3;
+ }
+
+ void nonInlineZeroStatements();
+ void nonInlineSingleStatement();
+ void nonInlineMultipleStatements();
+
+ virtual void shortVirtual(int parameter1, int parameter2, int parameter3) = 0;
+
+ // NOTE: Ops, awkward placement of "= 0;"
+ virtual void longerVirtual(
+ int parameter1, int parameter2, int parameter3, int parameter4, int parameter5)
+ = 0;
+
+ int someGetter() const;
+
+private:
+ int data1 = 0;
+ int data2 = 0;
+ int data3 = 0;
+ int data4 = 0;
+ int data5 = 0;
+ int data6 = 0;
+};
+
+void MyClass::nonInlineZeroStatements() {}
+
+void MyClass::nonInlineSingleStatement()
+{
+ aGlobalInt = 2;
+}
+
+void MyClass::nonInlineMultipleStatements()
+{
+ aGlobalInt = 2;
+ aGlobalInt = 2;
+}
+
+template<class T>
+class TemplateClass
+{};
+
+// -------------------------------------------------------------------------------------------------
+// Enums
+// -------------------------------------------------------------------------------------------------
+
+// NOTE: OK, enums: one-liner are possible
+enum MyEnum { SourceFiles = 0x1, GeneratedFiles = 0x2, AllFiles = SourceFiles | GeneratedFiles };
+
+// NOTE: OK, enums: a comment breaks one-liners
+enum MyEnumX {
+ OSourceFiles = 0x1 // fancy
+};
+
+// NOTE: OK, enums: longer ones are wrapped
+enum MyOtherEnum {
+ XSourceFiles = 0x1,
+ XGeneratedFiles = 0x2,
+ XAllFiles = XSourceFiles | XGeneratedFiles
+};
+
+enum {
+ Match_None,
+ Match_TooManyArgs,
+ Match_TooFewArgs,
+ Match_Ok
+} matchType
+ = Match_None; // NOTE: Ops, awkward placement of "= value;" at enum definition+use
+
+// -------------------------------------------------------------------------------------------------
+// Lambdas
+// -------------------------------------------------------------------------------------------------
+
+void takeLambda(std::function<int()>) {}
+void takeLambdaWithSomeLongerNameHa(std::function<int()>) {}
+bool UtilsFiltered(QVector<N::Baz>, std::function<int(const N::Baz &)>)
+{
+ return true;
+}
+
+void lambda()
+{
+ auto shortLambda = []() { return 1; };
+ auto longLambda = []() {
+ aGlobalInt = 3;
+ return;
+ };
+
+ takeLambda([]() { return true; });
+ takeLambda([]() {
+ aGlobalInt = 3;
+ return aGlobalInt;
+ });
+
+ // NOTE: Ops, lambda: capture and parameter list should be on separate line
+ int thisCouldBeSomeLongerFunctionCallExpressionOrSoSoSo;
+ takeLambdaWithSomeLongerNameHa(
+ [&]() { return thisCouldBeSomeLongerFunctionCallExpressionOrSoSoSo; });
+
+ QVector<N::Baz> myClasses;
+ UtilsFiltered(myClasses, [](const N::Baz &) { return 1; });
+
+ // NOTE: Ops, lambda: lambda not started on new line in if-condition
+ if (UtilsFiltered(myClasses, [](const N::Baz &) { return 1; })) {
+ ++aGlobalInt;
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+// Control flow
+// -------------------------------------------------------------------------------------------------
+
+int fn(int, int, int)
+{
+ return 1;
+}
+
+void controlFlow(int x)
+{
+ int y = -1;
+
+ // if
+ if (true)
+ fn(1, 2, 3);
+
+ int value = 3;
+ if (value) {
+ value += value + 1;
+ --value;
+ }
+
+ if (x == y)
+ fn(1, 2, 3);
+ else if (x > y)
+ fn(1, 2, 3);
+ else
+ fn(1, 2, 3);
+
+ if (x == y) {
+ fn(1, 2, 3);
+ return;
+ } else if (x > y) {
+ fn(1, 2, 3);
+ } else {
+ fn(1, 2, 3);
+ }
+
+ // switch
+ switch (x) {
+ case 0:
+ ++aGlobalInt;
+ return;
+ case 1:
+ fn(1, 2, 3);
+ ++aGlobalInt;
+ break;
+ case 2:
+ fn(1, 2, 3);
+ break;
+ default:
+ break;
+ }
+
+ // do-while
+ do {
+ value += value + 1;
+ ++value;
+ } while (true);
+
+ // for
+ QVector<int> myVector;
+ for (int i = 0; i < myVector.size(); ++i)
+ ++aGlobalInt;
+
+ for (QVector<int>::const_iterator i = myVector.begin(); i != myVector.end(); ++i)
+ ++aGlobalInt;
+
+ QVector<int> myVectorWithLongName;
+ for (QVector<int>::const_iterator i = myVectorWithLongName.begin(),
+ ei = myVectorWithLongName.end();
+ i != ei;
+ ++i) {
+ }
+
+ forever {
+ ++aGlobalInt;
+ --aGlobalInt;
+ }
+}
+
+// -------------------------------------------------------------------------------------------------
+// Function declarations and calls - extreme cases
+// -------------------------------------------------------------------------------------------------
+
+void extremeFunction(const char[]) {}
+void extremeFunction(
+ int uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunbelievableLongParameter)
+{
+ extremeFunction(
+ uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunbelievableLongParameter);
+
+ int uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunbelievableLongValue
+ = 3;
+ ++uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunbelievableLongValue;
+
+ extremeFunction(
+ "some super duper super duper super duper super duper super duper super duper long");
+
+ // BreakStringLiterals splits the string.
+ extremeFunction("some super duper super duper super duper super duper super duper super duper "
+ "super duper long");
+}
+
+void extremeFunction2(
+ int parameter1,
+ int parameter2,
+ int parameter3WithAVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVerVeryVeryLong)
+{
+ extremeFunction2(
+ parameter1,
+ parameter2,
+ parameter3WithAVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVerVeryVeryLong);
+}
+
+void extremeFunction3(
+ int parameter1,
+ int parameter2,
+ int parameter3WithAVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVerVeryVeryLongNameX)
+{
+ extremeFunction3(
+ parameter1,
+ parameter2,
+ parameter3WithAVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVerVeryVeryLongNameX);
+}
+
+// -------------------------------------------------------------------------------------------------
+// Misc
+// -------------------------------------------------------------------------------------------------
+
+static Q_LOGGING_CATEGORY(log, "qtc.cpptools.builtineditordocumentprocessor", QtWarningMsg)
+
+ int hello; // NOTE: Ops, awkward placement of next token after Q_LOGGING_CATEGORY (semicolon helps)
+
+// -------------------------------------------------------------------------------------------------
+
+int main() {}
+
+#include "clang-format.moc"
diff --git a/tests/fuzzy-test/CMakeLists.txt b/tests/fuzzy-test/CMakeLists.txt
new file mode 100644
index 000000000..43101f68b
--- /dev/null
+++ b/tests/fuzzy-test/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(SOURCES
+ commandlineparser.cpp
+ commandlineparser.h
+ fuzzytester.cpp
+ fuzzytester.h
+ main.cpp
+ )
+
+add_qbs_app(qbs_fuzzy-test
+ DEPENDS Qt${QT_VERSION_MAJOR}::Core
+ SOURCES ${SOURCES}
+ )
diff --git a/tests/fuzzy-test/commandlineparser.h b/tests/fuzzy-test/commandlineparser.h
index 6dcbf84bc..76b49331b 100644
--- a/tests/fuzzy-test/commandlineparser.h
+++ b/tests/fuzzy-test/commandlineparser.h
@@ -35,7 +35,7 @@
class ParseException : public std::exception
{
public:
- ParseException(const QString &error) : errorMessage(error) { }
+ ParseException(QString error) : errorMessage(std::move(error)) { }
~ParseException() throw() override = default;
QString errorMessage;
diff --git a/tests/fuzzy-test/fuzzy-test.pro b/tests/fuzzy-test/fuzzy-test.pro
deleted file mode 100644
index 97083ef7a..000000000
--- a/tests/fuzzy-test/fuzzy-test.pro
+++ /dev/null
@@ -1,11 +0,0 @@
-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
index 539374deb..cfa31363c 100644
--- a/tests/fuzzy-test/fuzzy-test.qbs
+++ b/tests/fuzzy-test/fuzzy-test.qbs
@@ -1,11 +1,9 @@
-import qbs
-
QtApplication {
name: "qbs_fuzzy-test"
type: "application"
consoleApplication: true
Depends { name: "qbsbuildconfig" }
- cpp.cxxLanguageVersion: "c++14"
+ cpp.cxxLanguageVersion: "c++17"
files: [
"commandlineparser.cpp",
"commandlineparser.h",
diff --git a/tests/fuzzy-test/fuzzytester.cpp b/tests/fuzzy-test/fuzzytester.cpp
index 045e46718..e601e006f 100644
--- a/tests/fuzzy-test/fuzzytester.cpp
+++ b/tests/fuzzy-test/fuzzytester.cpp
@@ -67,7 +67,7 @@ void FuzzyTester::runTest(const QString &profile, const QString &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.size());
+ int(allCommits.size()));
// Shuffle the initial sequence. Otherwise all invocations of the tool with the same start
// commit would try the same sequence of commits.
@@ -94,7 +94,7 @@ void FuzzyTester::runTest(const QString &profile, const QString &startCommit,
m_currentCommit = currentCommit;
buildSequence << currentCommit;
checkoutCommit(currentCommit);
- qDebug("Testing incremental build #%d (%s)", buildSequence.size() - 1,
+ qDebug("Testing incremental build #%d (%s)", int(buildSequence.size()) - 1,
qPrintable(currentCommit));
// Doing "resolve" and "build" separately introduces additional possibilities
@@ -151,7 +151,7 @@ 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);
+ return allCommitsString.simplified().split(QLatin1Char(' '), Qt::SkipEmptyParts);
}
QString FuzzyTester::findWorkingStartCommit(const QString &startCommit)
diff --git a/tests/fuzzy-test/fuzzytester.h b/tests/fuzzy-test/fuzzytester.h
index bc1fc5f18..2d0279e1a 100644
--- a/tests/fuzzy-test/fuzzytester.h
+++ b/tests/fuzzy-test/fuzzytester.h
@@ -35,7 +35,7 @@
class TestError {
public:
- TestError(const QString &errorMessage) : errorMessage(errorMessage) {}
+ TestError(QString errorMessage) : errorMessage(std::move(errorMessage)) {}
~TestError() throw() = default;
QString errorMessage;
diff --git a/tests/lspclient/CMakeLists.txt b/tests/lspclient/CMakeLists.txt
new file mode 100644
index 000000000..ba6f3dbdc
--- /dev/null
+++ b/tests/lspclient/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_qbs_app(qbs_lspclient
+ DEPENDS
+ Qt${QT_VERSION_MAJOR}::Core
+ Qt${QT_VERSION_MAJOR}::Network
+ qbscore
+ qtclsp
+ SOURCES lspclient.cpp
+ )
diff --git a/tests/lspclient/lspclient.cpp b/tests/lspclient/lspclient.cpp
new file mode 100644
index 000000000..40adc9288
--- /dev/null
+++ b/tests/lspclient/lspclient.cpp
@@ -0,0 +1,434 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qbs.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** 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 <lsp/clientcapabilities.h>
+#include <lsp/completion.h>
+#include <lsp/initializemessages.h>
+#include <lsp/languagefeatures.h>
+#include <lsp/textsynchronization.h>
+
+#include <QBuffer>
+#include <QCommandLineOption>
+#include <QCommandLineParser>
+#include <QCoreApplication>
+#include <QLocalSocket>
+
+#include <cstdlib>
+#include <iostream>
+
+enum class Command {
+ GotoDefinition,
+ Completion,
+};
+
+class LspClient : public QObject
+{
+public:
+ LspClient(Command command, const QString &socketPath, const QString &codeToInsert,
+ int insertLine, int insertColumn,
+ const QString &filePath, int line, int column);
+ void start();
+
+private:
+ void finishWithError(const QString &msg);
+ void exit(int code);
+ void initiateProtocol();
+ void handleIncomingData();
+ void sendMessage(const lsp::JsonObject &msg);
+ void sendMessage(const lsp::JsonRpcMessage &msg);
+ void handleCurrentMessage();
+ void handleInitializeReply();
+ void openDocument();
+ void insertCode();
+ void sendRequest();
+ void handleResponse();
+ void sendGotoDefinitionRequest();
+ void handleGotoDefinitionResponse();
+ void sendCompletionRequest();
+ void handleCompletionResponse();
+ lsp::DocumentUri uri() const;
+ lsp::DocumentUri::PathMapper mapper() const;
+
+ const Command m_command;
+ const QString m_socketPath;
+ const QString m_codeToInsert;
+ const int m_insertLine;
+ const int m_insertColumn;
+ const QString m_filePath;
+ int m_line;
+ int m_column;
+ QLocalSocket m_socket;
+ QBuffer m_incomingData;
+ lsp::BaseMessage m_currentMessage;
+ QJsonObject m_messageObject;
+
+ enum class State { Inactive, Connecting, Initializing, RunningCommand }
+ m_state = State::Inactive;
+};
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ const QCommandLineOption socketOption({"socket", "s"}, "The server socket to connect to.",
+ "socket");
+ const QCommandLineOption gotoDefinitionOption(
+ {"goto-def", "g"}, "Go to definition from the specified location.");
+ const QCommandLineOption completionOption(
+ {"completion", "c"}, "Request completion at the specified location.");
+ const QCommandLineOption insertCodeOption("insert-code",
+ "A piece of code to insert before doing the actual "
+ "operation.",
+ "code");
+ const QCommandLineOption insertLocationOption("insert-location",
+ "The location at which to insert the code.",
+ "<line>:<column>");
+ QCommandLineParser parser;
+ parser.addOptions(
+ {socketOption,
+ insertCodeOption,
+ insertLocationOption,
+ gotoDefinitionOption,
+ completionOption});
+ parser.addHelpOption();
+ parser.addPositionalArgument("location", "The location at which to operate.",
+ "<file>:<line>:<column>");
+ parser.process(app);
+
+ const auto complainAndExit = [&](const char *text) {
+ std::cerr << text << std::endl;
+ parser.showHelp(EXIT_FAILURE);
+ };
+
+ if (!parser.isSet(socketOption))
+ complainAndExit("Socket must be specified.");
+
+ // Initialized to suppress warning. TODO: In C++23, mark lambdas as noreturn instead.
+ Command command = Command::GotoDefinition;
+
+ if (parser.isSet(gotoDefinitionOption))
+ command = Command::GotoDefinition;
+ else if (parser.isSet(completionOption))
+ command = Command::Completion;
+ else
+ complainAndExit("Don't know what to do.");
+
+ if (parser.positionalArguments().size() != 1)
+ complainAndExit("Need location.");
+ const auto complainAboutLocationString = [&] { complainAndExit("Invalid location."); };
+ const QString loc = parser.positionalArguments().first();
+ int sep1 = loc.indexOf(':');
+ if (sep1 <= 0)
+ complainAboutLocationString();
+ if (qbs::Internal::HostOsInfo::isWindowsHost()) {
+ sep1 = loc.indexOf(':', sep1 + 1);
+ if (sep1 < 0)
+ complainAboutLocationString();
+ }
+ const int sep2 = loc.indexOf(':', sep1 + 1);
+ if (sep2 == -1 || sep2 == loc.size() - 1)
+ complainAboutLocationString();
+ const auto extractNumber = [&](const QString &s) {
+ bool ok;
+ const int n = s.toInt(&ok);
+ if (!ok || n <= 0)
+ complainAboutLocationString();
+ return n;
+ };
+ const int line = extractNumber(loc.mid(sep1 + 1, sep2 - sep1 - 1));
+ const int column = extractNumber(loc.mid(sep2 + 1));
+
+ const QString insertLoc = parser.value(insertLocationOption);
+ int insertLine = -1;
+ int insertColumn = -1;
+ if (insertLoc.isEmpty()) {
+ insertLine = line;
+ insertColumn = column;
+ } else {
+ const int sep = insertLoc.indexOf(':');
+ if (sep <= 0)
+ complainAboutLocationString();
+ insertLine = extractNumber(insertLoc.left(sep));
+ insertColumn = extractNumber(insertLoc.mid(sep + 1));
+ }
+
+ LspClient client(command, parser.value(socketOption), parser.value(insertCodeOption),
+ insertLine, insertColumn,
+ QDir::fromNativeSeparators(loc.left(sep1)), line, column);
+ QMetaObject::invokeMethod(&client, &LspClient::start, Qt::QueuedConnection);
+
+ return app.exec();
+}
+
+LspClient::LspClient(Command command, const QString &socketPath, const QString &codeToInsert,
+ int insertLine, int insertColumn,
+ const QString &filePath, int line, int column)
+ : m_command(command), m_socketPath(socketPath), m_codeToInsert(codeToInsert),
+ m_insertLine(insertLine), m_insertColumn(insertColumn),
+ m_filePath(filePath), m_line(line), m_column(column)
+{
+ connect(&m_socket, &QLocalSocket::disconnected, this, [this] {
+ finishWithError("Server disconnected unexpectedly.");
+ });
+ connect(&m_socket, &QLocalSocket::errorOccurred, this, [this] {
+ finishWithError(QString::fromLatin1("Socket error: %1").arg(m_socket.errorString()));
+ });
+ connect(&m_socket, &QLocalSocket::connected, this, &LspClient::initiateProtocol);
+ connect(&m_socket, &QLocalSocket::readyRead, this, &LspClient::handleIncomingData);
+}
+
+void LspClient::start()
+{
+ m_state = State::Connecting;
+ m_incomingData.open(QIODevice::ReadWrite | QIODevice::Append);
+ m_socket.connectToServer(m_socketPath);
+}
+
+void LspClient::finishWithError(const QString &msg)
+{
+ std::cerr << qPrintable(msg) << std::endl;
+ m_socket.disconnectFromServer();
+ exit(EXIT_FAILURE);
+}
+
+void LspClient::exit(int code)
+{
+ m_socket.disconnect(this);
+ qApp->exit(code);
+}
+
+void LspClient::initiateProtocol()
+{
+ if (m_state != State::Connecting) {
+ finishWithError(QString::fromLatin1("State should be %1, was %2.")
+ .arg(int(State::Connecting), int(m_state)));
+ return;
+ }
+ m_state = State::Initializing;
+
+ lsp::DynamicRegistrationCapabilities definitionCaps;
+ definitionCaps.setDynamicRegistration(false);
+ lsp::TextDocumentClientCapabilities docCaps;
+ docCaps.setDefinition(definitionCaps);
+ lsp::ClientCapabilities clientCaps;
+ clientCaps.setTextDocument(docCaps);
+ lsp::InitializeParams initParams;
+ initParams.setCapabilities(clientCaps);
+ sendMessage(lsp::InitializeRequest(initParams));
+}
+
+void LspClient::handleIncomingData()
+{
+ const int pos = m_incomingData.pos();
+ m_incomingData.write(m_socket.readAll());
+ m_incomingData.seek(pos);
+ QString parseError;
+ lsp::BaseMessage::parse(&m_incomingData, parseError, m_currentMessage);
+ if (!parseError.isEmpty()) {
+ return finishWithError(QString::fromLatin1("Error parsing server message: %1.")
+ .arg(parseError));
+ }
+ if (m_currentMessage.isComplete()) {
+ m_incomingData.buffer().remove(0, m_incomingData.pos());
+ m_incomingData.seek(0);
+ handleCurrentMessage();
+ m_currentMessage = {};
+ m_messageObject = {};
+ if (m_socket.state() == QLocalSocket::ConnectedState)
+ handleIncomingData();
+ }
+}
+
+void LspClient::sendMessage(const lsp::JsonObject &msg)
+{
+ sendMessage(lsp::JsonRpcMessage(msg));
+}
+
+void LspClient::sendMessage(const lsp::JsonRpcMessage &msg)
+{
+ lsp::BaseMessage baseMsg = msg.toBaseMessage();
+ m_socket.write(baseMsg.header());
+ m_socket.write(baseMsg.content);
+}
+
+void LspClient::handleCurrentMessage()
+{
+ m_messageObject = lsp::JsonRpcMessage(m_currentMessage).toJsonObject();
+ switch (m_state) {
+ case State::Inactive:
+ case State::Connecting:
+ finishWithError("Received message in non-connected state.");
+ break;
+ case State::Initializing:
+ handleInitializeReply();
+ openDocument();
+ insertCode();
+ sendRequest();
+ break;
+ case State::RunningCommand:
+ handleResponse();
+ break;
+ }
+}
+
+void LspClient::handleInitializeReply()
+{
+ lsp::ServerCapabilities serverCaps = lsp::InitializeRequest::Response(
+ m_messageObject).result().value_or(lsp::InitializeResult()).capabilities();
+ const auto defProvider = serverCaps.definitionProvider();
+ if (!defProvider)
+ return finishWithError("Expected definition provider.");
+ const bool * const defProviderValue = std::get_if<bool>(&(*defProvider));
+ if (!defProviderValue || !*defProviderValue)
+ return finishWithError("Expected definition provider.");
+ sendMessage(lsp::InitializeNotification(lsp::InitializedParams()));
+}
+
+void LspClient::openDocument()
+{
+ QFile file(m_filePath);
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return finishWithError(QString::fromLatin1("Could not open '%1': %2")
+ .arg(m_filePath, file.errorString()));
+ }
+ lsp::TextDocumentItem item;
+ item.setUri(uri());
+ item.setVersion(0);
+ item.setText(QString::fromUtf8(file.readAll()));
+ item.setLanguageId("application/x-qt.qbs+qml");
+ sendMessage(lsp::DidOpenTextDocumentNotification(lsp::DidOpenTextDocumentParams(item)));
+}
+
+void LspClient::insertCode()
+{
+ if (m_codeToInsert.isEmpty())
+ return;
+
+ lsp::VersionedTextDocumentIdentifier docId;
+ docId.setUri(uri());
+ docId.setVersion(1);
+ lsp::DidChangeTextDocumentParams params(docId);
+ lsp::DidChangeTextDocumentParams::TextDocumentContentChangeEvent change;
+ const lsp::Position insertPos(m_insertLine - 1, m_insertColumn- 1);
+ change.setRange({insertPos, insertPos});
+ change.setText(m_codeToInsert);
+ params.setContentChanges({change});
+ sendMessage(lsp::DidChangeTextDocumentNotification(params));
+
+ if (m_insertLine > m_line || (m_insertLine == m_line && m_insertColumn > m_column))
+ return;
+
+ const int newlineCount = m_codeToInsert.count('\n');
+ m_line += newlineCount;
+ m_column += m_codeToInsert.size() - (newlineCount == 0 ? 0 : m_codeToInsert.lastIndexOf('\n'));
+}
+
+void LspClient::sendRequest()
+{
+ m_state = State::RunningCommand;
+ switch (m_command) {
+ case Command::GotoDefinition:
+ return sendGotoDefinitionRequest();
+ case Command::Completion:
+ return sendCompletionRequest();
+ }
+}
+
+void LspClient::handleResponse()
+{
+ const QString error = m_messageObject.value(lsp::errorKey).toObject()
+ .value("message").toString();
+ if (!error.isEmpty())
+ return finishWithError(error);
+
+ switch (m_command) {
+ case Command::GotoDefinition:
+ return handleGotoDefinitionResponse();
+ case Command::Completion:
+ return handleCompletionResponse();
+ }
+}
+
+void LspClient::sendGotoDefinitionRequest()
+{
+ const lsp::TextDocumentIdentifier doc(uri());
+ const lsp::Position pos(m_line - 1, m_column - 1);
+ sendMessage(lsp::GotoDefinitionRequest({doc, pos}));
+}
+
+void LspClient::handleGotoDefinitionResponse()
+{
+ const lsp::GotoResult result(lsp::GotoDefinitionRequest::Response(m_messageObject)
+ .result().value_or(lsp::GotoResult()));
+ QList<lsp::Utils::Link> links;
+ const auto loc2Link = [this](const lsp::Location &loc) { return loc.toLink(mapper()); };
+ if (const auto loc = std::get_if<lsp::Location>(&result)) {
+ links << loc2Link(*loc);
+ } else if (const auto locs = std::get_if<QList<lsp::Location>>(&result)) {
+ links = lsp::Utils::transform(*locs, loc2Link);
+ }
+ for (const lsp::Utils::Link &link : std::as_const(links)) {
+ std::cout << qPrintable(link.targetFilePath) << ':' << link.targetLine << ':'
+ << (link.targetColumn + 1) << std::endl;
+ }
+ exit(EXIT_SUCCESS);
+}
+
+void LspClient::sendCompletionRequest()
+{
+ const lsp::TextDocumentIdentifier doc(uri());
+ const lsp::Position pos(m_line - 1, m_column - 1);
+ sendMessage(lsp::CompletionRequest({doc, pos}));
+}
+
+void LspClient::handleCompletionResponse()
+{
+ const lsp::CompletionResult result(lsp::CompletionRequest::Response(m_messageObject)
+ .result()
+ .value_or(lsp::CompletionResult()));
+ if (const auto items = std::get_if<QList<lsp::CompletionItem>>(&result)) {
+ for (const lsp::CompletionItem &item : *items) {
+ std::cout << qPrintable(item.label());
+ if (item.detail())
+ std::cout << ' ' << qPrintable(*item.detail());
+ std::cout << std::endl;
+ }
+ }
+ exit(EXIT_SUCCESS);
+}
+
+lsp::DocumentUri LspClient::uri() const
+{
+ return lsp::DocumentUri::fromFilePath(lsp::Utils::FilePath::fromUserInput(m_filePath),
+ mapper());
+}
+
+lsp::DocumentUri::PathMapper LspClient::mapper() const
+{
+ return [](const lsp::Utils::FilePath &fp) { return fp; };
+}
diff --git a/tests/lspclient/lspclient.qbs b/tests/lspclient/lspclient.qbs
new file mode 100644
index 000000000..c89b9e0c0
--- /dev/null
+++ b/tests/lspclient/lspclient.qbs
@@ -0,0 +1,10 @@
+QbsApp {
+ name: "qbs_lspclient"
+
+ Depends { name: "qtclsp" }
+ Depends { name: "Qt.network" }
+
+ cpp.defines: base.filter(function(d) { return d !== "QT_NO_CAST_FROM_ASCII"; })
+
+ files: "lspclient.cpp"
+}
diff --git a/tests/tests.pro b/tests/tests.pro
deleted file mode 100644
index e454771e4..000000000
--- a/tests/tests.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS = auto fuzzy-test
-
-qtHaveModule(concurrent): SUBDIRS += benchmarker
diff --git a/tests/tests.qbs b/tests/tests.qbs
index 52eafb3f0..3478ce8aa 100644
--- a/tests/tests.qbs
+++ b/tests/tests.qbs
@@ -1,11 +1,12 @@
-import qbs
import qbs.FileInfo
Project {
references: [
"auto/auto.qbs",
"benchmarker/benchmarker.qbs",
+ "clang-format-test/clang-format-test.qbs",
"fuzzy-test/fuzzy-test.qbs",
+ "lspclient/lspclient.qbs",
]
AutotestRunner {
diff --git a/tutorial/LICENSE b/tutorial/LICENSE
new file mode 100644
index 000000000..d7cbbf160
--- /dev/null
+++ b/tutorial/LICENSE
@@ -0,0 +1,49 @@
+/****************************************************************************
+**
+** Copyright (C) 2023 Ivan Komissarov (abbapoh@gmail.com)
+** Contact: https://www.qt.io/licensing/
+**
+** Files in this directory are part of the Qbs tutorial.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use these files in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing 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 these files 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$
+**
+****************************************************************************/ \ No newline at end of file
diff --git a/tutorial/chapter-1/app/app.qbs b/tutorial/chapter-1/app/app.qbs
new file mode 100644
index 000000000..8d0831dc1
--- /dev/null
+++ b/tutorial/chapter-1/app/app.qbs
@@ -0,0 +1,12 @@
+//! [0]
+CppApplication {
+ name: "My Application"
+ targetName: "myapp"
+ files: "main.c"
+ version: "1.0.0"
+
+ consoleApplication: true
+ install: true
+ installDebugInformation: true
+}
+//! [0]
diff --git a/tutorial/chapter-1/app/main.c b/tutorial/chapter-1/app/main.c
new file mode 100644
index 000000000..3f3560d25
--- /dev/null
+++ b/tutorial/chapter-1/app/main.c
@@ -0,0 +1,9 @@
+//! [0]
+#include <stdio.h>
+
+int main()
+{
+ printf("Hello, world\n");
+ return 0;
+}
+//! [0]
diff --git a/tutorial/chapter-1/myproject.qbs b/tutorial/chapter-1/myproject.qbs
new file mode 100644
index 000000000..b936f3738
--- /dev/null
+++ b/tutorial/chapter-1/myproject.qbs
@@ -0,0 +1,9 @@
+//! [0]
+Project {
+ name: "My Project"
+ minimumQbsVersion: "2.0"
+ references: [
+ "app/app.qbs"
+ ]
+}
+//! [0]
diff --git a/tutorial/chapter-2/app/app.qbs b/tutorial/chapter-2/app/app.qbs
new file mode 100644
index 000000000..a3ee757ab
--- /dev/null
+++ b/tutorial/chapter-2/app/app.qbs
@@ -0,0 +1,11 @@
+CppApplication {
+ Depends { name: "mylib" }
+ name: "My Application"
+ targetName: "myapp"
+ files: "main.c"
+ version: "1.0.0"
+
+ consoleApplication: true
+ install: true
+ installDebugInformation: true
+}
diff --git a/tutorial/chapter-2/app/main.c b/tutorial/chapter-2/app/main.c
new file mode 100644
index 000000000..a560d1ba1
--- /dev/null
+++ b/tutorial/chapter-2/app/main.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+#include "lib.h"
+
+int main()
+{
+ printf("Hello, world\n");
+ printf("%s\n", get_string());
+ return 0;
+}
diff --git a/tutorial/chapter-2/lib/lib.c b/tutorial/chapter-2/lib/lib.c
new file mode 100644
index 000000000..9fe020ba2
--- /dev/null
+++ b/tutorial/chapter-2/lib/lib.c
@@ -0,0 +1,15 @@
+//! [0]
+
+// lib/lib.cpp
+#include "lib.h"
+
+#ifndef CRUCIAL_DEFINE
+# error CRUCIAL_DEFINE not defined
+#endif
+
+const char *get_string()
+{
+ return "Hello from library";
+}
+
+//! [0]
diff --git a/tutorial/chapter-2/lib/lib.h b/tutorial/chapter-2/lib/lib.h
new file mode 100644
index 000000000..efc56ae0b
--- /dev/null
+++ b/tutorial/chapter-2/lib/lib.h
@@ -0,0 +1,11 @@
+//! [0]
+
+// lib/lib.h
+#ifndef LIB_H
+#define LIB_H
+
+const char *get_string();
+
+#endif // LIB_H
+
+//! [0]
diff --git a/tutorial/chapter-2/lib/lib.qbs b/tutorial/chapter-2/lib/lib.qbs
new file mode 100644
index 000000000..378d855a6
--- /dev/null
+++ b/tutorial/chapter-2/lib/lib.qbs
@@ -0,0 +1,28 @@
+//! [0]
+StaticLibrary {
+ name: "mylib"
+ files: [
+ "lib.c",
+ "lib.h",
+ ]
+ version: "1.0.0"
+ install: true
+
+ //! [1]
+ Depends { name: 'cpp' }
+ cpp.defines: ['CRUCIAL_DEFINE']
+ //! [1]
+
+ //! [2]
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ }
+ //! [2]
+
+ //! [3]
+ Depends { name: 'bundle' }
+ bundle.isBundle: false
+ //! [3]
+}
+//! [0]
diff --git a/tutorial/chapter-2/myproject.qbs b/tutorial/chapter-2/myproject.qbs
new file mode 100644
index 000000000..17ef04ac9
--- /dev/null
+++ b/tutorial/chapter-2/myproject.qbs
@@ -0,0 +1,10 @@
+//! [0]
+Project {
+ name: "My Project"
+ minimumQbsVersion: "2.0"
+ references: [
+ "app/app.qbs",
+ "lib/lib.qbs"
+ ]
+}
+//! [0]
diff --git a/tutorial/chapter-3/app/app.qbs b/tutorial/chapter-3/app/app.qbs
new file mode 100644
index 000000000..bb7108581
--- /dev/null
+++ b/tutorial/chapter-3/app/app.qbs
@@ -0,0 +1,29 @@
+//! [1]
+// app/app.qbs
+import qbs.FileInfo
+
+CppApplication {
+ Depends { name: "mylib" }
+ name: "My Application"
+ targetName: "myapp"
+ files: "main.c"
+ version: "1.0.0"
+
+ consoleApplication: true
+ install: true
+
+ //! [0]
+ cpp.rpaths: {
+ if (!cpp.rpathOrigin)
+ return [];
+ return [
+ FileInfo.joinPaths(
+ cpp.rpathOrigin,
+ FileInfo.relativePath(
+ FileInfo.joinPaths("/", product.installDir),
+ FileInfo.joinPaths("/", "lib")))
+ ];
+ }
+ //! [0]
+}
+//! [1]
diff --git a/tutorial/chapter-3/app/main.c b/tutorial/chapter-3/app/main.c
new file mode 100644
index 000000000..a560d1ba1
--- /dev/null
+++ b/tutorial/chapter-3/app/main.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+#include "lib.h"
+
+int main()
+{
+ printf("Hello, world\n");
+ printf("%s\n", get_string());
+ return 0;
+}
diff --git a/tutorial/chapter-3/lib/lib.c b/tutorial/chapter-3/lib/lib.c
new file mode 100644
index 000000000..1cbc5ef21
--- /dev/null
+++ b/tutorial/chapter-3/lib/lib.c
@@ -0,0 +1,16 @@
+
+//! [0]
+
+// lib/lib.cpp
+#include "lib.h"
+
+#ifndef CRUCIAL_DEFINE
+# error CRUCIAL_DEFINE not defined
+#endif
+
+const char *get_string()
+{
+ return "Hello from library";
+}
+
+//! [0]
diff --git a/tutorial/chapter-3/lib/lib.h b/tutorial/chapter-3/lib/lib.h
new file mode 100644
index 000000000..772e1d82b
--- /dev/null
+++ b/tutorial/chapter-3/lib/lib.h
@@ -0,0 +1,11 @@
+#ifndef LIB_H
+#define LIB_H
+
+//! [0]
+// lib/lib.h
+#include "lib_global.h"
+
+MYLIB_EXPORT const char *get_string();
+//! [0]
+
+#endif // LIB_H
diff --git a/tutorial/chapter-3/lib/lib.qbs b/tutorial/chapter-3/lib/lib.qbs
new file mode 100644
index 000000000..a52c29d97
--- /dev/null
+++ b/tutorial/chapter-3/lib/lib.qbs
@@ -0,0 +1,26 @@
+//! [0]
+// lib/lib.qbs
+
+DynamicLibrary {
+ name: "mylib"
+ files: [
+ "lib.c",
+ "lib.h",
+ "lib_global.h",
+ ]
+ version: "1.0.0"
+ install: true
+
+ Depends { name: "cpp" }
+ cpp.defines: ["MYLIB_LIBRARY", "CRUCIAL_DEFINE"]
+ cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ }
+
+ Depends { name: "bundle" }
+ bundle.isBundle: false
+}
+//! [0]
diff --git a/tutorial/chapter-3/lib/lib_global.h b/tutorial/chapter-3/lib/lib_global.h
new file mode 100644
index 000000000..07b7978ef
--- /dev/null
+++ b/tutorial/chapter-3/lib/lib_global.h
@@ -0,0 +1,23 @@
+//! [0]
+// lib/lib_global.h
+
+#ifndef LIB_GLOBAL_H
+#define LIB_GLOBAL_H
+
+#if defined(_WIN32) || defined(WIN32)
+#define MYLIB_DECL_EXPORT __declspec(dllexport)
+#define MYLIB_DECL_IMPORT __declspec(dllimport)
+#else
+#define MYLIB_DECL_EXPORT __attribute__((visibility("default")))
+#define MYLIB_DECL_IMPORT __attribute__((visibility("default")))
+#endif
+
+#if defined(MYLIB_LIBRARY)
+#define MYLIB_EXPORT MYLIB_DECL_EXPORT
+#else
+#define MYLIB_EXPORT MYLIB_DECL_IMPORT
+#endif
+
+#endif // LIB_GLOBAL_H
+
+//! [0]
diff --git a/tutorial/chapter-3/myproject.qbs b/tutorial/chapter-3/myproject.qbs
new file mode 100644
index 000000000..152d6cce5
--- /dev/null
+++ b/tutorial/chapter-3/myproject.qbs
@@ -0,0 +1,8 @@
+Project {
+ name: "My Project"
+ minimumQbsVersion: "2.0"
+ references: [
+ "app/app.qbs",
+ "lib/lib.qbs"
+ ]
+}
diff --git a/tutorial/chapter-4/app/app.qbs b/tutorial/chapter-4/app/app.qbs
new file mode 100644
index 000000000..7051ff554
--- /dev/null
+++ b/tutorial/chapter-4/app/app.qbs
@@ -0,0 +1,10 @@
+//! [0]
+// app/app.qbs
+
+MyApplication {
+ Depends { name: "mylib" }
+ name: "My Application"
+ targetName: "myapp"
+ files: "main.c"
+}
+//! [0]
diff --git a/tutorial/chapter-4/app/main.c b/tutorial/chapter-4/app/main.c
new file mode 100644
index 000000000..a560d1ba1
--- /dev/null
+++ b/tutorial/chapter-4/app/main.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+#include "lib.h"
+
+int main()
+{
+ printf("Hello, world\n");
+ printf("%s\n", get_string());
+ return 0;
+}
diff --git a/tutorial/chapter-4/lib/lib.c b/tutorial/chapter-4/lib/lib.c
new file mode 100644
index 000000000..5dcae0de3
--- /dev/null
+++ b/tutorial/chapter-4/lib/lib.c
@@ -0,0 +1,10 @@
+#include "lib.h"
+
+#ifndef CRUCIAL_DEFINE
+# error CRUCIAL_DEFINE not defined
+#endif
+
+const char *get_string()
+{
+ return "Hello from library";
+}
diff --git a/tutorial/chapter-4/lib/lib.h b/tutorial/chapter-4/lib/lib.h
new file mode 100644
index 000000000..ef39ca4a1
--- /dev/null
+++ b/tutorial/chapter-4/lib/lib.h
@@ -0,0 +1,8 @@
+#ifndef LIB_H
+#define LIB_H
+
+#include "lib_global.h"
+
+MYLIB_EXPORT const char *get_string();
+
+#endif // LIB_H
diff --git a/tutorial/chapter-4/lib/lib.qbs b/tutorial/chapter-4/lib/lib.qbs
new file mode 100644
index 000000000..1f7bf6e6b
--- /dev/null
+++ b/tutorial/chapter-4/lib/lib.qbs
@@ -0,0 +1,13 @@
+//! [0]
+// lib/lib.qbs
+
+MyLibrary {
+ name: "mylib"
+ files: [
+ "lib.c",
+ "lib.h",
+ "lib_global.h",
+ ]
+ cpp.defines: base.concat(["CRUCIAL_DEFINE"])
+}
+//! [0]
diff --git a/tutorial/chapter-4/lib/lib_global.h b/tutorial/chapter-4/lib/lib_global.h
new file mode 100644
index 000000000..76cce8d36
--- /dev/null
+++ b/tutorial/chapter-4/lib/lib_global.h
@@ -0,0 +1,18 @@
+#ifndef LIB_GLOBAL_H
+#define LIB_GLOBAL_H
+
+#if defined(_WIN32) || defined(WIN32)
+#define MYLIB_DECL_EXPORT __declspec(dllexport)
+#define MYLIB_DECL_IMPORT __declspec(dllimport)
+#else
+#define MYLIB_DECL_EXPORT __attribute__((visibility("default")))
+#define MYLIB_DECL_IMPORT __attribute__((visibility("default")))
+#endif
+
+#if defined(MYLIB_LIBRARY)
+#define MYLIB_EXPORT MYLIB_DECL_EXPORT
+#else
+#define MYLIB_EXPORT MYLIB_DECL_IMPORT
+#endif
+
+#endif // LIB_GLOBAL_H
diff --git a/tutorial/chapter-4/myproject.qbs b/tutorial/chapter-4/myproject.qbs
new file mode 100644
index 000000000..d429542e7
--- /dev/null
+++ b/tutorial/chapter-4/myproject.qbs
@@ -0,0 +1,11 @@
+//! [0]
+Project {
+ name: "My Project"
+ minimumQbsVersion: "2.0"
+ references: [
+ "app/app.qbs",
+ "lib/lib.qbs"
+ ]
+ qbsSearchPaths: "qbs"
+}
+//! [0]
diff --git a/tutorial/chapter-4/qbs/imports/MyApplication.qbs b/tutorial/chapter-4/qbs/imports/MyApplication.qbs
new file mode 100644
index 000000000..e98794f3c
--- /dev/null
+++ b/tutorial/chapter-4/qbs/imports/MyApplication.qbs
@@ -0,0 +1,23 @@
+//! [0]
+// qbs/imports/MyApplication.qbs
+
+import qbs.FileInfo
+
+CppApplication {
+ version: "1.0.0"
+ consoleApplication: true
+ install: true
+
+ cpp.rpaths: {
+ if (!cpp.rpathOrigin)
+ return [];
+ return [
+ FileInfo.joinPaths(
+ cpp.rpathOrigin,
+ FileInfo.relativePath(
+ FileInfo.joinPaths("/", product.installDir),
+ FileInfo.joinPaths("/", "lib")))
+ ];
+ }
+}
+//! [0]
diff --git a/tutorial/chapter-4/qbs/imports/MyLibrary.qbs b/tutorial/chapter-4/qbs/imports/MyLibrary.qbs
new file mode 100644
index 000000000..c819fa38e
--- /dev/null
+++ b/tutorial/chapter-4/qbs/imports/MyLibrary.qbs
@@ -0,0 +1,21 @@
+//! [0]
+// qbs/imports/MyLibrary.qbs
+
+DynamicLibrary {
+ version: "1.0.0"
+ install: true
+
+ Depends { name: 'cpp' }
+ property string libraryMacro: name.replace(" ", "_").toUpperCase() + "_LIBRARY"
+ cpp.defines: [libraryMacro]
+ cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ }
+
+ Depends { name: 'bundle' }
+ bundle.isBundle: false
+}
+//! [0]
diff --git a/tutorial/chapter-5/app/app.qbs b/tutorial/chapter-5/app/app.qbs
new file mode 100644
index 000000000..84c2430e4
--- /dev/null
+++ b/tutorial/chapter-5/app/app.qbs
@@ -0,0 +1,6 @@
+MyApplication {
+ Depends { name: "mylib" }
+ name: "My Application"
+ targetName: "myapp"
+ files: "main.c"
+}
diff --git a/tutorial/chapter-5/app/main.c b/tutorial/chapter-5/app/main.c
new file mode 100644
index 000000000..a560d1ba1
--- /dev/null
+++ b/tutorial/chapter-5/app/main.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+#include "lib.h"
+
+int main()
+{
+ printf("Hello, world\n");
+ printf("%s\n", get_string());
+ return 0;
+}
diff --git a/tutorial/chapter-5/lib/lib.c b/tutorial/chapter-5/lib/lib.c
new file mode 100644
index 000000000..5dcae0de3
--- /dev/null
+++ b/tutorial/chapter-5/lib/lib.c
@@ -0,0 +1,10 @@
+#include "lib.h"
+
+#ifndef CRUCIAL_DEFINE
+# error CRUCIAL_DEFINE not defined
+#endif
+
+const char *get_string()
+{
+ return "Hello from library";
+}
diff --git a/tutorial/chapter-5/lib/lib.h b/tutorial/chapter-5/lib/lib.h
new file mode 100644
index 000000000..ef39ca4a1
--- /dev/null
+++ b/tutorial/chapter-5/lib/lib.h
@@ -0,0 +1,8 @@
+#ifndef LIB_H
+#define LIB_H
+
+#include "lib_global.h"
+
+MYLIB_EXPORT const char *get_string();
+
+#endif // LIB_H
diff --git a/tutorial/chapter-5/lib/lib.qbs b/tutorial/chapter-5/lib/lib.qbs
new file mode 100644
index 000000000..7c303c8aa
--- /dev/null
+++ b/tutorial/chapter-5/lib/lib.qbs
@@ -0,0 +1,9 @@
+MyLibrary {
+ name: "mylib"
+ files: [
+ "lib.c",
+ "lib.h",
+ "lib_global.h",
+ ]
+ cpp.defines: base.concat(['CRUCIAL_DEFINE'])
+}
diff --git a/tutorial/chapter-5/lib/lib_global.h b/tutorial/chapter-5/lib/lib_global.h
new file mode 100644
index 000000000..76cce8d36
--- /dev/null
+++ b/tutorial/chapter-5/lib/lib_global.h
@@ -0,0 +1,18 @@
+#ifndef LIB_GLOBAL_H
+#define LIB_GLOBAL_H
+
+#if defined(_WIN32) || defined(WIN32)
+#define MYLIB_DECL_EXPORT __declspec(dllexport)
+#define MYLIB_DECL_IMPORT __declspec(dllimport)
+#else
+#define MYLIB_DECL_EXPORT __attribute__((visibility("default")))
+#define MYLIB_DECL_IMPORT __attribute__((visibility("default")))
+#endif
+
+#if defined(MYLIB_LIBRARY)
+#define MYLIB_EXPORT MYLIB_DECL_EXPORT
+#else
+#define MYLIB_EXPORT MYLIB_DECL_IMPORT
+#endif
+
+#endif // LIB_GLOBAL_H
diff --git a/tutorial/chapter-5/myproject.qbs b/tutorial/chapter-5/myproject.qbs
new file mode 100644
index 000000000..66e32b220
--- /dev/null
+++ b/tutorial/chapter-5/myproject.qbs
@@ -0,0 +1,15 @@
+Project {
+ name: "My Project"
+ minimumQbsVersion: "2.0"
+ // ![0]
+ references: [
+ "app/app.qbs",
+ "lib/lib.qbs",
+ "test/test.qbs",
+ ]
+ // ![0]
+ qbsSearchPaths: "qbs"
+ AutotestRunner {
+ timeout: 60
+ }
+}
diff --git a/tutorial/chapter-5/qbs/imports/MyApplication.qbs b/tutorial/chapter-5/qbs/imports/MyApplication.qbs
new file mode 100644
index 000000000..d8c4de25a
--- /dev/null
+++ b/tutorial/chapter-5/qbs/imports/MyApplication.qbs
@@ -0,0 +1,20 @@
+import qbs.FileInfo
+
+CppApplication {
+ version: "1.0.0"
+ consoleApplication: true
+ install: true
+ installDebugInformation: true
+
+ cpp.rpaths: {
+ if (!cpp.rpathOrigin)
+ return [];
+ return [
+ FileInfo.joinPaths(
+ cpp.rpathOrigin,
+ FileInfo.relativePath(
+ FileInfo.joinPaths("/", product.installDir),
+ FileInfo.joinPaths("/", "lib")))
+ ];
+ }
+}
diff --git a/tutorial/chapter-5/qbs/imports/MyAutoTest.qbs b/tutorial/chapter-5/qbs/imports/MyAutoTest.qbs
new file mode 100644
index 000000000..bb6712e85
--- /dev/null
+++ b/tutorial/chapter-5/qbs/imports/MyAutoTest.qbs
@@ -0,0 +1,7 @@
+//! [0]
+// qbs/imports/MyAutoTest.qbs
+
+MyApplication {
+ type: base.concat(["autotest"])
+}
+//! [0]
diff --git a/tutorial/chapter-5/qbs/imports/MyLibrary.qbs b/tutorial/chapter-5/qbs/imports/MyLibrary.qbs
new file mode 100644
index 000000000..5e6c2687a
--- /dev/null
+++ b/tutorial/chapter-5/qbs/imports/MyLibrary.qbs
@@ -0,0 +1,17 @@
+DynamicLibrary {
+ version: "1.0.0"
+ install: true
+
+ Depends { name: 'cpp' }
+ property string libraryMacro: name.replace(" ", "_").toUpperCase() + "_LIBRARY"
+ cpp.defines: [libraryMacro]
+ cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ }
+
+ Depends { name: 'bundle' }
+ bundle.isBundle: false
+}
diff --git a/tutorial/chapter-5/test/test.c b/tutorial/chapter-5/test/test.c
new file mode 100644
index 000000000..f3bdf646c
--- /dev/null
+++ b/tutorial/chapter-5/test/test.c
@@ -0,0 +1,22 @@
+//! [0]
+// test/test.c
+
+#include "lib.h"
+
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ if (argc > 2) {
+ printf("usage: test [value]\n");
+ return 1;
+ }
+ const char *expected = argc == 2 ? argv[1] : "Hello from library";
+ if (strcmp(get_string(), expected) != 0) {
+ printf("text differs\n");
+ return 1;
+ }
+ return 0;
+}
+//! [0] \ No newline at end of file
diff --git a/tutorial/chapter-5/test/test.qbs b/tutorial/chapter-5/test/test.qbs
new file mode 100644
index 000000000..1eab70c55
--- /dev/null
+++ b/tutorial/chapter-5/test/test.qbs
@@ -0,0 +1,9 @@
+//! [0]
+// test/test.qbs
+
+MyAutoTest {
+ Depends { name: "mylib" }
+ name: "mytest"
+ files: "test.c"
+}
+//! [0]
diff --git a/tutorial/chapter-6/app/app.qbs b/tutorial/chapter-6/app/app.qbs
new file mode 100644
index 000000000..84c2430e4
--- /dev/null
+++ b/tutorial/chapter-6/app/app.qbs
@@ -0,0 +1,6 @@
+MyApplication {
+ Depends { name: "mylib" }
+ name: "My Application"
+ targetName: "myapp"
+ files: "main.c"
+}
diff --git a/tutorial/chapter-6/app/main.c b/tutorial/chapter-6/app/main.c
new file mode 100644
index 000000000..a560d1ba1
--- /dev/null
+++ b/tutorial/chapter-6/app/main.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+#include "lib.h"
+
+int main()
+{
+ printf("Hello, world\n");
+ printf("%s\n", get_string());
+ return 0;
+}
diff --git a/tutorial/chapter-6/lib/lib.c b/tutorial/chapter-6/lib/lib.c
new file mode 100644
index 000000000..5dcae0de3
--- /dev/null
+++ b/tutorial/chapter-6/lib/lib.c
@@ -0,0 +1,10 @@
+#include "lib.h"
+
+#ifndef CRUCIAL_DEFINE
+# error CRUCIAL_DEFINE not defined
+#endif
+
+const char *get_string()
+{
+ return "Hello from library";
+}
diff --git a/tutorial/chapter-6/lib/lib.h b/tutorial/chapter-6/lib/lib.h
new file mode 100644
index 000000000..ef39ca4a1
--- /dev/null
+++ b/tutorial/chapter-6/lib/lib.h
@@ -0,0 +1,8 @@
+#ifndef LIB_H
+#define LIB_H
+
+#include "lib_global.h"
+
+MYLIB_EXPORT const char *get_string();
+
+#endif // LIB_H
diff --git a/tutorial/chapter-6/lib/lib.qbs b/tutorial/chapter-6/lib/lib.qbs
new file mode 100644
index 000000000..7c303c8aa
--- /dev/null
+++ b/tutorial/chapter-6/lib/lib.qbs
@@ -0,0 +1,9 @@
+MyLibrary {
+ name: "mylib"
+ files: [
+ "lib.c",
+ "lib.h",
+ "lib_global.h",
+ ]
+ cpp.defines: base.concat(['CRUCIAL_DEFINE'])
+}
diff --git a/tutorial/chapter-6/lib/lib_global.h b/tutorial/chapter-6/lib/lib_global.h
new file mode 100644
index 000000000..76cce8d36
--- /dev/null
+++ b/tutorial/chapter-6/lib/lib_global.h
@@ -0,0 +1,18 @@
+#ifndef LIB_GLOBAL_H
+#define LIB_GLOBAL_H
+
+#if defined(_WIN32) || defined(WIN32)
+#define MYLIB_DECL_EXPORT __declspec(dllexport)
+#define MYLIB_DECL_IMPORT __declspec(dllimport)
+#else
+#define MYLIB_DECL_EXPORT __attribute__((visibility("default")))
+#define MYLIB_DECL_IMPORT __attribute__((visibility("default")))
+#endif
+
+#if defined(MYLIB_LIBRARY)
+#define MYLIB_EXPORT MYLIB_DECL_EXPORT
+#else
+#define MYLIB_EXPORT MYLIB_DECL_IMPORT
+#endif
+
+#endif // LIB_GLOBAL_H
diff --git a/tutorial/chapter-6/myproject.qbs b/tutorial/chapter-6/myproject.qbs
new file mode 100644
index 000000000..86537af23
--- /dev/null
+++ b/tutorial/chapter-6/myproject.qbs
@@ -0,0 +1,35 @@
+//! [0]
+Project {
+ property string version: "1.0.0"
+ property bool installDebugInformation: true
+ property bool withTests: false
+ property stringList autotestArguments: []
+ property stringList autotestWrapper: []
+
+ name: "My Project"
+ minimumQbsVersion: "2.0"
+ // ...
+ //! [0]
+ references: [
+ "app/app.qbs",
+ "lib/lib.qbs",
+ ]
+ qbsSearchPaths: "qbs"
+
+ //! [1]
+ SubProject {
+ filePath: "test/test.qbs"
+ Properties {
+ condition: parent.withTests
+ }
+ }
+ //! [1]
+
+ //! [2]
+ AutotestRunner {
+ condition: parent.withTests
+ arguments: parent.autotestArguments
+ wrapper: parent.autotestWrapper
+ }
+ //! [2]
+}
diff --git a/tutorial/chapter-6/qbs/imports/MyApplication.qbs b/tutorial/chapter-6/qbs/imports/MyApplication.qbs
new file mode 100644
index 000000000..875bc10f3
--- /dev/null
+++ b/tutorial/chapter-6/qbs/imports/MyApplication.qbs
@@ -0,0 +1,23 @@
+import qbs.FileInfo
+
+//! [0]
+CppApplication {
+ version: project.version
+ consoleApplication: true
+ install: true
+ installDebugInformation: project.installDebugInformation
+ // ...
+ //! [0]
+
+ cpp.rpaths: {
+ if (!cpp.rpathOrigin)
+ return [];
+ return [
+ FileInfo.joinPaths(
+ cpp.rpathOrigin,
+ FileInfo.relativePath(
+ FileInfo.joinPaths("/", product.installDir),
+ FileInfo.joinPaths("/", "lib")))
+ ];
+ }
+}
diff --git a/tutorial/chapter-6/qbs/imports/MyAutoTest.qbs b/tutorial/chapter-6/qbs/imports/MyAutoTest.qbs
new file mode 100644
index 000000000..ba420e4d2
--- /dev/null
+++ b/tutorial/chapter-6/qbs/imports/MyAutoTest.qbs
@@ -0,0 +1,3 @@
+MyApplication {
+ type: base.concat(["autotest"])
+}
diff --git a/tutorial/chapter-6/qbs/imports/MyLibrary.qbs b/tutorial/chapter-6/qbs/imports/MyLibrary.qbs
new file mode 100644
index 000000000..ef27bf57b
--- /dev/null
+++ b/tutorial/chapter-6/qbs/imports/MyLibrary.qbs
@@ -0,0 +1,18 @@
+DynamicLibrary {
+ version: project.version
+ install: true
+ installDebugInformation: project.installDebugInformation
+
+ Depends { name: 'cpp' }
+ property string libraryMacro: name.replace(" ", "_").toUpperCase() + "_LIBRARY"
+ cpp.defines: [libraryMacro]
+ cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ }
+
+ Depends { name: 'bundle' }
+ bundle.isBundle: false
+}
diff --git a/tutorial/chapter-6/test/test.c b/tutorial/chapter-6/test/test.c
new file mode 100644
index 000000000..1fef24685
--- /dev/null
+++ b/tutorial/chapter-6/test/test.c
@@ -0,0 +1,18 @@
+#include "lib.h"
+
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ if (argc > 2) {
+ printf("usage: test [value]\n");
+ return 1;
+ }
+ const char *expected = argc == 2 ? argv[1] : "Hello from library";
+ if (strcmp(get_string(), expected) != 0) {
+ printf("text differs\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/tutorial/chapter-6/test/test.qbs b/tutorial/chapter-6/test/test.qbs
new file mode 100644
index 000000000..1e911da47
--- /dev/null
+++ b/tutorial/chapter-6/test/test.qbs
@@ -0,0 +1,5 @@
+MyAutoTest {
+ Depends { name: "mylib" }
+ name: "mytest"
+ files: "test.c"
+}
diff --git a/tutorial/chapter-7/app/app.qbs b/tutorial/chapter-7/app/app.qbs
new file mode 100644
index 000000000..84c2430e4
--- /dev/null
+++ b/tutorial/chapter-7/app/app.qbs
@@ -0,0 +1,6 @@
+MyApplication {
+ Depends { name: "mylib" }
+ name: "My Application"
+ targetName: "myapp"
+ files: "main.c"
+}
diff --git a/tutorial/chapter-7/app/main.c b/tutorial/chapter-7/app/main.c
new file mode 100644
index 000000000..a560d1ba1
--- /dev/null
+++ b/tutorial/chapter-7/app/main.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+#include "lib.h"
+
+int main()
+{
+ printf("Hello, world\n");
+ printf("%s\n", get_string());
+ return 0;
+}
diff --git a/tutorial/chapter-7/lib/lib.c b/tutorial/chapter-7/lib/lib.c
new file mode 100644
index 000000000..5dcae0de3
--- /dev/null
+++ b/tutorial/chapter-7/lib/lib.c
@@ -0,0 +1,10 @@
+#include "lib.h"
+
+#ifndef CRUCIAL_DEFINE
+# error CRUCIAL_DEFINE not defined
+#endif
+
+const char *get_string()
+{
+ return "Hello from library";
+}
diff --git a/tutorial/chapter-7/lib/lib.h b/tutorial/chapter-7/lib/lib.h
new file mode 100644
index 000000000..ef39ca4a1
--- /dev/null
+++ b/tutorial/chapter-7/lib/lib.h
@@ -0,0 +1,8 @@
+#ifndef LIB_H
+#define LIB_H
+
+#include "lib_global.h"
+
+MYLIB_EXPORT const char *get_string();
+
+#endif // LIB_H
diff --git a/tutorial/chapter-7/lib/lib.qbs b/tutorial/chapter-7/lib/lib.qbs
new file mode 100644
index 000000000..eccf6844d
--- /dev/null
+++ b/tutorial/chapter-7/lib/lib.qbs
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the examples 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$
+**
+****************************************************************************/
+
+MyLibrary {
+ name: "mylib"
+ files: [
+ "lib.c",
+ "lib.h",
+ ]
+ Depends { name: 'cpp' }
+ cpp.defines: ['CRUCIAL_DEFINE']
+}
+
diff --git a/tutorial/chapter-7/lib/lib_global.h b/tutorial/chapter-7/lib/lib_global.h
new file mode 100644
index 000000000..76cce8d36
--- /dev/null
+++ b/tutorial/chapter-7/lib/lib_global.h
@@ -0,0 +1,18 @@
+#ifndef LIB_GLOBAL_H
+#define LIB_GLOBAL_H
+
+#if defined(_WIN32) || defined(WIN32)
+#define MYLIB_DECL_EXPORT __declspec(dllexport)
+#define MYLIB_DECL_IMPORT __declspec(dllimport)
+#else
+#define MYLIB_DECL_EXPORT __attribute__((visibility("default")))
+#define MYLIB_DECL_IMPORT __attribute__((visibility("default")))
+#endif
+
+#if defined(MYLIB_LIBRARY)
+#define MYLIB_EXPORT MYLIB_DECL_EXPORT
+#else
+#define MYLIB_EXPORT MYLIB_DECL_IMPORT
+#endif
+
+#endif // LIB_GLOBAL_H
diff --git a/tutorial/chapter-7/myproject.qbs b/tutorial/chapter-7/myproject.qbs
new file mode 100644
index 000000000..aa7ec90bd
--- /dev/null
+++ b/tutorial/chapter-7/myproject.qbs
@@ -0,0 +1,27 @@
+Project {
+ property bool withTests: true
+ property bool installDebugInformation: true
+ property stringList autotestArguments: []
+ property stringList autotestWrapper: []
+
+ name: "My Project"
+ minimumQbsVersion: "2.0"
+ references: [
+ "app/app.qbs",
+ "lib/lib.qbs",
+ ]
+ qbsSearchPaths: "qbs"
+
+ SubProject {
+ filePath: "test/test.qbs"
+ Properties {
+ condition: parent.withTests
+ }
+ }
+
+ AutotestRunner {
+ condition: parent.withTests
+ arguments: parent.autotestArguments
+ wrapper: parent.autotestWrapper
+ }
+}
diff --git a/tutorial/chapter-7/qbs/imports/MyApplication.qbs b/tutorial/chapter-7/qbs/imports/MyApplication.qbs
new file mode 100644
index 000000000..622c3f4be
--- /dev/null
+++ b/tutorial/chapter-7/qbs/imports/MyApplication.qbs
@@ -0,0 +1,13 @@
+//! [0]
+CppApplication {
+ Depends { name: "mybuildconfig" }
+ installDir: mybuildconfig.appInstallDir
+
+ version: "1.0.0"
+ // ...
+//! [0]
+
+ consoleApplication: true
+ install: true
+ installDebugInformation: project.installDebugInformation
+}
diff --git a/tutorial/chapter-7/qbs/imports/MyAutoTest.qbs b/tutorial/chapter-7/qbs/imports/MyAutoTest.qbs
new file mode 100644
index 000000000..1faecdc33
--- /dev/null
+++ b/tutorial/chapter-7/qbs/imports/MyAutoTest.qbs
@@ -0,0 +1,3 @@
+MyApplication {
+ type: ["application", "autotest"]
+}
diff --git a/tutorial/chapter-7/qbs/imports/MyLibrary.qbs b/tutorial/chapter-7/qbs/imports/MyLibrary.qbs
new file mode 100644
index 000000000..7c130ba17
--- /dev/null
+++ b/tutorial/chapter-7/qbs/imports/MyLibrary.qbs
@@ -0,0 +1,25 @@
+DynamicLibrary {
+ version: project.version
+ install: true
+ installDebugInformation: project.installDebugInformation
+//! [0]
+// qbs/imports/MyLibrary.qbs
+ // ...
+ Depends { name: "mybuildconfig" }
+ installDir: mybuildconfig.libInstallDir
+
+ Depends { name: "cpp" }
+ property string libraryMacro: name.replace(" ", "_").toUpperCase() + "_LIBRARY"
+ cpp.defines: [libraryMacro]
+ cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+
+ Export {
+ // ...
+//! [0]
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ }
+
+ Depends { name: 'bundle' }
+ bundle.isBundle: false
+}
diff --git a/tutorial/chapter-7/qbs/modules/mybuildconfig/mybuildconfig.qbs b/tutorial/chapter-7/qbs/modules/mybuildconfig/mybuildconfig.qbs
new file mode 100644
index 000000000..73234bb0f
--- /dev/null
+++ b/tutorial/chapter-7/qbs/modules/mybuildconfig/mybuildconfig.qbs
@@ -0,0 +1,26 @@
+import qbs.FileInfo
+
+//! [1]
+//! [0]
+// qbs/modules/mybuildconfig.qbs
+Module {
+ property string appInstallDir: "bin"
+ property string libDirName: "lib"
+ property string libInstallDir: qbs.targetOS.contains("windows") ? appInstallDir : libDirName
+ //! [0]
+
+ Depends { name: "cpp" }
+
+ property bool enableRPath: true
+ property stringList libRPaths: {
+ if (enableRPath && cpp.rpathOrigin && product.installDir) {
+ return [FileInfo.joinPaths(cpp.rpathOrigin, FileInfo.relativePath(
+ FileInfo.joinPaths('/', product.installDir),
+ FileInfo.joinPaths('/', libInstallDir)))];
+ }
+ return [];
+ }
+
+ cpp.rpaths: libRPaths
+}
+//! [1]
diff --git a/tutorial/chapter-7/test/test.c b/tutorial/chapter-7/test/test.c
new file mode 100644
index 000000000..1fef24685
--- /dev/null
+++ b/tutorial/chapter-7/test/test.c
@@ -0,0 +1,18 @@
+#include "lib.h"
+
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ if (argc > 2) {
+ printf("usage: test [value]\n");
+ return 1;
+ }
+ const char *expected = argc == 2 ? argv[1] : "Hello from library";
+ if (strcmp(get_string(), expected) != 0) {
+ printf("text differs\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/tutorial/chapter-7/test/test.qbs b/tutorial/chapter-7/test/test.qbs
new file mode 100644
index 000000000..1e911da47
--- /dev/null
+++ b/tutorial/chapter-7/test/test.qbs
@@ -0,0 +1,5 @@
+MyAutoTest {
+ Depends { name: "mylib" }
+ name: "mytest"
+ files: "test.c"
+}
diff --git a/tutorial/chapter-8/app/app.qbs b/tutorial/chapter-8/app/app.qbs
new file mode 100644
index 000000000..84c2430e4
--- /dev/null
+++ b/tutorial/chapter-8/app/app.qbs
@@ -0,0 +1,6 @@
+MyApplication {
+ Depends { name: "mylib" }
+ name: "My Application"
+ targetName: "myapp"
+ files: "main.c"
+}
diff --git a/tutorial/chapter-8/app/main.c b/tutorial/chapter-8/app/main.c
new file mode 100644
index 000000000..a560d1ba1
--- /dev/null
+++ b/tutorial/chapter-8/app/main.c
@@ -0,0 +1,10 @@
+#include <stdio.h>
+
+#include "lib.h"
+
+int main()
+{
+ printf("Hello, world\n");
+ printf("%s\n", get_string());
+ return 0;
+}
diff --git a/tutorial/chapter-8/lib/lib.c b/tutorial/chapter-8/lib/lib.c
new file mode 100644
index 000000000..5dcae0de3
--- /dev/null
+++ b/tutorial/chapter-8/lib/lib.c
@@ -0,0 +1,10 @@
+#include "lib.h"
+
+#ifndef CRUCIAL_DEFINE
+# error CRUCIAL_DEFINE not defined
+#endif
+
+const char *get_string()
+{
+ return "Hello from library";
+}
diff --git a/tutorial/chapter-8/lib/lib.h b/tutorial/chapter-8/lib/lib.h
new file mode 100644
index 000000000..ef39ca4a1
--- /dev/null
+++ b/tutorial/chapter-8/lib/lib.h
@@ -0,0 +1,8 @@
+#ifndef LIB_H
+#define LIB_H
+
+#include "lib_global.h"
+
+MYLIB_EXPORT const char *get_string();
+
+#endif // LIB_H
diff --git a/tutorial/chapter-8/lib/lib.qbs b/tutorial/chapter-8/lib/lib.qbs
new file mode 100644
index 000000000..e8cf9bdb9
--- /dev/null
+++ b/tutorial/chapter-8/lib/lib.qbs
@@ -0,0 +1,10 @@
+MyLibrary {
+ name: "mylib"
+ files: [
+ "lib.c",
+ "lib.h",
+ ]
+ Depends { name: 'cpp' }
+ cpp.defines: ['CRUCIAL_DEFINE']
+}
+
diff --git a/tutorial/chapter-8/lib/lib_global.h b/tutorial/chapter-8/lib/lib_global.h
new file mode 100644
index 000000000..d830dd8ab
--- /dev/null
+++ b/tutorial/chapter-8/lib/lib_global.h
@@ -0,0 +1,25 @@
+#ifndef LIB_GLOBAL_H
+#define LIB_GLOBAL_H
+
+#if defined(_WIN32) || defined(WIN32)
+#define MY_LIB_DECL_EXPORT __declspec(dllexport)
+#define MY_LIB_DECL_IMPORT __declspec(dllimport)
+#else
+#define MY_LIB_DECL_EXPORT __attribute__((visibility("default")))
+#define MY_LIB_DECL_IMPORT __attribute__((visibility("default")))
+#endif
+
+// ![0]
+// lib/lib_global.h
+#if defined(MYLIB_STATIC_LIBRARY)
+#define MYLIB_EXPORT
+#else
+#if defined(MYLIB_LIBRARY)
+#define MYLIB_EXPORT MY_LIB_DECL_EXPORT
+#else
+#define MYLIB_EXPORT MY_LIB_DECL_IMPORT
+#endif
+#endif
+// ![0]
+
+#endif // LIB_GLOBAL_H \ No newline at end of file
diff --git a/tutorial/chapter-8/myproject.qbs b/tutorial/chapter-8/myproject.qbs
new file mode 100644
index 000000000..aa7ec90bd
--- /dev/null
+++ b/tutorial/chapter-8/myproject.qbs
@@ -0,0 +1,27 @@
+Project {
+ property bool withTests: true
+ property bool installDebugInformation: true
+ property stringList autotestArguments: []
+ property stringList autotestWrapper: []
+
+ name: "My Project"
+ minimumQbsVersion: "2.0"
+ references: [
+ "app/app.qbs",
+ "lib/lib.qbs",
+ ]
+ qbsSearchPaths: "qbs"
+
+ SubProject {
+ filePath: "test/test.qbs"
+ Properties {
+ condition: parent.withTests
+ }
+ }
+
+ AutotestRunner {
+ condition: parent.withTests
+ arguments: parent.autotestArguments
+ wrapper: parent.autotestWrapper
+ }
+}
diff --git a/tutorial/chapter-8/qbs/imports/MyApplication.qbs b/tutorial/chapter-8/qbs/imports/MyApplication.qbs
new file mode 100644
index 000000000..11b3bfb54
--- /dev/null
+++ b/tutorial/chapter-8/qbs/imports/MyApplication.qbs
@@ -0,0 +1,10 @@
+CppApplication {
+ Depends { name: "mybuildconfig" }
+ version: "1.0.0"
+
+ cpp.rpaths: mybuildconfig.libRPaths
+ consoleApplication: true
+ installDir: mybuildconfig.appInstallDir
+ install: true
+ installDebugInformation: project.installDebugInformation
+}
diff --git a/tutorial/chapter-8/qbs/imports/MyAutoTest.qbs b/tutorial/chapter-8/qbs/imports/MyAutoTest.qbs
new file mode 100644
index 000000000..1faecdc33
--- /dev/null
+++ b/tutorial/chapter-8/qbs/imports/MyAutoTest.qbs
@@ -0,0 +1,3 @@
+MyApplication {
+ type: ["application", "autotest"]
+}
diff --git a/tutorial/chapter-8/qbs/imports/MyLibrary.qbs b/tutorial/chapter-8/qbs/imports/MyLibrary.qbs
new file mode 100644
index 000000000..947706dd4
--- /dev/null
+++ b/tutorial/chapter-8/qbs/imports/MyLibrary.qbs
@@ -0,0 +1,26 @@
+// ![0]
+Library {
+ Depends { name: "cpp" }
+ Depends { name: "mybuildconfig" }
+ type: mybuildconfig.staticBuild ? "staticlibrary" : "dynamiclibrary"
+ version: "1.0.0"
+ install: !mybuildconfig.staticBuild || mybuildconfig.installStaticLib
+ installDir: mybuildconfig.libInstallDir
+
+ readonly property string _nameUpper : name.replace(" ", "_").toUpperCase()
+ property string libraryMacro: _nameUpper + "_LIBRARY"
+ property string staticLibraryMacro: _nameUpper + "_STATIC_LIBRARY"
+ cpp.defines: mybuildconfig.staticBuild ? [staticLibraryMacro] : [libraryMacro]
+ cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ cpp.defines: exportingProduct.mybuildconfig.staticBuild
+ ? [exportingProduct.staticLibraryMacro] : []
+ }
+
+ Depends { name: "bundle" }
+ bundle.isBundle: false
+}
+// ![0]
diff --git a/tutorial/chapter-8/qbs/modules/mybuildconfig/mybuildconfig.qbs b/tutorial/chapter-8/qbs/modules/mybuildconfig/mybuildconfig.qbs
new file mode 100644
index 000000000..0f13dde4d
--- /dev/null
+++ b/tutorial/chapter-8/qbs/modules/mybuildconfig/mybuildconfig.qbs
@@ -0,0 +1,25 @@
+import qbs.FileInfo
+
+Module {
+ Depends { name: "cpp" }
+
+ property string appInstallDir: "bin"
+ property string libDirName: "lib"
+ property string libInstallDir: qbs.targetOS.contains("windows") ? "bin" : libDirName
+ property bool staticBuild: false
+ property bool installStaticLib: true
+ property bool enableRPath: true
+
+ property stringList libRPaths: {
+ if (enableRPath && cpp.rpathOrigin && product.installDir) {
+ return [
+ FileInfo.joinPaths(
+ cpp.rpathOrigin,
+ FileInfo.relativePath(
+ FileInfo.joinPaths('/', product.installDir),
+ FileInfo.joinPaths('/', libDirName)))
+ ];
+ }
+ return [];
+ }
+}
diff --git a/tutorial/chapter-8/test/test.c b/tutorial/chapter-8/test/test.c
new file mode 100644
index 000000000..1fef24685
--- /dev/null
+++ b/tutorial/chapter-8/test/test.c
@@ -0,0 +1,18 @@
+#include "lib.h"
+
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ if (argc > 2) {
+ printf("usage: test [value]\n");
+ return 1;
+ }
+ const char *expected = argc == 2 ? argv[1] : "Hello from library";
+ if (strcmp(get_string(), expected) != 0) {
+ printf("text differs\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/tutorial/chapter-8/test/test.qbs b/tutorial/chapter-8/test/test.qbs
new file mode 100644
index 000000000..1e911da47
--- /dev/null
+++ b/tutorial/chapter-8/test/test.qbs
@@ -0,0 +1,5 @@
+MyAutoTest {
+ Depends { name: "mylib" }
+ name: "mytest"
+ files: "test.c"
+}
diff --git a/tutorial/chapter-9/app/app.qbs b/tutorial/chapter-9/app/app.qbs
new file mode 100644
index 000000000..837b66da3
--- /dev/null
+++ b/tutorial/chapter-9/app/app.qbs
@@ -0,0 +1,9 @@
+//! [0]
+MyApplication {
+ Depends { name: "mylib" }
+ Depends { name: "version_header" }
+ name: "My Application"
+ targetName: "myapp"
+ files: "main.c"
+}
+//! [0]
diff --git a/tutorial/chapter-9/app/main.c b/tutorial/chapter-9/app/main.c
new file mode 100644
index 000000000..d6cefc2ff
--- /dev/null
+++ b/tutorial/chapter-9/app/main.c
@@ -0,0 +1,14 @@
+//![0]
+#include <lib.h>
+#include <version.h>
+
+#include <stdio.h>
+
+int main()
+{
+ printf("Hello, world\n");
+ printf("%s\n", get_string());
+ printf("ProductVersion = %s\n", kProductVersion);
+ return 0;
+}
+//![0]
diff --git a/tutorial/chapter-9/lib/lib.c b/tutorial/chapter-9/lib/lib.c
new file mode 100644
index 000000000..5dcae0de3
--- /dev/null
+++ b/tutorial/chapter-9/lib/lib.c
@@ -0,0 +1,10 @@
+#include "lib.h"
+
+#ifndef CRUCIAL_DEFINE
+# error CRUCIAL_DEFINE not defined
+#endif
+
+const char *get_string()
+{
+ return "Hello from library";
+}
diff --git a/tutorial/chapter-9/lib/lib.h b/tutorial/chapter-9/lib/lib.h
new file mode 100644
index 000000000..ef39ca4a1
--- /dev/null
+++ b/tutorial/chapter-9/lib/lib.h
@@ -0,0 +1,8 @@
+#ifndef LIB_H
+#define LIB_H
+
+#include "lib_global.h"
+
+MYLIB_EXPORT const char *get_string();
+
+#endif // LIB_H
diff --git a/tutorial/chapter-9/lib/lib.qbs b/tutorial/chapter-9/lib/lib.qbs
new file mode 100644
index 000000000..e8cf9bdb9
--- /dev/null
+++ b/tutorial/chapter-9/lib/lib.qbs
@@ -0,0 +1,10 @@
+MyLibrary {
+ name: "mylib"
+ files: [
+ "lib.c",
+ "lib.h",
+ ]
+ Depends { name: 'cpp' }
+ cpp.defines: ['CRUCIAL_DEFINE']
+}
+
diff --git a/tutorial/chapter-9/lib/lib_global.h b/tutorial/chapter-9/lib/lib_global.h
new file mode 100644
index 000000000..329b62fd3
--- /dev/null
+++ b/tutorial/chapter-9/lib/lib_global.h
@@ -0,0 +1,25 @@
+#ifndef LIB_GLOBAL_H
+#define LIB_GLOBAL_H
+
+#if defined(_WIN32) || defined(WIN32)
+#define MY_LIB_DECL_EXPORT __declspec(dllexport)
+#define MY_LIB_DECL_IMPORT __declspec(dllimport)
+#else
+#define MY_LIB_DECL_EXPORT __attribute__((visibility("default")))
+#define MY_LIB_DECL_IMPORT __attribute__((visibility("default")))
+#endif
+
+// ![0]
+// lib/lib_global.h
+#if defined(MYLIB_STATIC_LIBRARY)
+#define MYLIB_EXPORT
+#else
+#if defined(MYLIB_LIBRARY)
+#define MYLIB_EXPORT MY_LIB_DECL_EXPORT
+#else
+#define MYLIB_EXPORT MY_LIB_DECL_IMPORT
+#endif
+#endif
+// ![0]
+
+#endif // LIB_GLOBAL_H
diff --git a/tutorial/chapter-9/myproject.qbs b/tutorial/chapter-9/myproject.qbs
new file mode 100644
index 000000000..fac217b71
--- /dev/null
+++ b/tutorial/chapter-9/myproject.qbs
@@ -0,0 +1,32 @@
+Project {
+ property string version: "1.0.0"
+ property bool withTests: true
+ property bool installDebugInformation: true
+ property stringList autotestArguments: []
+ property stringList autotestWrapper: []
+
+ name: "My Project"
+ minimumQbsVersion: "2.0"
+ //! [0]
+ // myproject.qbs
+ references: [
+ "app/app.qbs",
+ "lib/lib.qbs",
+ "version-header/version-header.qbs",
+ ]
+ //! [0]
+ qbsSearchPaths: "qbs"
+
+ SubProject {
+ filePath: "test/test.qbs"
+ Properties {
+ condition: parent.withTests
+ }
+ }
+
+ AutotestRunner {
+ condition: parent.withTests
+ arguments: parent.autotestArguments
+ wrapper: parent.autotestWrapper
+ }
+}
diff --git a/tutorial/chapter-9/qbs/imports/MyApplication.qbs b/tutorial/chapter-9/qbs/imports/MyApplication.qbs
new file mode 100644
index 000000000..451065190
--- /dev/null
+++ b/tutorial/chapter-9/qbs/imports/MyApplication.qbs
@@ -0,0 +1,10 @@
+CppApplication {
+ Depends { name: "mybuildconfig" }
+ version: mybuildconfig.productVersion
+
+ cpp.rpaths: mybuildconfig.libRPaths
+ consoleApplication: true
+ installDir: mybuildconfig.appInstallDir
+ install: true
+ installDebugInformation: project.installDebugInformation
+}
diff --git a/tutorial/chapter-9/qbs/imports/MyAutoTest.qbs b/tutorial/chapter-9/qbs/imports/MyAutoTest.qbs
new file mode 100644
index 000000000..1faecdc33
--- /dev/null
+++ b/tutorial/chapter-9/qbs/imports/MyAutoTest.qbs
@@ -0,0 +1,3 @@
+MyApplication {
+ type: ["application", "autotest"]
+}
diff --git a/tutorial/chapter-9/qbs/imports/MyLibrary.qbs b/tutorial/chapter-9/qbs/imports/MyLibrary.qbs
new file mode 100644
index 000000000..9f2590f50
--- /dev/null
+++ b/tutorial/chapter-9/qbs/imports/MyLibrary.qbs
@@ -0,0 +1,26 @@
+// ![0]
+Library {
+ Depends { name: "cpp" }
+ Depends { name: "mybuildconfig" }
+ type: mybuildconfig.staticBuild ? "staticlibrary" : "dynamiclibrary"
+ version: mybuildconfig.productVersion
+ install: !mybuildconfig.staticBuild || mybuildconfig.installStaticLib
+ installDir: mybuildconfig.libInstallDir
+
+ readonly property string _nameUpper : name.replace(" ", "_").toUpperCase()
+ property string libraryMacro: _nameUpper + "_LIBRARY"
+ property string staticLibraryMacro: _nameUpper + "_STATIC_LIBRARY"
+ cpp.defines: mybuildconfig.staticBuild ? [staticLibraryMacro] : [libraryMacro]
+ cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: [exportingProduct.sourceDirectory]
+ cpp.defines: exportingProduct.mybuildconfig.staticBuild
+ ? [exportingProduct.staticLibraryMacro] : []
+ }
+
+ Depends { name: "bundle" }
+ bundle.isBundle: false
+}
+// ![0]
diff --git a/tutorial/chapter-9/qbs/modules/mybuildconfig/mybuildconfig.qbs b/tutorial/chapter-9/qbs/modules/mybuildconfig/mybuildconfig.qbs
new file mode 100644
index 000000000..7aed8f7eb
--- /dev/null
+++ b/tutorial/chapter-9/qbs/modules/mybuildconfig/mybuildconfig.qbs
@@ -0,0 +1,30 @@
+import qbs.FileInfo
+
+//! [0]
+// qbs/modules/mybuildconfig/mybuildconfig.qbs
+Module {
+ Depends { name: "cpp" }
+
+ property string productVersion: "1.0.0"
+ // ...
+ //! [0]
+ property string appInstallDir: "bin"
+ property string libDirName: "lib"
+ property string libInstallDir: qbs.targetOS.contains("windows") ? "bin" : libDirName
+ property bool staticBuild: false
+ property bool installStaticLib: true
+ property bool enableRPath: true
+
+ property stringList libRPaths: {
+ if (enableRPath && cpp.rpathOrigin && product.installDir) {
+ return [
+ FileInfo.joinPaths(
+ cpp.rpathOrigin,
+ FileInfo.relativePath(
+ FileInfo.joinPaths('/', product.installDir),
+ FileInfo.joinPaths('/', libDirName)))
+ ];
+ }
+ return [];
+ }
+}
diff --git a/tutorial/chapter-9/test/test.c b/tutorial/chapter-9/test/test.c
new file mode 100644
index 000000000..1fef24685
--- /dev/null
+++ b/tutorial/chapter-9/test/test.c
@@ -0,0 +1,18 @@
+#include "lib.h"
+
+#include <stdio.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ if (argc > 2) {
+ printf("usage: test [value]\n");
+ return 1;
+ }
+ const char *expected = argc == 2 ? argv[1] : "Hello from library";
+ if (strcmp(get_string(), expected) != 0) {
+ printf("text differs\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/tutorial/chapter-9/test/test.qbs b/tutorial/chapter-9/test/test.qbs
new file mode 100644
index 000000000..1e911da47
--- /dev/null
+++ b/tutorial/chapter-9/test/test.qbs
@@ -0,0 +1,5 @@
+MyAutoTest {
+ Depends { name: "mylib" }
+ name: "mytest"
+ files: "test.c"
+}
diff --git a/tutorial/chapter-9/version-header/version-header.qbs b/tutorial/chapter-9/version-header/version-header.qbs
new file mode 100644
index 000000000..0a416d572
--- /dev/null
+++ b/tutorial/chapter-9/version-header/version-header.qbs
@@ -0,0 +1,57 @@
+//! [5]
+// version-header/version-header.qbs
+//! [0]
+import qbs.TextFile
+
+Product {
+ name: "version_header"
+ type: "hpp"
+
+ Depends { name: "mybuildconfig" }
+//! [0]
+
+//! [1]
+ Group {
+ files: ["version.h.in"]
+ fileTags: ["version_h_in"]
+ }
+//! [1]
+
+//! [2]
+ Rule {
+ inputs: ["version_h_in"]
+ Artifact {
+ filePath: "version.h"
+ fileTags: "hpp"
+ }
+//! [2]
+//! [3]
+ prepare: {
+ var cmd = new JavaScriptCommand();
+ cmd.description = "generating " + output.fileName;
+ cmd.highlight = "codegen";
+ cmd.sourceCode = function() {
+ var file = new TextFile(input.filePath, TextFile.ReadOnly);
+ var content = file.readAll();
+
+ content = content.replace(
+ "${PRODUCT_VERSION}",
+ product.mybuildconfig.productVersion);
+
+ file = new TextFile(output.filePath, TextFile.WriteOnly);
+ file.write(content);
+ file.close();
+ }
+ return cmd;
+ }
+//! [3]
+ }
+
+//! [4]
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: exportingProduct.buildDirectory
+ }
+//! [4]
+}
+//! [5]
diff --git a/tutorial/chapter-9/version-header/version.h.in b/tutorial/chapter-9/version-header/version.h.in
new file mode 100644
index 000000000..2e6604967
--- /dev/null
+++ b/tutorial/chapter-9/version-header/version.h.in
@@ -0,0 +1,9 @@
+//! [0]
+// version-header/version.h.in
+#ifndef VERSION_H
+#define VERSION_H
+
+const char kProductVersion[] = "${PRODUCT_VERSION}";
+
+#endif // VERSION_H
+//! [0]
diff --git a/tutorial/tutorial.qbs b/tutorial/tutorial.qbs
new file mode 100644
index 000000000..0d2ae3514
--- /dev/null
+++ b/tutorial/tutorial.qbs
@@ -0,0 +1,5 @@
+Product {
+ files: [
+ "*/**",
+ ]
+}